Update source headers for 2008. Omit CVS ID keyword subst.
[yaz-moved-to-github.git] / src / http.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2008 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file http.c
8  * \brief Implements HTTP decoding
9  */
10
11 #include <ctype.h>
12 #include <yaz/odr.h>
13 #include <yaz/yaz-version.h>
14 #include <yaz/yaz-iconv.h>
15 #include <yaz/zgdu.h>
16
17 #ifdef WIN32
18 #define strncasecmp _strnicmp
19 #define strcasecmp _stricmp
20 #endif
21  
22
23 /*
24  * This function's counterpart, yaz_base64decode(), is in srwutil.c.
25  * I feel bad that they're not together, but each function is only
26  * needed in one place, and those places are not together.  Maybe one
27  * day we'll move them into a new httputil.c, and declare them in a
28  * corresponding httputil.h
29  */
30 static void yaz_base64encode(const char *in, char *out)
31 {
32     static char encoding[] =
33         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
34     unsigned char buf[3];
35     long n;
36
37     while (*in != 0) {
38         char *pad = 0;
39         buf[0] = in[0];
40         buf[1] = in[1];
41         if (in[1] == 0) {
42             buf[2] = 0;
43             pad = "==";
44         } else {
45             buf[2] = in[2];
46             if (in[2] == 0)
47                 pad = "=";
48         }
49
50         /* Treat three eight-bit numbers as on 24-bit number */
51         n = (buf[0] << 16) + (buf[1] << 8) + buf[2];
52
53         /* Write the six-bit chunks out as four encoded characters */
54         *out++ = encoding[(n >> 18) & 63];
55         *out++ = encoding[(n >> 12) & 63];
56         if (in[1] != 0)
57             *out++ = encoding[(n >> 6) & 63];
58         if (in[1] != 0 && in[2] != 0)
59             *out++ = encoding[n & 63];
60
61         if (pad != 0) {
62             while (*pad != 0)
63                 *out++ = *pad++;
64             break;
65         }
66         in += 3;
67     }
68
69     *out++ = 0;
70 }
71
72
73 static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers,
74                                   char **content_buf, int *content_len)
75 {
76     int i = off;
77     int chunked = 0;
78
79     *headers = 0;
80     while (i < o->size-1 && o->buf[i] == '\n')
81     {
82         int po;
83         i++;
84         if (o->buf[i] == '\r' && i < o->size-1 && o->buf[i+1] == '\n')
85         {
86             i++;
87             break;
88         }
89         if (o->buf[i] == '\n')
90             break;
91         for (po = i; ; i++)
92         {
93             if (i == o->size)
94             {
95                 o->error = OHTTP;
96                 return 0;
97             }
98             else if (o->buf[i] == ':')
99                 break;
100         }
101         *headers = (Z_HTTP_Header *) odr_malloc(o, sizeof(**headers));
102         (*headers)->name = (char*) odr_malloc(o, i - po + 1);
103         memcpy ((*headers)->name, o->buf + po, i - po);
104         (*headers)->name[i - po] = '\0';
105         i++;
106         while (i < o->size-1 && o->buf[i] == ' ')
107             i++;
108         for (po = i; i < o->size-1 && !strchr("\r\n", o->buf[i]); i++)
109             ;
110         
111         (*headers)->value = (char*) odr_malloc(o, i - po + 1);
112         memcpy ((*headers)->value, o->buf + po, i - po);
113         (*headers)->value[i - po] = '\0';
114         
115         if (!strcasecmp((*headers)->name, "Transfer-Encoding")
116             &&
117             !strcasecmp((*headers)->value, "chunked"))
118             chunked = 1;
119         headers = &(*headers)->next;
120         if (i < o->size-1 && o->buf[i] == '\r')
121             i++;
122     }
123     *headers = 0;
124     if (o->buf[i] != '\n')
125     {
126         o->error = OHTTP;
127         return 0;
128     }
129     i++;
130
131     if (chunked)
132     {
133         int off = 0;
134         
135         /* we know buffer will be smaller than o->size - i*/
136         *content_buf = (char*) odr_malloc(o, o->size - i);  
137         
138         while (1)
139         {
140             /* chunk length .. */
141             int chunk_len = 0;
142             for (; i  < o->size-2; i++)
143                 if (isdigit(o->buf[i]))
144                     chunk_len = chunk_len * 16 + 
145                         (o->buf[i] - '0');
146                 else if (isupper(o->buf[i]))
147                     chunk_len = chunk_len * 16 + 
148                         (o->buf[i] - ('A'-10));
149                 else if (islower(o->buf[i]))
150                     chunk_len = chunk_len * 16 + 
151                         (o->buf[i] - ('a'-10));
152                 else
153                     break;
154             /* chunk extension ... */
155             while (o->buf[i] != '\r' && o->buf[i+1] != '\n')
156             {
157                 if (i >= o->size-2)
158                 {
159                     o->error = OHTTP;
160                     return 0;
161                 }
162                 i++;
163             }
164             i += 2;  /* skip CRLF */
165             if (chunk_len == 0)
166                 break;
167             if (chunk_len < 0 || off + chunk_len > o->size)
168             {
169                 o->error = OHTTP;
170                 return 0;
171             }
172             /* copy chunk .. */
173             memcpy (*content_buf + off, o->buf + i, chunk_len);
174             i += chunk_len + 2; /* skip chunk+CRLF */
175             off += chunk_len;
176         }
177         if (!off)
178             *content_buf = 0;
179         *content_len = off;
180     }
181     else
182     {
183         if (i > o->size)
184         {
185             o->error = OHTTP;
186             return 0;
187         }
188         else if (i == o->size)
189         {
190             *content_buf = 0;
191             *content_len = 0;
192         }
193         else 
194         {
195             *content_len = o->size - i;
196             *content_buf = (char*) odr_malloc(o, *content_len + 1);
197             memcpy(*content_buf, o->buf + i, *content_len);
198             (*content_buf)[*content_len] = '\0';
199         }
200     }
201     return 1;
202 }
203
204 void z_HTTP_header_add_content_type(ODR o, Z_HTTP_Header **hp,
205                                     const char *content_type,
206                                     const char *charset)
207 {
208     const char *l = "Content-Type";
209     if (charset)
210     {
211         char *ctype = (char *)
212             odr_malloc(o, strlen(content_type)+strlen(charset) + 15);
213         sprintf(ctype, "%s; charset=%s", content_type, charset);
214         z_HTTP_header_add(o, hp, l, ctype);
215     }
216     else
217         z_HTTP_header_add(o, hp, l, content_type);
218
219 }
220
221 /*
222  * HTTP Basic authentication is described at:
223  * http://tools.ietf.org/html/rfc1945#section-11.1
224  */
225 void z_HTTP_header_add_basic_auth(ODR o, Z_HTTP_Header **hp,
226                                   const char *username, const char *password)
227 {
228     char *tmp, *buf;
229     int len;
230
231     if (username == 0)
232         return;
233
234     len = strlen(username) + strlen(password);
235     tmp = (char *) odr_malloc(o, len+2);
236     sprintf(tmp, "%s:%s", username, password);
237     buf = (char *) odr_malloc(o, (len+1) * 8/6 + 12);
238     strcpy(buf, "Basic ");
239     yaz_base64encode(tmp, &buf[strlen(buf)]);
240     z_HTTP_header_add(o, hp, "Authorization", buf);
241 }
242
243
244 void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n,
245                        const char *v)
246 {
247     while (*hp)
248         hp = &(*hp)->next;
249     *hp = (Z_HTTP_Header *) odr_malloc(o, sizeof(**hp));
250     (*hp)->name = odr_strdup(o, n);
251     (*hp)->value = odr_strdup(o, v);
252     (*hp)->next = 0;
253 }
254
255 const char *z_HTTP_header_lookup(const Z_HTTP_Header *hp, const char *n)
256 {
257     for (; hp; hp = hp->next)
258         if (!yaz_matchstr(hp->name, n))
259             return hp->value;
260     return 0;
261 }
262
263
264 Z_GDU *z_get_HTTP_Request(ODR o)
265 {
266     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
267     Z_HTTP_Request *hreq;
268
269     p->which = Z_GDU_HTTP_Request;
270     p->u.HTTP_Request = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hreq));
271     hreq = p->u.HTTP_Request;
272     hreq->headers = 0;
273     hreq->content_len = 0;
274     hreq->content_buf = 0;
275     hreq->version = "1.1";
276     hreq->method = "POST";
277     hreq->path = "/";
278     z_HTTP_header_add(o, &hreq->headers, "User-Agent", "YAZ/" YAZ_VERSION);
279     return p;
280 }
281
282
283 Z_GDU *z_get_HTTP_Request_host_path(ODR odr,
284                                     const char *host,
285                                     const char *path)
286 {
287     Z_GDU *p = z_get_HTTP_Request(odr);
288
289     p->u.HTTP_Request->path = odr_strdup(odr, path);
290
291     if (host)
292     {
293         const char *cp0 = strstr(host, "://");
294         const char *cp1 = 0;
295         if (cp0)
296             cp0 = cp0+3;
297         else
298             cp0 = host;
299
300         cp1 = strchr(cp0, '/');
301         if (!cp1)
302             cp1 = cp0+strlen(cp0);
303
304         if (cp0 && cp1)
305         {
306             char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
307             memcpy (h, cp0, cp1 - cp0);
308             h[cp1-cp0] = '\0';
309             z_HTTP_header_add(odr, &p->u.HTTP_Request->headers,
310                               "Host", h);
311         }
312     }
313     return p;
314 }
315
316
317 Z_GDU *z_get_HTTP_Response(ODR o, int code)
318 {
319     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
320     Z_HTTP_Response *hres;
321
322     p->which = Z_GDU_HTTP_Response;
323     p->u.HTTP_Response = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hres));
324     hres = p->u.HTTP_Response;
325     hres->headers = 0;
326     hres->content_len = 0;
327     hres->content_buf = 0;
328     hres->code = code;
329     hres->version = "1.1";
330     z_HTTP_header_add(o, &hres->headers, "Server",
331                       "YAZ/" YAZ_VERSION);
332     if (code != 200)
333     {
334         hres->content_buf = (char*) odr_malloc(o, 400);
335         sprintf (hres->content_buf, 
336                  "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
337                  "<HTML>\n"
338                  " <HEAD>\n"
339                  "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
340                  " </HEAD>\n"
341                  " <BODY>\n"
342                  "  <P><A HREF=\"http://www.indexdata.dk/yaz/\">YAZ</A> " 
343                  YAZ_VERSION "</P>\n"
344                  "  <P>Error: %d</P>\n"
345                  "  <P>Description: %.50s</P>\n"
346                  " </BODY>\n"
347                  "</HTML>\n",
348                  code, z_HTTP_errmsg(code));
349         hres->content_len = strlen(hres->content_buf);
350         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
351     }
352     return p;
353 }
354
355 const char *z_HTTP_errmsg(int code)
356 {
357     if (code == 200)
358         return "OK";
359     else if (code == 400)
360         return "Bad Request";
361     else if (code == 404)
362         return "Not Found";
363     else if (code == 405)
364         return "Method Not Allowed";
365     else if (code == 500)
366         return "Internal Error";
367     else
368         return "Unknown Error";
369 }
370
371 int yaz_decode_http_response(ODR o, Z_HTTP_Response **hr_p)
372 {
373     int i, po;
374     Z_HTTP_Response *hr = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hr));
375     
376     *hr_p = hr;
377     hr->content_buf = 0;
378     hr->content_len = 0;
379     
380     po = i = 5;
381     while (i < o->size-2 && !strchr(" \r\n", o->buf[i]))
382         i++;
383     hr->version = (char *) odr_malloc(o, i - po + 1);
384     if (i - po)
385         memcpy(hr->version, o->buf + po, i - po);
386     hr->version[i-po] = 0;
387     if (o->buf[i] != ' ')
388     {
389         o->error = OHTTP;
390         return 0;
391     }
392     i++;
393     hr->code = 0;
394     while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9')
395     {
396         hr->code = hr->code*10 + (o->buf[i] - '0');
397         i++;
398     }
399     while (i < o->size-1 && o->buf[i] != '\n')
400         i++;
401     return decode_headers_content(o, i, &hr->headers,
402                                   &hr->content_buf, &hr->content_len);            
403 }
404
405 int yaz_decode_http_request(ODR o, Z_HTTP_Request **hr_p)
406 {
407     int i, po;
408     Z_HTTP_Request *hr = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
409     
410     *hr_p = hr;
411     
412     /* method .. */
413     for (i = 0; o->buf[i] != ' '; i++)
414         if (i >= o->size-5 || i > 30)
415         {
416             o->error = OHTTP;
417             return 0;
418         }
419     hr->method = (char *) odr_malloc(o, i+1);
420     memcpy (hr->method, o->buf, i);
421     hr->method[i] = '\0';
422     /* path */
423     po = i+1;
424     for (i = po; o->buf[i] != ' '; i++)
425         if (i >= o->size-5)
426         {
427             o->error = OHTTP;
428             return 0;
429         }
430     hr->path = (char *) odr_malloc(o, i - po + 1);
431     memcpy (hr->path, o->buf+po, i - po);
432     hr->path[i - po] = '\0';
433     /* HTTP version */
434     i++;
435     if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5))
436     {
437         o->error = OHTTP;
438         return 0;
439     }
440     i+= 5;
441     po = i;
442     while (i < o->size && !strchr("\r\n", o->buf[i]))
443         i++;
444     hr->version = (char *) odr_malloc(o, i - po + 1);
445     memcpy(hr->version, o->buf + po, i - po);
446     hr->version[i - po] = '\0';
447     /* headers */
448     if (i < o->size-1 && o->buf[i] == '\r')
449         i++;
450     if (o->buf[i] != '\n')
451     {
452         o->error = OHTTP;
453         return 0;
454     }
455     return decode_headers_content(o, i, &hr->headers,
456                                   &hr->content_buf, &hr->content_len);
457 }
458
459 int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr)
460 {
461     char sbuf[80];
462     Z_HTTP_Header *h;
463     int top0 = o->top;
464
465     sprintf(sbuf, "HTTP/%s %d %s\r\n", hr->version,
466             hr->code,
467             z_HTTP_errmsg(hr->code));
468     odr_write(o, (unsigned char *) sbuf, strlen(sbuf));
469     /* apply Content-Length if not already applied */
470     if (!z_HTTP_header_lookup(hr->headers,
471                               "Content-Length"))
472     {
473         char lstr[60];
474         sprintf(lstr, "Content-Length: %d\r\n",
475                 hr->content_len);
476         odr_write(o, (unsigned char *) lstr, strlen(lstr));
477     }
478     for (h = hr->headers; h; h = h->next)
479     {
480         odr_write(o, (unsigned char *) h->name, strlen(h->name));
481         odr_write(o, (unsigned char *) ": ", 2);
482         odr_write(o, (unsigned char *) h->value, strlen(h->value));
483         odr_write(o, (unsigned char *) "\r\n", 2);
484     }
485     odr_write(o, (unsigned char *) "\r\n", 2);
486     if (hr->content_buf)
487         odr_write(o, (unsigned char *) 
488                   hr->content_buf,
489                   hr->content_len);
490     if (o->direction == ODR_PRINT)
491     {
492         odr_printf(o, "-- HTTP response:\n%.*s\n", o->top - top0,
493                    o->buf + top0);
494         odr_printf(o, "-- \n");
495     }
496     return 1;
497 }
498
499 int yaz_encode_http_request(ODR o, Z_HTTP_Request *hr)
500 {
501     Z_HTTP_Header *h;
502     int top0 = o->top;
503
504     odr_write(o, (unsigned char *) hr->method,
505               strlen(hr->method));
506     odr_write(o, (unsigned char *) " ", 1);
507     odr_write(o, (unsigned char *) hr->path,
508               strlen(hr->path));
509     odr_write(o, (unsigned char *) " HTTP/", 6);
510     odr_write(o, (unsigned char *) hr->version,
511               strlen(hr->version));
512     odr_write(o, (unsigned char *) "\r\n", 2);
513     if (hr->content_len &&
514         !z_HTTP_header_lookup(hr->headers,
515                               "Content-Length"))
516     {
517         char lstr[60];
518         sprintf(lstr, "Content-Length: %d\r\n",
519                 hr->content_len);
520         odr_write(o, (unsigned char *) lstr, strlen(lstr));
521     }
522     for (h = hr->headers; h; h = h->next)
523     {
524         odr_write(o, (unsigned char *) h->name, strlen(h->name));
525         odr_write(o, (unsigned char *) ": ", 2);
526         odr_write(o, (unsigned char *) h->value, strlen(h->value));
527         odr_write(o, (unsigned char *) "\r\n", 2);
528     }
529     odr_write(o, (unsigned char *) "\r\n", 2);
530     if (hr->content_buf)
531         odr_write(o, (unsigned char *)
532                   hr->content_buf,
533                   hr->content_len);
534     if (o->direction == ODR_PRINT)
535     {
536         odr_printf(o, "-- HTTP request:\n%.*s\n", o->top - top0,
537                    o->buf + top0);
538         odr_printf(o, "-- \n");
539     }
540     return 1;
541 }
542
543 /*
544  * Local variables:
545  * c-basic-offset: 4
546  * indent-tabs-mode: nil
547  * End:
548  * vim: shiftwidth=4 tabstop=8 expandtab
549  */
550