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