c06777e5ca4c84573ef2c3f39b94ee481ebcc6ba
[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.1 2003-02-12 15:06:44 adam Exp $
6  */
7
8 #include <yaz/proto.h>
9
10 #define HTTP_DEBUG 0
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 = odr_malloc(o, sizeof(**headers));
41         (*headers)->name = 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 = 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 = 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 = 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_Response(ODR o, int code)
106 {
107     Z_GDU *p = odr_malloc(o, sizeof(*p));
108     Z_HTTP_Response *hres;
109
110     p->which = Z_GDU_HTTP_Response;
111     hres = p->u.HTTP_Response = odr_malloc(o, sizeof(*hres));
112     hres->headers = 0;
113     hres->content_len = 0;
114     hres->content_buf = 0;
115     hres->code = code;
116     hres->version = "1.1";
117     z_HTTP_header_add(o, &hres->headers, "Server",
118                       "YAZ/" YAZ_VERSION);
119     if (code != 200)
120     {
121         hres->content_buf = odr_malloc(o, 400);
122         sprintf (hres->content_buf, 
123                  "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
124                  "<HTML>\n"
125                  " <HEAD>\n"
126                  "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
127                  " </HEAD>\n"
128                  " <BODY>\n"
129                  "  <P><A HREF=\"http://www.indexdata.dk/yaz/\">YAZ</A> " 
130                  YAZ_VERSION "</P>\n"
131                  "  <P>Error: %d</P>\n"
132                  "  <P>Description: %.50s</P>\n"
133                  " </BODY>\n"
134                  "</HTML>\n",
135                  code, z_HTTP_errmsg(code));
136         hres->content_len = strlen(hres->content_buf);
137         z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
138     }
139     return p;
140 }
141
142 const char *z_HTTP_errmsg(int code)
143 {
144     if (code == 200)
145         return "OK";
146     else if (code == 400)
147         return "Bad Request";
148     else if (code == 404)
149         return "Not Found";
150     else if (code == 405)
151         return "Method Not Allowed";
152     else if (code == 500)
153         return "Internal Error";
154     else
155         return "Unknown Error";
156 }
157
158 int z_GDU (ODR o, Z_GDU **p, int opt, const char *name)
159 {
160     if (o->direction == ODR_DECODE) {
161         *p = odr_malloc(o, sizeof(**p));
162         if (o->size > 10 && !memcmp(o->buf, "HTTP/", 5))
163         {
164             int i, po;
165             Z_HTTP_Response *hr;
166             (*p)->which = Z_GDU_HTTP_Response;
167
168             hr = (*p)->u.HTTP_Response = odr_malloc(o, sizeof(*hr));
169
170             po = i = 5;
171             while (i < o->size-2 && o->buf[i] != ' ' && o->buf[i] != '\r')
172                 i++;
173             hr->version = odr_malloc(o, i - po + 1);
174             if (i - po)
175                 memcpy(hr->version, o->buf + po, i - po);
176             hr->version[i-po] = 0;
177             if (o->buf[i] != ' ')
178             {
179                 o->error = OHTTP;
180                 return 0;
181             }
182             i++;
183             hr->code = 0;
184             while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9')
185             {
186                 hr->code = hr->code*10 + (o->buf[i] - '0');
187                 i++;
188             }
189             while (i < o->size-1 && o->buf[i] != '\r')
190                 i++;
191             return decode_headers_content(o, i, &hr->headers,
192                                           &hr->content_buf, &hr->content_len);            
193         }
194         else if (o->size > 5 &&
195             o->buf[0] >= 0x20 && o->buf[0] < 0x7f
196             && o->buf[1] >= 0x20 && o->buf[1] < 0x7f
197             && o->buf[2] >= 0x20 && o->buf[2] < 0x7f
198             && o->buf[3] >= 0x20 && o->buf[3] < 0x7f)
199         {
200             int i, po;
201             Z_HTTP_Request *hr;
202
203 #if HTTP_DEBUG
204             fprintf(stderr, "HTTP decode:\n%.*s\n", o->size, o->buf);
205 #endif
206             (*p)->which = Z_GDU_HTTP_Request;
207             hr = (*p)->u.HTTP_Request = odr_malloc(o, sizeof(*hr));
208
209             /* method .. */
210             for (i = 0; o->buf[i] != ' '; i++)
211                 if (i >= o->size-5 || i > 30)
212                 {
213                     o->error = OHTTP;
214                     return 0;
215                 }
216             hr->method = odr_malloc(o, i+1);
217             memcpy (hr->method, o->buf, i);
218             hr->method[i] = '\0';
219             /* path */
220             po = i+1;
221             for (i = po; o->buf[i] != ' '; i++)
222                 if (i >= o->size-5)
223                 {
224                     o->error = OHTTP;
225                     return 0;
226                 }
227             hr->path = odr_malloc(o, i - po + 1);
228             memcpy (hr->path, o->buf+po, i - po);
229             hr->path[i - po] = '\0';
230             /* HTTP version */
231             i++;
232             if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5))
233             {
234                 o->error = OHTTP;
235                 return 0;
236             }
237             i+= 5;
238             po = i;
239             while (o->buf[i] != '\r')
240             {
241                 if (i >= o->size-1)
242                 {
243                     o->error = OHTTP;
244                     return 0;
245                 }
246                 i++;
247             }
248             hr->version = odr_malloc(o, i - po + 1);
249             memcpy(hr->version, o->buf + po, i - po);
250             hr->version[i - po] = '\0';
251             /* headers */
252             return decode_headers_content(o, i, &hr->headers,
253                                           &hr->content_buf, &hr->content_len);
254
255         }
256         else
257         {
258             (*p)->which = Z_GDU_Z3950;
259             return z_APDU(o, &(*p)->u.z3950, opt, 0);
260         }
261     }
262     else if (o->direction == ODR_ENCODE)
263     {
264         char sbuf[80];
265         Z_HTTP_Header *h;
266         switch((*p)->which)
267         {
268         case Z_GDU_HTTP_Response:
269             sprintf(sbuf, "HTTP/%s %d %s\r\n", (*p)->u.HTTP_Response->version,
270                     (*p)->u.HTTP_Response->code,
271                     z_HTTP_errmsg((*p)->u.HTTP_Response->code));
272             odr_write(o, sbuf, strlen(sbuf));
273             /* apply Content-Length if not already applied */
274             if (!z_HTTP_header_lookup((*p)->u.HTTP_Response->headers,
275                                       "Content-Length"))
276             {
277                 char lstr[20];
278                 sprintf(lstr, "%d", (*p)->u.HTTP_Response->content_len);
279                 z_HTTP_header_add(o,
280                                   &(*p)->u.HTTP_Response->headers,
281                                   "Content-Length", lstr);
282             }
283             for (h = (*p)->u.HTTP_Response->headers; h; h = h->next)
284             {
285                 odr_write(o, h->name, strlen(h->name));
286                 odr_write(o, ": ", 2);
287                 odr_write(o, h->value, strlen(h->value));
288                 odr_write(o, "\r\n", 2);
289             }
290             odr_write(o, "\r\n", 2);
291             if ((*p)->u.HTTP_Response->content_buf)
292                 odr_write(o, (*p)->u.HTTP_Response->content_buf,
293                           (*p)->u.HTTP_Response->content_len);
294 #if HTTP_DEBUG
295             fprintf(stderr, "HTTP response:\n%.*s\n", o->top, o->buf);
296 #endif
297             break;
298         case Z_GDU_HTTP_Request:
299             odr_write(o, (*p)->u.HTTP_Request->method,
300                       strlen((*p)->u.HTTP_Request->method));
301             odr_write(o, " ", 1);
302             odr_write(o, (*p)->u.HTTP_Request->path,
303                       strlen((*p)->u.HTTP_Request->path));
304             odr_write(o, " HTTP/", 6);
305             odr_write(o, (*p)->u.HTTP_Request->version,
306                       strlen((*p)->u.HTTP_Request->version));
307             odr_write(o, "\r\n", 2);
308             for (h = (*p)->u.HTTP_Request->headers; h; h = h->next)
309             {
310                 odr_write(o, h->name, strlen(h->name));
311                 odr_write(o, ": ", 2);
312                 odr_write(o, h->value, strlen(h->value));
313                 odr_write(o, "\r\n", 2);
314             }
315             odr_write(o, "\r\n", 2);
316             if ((*p)->u.HTTP_Request->content_buf)
317                 odr_write(o, (*p)->u.HTTP_Request->content_buf,
318                           (*p)->u.HTTP_Request->content_len);
319 #if HTTP_DEBUG
320             fprintf(stderr, "HTTP request:\n%.*s\n", o->top, o->buf);
321 #endif
322             break;
323         case Z_GDU_Z3950:
324             return z_APDU(o, &(*p)->u.z3950, opt, 0);
325         }
326     }
327     else if (o->direction == ODR_PRINT)
328     {
329         switch((*p)->which)
330         {
331         case Z_GDU_HTTP_Response:
332             fprintf (stderr, "not implemented");
333             break;
334         case Z_GDU_HTTP_Request:
335             fprintf (stderr, "not implemented");
336             break;
337         case Z_GDU_Z3950:
338             return z_APDU(o, &(*p)->u.z3950, opt, 0);
339         }
340     }
341     return 1;
342 }
343