Update source headers for 2008. Omit CVS ID keyword subst.
[yaz-moved-to-github.git] / src / comstack.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2008 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /** 
7  * \file comstack.c
8  * \brief Implements Generic COMSTACK functions
9  */
10
11 #include <string.h>
12 #include <ctype.h>
13 #include <errno.h>
14
15 #include <yaz/log.h>
16 #include <yaz/comstack.h>
17 #include <yaz/tcpip.h>
18 #include <yaz/unix.h>
19 #include <yaz/odr.h>
20
21 #ifdef WIN32
22 #define strncasecmp _strnicmp
23 #endif
24
25 static const char *cs_errlist[] =
26 {
27     "No error or unspecified error",
28     "System (lower-layer) error",
29     "Operation out of state",
30     "No data (operation would block)",
31     "New data while half of old buffer is on the line (flow control)",
32     "Permission denied",
33     "SSL error",
34     "Too large incoming buffer"
35 };
36
37 const char *cs_errmsg(int n)
38 {
39     static char buf[250];
40
41     if (n < CSNONE || n > CSLASTERROR) {
42         sprintf(buf, "unknown comstack error %d", n);
43         return buf;
44     }
45     if (n == CSYSERR) {
46         sprintf(buf, "%s: %s", cs_errlist[n], strerror(errno));
47         return buf;
48     }
49     return cs_errlist[n];
50 }
51
52 const char *cs_strerror(COMSTACK h)
53 {
54     return cs_errmsg(h->cerrno);
55 }
56
57 void cs_get_host_args(const char *type_and_host, const char **args)
58 {
59     
60     *args = "";
61     if (*type_and_host && strncmp(type_and_host, "unix:", 5))
62     {
63         const char *cp;
64         cp = strstr(type_and_host, "://");
65         if (cp)
66             cp = cp+3;
67         else
68             cp = type_and_host;
69         cp = strchr(cp, '/');
70         if (cp)
71             *args = cp+1;
72     }
73 }
74
75 static int cs_parse_host(const char *uri, const char **host,
76                          CS_TYPE *t, enum oid_proto *proto,
77                          char **connect_host)
78 {
79     *connect_host = 0;
80     if (strncmp(uri, "connect:", 8) == 0)
81     {
82         const char *cp = strchr(uri, ',');
83         if (cp)
84         {
85             size_t len = cp - (uri + 8);
86             *connect_host = (char *) xmalloc(len+1);
87             memcpy(*connect_host, uri + 8, len);
88             (*connect_host)[len] = '\0';
89             uri = cp+1;
90         }
91     }
92
93     if (strncmp (uri, "tcp:", 4) == 0)
94     {
95         *t = tcpip_type;
96         *host = uri + 4;
97         *proto = PROTO_Z3950;
98     }
99     else if (strncmp (uri, "ssl:", 4) == 0)
100     {
101 #if HAVE_OPENSSL_SSL_H
102         *t = ssl_type;
103         *host = uri + 4;
104         *proto = PROTO_Z3950;
105 #else
106         return 0;
107 #endif
108     }
109     else if (strncmp (uri, "unix:", 5) == 0)
110     {
111 #ifndef WIN32
112         *t = unix_type;
113         *host = uri + 5;
114         *proto = PROTO_Z3950;
115 #else
116         return 0;
117 #endif
118     }
119     else if (strncmp(uri, "http:", 5) == 0)
120     {
121         *t = tcpip_type;
122         *host = uri + 5;
123         while (**host == '/')
124             (*host)++;
125         *proto = PROTO_HTTP;
126     }
127     else if (strncmp(uri, "https:", 6) == 0)
128     {
129 #if HAVE_OPENSSL_SSL_H
130         *t = ssl_type;
131         *host = uri + 6;
132         while (**host == '/')
133             (*host)++;
134         *proto = PROTO_HTTP;
135 #else
136         return 0;
137 #endif
138     }
139     else
140     {
141         *proto = PROTO_Z3950;
142         *t = tcpip_type;
143         *host = uri;
144     }
145     return 1;
146 }
147
148 COMSTACK cs_create_host(const char *vhost, int blocking, void **vp)
149 {
150     enum oid_proto proto = PROTO_Z3950;
151     const char *host = 0;
152     COMSTACK cs;
153     CS_TYPE t;
154     char *connect_host = 0;
155
156     cs_parse_host(vhost, &host, &t, &proto, &connect_host);
157
158     if (t == tcpip_type)
159     {
160         cs = yaz_tcpip_create(-1, blocking, proto, connect_host ? host : 0);
161     }
162     else
163     {
164         cs = cs_create(t, blocking, proto);
165     }
166     if (cs)
167     {
168         if (!(*vp = cs_straddr(cs, connect_host ? connect_host : host)))
169         {
170             cs_close (cs);
171             cs = 0;
172         }    
173     }
174     xfree(connect_host);
175     return cs;
176 }
177
178 int cs_look (COMSTACK cs)
179 {
180     return cs->event;
181 }
182
183 static int skip_crlf(const char *buf, int len, int *i)
184 {
185     if (*i < len)
186     {
187         if (buf[*i] == '\r' && *i < len-1 && buf[*i + 1] == '\n')
188         {
189             (*i) += 2;
190             return 1;
191         }
192         else if (buf[*i] == '\n')
193         {
194             (*i)++;
195             return 1;
196         }
197     }
198     return 0;
199 }
200
201 #define CHUNK_DEBUG 0
202
203 static int cs_complete_http(const char *buf, int len, int head_only)
204 {
205     /* deal with HTTP request/response */
206     int i = 2, content_len = 0, chunked = 0;
207
208     if (len < 6)
209         return 0;
210
211     /* if dealing with HTTP responses - then default
212        content length is unlimited (socket close) */
213     if (!head_only && !memcmp(buf, "HTTP/", 5))
214         content_len = -1; 
215
216 #if 0
217     printf("len = %d\n", len);
218     fwrite (buf, 1, len, stdout);
219     printf("----------\n");
220 #endif
221     while (i <= len-2)
222     {
223         if (i > 8192)
224         {
225             return i;  /* do not allow more than 8K HTTP header */
226         }
227         if (skip_crlf(buf, len, &i))
228         {
229             if (skip_crlf(buf, len, &i))
230             {
231                 /* inside content */
232                 if (chunked)
233                 { 
234                     /* inside chunked body .. */
235                     while(1)
236                     {
237                         int chunk_len = 0;
238 #if CHUNK_DEBUG
239                         if (i < len-2)
240                         {
241                             printf ("\n<<<");
242                             int j;
243                             for (j = i; j <= i+3; j++)
244                                 printf ("%c", buf[j]);
245                             printf (">>>\n");
246                         }
247 #endif
248                         /* read chunk length */
249                         while (1)
250                             if (i >= len-2) {
251 #if CHUNK_DEBUG
252                                 printf ("returning incomplete read at 1\n");
253                                 printf ("i=%d len=%d\n", i, len);
254 #endif
255                                 return 0;
256                             } else if (isdigit(buf[i]))
257                                 chunk_len = chunk_len * 16 + 
258                                     (buf[i++] - '0');
259                             else if (isupper(buf[i]))
260                                 chunk_len = chunk_len * 16 + 
261                                     (buf[i++] - ('A'-10));
262                             else if (islower(buf[i]))
263                                 chunk_len = chunk_len * 16 + 
264                                     (buf[i++] - ('a'-10));
265                             else
266                                 break;
267                         if (chunk_len == 0)
268                             break;
269                         if (chunk_len < 0)
270                             return i;
271                         
272                         while (1)
273                         {
274                             if (i >= len -1)
275                                 return 0;
276                             if (skip_crlf(buf, len, &i))
277                                 break;
278                             i++;
279                         }
280                         /* got CRLF */
281 #if CHUNK_DEBUG
282                         printf ("chunk_len=%d\n", chunk_len);
283 #endif                      
284                         i += chunk_len;
285                         if (i >= len-2)
286                             return 0;
287                         if (!skip_crlf(buf, len, &i))
288                             return 0;
289                     }
290                     /* consider trailing headers .. */
291                     while (i < len)
292                     {
293                         if (skip_crlf(buf, len, &i))
294                         {
295                             if (skip_crlf(buf, len, &i))
296                                 return i;
297                         }
298                         else
299                             i++;
300                     }
301 #if CHUNK_DEBUG
302                     printf ("returning incomplete read at 2\n");
303                     printf ("i=%d len=%d\n", i, len);
304 #endif
305                     return 0;
306                 }
307                 else
308                 {   /* not chunked ; inside body */
309                     if (content_len == -1)
310                         return 0;   /* no content length */
311                     else if (len >= i + content_len)
312                     {
313                         return i + content_len;
314                     }
315                 }
316                 break;
317             }
318             else if (i < len - 20 && 
319                      !strncasecmp((const char *) buf+i, "Transfer-Encoding:", 18))
320             {
321                 i+=18;
322                 while (buf[i] == ' ')
323                     i++;
324                 if (i < len - 8)
325                     if (!strncasecmp((const char *) buf+i, "chunked", 7))
326                         chunked = 1;
327             }
328             else if (i < len - 17 &&
329                      !strncasecmp((const char *)buf+i, "Content-Length:", 15))
330             {
331                 i+= 15;
332                 while (buf[i] == ' ')
333                     i++;
334                 content_len = 0;
335                 while (i <= len-4 && isdigit(buf[i]))
336                     content_len = content_len*10 + (buf[i++] - '0');
337                 if (content_len < 0) /* prevent negative offsets */
338                     content_len = 0;
339             }
340             else
341                 i++;
342         }
343         else
344             i++;
345     }
346     return 0;
347 }
348
349 static int cs_complete_auto_x(const char *buf, int len, int head_only)
350 {
351     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
352                 && buf[1] >= 0x20 && buf[1] < 0x7f
353                 && buf[2] >= 0x20 && buf[2] < 0x7f)
354     {
355         int r = cs_complete_http(buf, len, head_only);
356         return r;
357     }
358     return completeBER((const unsigned char *) buf, len);
359 }
360
361
362 int cs_complete_auto(const char *buf, int len)
363 {
364     return cs_complete_auto_x(buf, len, 0);
365 }
366
367 int cs_complete_auto_head(const char *buf, int len)
368 {
369     return cs_complete_auto_x(buf, len, 1);
370 }
371
372 void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
373 {
374     cs->max_recv_bytes = max_recv_bytes;
375 }
376
377 /*
378  * Local variables:
379  * c-basic-offset: 4
380  * indent-tabs-mode: nil
381  * End:
382  * vim: shiftwidth=4 tabstop=8 expandtab
383  */
384