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