Do not mess with databaseName for HTTP redirect
[yaz-moved-to-github.git] / client / client.c
index 09c9a85..3c68c58 100644 (file)
@@ -182,6 +182,7 @@ static void close_session(void);
 static void marc_file_write(const char *buf, size_t sz);
 
 static void wait_and_handle_response(int one_response_only);
+static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url);
 
 ODR getODROutputStream(void)
 {
@@ -665,11 +666,10 @@ static int cmd_base(const char *arg)
     return set_base(arg);
 }
 
-int session_connect(const char *arg)
+static int session_connect_base(const char *arg, const char **basep)
 {
     void *add;
     char type_and_host[101];
-    const char *basep = 0;
     if (conn)
     {
         cs_close(conn);
@@ -681,7 +681,7 @@ int session_connect(const char *arg)
         session_mem = NULL;
         session_initResponse = 0;
     }
-    cs_get_host_args(arg, &basep);
+    cs_get_host_args(arg, basep);
 
     strncpy(type_and_host, arg, sizeof(type_and_host)-1);
     type_and_host[sizeof(type_and_host)-1] = '\0';
@@ -717,11 +717,6 @@ int session_connect(const char *arg)
     }
     printf("OK.\n");
     cs_print_session_info(conn);
-    if (basep && *basep)
-        set_base(basep);
-    else if (protocol == PROTO_Z3950)
-        set_base("Default");
-
     if (protocol == PROTO_Z3950)
     {
         send_initRequest(type_and_host);
@@ -730,6 +725,19 @@ int session_connect(const char *arg)
     return 0;
 }
 
+static int session_connect(const char *arg)
+{
+    int r;
+    const char *basep = 0;
+
+    r = session_connect_base(arg, &basep);
+    if (basep && *basep)
+        set_base(basep);
+    else if (protocol == PROTO_Z3950)
+        set_base("Default");
+    return r;
+}
+
 int cmd_open(const char *arg)
 {
     int r;
@@ -1191,12 +1199,39 @@ static int send_deleteResultSetRequest(const char *arg)
 }
 
 #if YAZ_HAVE_XML2
-static int send_srw(Z_SRW_PDU *sr)
+static int send_gdu(Z_GDU *gdu)
+{
+    if (z_GDU(out, &gdu, 0, 0))
+    {
+        /* encode OK */
+        char *buf_out;
+        int len_out;
+        int r;
+        if (apdu_file)
+        {
+            if (!z_GDU(print, &gdu, 0, 0))
+                printf("Failed to print outgoing SRU package\n");
+            odr_reset(print);
+        }
+        buf_out = odr_getbuf(out, &len_out, 0);
+
+        /* we don't odr_reset(out), since we may need the buffer again */
+
+        do_hex_dump(buf_out, len_out);
+
+        r = cs_put(conn, buf_out, len_out);
+
+        if (r >= 0)
+            return 2;
+    }
+    return 0;
+}
+
+static int send_srw_host_path(Z_SRW_PDU *sr, const char *host_port,
+                              char *path)
 {
     const char *charset = negotiationCharset;
-    const char *host_port = cur_host;
     Z_GDU *gdu;
-    char *path = yaz_encode_sru_dbpath_odr(out, databaseNames[0]);
 
     gdu = z_get_HTTP_Request_host_path(out, host_port, path);
 
@@ -1231,31 +1266,59 @@ static int send_srw(Z_SRW_PDU *sr)
     {
         yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, out, charset);
     }
+    return send_gdu(gdu);
+}
 
-    if (z_GDU(out, &gdu, 0, 0))
-    {
-        /* encode OK */
-        char *buf_out;
-        int len_out;
-        int r;
-        if (apdu_file)
-        {
-            if (!z_GDU(print, &gdu, 0, 0))
-                printf("Failed to print outgoing SRU package\n");
-            odr_reset(print);
-        }
-        buf_out = odr_getbuf(out, &len_out, 0);
+static int send_srw(Z_SRW_PDU *sr)
+{
+    char *path = yaz_encode_sru_dbpath_odr(out, databaseNames[0]);
+    return send_srw_host_path(sr, cur_host, path);
+}
 
-        /* we don't odr_reset(out), since we may need the buffer again */
+static int send_SRW_redirect(const char *uri, Z_HTTP_Response *cookie_hres)
+{
+    const char *username = 0;
+    const char *password = 0;
+    struct Z_HTTP_Header *h;
+    Z_GDU *gdu = get_HTTP_Request_url(out, uri);
 
-        do_hex_dump(buf_out, len_out);
+    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"))
+            z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers,
+                              "Cookie", h->value);
+    }
 
