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