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