Fix use of strcasecmp for WIN32
[yaz-moved-to-github.git] / src / zgdu.c
1 /*
2  * Copyright (c) 2002-2004, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: zgdu.c,v 1.7 2004-02-23 10:14:06 adam Exp $
6  */
7
8 #include <ctype.h>
9 #include <yaz/odr.h>
10 #include <yaz/yaz-version.h>
11 #include <yaz/yaz-iconv.h>
12 #include <yaz/zgdu.h>
13
14 #ifdef WIN32
15 #define strncasecmp _strnicmp
16 #define strcasecmp _stricmp
17 #endif
18  
19 static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers,
20                                   char **content_buf, int *content_len)
21 {
22     int i = off;
23     int chunked = 0;
24
25     *headers = 0;
26     while (i < o->size-1 && o->buf[i] == '\r')
27     {
28         int po;
29         i++;
30         if (o->buf[i] != '\n')
31         {
32             o->error = OHTTP;
33             return 0;
34         }
35         i++;
36         if (o->buf[i] == '\r')
37             break;
38         for (po = i; ; i++)
39         {
40             if (i == o->size)
41             {
42                 o->error = OHTTP;
43                 return 0;
44             }
45             else if (o->buf[i] == ':')
46                 break;
47         }
48         *headers = (Z_HTTP_Header *) odr_malloc(o, sizeof(**headers));
49         (*headers)->name = (char*) odr_malloc(o, i - po + 1);
50         memcpy ((*headers)->name, o->buf + po, i - po);
51         (*headers)->name[i - po] = '\0';
52         i++;
53         while (i < o->size-1 && o->buf[i] == ' ')
54             i++;
55         for (po = i; i < o->size-1 && o->buf[i] != '\r' ; i++)
56             ;
57         
58         (*headers)->value = (char*) odr_malloc(o, i - po + 1);
59         memcpy ((*headers)->value, o->buf + po, i - po);
60         (*headers)->value[i - po] = '\0';
61         
62         if (!strcasecmp((*headers)->name, "Transfer-Encoding")
63             &&
64             !strcasecmp((*headers)->value, "chunked"))
65             chunked = 1;
66         headers = &(*headers)->next;
67     }
68     *headers = 0;
69     i++;
70     if (o->buf[i] != '\n')
71     {
72         o->error = OHTTP;
73         return 0;
74     }
75     i++;
76
77     if (chunked)
78     {
79         int off = 0;
80         
81         /* we know buffer will be smaller than o->size - i - 2*/
82         *content_buf = (char*) odr_malloc(o, o->size - i - 2);  
83         
84         while (1)
85         {
86             int chunk_len = 0;
87             for (; i  < o->size-2; i++)
88                 if (isdigit(o->buf[i]))
89                     chunk_len = chunk_len * 16 + 
90                         (o->buf[i] - '0');
91                 else if (isupper(o->buf[i]))
92                     chunk_len = chunk_len * 16 + 
93                         (o->buf[i] - ('A'-10));
94                 else if (islower(o->buf[i]))
95                     chunk_len = chunk_len * 16 + 
96                         (o->buf[i] - ('a'-10));
97                 else
98                     break;
99             if (i > o->size - 2)
100                 break;
101             if (o->buf[i] != '\r' || o->buf[i+1] != '\n') 
102             {   /* chunk length must be followed by \r\n */
103                 o->error = OHTTP;
104                 return 0;
105             }
106             i += 2;
107             if (chunk_len == 0)
108                 break;
109             if (chunk_len < 0 || off + chunk_len > o->size)
110             {
111                 o->error = OHTTP;
112                 return 0;
113             }
114             memcpy (*content_buf + off, o->buf + i, chunk_len);
115             i += chunk_len;
116             off += chunk_len;
117         }
118         if (!off)
119             *content_buf = 0;
120         *content_len = off;
121     }
122     else
123     {
124         if (i > o->size)
125         {
126             o->error = OHTTP;
127             return 0;
128         }
129         else if (i == o->size)
130         {
131             *content_buf = 0;
132             *content_len = 0;
133         }
134         else 
135         {
136             *content_len = o->size - i;
137             *content_buf = (char*) odr_malloc(o, *content_len + 1);
138             memcpy(*content_buf, o->buf + i, *content_len);
139             (*content_buf)[*content_len] = '\0';
140         }
141     }
142     return 1;
143 }
144
145 void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n,
146                        const char *v)
147 {
148     while (*hp)
149         hp = &(*hp)->next;
150     *hp = (Z_HTTP_Header *) odr_malloc(o, sizeof(**hp));
151     (*hp)->name = odr_strdup(o, n);
152     (*hp)->value = odr_strdup(o, v);
153     (*hp)->next = 0;
154 }
155
156 const char *z_HTTP_header_lookup(Z_HTTP_Header *hp, const char *n)
157 {
158     for (; hp; hp = hp->next)
159         if (!yaz_matchstr(hp->name, n))
160             return hp->value;
161     return 0;
162 }
163
164
165 Z_GDU *z_get_HTTP_Request(ODR o)
166 {
167     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
168     Z_HTTP_Request *hreq;
169
170     p->which = Z_GDU_HTTP_Request;
171     p->u.HTTP_Request = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hreq));
172     hreq = p->u.HTTP_Request;
173     hreq->headers = 0;
174     hreq->content_len = 0;
175     hreq->content_buf = 0;
176     hreq->version = "1.1";
177     hreq->method = "POST";
178     hreq->path = "/";
179     z_HTTP_header_add(o, &hreq->headers, "User-Agent",
180                       "YAZ/" YAZ_VERSION);
181     return p;
182 }
183
184 Z_GDU *z_get_HTTP_Response(ODR o, int code)
185 {
186     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
187     Z_HTTP_Response *hres;
188
189     p->which = Z_GDU_HTTP_Response;
190     p->u.HTTP_Response = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hres));
191     hres = p->u.HTTP_Response;
192     hres->headers = 0;
193     hres->content_len = 0;
194     hres->content_buf = 0;
195     hres->code = code;
196     hres->version = "1.1";
197     z_HTTP_header_add(o, &hres->headers, "Server",
198                       "YAZ/" YAZ_VERSION);
199     if (code != 200)
200     {
201         hres->content_buf = (char*) odr_malloc(o, 400);
202         sprintf (hres->content_buf, 
203                  "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
204                  "<HTML>\n"
205                  " <HEAD>\n"
206                  "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
207                  " </HEAD>\n"
208                  " <BODY>\n"
209                  "  <P><A HREF=\"http://www.indexdata.dk/yaz/\">YAZ</A> " 
210                  YAZ_VERSION "</P>\n"
211                  "  <P>Error: %d</P>\n"
212                  "  <P>Description: %.50s</P>\n"
213                  " </BODY>\n"
214                  "</HTML>\n",
215                  code, z_HTTP_errmsg(code));
216         hres->content_len = strlen(hres->content_buf);
217         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
218     }
219     return p;
220 }
221
222 const char *z_HTTP_errmsg(int code)
223 {
224     if (code == 200)
225         return "OK";
226     else if (code == 400)
227         return "Bad Request";
228     else if (code == 404)
229         return "Not Found";
230     else if (code == 405)
231         return "Method Not Allowed";
232     else if (code == 500)
233         return "Internal Error";
234     else
235         return "Unknown Error";
236 }
237
238 int z_GDU (ODR o, Z_GDU **p, int opt, const char *name)
239 {
240     if (o->direction == ODR_DECODE) {
241         *p = (Z_GDU *) odr_malloc(o, sizeof(**p));
242         if (o->size > 10 && !memcmp(o->buf, "HTTP/", 5))
243         {
244             int i, po;
245             Z_HTTP_Response *hr;
246             (*p)->which = Z_GDU_HTTP_Response;
247
248             hr = (*p)->u.HTTP_Response = (Z_HTTP_Response *)
249                 odr_malloc(o, sizeof(*hr));
250             po = i = 5;
251             while (i < o->size-2 && o->buf[i] != ' ' && o->buf[i] != '\r')
252                 i++;
253             hr->version = (char *) odr_malloc(o, i - po + 1);
254             if (i - po)
255                 memcpy(hr->version, o->buf + po, i - po);
256             hr->version[i-po] = 0;
257             if (o->buf[i] != ' ')
258             {
259                 o->error = OHTTP;
260                 return 0;
261             }
262             i++;
263             hr->code = 0;
264             while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9')
265             {
266                 hr->code = hr->code*10 + (o->buf[i] - '0');
267                 i++;
268             }
269             while (i < o->size-1 && o->buf[i] != '\r')
270                 i++;
271             return decode_headers_content(o, i, &hr->headers,
272                                           &hr->content_buf, &hr->content_len);            
273         }
274         else if (o->size > 5 &&
275             o->buf[0] >= 0x20 && o->buf[0] < 0x7f
276             && o->buf[1] >= 0x20 && o->buf[1] < 0x7f
277             && o->buf[2] >= 0x20 && o->buf[2] < 0x7f
278             && o->buf[3] >= 0x20 && o->buf[3] < 0x7f)
279         {
280             int i, po;
281             Z_HTTP_Request *hr;
282
283             (*p)->which = Z_GDU_HTTP_Request;
284             hr = (*p)->u.HTTP_Request = 
285                 (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
286
287             /* method .. */
288             for (i = 0; o->buf[i] != ' '; i++)
289                 if (i >= o->size-5 || i > 30)
290                 {
291                     o->error = OHTTP;
292                     return 0;
293                 }
294             hr->method = (char *) odr_malloc(o, i+1);
295             memcpy (hr->method, o->buf, i);
296             hr->method[i] = '\0';
297             /* path */
298             po = i+1;
299             for (i = po; o->buf[i] != ' '; i++)
300                 if (i >= o->size-5)
301                 {
302                     o->error = OHTTP;
303                     return 0;
304                 }
305             hr->path = (char *) odr_malloc(o, i - po + 1);
306             memcpy (hr->path, o->buf+po, i - po);
307             hr->path[i - po] = '\0';
308             /* HTTP version */
309             i++;
310             if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5))
311             {
312                 o->error = OHTTP;
313                 return 0;
314             }
315             i+= 5;
316             po = i;
317             while (o->buf[i] != '\r')
318             {
319                 if (i >= o->size-1)
320                 {
321                     o->error = OHTTP;
322                     return 0;
323                 }
324                 i++;
325             }
326             hr->version = (char *) odr_malloc(o, i - po + 1);
327             memcpy(hr->version, o->buf + po, i - po);
328             hr->version[i - po] = '\0';
329             /* headers */
330             return decode_headers_content(o, i, &hr->headers,
331                                           &hr->content_buf, &hr->content_len);
332
333         }
334         else
335         {
336             (*p)->which = Z_GDU_Z3950;
337             return z_APDU(o, &(*p)->u.z3950, opt, 0);
338         }
339     }
340     else /* ENCODE or PRINT */
341     {
342         int top0 = o->top;
343         char sbuf[80];
344         Z_HTTP_Header *h;
345         switch((*p)->which)
346         {
347         case Z_GDU_HTTP_Response:
348             sprintf(sbuf, "HTTP/%s %d %s\r\n", (*p)->u.HTTP_Response->version,
349                     (*p)->u.HTTP_Response->code,
350                     z_HTTP_errmsg((*p)->u.HTTP_Response->code));
351             odr_write(o, (unsigned char *) sbuf, strlen(sbuf));
352             /* apply Content-Length if not already applied */
353             if (!z_HTTP_header_lookup((*p)->u.HTTP_Response->headers,
354                                       "Content-Length"))
355             {
356                 char lstr[60];
357                 sprintf(lstr, "Content-Length: %d\r\n",
358                         (*p)->u.HTTP_Response->content_len);
359                 odr_write(o, (unsigned char *) lstr, strlen(lstr));
360             }
361             for (h = (*p)->u.HTTP_Response->headers; h; h = h->next)
362             {
363                 odr_write(o, (unsigned char *) h->name, strlen(h->name));
364                 odr_write(o, (unsigned char *) ": ", 2);
365                 odr_write(o, (unsigned char *) h->value, strlen(h->value));
366                 odr_write(o, (unsigned char *) "\r\n", 2);
367             }
368             odr_write(o, (unsigned char *) "\r\n", 2);
369             if ((*p)->u.HTTP_Response->content_buf)
370                 odr_write(o, (unsigned char *) 
371                           (*p)->u.HTTP_Response->content_buf,
372                           (*p)->u.HTTP_Response->content_len);
373             if (o->direction == ODR_PRINT)
374             {
375                 fprintf(o->print, "-- HTTP response:\n%.*s\n", o->top - top0,
376                         o->buf + top0);
377                 fprintf(o->print, "-- \n");
378             }
379             break;
380         case Z_GDU_HTTP_Request:
381             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->method,
382                       strlen((*p)->u.HTTP_Request->method));
383             odr_write(o, (unsigned char *) " ", 1);
384             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->path,
385                       strlen((*p)->u.HTTP_Request->path));
386             odr_write(o, (unsigned char *) " HTTP/", 6);
387             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->version,
388                       strlen((*p)->u.HTTP_Request->version));
389             odr_write(o, (unsigned char *) "\r\n", 2);
390             if ((*p)->u.HTTP_Request->content_len &&
391                 !z_HTTP_header_lookup((*p)->u.HTTP_Request->headers,
392                                       "Content-Length"))
393             {
394                 char lstr[60];
395                 sprintf(lstr, "Content-Length: %d\r\n",
396                         (*p)->u.HTTP_Request->content_len);
397                 odr_write(o, (unsigned char *) lstr, strlen(lstr));
398             }
399             for (h = (*p)->u.HTTP_Request->headers; h; h = h->next)
400             {
401                 odr_write(o, (unsigned char *) h->name, strlen(h->name));
402                 odr_write(o, (unsigned char *) ": ", 2);
403                 odr_write(o, (unsigned char *) h->value, strlen(h->value));
404                 odr_write(o, (unsigned char *) "\r\n", 2);
405             }
406             odr_write(o, (unsigned char *) "\r\n", 2);
407             if ((*p)->u.HTTP_Request->content_buf)
408                 odr_write(o, (unsigned char *)
409                           (*p)->u.HTTP_Request->content_buf,
410                           (*p)->u.HTTP_Request->content_len);
411             if (o->direction == ODR_PRINT)
412             {
413                 fprintf(o->print, "-- HTTP request:\n%.*s\n", o->top - top0,
414                         o->buf + top0);
415                 fprintf(o->print, "-- \n");
416             }
417             break;
418         case Z_GDU_Z3950:
419             return z_APDU(o, &(*p)->u.z3950, opt, 0);
420         }
421     }
422     return 1;
423 }
424