Moved more members of public struct odr (ODR*) to struct Odr_private.
[yaz-moved-to-github.git] / src / comstack.c
index 4d842f7..db3b002 100644 (file)
@@ -1,18 +1,29 @@
 /*
 /*
- * Copyright (c) 1995-2003, Index Data
+ * Copyright (C) 1995-2007, Index Data ApS
  * See the file LICENSE for details.
  *
  * See the file LICENSE for details.
  *
- * $Id: comstack.c,v 1.1 2003-10-27 12:21:30 adam Exp $
+ * $Id: comstack.c,v 1.18 2007-01-11 10:30:41 adam Exp $
+ */
+
+/** 
+ * \file comstack.c
+ * \brief Implements Generic COMSTACK functions
  */
 
 #include <string.h>
 #include <ctype.h>
  */
 
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
 
 
+#include <yaz/log.h>
 #include <yaz/comstack.h>
 #include <yaz/tcpip.h>
 #include <yaz/unix.h>
 #include <yaz/odr.h>
 
 #include <yaz/comstack.h>
 #include <yaz/tcpip.h>
 #include <yaz/unix.h>
 #include <yaz/odr.h>
 
+#ifdef WIN32
+#define strncasecmp _strnicmp
+#endif
+
 static const char *cs_errlist[] =
 {
     "No error or unspecified error",
 static const char *cs_errlist[] =
 {
     "No error or unspecified error",
@@ -21,13 +32,22 @@ static const char *cs_errlist[] =
     "No data (operation would block)",
     "New data while half of old buffer is on the line (flow control)",
     "Permission denied",
     "No data (operation would block)",
     "New data while half of old buffer is on the line (flow control)",
     "Permission denied",
-    "SSL error"
+    "SSL error",
+    "Too large incoming buffer"
 };
 
 const char *cs_errmsg(int n)
 {
 };
 
 const char *cs_errmsg(int n)
 {
-    if (n < 0 || n > 6)
-       n = 0;
+    static char buf[250];
+
+    if (n < CSNONE || n > CSLASTERROR) {
+        sprintf(buf, "unknown comstack error %d", n);
+        return buf;
+    }
+    if (n == CSYSERR) {
+        sprintf(buf, "%s: %s", cs_errlist[n], strerror(errno));
+        return buf;
+    }
     return cs_errlist[n];
 }
 
     return cs_errlist[n];
 }
 
@@ -63,61 +83,60 @@ COMSTACK cs_create_host(const char *type_and_host, int blocking, void **vp)
 
     if (strncmp (type_and_host, "tcp:", 4) == 0)
     {
 
     if (strncmp (type_and_host, "tcp:", 4) == 0)
     {
-       t = tcpip_type;
+        t = tcpip_type;
         host = type_and_host + 4;
     }
     else if (strncmp (type_and_host, "ssl:", 4) == 0)
     {
 #if HAVE_OPENSSL_SSL_H
         host = type_and_host + 4;
     }
     else if (strncmp (type_and_host, "ssl:", 4) == 0)
     {
 #if HAVE_OPENSSL_SSL_H
-       t = ssl_type;
+        t = ssl_type;
         host = type_and_host + 4;
 #else
         host = type_and_host + 4;
 #else
-       return 0;
+        return 0;
 #endif
     }
     else if (strncmp (type_and_host, "unix:", 5) == 0)
     {
 #ifndef WIN32
 #endif
     }
     else if (strncmp (type_and_host, "unix:", 5) == 0)
     {
 #ifndef WIN32
-       t = unix_type;
+        t = unix_type;
         host = type_and_host + 5;
 #else
         host = type_and_host + 5;
 #else
-       return 0;
+        return 0;
 #endif
     }
     else if (strncmp(type_and_host, "http:", 5) == 0)
     {
 #endif
     }
     else if (strncmp(type_and_host, "http:", 5) == 0)
     {
-       t = tcpip_type;
+        t = tcpip_type;
         host = type_and_host + 5;
         host = type_and_host + 5;
-        if (host[0] == '/' && host[1] == '/')
-            host = host + 2;
+        while (host[0] == '/')
+            host++;
         proto = PROTO_HTTP;
     }
     else if (strncmp(type_and_host, "https:", 6) == 0)
     {
 #if HAVE_OPENSSL_SSL_H
         proto = PROTO_HTTP;
     }
     else if (strncmp(type_and_host, "https:", 6) == 0)
     {
 #if HAVE_OPENSSL_SSL_H
-       t = ssl_type;
+        t = ssl_type;
         host = type_and_host + 6;
         host = type_and_host + 6;
-        if (host[0] == '/' && host[1] == '/')
-            host = host + 2;
+        while (host[0] == '/')
+            host++;
         proto = PROTO_HTTP;
 #else
         proto = PROTO_HTTP;
 #else
-       return 0;
+        return 0;
 #endif
     }
     else
     {
 #endif
     }
     else
     {
-       t = tcpip_type;
-       host = type_and_host;
-        
+        t = tcpip_type;
+        host = type_and_host;
     }
     cs = cs_create (t, blocking, proto);
     if (!cs)
     }
     cs = cs_create (t, blocking, proto);
     if (!cs)
