Adjustments to make YAZ compile as C++ code.
[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.22 2007-11-30 11:44:47 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 static int cs_parse_host(const char *uri, const char **host,
78                          CS_TYPE *t, enum oid_proto *proto,
79                          char **connect_host)
80 {
81     *connect_host = 0;
82     if (strncmp(uri, "connect:", 8) == 0)
83     {
84         const char *cp = strchr(uri, ',');
85         if (cp)
86         {
87             size_t len = cp - (uri + 8);
88             *connect_host = (char *) xmalloc(len+1);
89             memcpy(*connect_host, uri + 8, len);
90             (*connect_host)[len] = '\0';
91             uri = cp+1;
92         }
93     }
94
95     if (strncmp (uri, "tcp:", 4) == 0)
96     {
97         *t = tcpip_type;
98         *host = uri + 4;
99         *proto = PROTO_Z3950;
100     }
101     else if (strncmp (uri, "ssl:", 4) == 0)
102     {
103 #if HAVE_OPENSSL_SSL_H
104         *t = ssl_type;
105         *host = uri + 4;
106         *proto = PROTO_Z3950;
107 #else
108         return 0;
109 #endif
110     }
111     else if (strncmp (uri, "unix:", 5) == 0)
112     {
113 #ifndef WIN32
114         *t = unix_type;
115         *host = uri + 5;
116         *proto = PROTO_Z3950;
117 #else
118         return 0;
119 #endif
120     }
121     else if (strncmp(uri, "http:", 5) == 0)
122     {
123         *t = tcpip_type;
124         *host = uri + 5;
125         while (**host == '/')
126             (*host)++;
127         *proto = PROTO_HTTP;
128     }
129     else if (strncmp(uri, "https:", 6) == 0)
130     {
131 #if HAVE_OPENSSL_SSL_H
132         *t = ssl_type;
133         *host = uri + 6;
134         while (**host == '/')
135             (*host)++;
136         *proto = PROTO_HTTP;
137 #else
138         return 0;
139 #endif
140     }
141     else
142     {
143         *proto = PROTO_Z3950;
144         *t = tcpip_type;
145         *host = uri;
146     }
147     return 1;
148 }
149
150 COMSTACK cs_create_host(const char *vhost, int blocking, void **vp)
151 {
152     enum oid_proto proto = PROTO_Z3950;
153     const char *host = 0;
154     COMSTACK cs;
155     CS_TYPE t;
156     char *connect_host = 0;
157
158     cs_parse_host(vhost, &host, &t, &proto, &connect_host);
159
160     if (t == tcpip_type)
161     {
162         cs = yaz_tcpip_create(-1, blocking, proto, connect_host ? host : 0);
163     }
164     else
165     {
166         cs = cs_create(t, blocking, proto);
167     }
168     if (cs)
169     {
170         if (!(*vp = cs_straddr(cs, connect_host ? connect_host : host)))
171         {
172             cs_close (cs);
173             cs = 0;
174         }    
175     }
176     xfree(connect_host);
177     return cs;
178 }
179
180 int cs_look (COMSTACK cs)
181 {
182     return cs->event;
183 }
184
185 static int skip_crlf(const char *buf, int len, int *i)
186 {
187     if (*i < len)
188     {
189         if (buf[*i] == '\r' && *i < len-1 && buf[*i + 1] == '\n')
190         {
191             (*i) += 2;
192             return 1;
193         }
194         else if (buf[*i] == '\n')
195         {
196             (*i)++;
197             return 1;
198         }
199     }
200     return 0;
201 }
202
203 #define CHUNK_DEBUG 0
204
205 static int cs_complete_http(const char *buf, int len, int head_only)
206 {
207     /* deal with HTTP request/response */
208     int i = 2, content_len = 0, chunked = 0;
209
210     if (len < 6)
211         return 0;
212
213     /* if dealing with HTTP responses - then default
214        content length is unlimited (socket close) */
215     if (!head_only && !memcmp(buf, "HTTP/", 5))
216         content_len = -1; 
217
218 #if 0
219     printf("len = %d\n", len);
220     fwrite (buf, 1, len, stdout);
221     printf("----------\n");
222 #endif
223     while (i <= len-2)
224     {
225         if (i > 8192)
226         {
227             return i;  /* do not allow more than 8K HTTP header */
228         }
229         if (skip_crlf(buf, len, &i))
230         {
231             if (skip_crlf(buf, len, &i))
232             {
233                 /* inside content */
234                 if (chunked)
235                 { 
236                     /* inside chunked body .. */
237                     while(1)
238                     {
239                         int chunk_len = 0;
240 #if CHUNK_DEBUG
241                         if (i < len-2)
242                         {
243                             printf ("\n<<<");
244                             int j;
245                             for (j = i; j <= i+3; j++)
246                                 printf ("%c", buf[j]);
247                             printf (">>>\n");
248                         }
249 #endif
250                         /* read chunk length */
251                         while (1)
252                             if (i >= len-2) {
253 #if CHUNK_DEBUG
254                                 printf ("returning incomplete read at 1\n");
255                                 printf ("i=%d len=%d\n", i, len);
256 #endif
257                                 return 0;
258                             } else if (isdigit(buf[i]))
259                                 chunk_len = chunk_len * 16 + 
260                                     (buf[i++] - '0');
261                             else if (isupper(buf[i]))
262                                 chunk_len = chunk_len * 16 + 
263                                     (buf[i++] - ('A'-10));
264                             else if (islower(buf[i]))
265                                 chunk_len = chunk_len * 16 + 
266                                     (buf[i++] - ('a'-10));
267                             else
268                                 break;
269                         if (chunk_len == 0)
270                             break;
271                         if (chunk_len < 0)
272                             return i;
273                         
274                         while (1)
275                         {
276                             if (i >= len -1)
277                                 return 0;
278                             if (skip_crlf(buf, len, &i))
279                                 break;
280                             i++;
281                         }
282                         /* got CRLF */
283 #if CHUNK_DEBUG
284                         printf ("chunk_len=%d\n", chunk_len);
285 #endif                      
286                         i += chunk_len;
287                         if (i >= len-2)
288                             return 0;
289                         if (!skip_crlf(buf, len, &i))
290                             return 0;
291                     }
292                     /* consider trailing headers .. */
293                     while (i < len)
294                     {
295                         if (skip_crlf(buf, len, &i))
296                         {
297                             if (skip_crlf(buf, len, &i))
298                                 return i;
299                         }
300                         else
301                             i++;
302                     }
303 #if CHUNK_DEBUG
304                     printf ("returning incomplete read at 2\n");
305                     printf ("i=%d len=%d\n", i, len);
306 #endif
307                     return 0;
308                 }
309                 else
310                 {   /* not chunked ; inside body */
311                     if (content_len == -1)
312                         return 0;   /* no content length */
313                     else if (len >= i + content_len)
314                     {
315                         return i + content_len;
316                     }
317                 }
318                 break;
319             }
320             else if (i < len - 20 && 
321                      !strncasecmp((const char *) buf+i, "Transfer-Encoding:", 18))
322             {
323                 i+=18;
324                 while (buf[i] == ' ')
325                     i++;
326                 if (i < len - 8)
327                     if (!strncasecmp((const char *) buf+i, "chunked", 7))
328                         chunked = 1;
329             }
330             else if (i < len - 17 &&
331                      !strncasecmp((const char *)buf+i, "Content-Length:", 15))
332             {
333                 i+= 15;
334                 while (buf[i] == ' ')
335                     i++;
336                 content_len = 0;
337                 while (i <= len-4 && isdigit(buf[i]))
338                     content_len = content_len*10 + (buf[i++] - '0');
339                 if (content_len < 0) /* prevent negative offsets */
340                     content_len = 0;
341             }
342             else
343                 i++;
344         }
345         else
346             i++;
347     }
348     return 0;
349 }
350
351 static int cs_complete_auto_x(const char *buf, int len, int head_only)
352 {
353     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
354                 && buf[1] >= 0x20 && buf[1] < 0x7f
355                 && buf[2] >= 0x20 && buf[2] < 0x7f)
356     {
357         int r = cs_complete_http(buf, len, head_only);
358         return r;
359     }
360     return completeBER((const unsigned char *) buf, len);
361 }
362
363
364 int cs_complete_auto(const char *buf, int len)
365 {
366     return cs_complete_auto_x(buf, len, 0);
367 }
368
369 int cs_complete_auto_head(const char *buf, int len)
370 {
371     return cs_complete_auto_x(buf, len, 1);
372 }
373
374 void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
375 {
376     cs->max_recv_bytes = max_recv_bytes;
377 }
378
379 /*
380  * Local variables:
381  * c-basic-offset: 4
382  * indent-tabs-mode: nil
383  * End:
384  * vim: shiftwidth=4 tabstop=8 expandtab
385  */
386