Merge branch 'master' of ssh://git.indexdata.com/home/git/pub/yaz
[yaz-moved-to-github.git] / src / http.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 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 <ctype.h>
14 #include <yaz/odr.h>
15 #include <yaz/yaz-version.h>
16 #include <yaz/yaz-iconv.h>
17 #include <yaz/matchstr.h>
18 #include <yaz/zgdu.h>
19
20 #ifdef WIN32
21 #define strncasecmp _strnicmp
22 #define strcasecmp _stricmp
23 #endif
24  
25
26 /*
27  * This function's counterpart, yaz_base64decode(), is in srwutil.c.
28  * I feel bad that they're not together, but each function is only
29  * needed in one place, and those places are not together.  Maybe one
30  * day we'll move them into a new httputil.c, and declare them in a
31  * corresponding httputil.h
32  */
33 static void yaz_base64encode(const char *in, char *out)
34 {
35     static char encoding[] =
36         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
37     unsigned char buf[3];
38     long n;
39
40     while (*in != 0) {
41         char *pad = 0;
42         buf[0] = in[0];
43         buf[1] = in[1];
44         if (in[1] == 0) {
45             buf[2] = 0;
46             pad = "==";
47         } else {
48             buf[2] = in[2];
49             if (in[2] == 0)
50                 pad = "=";
51         }
52
53         /* Treat three eight-bit numbers as on 24-bit number */
54         n = (buf[0] << 16) + (buf[1] << 8) + buf[2];
55
56         /* Write the six-bit chunks out as four encoded characters */
57         *out++ = encoding[(n >> 18) & 63];
58         *out++ = encoding[(n >> 12) & 63];
59         if (in[1] != 0)
60             *out++ = encoding[(n >> 6) & 63];
61         if (in[1] != 0 && in[2] != 0)
62             *out++ = encoding[n & 63];
63
64         if (pad != 0) {
65             while (*pad != 0)
66                 *out++ = *pad++;
67             break;
68         }
69         in += 3;
70     }
71
72     *out++ = 0;
73 }
74
75
76 static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers,
77                                   char **content_buf, int *content_len)
78 {
79     int i = off;
80     int chunked = 0;
81
82     *headers = 0;
83     while (i < o->size-1 && o->buf[i] == '\n')
84     {
85         int po;
86         i++;
87         if (o->buf[i] == '\r' && i < o->size-1 && o->buf[i+1] == '\n')
88         {
89             i++;
90             break;
91         }
92         if (o->buf[i] == '\n')
93             break;
94         for (po = i; ; i++)
95         {
96             if (i == o->size)
97             {
98                 o->error = OHTTP;
99                 return 0;
100             }
101             else if (o->buf[i] == ':')
102                 break;
103         }
104         *headers = (Z_HTTP_Header *) odr_malloc(o, sizeof(**headers));
105         (*headers)->name = (char*) odr_malloc(o, i - po + 1);
106         memcpy ((*headers)->name, o->buf + po, i - po);
107         (*headers)->name[i - po] = '\0';
108         i++;
109         while (i < o->size-1 && o->buf[i] == ' ')
110             i++;
111         for (po = i; i < o->size-1 && !strchr("\r\n", o->buf[i]); i++)
112             ;
113         
114         (*headers)->value = (char*) odr_malloc(o, i - po + 1);
115         memcpy ((*headers)->value, o->buf + po, i - po);
116         (*headers)->value[i - po] = '\0';
117         
118         if (!strcasecmp((*headers)->name, "Transfer-Encoding")
119             &&
120             !strcasecmp((*headers)->value, "chunked"))
121             chunked = 1;
122         headers = &(*headers)->next;
123         if (i < o->size-1 && o->buf[i] == '\r')
124             i++;
125     }
126     *headers = 0;
127     if (o->buf[i] != '\n')
128     {
129         o->error = OHTTP;
130         return 0;
131     }
132     i++;
133
134     if (chunked)
135     {
136         int off = 0;
137         
138         /* we know buffer will be smaller than o->size - i*/
139         *content_buf = (char*) odr_malloc(o, o->size - i);  
140         
141         while (1)
142         {
143             /* chunk length .. */
144             int chunk_len = 0;
145             for (; i  < o->size-2; i++)
146                 if (isdigit(o->buf[i]))
147                     chunk_len = chunk_len * 16 + 
148                         (o->buf[i] - '0');
149                 else if (isupper(o->buf[i]))
150                     chunk_len = chunk_len * 16 + 
151                         (o->buf[i] - ('A'-10));
152                 else if (islower(o->buf[i]))
153                     chunk_len = chunk_len * 16 + 
154                         (o->buf[i] - ('a'-10));
155                 else
156                     break;
157             /* chunk extension ... */
158             while (o->buf[i] != '\r' && o->buf[i+1] != '\n')
159             {
160                 if (i >= o->size-2)
161                 {
162                     o->error = OHTTP;
163                     return 0;
164                 }
165                 i++;
166             }
167             i += 2;  /* skip CRLF */
168             if (chunk_len == 0)
169                 break;
170             if (chunk_len < 0 || off + chunk_len > o->size)
171             {
172                 o->error = OHTTP;
173                 return 0;
174             }
175             /* copy chunk .. */
176             memcpy (*content_buf + off, o->buf + i, chunk_len);
177             i += chunk_len + 2; /* skip chunk+CRLF */
178             off += chunk_len;
179         }
180         if (!off)
181             *content_buf = 0;
182         *content_len = off;
183     }
184     else
185     {
186         if (i > o->size)
187         {
188             o->error = OHTTP;
189             return 0;
190         }
191         else if (i == o->size)
192         {
193             *content_buf = 0;
194             *content_len = 0;
195         }
196         else 
197         {
198             *content_len = o->size - i;
199             *content_buf = (char*) odr_malloc(o, *content_len + 1);
200             memcpy(*content_buf, o->buf + i, *content_len);
201             (*content_buf)[*content_len] = '\0';
202         }
203     }
204     return 1;
205 }
206
207 void z_HTTP_header_add_content_type(ODR o, Z_HTTP_Header **hp,
208                                     const char *content_type,
209                                     const char *charset)
210 {
211     const char *l = "Content-Type";
212     if (charset)
213     {
214         char *ctype = (char *)
215             odr_malloc(o, strlen(content_type)+strlen(charset) + 15);
216         sprintf(ctype, "%s; charset=%s", content_type, charset);
217         z_HTTP_header_add(o, hp, l, ctype);
218     }
219     else
220         z_HTTP_header_add(o, hp, l, content_type);
221
222 }
223
224 /*
225  * HTTP Basic authentication is described at:
226  * http://tools.ietf.org/html/rfc1945#section-11.1
227  */
228 void z_HTTP_header_add_basic_auth(ODR o, Z_HTTP_Header **hp,
229                                   const char *username, const char *password)
230 {
231     char *tmp, *buf;
232     int len;
233
234     if (username == 0)
235         return;
236
237     len = strlen(username) + strlen(password);
238     tmp = (char *) odr_malloc(o, len+2);
239     sprintf(tmp, "%s:%s", username, password);
240     buf = (char *) odr_malloc(o, (len+1) * 8/6 + 12);
241     strcpy(buf, "Basic ");
242     yaz_base64encode(tmp, &buf[strlen(buf)]);
243     z_HTTP_header_add(o, hp, "Authorization", buf);
244 }
245
246
247 void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n,
248                        const char *v)
249 {
250     while (*hp)
251         hp = &(*hp)->next;
252     *hp = (Z_HTTP_Header *) odr_malloc(o, sizeof(**hp));
253     (*hp)->name = odr_strdup(o, n);
254     (*hp)->value = odr_strdup(o, v);
255     (*hp)->next = 0;
256 }
257
258 const char *z_HTTP_header_lookup(const Z_HTTP_Header *hp, const char *n)
259 {
260     for (; hp; hp = hp->next)
261         if (!yaz_matchstr(hp->name, n))
262             return hp->value;
263     return 0;
264 }
265
266
267 Z_GDU *z_get_HTTP_Request(ODR o)
268 {
269     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
270     Z_HTTP_Request *hreq;
271
272     p->which = Z_GDU_HTTP_Request;
273     p->u.HTTP_Request = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hreq));
274     hreq = p->u.HTTP_Request;
275     hreq->headers = 0;
276     hreq->content_len = 0;
277     hreq->content_buf = 0;
278     hreq->version = "1.1";
279     hreq->method = "POST";
280     hreq->path = "/";
281     z_HTTP_header_add(o, &hreq->headers, "User-Agent", "YAZ/" YAZ_VERSION);
282     return p;
283 }
284
285
286 Z_GDU *z_get_HTTP_Request_host_path(ODR odr,
287                                     const char *host,
288                                     const char *path)
289 {
290     Z_GDU *p = z_get_HTTP_Request(odr);
291
292     p->u.HTTP_Request->path = odr_strdup(odr, path);
293
294     if (host)
295     {
296         const char *cp0 = strstr(host, "://");
297         const char *cp1 = 0;
298         if (cp0)
299             cp0 = cp0+3;
300         else
301             cp0 = host;
302
303         cp1 = strchr(cp0, '/');
304         if (!cp1)
305             cp1 = cp0+strlen(cp0);
306
307         if (cp0 && cp1)
308         {
309             char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
310             memcpy (h, cp0, cp1 - cp0);
311             h[cp1-cp0] = '\0';
312             z_HTTP_header_add(odr, &p->u.HTTP_Request->headers,
313                               "Host", h);
314         }
315     }
316     return p;
317 }
318
319
320 Z_GDU *z_get_HTTP_Response(ODR o, int code)
321 {
322     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
323     Z_HTTP_Response *hres;
324
325     p->which = Z_GDU_HTTP_Response;
326     p->u.HTTP_Response = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hres));
327     hres = p->u.HTTP_Response;
328     hres->headers = 0;
329     hres->content_len = 0;
330     hres->content_buf = 0;
331     hres->code = code;
332     hres->version = "1.1";
333     z_HTTP_header_add(o, &hres->headers, "Server",
334                       "YAZ/" YAZ_VERSION);
335     if (code != 200)
336     {
337         hres->content_buf = (char*) odr_malloc(o, 400);
338         sprintf(hres->content_buf, 
339                 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
340                 " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
341                 "<HTML>\n"
342                 " <HEAD>\n"
343                 "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
344                 " </HEAD>\n"
345                 " <BODY>\n"
346                 "  <P><A HREF=\"http://www.indexdata.com/yaz/\">YAZ</A> " 
347                 YAZ_VERSION "</P>\n"
348                 "  <P>Error: %d</P>\n"
349                 "  <P>Description: %.50s</P>\n"
350                 " </BODY>\n"
351                 "</HTML>\n",
352                 code, z_HTTP_errmsg(code));
353         hres->content_len = strlen(hres->content_buf);
354         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
355     }
356     return p;
357 }
358
359 const char *z_HTTP_errmsg(int code)
360 {
361     if (code == 200)
362         return "OK";
363     else if (code == 400)
364         return "Bad Request";
365     else if (code == 404)
366         return "Not Found";
367     else if (code == 405)
368         return "Method Not Allowed";
369     else if (code == 500)
370         return "Internal Error";
371     else
372         return "Unknown Error";
373 }
374
375 int yaz_decode_http_response(ODR o, Z_HTTP_Response **hr_p)
376 {
377     int i, po;
378     Z_HTTP_Response *hr = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hr));
379     
380     *hr_p = hr;
381     hr->content_buf = 0;
382     hr->content_len = 0;
383     
384     po = i = 5;
385     while (i < o->size-2 && !strchr(" \r\n", o->buf[i]))
386         i++;
387     hr->version = (char *) odr_malloc(o, i - po + 1);
388     if (i - po)
389         memcpy(hr->version, o->buf + po, i - po);
390     hr->version[i-po] = 0;
391     if (o->buf[i] != ' ')
392     {
393         o->error = OHTTP;
394         return 0;
395     }
396     i++;
397     hr->code = 0;
398     while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9')
399     {
400         hr->code = hr->code*10 + (o->buf[i] - '0');
401         i++;
402     }
403     while (i < o->size-1 && o->buf[i] != '\n')
404         i++;
405     return decode_headers_content(o, i, &hr->headers,
406                                   &hr->content_buf, &hr->content_len);            
407 }
408
409 int yaz_decode_http_request(ODR o, Z_HTTP_Request **hr_p)
410 {
411     int i, po;
412     Z_HTTP_Request *hr = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
413     
414     *hr_p = hr;
415     
416     /* method .. */
417     for (i = 0; o->buf[i] != ' '; i++)
418         if (i >= o->size-5 || i > 30)
419         {
420             o->error = OHTTP;
421             return 0;
422         }
423     hr->method = (char *) odr_malloc(o, i+1);
424     memcpy (hr->method, o->buf, i);
425     hr->method[i] = '\0';
426     /* path */
427     po = i+1;
428     for (i = po; o->buf[i] != ' '; i++)
429         if (i >= o->size-5)
430         {
431             o->error = OHTTP;
432             return 0;
433         }
434     hr->path = (char *) odr_malloc(o, i - po + 1);
435     memcpy (hr->path, o->buf+po, i - po);
436     hr->path[i - po] = '\0';
437     /* HTTP version */
438     i++;
439     if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5))
440     {
441         o->error = OHTTP;
442         return 0;
443     }
444     i+= 5;
445     po = i;
446     while (i < o->size && !strchr("\r\n", o->buf[i]))
447         i++;
448     hr->version = (char *) odr_malloc(o, i - po + 1);
449     memcpy(hr->version, o->buf + po, i - po);
450     hr->version[i - po] = '\0';
451     /* headers */
452     if (i < o->size-1 && o->buf[i] == '\r')
453         i++;
454     if (o->buf[i] != '\n')
455     {
456         o->error = OHTTP;
457         return 0;
458     }
459     return decode_headers_content(o, i, &hr->headers,
460                                   &hr->content_buf, &hr->content_len);
461 }
462
463 int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr)
464 {
465     char sbuf[80];
466     Z_HTTP_Header *h;
467     int top0 = o->top;
468
469     sprintf(sbuf, "HTTP/%s %d %s\r\n", hr->version,
470             hr->code,
471             z_HTTP_errmsg(hr->code));
472     odr_write(o, (unsigned char *) sbuf, strlen(sbuf));
473     /* apply Content-Length if not already applied */
474     if (!z_HTTP_header_lookup(hr->headers,
475                               "Content-Length"))
476     {
477         char lstr[60];
478         sprintf(lstr, "Content-Length: %d\r\n",
479                 hr->content_len);
480         odr_write(o, (unsigned char *) lstr, strlen(lstr));
481     }
482     for (h = hr->headers; h; h = h->next)
483     {
484         odr_write(o, (unsigned char *) h->name, strlen(h->name));
485         odr_write(o, (unsigned char *) ": ", 2);
486         odr_write(o, (unsigned char *) h->value, strlen(h->value));
487         odr_write(o, (unsigned char *) "\r\n", 2);
488     }
489     odr_write(o, (unsigned char *) "\r\n", 2);
490     if (hr->content_buf)
491         odr_write(o, (unsigned char *) 
492                   hr->content_buf,
493                   hr->content_len);
494     if (o->direction == ODR_PRINT)
495     {
496         odr_printf(o, "-- HTTP response:\n%.*s\n", o->top - top0,
497                    o->buf + top0);
498         odr_printf(o, "-- \n");
499     }
500     return 1;
501 }
502
503 int yaz_encode_http_request(ODR o, Z_HTTP_Request *hr)
504 {
505     Z_HTTP_Header *h;
506     int top0 = o->top;
507
508     odr_write(o, (unsigned char *) hr->method,
509               strlen(hr->method));
510     odr_write(o, (unsigned char *) " ", 1);
511     odr_write(o, (unsigned char *) hr->path,
512               strlen(hr->path));
513     odr_write(o, (unsigned char *) " HTTP/", 6);
514     odr_write(o, (unsigned char *) hr->version,
515               strlen(hr->version));
516     odr_write(o, (unsigned char *) "\r\n", 2);
517     if (hr->content_len &&
518         !z_HTTP_header_lookup(hr->headers,
519                               "Content-Length"))
520     {
521         char lstr[60];
522         sprintf(lstr, "Content-Length: %d\r\n",
523                 hr->content_len);
524         odr_write(o, (unsigned char *) lstr, strlen(lstr));
525     }
526     for (h = hr->headers; h; h = h->next)
527     {
528         odr_write(o, (unsigned char *) h->name, strlen(h->name));
529         odr_write(o, (unsigned char *) ": ", 2);
530         odr_write(o, (unsigned char *) h->value, strlen(h->value));
531         odr_write(o, (unsigned char *) "\r\n", 2);
532     }
533     odr_write(o, (unsigned char *) "\r\n", 2);
534     if (hr->content_buf)
535         odr_write(o, (unsigned char *)
536                   hr->content_buf,
537                   hr->content_len);
538     if (o->direction == ODR_PRINT)
539     {
540         odr_printf(o, "-- HTTP request:\n%.*s\n", o->top - top0,
541                    o->buf + top0);
542         odr_printf(o, "-- \n");
543     }
544     return 1;
545 }
546
547 /*
548  * Local variables:
549  * c-basic-offset: 4
550  * c-file-style: "Stroustrup"
551  * indent-tabs-mode: nil
552  * End:
553  * vim: shiftwidth=4 tabstop=8 expandtab
554  */
555