Merge branch 'master' into sru_2_0
[yaz-moved-to-github.git] / src / http.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file http.c
7  * \brief Implements HTTP decoding
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include "odr-priv.h"
14 #include <yaz/yaz-version.h>
15 #include <yaz/yaz-iconv.h>
16 #include <yaz/matchstr.h>
17 #include <yaz/zgdu.h>
18 #include <yaz/base64.h>
19
20 static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers,
21                                   char **content_buf, int *content_len)
22 {
23     int i = off;
24     int chunked = 0;
25     const char *buf = o->op->buf;
26     int size = o->op->size;
27
28     *headers = 0;
29     while (i < size-1 && buf[i] == '\n')
30     {
31         int po;
32         i++;
33         if (buf[i] == '\r' && i < size-1 && buf[i+1] == '\n')
34         {
35             i++;
36             break;
37         }
38         if (buf[i] == '\n')
39             break;
40         for (po = i; ; i++)
41         {
42             if (i == size)
43             {
44                 o->error = OHTTP;
45                 return 0;
46             }
47             else if (buf[i] == ':')
48                 break;
49         }
50         *headers = (Z_HTTP_Header *) odr_malloc(o, sizeof(**headers));
51         (*headers)->name = (char*) odr_malloc(o, i - po + 1);
52         memcpy ((*headers)->name, buf + po, i - po);
53         (*headers)->name[i - po] = '\0';
54         i++;
55         while (i < size-1 && buf[i] == ' ')
56             i++;
57         for (po = i; i < size-1 && !strchr("\r\n", buf[i]); i++)
58             ;
59
60         (*headers)->value = (char*) odr_malloc(o, i - po + 1);
61         memcpy ((*headers)->value, buf + po, i - po);
62         (*headers)->value[i - po] = '\0';
63
64         if (!yaz_strcasecmp((*headers)->name, "Transfer-Encoding")
65             &&
66             !yaz_strcasecmp((*headers)->value, "chunked"))
67             chunked = 1;
68         headers = &(*headers)->next;
69         if (i < size-1 && buf[i] == '\r')
70             i++;
71     }
72     *headers = 0;
73     if (buf[i] != '\n')
74     {
75         o->error = OHTTP;
76         return 0;
77     }
78     i++;
79
80     if (chunked)
81     {
82         int off = 0;
83
84         /* we know buffer will be smaller than o->size - i*/
85         *content_buf = (char*) odr_malloc(o, size - i);
86
87         while (1)
88         {
89             /* chunk length .. */
90             int chunk_len = 0;
91             for (; i  < size-2; i++)
92                 if (yaz_isdigit(buf[i]))
93                     chunk_len = chunk_len * 16 +
94                         (buf[i] - '0');
95                 else if (yaz_isupper(buf[i]))
96                     chunk_len = chunk_len * 16 +
97                         (buf[i] - ('A'-10));
98                 else if (yaz_islower(buf[i]))
99                     chunk_len = chunk_len * 16 +
100                         (buf[i] - ('a'-10));
101                 else
102                     break;
103             /* chunk extension ... */
104             while (buf[i] != '\r' && buf[i+1] != '\n')
105             {
106                 if (i >= size-2)
107                 {
108                     o->error = OHTTP;
109                     return 0;
110                 }
111                 i++;
112             }
113             i += 2;  /* skip CRLF */
114             if (chunk_len == 0)
115                 break;
116             if (chunk_len < 0 || off + chunk_len > size)
117             {
118                 o->error = OHTTP;
119                 return 0;
120             }
121             /* copy chunk .. */
122             memcpy (*content_buf + off, buf + i, chunk_len);
123             i += chunk_len + 2; /* skip chunk+CRLF */
124             off += chunk_len;
125         }
126         if (!off)
127             *content_buf = 0;
128         *content_len = off;
129     }
130     else
131     {
132         if (i > size)
133         {
134             o->error = OHTTP;
135             return 0;
136         }
137         else if (i == size)
138         {
139             *content_buf = 0;
140             *content_len = 0;
141         }
142         else
143         {
144             *content_len = size - i;
145             *content_buf = (char*) odr_malloc(o, *content_len + 1);
146             memcpy(*content_buf, buf + i, *content_len);
147             (*content_buf)[*content_len] = '\0';
148         }
149     }
150     return 1;
151 }
152
153 void z_HTTP_header_add_content_type(ODR o, Z_HTTP_Header **hp,
154                                     const char *content_type,
155                                     const char *charset)
156 {
157     const char *l = "Content-Type";
158     if (charset)
159     {
160         char *ctype = (char *)
161             odr_malloc(o, strlen(content_type)+strlen(charset) + 15);
162         sprintf(ctype, "%s; charset=%s", content_type, charset);
163         z_HTTP_header_add(o, hp, l, ctype);
164     }
165     else
166         z_HTTP_header_add(o, hp, l, content_type);
167
168 }
169
170 /*
171  * HTTP Basic authentication is described at:
172  * http://tools.ietf.org/html/rfc1945#section-11.1
173  */
174 void z_HTTP_header_add_basic_auth(ODR o, Z_HTTP_Header **hp,
175                                   const char *username, const char *password)
176 {
177     char *tmp, *buf;
178     int len;
179
180     if (username == 0)
181         return;
182     if (password == 0)
183         password = "";
184
185     len = strlen(username) + strlen(password);
186     tmp = (char *) odr_malloc(o, len+2);
187     sprintf(tmp, "%s:%s", username, password);
188     buf = (char *) odr_malloc(o, (len+1) * 8/6 + 12);
189     strcpy(buf, "Basic ");
190     yaz_base64encode(tmp, &buf[strlen(buf)]);
191     z_HTTP_header_set(o, hp, "Authorization", buf);
192 }
193
194
195 void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n,
196                        const char *v)
197 {
198     while (*hp)
199         hp = &(*hp)->next;
200     *hp = (Z_HTTP_Header *) odr_malloc(o, sizeof(**hp));
201     (*hp)->name = odr_strdup(o, n);
202     (*hp)->value = odr_strdup(o, v);
203     (*hp)->next = 0;
204 }
205
206 void z_HTTP_header_set(ODR o, Z_HTTP_Header **hp, const char *n,
207                        const char *v)
208 {
209     while (*hp)
210     {
211         if (!yaz_strcasecmp((*hp)->name, n))
212         {
213             (*hp)->value = odr_strdup(o, v);
214             return;
215         }
216         hp = &(*hp)->next;
217     }
218     *hp = (Z_HTTP_Header *) odr_malloc(o, sizeof(**hp));
219     (*hp)->name = odr_strdup(o, n);
220     (*hp)->value = odr_strdup(o, v);
221     (*hp)->next = 0;
222 }
223
224 const char *z_HTTP_header_remove(Z_HTTP_Header **hp, const char *n)
225 {
226     while (*hp)
227     {
228         if (!yaz_strcasecmp((*hp)->name, n))
229         {
230             const char *v = (*hp)->value;
231             *hp = (*hp)->next;
232             return v;
233         }
234         hp = &(*hp)->next;
235     }
236     return 0;
237 }
238
239 const char *z_HTTP_header_lookup(const Z_HTTP_Header *hp, const char *n)
240 {
241     for (; hp; hp = hp->next)
242         if (!yaz_strcasecmp(hp->name, n))
243             return hp->value;
244     return 0;
245 }
246
247
248 Z_GDU *z_get_HTTP_Request(ODR o)
249 {
250     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
251     Z_HTTP_Request *hreq;
252
253     p->which = Z_GDU_HTTP_Request;
254     p->u.HTTP_Request = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hreq));
255     hreq = p->u.HTTP_Request;
256     hreq->headers = 0;
257     hreq->content_len = 0;
258     hreq->content_buf = 0;
259     hreq->version = "1.1";
260     hreq->method = "POST";
261     hreq->path = "/";
262     z_HTTP_header_add(o, &hreq->headers, "User-Agent", "YAZ/" YAZ_VERSION);
263     return p;
264 }
265
266
267 Z_GDU *z_get_HTTP_Request_host_path(ODR odr,
268                                     const char *host,
269                                     const char *path)
270 {
271     Z_GDU *p = z_get_HTTP_Request(odr);
272
273     p->u.HTTP_Request->path = odr_strdup(odr, path);
274
275     if (host)
276     {
277         const char *cp0 = strstr(host, "://");
278         const char *cp1 = 0;
279         if (cp0)
280             cp0 = cp0+3;
281         else
282             cp0 = host;
283
284         cp1 = strchr(cp0, '/');
285         if (!cp1)
286             cp1 = cp0+strlen(cp0);
287
288         if (cp0 && cp1)
289         {
290             char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
291             memcpy (h, cp0, cp1 - cp0);
292             h[cp1-cp0] = '\0';
293             z_HTTP_header_add(odr, &p->u.HTTP_Request->headers,
294                               "Host", h);
295         }
296     }
297     return p;
298 }
299
300 Z_GDU *z_get_HTTP_Request_uri(ODR odr, const char *uri, const char *args,
301                               int use_full_uri)
302 {
303     Z_GDU *p = z_get_HTTP_Request(odr);
304     const char *cp0 = strstr(uri, "://");
305     const char *cp1 = 0;
306     if (cp0)
307         cp0 = cp0+3;
308     else
309         cp0 = uri;
310
311     cp1 = strchr(cp0, '/');
312     if (!cp1)
313         cp1 = cp0+strlen(cp0);
314
315     if (cp0 && cp1)
316     {
317         char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
318         memcpy (h, cp0, cp1 - cp0);
319         h[cp1-cp0] = '\0';
320         z_HTTP_header_add(odr, &p->u.HTTP_Request->headers,
321                           "Host", h);
322     }
323
324     if (!args)
325     {
326         if (*cp1)
327             args = cp1 + 1;
328         else
329             args = "";
330     }
331     p->u.HTTP_Request->path = odr_malloc(odr, cp1 - uri + strlen(args) + 2);
332     if (use_full_uri)
333     {
334         memcpy(p->u.HTTP_Request->path, uri, cp1 - uri);
335         strcpy(p->u.HTTP_Request->path + (cp1 - uri), "/");
336     }
337     else
338         strcpy(p->u.HTTP_Request->path, "/");
339     strcat(p->u.HTTP_Request->path, args);
340     return p;
341 }
342
343 Z_GDU *z_get_HTTP_Response_server(ODR o, int code, const char *details,
344                                   const char *server, const char *server_url)
345 {
346     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
347     Z_HTTP_Response *hres;
348
349     p->which = Z_GDU_HTTP_Response;
350     p->u.HTTP_Response = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hres));
351     hres = p->u.HTTP_Response;
352     hres->headers = 0;
353     hres->content_len = 0;
354     hres->content_buf = 0;
355     hres->code = code;
356     hres->version = "1.1";
357     z_HTTP_header_add(o, &hres->headers, "Server", server);
358     if (code != 200)
359     {
360         const char *http_err = z_HTTP_errmsg(code);
361         size_t sz = 400 + strlen(http_err) + (details ?
362                                               strlen(details) : 0);
363         hres->content_buf = (char*) odr_malloc(o, sz);
364         sprintf(hres->content_buf,
365                 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
366                 " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
367                 "<HTML>\n"
368                 " <HEAD>\n"
369                 "  <TITLE>%s</TITLE>\n"
370                 " </HEAD>\n"
371                 " <BODY>\n"
372                 "  <P><A HREF=\"%s\">%s</A></P>\n"
373                 "  <P>Error: %d</P>\n"
374                 "  <P>Description: %s</P>\n", server, server_url, server,
375                 code, http_err);
376         if (details)
377         {
378             sprintf(hres->content_buf + strlen(hres->content_buf),
379                     "<P>Details: %s</P>\n", details);
380         }
381         sprintf(hres->content_buf + strlen(hres->content_buf),
382                 " </BODY>\n"
383                 "</HTML>\n");
384         hres->content_len = strlen(hres->content_buf);
385         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
386     }
387     return p;
388 }
389
390 Z_GDU *z_get_HTTP_Response_details(ODR o, int code, const char *details)
391 {
392     return z_get_HTTP_Response_server(o, code, details, "YAZ/" YAZ_VERSION,
393                                       "http://www.indexdata.com/yaz");
394 }
395
396 Z_GDU *z_get_HTTP_Response(ODR o, int code)
397 {
398     return z_get_HTTP_Response_details(o, code, 0);
399 }
400
401 const char *z_HTTP_errmsg(int code)
402 {
403     switch (code)
404     {
405     case 100:
406         return "Continue";
407     case 101:
408         return "Switching Protocols";
409     case 200:
410         return "OK";
411     case 201:
412         return "Created";
413     case 202:
414         return "Accepted";
415     case 203:
416         return "Non-Authoritative Information";
417     case 204:
418         return "No Content";
419     case 205:
420         return "Reset Content";
421     case 206:
422         return "Partial Content";
423     case 300:
424         return "Multiple Choices";
425     case 301:
426         return "Moved Permenently";
427     case 302:
428         return "Found";
429     case 303:
430         return "See Other";
431     case 304:
432         return "Not Modified";
433     case 305:
434         return "Use Proxy";
435     case 307:
436         return "Temporary Redirect";
437     case 400:
438         return "Bad Request";
439     case 404:
440         return "Not Found";
441     case 405:
442         return "Method Not Allowed";
443     case 406:
444         return "Not Acceptable";
445     case 407:
446         return "Proxy Authentication Required";
447     case 408:
448         return "Request Timeout";
449     case 409:
450         return "Conflict";
451     case 410:
452         return "Gone";
453     case 411:
454         return "Length Required";
455     case 412:
456         return "Precondition Failed";
457     case 413:
458         return "Request Entity Too Large";
459     case 414:
460         return "Request-URI Too Long";
461     case 415:
462         return "Unsupported Media Type";
463     case 416:
464         return "Requested Range Not Satisfiable";
465     case 417:
466         return "Expectation Failed";
467     case 500:
468         return "Internal Error";
469     case 501:
470         return "Not Implemented";
471     case 502:
472         return "Bad Gateway";
473     case 503:
474         return "Service Unavailable";
475     case 504:
476         return "Gateway Timeout";
477     case 505:
478         return "HTTP Version Not Supported";
479     default:
480         return "Unknown Error";
481     }
482 }
483
484 int yaz_decode_http_response(ODR o, Z_HTTP_Response **hr_p)
485 {
486     int i, po;
487     Z_HTTP_Response *hr = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hr));
488     const char *buf = o->op->buf;
489     int size = o->op->size;
490
491     *hr_p = hr;
492     hr->content_buf = 0;
493     hr->content_len = 0;
494
495     po = i = 5;
496     while (i < size-2 && !strchr(" \r\n", buf[i]))
497         i++;
498     hr->version = (char *) odr_malloc(o, i - po + 1);
499     if (i - po)
500         memcpy(hr->version, buf + po, i - po);
501     hr->version[i-po] = 0;
502     if (buf[i] != ' ')
503     {
504         o->error = OHTTP;
505         return 0;
506     }
507     i++;
508     hr->code = 0;
509     while (i < size-2 && buf[i] >= '0' && buf[i] <= '9')
510     {
511         hr->code = hr->code*10 + (buf[i] - '0');
512         i++;
513     }
514     while (i < size-1 && buf[i] != '\n')
515         i++;
516     return decode_headers_content(o, i, &hr->headers,
517                                   &hr->content_buf, &hr->content_len);
518 }
519
520 int yaz_decode_http_request(ODR o, Z_HTTP_Request **hr_p)
521 {
522     int i, po;
523     Z_HTTP_Request *hr = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
524     const char *buf = o->op->buf;
525     int size = o->op->size;
526
527     *hr_p = hr;
528
529     /* method .. */
530     for (i = 0; buf[i] != ' '; i++)
531         if (i >= size-5 || i > 30)
532         {
533             o->error = OHTTP;
534             return 0;
535         }
536     hr->method = (char *) odr_malloc(o, i+1);
537     memcpy (hr->method, buf, i);
538     hr->method[i] = '\0';
539     /* path */
540     po = i+1;
541     for (i = po; buf[i] != ' '; i++)
542         if (i >= size-5)
543         {
544             o->error = OHTTP;
545             return 0;
546         }
547     hr->path = (char *) odr_malloc(o, i - po + 1);
548     memcpy (hr->path, buf+po, i - po);
549     hr->path[i - po] = '\0';
550     /* HTTP version */
551     i++;
552     if (i > size-5 || memcmp(buf+i, "HTTP/", 5))
553     {
554         o->error = OHTTP;
555         return 0;
556     }
557     i+= 5;
558     po = i;
559     while (i < size && !strchr("\r\n", buf[i]))
560         i++;
561     hr->version = (char *) odr_malloc(o, i - po + 1);
562     memcpy(hr->version, buf + po, i - po);
563     hr->version[i - po] = '\0';
564     /* headers */
565     if (i < size-1 && buf[i] == '\r')
566         i++;
567     if (buf[i] != '\n')
568     {
569         o->error = OHTTP;
570         return 0;
571     }
572     return decode_headers_content(o, i, &hr->headers,
573                                   &hr->content_buf, &hr->content_len);
574 }
575
576 static void dump_http_package(ODR o, const char *buf, size_t len)
577 {
578     int i;
579     for (i = 0; ; i++)
580     {
581         if (i == len)
582         {
583             odr_printf(o, "%.*s\n", i, buf);
584             break;
585         }
586         else if (i > 8192)
587         {
588             odr_printf(o, "%.*s\n", i, buf);
589             odr_printf(o, "(truncated\n", (long) len);
590             break;
591         }
592         else if (buf[i] == 0)
593         {
594             odr_printf(o, "%.*s\n", i, buf);
595             odr_printf(o, "(binary data)\n", (long) len);
596             break;
597         }
598     }
599 }
600
601 int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr)
602 {
603     char sbuf[80];
604     Z_HTTP_Header *h;
605     int top0 = o->op->top;
606
607     sprintf(sbuf, "HTTP/%s %d %s\r\n", hr->version,
608             hr->code,
609             z_HTTP_errmsg(hr->code));
610     odr_write(o, sbuf, strlen(sbuf));
611     /* use content_len for Content-Length */
612     sprintf(sbuf, "Content-Length: %d\r\n", hr->content_len);
613     odr_write(o, sbuf, strlen(sbuf));
614     for (h = hr->headers; h; h = h->next)
615     {
616         if (yaz_strcasecmp(h->name, "Content-Length")
617             && yaz_strcasecmp(h->name, "Transfer-Encoding"))
618         {   /* skip Content-Length if given. content_len rules */
619             odr_write(o, h->name, strlen(h->name));
620             odr_write(o, ": ", 2);
621             odr_write(o, h->value, strlen(h->value));
622             odr_write(o, "\r\n", 2);
623         }
624     }
625     odr_write(o, "\r\n", 2);
626     if (hr->content_buf)
627         odr_write(o, hr->content_buf, hr->content_len);
628     if (o->direction == ODR_PRINT)
629     {
630         odr_printf(o, "-- HTTP response:\n");
631         dump_http_package(o, o->op->buf + top0, o->op->top - top0);
632         odr_printf(o, "--\n");
633     }
634     return 1;
635 }
636
637 int yaz_encode_http_request(ODR o, Z_HTTP_Request *hr)
638 {
639     Z_HTTP_Header *h;
640     int top0 = o->op->top;
641
642     odr_write(o, hr->method, strlen(hr->method));
643     odr_write(o, " ", 1);
644     odr_write(o, hr->path, strlen(hr->path));
645     odr_write(o, " HTTP/", 6);
646     odr_write(o, hr->version, strlen(hr->version));
647     odr_write(o, "\r\n", 2);
648     if (hr->content_len &&
649         !z_HTTP_header_lookup(hr->headers,
650                               "Content-Length"))
651     {
652         char lstr[60];
653         sprintf(lstr, "Content-Length: %d\r\n",
654                 hr->content_len);
655         odr_write(o, lstr, strlen(lstr));
656     }
657     for (h = hr->headers; h; h = h->next)
658     {
659         odr_write(o, h->name, strlen(h->name));
660         odr_write(o, ": ", 2);
661         odr_write(o, h->value, strlen(h->value));
662         odr_write(o, "\r\n", 2);
663     }
664     odr_write(o, "\r\n", 2);
665     if (hr->content_buf)
666         odr_write(o, hr->content_buf, hr->content_len);
667     if (o->direction == ODR_PRINT)
668     {
669         odr_printf(o, "-- HTTP request:\n");
670         dump_http_package(o, o->op->buf + top0, o->op->top - top0);
671         odr_printf(o, "--\n");
672     }
673     return 1;
674 }
675
676 /*
677  * Local variables:
678  * c-basic-offset: 4
679  * c-file-style: "Stroustrup"
680  * indent-tabs-mode: nil
681  * End:
682  * vim: shiftwidth=4 tabstop=8 expandtab
683  */
684