Towards 2.1.44. Bump copyright year.
[yaz-moved-to-github.git] / src / comstack.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: comstack.c,v 1.17 2007-01-03 08:42:15 adam Exp $
6  */
7
8 /** 
9  * \file comstack.c
10  * \brief Implements Generic COMSTACK functions
11  */
12
13 #include <string.h>
14 #include <ctype.h>
15 #include <errno.h>
16
17 #include <yaz/log.h>
18 #include <yaz/comstack.h>
19 #include <yaz/tcpip.h>
20 #include <yaz/unix.h>
21 #include <yaz/odr.h>
22
23 #ifdef WIN32
24 #define strncasecmp _strnicmp
25 #endif
26
27 static const char *cs_errlist[] =
28 {
29     "No error or unspecified error",
30     "System (lower-layer) error",
31     "Operation out of state",
32     "No data (operation would block)",
33     "New data while half of old buffer is on the line (flow control)",
34     "Permission denied",
35     "SSL error",
36     "Too large incoming buffer"
37 };
38
39 const char *cs_errmsg(int n)
40 {
41     static char buf[250];
42
43     if (n < CSNONE || n > CSLASTERROR) {
44         sprintf(buf, "unknown comstack error %d", n);
45         return buf;
46     }
47     if (n == CSYSERR) {
48         sprintf(buf, "%s: %s", cs_errlist[n], strerror(errno));
49         return buf;
50     }
51     return cs_errlist[n];
52 }
53
54 const char *cs_strerror(COMSTACK h)
55 {
56     return cs_errmsg(h->cerrno);
57 }
58
59 void cs_get_host_args(const char *type_and_host, const char **args)
60 {
61     
62     *args = "";
63     if (*type_and_host && strncmp(type_and_host, "unix:", 5))
64     {
65         const char *cp;
66         cp = strstr(type_and_host, "://");
67         if (cp)
68             cp = cp+3;
69         else
70             cp = type_and_host;
71         cp = strchr(cp, '/');
72         if (cp)
73             *args = cp+1;
74     }
75 }
76
77 COMSTACK cs_create_host(const char *type_and_host, int blocking, void **vp)
78 {
79     enum oid_proto proto = PROTO_Z3950;
80     const char *host = 0;
81     COMSTACK cs;
82     CS_TYPE t;
83
84     if (strncmp (type_and_host, "tcp:", 4) == 0)
85     {
86         t = tcpip_type;
87         host = type_and_host + 4;
88     }
89     else if (strncmp (type_and_host, "ssl:", 4) == 0)
90     {
91 #if HAVE_OPENSSL_SSL_H
92         t = ssl_type;
93         host = type_and_host + 4;
94 #else
95         return 0;
96 #endif
97     }
98     else if (strncmp (type_and_host, "unix:", 5) == 0)
99     {
100 #ifndef WIN32
101         t = unix_type;
102         host = type_and_host + 5;
103 #else
104         return 0;
105 #endif
106     }
107     else if (strncmp(type_and_host, "http:", 5) == 0)
108     {
109         t = tcpip_type;
110         host = type_and_host + 5;
111         while (host[0] == '/')
112             host++;
113         proto = PROTO_HTTP;
114     }
115     else if (strncmp(type_and_host, "https:", 6) == 0)
116     {
117 #if HAVE_OPENSSL_SSL_H
118         t = ssl_type;
119         host = type_and_host + 6;
120         while (host[0] == '/')
121             host++;
122         proto = PROTO_HTTP;
123 #else
124         return 0;
125 #endif
126     }
127     else
128     {
129         t = tcpip_type;
130         host = type_and_host;
131     }
132     cs = cs_create (t, blocking, proto);
133     if (!cs)
134         return 0;
135
136     if (!(*vp = cs_straddr(cs, host)))
137     {
138         cs_close (cs);
139         return 0;
140     }    
141     return cs;
142 }
143
144 int cs_look (COMSTACK cs)
145 {
146     return cs->event;
147 }
148
149 #define CHUNK_DEBUG 0
150 int cs_complete_auto(const unsigned char *buf, int len)
151 {
152     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
153                 && buf[1] >= 0x20 && buf[1] < 0x7f
154                 && buf[2] >= 0x20 && buf[2] < 0x7f)
155     {
156         /* deal with HTTP request/response */
157         int i = 2, content_len = 0, chunked = 0;
158
159         /* if dealing with HTTP responses - then default
160            content length is unlimited (socket close) */
161         if (!memcmp(buf, "HTTP/", 5))
162             content_len = -1; 
163
164         while (i <= len-4)
165         {
166             if (i > 8192)
167             {
168                 return i;  /* do not allow more than 8K HTTP header */
169             }
170             if (buf[i] == '\r' && buf[i+1] == '\n')
171             {
172                 i += 2;
173                 if (buf[i] == '\r' && buf[i+1] == '\n')
174                 {
175                     if (chunked)
176                     { 
177                         /* inside chunked body .. */
178                         while(1)
179                         {
180                             int j, chunk_len = 0;
181                             i += 2;
182 #if CHUNK_DEBUG
183 /* debugging */
184                             if (i <len-2)
185                             {
186                                 printf ("\n<<<");
187                                 int j;
188                                 for (j = i; j <= i+4; j++)
189                                     printf ("%c", buf[j]);
190                                 printf (">>>\n");
191                             }
192 #endif
193                             /* read chunk length */
194                             while (1)
195                                 if (i >= len-2) {
196 #if CHUNK_DEBUG
197 /* debugging */                                    
198                                     printf ("XXXXXXXX not there yet 1\n");
199                                     printf ("i=%d len=%d\n", i, len);
200 #endif
201                                     return 0;
202                                 } else if (isdigit(buf[i]))
203                                     chunk_len = chunk_len * 16 + 
204                                         (buf[i++] - '0');
205                                 else if (isupper(buf[i]))
206                                     chunk_len = chunk_len * 16 + 
207                                         (buf[i++] - ('A'-10));
208                                 else if (islower(buf[i]))
209                                     chunk_len = chunk_len * 16 + 
210                                         (buf[i++] - ('a'-10));
211                                 else
212                                     break;
213                             /* move forward until CRLF - skip chunk ext */
214                             j = 0;
215                             while (buf[i] != '\r' && buf[i+1] != '\n')
216                             {
217                                 if (i >= len-2)
218                                     return 0;   /* need more buffer .. */
219                                 if (++j > 1000)
220                                     return i; /* enough.. stop */
221                                 i++;
222                             }
223                             /* got CRLF */
224 #if CHUNK_DEBUG
225                             printf ("XXXXXX chunk_len=%d\n", chunk_len);
226 #endif                      
227                             if (chunk_len < 0)
228                                 return i+2;    /* bad chunk_len */
229                             if (chunk_len == 0)
230                                 break;
231                             i += chunk_len+2;
232                         }
233                         /* consider trailing headers .. */
234                         while(i <= len-4)
235                         {
236                             if (buf[i] == '\r' &&  buf[i+1] == '\n' &&
237                                 buf[i+2] == '\r' && buf[i+3] == '\n')
238                                 if (len >= i+4)
239                                     return i+4;
240                             i++;
241                         }
242 #if CHUNK_DEBUG
243 /* debugging */
244                         printf ("XXXXXXXXX not there yet 2\n");
245                         printf ("i=%d len=%d\n", i, len);
246 #endif
247                         return 0;
248                     }
249                     else
250                     {   /* not chunked ; inside body */
251                         /* i += 2 seems not to work with GCC -O2 .. 
252                            so i+2 is used instead .. */
253                         if (content_len == -1)
254                             return 0;   /* no content length */
255                         else if (len >= (i+2)+ content_len)
256                         {
257                             return (i+2)+ content_len;
258                         }
259                     }
260                     break;
261                 }
262                 else if (i < len - 20 && 
263                          !strncasecmp((const char *) buf+i, "Transfer-Encoding:", 18))
264                 {
265                     i+=18;
266                     while (buf[i] == ' ')
267                         i++;
268                     if (i < len - 8)
269                         if (!strncasecmp((const char *) buf+i, "chunked", 7))
270                             chunked = 1;
271                 }
272                 else if (i < len - 17 &&
273                          !strncasecmp((const char *)buf+i, "Content-Length:", 15))
274                 {
275                     i+= 15;
276                     while (buf[i] == ' ')
277                         i++;
278                     content_len = 0;
279                     while (i <= len-4 && isdigit(buf[i]))
280                         content_len = content_len*10 + (buf[i++] - '0');
281                     if (content_len < 0) /* prevent negative offsets */
282                         content_len = 0;
283                 }
284                 else
285                     i++;
286             }
287             else
288                 i++;
289         }
290         return 0;
291     }
292     return completeBER(buf, len);
293 }
294
295 void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
296 {
297     cs->max_recv_bytes = max_recv_bytes;
298 }
299
300 /*
301  * Local variables:
302  * c-basic-offset: 4
303  * indent-tabs-mode: nil
304  * End:
305  * vim: shiftwidth=4 tabstop=8 expandtab
306  */
307