odr: move 5 members of struct odr to private
[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_details(ODR o, int code, const char *details)
344 {
345     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
346     Z_HTTP_Response *hres;
347
348     p->which = Z_GDU_HTTP_Response;
349     p->u.HTTP_Response = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hres));
350     hres = p->u.HTTP_Response;
351     hres->headers = 0;
352     hres->content_len = 0;
353     hres->content_buf = 0;
354     hres->code = code;
355     hres->version = "1.1";
356     z_HTTP_header_add(o, &hres->headers, "Server",
357                       "YAZ/" YAZ_VERSION);
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>YAZ " YAZ_VERSION "</TITLE>\n"
370                 " </HEAD>\n"
371                 " <BODY>\n"
372                 "  <P><A HREF=\"http://www.indexdata.com/yaz/\">YAZ</A> "
373                 YAZ_VERSION "</P>\n"
374                 "  <P>Error: %d</P>\n"
375                 "  <P>Description: %s</P>\n", 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(ODR o, int code)
391 {
392     return z_get_HTTP_Response_details(o, code, 0);
393 }
394
395 const char *z_HTTP_errmsg(int code)
396 {
397     switch (code)
398     {
399     case 100:
400         return "Continue";
401     case 101:
402         return "Switching Protocols";
403     case 200:
404         return "OK";
405     case 201:
406         return "Created";
407     case 202:
408         return "Accepted";
409     case 203:
410         return "Non-Authoritative Information";
411     case 204:
412         return "No Content";
413     case 205:
414         return "Reset Content";
415     case 206:
416         return "Partial Content";
417     case 300:
418         return "Multiple Choices";
419     case 301:
420         return "Moved Permenently";
421     case 302:
422         return "Found";
423     case 303:
424         return "See Other";
425     case 304:
426         return "Not Modified";
427     case 305:
428         return "Use Proxy";
429     case 307:
430         return "Temporary Redirect";
431     case 400:
432         return "Bad Request";
433     case 404:
434         return "Not Found";
435     case 405:
436         return "Method Not Allowed";
437     case 406:
438         return "Not Acceptable";
439     case 407:
440         return "Proxy Authentication Required";
441     case 408:
442         return "Request Timeout";
443     case 409:
444         return "Conflict";
445     case 410:
446         return "Gone";
447     case 411:
448         return "Length Required";
449     case 412:
450         return "Precondition Failed";
451     case 413:
452         return "Request Entity Too Large";
453     case 414:
454         return "Request-URI Too Long";
455     case 415:
456         return "Unsupported Media Type";
457     case 416:
458         return "Requested Range Not Satisfiable";
459     case 417:
460         return "Expectation Failed";
461     case 500:
462         return "Internal Error";
463     case 501:
464         return "Not Implemented";
465     case 502:
466         return "Bad Gateway";
467     case 503:
468         return "Service Unavailable";
469     case 504:
470         return "Gateway Timeout";
471     case 505:
472         return "HTTP Version Not Supported";
473     default:
474         return "Unknown Error";
475     }
476 }
477
478 int yaz_decode_http_response(ODR o, Z_HTTP_Response **hr_p)
479 {
480     int i, po;
481     Z_HTTP_Response *hr = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hr));
482     const char *buf = o->op->buf;
483     int size = o->op->size;
484
485     *hr_p = hr;
486     hr->content_buf = 0;
487     hr->content_len = 0;
488
489     po = i = 5;
490     while (i < size-2 && !strchr(" \r\n", buf[i]))
491         i++;
492     hr->version = (char *) odr_malloc(o, i - po + 1);
493     if (i - po)
494         memcpy(hr->version, buf + po, i - po);
495     hr->version[i-po] = 0;
496     if (buf[i] != ' ')
497     {
498         o->error = OHTTP;
499         return 0;
500     }
501     i++;
502     hr->code = 0;
503     while (i < size-2 && buf[i] >= '0' && buf[i] <= '9')
504     {
505         hr->code = hr->code*10 + (buf[i] - '0');
506         i++;
507     }
508     while (i < size-1 && buf[i] != '\n')
509         i++;
510     return decode_headers_content(o, i, &hr->headers,
511                                   &hr->content_buf, &hr->content_len);
512 }
513
514 int yaz_decode_http_request(ODR o, Z_HTTP_Request **hr_p)
515 {
516     int i, po;
517     Z_HTTP_Request *hr = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
518     const char *buf = o->op->buf;
519     int size = o->op->size;
520
521     *hr_p = hr;
522
523     /* method .. */
524     for (i = 0; buf[i] != ' '; i++)
525         if (i >= size-5 || i > 30)
526         {
527             o->error = OHTTP;
528             return 0;
529         }
530     hr->method = (char *) odr_malloc(o, i+1);
531     memcpy (hr->method, buf, i);
532     hr->method[i] = '\0';
533     /* path */
534     po = i+1;
535     for (i = po; buf[i] != ' '; i++)
536         if (i >= size-5)
537         {
538             o->error = OHTTP;
539             return 0;
540         }
541     hr->path = (char *) odr_malloc(o, i - po + 1);
542     memcpy (hr->path, buf+po, i - po);
543     hr->path[i - po] = '\0';
544     /* HTTP version */
545     i++;
546     if (i > size-5 || memcmp(buf+i, "HTTP/", 5))
547     {
548         o->error = OHTTP;
549         return 0;
550     }
551     i+= 5;
552     po = i;
553     while (i < size && !strchr("\r\n", buf[i]))
554         i++;
555     hr->version = (char *) odr_malloc(o, i - po + 1);
556     memcpy(hr->version, buf + po, i - po);
557     hr->version[i - po] = '\0';
558     /* headers */
559     if (i < size-1 && buf[i] == '\r')
560         i++;
561     if (buf[i] != '\n')
562     {
563         o->error = OHTTP;
564         return 0;
565     }
566     return decode_headers_content(o, i, &hr->headers,
567                                   &hr->content_buf, &hr->content_len);
568 }
569
570 static void dump_http_package(ODR o, const char *buf, size_t len)
571 {
572     int i;
573     for (i = 0; ; i++)
574     {
575         if (i == len)
576         {
577             odr_printf(o, "%.*s\n", i, buf);
578             break;
579         }
580         else if (i > 8192)
581         {
582             odr_printf(o, "%.*s\n", i, buf);
583             odr_printf(o, "(truncated\n", (long) len);
584             break;
585         }
586         else if (buf[i] == 0)
587         {
588             odr_printf(o, "%.*s\n", i, buf);
589             odr_printf(o, "(binary data)\n", (long) len);
590             break;
591         }
592     }
593 }
594
595 int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr)
596 {
597     char sbuf[80];
598     Z_HTTP_Header *h;
599     int top0 = o->op->top;
600
601     sprintf(sbuf, "HTTP/%s %d %s\r\n", hr->version,
602             hr->code,
603             z_HTTP_errmsg(hr->code));
604     odr_write(o, sbuf, strlen(sbuf));
605     /* use content_len for Content-Length */
606     sprintf(sbuf, "Content-Length: %d\r\n", hr->content_len);
607     odr_write(o, sbuf, strlen(sbuf));
608     for (h = hr->headers; h; h = h->next)
609     {
610         if (yaz_strcasecmp(h->name, "Content-Length")
611             && yaz_strcasecmp(h->name, "Transfer-Encoding"))
612         {   /* skip Content-Length if given. content_len rules */
613             odr_write(o, h->name, strlen(h->name));
614             odr_write(o, ": ", 2);
615             odr_write(o, h->value, strlen(h->value));
616             odr_write(o, "\r\n", 2);
617         }
618     }
619     odr_write(o, "\r\n", 2);
620     if (hr->content_buf)
621         odr_write(o, hr->content_buf, hr->content_len);
622     if (o->direction == ODR_PRINT)
623     {
624         odr_printf(o, "-- HTTP response:\n");
625         dump_http_package(o, o->op->buf + top0, o->op->top - top0);
626         odr_printf(o, "--\n");
627     }
628     return 1;
629 }
630
631 int yaz_encode_http_request(ODR o, Z_HTTP_Request *hr)
632 {
633     Z_HTTP_Header *h;
634     int top0 = o->op->top;
635
636     odr_write(o, hr->method, strlen(hr->method));
637     odr_write(o, " ", 1);
638     odr_write(o, hr->path, strlen(hr->path));
639     odr_write(o, " HTTP/", 6);
640     odr_write(o, hr->version, strlen(hr->version));
641     odr_write(o, "\r\n", 2);
642     if (hr->content_len &&
643         !z_HTTP_header_lookup(hr->headers,
644                               "Content-Length"))
645     {
646         char lstr[60];
647         sprintf(lstr, "Content-Length: %d\r\n",
648                 hr->content_len);
649         odr_write(o, lstr, strlen(lstr));
650     }
651     for (h = hr->headers; h; h = h->next)
652     {
653         odr_write(o, h->name, strlen(h->name));
654         odr_write(o, ": ", 2);
655         odr_write(o, h->value, strlen(h->value));
656         odr_write(o, "\r\n", 2);
657     }
658     odr_write(o, "\r\n", 2);
659     if (hr->content_buf)
660         odr_write(o, hr->content_buf, hr->content_len);
661     if (o->direction == ODR_PRINT)
662     {
663         odr_printf(o, "-- HTTP request:\n");
664         dump_http_package(o, o->op->buf + top0, o->op->top - top0);
665         odr_printf(o, "--\n");
666     }
667     return 1;
668 }
669
670 /*
671  * Local variables:
672  * c-basic-offset: 4
673  * c-file-style: "Stroustrup"
674  * indent-tabs-mode: nil
675  * End:
676  * vim: shiftwidth=4 tabstop=8 expandtab
677  */
678