Initialize content_{len,buf} always
[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.8 2004-02-25 10:37:02 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             hr->content_buf = 0;
251             hr->content_len = 0;
252
253             po = i = 5;
254             while (i < o->size-2 && o->buf[i] != ' ' && o->buf[i] != '\r')
255                 i++;
256             hr->version = (char *) odr_malloc(o, i - po + 1);
257             if (i - po)
258                 memcpy(hr->version, o->buf + po, i - po);
259             hr->version[i-po] = 0;
260             if (o->buf[i] != ' ')
261             {
262                 o->error = OHTTP;
263                 return 0;
264             }
265             i++;
266             hr->code = 0;
267             while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9')
268             {
269                 hr->code = hr->code*10 + (o->buf[i] - '0');
270                 i++;
271             }
272             while (i < o->size-1 && o->buf[i] != '\r')
273                 i++;
274             return decode_headers_content(o, i, &hr->headers,
275                                           &hr->content_buf, &hr->content_len);            
276         }
277         else if (o->size > 5 &&
278             o->buf[0] >= 0x20 && o->buf[0] < 0x7f
279             && o->buf[1] >= 0x20 && o->buf[1] < 0x7f
280             && o->buf[2] >= 0x20 && o->buf[2] < 0x7f
281             && o->buf[3] >= 0x20 && o->buf[3] < 0x7f)
282         {
283             int i, po;
284             Z_HTTP_Request *hr;
285
286             (*p)->which = Z_GDU_HTTP_Request;
287             hr = (*p)->u.HTTP_Request = 
288                 (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
289
290             /* method .. */
291             for (i = 0; o->buf[i] != ' '; i++)
292                 if (i >= o->size-5 || i > 30)
293                 {
294                     o->error = OHTTP;
295                     return 0;
296                 }
297             hr->method = (char *) odr_malloc(o, i+1);
298             memcpy (hr->method, o->buf, i);
299             hr->method[i] = '\0';
300             /* path */
301             po = i+1;
302             for (i = po; o->buf[i] != ' '; i++)
303                 if (i >= o->size-5)
304                 {
305                     o->error = OHTTP;
306                     return 0;
307                 }
308             hr->path = (char *) odr_malloc(o, i - po + 1);
309             memcpy (hr->path, o->buf+po, i - po);
310             hr->path[i - po] = '\0';
311             /* HTTP version */
312             i++;
313             if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5))
314             {
315                 o->error = OHTTP;
316                 return 0;
317             }
318             i+= 5;
319             po = i;
320             while (o->buf[i] != '\r')
321             {
322                 if (i >= o->size-1)
323                 {
324                     o->error = OHTTP;
325                     return 0;
326                 }
327                 i++;
328             }
329             hr->version = (char *) odr_malloc(o, i - po + 1);
330             memcpy(hr->version, o->buf + po, i - po);
331             hr->version[i - po] = '\0';
332             /* headers */
333             return decode_headers_content(o, i, &hr->headers,
334                                           &hr->content_buf, &hr->content_len);
335
336         }
337         else
338         {
339             (*p)->which = Z_GDU_Z3950;
340             return z_APDU(o, &(*p)->u.z3950, opt, 0);
341         }
342     }
343     else /* ENCODE or PRINT */
344     {
345         int top0 = o->top;
346         char sbuf[80];
347         Z_HTTP_Header *h;
348         switch((*p)->which)
349         {
350         case Z_GDU_HTTP_Response:
351             sprintf(sbuf, "HTTP/%s %d %s\r\n", (*p)->u.HTTP_Response->version,
352                     (*p)->u.HTTP_Response->code,
353                     z_HTTP_errmsg((*p)->u.HTTP_Response->code));
354             odr_write(o, (unsigned char *) sbuf, strlen(sbuf));
355             /* apply Content-Length if not already applied */
356             if (!z_HTTP_header_lookup((*p)->u.HTTP_Response->headers,
357                                       "Content-Length"))
358             {
359                 char lstr[60];
360                 sprintf(lstr, "Content-Length: %d\r\n",
361                         (*p)->u.HTTP_Response->content_len);
362                 odr_write(o, (unsigned char *) lstr, strlen(lstr));
363             }
364             for (h = (*p)->u.HTTP_Response->headers; h; h = h->next)
365             {
366                 odr_write(o, (unsigned char *) h->name, strlen(h->name));
367                 odr_write(o, (unsigned char *) ": ", 2);
368                 odr_write(o, (unsigned char *) h->value, strlen(h->value));
369                 odr_write(o, (unsigned char *) "\r\n", 2);
370             }
371             odr_write(o, (unsigned char *) "\r\n", 2);
372             if ((*p)->u.HTTP_Response->content_buf)
373                 odr_write(o, (unsigned char *) 
374                           (*p)->u.HTTP_Response->content_buf,
375                           (*p)->u.HTTP_Response->content_len);
376             if (o->direction == ODR_PRINT)
377             {
378                 fprintf(o->print, "-- HTTP response:\n%.*s\n", o->top - top0,
379                         o->buf + top0);
380                 fprintf(o->print, "-- \n");
381             }
382             break;
383         case Z_GDU_HTTP_Request:
384             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->method,
385                       strlen((*p)->u.HTTP_Request->method));
386             odr_write(o, (unsigned char *) " ", 1);
387             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->path,
388                       strlen((*p)->u.HTTP_Request->path));
389             odr_write(o, (unsigned char *) " HTTP/", 6);
390             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->version,
391                       strlen((*p)->u.HTTP_Request->version));
392             odr_write(o, (unsigned char *) "\r\n", 2);
393             if ((*p)->u.HTTP_Request->content_len &&
394                 !z_HTTP_header_lookup((*p)->u.HTTP_Request->headers,
395                                       "Content-Length"))
396             {
397                 char lstr[60];
398                 sprintf(lstr, "Content-Length: %d\r\n",
399                         (*p)->u.HTTP_Request->content_len);
400                 odr_write(o, (unsigned char *) lstr, strlen(lstr));
401             }
402             for (h = (*p)->u.HTTP_Request->headers; h; h = h->next)
403             {
404                 odr_write(o, (unsigned char *) h->name, strlen(h->name));
405                 odr_write(o, (unsigned char *) ": ", 2);
406                 odr_write(o, (unsigned char *) h->value, strlen(h->value));
407                 odr_write(o, (unsigned char *) "\r\n", 2);
408             }
409             odr_write(o, (unsigned char *) "\r\n", 2);
410             if ((*p)->u.HTTP_Request->content_buf)
411                 odr_write(o, (unsigned char *)
412                           (*p)->u.HTTP_Request->content_buf,
413                           (*p)->u.HTTP_Request->content_len);
414             if (o->direction == ODR_PRINT)
415             {
416                 fprintf(o->print, "-- HTTP request:\n%.*s\n", o->top - top0,
417                         o->buf + top0);
418                 fprintf(o->print, "-- \n");
419             }
420             break;
421         case Z_GDU_Z3950:
422             return z_APDU(o, &(*p)->u.z3950, opt, 0);
423         }
424     }
425     return 1;
426 }
427