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