-       return 0;
+        return 0;
 
     if (!(*vp = cs_straddr(cs, host)))
     {
 
     if (!(*vp = cs_straddr(cs, host)))
     {
-       cs_close (cs);
-       return 0;
+        cs_close (cs);
+        return 0;
     }    
     return cs;
 }
     }    
     return cs;
 }
@@ -127,121 +146,194 @@ int cs_look (COMSTACK cs)
     return cs->event;
 }
 
     return cs->event;
 }
 
-int cs_complete_auto(const unsigned char *buf, int len)
+static int skip_crlf(const char *buf, int len, int *i)
 {
 {
-    if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
-               && buf[1] >= 0x20 && buf[1] < 0x7f
-               && buf[2] >= 0x20 && buf[2] < 0x7f)
+    if (*i < len)
     {
     {
-        /* deal with HTTP request/response */
-       int i = 2, content_len = 0, chunked = 0;
+        if (buf[*i] == '\r' && *i < len-1 && buf[*i + 1] == '\n')
+        {
+            (*i) += 2;
+            return 1;
+        }
+        else if (buf[*i] == '\n')
+        {
+            (*i)++;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+#define CHUNK_DEBUG 0
+
+int cs_complete_http(const char *buf, int len)
+{
+    /* deal with HTTP request/response */
+    int i = 2, content_len = 0, chunked = 0;
+
+    if (len < 6)
+        return 0;
+
+    /* if dealing with HTTP responses - then default
+       content length is unlimited (socket close) */
+    if (!memcmp(buf, "HTTP/", 5))
+        content_len = -1; 
 
 
-        while (i <= len-4)
+#if 0
+    printf("len = %d\n", len);
+    fwrite (buf, 1, len, stdout);
+    printf("----------\n");
+#endif
+    while (i <= len-2)
+    {
+        if (i > 8192)
+        {
+            return i;  /* do not allow more than 8K HTTP header */
+        }
+        if (skip_crlf(buf, len, &i))
         {
         {
-           if (i > 8192)
-               return i;  /* do not allow more than 8K HTTP header */
-            if (buf[i] == '\r' && buf[i+1] == '\n')
+            if (skip_crlf(buf, len, &i))
             {
             {
-                i += 2;
-                if (buf[i] == '\r' && buf[i+1] == '\n')
-                {
-                    if (chunked)
-                    { 
-                        while(1)
+                /* inside content */
+                if (chunked)
+                { 
+                    /* inside chunked body .. */
+                    while(1)
+                    {
+                        int chunk_len = 0;
+#if CHUNK_DEBUG
+                        if (i < len-2)
                         {
                         {
-                            int chunk_len = 0;
-                            i += 2;
-#if 0
-/* debugging */
-                            if (i <len-2)
-                            {
-                                printf ("\n>>>");
-                                for (j = i; j <= i+4; j++)
-                                    printf ("%c", buf[j]);
-                                printf ("<<<\n");
-                            }
-#endif
-                            while (1)
-                                if (i >= len-2) {
-#if 0
-/* debugging */                                    
-                                    printf ("XXXXXXXX not there yet 1\n");
-                                    printf ("i=%d len=%d\n", i, len);
+                            printf ("\n<<<");
+                            int j;
+                            for (j = i; j <= i+3; j++)
+                                printf ("%c", buf[j]);
+                            printf (">>>\n");
+                        }
 #endif
 #endif
-                                    return 0;
-                                } else if (isdigit(buf[i]))
-                                    chunk_len = chunk_len * 16 + 
-                                        (buf[i++] - '0');
-                                else if (isupper(buf[i]))
-                                    chunk_len = chunk_len * 16 + 
-                                        (buf[i++] - ('A'-10));
-                                else if (islower(buf[i]))
-                                    chunk_len = chunk_len * 16 + 
-                                        (buf[i++] - ('a'-10));
-                                else
-                                    break;
-                            if (buf[i] != '\r' || buf[i+1] != '\n' ||
-                                chunk_len < 0)
-                                return i+2;    /* bad. stop now */
-                            if (chunk_len == 0)
-                            {
-                                /* consider trailing headers .. */
-                                while(i <= len-4)
-                                {
-                                    if (buf[i] == '\r' &&  buf[i+1] == '\n' &&
-                                        buf[i+2] == '\r' && buf[i+3] == '\n')
-                                        if (len >= i+4)
-                                            return i+4;
-                                    i++;
-                                }
-#if 0
-/* debugging */
-                                printf ("XXXXXXXXX not there yet 2\n");
+                        /* read chunk length */
+                        while (1)
+                            if (i >= len-2) {
+#if CHUNK_DEBUG
+                                printf ("returning incomplete read at 1\n");
                                 printf ("i=%d len=%d\n", i, len);
 #endif
                                 return 0;
                                 printf ("i=%d len=%d\n", i, len);
 #endif
                                 return 0;
-                            }
-                            i += chunk_len+2;
+                            } else if (isdigit(buf[i]))
+                                chunk_len = chunk_len * 16 + 
+                                    (buf[i++] - '0');
+                            else if (isupper(buf[i]))
+                                chunk_len = chunk_len * 16 + 
+                                    (buf[i++] - ('A'-10));
+                            else if (islower(buf[i]))
+                                chunk_len = chunk_len * 16 + 
+                                    (buf[i++] - ('a'-10));
+                            else
+                                break;
+                        if (chunk_len == 0)
+                            break;
+                        if (chunk_len < 0)
+                            return i;
+                        
+                        while (1)
+                        {
+                            if (i >= len -1)
+                                return 0;
+                            if (skip_crlf(buf, len, &i))
+                                break;
+                            i++;
                         }
                         }
+                        /* got CRLF */
+#if CHUNK_DEBUG
+                        printf ("chunk_len=%d\n", chunk_len);
+#endif                      
+                        i += chunk_len;
+                        if (i >= len-2)
+                            return 0;
+                        if (!skip_crlf(buf, len, &i))
+                            return 0;
                     }
                     }
-                    else
-                    {   /* not chunked ; inside body */
-                        /* i += 2 seems not to work with GCC -O2 .. 
-                           so i+2 is used instead .. */
-                        if (len >= (i+2)+ content_len)
-                            return (i+2)+ content_len;
+                    /* consider trailing headers .. */
+                    while (i < len)
+                    {
+                        if (skip_crlf(buf, len, &i))
+                        {
+                            if (skip_crlf(buf, len, &i))
+                                return i;
+                        }
+                        else
+                            i++;
                     }
                     }
-                    break;
-                }
-                else if (i < len - 21 &&
-                         !memcmp(buf+i, "Transfer-Encoding: ", 18))
-                {
-                    i+=18;
-                    if (buf[i] == ' ')
-                        i++;
-                    if (i < len - 8)
-                        if (!memcmp(buf+i, "chunked", 7))
-                            chunked = 1;
-                }
-                else if (i < len - 18 &&
-                         !memcmp(buf+i, "Content-Length: ", 15))
-                {
-                    i+= 15;
-                    if (buf[i] == ' ')
-                        i++;
-                    content_len = 0;
-                    while (i <= len-4 && isdigit(buf[i]))
-                        content_len = content_len*10 + (buf[i++] - '0');
-                    if (content_len < 0) /* prevent negative offsets */
-                        content_len = 0;
+#if CHUNK_DEBUG
+                    printf ("returning incomplete read at 2\n");
+                    printf ("i=%d len=%d\n", i, len);
+#endif
+                    return 0;
                 }
                 else
                 }
                 else
+                {   /* not chunked ; inside body */
+                    if (content_len == -1)
+                        return 0;   /* no content length */
+                    else if (len >= i + content_len)
+                    {
+                        return i + content_len;
+                    }
+                }
+                break;
+            }
+            else if (i < len - 20 && 
+                     !strncasecmp((const char *) buf+i, "Transfer-Encoding:", 18))
+            {
+                i+=18;
+                while (buf[i] == ' ')
+                    i++;
+                if (i < len - 8)
+                    if (!strncasecmp((const char *) buf+i, "chunked", 7))
+                        chunked = 1;
+            }
+            else if (i < len - 17 &&
+                     !strncasecmp((const char *)buf+i, "Content-Length:", 15))
+            {
+                i+= 15;
+                while (buf[i] == ' ')
                     i++;
                     i++;
+                content_len = 0;
+                while (i <= len-4 && isdigit(buf[i]))
+                    content_len = content_len*10 + (buf[i++] - '0');
+                if (content_len < 0) /* prevent negative offsets */
+                    content_len = 0;
             }
             else
                 i++;
         }
             }
             else
                 i++;
         }
-        return 0;
+        else
+            i++;
+    }
+    return 0;
+}
+
+int cs_complete_auto(const unsigned char *buf, int len)
+{
+    if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
+                && buf[1] >= 0x20 && buf[1] < 0x7f
+                && buf[2] >= 0x20 && buf[2] < 0x7f)
+    {
+        int r = cs_complete_http((const char *) buf, len);
+        return r;
     }
     return completeBER(buf, len);
 }
     }
     return completeBER(buf, len);
 }
+
+void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
+{
+    cs->max_recv_bytes = max_recv_bytes;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+