Allow HTTP protocol on unix local socket
[yaz-moved-to-github.git] / src / http.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2012 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_add(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 const char *z_HTTP_header_lookup(const Z_HTTP_Header *hp, const char *n)
210 {
211     for (; hp; hp = hp->next)
212         if (!yaz_matchstr(hp->name, n))
213             return hp->value;
214     return 0;
215 }
216
217
218 Z_GDU *z_get_HTTP_Request(ODR o)
219 {
220     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
221     Z_HTTP_Request *hreq;
222
223     p->which = Z_GDU_HTTP_Request;
224     p->u.HTTP_Request = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hreq));
225     hreq = p->u.HTTP_Request;
226     hreq->headers = 0;
227     hreq->content_len = 0;
228     hreq->content_buf = 0;
229     hreq->version = "1.1";
230     hreq->method = "POST";
231     hreq->path = "/";
232     z_HTTP_header_add(o, &hreq->headers, "User-Agent", "YAZ/" YAZ_VERSION);
233     return p;
234 }
235
236
237 Z_GDU *z_get_HTTP_Request_host_path(ODR odr,
238                                     const char *host,
239                                     const char *path)
240 {
241     Z_GDU *p = z_get_HTTP_Request(odr);
242
243     p->u.HTTP_Request->path = odr_strdup(odr, path);
244
245     if (host)
246     {
247         const char *cp0 = strstr(host, "://");
248         const char *cp1 = 0;
249         if (cp0)
250             cp0 = cp0+3;
251         else
252             cp0 = host;
253
254         cp1 = strchr(cp0, '/');
255         if (!cp1)
256             cp1 = cp0+strlen(cp0);
257
258         if (cp0 && cp1)
259         {
260             char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
261             memcpy (h, cp0, cp1 - cp0);
262             h[cp1-cp0] = '\0';
263             z_HTTP_header_add(odr, &p->u.HTTP_Request->headers,
264                               "Host", h);
265         }
266     }
267     return p;
268 }
269
270 Z_GDU *z_get_HTTP_Request_uri(ODR odr, const char *uri, const char *args,
271                               int use_full_uri)
272 {
273     Z_GDU *p = z_get_HTTP_Request(odr);
274     const char *cp0 = strstr(uri, "://");
275     const char *cp1 = 0;
276     if (cp0)
277         cp0 = cp0+3;
278     else
279         cp0 = uri;
280
281     cp1 = strchr(cp0, '/');
282     if (!cp1)
283         cp1 = cp0+strlen(cp0);
284
285     if (cp0 && cp1)
286     {
287         char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
288         memcpy (h, cp0, cp1 - cp0);
289         h[cp1-cp0] = '\0';
290         z_HTTP_header_add(odr, &p->u.HTTP_Request->headers,
291                           "Host", h);
292     }
293
294     if (!args)
295     {
296         if (*cp1)
297             args = cp1 + 1;
298         else
299             args = "";
300     }
301     p->u.HTTP_Request->path = odr_malloc(odr, cp1 - uri + strlen(args) + 2);
302     if (use_full_uri)
303     {
304         memcpy(p->u.HTTP_Request->path, uri, cp1 - uri);
305         strcpy(p->u.HTTP_Request->path + (cp1 - uri), "/");
306     }
307     else
308         strcpy(p->u.HTTP_Request->path, "/");
309     strcat(p->u.HTTP_Request->path, args);
310     return p;
311 }
312
313 Z_GDU *z_get_HTTP_Response(ODR o, int code)
314 {
315     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
316     Z_HTTP_Response *hres;
317
318     p->which = Z_GDU_HTTP_Response;
319     p->u.HTTP_Response = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hres));
320     hres = p->u.HTTP_Response;
321     hres->headers = 0;
322     hres->content_len = 0;
323     hres->content_buf = 0;
324     hres->code = code;
325     hres->version = "1.1";
326     z_HTTP_header_add(o, &hres->headers, "Server",
327                       "YAZ/" YAZ_VERSION);
328     if (code != 200)
329     {
330         hres->content_buf = (char*) odr_malloc(o, 400);
331         sprintf(hres->content_buf,
332                 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
333                 " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
334                 "<HTML>\n"
335                 " <HEAD>\n"
336                 "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
337                 " </HEAD>\n"
338                 " <BODY>\n"
339                 "  <P><A HREF=\"http://www.indexdata.com/yaz/\">YAZ</A> "
340                 YAZ_VERSION "</P>\n"
341                 "  <P>Error: %d</P>\n"
342                 "  <P>Description: %.50s</P>\n"
343                 " </BODY>\n"
344                 "</HTML>\n",
345                 code, z_HTTP_errmsg(code));
346         hres->content_len = strlen(hres->content_buf);
347         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
348     }
349     return p;
350 }
351
352 const char *z_HTTP_errmsg(int code)
353 {
354     if (code == 200)
355         return "OK";
356     else if (code == 400)
357         return "Bad Request";
358     else if (code == 404)
359         return "Not Found";
360     else if (code == 405)
361         return "Method Not Allowed";
362     else if (code == 500)
363         return "Internal Error";
364     else
365         return "Unknown Error";
366 }
367
368 int yaz_decode_http_response(ODR o, Z_HTTP_Response **hr_p)
369 {
370     int i, po;
371     Z_HTTP_Response *hr = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hr));
372
373     *hr_p = hr;
374     hr->content_buf = 0;
375     hr->content_len = 0;
376
377     po = i = 5;
378     while (i < o->size-2 && !strchr(" \r\n", o->buf[i]))
379         i++;
380     hr->version = (char *) odr_malloc(o, i - po + 1);
381     if (i - po)
382         memcpy(hr->version, o->buf + po, i - po);
383     hr->version[i-po] = 0;
384     if (o->buf[i] != ' ')
385     {
386         o->error = OHTTP;
387         return 0;
388     }
389     i++;
390     hr->code = 0;
391     while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9')
392     {
393         hr->code = hr->code*10 + (o->buf[i] - '0');
394         i++;
395     }
396     while (i < o->size-1 && o->buf[i] != '\n')
397         i++;
398     return decode_headers_content(o, i, &hr->headers,
399                                   &hr->content_buf, &hr->content_len);
400 }
401
402 int yaz_decode_http_request(ODR o, Z_HTTP_Request **hr_p)
403 {
404     int i, po;
405     Z_HTTP_Request *hr = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
406
407     *hr_p = hr;
408
409     /* method .. */
410     for (i = 0; o->buf[i] != ' '; i++)
411         if (i >= o->size-5 || i > 30)
412         {
413             o->error = OHTTP;
414             return 0;
415         }
416     hr->method = (char *) odr_malloc(o, i+1);
417     memcpy (hr->method, o->buf, i);
418     hr->method[i] = '\0';
419     /* path */
420     po = i+1;
421     for (i = po; o->buf[i] != ' '; i++)
422         if (i >= o->size-5)
423         {
424             o->error = OHTTP;
425             return 0;
426         }
427     hr->path = (char *) odr_malloc(o, i - po + 1);
428     memcpy (hr->path, o->buf+po, i - po);
429     hr->path[i - po] = '\0';
430     /* HTTP version */
431     i++;
432     if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5))
433     {
434         o->error = OHTTP;
435         return 0;
436     }
437     i+= 5;
438     po = i;
439     while (i < o->size && !strchr("\r\n", o->buf[i]))
440         i++;
441     hr->version = (char *) odr_malloc(o, i - po + 1);
442     memcpy(hr->version, o->buf + po, i - po);
443     hr->version[i - po] = '\0';
444     /* headers */
445     if (i < o->size-1 && o->buf[i] == '\r')
446         i++;
447     if (o->buf[i] != '\n')
448     {
449         o->error = OHTTP;
450         return 0;
451     }
452     return decode_headers_content(o, i, &hr->headers,
453                                   &hr->content_buf, &hr->content_len);
454 }
455
456 int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr)
457 {
458     char sbuf[80];
459     Z_HTTP_Header *h;
460     int top0 = o->top;
461
462     sprintf(sbuf, "HTTP/%s %d %s\r\n", hr->version,
463             hr->code,
464             z_HTTP_errmsg(hr->code));
465     odr_write(o, (unsigned char *) sbuf, strlen(sbuf));
466     /* apply Content-Length if not already applied */
467     if (!z_HTTP_header_lookup(hr->headers,
468                               "Content-Length"))
469     {
470         char lstr[60];
471         sprintf(lstr, "Content-Length: %d\r\n",
472                 hr->content_len);
473         odr_write(o, (unsigned char *) lstr, strlen(lstr));
474     }
475     for (h = hr->headers; h; h = h->next)
476     {
477         odr_write(o, (unsigned char *) h->name, strlen(h->name));
478         odr_write(o, (unsigned char *) ": ", 2);
479         odr_write(o, (unsigned char *) h->value, strlen(h->value));
480         odr_write(o, (unsigned char *) "\r\n", 2);
481     }
482     odr_write(o, (unsigned char *) "\r\n", 2);
483     if (hr->content_buf)
484         odr_write(o, (unsigned char *)
485                   hr->content_buf,
486                   hr->content_len);
487     if (o->direction == ODR_PRINT)
488     {
489         odr_printf(o, "-- HTTP response:\n%.*s\n", o->top - top0,
490                    o->buf + top0);
491         odr_printf(o, "-- \n");
492     }
493     return 1;
494 }
495
496 int yaz_encode_http_request(ODR o, Z_HTTP_Request *hr)
497 {
498     Z_HTTP_Header *h;
499     int top0 = o->top;
500
501     odr_write(o, (unsigned char *) hr->method,
502               strlen(hr->method));
503     odr_write(o, (unsigned char *) " ", 1);
504     odr_write(o, (unsigned char *) hr->path,
505               strlen(hr->path));
506     odr_write(o, (unsigned char *) " HTTP/", 6);
507     odr_write(o, (unsigned char *) hr->version,
508               strlen(hr->version));
509     odr_write(o, (unsigned char *) "\r\n", 2);
510     if (hr->content_len &&
511         !z_HTTP_header_lookup(hr->headers,
512                               "Content-Length"))
513     {
514         char lstr[60];
515         sprintf(lstr, "Content-Length: %d\r\n",
516                 hr->content_len);
517         odr_write(o, (unsigned char *) lstr, strlen(lstr));
518     }
519     for (h = hr->headers; h; h = h->next)
520     {
521         odr_write(o, (unsigned char *) h->name, strlen(h->name));
522         odr_write(o, (unsigned char *) ": ", 2);
523         odr_write(o, (unsigned char *) h->value, strlen(h->value));
524         odr_write(o, (unsigned char *) "\r\n", 2);
525     }
526     odr_write(o, (unsigned char *) "\r\n", 2);
527     if (hr->content_buf)
528         odr_write(o, (unsigned char *)
529                   hr->content_buf,
530                   hr->content_len);
531     if (o->direction == ODR_PRINT)
532     {
533         odr_printf(o, "-- HTTP request:\n%.*s\n", o->top - top0,
534                    o->buf + top0);
535         odr_printf(o, "-- \n");
536     }
537     return 1;
538 }
539
540 /*
541  * Local variables:
542  * c-basic-offset: 4
543  * c-file-style: "Stroustrup"
544  * indent-tabs-mode: nil
545  * End:
546  * vim: shiftwidth=4 tabstop=8 expandtab
547  */
548