* Improvements in the http code in processing http headers.
[alpine.git] / imap / src / c-client / http.c
blobd291c975aed7dcb001ec53cff2602e49a2b66521
1 /*
2 * Copyright 2018-2022 Eduardo Chappa
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
11 #include <ctype.h>
12 #include <stdio.h>
13 #include <time.h>
14 #include "c-client.h" /* this includes http.h */
15 #include "flstring.h"
16 #include "netmsg.h"
18 unsigned long http_debug;
20 //char t[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~";
21 static char http_notok[] = "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\42\50\51\54\57\72\73\74\75\76\77\100\133\134\135\173\175\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";
22 static char http_noparam_val[] = "\1\2\3\4\5\6\7\10\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\42\134\177";
24 #define HTP_NOVAL 0x001 /* the header accepts parameters without value */
26 #define HTP_UNLIMITED (-1) /* parse and infinite list */
28 #if 0
29 typedef struct http_val_param_s {
30 unsigned char *value;
31 PARAMETER *plist;
32 } HTTP_VAL_PARAM_S;
34 typedef struct http_param_list_s {
35 HTTP_VAL_PARAM_S *vp;
36 struct http_param_list_s *next;
37 } HTTP_PARAM_LIST_S;
39 typedef struct http_header_value_s {
40 unsigned char *data;
41 HTTP_PARAM_LIST_S *p;
42 } HTTP_HEADER_S;
44 typedef struct http_header_data_s {
45 HTTP_HEADER_S *accept, /* RFC 7231, Section 5.3.2 */
46 *accept_charset, /* RFC 7231, Section 5.3.3 */
47 *accept_encoding, /* RFC 7231, Section 5.3.4 */
48 *accept_language, /* RFC 7231, Section 5.3.5 */
49 *accept_ranges, /* RFC 7233, Section 2.3 */
50 *age, /* RFC 7234, Section 5.1 */
51 *allow, /* RFC 7231, Section 7.4.1 */
52 *cache_control, /* RFC 7234, Section 5.2 */
53 *connection, /* RFC 7230, Section 6.1 */
54 *content_encoding, /* RFC 7231, Section 3.1.2.2 */
55 *content_disposition, /* RFC 6266 */
56 *content_language, /* RFC 7231, Section 3.1.3.2 */
57 *content_length, /* RFC 7230, Section 3.3.2 */
58 *content_location, /* RFC 7231, Section 3.1.4.2 */
59 *content_type, /* RFC 7231, Section 3.1.1.5 */
60 *date, /* RFC 7231, Section 7.1.1.2 */
61 *etag, /* RFC 7232, Section 2.3 */
62 *expect, /* RFC 7231, Section 5.1.1 */
63 *expires, /* RFC 7234, Section 5.3 */
64 *from, /* RFC 7231, Section 5.5.1 */
65 *host, /* RFC 7230, Section 5.4 */
66 *last_modified, /* RFC 7232, Section 2.2 */
67 *location, /* RFC 7231, Section 7.1.2 */
68 *max_forwards, /* RFC 7231, Section 5.1.2 */
69 *mime_version, /* RFC 7231, Appendix A.1 */
70 *pragma, /* RFC 7234, Section 5.4 */
71 *proxy_authenticate, /* RFC 7235, Section 4.3 */
72 *referer, /* RFC 7231, Section 5.5.2 */
73 *retry_after, /* RFC 7231, Section 7.1.3 */
74 *server, /* RFC 7231, Section 7.4.2 */
75 *te, /* RFC 7230, Section 4.3 */
76 *trailer, /* RFC 7230, Section 4.4 */
77 *transfer_encoding, /* RFC 7230, Section 3.3.1 */
78 *upgrade, /* RFC 7230, Section 6.7 */
79 *user_agent, /* RFC 7231, Section 5.5.3 */
80 *via, /* RFC 7230, Section 5.7.1 */
81 *vary, /* RFC 7231, Section 7.1.4 */
82 *warning, /* RFC 7234, Section 5.5 */
83 *www_authenticate; /* RFC 7235, Section 4.1 */
84 } HTTP_HEADER_DATA_S;
85 #endif
87 /* helper functions */
88 HTTP_STATUS_S *http_status_line_get(unsigned char *);
89 void http_status_line_free(HTTP_STATUS_S **);
90 void http_header_free(HTTP_HEADER_DATA_S **);
91 void buffer_add(unsigned char **, unsigned char *);
92 unsigned char *hex_escape_url_part(unsigned char *, unsigned char *);
93 unsigned char *encode_url_body_part(unsigned char *, unsigned char *);
95 /* HTTP function prototypes */
96 long http_reply (HTTPSTREAM *);
97 long http_fake (HTTPSTREAM *, unsigned char *);
99 void http_skipows(unsigned char **);
100 void http_remove_trailing_ows(unsigned char *);
102 int valid_dquote_text(unsigned char *);
103 #define valid_token_name(X) (strpbrk((X), http_notok) ? 0 : 1)
104 #define valid_parameter_value(X) \
105 ((valid_token_name((X)) || valid_dquote_text((X))) ? 1 : 0)
107 /* HTTP HEADER FUNCTIONS */
108 void http_add_header_data(HTTPSTREAM *, unsigned char *);
109 void http_add_data_to_header(HTTP_HEADER_S **, unsigned char *);
111 HTTP_PARAM_LIST_S *http_parse_token_parameter(unsigned char *, int);
112 HTTP_PARAM_LIST_S *http_parse_token_list(unsigned char *, int);
113 PARAMETER *http_parse_parameter(unsigned char *, int);
115 void http_parse_headers(HTTPSTREAM *);
117 void *
118 http_parameters (long function,void *value)
120 void *ret = NIL;
121 switch((int) function){
122 case SET_HTTPDEBUG: http_debug = (long) value;
123 case GET_HTTPDEBUG: ret = (void *) http_debug;
124 break;
126 return ret;
130 unsigned char *
131 http_response_from_reply(HTTPSTREAM *stream)
133 unsigned char *rv = NULL, *s;
135 if(stream == NULL || stream->reply == NULL || stream->header == NULL)
136 return rv;
138 s = strstr(stream->reply, "\r\n\r\n");
139 if(s != NULL) rv = s + 4;
141 return s ? rv : NIL;
144 void
145 http_parse_headers(HTTPSTREAM *stream)
147 HTTP_HEADER_DATA_S *hd;
148 HTTP_HEADER_S *h;
150 if(!stream || !stream->header) return;
152 hd = stream->header;
154 if(((h = hd->accept)) && h->data){ /* RFC 7231, Section 5.3.2 */
155 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
156 fs_give((void **) &h->data);
159 if(((h = hd->accept_charset)) && h->data){ /* RFC 7231, Section 5.3.3 */
160 h->p = http_parse_token_parameter(h->data, 0);
161 fs_give((void **) &h->data);
164 if(((h = hd->accept_encoding)) && h->data){ /* RFC 7231, Section 5.3.4 */
165 h->p = http_parse_token_parameter(h->data, 0);
166 fs_give((void **) &h->data);
169 if(((h = hd->accept_language)) && h->data){ /* RFC 7231, Section 5.3.5 */
170 h->p = http_parse_token_parameter(h->data, 0);
171 fs_give((void **) &h->data);
174 if(((h = hd->accept_ranges)) && h->data){ /* RFC 7233, Section 2.3 */
175 h->p = http_parse_token_parameter(h->data, 0);
176 fs_give((void **) &h->data);
179 if(((h = hd->age)) && h->data){ /* RFC 7234, Section 5.1 */
180 h->p = http_parse_token_list(h->data, 1);
181 fs_give((void **) &h->data);
184 if(((h = hd->allow)) && h->data){ /* RFC 7231, Section 7.4.1 */
185 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
186 fs_give((void **) &h->data);
189 if(((h = hd->cache_control)) && h->data){ /* RFC 7234, Section 5.2 */
190 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
191 fs_give((void **) &h->data);
194 if(((h = hd->connection)) && h->data){ /* RFC 7230, Section 6.1 */
195 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
196 fs_give((void **) &h->data);
199 if(((h = hd->content_encoding)) && h->data){ /* RFC 7231, Section 3.1.2.2 */
200 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
201 fs_give((void **) &h->data);
204 if(((h = hd->content_disposition)) && h->data){ /* RFC 6266 */
205 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
206 fs_give((void **) &h->data);
209 if(((h = hd->content_language)) && h->data){ /* RFC 7231, Section 3.1.3.2 */
210 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
211 fs_give((void **) &h->data);
214 if(((h = hd->content_length)) && h->data){ /* RFC 7230, Section 3.3.2 */
215 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
216 fs_give((void **) &h->data);
219 if(((h = hd->content_location)) && h->data){ /* RFC 7231, Section 3.1.4.2 */
220 h->p = http_parse_token_list(h->data, 1);
221 fs_give((void **) &h->data);
224 if(((h = hd->content_type)) && h->data){ /* RFC 7231, Section 3.1.1.5 */
225 h->p = http_parse_token_parameter(h->data, 0);
226 fs_give((void **) &h->data);
229 if(((h = hd->date)) && h->data){ /* RFC 7231, Section 7.1.1.2 */
230 h->p = http_parse_token_list(h->data, 1);
231 fs_give((void **) &h->data);
234 if(((h = hd->etag)) && h->data){ /* Rewrite this. RFC 7232, Section 2.3 */
235 h->p = http_parse_token_list(h->data, 1);
236 fs_give((void **) &h->data);
239 if(((h = hd->expect)) && h->data){ /* Rewrite this. RFC 7231, Section 5.1.1 */
240 h->p = http_parse_token_list(h->data, 1);
241 fs_give((void **) &h->data);
244 if(((h = hd->expires)) && h->data){ /* Rewrite this. RFC 7234, Section 5.3 */
245 h->p = http_parse_token_list(h->data, 1);
246 fs_give((void **) &h->data);
249 if(((h = hd->from)) && h->data){ /* Rewrite this. RFC 7231, Section 5.5.1 */
250 h->p = http_parse_token_list(h->data, 1);
251 fs_give((void **) &h->data);
254 if(((h = hd->host)) && h->data){ /* Rewrite this. RFC 7230, Section 5.4 */
255 h->p = http_parse_token_list(h->data, 1);
256 fs_give((void **) &h->data);
259 if(((h = hd->last_modified)) && h->data){ /* Rewrite this. RFC 7232, Section 2.2 */
260 h->p = http_parse_token_list(h->data, 1);
261 fs_give((void **) &h->data);
264 if(((h = hd->location)) && h->data){ /* Rewrite this. RFC 7231, Section 7.1.2 */
265 h->p = http_parse_token_list(h->data, 1);
266 fs_give((void **) &h->data);
269 if(((h = hd->max_forwards)) && h->data){ /* RFC 7231, Section 5.1.2 */
270 h->p = http_parse_token_list(h->data, 1);
271 fs_give((void **) &h->data);
274 if(((h = hd->mime_version)) && h->data){ /* Rewrite this. RFC 7231, Appendix A.1 */
275 h->p = http_parse_token_list(h->data, 1);
276 fs_give((void **) &h->data);
279 if(((h = hd->pragma)) && h->data){ /* RFC 7234, Section 5.4 */
280 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
281 fs_give((void **) &h->data);
284 if(((h = hd->proxy_authenticate)) && h->data){ /* Rewrite this. RFC 7235, Section 4.3 */
285 h->p = http_parse_token_parameter(h->data, 0);
286 fs_give((void **) &h->data);
289 if(((h = hd->referer)) && h->data){ /* Rewrite this. RFC 7231, Section 5.5.2 */
290 h->p = http_parse_token_list(h->data, 1);
291 fs_give((void **) &h->data);
294 if(((h = hd->retry_after)) && h->data){ /* Rewrite this. RFC 7231, Section 7.1.3 */
295 h->p = http_parse_token_list(h->data, 1);
296 fs_give((void **) &h->data);
299 if(((h = hd->server)) && h->data){ /* Rewrite this. RFC 7231, Section 7.4.2 */
300 h->p = http_parse_token_list(h->data, 1);
301 fs_give((void **) &h->data);
304 if(((h = hd->te)) && h->data){ /* Rewrite this. RFC 7230, Section 4.3 */
305 h->p = http_parse_token_parameter(h->data, 0);
306 fs_give((void **) &h->data);
309 if(((h = hd->trailer)) && h->data){ /* RFC 7230, Section 4.4 */
310 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
311 fs_give((void **) &h->data);
314 if(((h = hd->transfer_encoding)) && h->data){ /* Rewrite this. RFC 7230, Section 3.3.1 */
315 h->p = http_parse_token_parameter(h->data, 0);
316 fs_give((void **) &h->data);
319 if(((h = hd->upgrade)) && h->data){ /* Rewrite this. RFC 7230, Section 6.7 */
320 h->p = http_parse_token_list(h->data, 1);
321 fs_give((void **) &h->data);
324 if(((h = hd->user_agent)) && h->data){ /* Rewrite this. RFC 7231, Section 5.5.3 */
325 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
326 fs_give((void **) &h->data);
329 if(((h = hd->via)) && h->data){ /* Rewrite this. RFC 7230, Section 5.7.1 */
330 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
331 fs_give((void **) &h->data);
334 if(((h = hd->vary)) && h->data){ /* Rewrite this. RFC 7231, Section 7.1.4 */
335 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
336 fs_give((void **) &h->data);
339 if(((h = hd->warning)) && h->data){ /* Rewrite this. RFC 7234, Section 5.5 */
340 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
341 fs_give((void **) &h->data);
344 if(((h = hd->www_authenticate)) && h->data){ /* Rewrite this. RFC 7235, Section 4.1 */
345 h->p = http_parse_token_parameter(h->data, 0);
346 fs_give((void **) &h->data);
351 void
352 http_add_data_to_header(HTTP_HEADER_S **headerp, unsigned char *data)
354 HTTP_HEADER_S *h = *headerp;
356 if(!h){
357 h = fs_get(sizeof(HTTP_HEADER_S));
358 memset((void *)h, 0, sizeof(HTTP_HEADER_S));
361 if(h->data) buffer_add(&h->data, ", ");
362 buffer_add(&h->data, data);
363 *headerp = h;
366 void
367 http_add_header_data(HTTPSTREAM *stream, unsigned char *hdata)
369 unsigned char *hname, *h;
370 int found = 1;
372 if(!stream || !hdata || !*hdata) return;
374 if(!stream->header){
375 stream->header = fs_get(sizeof(HTTP_HEADER_DATA_S));
376 memset((void *) stream->header, 0, sizeof(HTTP_HEADER_DATA_S));
380 /* extract header name first */
381 if((h = strchr(hdata, ':'))){
382 *h = '\0';
383 hname = fs_get((h-hdata+2)*sizeof(char));
384 strncpy(hname, hdata, h-hdata);
385 hname[h-hdata] = '\0';
386 if(!valid_token_name(hname))
387 return;
388 hname[h-hdata] = ':';
389 hname[h-hdata+1] = '\0';
390 *h++ = ':';
392 else return;
394 switch(*hname){
395 case 'A':
396 case 'a': if(!compare_cstring(hname+1, "ccept:")) /* RFC 7231, Section 5.3.2 */
397 http_add_data_to_header(&stream->header->accept, h);
398 else if(!compare_cstring(hname+1, "ccept-charset:")) /* RFC 7231, Section 5.3.3 */
399 http_add_data_to_header(&stream->header->accept_charset, h);
400 else if(!compare_cstring(hname+1, "ccept-encoding:")) /* RFC 7231, Section 5.3.4 */
401 http_add_data_to_header(&stream->header->accept_encoding, h);
402 else if(!compare_cstring(hname+1, "ccept-language:")) /* RFC 7231, Section 5.3.5 */
403 http_add_data_to_header(&stream->header->accept_language, h);
404 else if(!compare_cstring(hname+1, "ccept-ranges:")) /* RFC 7233, Section 2.3 */
405 http_add_data_to_header(&stream->header->accept_ranges, h);
406 else if(!compare_cstring(hname+1, "ge:")) /* RFC 7234, Section 5.1 */
407 http_add_data_to_header(&stream->header->age, h);
408 else if(!compare_cstring(hname+1, "llow:")) /* RFC 7231, Section 7.4.1 */
409 http_add_data_to_header(&stream->header->allow, h);
410 else found = 0;
411 break;
413 case 'C':
414 case 'c': if(!compare_cstring(hname+1, "ache-control:")) /* RFC 7234, Section 5.2 */
415 http_add_data_to_header(&stream->header->cache_control, h);
416 else if(!compare_cstring(hname+1, "onnection:")) /* RFC 7230, Section 6.1 */
417 http_add_data_to_header(&stream->header->connection, h);
418 else if(!compare_cstring(hname+1, "ontent-disposition:")) /* RFC 6266 */
419 http_add_data_to_header(&stream->header->content_disposition, h);
420 else if(!compare_cstring(hname+1, "ontent-encoding:")) /* RFC 7231, Section 3.1.2.2 */
421 http_add_data_to_header(&stream->header->content_encoding, h);
422 else if(!compare_cstring(hname+1, "ontent-language:")) /* RFC 7231, Section 3.1.3.2 */
423 /* rewrite this */ http_add_data_to_header(&stream->header->content_language, h);
424 else if(!compare_cstring(hname+1, "ontent-length:")) /* RFC 7230, Section 3.3.2 */
425 http_add_data_to_header(&stream->header->content_length, h);
426 else if(!compare_cstring(hname+1, "ontent-location:")) /* RFC 7231, Section 3.1.4.2 */
427 /* rewrite this */ http_add_data_to_header(&stream->header->content_location, h);
428 else if(!compare_cstring(hname+1, "ontent-type:")) /* RFC 7231, Section 3.1.1.5 */
429 http_add_data_to_header(&stream->header->content_type, h);
430 else found = 0;
431 break;
433 case 'D':
434 case 'd': if(!compare_cstring(hname+1, "ate:")) /* RFC 7231, Section 7.1.1.2 */
435 /* revise this */ http_add_data_to_header(&stream->header->date, h);
436 else found = 0;
437 break;
439 case 'E':
440 case 'e': if(!compare_cstring(hname+1, "tag:")) /* RFC 7232, Section 2.3 */
441 /* rewrite this */ http_add_data_to_header(&stream->header->etag, h);
442 else if(!compare_cstring(hname+1, "xpect:")) /* RFC 7231, Section 5.1.1 */
443 /* rewrite this */ http_add_data_to_header(&stream->header->expect, h);
444 else if(!compare_cstring(hname+1, "xpires:")) /* RFC 7234, Section 5.3 */
445 /* rewrite this */ http_add_data_to_header(&stream->header->expires, h);
446 else found = 0;
447 break;
449 case 'F':
450 case 'f': if(!compare_cstring(hname+1, "rom:")) /* RFC 7231, Section 5.5.1 */
451 /* rewrite this */ http_add_data_to_header(&stream->header->from, h);
452 else found = 0;
453 break;
455 case 'H':
456 case 'h': if(!compare_cstring(hname+1, "ost:")) /* RFC 7230, Section 5.4 */
457 http_add_data_to_header(&stream->header->host, h);
458 else found = 0;
459 break;
461 case 'L':
462 case 'l': if(!compare_cstring(hname+1, "ast-modified:")) /* RFC 7232, Section 2.2 */
463 http_add_data_to_header(&stream->header->last_modified, h);
464 else if(!compare_cstring(hname+1, "ocation:")) /* RFC 7231, Section 7.1.2 */
465 http_add_data_to_header(&stream->header->location, h);
466 else found = 0;
467 break;
469 case 'M':
470 case 'm': if(!compare_cstring(hname+1, "ax-forwards:")) /* RFC 7231, Section 5.1.2 */
471 http_add_data_to_header(&stream->header->max_forwards, h);
472 else if(!compare_cstring(hname+1, "ime-version:")) /* RFC 7231, Appendix A.1 */
473 http_add_data_to_header(&stream->header->mime_version, h);
474 else found = 0;
475 break;
477 case 'P':
478 case 'p': if(!compare_cstring(hname+1, "ragma:")) /* RFC 7234, Section 5.4 */
479 http_add_data_to_header(&stream->header->pragma, h);
480 else if(!compare_cstring(hname+1, "roxy-authenticate:")) /* RFC 7235, Section 4.3 */
481 http_add_data_to_header(&stream->header->proxy_authenticate, h);
482 else found = 0;
483 break;
485 case 'R':
486 case 'r': if(!compare_cstring(hname+1, "eferer:")) /* RFC 7231, Section 5.5.2 */
487 http_add_data_to_header(&stream->header->referer, h);
488 else if(!compare_cstring(hname+1, "etry-after:")) /* RFC 7231, Section 7.1.3 */
489 http_add_data_to_header(&stream->header->retry_after, h);
490 else found = 0;
491 break;
493 case 'S':
494 case 's': if(!compare_cstring(hname+1, "erver:")) /* RFC 7231, Section 7.4.2 */
495 http_add_data_to_header(&stream->header->server, h);
496 else found = 0;
497 break;
499 case 'T':
500 case 't': if(!compare_cstring(hname+1, "e:")) /* RFC 7230, Section 4.3 */
501 http_add_data_to_header(&stream->header->te, h);
502 else if(!compare_cstring(hname+1, "railer:")) /* RFC 7230, Section 4.4 */
503 http_add_data_to_header(&stream->header->trailer, h);
504 else if(!compare_cstring(hname+1, "ransfer-encoding:")) /* RFC 7230, Section 3.3.1 */
505 http_add_data_to_header(&stream->header->transfer_encoding, h);
506 else found = 0;
507 break;
508 break;
510 case 'U':
511 case 'u': if(!compare_cstring(hname+1, "pgrade:")) /* RFC 7230, Section 6.7 */
512 http_add_data_to_header(&stream->header->upgrade, h);
513 else if(!compare_cstring(hname+1, "ser-agent:")) /* RFC 7231, Section 5.5.3 */
514 http_add_data_to_header(&stream->header->user_agent, h);
515 else found = 0;
516 break;
518 case 'V':
519 case 'v': if(!compare_cstring(hname+1, "ia:")) /* RFC 7230, Section 5.7.1 */
520 http_add_data_to_header(&stream->header->via, h);
521 else if(!compare_cstring(hname+1, "ary:")) /* RFC 7231, Section 7.1.4 */
522 http_add_data_to_header(&stream->header->vary, h);
523 else found = 0;
524 break;
526 case 'W':
527 case 'w': if(!compare_cstring(hname+1, "arning:")) /* RFC 7234, Section 5.5 */
528 http_add_data_to_header(&stream->header->warning, h);
529 else if(!compare_cstring(hname+1, "ww-authenticate:")) /* RFC 7235, Section 4.1 */
530 http_add_data_to_header(&stream->header->www_authenticate, h);
531 else found = 0;
532 break;
534 default: break;
539 /* parse a list of tokens. If num is positive, parse at most
540 * num members in the list. Set num to HTP_UNLIMITED for a list
541 * without bounds
543 HTTP_PARAM_LIST_S *
544 http_parse_token_list(unsigned char *h, int num)
546 unsigned char *s = h, *t, c;
547 HTTP_PARAM_LIST_S *rv = NIL;
549 if(!s || !*s || num == 0) return NIL;
550 http_skipows(&s);
551 if(!*s) return NIL;
552 for(t = s; *t != '\0' && *t != ','; t++);
553 c = *t; *t = '\0';
554 http_remove_trailing_ows(s);
556 if(!valid_token_name(s))
557 return c == ',' ? http_parse_token_list(t+1, num) : NIL;
559 if(num > 0) num--; /* this one counts! */
560 rv = fs_get(sizeof(HTTP_PARAM_LIST_S));
561 memset((void *) rv, 0, sizeof(HTTP_PARAM_LIST_S));
562 rv->vp = fs_get(sizeof(HTTP_VAL_PARAM_S));
563 memset((void *) rv->vp, 0, sizeof(HTTP_VAL_PARAM_S));
564 rv->vp->value = cpystr(s);
565 *t = c;
566 if(c == ',')
567 rv->next = http_parse_token_list(t+1, num);
569 return rv;
574 * parse a list of tokens with optional parameters
575 * into a HEADER_DATA structure. Do not parse into
576 * it anything invalid.
578 HTTP_PARAM_LIST_S *
579 http_parse_token_parameter(unsigned char *h, int flag)
581 unsigned char *s = h, *t, *u, c, d;
582 HTTP_PARAM_LIST_S *rv = NIL;
585 * Step 1:
586 * isolate first list element from list and remove
587 * leading and trailing white space.
589 if(!s) return NIL;
590 http_skipows(&s);
591 if(!*s) return NIL;
592 for(t = s; *t != '\0' && *t != ','; t++);
593 c = *t; *t = '\0';
594 http_remove_trailing_ows(s);
597 * Step 2:
598 * isolate token name from its parameters. Remove
599 * any trailing spaces. If not valid token, move
600 * to the next entry in the list.
602 for(u = s; *u != '\0' && *u != ';'; u++);
603 d = *u; *u = '\0';
604 http_remove_trailing_ows(s);
605 if(!valid_token_name(s))
606 return c == ',' ? http_parse_token_parameter(t+1, flag) : NIL;
609 * Step 3:
610 * If we make it this far, create a non-null reply
611 * and parse the token and parameters into a
612 * HTTP_HEADER_DATA_S structure
614 rv = fs_get(sizeof(HTTP_PARAM_LIST_S));
615 memset((void *) rv, 0, sizeof(HTTP_PARAM_LIST_S));
616 rv->vp = fs_get(sizeof(HTTP_VAL_PARAM_S));
617 memset((void *) rv->vp, 0, sizeof(HTTP_VAL_PARAM_S));
618 rv->vp->value = cpystr(s);
619 if(d == ';')
620 rv->vp->plist = http_parse_parameter(u+1, flag);
621 *u = d;
622 *t = c;
623 if(c == ',')
624 rv->next = http_parse_token_parameter(t+1, flag);
626 return rv;
630 valid_dquote_text(unsigned char *s)
632 unsigned char *t;
634 if(!s || *s != '\"') return 0;
636 t = strchr(s+1, '\"');
637 return (t && !t[1]) ? 1 : 0;
641 void
642 http_skipows(unsigned char **sp)
644 unsigned char *s = *sp;
645 for(; *s == ' ' || *s == '\t'; s++);
646 *sp = s;
649 void
650 http_remove_trailing_ows(unsigned char *s)
652 unsigned char *t;
653 for(t = s; strlen(t) > 0 ;)
654 if(t[strlen(t)-1] == ' ' || t[strlen(t)-1] == '\t')
655 t[strlen(t)-1] = '\0';
656 else
657 break;
660 PARAMETER *
661 http_parse_parameter(unsigned char *s, int flag)
663 PARAMETER *p;
664 unsigned char *t, *u, c;
666 /* Step 1:
667 * separate the parameters into a list separated by ";"
669 if(!s || !*s) return NIL;
670 http_skipows(&s);
671 if(!*s) return NIL;
672 for(t = s; *t != '\0' && *t != ';'; t++);
673 c = *t; *t = '\0';
675 /* Now we look for separation of attribute and value */
676 u = strchr(s, '=');
678 if(u){
679 *u = '\0';
680 http_remove_trailing_ows(s); http_remove_trailing_ows(u+1);
681 if(!valid_token_name(s) || !valid_parameter_value(u+1))
682 return c == ';' ? http_parse_parameter(t+1, flag) : NIL;
683 p = mail_newbody_parameter();
684 p->attribute = cpystr(s);
685 p->value = cpystr(u+1);
686 p->next = c == ';' ? http_parse_parameter(t+1, flag) : NIL;
687 *u = '=';
689 else if(flag & HTP_NOVAL){
690 /* this is a parameter with attribute but no value. RFC 7231
691 * section 5.3.2 allows this.
693 http_remove_trailing_ows(s);
694 if(!valid_token_name(s))
695 return c == ';' ? http_parse_parameter(t+1, flag) : NIL;
696 p = mail_newbody_parameter();
697 p->attribute = cpystr(s);
698 p->next = c == ';' ? http_parse_parameter(t+1, flag) : NIL;
699 } else
700 p = c == ';' ? http_parse_parameter(t+1, flag) : NIL;
702 return p;
705 unsigned char *
706 http_get_param_url(unsigned char *url, HTTP_PARAM_S *param)
708 int i;
709 unsigned char *rv = NULL;
710 HTTP_PARAM_S enc_param;
712 buffer_add(&rv, url);
713 for(i = 0; param[i].name != NULL; i++){
714 enc_param.name = hex_escape_url_part(param[i].name, NULL);
715 enc_param.value = hex_escape_url_part(param[i].value, NULL);
716 buffer_add(&rv, i == 0 ? "?" : "&");
717 buffer_add(&rv, enc_param.name);
718 buffer_add(&rv, "=");
719 buffer_add(&rv, enc_param.value);
720 fs_give((void **) &enc_param.name);
721 fs_give((void **) &enc_param.value);
724 return rv;
727 HTTP_REQUEST_S *
728 http_request_get(void)
730 HTTP_REQUEST_S *rv = fs_get(sizeof(HTTP_REQUEST_S));
731 memset((void *) rv, 0, sizeof(HTTP_REQUEST_S));
733 return rv;
736 void
737 http_request_free(HTTP_REQUEST_S **hr)
739 if(!hr) return;
741 if((*hr)->request) fs_give((void **) &(*hr)->request);
742 if((*hr)->header) fs_give((void **) &(*hr)->header);
743 if((*hr)->body) fs_give((void **) &(*hr)->body);
744 fs_give((void **) hr);
747 unsigned char *
748 http_request_line(unsigned char *method, unsigned char *target, unsigned char *version)
750 int len = strlen(method) + strlen(target) + strlen(version) + 2 + 1;
751 unsigned char *line = fs_get(len*sizeof(char));
753 sprintf(line, "%s %s %s", method, target, version);
754 return line;
757 void
758 http_add_header(HTTP_REQUEST_S **reqp, unsigned char *name, unsigned char *value)
760 int len, hlen;
762 if(!reqp) return;
764 if(!*reqp) *reqp = http_request_get();
766 len = strlen(name) + 2 + strlen(value) + 2 + 1;
767 hlen = (*reqp)->header ? strlen((*reqp)->header) : 0;
768 len += hlen;
769 fs_resize((void **) &(*reqp)->header, len*sizeof(char));
770 sprintf((*reqp)->header + hlen, "%s: %s\015\012", name, value);
773 void
774 buffer_add(unsigned char **bufp, unsigned char *text)
776 int len;
778 if(!bufp || !text || !*text) return;
780 len = *bufp ? strlen(*bufp) : 0;
781 fs_resize((void **) bufp, (len + strlen(text) + 1)*sizeof(char));
782 (*bufp)[len] = '\0';
783 strcat(*bufp, text);
786 void
787 http_add_body(HTTP_REQUEST_S **reqp, unsigned char *text)
789 if(!reqp) return;
791 if(!*reqp) *reqp = http_request_get();
793 buffer_add(&(*reqp)->body, text);
797 /* NULL terminated list of HTTP_PARAM_S objects.
798 * If caller needs "x" parameters, call this function
799 * with argument "x+1".
801 HTTP_PARAM_S *
802 http_param_get(int len)
804 HTTP_PARAM_S *http_params;
806 http_params = fs_get(len*sizeof(HTTP_PARAM_S));
807 memset((void *) http_params, 0, len*sizeof(HTTP_PARAM_S));
808 return http_params;
811 void
812 http_param_free(HTTP_PARAM_S **param)
814 int i;
816 if(param == NULL) return;
818 for(i = 0; (*param)[i].name != NULL; i++)
819 fs_give((void **) &(*param)[i].name);
821 for(i = 0; (*param)[i].value != NULL; i++)
822 fs_give((void **) &(*param)[i].value);
824 fs_give((void **) param);
828 /* This encodes for a GET request */
829 unsigned char *
830 hex_escape_url_part(unsigned char *text, unsigned char *addsafe)
832 char *safechars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-";
833 unsigned char *s = fs_get((3*strlen(text) + 1)*sizeof(char)), *t;
835 *s = '\0';
836 for(t = text; t != NULL && *t != '\0'; t++)
837 if(strchr(safechars, *t) != NULL
838 || (addsafe != NULL && strchr(addsafe, *t) != NULL))
839 sprintf(s + strlen(s), "%c", *t);
840 else
841 sprintf(s + strlen(s), "%%%X", *t);
842 fs_resize((void **) &s, (strlen(s)+1)*sizeof(char));
843 return s;
846 /* this encodes for a POST request */
847 unsigned char *
848 encode_url_body_part(unsigned char *text, unsigned char *addsafe)
850 char *safechars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-";
851 unsigned char *s = fs_get((3*strlen(text) + 1)*sizeof(char)), *t;
853 *s = '\0';
854 for(t = text; t != NULL && *t != '\0'; t++)
855 if(*t == ' ') /* ASCII 32 is never safe, must always be encoded */
856 sprintf(s + strlen(s), "%c", '+');
857 else if(strchr(safechars, *t) != NULL
858 || (addsafe != NULL && strchr(addsafe, *t) != NULL))
859 sprintf(s + strlen(s), "%c", *t);
860 else
861 sprintf(s + strlen(s), "%%%X", *t);
862 fs_resize((void **) &s, (strlen(s)+1)*sizeof(char));
863 return s;
867 http_valid_net_parse (unsigned char *url, NETMBX *mb)
869 int i, len;
870 unsigned char *s;
871 char *p;
873 if((url == NIL)
874 || (url[0] != 'h' && url[0] != 'H')
875 || (url[1] == 't' && url[1] == 'T')
876 || (url[2] == 't' && url[2] == 'T')
877 || (url[3] == 'p' && url[3] == 'P'))
878 return 0;
880 if(url[i = 4] == 's' || url[i] == 'S')
881 mb->sslflag = mb->notlsflag = T;
882 else i = 3;
884 if(url[++i] != ':' || url[++i] != '/' || url[++i] != '/')
885 return 0;
887 strcpy(mb->service, "http");
888 s = strchr(url+i+1, '/');
889 len = s ? s - url - i - 1 : strlen(url+i+1);
890 strncpy(mb->orighost, url+i+1, len);
891 mb->orighost[len] = '\0';
892 if((p = strchr(mb->orighost, ':')) != NULL){
893 *p++ = '\0';
894 mb->port = strtoul(p, &p, 10);
895 if(mb->port == 0L || *p != '\0')
896 return NIL;
898 strcpy(mb->host, mb->orighost);
899 return T;
902 HTTPSTREAM *
903 http_open (unsigned char *url)
905 HTTPSTREAM *stream;
906 NETMBX mb;
907 unsigned char *s;
909 memset((void *) &mb, 0, sizeof(NETMBX));
910 if(http_valid_net_parse (url,&mb) == 0)
911 return NIL;
913 stream = fs_get(sizeof(HTTPSTREAM));
914 memset((void *) stream, 0, sizeof(HTTPSTREAM));
916 s = strchr((char *) url + 7 + (mb.trysslflag ? 1 : 0) + 1, '/'); /* 7 = strlen("http://") + 1 */
917 stream->url = cpystr(url);
918 stream->urlhost = cpystr(mb.orighost);
919 stream->urltail = cpystr(s ? (char *) s : "/");
920 stream->netstream = net_open (&mb, NIL, mb.port ? mb.port : HTTPTCPPORT,
921 (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
922 "https", mb.port ? mb.port : HTTPSSLPORT);
923 stream->debug = http_debug;
924 if(!stream->netstream){
925 http_close(stream);
926 stream = NIL;
928 return stream;
931 unsigned char *
932 http_post_param(HTTPSTREAM *stream, HTTP_PARAM_S *param)
934 HTTP_PARAM_S enc_param;
935 HTTP_REQUEST_S *http_request;
936 unsigned char *response = NULL;
937 int i;
939 if(stream == NULL || param == NULL ) return response;
941 http_request = http_request_get();
942 http_request->request = http_request_line("POST", stream->urltail, HTTP_1_1_VERSION);
943 http_add_header(&http_request, "Host", stream->urlhost);
944 http_add_header(&http_request, "Content-Type", HTTP_MIME_URLENCODED);
946 for(i = 0; param[i].name != NULL; i++){
947 enc_param.name = encode_url_body_part(param[i].name, NULL);
948 enc_param.value = encode_url_body_part(param[i].value, NULL);
949 if(i > 0)
950 http_add_body(&http_request, "&");
951 http_add_body(&http_request, enc_param.name);
952 http_add_body(&http_request, "=");
953 http_add_body(&http_request, enc_param.value);
954 fs_give((void **) &enc_param.name);
955 fs_give((void **) &enc_param.value);
958 if(http_send(stream, http_request)){
959 unsigned char *s = http_response_from_reply(stream);
960 response = cpystr(s ? (char *) s : "");
963 http_request_free(&http_request);
965 return response;
968 unsigned char *
969 http_get(HTTPSTREAM *stream, HTTP_PARAM_S **h)
971 HTTP_REQUEST_S *http_request;
972 unsigned char *response = NIL;
973 int i;
975 if(!stream) return response;
977 http_request = http_request_get();
978 http_request->request = http_request_line("GET", stream->urltail, HTTP_1_1_VERSION);
979 http_add_header(&http_request, "Host", stream->urlhost);
980 for(i = 0; h && h[i]->name && h[i]->value; i++)
981 http_add_header(&http_request, h[i]->name, h[i]->value);
983 if(http_send(stream, http_request)){
984 unsigned char *s = http_response_from_reply(stream);
985 response = cpystr(s ? (char *) s : "");
988 http_request_free(&http_request);
990 return response;
993 void
994 http_header_free(HTTP_HEADER_DATA_S **hdata)
996 if(hdata == NULL || *hdata == NULL) return;
998 fs_give((void **) hdata);
1001 void
1002 http_close (HTTPSTREAM *stream)
1004 if(stream){
1005 if (stream->netstream) net_close (stream->netstream);
1006 stream->netstream = NIL;
1007 if(stream->status) http_status_line_free(&stream->status);
1008 if(stream->header) http_header_free(&stream->header);
1009 if (stream->url) fs_give ((void **) &stream->url);
1010 if (stream->urlhost) fs_give ((void **) &stream->urlhost);
1011 if (stream->urltail) fs_give ((void **) &stream->urltail);
1012 if (stream->response) fs_give ((void **) &stream->response);
1013 if (stream->reply) fs_give ((void **) &stream->reply);
1014 fs_give((void **) &stream);
1018 long
1019 http_send (HTTPSTREAM *stream, HTTP_REQUEST_S *req)
1021 long ret;
1022 unsigned char *s = NULL;
1024 if (!stream->netstream)
1025 ret = http_fake (stream,"http connection lost");
1026 else {
1027 if(req->body){
1028 char length[20];
1030 sprintf(length, "%lu", strlen(req->body));
1031 http_add_header(&req, "Content-Length", length);
1034 buffer_add(&s, req->request); buffer_add(&s, "\015\012");
1035 buffer_add(&s, req->header); buffer_add(&s, "\015\012");
1036 buffer_add(&s, req->body); buffer_add(&s, "\015\012");
1038 if(stream->debug) mm_log(s, HTTPDEBUG);
1040 ret = net_soutr (stream->netstream,s)
1041 ? http_reply (stream)
1042 : http_fake (stream,"http connection broken in command");
1043 fs_give ((void **) &s);
1045 return ret;
1048 HTTP_STATUS_S *
1049 http_status_line_get(unsigned char *status_line)
1051 HTTP_STATUS_S *rv = NULL;
1052 char *version, *s;
1053 int code;
1055 if(!status_line) return NIL;
1057 if((s = strchr(status_line, ' ')) != NIL){
1058 *s = '\0';
1059 version = cpystr(status_line);
1060 *s++ = ' ';
1061 code = strtoul(s, &s, 10);
1062 if(s && *s == ' ' && code >= 100 && code < 600){
1063 rv = fs_get(sizeof(HTTP_STATUS_S));
1064 rv->version = version;
1065 rv->code = code;
1066 rv->text = cpystr(++s);
1068 else
1069 fs_give((void **) &version);
1071 return rv;
1074 void
1075 http_status_line_free(HTTP_STATUS_S **status)
1077 if(status == NULL) return;
1079 if((*status)->version) fs_give((void **) &(*status)->version);
1080 if((*status)->text) fs_give((void **) &(*status)->text);
1081 fs_give((void **) status);
1085 long
1086 http_reply (HTTPSTREAM *stream)
1088 int in_header = 1;
1089 unsigned long size;
1091 if (stream->response) fs_give ((void **) &stream->response);
1092 stream->response = (unsigned char *) net_getline(stream->netstream);
1094 if(stream->debug) mm_log(stream->response ? stream->response : (unsigned char *) "<NIL RESPONSE>", HTTPDEBUG);
1096 if(stream->response){
1097 buffer_add(&stream->reply, stream->response);
1098 buffer_add(&stream->reply, "\015\012");
1101 if(stream->status) http_status_line_free(&stream->status);
1102 stream->status = http_status_line_get(stream->response);
1104 if(!stream->status){
1105 http_fake(stream, "Invalid status line received. Closing connection");
1106 return NIL;
1109 while (in_header > 0){
1110 if (stream->response) fs_give ((void **) &stream->response);
1111 stream->response = (unsigned char *) net_getline (stream->netstream);
1112 if(stream->response){
1113 buffer_add(&stream->reply, stream->response);
1114 http_add_header_data(stream, stream->response);
1115 if(stream->debug) mm_log(stream->response, HTTPDEBUG);
1117 buffer_add(&stream->reply, "\015\012");
1118 // save_header(stream->headers, stream->response);
1119 if(!stream->response || *stream->response == '\0')
1120 in_header--;
1123 http_parse_headers(stream);
1124 if(stream->header->content_length){
1125 size = atol(stream->header->content_length->p->vp->value);
1126 if (stream->response) fs_give ((void **) &stream->response);
1127 if(size > 0L){
1128 stream->response = (unsigned char *) net_getsize (stream->netstream, size);
1129 if(stream->response){
1130 buffer_add(&stream->reply, stream->response);
1131 if(stream->debug) mm_log(stream->response, HTTPDEBUG);
1135 else if (stream->header->transfer_encoding){
1136 HTTP_PARAM_LIST_S *p = stream->header->transfer_encoding->p;
1137 for(; p ; p = p->next){
1138 if(!compare_cstring(p->vp->value, "chunked"))
1139 break;
1141 if(p && p->vp->value){ /* chunked transfer */
1142 unsigned char *s = NIL;
1143 do {
1144 if (s) fs_give ((void **) &s);
1145 size = 0L;
1146 if((s = (unsigned char *) net_getline (stream->netstream)) != NIL){
1147 if(stream->debug) mm_log(s, HTTPDEBUG);
1148 size = strtol(s, NIL, 16);
1149 fs_give ((void **) &stream->response);
1150 if(size > 0){
1151 stream->response = (unsigned char *) net_getsize (stream->netstream, size);
1152 buffer_add(&stream->reply, stream->response);
1153 if(stream->debug) mm_log(stream->response, HTTPDEBUG);
1156 } while (stream && stream->netstream && s && (size > 0 || !*s ));
1160 if(!stream->netstream)
1161 http_fake(stream, "Connection to HTTP server closed");
1162 return stream->netstream ? T : NIL;
1165 long
1166 http_fake (HTTPSTREAM *stream, unsigned char *text)
1168 if (stream->netstream) net_close (stream->netstream);
1169 stream->netstream = NIL;
1170 if (stream->response) fs_give ((void **) &stream->response);
1171 /* add *text to the log, to pass this to the client */
1172 return NIL;