From 1f3fe256d54ab81d998cd622abda89580cc0b3ff Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Wed, 1 Mar 2006 23:24:24 +0000 Subject: [PATCH] Added SRU-GET and SRU-POST support for ZOOM C. Option "sru" specifies which mode to use; one of "post", "get" or "soap" (SRW, default). --- NEWS | 3 + include/yaz/srw.h | 6 +- include/yaz/zgdu.h | 6 +- src/soap.c | 41 ++++++++++- src/srwutil.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/zgdu.c | 18 ++++- src/zoom-c.c | 68 +++++++++++------ src/zoom-p.h | 11 ++- zoom/zoomtst2.c | 5 +- 9 files changed, 331 insertions(+), 33 deletions(-) diff --git a/NEWS b/NEWS index a2e8fff..f36d557 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,7 @@ +Added SRU-GET and SRU-POST support for ZOOM C. Option "sru" specifies +which mode to use; one of "post", "get" or "soap" (SRW, default). + Fixed bug in character set conversion yaz_iconv. Some three byte UTF-8 sequences where not read correctly. Fix by Rustam Usmanov. diff --git a/include/yaz/srw.h b/include/yaz/srw.h index 7e221f7..dc721a1 100644 --- a/include/yaz/srw.h +++ b/include/yaz/srw.h @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: srw.h,v 1.24 2005-12-14 14:05:55 adam Exp $ + * $Id: srw.h,v 1.25 2006-03-01 23:24:24 adam Exp $ */ /** * \file srw.h @@ -220,6 +220,10 @@ YAZ_EXPORT void yaz_mk_srw_diagnostic(ODR o, Z_SRW_diagnostic *d, const char *uri, const char *message, const char *details); +YAZ_EXPORT int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu, + ODR encode, char *charset); +YAZ_EXPORT int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu, + ODR encode, char *charset); YAZ_END_CDECL #endif diff --git a/include/yaz/zgdu.h b/include/yaz/zgdu.h index e7a97c0..daa9d4a 100644 --- a/include/yaz/zgdu.h +++ b/include/yaz/zgdu.h @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: zgdu.h,v 1.5 2005-06-25 15:46:03 adam Exp $ + * $Id: zgdu.h,v 1.6 2006-03-01 23:24:25 adam Exp $ */ /** @@ -56,6 +56,10 @@ typedef struct { YAZ_EXPORT int z_GDU (ODR o, Z_GDU **p, int opt, const char *name); YAZ_EXPORT void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n, const char *v); +YAZ_EXPORT void z_HTTP_header_add_content_type(ODR o, Z_HTTP_Header **hp, + const char *content_type, + const char *charset); + YAZ_EXPORT const char *z_HTTP_header_lookup(Z_HTTP_Header *hp, const char *n); YAZ_EXPORT const char *z_HTTP_errmsg(int code); diff --git a/src/soap.c b/src/soap.c index 5bbc9cc..45e86cb 100644 --- a/src/soap.c +++ b/src/soap.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: soap.c,v 1.12 2005-08-22 20:34:21 adam Exp $ + * $Id: soap.c,v 1.13 2006-03-01 23:24:25 adam Exp $ */ /** * \file soap.c @@ -45,8 +45,45 @@ int z_soap_codec_enc_xsl(ODR o, Z_SOAP **pp, return z_soap_error(o, p, "SOAP-ENV:Client", "Bad XML Document", 0); - /* check that root node is Envelope */ ptr = xmlDocGetRootElement(doc); + if (!ptr || !ptr->ns) + { + xmlFreeDoc(doc); + return z_soap_error(o, p, "SOAP-ENV:Client", + "No Envelope element", 0); + } + /* check for SRU root node match */ + + for (i = 0; handlers[i].ns; i++) + if (!xmlStrcmp(ptr->ns->href, BAD_CAST handlers[i].ns)) + break; + if (handlers[i].ns) + { + void *handler_data = 0; + xmlNode p_top_tmp; /* pseudo parent node needed */ + + p_top_tmp.children = ptr; + ret = (*handlers[i].f)(o, &p_top_tmp, &handler_data, + handlers[i].client_data, + handlers[i].ns); + + if (ret || !handler_data) + z_soap_error(o, p, "SOAP-ENV:Client", + "SOAP Handler returned error", 0); + else + { + p->which = Z_SOAP_generic; + p->u.generic = (Z_SOAP_Generic *) + odr_malloc(o, sizeof(*p->u.generic)); + p->u.generic->no = i; + p->u.generic->ns = handlers[i].ns; + p->u.generic->p = handler_data; + } + xmlFreeDoc(doc); + return ret; + } + /* OK: assume SOAP */ + if (!ptr || ptr->type != XML_ELEMENT_NODE || xmlStrcmp(ptr->name, BAD_CAST "Envelope") || !ptr->ns) { diff --git a/src/srwutil.c b/src/srwutil.c index 168d12b..e075dad 100644 --- a/src/srwutil.c +++ b/src/srwutil.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: srwutil.c,v 1.35 2006-02-01 20:28:44 adam Exp $ + * $Id: srwutil.c,v 1.36 2006-03-01 23:24:25 adam Exp $ */ /** * \file srwutil.c @@ -24,6 +24,52 @@ static int hex_digit (int ch) return 0; } +void encode_uri_char(char *dst, char ch) +{ + if (ch == ' ') + strcpy(dst, "+"); + else if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || + (ch >= '0' && ch <= '9')) + { + dst[0] = ch; + dst[1] = '\0'; + } + else + { + dst[0] = '%'; + sprintf(dst+1, "%02X", (unsigned char ) ch); + } +} + +void yaz_array_to_uri(char **path, ODR o, char **name, char **value) +{ + size_t i, szp = 0, sz = 0; + for(i = 0; name[i]; i++) + sz += strlen(name[i]) + 3 + strlen(value[i]) * 3; + *path = odr_malloc(o, sz); + + for(i = 0; name[i]; i++) + { + size_t j, ilen; + if (i) + (*path)[szp++] = '&'; + ilen = strlen(name[i]); + memcpy(*path+szp, name[i], ilen); + szp += ilen; + (*path)[szp++] = '='; + for (j = 0; value[i][j]; j++) + { + size_t vlen; + char vstr[5]; + encode_uri_char(vstr, value[i][j]); + vlen = strlen(vstr); + memcpy(*path+szp, vstr, vlen); + szp += vlen; + } + } + (*path)[szp] = '\0'; +} + int yaz_uri_array(const char *path, ODR o, char ***name, char ***val) { int no = 2; @@ -32,7 +78,7 @@ int yaz_uri_array(const char *path, ODR o, char ***name, char ***val) if (*path == '?') path++; if (!*path) - return no; + return 0; cp = path; while ((cp = strchr(cp, '&'))) { @@ -872,6 +918,162 @@ int yaz_diag_srw_to_bib1(int code) return 1; } +static void add_val_int(ODR o, char **name, char **value, int *i, + char *a_name, int *val) +{ + if (val) + { + name[*i] = a_name; + value[*i] = odr_malloc(o, 30); + sprintf(value[*i], "%d", *val); + (*i)++; + } +} + +static void add_val_str(ODR o, char **name, char **value, int *i, + char *a_name, char *val) +{ + if (val) + { + name[*i] = a_name; + value[*i] = val; + (*i)++; + } +} + +static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode, + char **name, char **value) +{ + int i = 0; + add_val_str(encode, name, value, &i, "version", srw_pdu->srw_version); + name[i] = "operation"; + switch(srw_pdu->which) + { + case Z_SRW_searchRetrieve_request: + value[i++] = "searchRetrieve"; + switch(srw_pdu->u.request->query_type) + { + case Z_SRW_query_type_cql: + add_val_str(encode, name, value, &i, "query", + srw_pdu->u.request->query.cql); + break; + case Z_SRW_query_type_pqf: + add_val_str(encode, name, value, &i, "x-pquery", + srw_pdu->u.request->query.pqf); + break; + case Z_SRW_query_type_xcql: + add_val_str(encode, name, value, &i, "x-cql", + srw_pdu->u.request->query.xcql); + break; + } + switch(srw_pdu->u.request->sort_type) + { + case Z_SRW_sort_type_none: + break; + case Z_SRW_sort_type_sort: + add_val_str(encode, name, value, &i, "sortKeys", + srw_pdu->u.request->sort.sortKeys); + break; + } + add_val_int(encode, name, value, &i, "startRecord", + srw_pdu->u.request->startRecord); + add_val_int(encode, name, value, &i, "maximumRecords", + srw_pdu->u.request->maximumRecords); + add_val_str(encode, name, value, &i, "recordSchema", + srw_pdu->u.request->recordSchema); + add_val_str(encode, name, value, &i, "recordPacking", + srw_pdu->u.request->recordPacking); + add_val_str(encode, name, value, &i, "recordXPath", + srw_pdu->u.request->recordXPath); + add_val_str(encode, name, value, &i, "stylesheet", + srw_pdu->u.request->stylesheet); + add_val_int(encode, name, value, &i, "resultSetTTL", + srw_pdu->u.request->resultSetTTL); + break; + case Z_SRW_explain_request: + value[i++] = "explain"; + add_val_str(encode, name, value, &i, "stylesheet", + srw_pdu->u.explain_request->stylesheet); + break; + case Z_SRW_scan_request: + value[i++] = "scan"; + + switch(srw_pdu->u.scan_request->query_type) + { + case Z_SRW_query_type_cql: + add_val_str(encode, name, value, &i, "scanClause", + srw_pdu->u.scan_request->scanClause.cql); + break; + case Z_SRW_query_type_pqf: + add_val_str(encode, name, value, &i, "x-pScanClause", + srw_pdu->u.scan_request->scanClause.pqf); + break; + case Z_SRW_query_type_xcql: + add_val_str(encode, name, value, &i, "x-cqlScanClause", + srw_pdu->u.scan_request->scanClause.xcql); + break; + } + add_val_int(encode, name, value, &i, "responsePosition", + srw_pdu->u.scan_request->responsePosition); + add_val_int(encode, name, value, &i, "maximumTerms", + srw_pdu->u.scan_request->maximumTerms); + add_val_str(encode, name, value, &i, "stylesheet", + srw_pdu->u.scan_request->stylesheet); + break; + case Z_SRW_update_request: + value[i++] = "update"; + break; + default: + return -1; + } + name[i++] = 0; + return 0; +} + +int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu, + ODR encode, char *charset) +{ + char *name[30], *value[30]; /* definite upper limit for SRU params */ + char *uri_args; + char *path; + + if (yaz_get_sru_parms(srw_pdu, encode, name, value)) + return -1; + yaz_array_to_uri(&uri_args, encode, name, value); + + hreq->method = "GET"; + + path = odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 3); + sprintf(path, "%s?%s", hreq->path, uri_args); + hreq->path = path; + + z_HTTP_header_add_content_type(encode, &hreq->headers, + "text/xml", charset); + return 0; +} + +int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu, + ODR encode, char *charset) +{ + char *name[30], *value[30]; /* definite upper limit for SRU params */ + char *uri_args; + + if (yaz_get_sru_parms(srw_pdu, encode, name, value)) + return -1; + + yaz_array_to_uri(&uri_args, encode, name, value); + + hreq->method = "POST"; + + hreq->content_buf = uri_args; + hreq->content_len = strlen(uri_args); + + z_HTTP_header_add_content_type(encode, &hreq->headers, + "application/x-www-form-urlencoded", + charset); + return 0; +} + /* * Local variables: * c-basic-offset: 4 diff --git a/src/zgdu.c b/src/zgdu.c index 88c92a5..0dfdbaf 100644 --- a/src/zgdu.c +++ b/src/zgdu.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: zgdu.c,v 1.13 2005-06-25 15:46:06 adam Exp $ + * $Id: zgdu.c,v 1.14 2006-03-01 23:24:25 adam Exp $ */ /** @@ -152,6 +152,22 @@ static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers, return 1; } +void z_HTTP_header_add_content_type(ODR o, Z_HTTP_Header **hp, + const char *content_type, + const char *charset) +{ + const char *l = "Content-Type"; + if (charset) + { + char *ctype = odr_malloc(o, strlen(content_type)+strlen(charset) + 15); + sprintf(ctype, "%s; charset=%s", content_type, charset); + z_HTTP_header_add(o, hp, l, ctype); + } + else + z_HTTP_header_add(o, hp, l, content_type); + +} + void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n, const char *v) { diff --git a/src/zoom-c.c b/src/zoom-c.c index 6b90468..384ef76 100644 --- a/src/zoom-c.c +++ b/src/zoom-c.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: zoom-c.c,v 1.63 2006-02-19 18:36:10 adam Exp $ + * $Id: zoom-c.c,v 1.64 2006-03-01 23:24:25 adam Exp $ */ /** * \file zoom-c.c @@ -54,7 +54,6 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c); static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out); static char *cql2pqf(ZOOM_connection c, const char *cql); - static void initlog() { static int log_level_initialized = 0; @@ -339,6 +338,19 @@ ZOOM_connection_new (const char *host, int portnum) return c; } +static zoom_sru_mode get_sru_mode_from_string(const char *s) +{ + if (!s || !*s) + return zoom_sru_soap; + if (!yaz_matchstr(s, "soap")) + return zoom_sru_soap; + else if (!yaz_matchstr(s, "get")) + return zoom_sru_get; + else if (!yaz_matchstr(s, "post")) + return zoom_sru_post; + return zoom_sru_error; +} + ZOOM_API(void) ZOOM_connection_connect(ZOOM_connection c, const char *host, int portnum) @@ -388,6 +400,9 @@ ZOOM_connection_connect(ZOOM_connection c, else c->lang = 0; + val = ZOOM_options_get (c->options, "sru"); + c->sru_mode = get_sru_mode_from_string(val); + xfree (c->host_port); if (portnum) { @@ -1129,7 +1144,7 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c) ZOOM_options_get(c->options, "implementationName"), odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName)); - version = odr_strdup(c->odr_out, "$Revision: 1.63 $"); + version = odr_strdup(c->odr_out, "$Revision: 1.64 $"); if (strlen(version) > 10) /* check for unexpanded CVS strings */ version[strlen(version)-2] = '\0'; ireq->implementationVersion = odr_prepend(c->odr_out, @@ -1225,7 +1240,6 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c) #if HAVE_XML2 static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr) { - char ctype[50]; Z_SOAP_Handler h[2] = { {"http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec}, {0, 0, 0} @@ -1262,34 +1276,40 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr) } } - strcpy(ctype, "text/xml"); - if (c->charset && strlen(c->charset) < 20) + if (c->sru_mode == zoom_sru_get) + { + yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset); + } + else if (c->sru_mode == zoom_sru_post) { - strcat(ctype, "; charset="); - strcat(ctype, c->charset); + yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset); } - z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, - "Content-Type", ctype); - z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, - "SOAPAction", "\"\""); - p->which = Z_SOAP_generic; - p->u.generic = (Z_SOAP_Generic *) odr_malloc(o, sizeof(*p->u.generic)); - p->u.generic->no = 0; - p->u.generic->ns = 0; - p->u.generic->p = sr; - p->ns = "http://schemas.xmlsoap.org/soap/envelope/"; + else if (c->sru_mode == zoom_sru_soap) + { + z_HTTP_header_add_content_type(c->odr_out, + &gdu->u.HTTP_Request->headers, + "text/xml", c->charset); - ret = z_soap_codec_enc(o, &p, - &gdu->u.HTTP_Request->content_buf, - &gdu->u.HTTP_Request->content_len, h, - c->charset); + z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, + "SOAPAction", "\"\""); + p->which = Z_SOAP_generic; + p->u.generic = (Z_SOAP_Generic *) odr_malloc(o, sizeof(*p->u.generic)); + p->u.generic->no = 0; + p->u.generic->ns = 0; + p->u.generic->p = sr; + p->ns = "http://schemas.xmlsoap.org/soap/envelope/"; + + ret = z_soap_codec_enc(o, &p, + &gdu->u.HTTP_Request->content_buf, + &gdu->u.HTTP_Request->content_len, h, + c->charset); + } if (!z_GDU(c->odr_out, &gdu, 0, 0)) return zoom_complete; c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0); - odr_destroy(o); - + event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU); ZOOM_connection_put_event (c, event); odr_reset(c->odr_out); diff --git a/src/zoom-p.h b/src/zoom-p.h index d445957..5213cfc 100644 --- a/src/zoom-p.h +++ b/src/zoom-p.h @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: zoom-p.h,v 1.9 2005-10-17 12:25:39 mike Exp $ + * $Id: zoom-p.h,v 1.10 2006-03-01 23:24:26 adam Exp $ */ /** * \file zoom-p.h @@ -24,6 +24,14 @@ struct ZOOM_query_p { char *query_string; }; +typedef enum { + zoom_sru_error, + zoom_sru_soap, + zoom_sru_get, + zoom_sru_post, +} zoom_sru_mode; + + typedef struct ZOOM_task_p *ZOOM_task; #define STATE_IDLE 0 @@ -65,6 +73,7 @@ struct ZOOM_connection_p { ZOOM_resultset resultsets; ZOOM_Event m_queue_front; ZOOM_Event m_queue_back; + zoom_sru_mode sru_mode; }; struct ZOOM_options_entry { diff --git a/zoom/zoomtst2.c b/zoom/zoomtst2.c index f670802..40b197c 100644 --- a/zoom/zoomtst2.c +++ b/zoom/zoomtst2.c @@ -1,5 +1,5 @@ /* - * $Id: zoomtst2.c,v 1.6 2005-06-25 15:46:08 adam Exp $ + * $Id: zoomtst2.c,v 1.7 2006-03-01 23:24:26 adam Exp $ * * Asynchronous single-target client performing search (no retrieval) */ @@ -27,6 +27,9 @@ int main(int argc, char **argv) /* create connection (don't connect yet) */ z = ZOOM_connection_create(0); + /* option: set sru/get operation (only applicable if http: is used) */ + ZOOM_connection_option_set (z, "sru", "post"); + /* option: set async operation */ ZOOM_connection_option_set (z, "async", "1"); -- 1.7.10.4