7ee51fe4234c00a4a1d6ec895179b47f5ec04914
[yaz-moved-to-github.git] / src / comstack.c
1 /*
2  * Copyright (c) 1995-2004, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: comstack.c,v 1.7 2004-04-28 22:44:59 adam Exp $
6  */
7
8 #include <string.h>
9 #include <ctype.h>
10 #include <errno.h>
11
12 #include <yaz/comstack.h>
13 #include <yaz/tcpip.h>
14 #include <yaz/unix.h>
15 #include <yaz/odr.h>
16
17 #ifdef WIN32
18 #define strncasecmp _strnicmp
19 #endif
20
21 static const char *cs_errlist[] =
22 {
23     "No error or unspecified error",
24     "System (lower-layer) error",
25     "Operation out of state",
26     "No data (operation would block)",
27     "New data while half of old buffer is on the line (flow control)",
28     "Permission denied",
29     "SSL error"
30 };
31
32 const char *cs_errmsg(int n)
33 {
34     static char buf[250];
35
36     if (n < CSNONE || n > CSLASTERROR) {
37         sprintf(buf, "unknown comstack error %d", n);
38         return buf;
39     }
40     if (n == CSYSERR) {
41         sprintf(buf, "%s: %s", cs_errlist[n], strerror(errno));
42         return buf;
43     }
44     return cs_errlist[n];
45 }
46
47 const char *cs_strerror(COMSTACK h)
48 {
49     return cs_errmsg(h->cerrno);
50 }
51
52 void cs_get_host_args(const char *type_and_host, const char **args)
53 {
54     
55     *args = "";
56     if (*type_and_host && strncmp(type_and_host, "unix:", 5))
57     {
58         const char *cp;
59         cp = strstr(type_and_host, "://");
60         if (cp)
61             cp = cp+3;
62         else
63             cp = type_and_host;
64         cp = strchr(cp, '/');
65         if (cp)
66             *args = cp+1;
67     }
68 }
69
70 COMSTACK cs_create_host(const char *type_and_host, int blocking, void **vp)
71 {
72     enum oid_proto proto = PROTO_Z3950;
73     const char *host = 0;
74     COMSTACK cs;
75     CS_TYPE t;
76
77     printf (
78 #if HAVE_OPENSSL_SSL_H
79             "cs_create_host SSL\n"
80 #else
81             "cs_create_host\n"
82 #endif
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         if (host[0] == '/' && host[1] == '/')
112             host = host + 2;
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         if (host[0] == '/' && host[1] == '/')
121             host = host + 2;
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     }
133     cs = cs_create (t, blocking, proto);
134     if (!cs)
135         return 0;
136
137     if (!(*vp = cs_straddr(cs, host)))
138     {
139         cs_close (cs);
140         return 0;
141     }    
142     return cs;
143 }
144
145 int cs_look (COMSTACK cs)
146 {
147     return cs->event;
148 }
149
150 #define CHUNK_DEBUG 0
151 int cs_complete_auto(const unsigned char *buf, int len)
152 {
153     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
154                 && buf[1] >= 0x20 && buf[1] < 0x7f
155                 && buf[2] >= 0x20 && buf[2] < 0x7f)
156     {
157         /* deal with HTTP request/response */
158         int i = 2, content_len = 0, chunked = 0;
159
160         while (i <= len-4)
161         {
162             if (i > 8192)
163                 return i;  /* do not allow more than 8K HTTP header */
164             if (buf[i] == '\r' && buf[i+1] == '\n')
165             {
166                 i += 2;
167                 if (buf[i] == '\r' && buf[i+1] == '\n')
168                 {
169                     if (chunked)
170                     { 
171                         /* inside chunked body .. */
172                         while(1)
173                         {
174                             int j, chunk_len = 0;
175                             i += 2;
176 #if CHUNK_DEBUG
177 /* debugging */
178                             if (i <len-2)
179                             {
180                                 printf ("\n<<<");
181                                 int j;
182                                 for (j = i; j <= i+4; j++)
183                                     printf ("%c", buf[j]);
184                                 printf (">>>\n");
185                             }
186 #endif
187                             /* read chunk length */
188                             while (1)
189                                 if (i >= len-2) {
190 #if CHUNK_DEBUG
191 /* debugging */                                    
192                                     printf ("XXXXXXXX not there yet 1\n");
193                                     printf ("i=%d len=%d\n", i, len);
194 #endif
195                                     return 0;
196                                 } else if (isdigit(buf[i]))
197                                     chunk_len = chunk_len * 16 + 
198                                         (buf[i++] - '0');
199                                 else if (isupper(buf[i]))
200                                     chunk_len = chunk_len * 16 + 
201                                         (buf[i++] - ('A'-10));
202                                 else if (islower(buf[i]))
203                                     chunk_len = chunk_len * 16 + 
204                                         (buf[i++] - ('a'-10));
205                                 else
206                                     break;
207                             /* move forward until CRLF - skip chunk ext */
208                             j = 0;
209                             while (buf[i] != '\r' && buf[i+1] != '\n')
210                             {
211                                 if (i >= len-2)
212                                     return 0;   /* need more buffer .. */
213                                 if (++j > 1000)
214                                     return i; /* enough.. stop */
215                                 i++;
216                             }
217                             /* got CRLF */
218 #if CHUNK_DEBUG
219                             printf ("XXXXXX chunk_len=%d\n", chunk_len);
220 #endif                      
221                             if (chunk_len < 0)
222                                 return i+2;    /* bad chunk_len */
223                             if (chunk_len == 0)
224                                 break;
225                             i += chunk_len+2;
226                         }
227                         /* consider trailing headers .. */
228                         while(i <= len-4)
229                         {
230                             if (buf[i] == '\r' &&  buf[i+1] == '\n' &&
231                                 buf[i+2] == '\r' && buf[i+3] == '\n')
232                                 if (len >= i+4)
233                                     return i+4;
234                             i++;
235                         }
236 #if CHUNK_DEBUG
237 /* debugging */
238                         printf ("XXXXXXXXX not there yet 2\n");
239                         printf ("i=%d len=%d\n", i, len);
240 #endif
241                         return 0;
242                     }
243                     else
244                     {   /* not chunked ; inside body */
245                         /* i += 2 seems not to work with GCC -O2 .. 
246                            so i+2 is used instead .. */
247                         if (len >= (i+2)+ content_len)
248                             return (i+2)+ content_len;
249                     }
250                     break;
251                 }
252                 else if (i < len - 20 && 
253                          !strncasecmp(buf+i, "Transfer-Encoding:", 18))
254                 {
255                     i+=18;
256                     while (buf[i] == ' ')
257                         i++;
258                     if (i < len - 8)
259                         if (!strncasecmp(buf+i, "chunked", 7))
260                             chunked = 1;
261                 }
262                 else if (i < len - 17 &&
263                          !strncasecmp(buf+i, "Content-Length:", 15))
264                 {
265                     i+= 15;
266                     while (buf[i] == ' ')
267                         i++;
268                     content_len = 0;
269                     while (i <= len-4 && isdigit(buf[i]))
270                         content_len = content_len*10 + (buf[i++] - '0');
271                     if (content_len < 0) /* prevent negative offsets */
272                         content_len = 0;
273                 }
274                 else
275                     i++;
276             }
277             else
278                 i++;
279         }
280         return 0;
281     }
282     return completeBER(buf, len);
283 }