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