Merge commit 'origin/ser_core_cvs'
[sip-router] / parser / parse_param.c
1 /* 
2  * $Id$
3  *
4  * Generic Parameter Parser
5  *
6  * Copyright (C) 2001-2003 FhG Fokus
7  *
8  * This file is part of ser, a free SIP server.
9  *
10  * ser is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * For a license to use the ser software under conditions
16  * other than those described here, or to purchase support for this
17  * software, please contact iptel.org by e-mail at the following addresses:
18  *    info@iptel.org
19  *
20  * ser is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License 
26  * along with this program; if not, write to the Free Software 
27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28  *
29  * History:
30  * -------
31  * 2003-03-24 Created by janakj
32  * 2003-04-07 shm duplication added (janakj)
33  * 2003-04-07 URI class added (janakj)
34  */
35
36 #include <string.h>
37 #include "../str.h"
38 #include "../ut.h"
39 #include "../dprint.h"
40 #include "../trim.h"
41 #include "../mem/mem.h"
42 #include "../mem/shm_mem.h"
43 #include "parse_param.h"
44
45
46 static inline void parse_event_dialog_class(param_hooks_t* h, param_t* p)
47 {
48
49         if (!p->name.s) {
50                 LOG(L_ERR, "ERROR: parse_event_dialog_class: empty value\n");
51                 return;
52         }
53         if (!h) {
54                 LOG(L_CRIT, "BUG: parse_event_dialog_class: NULL param hook pointer\n");
55                 return;
56         }
57         switch(p->name.s[0]) {
58         case 'c':
59         case 'C':
60                 if ((p->name.len == 7) &&
61                     (!strncasecmp(p->name.s + 1, "all-id", 6))) {
62                         p->type = P_CALL_ID;
63                         h->event_dialog.call_id = p;
64                 }
65                 break;
66
67         case 'f':
68         case 'F':
69                 if ((p->name.len == 8) &&
70                     (!strncasecmp(p->name.s + 1, "rom-tag", 7))) {
71                         p->type = P_FROM_TAG;
72                         h->event_dialog.from_tag = p;
73                 }
74                 break;
75
76         case 't':
77         case 'T':
78                 if ((p->name.len == 6) &&
79                     (!strncasecmp(p->name.s + 1, "o-tag", 5))) {
80                         p->type = P_TO_TAG;
81                         h->event_dialog.to_tag = p;
82                 }
83                 break;
84
85         case 'i':
86         case 'I':
87                 if ((p->name.len == 27) &&
88                     (!strncasecmp(p->name.s + 1, "nclude-session-description", 26))) {
89                         p->type = P_ISD;
90                         h->event_dialog.include_session_description = p;
91                 }
92                 break;
93
94         case 's':
95         case 'S':
96                 if ((p->name.len == 3) &&
97                     (!strncasecmp(p->name.s + 1, "la", 2))) {
98                         p->type = P_SLA;
99                         h->event_dialog.sla = p;
100                 }
101                 break;
102         }
103 }
104
105
106 /*
107  * Try to find out parameter name, recognized parameters
108  * are q, expires and methods
109  */
110 static inline void parse_contact_class(param_hooks_t* _h, param_t* _p)
111 {
112
113         if (!_p->name.s) {
114                 LOG(L_ERR, "ERROR: parse_contact_class: empty value\n");
115                 return;
116         }
117         if (!_h) {
118                 LOG(L_CRIT, "BUG: parse_uri_class: NULL param hook pointer\n");
119                 return;
120         }
121         switch(_p->name.s[0]) {
122         case 'q':
123         case 'Q':
124                 if (_p->name.len == 1) {
125                         _p->type = P_Q;
126                         _h->contact.q = _p;
127                 }
128                 break;
129                 
130         case 'e':
131         case 'E':
132                 if ((_p->name.len == 7) &&
133                     (!strncasecmp(_p->name.s + 1, "xpires", 6))) {
134                         _p->type = P_EXPIRES;
135                         _h->contact.expires = _p;
136                 }
137                 break;
138                 
139         case 'm':
140         case 'M':
141                 if ((_p->name.len == 7) &&
142                     (!strncasecmp(_p->name.s + 1, "ethods", 6))) {
143                         _p->type = P_METHODS;
144                         _h->contact.methods = _p;
145                 }
146                 break;
147                 
148         case 'r':
149         case 'R':
150                 if ((_p->name.len == 8) &&
151                     (!strncasecmp(_p->name.s + 1, "eceived", 7))) {
152                         _p->type = P_RECEIVED;
153                         _h->contact.received = _p;
154                 }
155                 break;
156         case '+':
157                 if ((_p->name.len == 13) &&
158                         (!strncasecmp(_p->name.s + 1, "sip.instance", 12))) {
159                         _p->type = P_INSTANCE;
160                         _h->contact.instance = _p;
161                 }
162                 break;
163         }
164 }
165
166
167 /*
168  * Try to find out parameter name, recognized parameters
169  * are transport, lr, r2, maddr
170  */
171 static inline void parse_uri_class(param_hooks_t* _h, param_t* _p)
172 {
173
174         if (!_p->name.s) {
175                 LOG(L_ERR, "ERROR: parse_uri_class: empty value\n");
176                 return;
177         }
178         if (!_h) {
179                 LOG(L_CRIT, "BUG: parse_uri_class: NULL param hook pointer\n");
180                 return;
181         }
182         switch(_p->name.s[0]) {
183         case 't':
184         case 'T':
185                 if ((_p->name.len == 9) &&
186                     (!strncasecmp(_p->name.s + 1, "ransport", 8))) {
187                         _p->type = P_TRANSPORT;
188                         _h->uri.transport = _p;
189                 } else if (_p->name.len == 2) {
190                         if (((_p->name.s[1] == 't') || (_p->name.s[1] == 'T')) &&
191                             ((_p->name.s[2] == 'l') || (_p->name.s[2] == 'L'))) {
192                                 _p->type = P_TTL;
193                                 _h->uri.ttl = _p;
194                         }
195                 }
196                 break;
197
198         case 'l':
199         case 'L':
200                 if ((_p->name.len == 2) && ((_p->name.s[1] == 'r') || (_p->name.s[1] == 'R'))) {
201                         _p->type = P_LR;
202                         _h->uri.lr = _p;
203                 }
204                 break;
205
206         case 'r':
207         case 'R':
208                 if ((_p->name.len == 2) && (_p->name.s[1] == '2')) {
209                         _p->type = P_R2;
210                         _h->uri.r2 = _p;
211                 }
212                 break;
213
214         case 'm':
215         case 'M':
216                 if ((_p->name.len == 5) &&
217                     (!strncasecmp(_p->name.s + 1, "addr", 4))) {
218                         _p->type = P_MADDR;
219                         _h->uri.maddr = _p;
220                 }
221                 break;
222                 
223         case 'd':
224         case 'D':
225                 if ((_p->name.len == 5) &&
226                     (!strncasecmp(_p->name.s + 1, "stip", 4))) {
227                         _p->type = P_DSTIP;
228                         _h->uri.dstip = _p;
229                 } else if ((_p->name.len == 7) &&
230                            (!strncasecmp(_p->name.s + 1, "stport", 6))) {
231                         _p->type = P_DSTPORT;
232                         _h->uri.dstport = _p;
233                 }
234                 break;
235         case 'f':
236         case 'F':
237                 if ((_p->name.len == 4) &&
238                     (!strncasecmp(_p->name.s + 1, "tag", 3))) {
239                         _p->type = P_FTAG;
240                         _h->uri.ftag = _p;
241                 }
242                 break;
243         }
244
245 }
246
247
248 /*
249  * Parse quoted string in a parameter body
250  * return the string without quotes in _r
251  * parameter and update _s to point behind the
252  * closing quote
253  */
254 static inline int parse_quoted_param(str* _s, str* _r)
255 {
256         char* end_quote;
257
258              /* The string must have at least
259               * surrounding quotes
260               */
261         if (_s->len < 2) {
262                 return -1;
263         }
264
265              /* Skip opening quote */
266         _s->s++;
267         _s->len--;
268
269
270              /* Find closing quote */
271         end_quote = q_memchr(_s->s, '\"', _s->len);
272
273              /* Not found, return error */
274         if (!end_quote) {
275                 return -2;
276         }
277
278              /* Let _r point to the string without
279               * surrounding quotes
280               */
281         _r->s = _s->s;
282         _r->len = end_quote - _s->s;
283
284              /* Update _s parameter to point
285               * behind the closing quote
286               */
287         _s->len -= (end_quote - _s->s + 1);
288         _s->s = end_quote + 1;
289
290              /* Everything went OK */
291         return 0;
292 }
293
294
295 /*
296  * Parse unquoted token in a parameter body
297  * let _r point to the token and update _s
298  * to point right behind the token
299  */
300 static inline int parse_token_param(str* _s, str* _r)
301 {
302         int i;
303
304              /* There is nothing to parse,
305               * return error
306               */
307         if (_s->len == 0) {
308                 return -1;
309         }
310
311              /* Save the begining of the
312               * token in _r->s
313               */
314         _r->s = _s->s;
315
316              /* Iterate through the
317               * token body
318               */
319         for(i = 0; i < _s->len; i++) {
320
321                      /* All these characters
322                       * mark end of the token
323                       */
324                 switch(_s->s[i]) {
325                 case ' ':
326                 case '\t':
327                 case '\r':
328                 case '\n':
329                 case ',':
330                 case ';':
331                              /* So if you find
332                               * any of them
333                               * stop iterating
334                               */
335                         goto out;
336                 }
337         }
338  out:
339         if (i == 0) {
340                 return -1;
341         }
342
343              /* Save length of the token */
344         _r->len = i;
345
346              /* Update _s parameter so it points
347               * right behind the end of the token
348               */
349         _s->s = _s->s + i;
350         _s->len -= i;
351
352              /* Everything went OK */
353         return 0;
354 }
355
356
357 /*
358  * Parse a parameter name
359  */
360 static inline void parse_param_name(str* _s, pclass_t _c, param_hooks_t* _h, param_t* _p)
361 {
362
363         if (!_s->s) {
364                 DBG("DEBUG: parse_param_name: empty parameter\n");
365                 return;
366         }
367
368         _p->name.s = _s->s;
369
370         while(_s->len) {
371                 switch(_s->s[0]) {
372                 case ' ':
373                 case '\t':
374                 case '\r':
375                 case '\n':
376                 case ';':
377                 case ',':
378                 case '=':
379                         goto out;
380                 }
381                 _s->s++;
382                 _s->len--;
383         }
384
385  out:
386         _p->name.len = _s->s - _p->name.s;
387
388         switch(_c) {
389         case CLASS_CONTACT: parse_contact_class(_h, _p); break;
390         case CLASS_URI:     parse_uri_class(_h, _p);     break;
391         case CLASS_EVENT_DIALOG: parse_event_dialog_class(_h, _p); break;
392         default: break;
393         }
394 }
395
396
397
398
399
400 /*
401  * Parse body of a parameter. It can be quoted string or
402  * a single token.
403  */
404 static inline int parse_param_body(str* _s, param_t* _c)
405 {
406         if (_s->s[0] == '\"') {
407                 if (parse_quoted_param(_s, &(_c->body)) < 0) {
408                         LOG(L_ERR, "parse_param_body(): Error while parsing quoted string\n");
409                         return -2;
410                 }
411         } else {
412                 if (parse_token_param(_s, &(_c->body)) < 0) {
413                         LOG(L_ERR, "parse_param_body(): Error while parsing token\n");
414                         return -3;
415                 }
416         }
417
418         return 0;
419 }
420
421
422 /**
423  * Only parse one parameter
424  * Returns:
425  *      t: out parameter
426  *      -1: on error
427  *      0: success, but expect a next paramter
428  *      1: success and exepect no more parameters
429  */
430 inline int parse_param(str *_s, pclass_t _c, param_hooks_t *_h, param_t *t)
431 {
432         memset(t, 0, sizeof(param_t));
433
434         parse_param_name(_s, _c, _h, t);
435         trim_leading(_s);
436         
437         if (_s->len == 0) { /* The last parameter without body */
438                 t->len = t->name.len;
439                 goto ok;
440         }
441         
442         if (_s->s[0] == '=') {
443                 _s->s++;
444                 _s->len--;
445                 trim_leading(_s);
446
447                 if (_s->len == 0) {
448                         LOG(L_ERR, "parse_params(): Body missing\n");
449                         goto error;
450                 }
451
452                 if (parse_param_body(_s, t) < 0) {
453                         LOG(L_ERR, "parse_params(): Error while parsing param body\n");
454                         goto error;
455                 }
456
457                 t->len = _s->s - t->name.s;
458
459                 trim_leading(_s);
460                 if (_s->len == 0) {
461                         goto ok;
462                 }
463         } else {
464                 t->len = t->name.len;
465         }
466
467         if (_s->s[0] == ',') goto ok; /* To be able to parse header parameters */
468         if (_s->s[0] == '>') goto ok; /* To be able to parse URI parameters */
469
470         if (_s->s[0] != ';') {
471                 LOG(L_ERR, "parse_params(): Invalid character, ; expected\n");
472                 goto error;
473         }
474
475         _s->s++;
476         _s->len--;
477         trim_leading(_s);
478         
479         if (_s->len == 0) {
480                 LOG(L_ERR, "parse_params(): Param name missing after ;\n");
481                 goto error;
482         }
483
484         return 0; /* expect more params */
485
486 ok:
487         return 1; /* done with parsing for params */
488 error:
489         return -1;
490 }
491
492
493
494 /*
495  * Parse parameters
496  * _s is string containing parameters, it will be updated to point behind the parameters
497  * _c is class of parameters
498  * _h is pointer to structure that will be filled with pointer to well known parameters
499  * linked list of parsed parameters will be stored in
500  * the variable _p is pointing to
501  * The function returns 0 on success and negative number
502  * on an error
503  */
504 int parse_params(str* _s, pclass_t _c, param_hooks_t* _h, param_t** _p)
505 {
506         param_t* t;
507
508         if (!_s || !_p) {
509                 LOG(L_ERR, "parse_params(): Invalid parameter value\n");
510                 return -1;
511         }
512
513         if (_h)
514                 memset(_h, 0, sizeof(param_hooks_t));
515         *_p = 0;
516
517         if (!_s->s) { /* no parameters at all -- we're done */
518                 DBG("DEBUG: parse_params: empty uri params, skipping\n");
519                 return 0;
520         }
521                         
522         while(1) {
523                 t = (param_t*)pkg_malloc(sizeof(param_t));
524                 if (t == 0) {
525                         LOG(L_ERR, "parse_params(): No memory left\n");
526                         goto error;
527                 }
528
529                 switch(parse_param(_s, _c, _h, t)) {
530                 case 0: break;
531                 case 1: goto ok;
532                 default: goto error;
533                 }
534
535                 t->next = *_p;
536                 *_p = t;
537         }
538
539  error:
540         if (t) pkg_free(t);
541         free_params(*_p);
542         return -2;
543
544  ok:
545         t->next = *_p;
546         *_p = t;
547         return 0;
548 }
549
550
551 /*
552  * Free linked list of parameters
553  */
554 static inline void do_free_params(param_t* _p, int _shm)
555 {
556         param_t* ptr;
557         
558         while(_p) {
559                 ptr = _p;
560                 _p = _p->next;
561                 if (_shm) shm_free(ptr);
562                 else pkg_free(ptr);
563         }       
564 }
565
566
567 /*
568  * Free linked list of parameters
569  */
570 void free_params(param_t* _p)
571 {
572         do_free_params(_p, 0);
573 }
574
575
576 /*
577  * Free linked list of parameters
578  */
579 void shm_free_params(param_t* _p)
580 {
581         do_free_params(_p, 1);
582 }
583
584
585 /*
586  * Print a parameter structure, just for debugging
587  */
588 static inline void print_param(FILE* _o, param_t* _p)
589 {
590         char* type;
591
592         fprintf(_o, "---param(%p)---\n", _p);
593         
594         switch(_p->type) {
595         case P_OTHER:     type = "P_OTHER";     break;
596         case P_Q:         type = "P_Q";         break;
597         case P_EXPIRES:   type = "P_EXPIRES";   break;
598         case P_METHODS:   type = "P_METHODS";   break;
599         case P_TRANSPORT: type = "P_TRANSPORT"; break;
600         case P_LR:        type = "P_LR";        break;
601         case P_R2:        type = "P_R2";        break;
602         case P_MADDR:     type = "P_MADDR";     break;
603         case P_TTL:       type = "P_TTL";       break;
604         case P_RECEIVED:  type = "P_RECEIVED";  break;
605         case P_DSTIP:     type = "P_DSTIP";     break;
606         case P_DSTPORT:   type = "P_DSTPORT";   break;
607         case P_INSTANCE:  type = "P_INSTANCE";  break;
608         case P_FTAG:      type = "P_FTAG";      break;
609         case P_CALL_ID:   type = "P_CALL_ID";   break;
610         case P_FROM_TAG:  type = "P_FROM_TAG";  break;
611         case P_TO_TAG:    type = "P_TO_TAG";    break;
612         case P_ISD:       type = "P_ISD";       break;
613         case P_SLA:       type = "P_SLA";       break;
614         default:          type = "UNKNOWN";     break;
615         }
616         
617         fprintf(_o, "type: %s\n", type);
618         fprintf(_o, "name: \'%.*s\'\n", _p->name.len, _p->name.s);
619         fprintf(_o, "body: \'%.*s\'\n", _p->body.len, _p->body.s);
620         fprintf(_o, "len : %d\n", _p->len);
621         fprintf(_o, "---/param---\n");
622 }
623
624
625 /*
626  * Print linked list of parameters, just for debugging
627  */
628 void print_params(FILE* _o, param_t* _p)
629 {
630         param_t* ptr;
631         
632         ptr = _p;
633         while(ptr) {
634                 print_param(_o, ptr);
635                 ptr = ptr->next;
636         }
637 }
638
639
640 /*
641  * Duplicate linked list of parameters
642  */
643 static inline int do_duplicate_params(param_t** _n, param_t* _p, int _shm)
644 {
645         param_t* last, *ptr, *t;
646
647         if (!_n) {
648                 LOG(L_ERR, "duplicate_params(): Invalid parameter value\n");
649                 return -1;
650         }
651         
652         last = 0;
653         *_n = 0;
654         ptr = _p;
655         while(ptr) {
656                 if (_shm) {
657                         t = (param_t*)shm_malloc(sizeof(param_t));
658                 } else {
659                         t = (param_t*)pkg_malloc(sizeof(param_t));
660                 }
661                 if (!t) {
662                         LOG(L_ERR, "duplicate_params(): Invalid parameter value\n");
663                         goto err;
664                 }
665                 memcpy(t, ptr, sizeof(param_t));
666                 t->next = 0;
667
668                 if (!*_n) *_n = t;
669                 if (last) last->next = t;
670                 last = t;
671
672                 ptr = ptr->next;
673         }
674         return 0;
675
676  err:
677         do_free_params(*_n, _shm);
678         return -2;
679 }
680
681
682 /*
683  * Duplicate linked list of parameters
684  */
685 int duplicate_params(param_t** _n, param_t* _p)
686 {
687         return do_duplicate_params(_n, _p, 0);
688 }
689
690
691 /*
692  * Duplicate linked list of parameters
693  */
694 int shm_duplicate_params(param_t** _n, param_t* _p)
695 {
696         return do_duplicate_params(_n, _p, 1);
697 }