1 /* Httpp.c
2 **
3 ** http parsing engine
4 **
5 ** This program is distributed under the GNU General Public License, version 2.
6 ** A copy of this license is included with this source.
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdio.h>
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <ctype.h>
18 #ifdef HAVE_STRINGS_H
19 #include <strings.h>
20 #endif
21
22 #include <avl/avl.h>
23 #include "httpp.h"
24
25 #if defined(_WIN32) && !defined(HAVE_STRCASECMP)
26 #define strcasecmp stricmp
27 #endif
28
29 #define MAX_HEADERS 32
30
31 /* internal functions */
32
33 /* misc */
34 static char *_lowercase(char *str);
35
36 /* for avl trees */
37 static int _compare_vars(void *compare_arg, void *a, void *b);
38 static int _free_vars(void *key);
39
httpp_create_parser(void)40 http_parser_t *httpp_create_parser(void)
41 {
42 return (http_parser_t *)malloc(sizeof(http_parser_t));
43 }
44
httpp_initialize(http_parser_t * parser,http_varlist_t * defaults)45 void httpp_initialize(http_parser_t *parser, http_varlist_t *defaults)
46 {
47 http_varlist_t *list;
48
49 parser->req_type = httpp_req_none;
50 parser->uri = NULL;
51 parser->vars = avl_tree_new(_compare_vars, NULL);
52 parser->queryvars = avl_tree_new(_compare_vars, NULL);
53
54 /* now insert the default variables */
55 list = defaults;
56 while (list != NULL) {
57 httpp_setvar(parser, list->var.name, list->var.value);
58 list = list->next;
59 }
60 }
61
split_headers(char * data,unsigned long len,char ** line)62 static int split_headers(char *data, unsigned long len, char **line)
63 {
64 /* first we count how many lines there are
65 ** and set up the line[] array
66 */
67 int lines = 0;
68 unsigned long i;
69 line[lines] = data;
70 for (i = 0; i < len && lines < MAX_HEADERS; i++) {
71 if (data[i] == '\r')
72 data[i] = '\0';
73 if (data[i] == '\n') {
74 lines++;
75 data[i] = '\0';
76 if (lines >= MAX_HEADERS)
77 return MAX_HEADERS;
78 if (i + 1 < len) {
79 if (data[i + 1] == '\n' || data[i + 1] == '\r')
80 break;
81 line[lines] = &data[i + 1];
82 }
83 }
84 }
85
86 i++;
87 while (i < len && data[i] == '\n') i++;
88
89 return lines;
90 }
91
parse_headers(http_parser_t * parser,char ** line,int lines)92 static void parse_headers(http_parser_t *parser, char **line, int lines)
93 {
94 int i, l;
95 int whitespace, slen;
96 char *name = NULL;
97 char *value = NULL;
98
99 /* parse the name: value lines. */
100 for (l = 1; l < lines; l++) {
101 whitespace = 0;
102 name = line[l];
103 value = NULL;
104 slen = strlen(line[l]);
105 for (i = 0; i < slen; i++) {
106 if (line[l][i] == ':') {
107 whitespace = 1;
108 line[l][i] = '\0';
109 } else {
110 if (whitespace) {
111 whitespace = 0;
112 while (i < slen && line[l][i] == ' ')
113 i++;
114
115 if (i < slen)
116 value = &line[l][i];
117
118 break;
119 }
120 }
121 }
122
123 if (name != NULL && value != NULL) {
124 httpp_setvar(parser, _lowercase(name), value);
125 name = NULL;
126 value = NULL;
127 }
128 }
129 }
130
httpp_parse_response(http_parser_t * parser,const char * http_data,unsigned long len,const char * uri)131 int httpp_parse_response(http_parser_t *parser, const char *http_data, unsigned long len, const char *uri)
132 {
133 char *data;
134 char *line[MAX_HEADERS];
135 int lines, slen,i, whitespace=0, where=0,code;
136 char *version=NULL, *resp_code=NULL, *message=NULL;
137
138 if(http_data == NULL)
139 return 0;
140
141 /* make a local copy of the data, including 0 terminator */
142 data = (char *)malloc(len+1);
143 if (data == NULL) return 0;
144 memcpy(data, http_data, len);
145 data[len] = 0;
146
147 lines = split_headers(data, len, line);
148
149 /* In this case, the first line contains:
150 * VERSION RESPONSE_CODE MESSAGE, such as HTTP/1.0 200 OK
151 */
152 slen = strlen(line[0]);
153 version = line[0];
154 for(i=0; i < slen; i++) {
155 if(line[0][i] == ' ') {
156 line[0][i] = 0;
157 whitespace = 1;
158 } else if(whitespace) {
159 whitespace = 0;
160 where++;
161 if(where == 1)
162 resp_code = &line[0][i];
163 else {
164 message = &line[0][i];
165 break;
166 }
167 }
168 }
169
170 if(version == NULL || resp_code == NULL || message == NULL) {
171 free(data);
172 return 0;
173 }
174
175 httpp_setvar(parser, HTTPP_VAR_ERROR_CODE, resp_code);
176 code = atoi(resp_code);
177 if(code < 200 || code >= 300) {
178 httpp_setvar(parser, HTTPP_VAR_ERROR_MESSAGE, message);
179 }
180
181 httpp_setvar(parser, HTTPP_VAR_URI, uri);
182 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "NONE");
183
184 parse_headers(parser, line, lines);
185
186 free(data);
187
188 return 1;
189 }
190
hex(char c)191 static int hex(char c)
192 {
193 if(c >= '0' && c <= '9')
194 return c - '0';
195 else if(c >= 'A' && c <= 'F')
196 return c - 'A' + 10;
197 else if(c >= 'a' && c <= 'f')
198 return c - 'a' + 10;
199 else
200 return -1;
201 }
202
url_escape(const char * src)203 static char *url_escape(const char *src)
204 {
205 int len = strlen(src);
206 unsigned char *decoded;
207 int i;
208 char *dst;
209 int done = 0;
210
211 decoded = calloc(1, len + 1);
212
213 dst = (char *)decoded;
214
215 for(i=0; i < len; i++) {
216 switch(src[i]) {
217 case '%':
218 if(i+2 >= len) {
219 free(decoded);
220 return NULL;
221 }
222 if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
223 free(decoded);
224 return NULL;
225 }
226
227 *dst++ = hex(src[i+1]) * 16 + hex(src[i+2]);
228 i+= 2;
229 break;
230 case '+':
231 *dst++ = ' ';
232 break;
233 case '#':
234 done = 1;
235 break;
236 case 0:
237 free(decoded);
238 return NULL;
239 break;
240 default:
241 *dst++ = src[i];
242 break;
243 }
244 if(done)
245 break;
246 }
247
248 *dst = 0; /* null terminator */
249
250 return (char *)decoded;
251 }
252
253 /** TODO: This is almost certainly buggy in some cases */
parse_query(http_parser_t * parser,char * query)254 static void parse_query(http_parser_t *parser, char *query)
255 {
256 int len;
257 int i=0;
258 char *key = query;
259 char *val=NULL;
260
261 if(!query || !*query)
262 return;
263
264 len = strlen(query);
265
266 while(i<len) {
267 switch(query[i]) {
268 case '&':
269 query[i] = 0;
270 if(val && key)
271 httpp_set_query_param(parser, key, val);
272 key = query+i+1;
273 break;
274 case '=':
275 query[i] = 0;
276 val = query+i+1;
277 break;
278 }
279 i++;
280 }
281
282 if(val && key) {
283 httpp_set_query_param(parser, key, val);
284 }
285 }
286
httpp_parse(http_parser_t * parser,const char * http_data,unsigned long len)287 int httpp_parse(http_parser_t *parser, const char *http_data, unsigned long len)
288 {
289 char *data, *tmp;
290 char *line[MAX_HEADERS]; /* limited to 32 lines, should be more than enough */
291 int i;
292 int lines;
293 char *req_type = NULL;
294 char *uri = NULL;
295 char *version = NULL;
296 int whitespace, where, slen;
297
298 if (http_data == NULL)
299 return 0;
300
301 /* make a local copy of the data, including 0 terminator */
302 data = (char *)malloc(len+1);
303 if (data == NULL) return 0;
304 memcpy(data, http_data, len);
305 data[len] = 0;
306
307 lines = split_headers(data, len, line);
308
309 /* parse the first line special
310 ** the format is:
311 ** REQ_TYPE URI VERSION
312 ** eg:
313 ** GET /index.html HTTP/1.0
314 */
315 where = 0;
316 whitespace = 0;
317 slen = strlen(line[0]);
318 req_type = line[0];
319 for (i = 0; i < slen; i++) {
320 if (line[0][i] == ' ') {
321 whitespace = 1;
322 line[0][i] = '\0';
323 } else {
324 /* we're just past the whitespace boundry */
325 if (whitespace) {
326 whitespace = 0;
327 where++;
328 switch (where) {
329 case 1:
330 uri = &line[0][i];
331 break;
332 case 2:
333 version = &line[0][i];
334 break;
335 }
336 }
337 }
338 }
339
340 if (strcasecmp("GET", req_type) == 0) {
341 parser->req_type = httpp_req_get;
342 } else if (strcasecmp("POST", req_type) == 0) {
343 parser->req_type = httpp_req_post;
344 } else if (strcasecmp("PUT", req_type) == 0) {
345 parser->req_type = httpp_req_put;
346 } else if (strcasecmp("HEAD", req_type) == 0) {
347 parser->req_type = httpp_req_head;
348 } else if (strcasecmp("SOURCE", req_type) == 0) {
349 parser->req_type = httpp_req_source;
350 } else if (strcasecmp("PLAY", req_type) == 0) {
351 parser->req_type = httpp_req_play;
352 } else if (strcasecmp("STATS", req_type) == 0) {
353 parser->req_type = httpp_req_stats;
354 } else {
355 parser->req_type = httpp_req_unknown;
356 }
357
358 if (uri != NULL && strlen(uri) > 0) {
359 char *query;
360 if((query = strchr(uri, '?')) != NULL) {
361 httpp_setvar(parser, HTTPP_VAR_RAWURI, uri);
362 httpp_setvar(parser, HTTPP_VAR_QUERYARGS, query);
363 *query = 0;
364 query++;
365 parse_query(parser, query);
366 }
367
368 parser->uri = strdup(uri);
369 } else {
370 free(data);
371 return 0;
372 }
373
374 if ((version != NULL) && ((tmp = strchr(version, '/')) != NULL)) {
375 tmp[0] = '\0';
376 if ((strlen(version) > 0) && (strlen(&tmp[1]) > 0)) {
377 httpp_setvar(parser, HTTPP_VAR_PROTOCOL, version);
378 httpp_setvar(parser, HTTPP_VAR_VERSION, &tmp[1]);
379 } else {
380 free(data);
381 return 0;
382 }
383 } else {
384 free(data);
385 return 0;
386 }
387
388 if (parser->req_type != httpp_req_none && parser->req_type != httpp_req_unknown) {
389 switch (parser->req_type) {
390 case httpp_req_get:
391 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "GET");
392 break;
393 case httpp_req_post:
394 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "POST");
395 break;
396 case httpp_req_put:
397 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "PUT");
398 break;
399 case httpp_req_head:
400 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "HEAD");
401 break;
402 case httpp_req_source:
403 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "SOURCE");
404 break;
405 case httpp_req_play:
406 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "PLAY");
407 break;
408 case httpp_req_stats:
409 httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "STATS");
410 break;
411 default:
412 break;
413 }
414 } else {
415 free(data);
416 return 0;
417 }
418
419 if (parser->uri != NULL) {
420 httpp_setvar(parser, HTTPP_VAR_URI, parser->uri);
421 } else {
422 free(data);
423 return 0;
424 }
425
426 parse_headers(parser, line, lines);
427
428 free(data);
429
430 return 1;
431 }
432
httpp_deletevar(http_parser_t * parser,const char * name)433 void httpp_deletevar(http_parser_t *parser, const char *name)
434 {
435 http_var_t var;
436
437 if (parser == NULL || name == NULL)
438 return;
439 var.name = (char*)name;
440 var.value = NULL;
441 avl_delete(parser->vars, (void *)&var, _free_vars);
442 }
443
httpp_setvar(http_parser_t * parser,const char * name,const char * value)444 void httpp_setvar(http_parser_t *parser, const char *name, const char *value)
445 {
446 http_var_t *var;
447
448 if (name == NULL || value == NULL)
449 return;
450
451 var = (http_var_t *)malloc(sizeof(http_var_t));
452 if (var == NULL) return;
453
454 var->name = strdup(name);
455 var->value = strdup(value);
456
457 if (httpp_getvar(parser, name) == NULL) {
458 avl_insert(parser->vars, (void *)var);
459 } else {
460 avl_delete(parser->vars, (void *)var, _free_vars);
461 avl_insert(parser->vars, (void *)var);
462 }
463 }
464
httpp_getvar(http_parser_t * parser,const char * name)465 const char *httpp_getvar(http_parser_t *parser, const char *name)
466 {
467 http_var_t var;
468 http_var_t *found;
469 void *fp;
470
471 if (parser == NULL || name == NULL)
472 return NULL;
473
474 fp = &found;
475 var.name = (char*)name;
476 var.value = NULL;
477
478 if (avl_get_by_key(parser->vars, &var, fp) == 0)
479 return found->value;
480 else
481 return NULL;
482 }
483
httpp_set_query_param(http_parser_t * parser,const char * name,const char * value)484 void httpp_set_query_param(http_parser_t *parser, const char *name, const char *value)
485 {
486 http_var_t *var;
487
488 if (name == NULL || value == NULL)
489 return;
490
491 var = (http_var_t *)malloc(sizeof(http_var_t));
492 if (var == NULL) return;
493
494 var->name = strdup(name);
495 var->value = url_escape(value);
496
497 if (httpp_get_query_param(parser, name) == NULL) {
498 avl_insert(parser->queryvars, (void *)var);
499 } else {
500 avl_delete(parser->queryvars, (void *)var, _free_vars);
501 avl_insert(parser->queryvars, (void *)var);
502 }
503 }
504
httpp_get_query_param(http_parser_t * parser,const char * name)505 const char *httpp_get_query_param(http_parser_t *parser, const char *name)
506 {
507 http_var_t var;
508 http_var_t *found;
509 void *fp;
510
511 fp = &found;
512 var.name = (char *)name;
513 var.value = NULL;
514
515 if (avl_get_by_key(parser->queryvars, (void *)&var, fp) == 0)
516 return found->value;
517 else
518 return NULL;
519 }
520
httpp_clear(http_parser_t * parser)521 void httpp_clear(http_parser_t *parser)
522 {
523 parser->req_type = httpp_req_none;
524 if (parser->uri)
525 free(parser->uri);
526 parser->uri = NULL;
527 avl_tree_free(parser->vars, _free_vars);
528 avl_tree_free(parser->queryvars, _free_vars);
529 parser->vars = NULL;
530 }
531
httpp_destroy(http_parser_t * parser)532 void httpp_destroy(http_parser_t *parser)
533 {
534 httpp_clear(parser);
535 free(parser);
536 }
537
_lowercase(char * str)538 static char *_lowercase(char *str)
539 {
540 char *p = str;
541 for (; *p != '\0'; p++)
542 *p = tolower(*p);
543
544 return str;
545 }
546
_compare_vars(void * compare_arg,void * a,void * b)547 static int _compare_vars(void *compare_arg, void *a, void *b)
548 {
549 http_var_t *vara, *varb;
550
551 vara = (http_var_t *)a;
552 varb = (http_var_t *)b;
553
554 return strcmp(vara->name, varb->name);
555 }
556
_free_vars(void * key)557 static int _free_vars(void *key)
558 {
559 http_var_t *var;
560
561 var = (http_var_t *)key;
562
563 if (var->name)
564 free(var->name);
565 if (var->value)
566 free(var->value);
567 free(var);
568
569 return 1;
570 }
571
572