From: Adam Dickmeiss Date: Thu, 9 Jan 2014 12:29:58 +0000 (+0100) Subject: Cookie handling; deal with relative URI in Location YAZ-719 X-Git-Tag: v5.0.10~1 X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=commitdiff_plain;h=2cb501be3b31d5ddc14de30498fadb4b31d56fb5 Cookie handling; deal with relative URI in Location YAZ-719 --- diff --git a/client/client.c b/client/client.c index 46b6450..6fea730 100644 --- a/client/client.c +++ b/client/client.c @@ -64,6 +64,7 @@ #include #include #include +#include #if HAVE_READLINE_READLINE_H #include @@ -149,6 +150,7 @@ static char cur_host[200]; static Odr_int last_hit_count = 0; static int pretty_xml = 0; static Odr_int sru_maximumRecords = 0; +static yaz_cookies_t yaz_cookies = 0; typedef enum { QueryType_Prefix, @@ -742,6 +744,9 @@ static int session_connect(const char *arg) int r; const char *basep = 0; + yaz_cookies_destroy(yaz_cookies); + yaz_cookies = yaz_cookies_create(); + r = session_connect_base(arg, &basep); if (basep && *basep) set_base(basep); @@ -1361,43 +1366,17 @@ static int send_srw(Z_SRW_PDU *sr) return send_srw_host_path(sr, cur_host, path); } -static int send_SRW_redirect(const char *uri, Z_HTTP_Response *cookie_hres) +static int send_SRW_redirect(const char *uri) { const char *username = 0; const char *password = 0; - struct Z_HTTP_Header *h; - char *combined_cookies = 0; - int combined_cookies_len = 0; Z_GDU *gdu = get_HTTP_Request_url(out, uri); gdu->u.HTTP_Request->method = odr_strdup(out, "GET"); z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers, "Accept", "text/xml"); - for (h = cookie_hres->headers; h; h = h->next) - { - if (!strcmp(h->name, "Set-Cookie")) - { - char *cp; - - if (!(cp = strchr(h->value, ';'))) - cp = h->value + strlen(h->value); - if (cp - h->value >= 1) - { - combined_cookies = xrealloc(combined_cookies, combined_cookies_len + cp - h->value + 3); - memcpy(combined_cookies+combined_cookies_len, h->value, cp - h->value); - combined_cookies[combined_cookies_len + cp - h->value] = '\0'; - strcat(combined_cookies,"; "); - combined_cookies_len = strlen(combined_cookies); - } - } - } - if (combined_cookies_len) - { - z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers, "Cookie", combined_cookies); - xfree(combined_cookies); - } - + yaz_cookies_request(yaz_cookies, out, gdu->u.HTTP_Request); if (auth) { if (auth->which == Z_IdAuthentication_open) @@ -3265,6 +3244,7 @@ static int cmd_show(const char *arg) static void exit_client(int code) { + yaz_cookies_destroy(yaz_cookies); file_history_save(file_history); file_history_destroy(&file_history); nmem_destroy(nmem_auth); @@ -4529,7 +4509,7 @@ static void http_response(Z_HTTP_Response *hres) } #endif -#define max_HTTP_redirects 2 +#define max_HTTP_redirects 3 static void wait_and_handle_response(int one_response_only) { @@ -4666,17 +4646,37 @@ static void wait_and_handle_response(int one_response_only) Z_HTTP_Response *hres = gdu->u.HTTP_Response; int code = hres->code; const char *location = 0; + + yaz_cookies_response(yaz_cookies, hres); if ((code == 301 || code == 302) && no_redirects < max_HTTP_redirects && !yaz_matchstr(sru_method, "get") && (location = z_HTTP_header_lookup(hres->headers, "Location"))) { const char *base_tmp; - session_connect_base(location, &base_tmp); + + if (*location == '/') + { + char *args = 0; + char *nlocation = odr_malloc(in, strlen(location) + + strlen(cur_host) + 3); + strcpy(nlocation, cur_host); + cs_get_host_args(nlocation, (const char **) &args); + if (!args || !*args) + args = nlocation + strlen(nlocation); + else + args--; + strcpy(args, location); + location = nlocation; + } + else + { + session_connect_base(location, &base_tmp); + } no_redirects++; if (conn) { - if (send_SRW_redirect(location, hres) == 2) + if (send_SRW_redirect(location) == 2) continue; } printf("Redirect failed\n"); diff --git a/include/yaz/Makefile.am b/include/yaz/Makefile.am index 61e64b2..0c5ab8a 100644 --- a/include/yaz/Makefile.am +++ b/include/yaz/Makefile.am @@ -4,7 +4,7 @@ noinst_HEADERS = icu_I18N.h pkginclude_HEADERS= backend.h base64.h \ - ccl.h ccl_xml.h cql.h rpn2cql.h rpn2solr.h \ + ccl.h ccl_xml.h cookie.h cql.h rpn2cql.h rpn2solr.h \ solr.h comstack.h \ diagbib1.h diagsrw.h diagsru_update.h sortspec.h log.h logrpn.h marcdisp.h \ nmem.h nmem_xml.h odr.h errno.h facet.h \ diff --git a/include/yaz/cookie.h b/include/yaz/cookie.h new file mode 100644 index 0000000..8d83809 --- /dev/null +++ b/include/yaz/cookie.h @@ -0,0 +1,62 @@ +/* This file is part of the YAZ toolkit. + * Copyright (C) Index Data. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Index Data nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file cookie.h + * \brief HTTP cookie handling + */ + +#ifndef YAZ_COOKIE_H +#define YAZ_COOKIE_H + +#include + +YAZ_BEGIN_CDECL + +typedef struct yaz_cookies_s *yaz_cookies_t; + +YAZ_EXPORT yaz_cookies_t yaz_cookies_create(void); + +YAZ_EXPORT void yaz_cookies_destroy(yaz_cookies_t yt); + +YAZ_EXPORT void yaz_cookies_response(yaz_cookies_t yc, Z_HTTP_Response *res); + +YAZ_EXPORT void yaz_cookies_request(yaz_cookies_t yc, ODR odr, + Z_HTTP_Request *req); + +YAZ_END_CDECL + +#endif +/* + * Local variables: + * c-basic-offset: 4 + * c-file-style: "Stroustrup" + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ + diff --git a/src/Makefile.am b/src/Makefile.am index b270289..ca9d193 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -73,7 +73,7 @@ GEN_FILES = oid_std.c \ libyaz_la_SOURCES=base64.c version.c options.c log.c \ $(GEN_FILES) \ - marcdisp.c \ + cookie.c marcdisp.c \ marc_read_json.c marc_read_xml.c marc_read_iso2709.c marc_read_line.c \ wrbuf.c oid_db.c errno.c \ nmemsdup.c xmalloc.c readconf.c tpath.c nmem.c matchstr.c atoin.c \ diff --git a/src/cookie.c b/src/cookie.c new file mode 100644 index 0000000..2a22b2f --- /dev/null +++ b/src/cookie.c @@ -0,0 +1,127 @@ +/* This file is part of the YAZ toolkit. + * Copyright (C) Index Data + * See the file LICENSE for details. + */ +/** + * \file cookie.c + * \brief HTTP cookie utility + */ +#if HAVE_CONFIG_H +#include +#endif + +#include +#include + +struct cookie { + char *name; + char *value; + char *path; + char *domain; + struct cookie *next; +}; + +struct yaz_cookies_s { + struct cookie *list; +}; + +yaz_cookies_t yaz_cookies_create(void) +{ + yaz_cookies_t yc = xmalloc(sizeof(*yc)); + yc->list = 0; + return yc; +} + +void yaz_cookies_destroy(yaz_cookies_t yc) +{ + if (yc) + { + struct cookie *c = yc->list; + while (c) + { + struct cookie *c1 = c->next; + xfree(c->name); + xfree(c->value); + xfree(c->path); + xfree(c->domain); + xfree(c); + c = c1; + } + xfree(yc); + } +} + +void yaz_cookies_response(yaz_cookies_t yc, Z_HTTP_Response *res) +{ + struct Z_HTTP_Header *h; + for (h = res->headers; h; h = h->next) + { + if (!strcmp(h->name, "Set-Cookie")) + { + const char *cp; + const char *cp1; + size_t len; + struct cookie *c; + cp = strchr(h->value, '='); + if (!cp) + continue; + len = cp - h->value; + for (c = yc->list; c; c = c->next) + if (!strncmp(h->value, c->name, len) && c->name[len] == '\0') + break; + if (!c) + { + c = xmalloc(sizeof(*c)); + c->name = xstrndup(h->value, len); + c->value = 0; + c->path = 0; + c->domain = 0; + c->next = yc->list; + yc->list = c; + } + cp++; /* skip = */ + cp1 = strchr(cp, ';'); + if (!cp1) + cp1 = cp + strlen(cp); + xfree(c->value); + c->value = xstrndup(cp, cp1 - cp); + } + } +} + +void yaz_cookies_request(yaz_cookies_t yc, ODR odr, Z_HTTP_Request *req) +{ + struct cookie *c; + size_t sz = 0; + + for (c = yc->list; c; c = c->next) + { + if (c->name && c->value) + sz += strlen(c->name) + strlen(c->value) + 3; + } + if (sz) + { + char *buf = odr_malloc(odr, sz + 1); + + *buf = '\0'; + for (c = yc->list; c; c = c->next) + { + if (*buf) + strcat(buf, "; "); + strcat(buf, c->name); + strcat(buf, "="); + strcat(buf, c->value); + } + z_HTTP_header_add(odr, &req->headers, "Cookie", buf); + } +} + +/* + * Local variables: + * c-basic-offset: 4 + * c-file-style: "Stroustrup" + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ + diff --git a/src/zoom-c.c b/src/zoom-c.c index c23e631..b162b6d 100644 --- a/src/zoom-c.c +++ b/src/zoom-c.c @@ -306,6 +306,7 @@ ZOOM_API(ZOOM_connection) c->sru_version = 0; c->no_redirects = 0; + c->cookies = 0; c->saveAPDU_wrbuf = 0; return c; } @@ -542,6 +543,9 @@ ZOOM_API(void) c->async = ZOOM_options_get_bool(c->options, "async", 0); + yaz_cookies_destroy(c->cookies); + c->cookies = yaz_cookies_create(); + if (c->sru_mode == zoom_sru_error) { ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, val); @@ -619,6 +623,7 @@ ZOOM_API(void) xfree(c->group); xfree(c->password); xfree(c->sru_version); + yaz_cookies_destroy(c->cookies); wrbuf_destroy(c->saveAPDU_wrbuf); xfree(c); } @@ -1478,43 +1483,14 @@ ZOOM_API(int) #if YAZ_HAVE_XML2 -static zoom_ret send_HTTP_redirect(ZOOM_connection c, const char *uri, - Z_HTTP_Response *cookie_hres) +static zoom_ret send_HTTP_redirect(ZOOM_connection c, const char *uri) { - struct Z_HTTP_Header *h; - char *combined_cookies = 0; - int combined_cookies_len = 0; Z_GDU *gdu = z_get_HTTP_Request_uri(c->odr_out, uri, 0, c->proxy ? 1 : 0); gdu->u.HTTP_Request->method = odr_strdup(c->odr_out, "GET"); z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, "Accept", "text/xml"); - - for (h = cookie_hres->headers; h; h = h->next) - { - if (!strcmp(h->name, "Set-Cookie")) - { - char *cp; - - if (!(cp = strchr(h->value, ';'))) - cp = h->value + strlen(h->value); - if (cp - h->value >= 1) { - combined_cookies = xrealloc(combined_cookies, combined_cookies_len + cp - h->value + 3); - memcpy(combined_cookies+combined_cookies_len, h->value, cp - h->value); - combined_cookies[combined_cookies_len + cp - h->value] = '\0'; - strcat(combined_cookies,"; "); - combined_cookies_len = strlen(combined_cookies); - } - } - } - - if (combined_cookies_len) - { - z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, - "Cookie", combined_cookies); - xfree(combined_cookies); - } - + yaz_cookies_request(c->cookies, c->odr_out, gdu->u.HTTP_Request); if (c->user && c->password) { z_HTTP_header_add_basic_auth(c->odr_out, &gdu->u.HTTP_Request->headers, @@ -1564,6 +1540,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres) ZOOM_connection_set_mask(c, 0); yaz_log(c->log_details, "%p handle_http", c); + yaz_cookies_response(c->cookies, hres); if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get && (location = z_HTTP_header_lookup(hres->headers, "Location"))) { @@ -1578,9 +1555,26 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres) { /* since redirect may change host we just reconnect. A smarter implementation might check whether it's the same server */ - do_connect_host(c, location); - send_HTTP_redirect(c, location, hres); - /* we're OK for now. Operation is not really complete */ + if (*location != '/') + { + /* full header */ + do_connect_host(c, location); + send_HTTP_redirect(c, location); + } + else + { /* relative header - same host */ + char *args = 0; + char *nlocation = odr_malloc(c->odr_in, strlen(location) + + strlen(c->host_port) + 3); + strcpy(nlocation, c->host_port); + cs_get_host_args(nlocation, (const char **) &args); + if (!args || !*args) + args = nlocation + strlen(nlocation); + else + args--; + strcpy(args, location); + send_HTTP_redirect(c, nlocation); + } return; } } diff --git a/src/zoom-p.h b/src/zoom-p.h index 295893e..77f39e9 100644 --- a/src/zoom-p.h +++ b/src/zoom-p.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #define SHPTR 1 @@ -102,6 +103,7 @@ struct ZOOM_connection_p { ZOOM_Event m_queue_back; zoom_sru_mode sru_mode; int no_redirects; /* 0 for no redirects. >0 for number of redirects */ + yaz_cookies_t cookies; int log_details; int log_api; diff --git a/win/makefile b/win/makefile index 279cc86..2b9ae83 100644 --- a/win/makefile +++ b/win/makefile @@ -515,6 +515,7 @@ MISC_OBJS= \ $(OBJDIR)\xmlquery.obj \ $(OBJDIR)\xmlerror.obj \ $(OBJDIR)\mime.obj \ + $(OBJDIR)\cookie.obj \ $(OBJDIR)\cql.obj \ $(OBJDIR)\cql2ccl.obj \ $(OBJDIR)\cql_sortkeys.obj \