Merge kamailio modules into sip-router master branch
[sip-router] / parser / contact / contact.c
1 /*
2  * $Id$
3  *
4  * Parses one Contact in Contact HF body
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-25 Adapted to use new parameter parser (janakj)
32  */
33
34 #include <string.h>        /* memset */
35 #include "../../mem/mem.h" /* pkg_malloc, pkg_free */
36 #include "../../dprint.h"
37 #include "../../trim.h"    /* trim_leading, trim_trailing */
38 #include "contact.h"
39
40
41 #define ST1 1 /* Basic state */
42 #define ST2 2 /* Quoted */
43 #define ST3 3 /* Angle quoted */
44 #define ST4 4 /* Angle quoted and quoted */
45 #define ST5 5 /* Escape in quoted */
46 #define ST6 6 /* Escape in angle quoted and quoted */
47
48
49 /*
50  * Skip URI, stops when , (next contact)
51  * or ; (parameter) is found
52  */
53 static inline int skip_uri(str* _s)
54 {
55         register int st = ST1;
56
57         while(_s->len) {
58                 switch(*(_s->s)) {
59                 case ',':
60                 case ';':
61                         if (st == ST1) return 0;
62                         break;
63
64                 case '\"':
65                         switch(st) {
66                         case ST1: st = ST2; break;
67                         case ST2: st = ST1; break;
68                         case ST3: st = ST4; break;
69                         case ST4: st = ST3; break;
70                         case ST5: st = ST2; break;
71                         case ST6: st = ST4; break;
72                         }
73                         break;
74
75                 case '<':
76                         switch(st) {
77                         case ST1: st = ST3; break;
78                         case ST3: 
79                                 LOG(L_ERR, "skip_uri(): Second < found\n");
80                                 return -1;
81                         case ST5: st = ST2; break;
82                         case ST6: st = ST4; break;
83                         }
84                         break;
85                         
86                 case '>':
87                         switch(st) {
88                         case ST1: 
89                                 LOG(L_ERR, "skip_uri(): > is first\n");
90                                 return -2;
91
92                         case ST3: st = ST1; break;
93                         case ST5: st = ST2; break;
94                         case ST6: st = ST4; break;
95                         }
96                         break;
97
98                 case '\\':
99                         switch(st) {
100                         case ST2: st = ST5; break;
101                         case ST4: st = ST6; break;
102                         case ST5: st = ST2; break;
103                         case ST6: st = ST4; break;
104                         }
105                         break;
106
107                 default: break;
108
109                 }
110
111                 _s->s++;
112                 _s->len--;
113         }
114
115         if (st != ST1) {
116                 LOG(L_ERR, "skip_uri(): < or \" not closed\n");
117                 return -3;
118         }
119
120         return 0;
121 }
122
123
124 /*
125  * Skip name part
126  *
127  * _s will be adjusted to point at the beginning
128  * of URI
129  */
130 static inline int skip_name(str* _s)
131 {
132         char* last_wsp, *p;
133         int i, quoted = 0;
134         
135
136         if (!_s) {
137                 LOG(L_ERR, "skip_name(): Invalid parameter value\n");
138                 return -1;
139         }
140
141         p = _s->s;
142
143         last_wsp = 0;
144
145         for(i = 0; i < _s->len; i++) {
146                 if (!quoted) {
147                         if ((*p == ' ') || (*p == '\t')) {
148                                 last_wsp = p;
149                         } else {
150                                 if (*p == '<') {
151                                         _s->s = p;
152                                         _s->len -= i;
153                                         return 0;
154                                 }
155                                 
156                                 if (*p == ':') {
157                                         if (last_wsp) {
158                                                 _s->s = last_wsp;
159                                                 _s->len -= last_wsp - _s->s + 1;
160                                         }
161                                         return 0;
162                                 }
163
164                                 if (*p == '\"') {
165                                         quoted = 1;
166                                 }
167                         }
168                 } else {
169                         if ((*p == '\"') && (*(p-1) != '\\')) quoted = 0;
170                 }
171                 p++;
172         }
173
174         if (quoted) {
175                 LOG(L_ERR, "skip_name(): Closing quote missing in name part of Contact\n");
176         } else {
177                 LOG(L_ERR, "skip_name(): Error in contact, scheme separator not found\n");
178         }
179
180         return -1;
181 }
182
183
184 /*
185  * Parse contacts in a Contact HF
186  */
187 int parse_contacts(str* _s, contact_t** _c)
188 {
189         contact_t* c;
190         param_hooks_t hooks;
191
192         while(1) {
193                      /* Allocate and clear contact structure */
194                 c = (contact_t*)pkg_malloc(sizeof(contact_t));
195                 if (c == 0) {
196                         LOG(L_ERR, "parse_contacts(): No memory left\n");
197                         goto error;
198                 }
199                 memset(c, 0, sizeof(contact_t));
200                 
201                 c->name.s = _s->s;
202
203                 if (skip_name(_s) < 0) {
204                         LOG(L_ERR, "parse_contacts(): Error while skipping name part\n");
205                         goto error;
206                 }
207
208                 c->uri.s = _s->s;
209                 c->name.len = _s->s - c->name.s;
210                 trim_trailing(&c->name);
211                 
212                      /* Find the end of the URI */
213                 if (skip_uri(_s) < 0) {
214                         LOG(L_ERR, "parse_contacts(): Error while skipping URI\n");
215                         goto error;
216                 }
217                 
218                 c->uri.len = _s->s - c->uri.s; /* Calculate URI length */
219                 trim_trailing(&(c->uri));      /* Remove any trailing spaces from URI */
220
221                      /* Remove <> if any */
222                 if ((c->uri.len >= 2) && (c->uri.s[0] == '<') && (c->uri.s[c->uri.len - 1] == '>')) {
223                         c->uri.s++;
224                         c->uri.len -= 2;
225                 }
226
227                 trim(&c->uri);
228                 
229                 if (_s->len == 0) goto ok;
230                 
231                 if (_s->s[0] == ';') {         /* Contact parameter found */
232                         _s->s++;
233                         _s->len--;
234                         trim_leading(_s);
235                         
236                         if (_s->len == 0) {
237                                 LOG(L_ERR, "parse_contacts(): Error while parsing params\n");
238                                 goto error;
239                         }
240
241                         if (parse_params(_s, CLASS_CONTACT, &hooks, &c->params) < 0) {
242                                 LOG(L_ERR, "parse_contacts(): Error while parsing parameters\n");
243                                 goto error;
244                         }
245
246                         c->q = hooks.contact.q;
247                         c->expires = hooks.contact.expires;
248                         c->received = hooks.contact.received;
249                         c->methods = hooks.contact.methods;
250                         c->instance = hooks.contact.instance;
251
252                         if (_s->len == 0) goto ok;
253                 }
254
255                      /* Next character is comma */
256                 c->len = _s->s - c->name.s;
257                 _s->s++;
258                 _s->len--;
259                 trim_leading(_s);
260
261                 if (_s->len == 0) {
262                         LOG(L_ERR, "parse_contacts(): Text after comma missing\n");
263                         goto error;
264                 }
265
266                 c->next = *_c;
267                 *_c = c;
268         }
269
270  error:
271         if (c) pkg_free(c);
272         free_contacts(_c); /* Free any contacts created so far */
273         return -1;
274
275  ok:
276         c->len = _s->s - c->name.s;
277         c->next = *_c;
278         *_c = c;
279         return 0;
280 }
281
282
283 /*
284  * Free list of contacts
285  * _c is head of the list
286  */
287 void free_contacts(contact_t** _c)
288 {
289         contact_t* ptr;
290
291         while(*_c) {
292                 ptr = *_c;
293                 *_c = (*_c)->next;
294                 if (ptr->params) {
295                         free_params(ptr->params);
296                 }
297                 pkg_free(ptr);
298         }
299 }
300
301
302 /*
303  * Print list of contacts, just for debugging
304  */
305 void print_contacts(FILE* _o, contact_t* _c)
306 {
307         contact_t* ptr;
308
309         ptr = _c;
310
311         while(ptr) {
312                 fprintf(_o, "---Contact---\n");
313                 fprintf(_o, "name    : '%.*s'\n", ptr->name.len, ptr->name.s);
314                 fprintf(_o, "URI     : '%.*s'\n", ptr->uri.len, ptr->uri.s);
315                 fprintf(_o, "q       : %p\n", ptr->q);
316                 fprintf(_o, "expires : %p\n", ptr->expires);
317                 fprintf(_o, "received: %p\n", ptr->received);
318                 fprintf(_o, "methods  : %p\n", ptr->methods);
319                 fprintf(_o, "instance: %p\n", ptr->instance);
320                 fprintf(_o, "len     : %d\n", ptr->len);
321                 if (ptr->params) {
322                         print_params(_o, ptr->params);
323                 }
324                 fprintf(_o, "---/Contact---\n");
325                 ptr = ptr->next;
326         }
327 }