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