49060b7b2ef2c0519eba528b53d70a6b51620192
[yaz-moved-to-github.git] / zutil / zgdu.c
1 /*
2  * Copyright (c) 2002-2003, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: zgdu.c,v 1.5 2003-02-17 22:34:40 adam Exp $
6  */
7
8 #include <yaz/proto.h>
9
10 #define HTTP_DEBUG 1
11
12 static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers,
13                                   char **content_buf, int *content_len)
14 {
15     int i = off;
16
17     *headers = 0;
18     while (i < o->size-1 && o->buf[i] == '\r')
19     {
20         int po;
21         i++;
22         if (o->buf[i] != '\n')
23         {
24             o->error = OHTTP;
25             return 0;
26         }
27         i++;
28         if (o->buf[i] == '\r')
29             break;
30         for (po = i; ; i++)
31         {
32             if (i == o->size)
33             {
34                 o->error = OHTTP;
35                 return 0;
36             }
37             else if (o->buf[i] == ':')
38                 break;
39         }
40         *headers = (Z_HTTP_Header *) odr_malloc(o, sizeof(**headers));
41         (*headers)->name = (char*) odr_malloc(o, i - po + 1);
42         memcpy ((*headers)->name, o->buf + po, i - po);
43         (*headers)->name[i - po] = '\0';
44         i++;
45         while (i < o->size-1 && o->buf[i] == ' ')
46             i++;
47         for (po = i; i < o->size-1 && o->buf[i] != '\r' ; i++)
48             ;
49         
50         (*headers)->value = (char*) odr_malloc(o, i - po + 1);
51         memcpy ((*headers)->value, o->buf + po, i - po);
52         (*headers)->value[i - po] = '\0';
53         
54         headers = &(*headers)->next;
55     }
56     *headers = 0;
57     i++;
58     if (o->buf[i] != '\n')
59     {
60         o->error = OHTTP;
61         return 0;
62     }
63     i++;
64
65     if (i > o->size)
66     {
67         o->error = OHTTP;
68         return 0;
69     }
70     else if (i == o->size)
71     {
72         *content_buf = 0;
73         *content_len = 0;
74     }
75     else 
76     {
77         *content_len = o->size - i;
78         *content_buf = (char*) odr_malloc(o, *content_len + 1);
79         memcpy(*content_buf, o->buf + i, *content_len);
80         (*content_buf)[*content_len] = '\0';
81     }
82     return 1;
83 }
84
85 void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n,
86                        const char *v)
87 {
88     while (*hp)
89         hp = &(*hp)->next;
90     *hp = (Z_HTTP_Header *) odr_malloc(o, sizeof(**hp));
91     (*hp)->name = odr_strdup(o, n);
92     (*hp)->value = odr_strdup(o, v);
93     (*hp)->next = 0;
94 }
95
96 const char *z_HTTP_header_lookup(Z_HTTP_Header *hp, const char *n)
97 {
98     for (; hp; hp = hp->next)
99         if (!strcmp(hp->name, n))
100             return hp->value;
101     return 0;
102 }
103
104
105 Z_GDU *z_get_HTTP_Request(ODR o)
106 {
107     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
108     Z_HTTP_Request *hreq;
109
110     p->which = Z_GDU_HTTP_Request;
111     p->u.HTTP_Request = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hreq));
112     hreq = p->u.HTTP_Request;
113     hreq->headers = 0;
114     hreq->content_len = 0;
115     hreq->content_buf = 0;
116     hreq->version = "1.1";
117     hreq->method = "POST";
118     hreq->path = "/";
119     z_HTTP_header_add(o, &hreq->headers, "User-Agent",
120                       "YAZ/" YAZ_VERSION);
121     return p;
122 }
123
124 Z_GDU *z_get_HTTP_Response(ODR o, int code)
125 {
126     Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p));
127     Z_HTTP_Response *hres;
128
129     p->which = Z_GDU_HTTP_Response;
130     p->u.HTTP_Response = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hres));
131     hres = p->u.HTTP_Response;
132     hres->headers = 0;
133     hres->content_len = 0;
134     hres->content_buf = 0;
135     hres->code = code;
136     hres->version = "1.1";
137     z_HTTP_header_add(o, &hres->headers, "Server",
138                       "YAZ/" YAZ_VERSION);
139     if (code != 200)
140     {
141         hres->content_buf = (char*) odr_malloc(o, 400);
142         sprintf (hres->content_buf, 
143                  "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
144                  "<HTML>\n"
145                  " <HEAD>\n"
146                  "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
147                  " </HEAD>\n"
148                  " <BODY>\n"
149                  "  <P><A HREF=\"http://www.indexdata.dk/yaz/\">YAZ</A> " 
150                  YAZ_VERSION "</P>\n"
151                  "  <P>Error: %d</P>\n"
152                  "  <P>Description: %.50s</P>\n"
153                  " </BODY>\n"
154                  "</HTML>\n",
155                  code, z_HTTP_errmsg(code));
156         hres->content_len = strlen(hres->content_buf);
157         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
158     }
159     return p;
160 }
161
162 const char *z_HTTP_errmsg(int code)
163 {
164     if (code == 200)
165         return "OK";
166     else if (code == 400)
167         return "Bad Request";
168     else if (code == 404)
169         return "Not Found";
170     else if (code == 405)
171         return "Method Not Allowed";
172     else if (code == 500)
173         return "Internal Error";
174     else
175         return "Unknown Error";
176 }
177
178 int z_GDU (ODR o, Z_GDU **p, int opt, const char *name)
179 {
180     if (o->direction == ODR_DECODE) {
181         *p = (Z_GDU *) odr_malloc(o, sizeof(**p));
182         if (o->size > 10 && !memcmp(o->buf, "HTTP/", 5))
183         {
184             int i, po;
185             Z_HTTP_Response *hr;
186             (*p)->which = Z_GDU_HTTP_Response;
187
188 #if HTTP_DEBUG
189             fprintf(stderr, "-- HTTP decode:\n%.*s\n", o->size, o->buf);
190 #endif
191             hr = (*p)->u.HTTP_Response = (Z_HTTP_Response *)
192                 odr_malloc(o, sizeof(*hr));
193             po = i = 5;
194             while (i < o->size-2 && o->buf[i] != ' ' && o->buf[i] != '\r')
195                 i++;
196             hr->version = (char *) odr_malloc(o, i - po + 1);
197             if (i - po)
198                 memcpy(hr->version, o->buf + po, i - po);
199             hr->version[i-po] = 0;
200             if (o->buf[i] != ' ')
201             {
202                 o->error = OHTTP;
203                 return 0;
204             }
205             i++;
206             hr->code = 0;
207             while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9')
208             {
209                 hr->code = hr->code*10 + (o->buf[i] - '0');
210                 i++;
211             }
212             while (i < o->size-1 && o->buf[i] != '\r')
213                 i++;
214             return decode_headers_content(o, i, &hr->headers,
215                                           &hr->content_buf, &hr->content_len);            
216         }
217         else if (o->size > 5 &&
218             o->buf[0] >= 0x20 && o->buf[0] < 0x7f
219             && o->buf[1] >= 0x20 && o->buf[1] < 0x7f
220             && o->buf[2] >= 0x20 && o->buf[2] < 0x7f
221             && o->buf[3] >= 0x20 && o->buf[3] < 0x7f)
222         {
223             int i, po;
224             Z_HTTP_Request *hr;
225
226 #if HTTP_DEBUG
227             fprintf(stderr, "-- HTTP decode:\n%.*s\n", o->size, o->buf);
228 #endif
229             (*p)->which = Z_GDU_HTTP_Request;
230             hr = (*p)->u.HTTP_Request = 
231                 (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr));
232
233             /* method .. */
234             for (i = 0; o->buf[i] != ' '; i++)
235                 if (i >= o->size-5 || i > 30)
236                 {
237                     o->error = OHTTP;
238                     return 0;
239                 }
240             hr->method = (char *) odr_malloc(o, i+1);
241             memcpy (hr->method, o->buf, i);
242             hr->method[i] = '\0';
243             /* path */
244             po = i+1;
245             for (i = po; o->buf[i] != ' '; i++)
246                 if (i >= o->size-5)
247                 {
248                     o->error = OHTTP;
249                     return 0;
250                 }
251             hr->path = (char *) odr_malloc(o, i - po + 1);
252             memcpy (hr->path, o->buf+po, i - po);
253             hr->path[i - po] = '\0';
254             /* HTTP version */
255             i++;
256             if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5))
257             {
258                 o->error = OHTTP;
259                 return 0;
260             }
261             i+= 5;
262             po = i;
263             while (o->buf[i] != '\r')
264             {
265                 if (i >= o->size-1)
266                 {
267                     o->error = OHTTP;
268                     return 0;
269                 }
270                 i++;
271             }
272             hr->version = (char *) odr_malloc(o, i - po + 1);
273             memcpy(hr->version, o->buf + po, i - po);
274             hr->version[i - po] = '\0';
275             /* headers */
276             return decode_headers_content(o, i, &hr->headers,
277                                           &hr->content_buf, &hr->content_len);
278
279         }
280         else
281         {
282             (*p)->which = Z_GDU_Z3950;
283             return z_APDU(o, &(*p)->u.z3950, opt, 0);
284         }
285     }
286     else if (o->direction == ODR_ENCODE)
287     {
288         char sbuf[80];
289         Z_HTTP_Header *h;
290         switch((*p)->which)
291         {
292         case Z_GDU_HTTP_Response:
293             sprintf(sbuf, "HTTP/%s %d %s\r\n", (*p)->u.HTTP_Response->version,
294                     (*p)->u.HTTP_Response->code,
295                     z_HTTP_errmsg((*p)->u.HTTP_Response->code));
296             odr_write(o, (unsigned char *) sbuf, strlen(sbuf));
297             /* apply Content-Length if not already applied */
298             if (!z_HTTP_header_lookup((*p)->u.HTTP_Response->headers,
299                                       "Content-Length"))
300             {
301                 char lstr[20];
302                 sprintf(lstr, "%d", (*p)->u.HTTP_Response->content_len);
303                 z_HTTP_header_add(o,
304                                   &(*p)->u.HTTP_Response->headers,
305                                   "Content-Length", lstr);
306             }
307             for (h = (*p)->u.HTTP_Response->headers; h; h = h->next)
308             {
309                 odr_write(o, (unsigned char *) h->name, strlen(h->name));
310                 odr_write(o, (unsigned char *) ": ", 2);
311                 odr_write(o, (unsigned char *) h->value, strlen(h->value));
312                 odr_write(o, (unsigned char *) "\r\n", 2);
313             }
314             odr_write(o, (unsigned char *) "\r\n", 2);
315             if ((*p)->u.HTTP_Response->content_buf)
316                 odr_write(o, (unsigned char *) 
317                           (*p)->u.HTTP_Response->content_buf,
318                           (*p)->u.HTTP_Response->content_len);
319 #if HTTP_DEBUG
320             fprintf(stderr, "-- HTTP response:\n%.*s\n", o->top, o->buf);
321 #endif
322             break;
323         case Z_GDU_HTTP_Request:
324             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->method,
325                       strlen((*p)->u.HTTP_Request->method));
326             odr_write(o, (unsigned char *) " ", 1);
327             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->path,
328                       strlen((*p)->u.HTTP_Request->path));
329             odr_write(o, (unsigned char *) " HTTP/", 6);
330             odr_write(o, (unsigned char *) (*p)->u.HTTP_Request->version,
331                       strlen((*p)->u.HTTP_Request->version));
332             odr_write(o, (unsigned char *) "\r\n", 2);
333             if ((*p)->u.HTTP_Request->content_len &&
334                 !z_HTTP_header_lookup((*p)->u.HTTP_Request->headers,
335                                       "Content-Length"))
336             {
337                 char lstr[20];
338                 sprintf(lstr, "%d", (*p)->u.HTTP_Request->content_len);
339                 z_HTTP_header_add(o,
340                                   &(*p)->u.HTTP_Request->headers,
341                                   "Content-Length", lstr);
342             }
343             for (h = (*p)->u.HTTP_Request->headers; h; h = h->next)
344             {
345                 odr_write(o, (unsigned char *) h->name, strlen(h->name));
346                 odr_write(o, (unsigned char *) ": ", 2);
347                 odr_write(o, (unsigned char *) h->value, strlen(h->value));
348                 odr_write(o, (unsigned char *) "\r\n", 2);
349             }
350             odr_write(o, (unsigned char *) "\r\n", 2);
351             if ((*p)->u.HTTP_Request->content_buf)
352                 odr_write(o, (unsigned char *)
353                           (*p)->u.HTTP_Request->content_buf,
354                           (*p)->u.HTTP_Request->content_len);
355 #if HTTP_DEBUG
356             fprintf(stderr, "-- HTTP request:\n%.*s\n", o->top, o->buf);
357 #endif
358             break;
359         case Z_GDU_Z3950:
360             return z_APDU(o, &(*p)->u.z3950, opt, 0);
361         }
362     }
363     else if (o->direction == ODR_PRINT)
364     {
365         switch((*p)->which)
366         {
367         case Z_GDU_HTTP_Response:
368             fprintf (stderr, "not implemented");
369             break;
370         case Z_GDU_HTTP_Request:
371             fprintf (stderr, "not implemented");
372             break;
373         case Z_GDU_Z3950:
374             return z_APDU(o, &(*p)->u.z3950, opt, 0);
375         }
376     }
377     return 1;
378 }
379