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