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
14 #include "c-client.h" /* this includes http.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 */
29 typedef struct http_val_param_s
{
34 typedef struct http_param_list_s
{
36 struct http_param_list_s
*next
;
39 typedef struct http_header_value_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 */
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
*);
118 http_parameters (long function
,void *value
)
121 switch((int) function
){
122 case SET_HTTPDEBUG
: http_debug
= (long) value
;
123 case GET_HTTPDEBUG
: ret
= (void *) http_debug
;
131 http_response_from_reply(HTTPSTREAM
*stream
)
133 unsigned char *rv
= NULL
, *s
;
135 if(stream
== NULL
|| stream
->reply
== NULL
|| stream
->header
== NULL
)
138 s
= strstr(stream
->reply
, "\r\n\r\n");
139 if(s
!= NULL
) rv
= s
+ 4;
145 http_parse_headers(HTTPSTREAM
*stream
)
147 HTTP_HEADER_DATA_S
*hd
;
150 if(!stream
|| !stream
->header
) return;
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
);
352 http_add_data_to_header(HTTP_HEADER_S
**headerp
, unsigned char *data
)
354 HTTP_HEADER_S
*h
= *headerp
;
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
);
367 http_add_header_data(HTTPSTREAM
*stream
, unsigned char *hdata
)
369 unsigned char *hname
, *h
;
372 if(!stream
|| !hdata
|| !*hdata
) return;
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
, ':'))){
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
))
388 hname
[h
-hdata
] = ':';
389 hname
[h
-hdata
+1] = '\0';
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
);
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
);
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
);
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
);
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
);
456 case 'h': if(!compare_cstring(hname
+1, "ost:")) /* RFC 7230, Section 5.4 */
457 http_add_data_to_header(&stream
->header
->host
, h
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
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
;
552 for(t
= s
; *t
!= '\0' && *t
!= ','; t
++);
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
);
567 rv
->next
= http_parse_token_list(t
+1, num
);
574 * parse a list of tokens with optional parameters
575 * into a HEADER_DATA structure. Do not parse into
576 * it anything invalid.
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
;
586 * isolate first list element from list and remove
587 * leading and trailing white space.
592 for(t
= s
; *t
!= '\0' && *t
!= ','; t
++);
594 http_remove_trailing_ows(s
);
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
++);
604 http_remove_trailing_ows(s
);
605 if(!valid_token_name(s
))
606 return c
== ',' ? http_parse_token_parameter(t
+1, flag
) : NIL
;
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
);
620 rv
->vp
->plist
= http_parse_parameter(u
+1, flag
);
624 rv
->next
= http_parse_token_parameter(t
+1, flag
);
630 valid_dquote_text(unsigned char *s
)
634 if(!s
|| *s
!= '\"') return 0;
636 t
= strchr(s
+1, '\"');
637 return (t
&& !t
[1]) ? 1 : 0;
642 http_skipows(unsigned char **sp
)
644 unsigned char *s
= *sp
;
645 for(; *s
== ' ' || *s
== '\t'; s
++);
650 http_remove_trailing_ows(unsigned char *s
)
653 for(t
= s
; strlen(t
) > 0 ;)
654 if(t
[strlen(t
)-1] == ' ' || t
[strlen(t
)-1] == '\t')
655 t
[strlen(t
)-1] = '\0';
661 http_parse_parameter(unsigned char *s
, int flag
)
664 unsigned char *t
, *u
, c
;
667 * separate the parameters into a list separated by ";"
669 if(!s
|| !*s
) return NIL
;
672 for(t
= s
; *t
!= '\0' && *t
!= ';'; t
++);
675 /* Now we look for separation of attribute and value */
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
;
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
;
700 p
= c
== ';' ? http_parse_parameter(t
+1, flag
) : NIL
;
706 http_get_param_url(unsigned char *url
, HTTP_PARAM_S
*param
)
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
);
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
));
737 http_request_free(HTTP_REQUEST_S
**hr
)
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
);
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
);
758 http_add_header(HTTP_REQUEST_S
**reqp
, unsigned char *name
, unsigned char *value
)
764 if(!*reqp
) *reqp
= http_request_get();
766 len
= strlen(name
) + 2 + strlen(value
) + 2 + 1;
767 hlen
= (*reqp
)->header
? strlen((*reqp
)->header
) : 0;
769 fs_resize((void **) &(*reqp
)->header
, len
*sizeof(char));
770 sprintf((*reqp
)->header
+ hlen
, "%s: %s\015\012", name
, value
);
774 buffer_add(unsigned char **bufp
, unsigned char *text
)
778 if(!bufp
|| !text
|| !*text
) return;
780 len
= *bufp
? strlen(*bufp
) : 0;
781 fs_resize((void **) bufp
, (len
+ strlen(text
) + 1)*sizeof(char));
787 http_add_body(HTTP_REQUEST_S
**reqp
, unsigned char *text
)
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".
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
));
812 http_param_free(HTTP_PARAM_S
**param
)
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 */
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
;
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
);
841 sprintf(s
+ strlen(s
), "%%%X", *t
);
842 fs_resize((void **) &s
, (strlen(s
)+1)*sizeof(char));
846 /* this encodes for a POST request */
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
;
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
);
861 sprintf(s
+ strlen(s
), "%%%X", *t
);
862 fs_resize((void **) &s
, (strlen(s
)+1)*sizeof(char));
867 http_valid_net_parse (unsigned char *url
, NETMBX
*mb
)
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'))
880 if(url
[i
= 4] == 's' || url
[i
] == 'S')
881 mb
->sslflag
= mb
->notlsflag
= T
;
884 if(url
[++i
] != ':' || url
[++i
] != '/' || url
[++i
] != '/')
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
){
894 mb
->port
= strtoul(p
, &p
, 10);
895 if(mb
->port
== 0L || *p
!= '\0')
898 strcpy(mb
->host
, mb
->orighost
);
903 http_open (unsigned char *url
)
909 memset((void *) &mb
, 0, sizeof(NETMBX
));
910 if(http_valid_net_parse (url
,&mb
) == 0)
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
){
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
;
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
);
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
);
969 http_get(HTTPSTREAM
*stream
, HTTP_PARAM_S
**h
)
971 HTTP_REQUEST_S
*http_request
;
972 unsigned char *response
= NIL
;
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
);
994 http_header_free(HTTP_HEADER_DATA_S
**hdata
)
996 if(hdata
== NULL
|| *hdata
== NULL
) return;
998 fs_give((void **) hdata
);
1002 http_close (HTTPSTREAM
*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
);
1019 http_send (HTTPSTREAM
*stream
, HTTP_REQUEST_S
*req
)
1022 unsigned char *s
= NULL
;
1024 if (!stream
->netstream
)
1025 ret
= http_fake (stream
,"http connection lost");
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
);
1049 http_status_line_get(unsigned char *status_line
)
1051 HTTP_STATUS_S
*rv
= NULL
;
1055 if(!status_line
) return NIL
;
1057 if((s
= strchr(status_line
, ' ')) != NIL
){
1059 version
= cpystr(status_line
);
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
;
1066 rv
->text
= cpystr(++s
);
1069 fs_give((void **) &version
);
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
);
1086 http_reply (HTTPSTREAM
*stream
)
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");
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')
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
);
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"))
1141 if(p
&& p
->vp
->value
){ /* chunked transfer */
1142 unsigned char *s
= NIL
;
1144 if (s
) fs_give ((void **) &s
);
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
);
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
;
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 */