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