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