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