Cookie handling; deal with relative URI in Location YAZ-719
authorAdam Dickmeiss <adam@indexdata.dk>
Thu, 9 Jan 2014 12:29:58 +0000 (13:29 +0100)
committerAdam Dickmeiss <adam@indexdata.dk>
Thu, 9 Jan 2014 12:29:58 +0000 (13:29 +0100)
client/client.c
include/yaz/Makefile.am
include/yaz/cookie.h [new file with mode: 0644]
src/Makefile.am
src/cookie.c [new file with mode: 0644]
src/zoom-c.c
src/zoom-p.h
win/makefile

index 46b6450..6fea730 100644 (file)
@@ -64,6 +64,7 @@
 #include <yaz/cql.h>
 #include <yaz/log.h>
 #include <yaz/facet.h>
+#include <yaz/cookie.h>
 
 #if HAVE_READLINE_READLINE_H
 #include <readline/readline.h>
@@ -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");
index 61e64b2..0c5ab8a 100644 (file)
@@ -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 (file)
index 0000000..8d83809
--- /dev/null
@@ -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/zgdu.h>
+
+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
+ */
+
index b270289..ca9d193 100644 (file)
@@ -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 (file)
index 0000000..2a22b2f
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <yaz/cookie.h>
+#include <yaz/log.h>
+
+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
+ */
+
index c23e631..b162b6d 100644 (file)
@@ -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;
         }
     }
index 295893e..77f39e9 100644 (file)
@@ -34,6 +34,7 @@
 #include <yaz/wrbuf.h>
 #include <yaz/zoom.h>
 #include <yaz/srw.h>
+#include <yaz/cookie.h>
 #include <yaz/mutex.h>
 
 #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;
index 279cc86..2b9ae83 100644 (file)
@@ -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 \