Extend get_org_info (snippets) to return original string YAZ-836
[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     int proxy_mode;
182     return cs_create_host2(vhost, blocking, vp, proxy_host, &proxy_mode);
183 }
184
185 COMSTACK cs_create_host2(const char *vhost, int blocking, void **vp,
186                          const char *proxy_host, int *proxy_mode)
187 {
188     enum oid_proto proto = PROTO_Z3950;
189     const char *host = 0;
190     COMSTACK cs;
191     CS_TYPE t;
192     char *connect_host = 0;
193
194     const char *bind_host = strchr(vhost, ' ');
195     if (bind_host && bind_host[1])
196         bind_host++;
197     else
198         bind_host = 0;
199
200     *proxy_mode = 0;
201     if (!cs_parse_host(vhost, &host, &t, &proto, &connect_host))
202         return 0;
203
204     /*  vhost      proxy       proxy method  proxy-flag */
205     /*  TCP+Z3950  TCP+Z3950   TCP+Z3950      1 */
206     /*  TCP+Z3950  TCP+HTTP    CONNECT        0 */
207     /*  TCP+HTTP   TCP+Z3950   TCP+HTTP       1 */
208     /*  TCP+HTTP   TCP+HTTP    TCP+HTTP       1 */
209     /*  SSL+*      TCP+*       CONNECT        0 */
210     /*  ?          SSL         error */
211
212     if (proxy_host && !connect_host)
213     {
214         enum oid_proto proto1;
215         CS_TYPE t1;
216         const char *host1 = 0;
217
218         if (!cs_parse_host(proxy_host, &host1, &t1, &proto1, &connect_host))
219             return 0;
220         if (connect_host)
221         {
222             xfree(connect_host);
223             return 0;
224         }
225         if (t1 != tcpip_type)
226             return 0;
227
228         if (t == ssl_type || (proto == PROTO_Z3950 && proto1 == PROTO_HTTP))
229             connect_host = xstrdup(host1);
230         else
231         {
232             *proxy_mode = 1;
233             host = host1;
234         }
235     }
236
237     if (t == tcpip_type)
238     {
239         cs = yaz_tcpip_create3(-1, blocking, proto, connect_host ? host : 0,
240                                0 /* user:pass */, bind_host);
241     }
242     else if (t == ssl_type)
243     {
244         cs = yaz_ssl_create(-1, blocking, proto, connect_host ? host : 0,
245                             0 /* user:pass */, bind_host);
246     }
247     else
248     {
249         cs = cs_create(t, blocking, proto);
250     }
251     if (cs)
252     {
253         if (!(*vp = cs_straddr(cs, connect_host ? connect_host : host)))
254         {
255             cs_close (cs);
256             cs = 0;
257         }
258     }
259     xfree(connect_host);
260     return cs;
261 }
262
263 int cs_look (COMSTACK cs)
264 {
265     return cs->event;
266 }
267
268 static int skip_crlf(const char *buf, int len, int *i)
269 {
270     if (*i < len)
271     {
272         if (buf[*i] == '\r' && *i < len-1 && buf[*i + 1] == '\n')
273         {
274             (*i) += 2;
275             return 1;
276         }
277         else if (buf[*i] == '\n')
278         {
279             (*i)++;
280             return 1;
281         }
282     }
283     return 0;
284 }
285
286 #define CHUNK_DEBUG 0
287
288 static int cs_read_chunk(const char *buf, int i, int len)
289 {
290     /* inside chunked body .. */
291     while (1)
292     {
293         int chunk_len = 0;
294 #if CHUNK_DEBUG
295         if (i < len-2)
296         {
297             int j;
298             printf ("\n<<<");
299             for (j = i; j <= i+3; j++)
300                 printf ("%c", buf[j]);
301             printf (">>>\n");
302         }
303 #endif
304         /* read chunk length */
305         while (1)
306             if (i >= len-2) {
307 #if CHUNK_DEBUG
308                 printf ("returning incomplete read at 1\n");
309                 printf ("i=%d len=%d\n", i, len);
310 #endif
311                 return 0;
312             } else if (yaz_isdigit(buf[i]))
313                 chunk_len = chunk_len * 16 +
314                     (buf[i++] - '0');
315             else if (yaz_isupper(buf[i]))
316                 chunk_len = chunk_len * 16 +
317                     (buf[i++] - ('A'-10));
318             else if (yaz_islower(buf[i]))
319                 chunk_len = chunk_len * 16 +
320                     (buf[i++] - ('a'-10));
321             else
322                 break;
323         if (chunk_len == 0)
324             break;
325         if (chunk_len < 0)
326             return i;
327
328         while (1)
329         {
330             if (i >= len -1)
331                 return 0;
332             if (skip_crlf(buf, len, &i))
333                 break;
334             i++;
335         }
336         /* got CRLF */
337 #if CHUNK_DEBUG
338         printf ("chunk_len=%d\n", chunk_len);
339 #endif
340         i += chunk_len;
341         if (i >= len-2)
342             return 0;
343         if (!skip_crlf(buf, len, &i))
344             return 0;
345     }
346     /* consider trailing headers .. */
347     while (i < len)
348     {
349         if (skip_crlf(buf, len, &i))
350         {
351             if (skip_crlf(buf, len, &i))
352                 return i;
353         }
354         else
355             i++;
356     }
357 #if CHUNK_DEBUG
358     printf ("returning incomplete read at 2\n");
359     printf ("i=%d len=%d\n", i, len);
360 #endif
361     return 0;
362 }
363
364 static int cs_complete_http(const char *buf, int len, int head_only)
365 {
366     /* deal with HTTP request/response */
367     int i, content_len = 0, chunked = 0;
368
369     /* need at least one line followed by \n or \r .. */
370     for (i = 0; ; i++)
371         if (i == len)
372             return 0; /* incomplete */
373         else if (buf[i] == '\n' || buf[i] == '\r')
374             break;
375
376     /* check to see if it's a response with content */
377     if (!head_only && !memcmp(buf, "HTTP/", 5))
378     {
379         int j;
380         for (j = 5; j < i; j++)
381             if (buf[j] == ' ')
382             {
383                 ++j;
384                 if (buf[j] == '1') /* 1XX */
385                     ;
386                 else if (!memcmp(buf + j, "204", 3))
387                     ;
388                 else if (!memcmp(buf + j, "304", 3))
389                     ;
390                 else
391                     content_len = -1;
392                 break;
393             }
394     }
395 #if 0
396     printf("len = %d\n", len);
397     fwrite (buf, 1, len, stdout);
398     printf("----------\n");
399 #endif
400     for (i = 2; i <= len-2; )
401     {
402         if (i > 8192)
403         {
404             return i;  /* do not allow more than 8K HTTP header */
405         }
406         if (skip_crlf(buf, len, &i))
407         {
408             if (skip_crlf(buf, len, &i))
409             {
410                 /* inside content */
411                 if (chunked)
412                     return cs_read_chunk(buf, i, len);
413                 else
414                 {   /* not chunked ; inside body */
415                     if (content_len == -1)
416                         return 0;   /* no content length */
417                     else if (len >= i + content_len)
418                     {
419                         return i + content_len;
420                     }
421                 }
422                 break;
423             }
424             else if (i < len - 20 &&
425                      !yaz_strncasecmp((const char *) buf+i,
426                                       "Transfer-Encoding:", 18))
427             {
428                 i+=18;
429                 while (buf[i] == ' ')
430                     i++;
431                 if (i < len - 8)
432                     if (!yaz_strncasecmp((const char *) buf+i, "chunked", 7))
433                         chunked = 1;
434             }
435             else if (i < len - 17 &&
436                      !yaz_strncasecmp((const char *)buf+i,
437                                       "Content-Length:", 15))
438             {
439                 i+= 15;
440                 while (buf[i] == ' ')
441                     i++;
442                 content_len = 0;
443                 while (i <= len-4 && yaz_isdigit(buf[i]))
444                     content_len = content_len*10 + (buf[i++] - '0');
445                 if (content_len < 0) /* prevent negative offsets */
446                     content_len = 0;
447             }
448             else
449                 i++;
450         }
451         else
452             i++;
453     }
454     return 0;
455 }
456
457 static int cs_complete_auto_x(const char *buf, int len, int head_only)
458 {
459     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
460                 && buf[1] >= 0x20 && buf[1] < 0x7f
461                 && buf[2] >= 0x20 && buf[2] < 0x7f)
462     {
463         int r = cs_complete_http(buf, len, head_only);
464         return r;
465     }
466     return completeBER(buf, len);
467 }
468
469
470 int cs_complete_auto(const char *buf, int len)
471 {
472     return cs_complete_auto_x(buf, len, 0);
473 }
474
475 int cs_complete_auto_head(const char *buf, int len)
476 {
477     return cs_complete_auto_x(buf, len, 1);
478 }
479
480 void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
481 {
482     cs->max_recv_bytes = max_recv_bytes;
483 }
484
485 /*
486  * Local variables:
487  * c-basic-offset: 4
488  * c-file-style: "Stroustrup"
489  * indent-tabs-mode: nil
490  * End:
491  * vim: shiftwidth=4 tabstop=8 expandtab
492  */
493