-        r = cs_put(conn, buf_out, len_out);
+    if (auth)
+    {
+        if (auth->which == Z_IdAuthentication_open)
+        {
+            char **darray;
+            int num;
+            nmem_strsplit(out->mem, "/", auth->u.open, &darray, &num);
+            if (num >= 1)
+                username = darray[0];
+            if (num >= 2)
+                password = darray[1];
+        }
+        else if (auth->which == Z_IdAuthentication_idPass)
+        {
+            username = auth->u.idPass->userId;
+            password = auth->u.idPass->password;
+        }
+    }
 
-        if (r >= 0)
-            return 2;
+    if (username && password)
+    {
+        z_HTTP_header_add_basic_auth(out, &gdu->u.HTTP_Request->headers,
+                                     username, password);
     }
-    return 0;
+
+    return send_gdu(gdu);
 }
 #endif
 
@@ -2783,36 +2846,50 @@ static int cmd_setnames(const char *arg)
 
 /* PRESENT SERVICE ----------------------------- */
 
-static void parse_show_args(const char *arg_c, char *setstring,
-                            Odr_int *start, Odr_int *number)
+static int parse_show_args(const char *arg_c, char *setstring,
+                           Odr_int *start, Odr_int *number)
 {
-    char arg[40];
-    char *p;
+    char *end_ptr;
 
-    strncpy(arg, arg_c, sizeof(arg)-1);
-    arg[sizeof(arg)-1] = '\0';
+    if (setnumber >= 0)
+        sprintf(setstring, "%d", setnumber);
+    else
+        *setstring = '\0';
 
-    if ((p = strchr(arg, '+')))
+    if (!strcmp(arg_c, "all"))
     {
-        *number = odr_atoi(p + 1);
-        *p = '\0';
+        *number = last_hit_count;
+        *start = 1;
     }
-    if (*arg)
+    *start = odr_strtol(arg_c, &end_ptr, 10);
+    if (end_ptr == arg_c || *end_ptr == '\0')
+        return 1;
+    while (isspace(*(unsigned char *)end_ptr))
+        end_ptr++;
+    if (*end_ptr != '+')
     {
-        if (!strcmp(arg, "all"))
-        {
-            *number = last_hit_count;
-            *start = 1;
-        }
-        else
-            *start = odr_atoi(arg);
+        printf("Bad show arg: expected +. Got %s\n", end_ptr);
+        return 0;
     }
-    if (p && (p=strchr(p+1, '+')))
-        strcpy(setstring, p+1);
-    else if (setnumber >= 0)
-        sprintf(setstring, "%d", setnumber);
-    else
-        *setstring = '\0';
+    end_ptr++;
+    arg_c = end_ptr;
+    *number = odr_strtol(arg_c, &end_ptr, 10);
+    if (end_ptr == arg_c)
+    {
+        printf("Bad show arg: expected number after +\n");
+        return 0;
+    }
+    if (*end_ptr == '\0')
+        return 1;
+    while (isspace(*(unsigned char *)end_ptr))
+        end_ptr++;
+    if (*end_ptr != '+')
+    {
+        printf("Bad show arg: + expected. Got %s\n", end_ptr);
+        return 0;
+    }
+    strcpy(setstring, end_ptr+1);
+    return 1;
 }
 
 static int send_presentRequest(const char *arg)
@@ -2825,7 +2902,8 @@ static int send_presentRequest(const char *arg)
 
     req->referenceId = set_refid(out);
 
-    parse_show_args(arg, setstring, &setno, &nos);
+    if (!parse_show_args(arg, setstring, &setno, &nos))
+        return 0;
     if (*setstring)
         req->resultSetId = setstring;
 
@@ -2916,7 +2994,8 @@ static int send_SRW_presentRequest(const char *arg)
 
     if (!sr)
         return 0;
-    parse_show_args(arg, setstring, &setno, &nos);
+    if (!parse_show_args(arg, setstring, &setno, &nos))
+        return 0;
     sr->u.request->startRecord = odr_intdup(out, setno);
     sr->u.request->maximumRecords = odr_intdup(out, nos);
     if (record_schema)
@@ -4208,9 +4287,12 @@ static void http_response(Z_HTTP_Response *hres)
 }
 #endif
 
+#define max_HTTP_redirects 2
+
 static void wait_and_handle_response(int one_response_only)
 {
     int reconnect_ok = 1;
+    int no_redirects = 0;
     int res;
     char *netbuffer= 0;
     int netbufferlen = 0;
@@ -4339,7 +4421,26 @@ static void wait_and_handle_response(int one_response_only)
 #if YAZ_HAVE_XML2
         else if (gdu->which == Z_GDU_HTTP_Response)
         {
-            http_response(gdu->u.HTTP_Response);
+            Z_HTTP_Response *hres = gdu->u.HTTP_Response;
+            int code = hres->code;
+            const char *location = 0;
+            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);
+                no_redirects++;
+                if (conn)
+                {
+                    if (send_SRW_redirect(location, hres) == 2)
+                        continue;
+                }
+                printf("Redirect failed\n");
+            }
+            else
+                http_response(gdu->u.HTTP_Response);
         }
 #endif
         if (one_response_only)