No unsigned char's in public API (except for iconv)
[yaz-moved-to-github.git] / src / http.c
index 43e2786..e4c3001 100644 (file)
 #include <yaz/zgdu.h>
 #include <yaz/base64.h>
 
-#ifdef WIN32
-#define strncasecmp _strnicmp
-#define strcasecmp _stricmp
-#endif
-
 static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers,
                                   char **content_buf, int *content_len)
 {
@@ -64,9 +59,9 @@ static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers,
         memcpy ((*headers)->value, o->buf + po, i - po);
         (*headers)->value[i - po] = '\0';
 
-        if (!strcasecmp((*headers)->name, "Transfer-Encoding")
+        if (!yaz_strcasecmp((*headers)->name, "Transfer-Encoding")
             &&
-            !strcasecmp((*headers)->value, "chunked"))
+            !yaz_strcasecmp((*headers)->value, "chunked"))
             chunked = 1;
         headers = &(*headers)->next;
         if (i < o->size-1 && o->buf[i] == '\r')
@@ -191,7 +186,7 @@ void z_HTTP_header_add_basic_auth(ODR o, Z_HTTP_Header **hp,
     buf = (char *) odr_malloc(o, (len+1) * 8/6 + 12);
     strcpy(buf, "Basic ");
     yaz_base64encode(tmp, &buf[strlen(buf)]);
-    z_HTTP_header_add(o, hp, "Authorization", buf);
+    z_HTTP_header_set(o, hp, "Authorization", buf);
 }
 
 
@@ -206,14 +201,12 @@ void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n,
     (*hp)->next = 0;
 }
 
