Fixed 523: PQF parser does not properly diagnose @prox syntax errors.
[yaz-moved-to-github.git] / src / comstack.c
1 /*
2  * Copyright (C) 1995-2005, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: comstack.c,v 1.15 2005-06-25 15:46:03 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/comstack.h>
18 #include <yaz/tcpip.h>
19 #include <yaz/unix.h>
20 #include <yaz/odr.h>
21
22 #ifdef WIN32
23 #define strncasecmp _strnicmp
24 #endif
25
26 static const char *cs_errlist[] =
27 {
28     "No error or unspecified error",
29     "System (lower-layer) error",
30     "Operation out of state",
31     "No data (operation would block)",
32     "New data while half of old buffer is on the line (flow control)",
33     "Permission denied",
34     "SSL error"
35 };
36
37 const char *cs_errmsg(int n)
38 {
39     static char buf[250];
40
41     if (n < CSNONE || n > CSLASTERROR) {
42         sprintf(buf, "unknown comstack error %d", n);
43         return buf;
44     }
45     if (n == CSYSERR) {
46         sprintf(buf, "%s: %s", cs_errlist[n], strerror(errno));
47         return buf;
48     }
49     return cs_errlist[n];
50 }
51
52 const char *cs_strerror(COMSTACK h)
53 {
54     return cs_errmsg(h->cerrno);
55 }
56
57 void cs_get_host_args(const char *type_and_host, const char **args)
58 {
59     
60     *args = "";
61     if (*type_and_host && strncmp(type_and_host, "unix:", 5))
62     {
63         const char *cp;
64         cp = strstr(type_and_host, "://");
65         if (cp)
66             cp = cp+3;
67         else
68             cp = type_and_host;
69         cp = strchr(cp, '/');
70         if (cp)
71             *args = cp+1;
72     }
73 }
74
75 COMSTACK cs_create_host(const char *type_and_host, int blocking, void **vp)
76 {
77     enum oid_proto proto = PROTO_Z3950;
78     const char *host = 0;
79     COMSTACK cs;
80     CS_TYPE t;
81
82     if (strncmp (type_and_host, "tcp:", 4) == 0)
83     {
84         t = tcpip_type;
85         host = type_and_host + 4;
86     }
87     else if (strncmp (type_and_host, "ssl:", 4) == 0)
88     {
89 #if HAVE_OPENSSL_SSL_H
90         t = ssl_type;
91         host = type_and_host + 4;
92 #else
93         return 0;
94 #endif
95     }
96     else if (strncmp (type_and_host, "unix:", 5) == 0)
97     {
98 #ifndef WIN32
99         t = unix_type;
100         host = type_and_host + 5;
101 #else
102         return 0;
103 #endif
104     }
105     else if (strncmp(type_and_host, "http:", 5) == 0)
106     {
107         t = tcpip_type;
108         host = type_and_host + 5;
109         while (host[0] == '/')
110             host++;
111         proto = PROTO_HTTP;
112     }
113     else if (strncmp(type_and_host, "https:", 6) == 0)
114     {
115 #if HAVE_OPENSSL_SSL_H
116         t = ssl_type;
117         host = type_and_host + 6;
118         while (host[0] == '/')
119             host++;
120         proto = PROTO_HTTP;
121 #else
122         return 0;
123 #endif
124     }
125     else
126     {
127         t = tcpip_type;
128         host = type_and_host;
129     }
130     cs = cs_create (t, blocking, proto);
131     if (!cs)
132         return 0;
133
134     if (!(*vp = cs_straddr(cs, host)))
135     {
136         cs_close (cs);
137         return 0;
138     }    
139     return cs;
140 }
141
142 int cs_look (COMSTACK cs)
143 {
144     return cs->event;
145 }
146
147 #define CHUNK_DEBUG 0
148 int cs_complete_auto(const unsigned char *buf, int len)
149 {
150     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
151                 && buf[1] >= 0x20 && buf[1] < 0x7f
152                 && buf[2] >= 0x20 && buf[2] < 0x7f)
153     {
154         /* deal with HTTP request/response */
155         int i = 2, content_len = 0, chunked = 0;
156         
157         /* if dealing with HTTP responses - then default
158            content length is unlimited (socket close) */
159         if (!memcmp(buf, "HTTP/", 5))
160             content_len = -1; 
161
162         while (i <= len-4)
163         {
164             if (i > 8192)
165             {
166                 return i;  /* do not allow more than 8K HTTP header */
167             }
168             if (buf[i] == '\r' && buf[i+1] == '\n')
169             {
170                 i += 2;
171                 if (buf[i] == '\r' && buf[i+1] == '\n')
172                 {
173                     if (chunked)
174                     { 
175                         /* inside chunked body .. */
176                         while(1)
177                         {
178                             int j, chunk_len = 0;
179                             i += 2;
180 #if CHUNK_DEBUG
181 /* debugging */
182                             if (i <len-2)
183                             {
184                                 printf ("\n<<<");
185                                 int j;
186                                 for (j = i; j <= i+4; j++)
187                                     printf ("%c", buf[j]);
188                                 printf (">>>\n");
189                             }
190 #endif
191                             /* read chunk length */
192                             while (1)
193                                 if (i >= len-2) {
194 #if CHUNK_DEBUG
195 /* debugging */                                    
196                                     printf ("XXXXXXXX not there yet 1\n");
197                                     printf ("i=%d len=%d\n", i, len);
198 #endif
199                                     return 0;
200                                 } else if (isdigit(buf[i]))
201                                     chunk_len = chunk_len * 16 + 
202                                         (buf[i++] - '0');
203                                 else if (isupper(buf[i]))
204                                     chunk_len = chunk_len * 16 + 
205                                         (buf[i++] - ('A'-10));
206                                 else if (islower(buf[i]))
207                                     chunk_len = chunk_len * 16 + 
208                                         (buf[i++] - ('a'-10));
209                                 else
210                                     break;
211                             /* move forward until CRLF - skip chunk ext */
212                             j = 0;
213                             while (buf[i] != '\r' && buf[i+1] != '\n')
214                             {
215                                 if (i >= len-2)
216                                     return 0;   /* need more buffer .. */
217                                 if (++j > 1000)
218                                     return i; /* enough.. stop */
219                                 i++;
220                             }
221                             /* got CRLF */
222 #if CHUNK_DEBUG
223                             printf ("XXXXXX chunk_len=%d\n", chunk_len);
224 #endif                      
225                             if (chunk_len < 0)
226                                 return i+2;    /* bad chunk_len */
227                             if (chunk_len == 0)
228                                 break;
229                             i += chunk_len+2;
230                         }
231                         /* consider trailing headers .. */
232                         while(i <= len-4)
233                         {
234                             if (buf[i] == '\r' &&  buf[i+1] == '\n' &&
235                                 buf[i+2] == '\r' && buf[i+3] == '\n')
236                                 if (len >= i+4)
237                                     return i+4;
238                             i++;
239                         }
240 #if CHUNK_DEBUG
241 /* debugging */
242                         printf ("XXXXXXXXX not there yet 2\n");
243                         printf ("i=%d len=%d\n", i, len);
244 #endif
245                         return 0;
246                     }
247                     else
248                     {   /* not chunked ; inside body */
249                         /* i += 2 seems not to work with GCC -O2 .. 
250                            so i+2 is used instead .. */
251                         if (content_len == -1)
252                             return 0;   /* no content length */
253                         else if (len >= (i+2)+ content_len)
254                         {
255                             return (i+2)+ content_len;
256                         }
257                     }
258                     break;
259                 }
260                 else if (i < len - 20 && 
261                          !strncasecmp((const char *) buf+i, "Transfer-Encoding:", 18))
262                 {
263                     i+=18;
264                     while (buf[i] == ' ')
265                         i++;
266                     if (i < len - 8)
267                         if (!strncasecmp((const char *) buf+i, "chunked", 7))
268                             chunked = 1;
269                 }
270                 else if (i < len - 17 &&
271                          !strncasecmp((const char *)buf+i, "Content-Length:", 15))
272                 {
273                     i+= 15;
274                     while (buf[i] == ' ')
275                         i++;
276                     content_len = 0;
277                     while (i <= len-4 && isdigit(buf[i]))
278                         content_len = content_len*10 + (buf[i++] - '0');
279                     if (content_len < 0) /* prevent negative offsets */
280                         content_len = 0;
281                 }
282                 else
283                     i++;
284             }
285             else
286                 i++;
287         }
288         return 0;
289     }
290     return completeBER(buf, len);
291 }
292 /*
293  * Local variables:
294  * c-basic-offset: 4
295  * indent-tabs-mode: nil
296  * End:
297  * vim: shiftwidth=4 tabstop=8 expandtab
298  */
299