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