-#if 0
-/* not in use yet */
-static void z_HTTP_header_set(ODR o, Z_HTTP_Header **hp, const char *n,
+void z_HTTP_header_set(ODR o, Z_HTTP_Header **hp, const char *n,
                        const char *v)
 {
     while (*hp)
     {
-        if (!strcmp((*hp)->name, n))
+        if (!yaz_strcasecmp((*hp)->name, n))
         {
             (*hp)->value = odr_strdup(o, v);
             return;
@@ -225,12 +218,26 @@ static void z_HTTP_header_set(ODR o, Z_HTTP_Header **hp, const char *n,
     (*hp)->value = odr_strdup(o, v);
     (*hp)->next = 0;
 }
-#endif
+
+const char *z_HTTP_header_remove(Z_HTTP_Header **hp, const char *n)
+{
+    while (*hp)
+    {
+        if (!yaz_strcasecmp((*hp)->name, n))
+        {
+            const char *v = (*hp)->value;
+            *hp = (*hp)->next;
+            return v;
+        }
+        hp = &(*hp)->next;
+    }
+    return 0;
+}
 
 const char *z_HTTP_header_lookup(const Z_HTTP_Header *hp, const char *n)
 {
     for (; hp; hp = hp->next)
-        if (!yaz_matchstr(hp->name, n))
+        if (!yaz_strcasecmp(hp->name, n))
             return hp->value;
     return 0;
 }
@@ -331,7 +338,7 @@ Z_GDU *z_get_HTTP_Request_uri(ODR odr, const char *uri, const char *args,
     return p;
 }
 
-Z_GDU *z_get_HTTP_Response(ODR o, int code)
+Z_GDU *z_get_HTTP_Response_details(ODR o, int code, const char *details)
 {
     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
     Z_HTTP_Response *hres;
@@ -348,7 +355,10 @@ Z_GDU *z_get_HTTP_Response(ODR o, int code)
                       "YAZ/" YAZ_VERSION);
     if (code != 200)
     {
-        hres->content_buf = (char*) odr_malloc(o, 400);
+        const char *http_err = z_HTTP_errmsg(code);
+        size_t sz = 400 + strlen(http_err) + (details ?
+                                              strlen(details) : 0);
+        hres->content_buf = (char*) odr_malloc(o, sz);
         sprintf(hres->content_buf,
                 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
                 " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
@@ -360,30 +370,107 @@ Z_GDU *z_get_HTTP_Response(ODR o, int code)
                 "  <P><A HREF=\"http://www.indexdata.com/yaz/\">YAZ</A> "
                 YAZ_VERSION "</P>\n"
                 "  <P>Error: %d</P>\n"
-                "  <P>Description: %.50s</P>\n"
+                "  <P>Description: %s</P>\n", code, http_err);
+        if (details)
+        {
+            sprintf(hres->content_buf + strlen(hres->content_buf),
+                    "<P>Details: %s</P>\n", details);
+        }
+        sprintf(hres->content_buf + strlen(hres->content_buf),
                 " </BODY>\n"
-                "</HTML>\n",
-                code, z_HTTP_errmsg(code));
+                "</HTML>\n");
         hres->content_len = strlen(hres->content_buf);
         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
     }
     return p;
 }
 
+Z_GDU *z_get_HTTP_Response(ODR o, int code)
+{
+    return z_get_HTTP_Response_details(o, code, 0);
+}
+
 const char *z_HTTP_errmsg(int code)
 {
-    if (code == 200)
+    switch (code)
+    {
+    case 100:
+        return "Continue";
+    case 101:
+        return "Switching Protocols";
+    case 200:
         return "OK";
-    else if (code == 400)
+    case 201:
+        return "Created";
+    case 202:
+        return "Accepted";
+    case 203:
+        return "Non-Authoritative Information";
+    case 204:
+        return "No Content";
+    case 205:
+        return "Reset Content";
+    case 206:
+        return "Partial Content";
+    case 300:
+        return "Multiple Choices";
+    case 301:
+        return "Moved Permenently";
+    case 302:
+        return "Found";
+    case 303:
+        return "See Other";
+    case 304:
+        return "Not Modified";
+    case 305:
+        return "Use Proxy";
+    case 307:
+        return "Temporary Redirect";
+    case 400:
         return "Bad Request";
-    else if (code == 404)
+    case 404:
         return "Not Found";
-    else if (code == 405)
+    case 405:
         return "Method Not Allowed";
-    else if (code == 500)
+    case 406:
+        return "Not Acceptable";
+    case 407:
+        return "Proxy Authentication Required";
+    case 408:
+        return "Request Timeout";
+    case 409:
+        return "Conflict";
+    case 410:
+        return "Gone";
+    case 411:
+        return "Length Required";
+    case 412:
+        return "Precondition Failed";
+    case 413:
+        return "Request Entity Too Large";
+    case 414:
+        return "Request-URI Too Long";
+    case 415:
+        return "Unsupported Media Type";
+    case 416:
+        return "Requested Range Not Satisfiable";
+    case 417:
+        return "Expectation Failed";
+    case 500:
         return "Internal Error";
-    else
+    case 501:
+        return "Not Implemented";
+    case 502:
+        return "Bad Gateway";
+    case 503:
+        return "Service Unavailable";
+    case 504:
+        return "Gateway Timeout";
+    case 505:
+        return "HTTP Version Not Supported";
+    default:
         return "Unknown Error";
+    }
 }
 
 int yaz_decode_http_response(ODR o, Z_HTTP_Response **hr_p)
@@ -509,23 +596,21 @@ int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr)
             hr->code,
             z_HTTP_errmsg(hr->code));
     odr_write2(o, sbuf, strlen(sbuf));
-    /* apply Content-Length if not already applied */
-    if (!z_HTTP_header_lookup(hr->headers,
-                              "Content-Length"))
-    {
-        char lstr[60];
-        sprintf(lstr, "Content-Length: %d\r\n",
-                hr->content_len);
-        odr_write2(o, lstr, strlen(lstr));
-    }
+    /* use content_len for Content-Length */
+    sprintf(sbuf, "Content-Length: %d\r\n", hr->content_len);
+    odr_write2(o, sbuf, strlen(sbuf));
     for (h = hr->headers; h; h = h->next)
     {
-        odr_write2(o, h->name, strlen(h->name));
-        odr_write2(o, ": ", 2);
-        odr_write2(o, h->value, strlen(h->value));
-        odr_write2(o, "\r\n", 2);
+        if (yaz_strcasecmp(h->name, "Content-Length")
+            && yaz_strcasecmp(h->name, "Transfer-Encoding"))
+        {   /* skip Content-Length if given. content_len rules */
+            odr_write2(o, h->name, strlen(h->name));
+            odr_write2(o, ": ", 2);
+            odr_write2(o, h->value, strlen(h->value));
+            odr_write2(o, "\r\n", 2);
+        }
     }
-    odr_write(o, (unsigned char *) "\r\n", 2);
+    odr_write2(o, "\r\n", 2);
     if (hr->content_buf)
         odr_write2(o, hr->content_buf, hr->content_len);
     if (o->direction == ODR_PRINT)