1 /*
2 * Copyright (C) 2006 iptego GmbH
3 *
4 * This file is part of SEMS, a free SIP media server.
5 *
6 * SEMS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version. This program is released under
10 * the GPL with the additional exemption that compiling, linking,
11 * and/or using OpenSSL is allowed.
12 *
13 * For a license to use the SEMS software under conditions
14 * other than those described here, or to purchase support for this
15 * software, please contact iptel.org by e-mail at the following addresses:
16 * info@iptel.org
17 *
18 * SEMS is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28 #include "AmUriParser.h"
29 #include "log.h"
30
31 // Not on Solaris!
32 #if !defined (__SVR4) && !defined (__sun)
33 #include <strings.h>
34 #endif
35
36 #include <iostream>
37 using namespace std;
38
isEqual(const AmUriParser & c) const39 bool AmUriParser::isEqual(const AmUriParser& c) const {
40 return (uri_user == c.uri_user) &&
41 (!strcasecmp(uri_host.c_str(),
42 c.uri_host.c_str())) &&
43 (uri_port == c.uri_port);
44 }
45
46 /*
47 * Skip display name part
48 */
skip_name(const string & s,unsigned int pos)49 static inline int skip_name(const string& s, unsigned int pos)
50 {
51 size_t i;
52 int quoted = 0;
53
54 for(i = pos; i < s.length(); i++) {
55 char c = s[i];
56 if (!quoted) {
57 if (c == '<') {
58 return i;
59 }
60
61 if (c == '\"') {
62 quoted = 1;
63 }
64 } else {
65 if ((c == '\"') && (s[i-1] != '\\')) quoted = 0;
66 }
67 }
68
69 if (quoted) {
70 ERROR("skip_name(): Closing quote missing in name part of URI\n");
71 return -1;
72 }
73
74 return pos; // no name to skip
75 }
76
77 #define ST1 1 /* Basic state */
78 #define ST2 2 /* Quoted */
79 #define ST3 3 /* Angle quoted */
80 #define ST4 4 /* Angle quoted and quoted */
81 #define ST5 5 /* Escape in quoted */
82 #define ST6 6 /* Escape in angle quoted and quoted */
83
84 /*
85 * Skip URI, stops when , (next contact)
86 * or ; (parameter) is found
87 */
skip_uri(const string & s,unsigned int pos)88 static inline int skip_uri(const string& s, unsigned int pos)
89 {
90 unsigned int len = s.length() - pos;
91 unsigned int p = pos;
92
93 register int st = ST1;
94
95 while(len) {
96 switch(s[p]) {
97 case ',':
98 case ';':
99 if (st == ST1) return p;
100 break;
101
102 case '\"':
103 switch(st) {
104 case ST1: st = ST2; break;
105 case ST2: st = ST1; break;
106 case ST3: st = ST4; break;
107 case ST4: st = ST3; break;
108 case ST5: st = ST2; break;
109 case ST6: st = ST4; break;
110 }
111 break;
112
113 case '<':
114 switch(st) {
115 case ST1: st = ST3; break;
116 case ST3:
117 DBG("ERROR skip_uri(): Second < found\n");
118 return -1;
119 case ST5: st = ST2; break;
120 case ST6: st = ST4; break;
121 }
122 break;
123
124 case '>':
125 switch(st) {
126 case ST1:
127 DBG("ERROR skip_uri(): > is first\n");
128 return -2;
129
130 case ST3: st = ST1; break;
131 case ST5: st = ST2; break;
132 case ST6: st = ST4; break;
133 }
134 break;
135
136 case '\\':
137 switch(st) {
138 case ST2: st = ST5; break;
139 case ST4: st = ST6; break;
140 case ST5: st = ST2; break;
141 case ST6: st = ST4; break;
142 }
143 break;
144
145 default: break;
146
147 }
148
149 p++;
150 len--;
151 }
152
153 if (st != ST1) {
154 DBG("ERROR skip_uri(): < or \" not closed\n");
155 return -3;
156 }
157 return p;
158 }
159 enum {
160 uS0= 0, // start
161 uSPROT, // protocol
162 uSUSER, // user
163 uSHOST, // host
164 uSHOSTWSP, // wsp after host
165 uSPORT, // port
166 uSPORTWSP, // wsp after port
167 uSHDR, // header
168 uSHDRWSP, // wsp after header
169 uSPARAM, // params
170 uSPARAMWSP, // wsp after params
171 uS6 // end
172 };
173 /**
174 * parse uri into user, host, port, param
175 *
176 */
parse_uri()177 bool AmUriParser::parse_uri() {
178 // assuming user@host
179 size_t pos; int st = uS0;
180 size_t p1 = 0;
181 int eq = 0; const char* sip_prot = "SIP:";
182 uri_user = ""; uri_host = ""; uri_port = ""; uri_param = "";
183
184 if (uri.empty())
185 return false;
186
187 pos = skip_name(uri, 0);
188
189 while (pos<uri.length()) {
190 char c = uri[pos];
191 // DBG("(1) c = %c, st = %d\n", c, st);
192 switch(st) {
193 case uS0: {
194 switch (c) {
195 case '<': { st = uSPROT; } break;
196 default: {
197 if ((eq<=4)&&(toupper(c) ==sip_prot[eq]))
198 eq++;
199 if (eq==4) { // found sip:
200 uri.find('@', pos+1) == string::npos? st = uSHOST : st = uSUSER; p1 = pos;
201 };
202 } break;
203 };
204 } break;
205 case uSPROT: {
206 if (c == ':') { uri.find('@', pos+1) == string::npos? st = uSHOST : st = uSUSER; p1 = pos;}
207 } break;
208 case uSUSER: {
209 switch(c) {
210 case '@': { uri_user = uri.substr(p1+1, pos-p1-1);
211 st = uSHOST; p1 = pos; }; break;
212 case '>': { uri_host = uri.substr(p1+1, pos-p1-1);
213 st = uS6; p1 = pos; }; break;
214 };
215 } break;
216 case uSHOST: {
217 switch (c) {
218 case ':': { uri_host = uri.substr(p1+1, pos-p1-1);
219 st = uSPORT; p1 = pos; } break;
220 case '?': { uri_host = uri.substr(p1+1, pos-p1-1);
221 st = uSHDR; p1 = pos; } break;
222 case ';': { uri_host = uri.substr(p1+1, pos-p1-1);
223 st = uSPARAM; p1 = pos; } break;
224 case '>': { uri_host = uri.substr(p1+1, pos-p1-1);
225 st = uS6; p1 = pos; } break;
226 case ' ':
227 case '\t': { uri_host = uri.substr(p1+1, pos-p1-1);
228 st = uSHOSTWSP; p1 = pos; }
229 break;
230 };
231 } break;
232 case uSHOSTWSP: {
233 switch (c) {
234 case ':': { st = uSPORT; p1 = pos; } break;
235 case '?': { st = uSHDR; p1 = pos; } break;
236 case ';': { st = uSPARAM; p1 = pos; } break;
237 case '>': { st = uS6; p1 = pos; } break;
238 }
239 }; break;
240
241 case uSPORT: {
242 switch (c) {
243 case ';': { uri_port = uri.substr(p1+1, pos-p1-1);
244 st = uSPARAM; p1 = pos; } break;
245 case '?': { uri_port = uri.substr(p1+1, pos-p1-1);
246 st = uSHDR; p1 = pos; } break;
247 case '>': { uri_port = uri.substr(p1+1, pos-p1-1);
248 st = uS6; p1 = pos; } break;
249 case ' ':
250 case '\t':{ uri_port = uri.substr(p1+1, pos-p1-1);
251 st = uSPORTWSP; p1 = pos; } break;
252 };
253 } break;
254 case uSPORTWSP: {
255 switch (c) {
256 case '?': { st = uSHDR; p1 = pos; } break;
257 case ';': { st = uSPARAM; p1 = pos; } break;
258 case '>': { st = uS6; p1 = pos; } break;
259 };
260 } break;
261
262 case uSHDR: {
263 switch (c) {
264 case '>': { uri_headers = uri.substr(p1+1, pos-p1-1);
265 st = uS6; p1 = pos; } break;
266 case '\t':
267 case ' ': { uri_headers = uri.substr(p1+1, pos-p1-1);
268 st = uSHDRWSP; p1 = pos; } break;
269 }
270 } break;
271 case uSHDRWSP: {
272 switch (c) {
273 case ';': { st = uSPARAM; p1 = pos; }; break;
274 case '>': { st = uS6; p1 = pos; } break;
275 }
276 } break;
277
278 case uSPARAM: {
279 switch (c) {
280 case '>': { uri_param = uri.substr(p1+1, pos-p1-1);
281 st = uS6; p1 = pos; } break;
282 case '?': { uri_param = uri.substr(p1+1, pos-p1-1);
283 st = uSHDR; p1 = pos; } break;
284 case '\t':
285 case ' ': { uri_param = uri.substr(p1+1, pos-p1-1);
286 st = uSPARAMWSP; p1 = pos; } break;
287 };
288 } break;
289 case uSPARAMWSP: {
290 switch (c) {
291 case '>': { st = uS6; p1 = pos; } break;
292 };
293 } break;
294
295 };
296 // DBG("(2) c = %c, st = %d\n", c, st);
297 pos++;
298 }
299 switch(st) {
300 case uSUSER:
301 case uSHOST: uri_host = uri.substr(p1+1, pos-p1-1); break;
302 case uSPORT: uri_port = uri.substr(p1+1, pos-p1-1); break;
303 case uSHDR: uri_headers = uri.substr(p1+1, pos-p1-1); break;
304 case uSPARAM: uri_param = uri.substr(p1+1, pos-p1-1); break;
305 case uS0:
306 case uSPROT: { DBG("ERROR while parsing uri\n"); return false; } break;
307 };
308 return true;
309 }
310
nocase_equals(const string & a,const string & b)311 static bool nocase_equals(const string &a, const string &b)
312 {
313 if (a.length() != b.length()) return false;
314 return strcasecmp(a.c_str(), b.c_str()) == 0;
315 }
316
add_param(map<string,string> & params,const string & name,const string & value)317 static void add_param(map<string, string> ¶ms, const string &name, const string &value)
318 {
319 // convert known parameter names to lower
320 // (we can not use case insensitive map because parameters can be defined as
321 // case sensitive (RFC 3261: "Unless otherwise stated in the definition of
322 // a particular header field, field values, parameter names, and parameter
323 // values are case-insensitive.)"
324
325 static const string expires("expires");
326
327 if (nocase_equals(name, expires)) {
328 params[expires] = value;
329 return;
330 }
331
332 params[name] = value;
333 }
334
335 #define pS0 0 // start
336 #define pS1 1 // name
337 #define pS2 2 // val
338 /**
339 * parse params in param map
340 *
341 */
parse_params(const string & line,int & pos)342 bool AmUriParser::parse_params(const string& line, int& pos) {
343 size_t p1=pos, p2=pos;
344 int st = 0; int quoted = false;
345 char last_c = ' ';
346 bool hit_comma = false;
347 params.clear();
348 while((size_t)pos < line.length()) {
349 char c = line[pos];
350 if (!quoted) {
351 if (c == ',') {
352 hit_comma = true;
353 break;
354 }
355 if (c == '\"') {
356 quoted = 1;
357 } else if (c == '=') {
358 p2 = pos; st = pS2;
359 } else if (c == ';') {
360 if (st == pS1) {
361 add_param(params, line.substr(p1, pos-p1), "");
362 st = pS0;
363 p1 = pos;
364 } else if (st == pS2) {
365 add_param(params, line.substr(p1, p2-p1), line.substr(p2+1, pos-p2-1));
366 st = pS0;
367 p1 = pos;
368 }
369 } else {
370 if (st == pS0) {
371 st = pS1;
372 p1 = pos;
373 }
374 }
375
376 } else {
377 if ((c == '\"') && (last_c != '\\')) quoted = 0;
378 }
379 last_c = c;
380 pos++;
381 }
382
383 if (st == pS2) {
384 if (hit_comma)
385 add_param(params, line.substr(p1, p2-p1), line.substr(p2+1, pos-p2 -1));
386 else
387 add_param(params, line.substr(p1, p2-p1), line.substr(p2+1, pos-p2));
388 } else if (st == pS1) {
389 add_param(params, line.substr(p1, pos-p1), "");
390 }
391 return true;
392 }
393
parse_nameaddr(const string & line)394 bool AmUriParser::parse_nameaddr(const string& line) {
395 size_t pos=0; size_t end=0;
396 return parse_contact(line, pos, end);
397 }
398
parse_contact(const string & line,size_t pos,size_t & end)399 bool AmUriParser::parse_contact(const string& line, size_t pos, size_t& end) {
400 int p0 = skip_name(line, pos);
401 if (p0 < 0) { return false; }
402 if ((size_t)p0 > pos) {
403 // save display name
404 size_t dn_b = pos; size_t dn_e = p0;
405 while (dn_b < line.size() && line[dn_b] == ' ') { dn_b++; } // skip leading WS
406 while (dn_e > 0 && line[dn_e-1] == ' ') dn_e--; // skip trailing WS
407 if (dn_e > dn_b) {
408 if (line[dn_e-1] == '"' && line[dn_b] == '"') {
409 dn_b++; dn_e--; // skip quotes
410 }
411 display_name = line.substr(dn_b, dn_e - dn_b);
412 }
413 }
414 int p1 = skip_uri(line, p0);
415 if (p1 < 0) { return false; }
416 // if (p1 < 0) return false;
417 uri = line.substr(p0, p1-p0);
418 if (!parse_uri()) { return false; }
419 parse_params(line, p1);
420 end = p1;
421 return true;
422 }
423
add_param_to_param_list(const string & param_name,const string & param_value,const string & param_list)424 string AmUriParser::add_param_to_param_list(const string& param_name,
425 const string& param_value, const string& param_list)
426 {
427 string list_of_params(param_list);
428
429 string param = param_name;
430 if (!param_value.empty())
431 param += "=" + param_value;
432
433 // if param_string empty - set it
434 if (list_of_params.empty()) {
435 list_of_params = param;
436 }
437 else {
438 // check if parameter already exists; if yes - replace it
439
440 size_t start = 0, end = 0, eq = 0, length = 0;
441 bool replaced = false;
442
443 do {
444 // get next param
445 end = list_of_params.find_first_of(';', start);
446
447 length = (end == string::npos) ? list_of_params.size() - start : end - start;
448
449 // it the param empty?
450 eq = list_of_params.substr(start, length).find('=');
451
452 if (eq != string::npos) { // non-empty param found
453 if (list_of_params.substr(start, eq) == param_name) {
454 list_of_params.replace(start, length, param);
455 replaced = true;
456 break;
457 }
458 }
459 else { // empty param found
460 if (list_of_params.substr(start, length) == param_name) {
461 list_of_params.replace(start, length, param);
462 replaced = true;
463 break;
464 }
465 }
466
467 start = end + 1;
468 }
469 while (end != string::npos && start != string::npos);
470
471 // if parameter doesn't exist - append it
472 if (!replaced)
473 list_of_params.append(";" + param);
474 }
475 return list_of_params;
476 }
477
dump() const478 void AmUriParser::dump() const {
479 DBG("--- Uri Info --- \n");
480 DBG(" uri '%s'\n", uri.c_str());
481 DBG(" display_name '%s'\n", display_name.c_str());
482 DBG(" uri_user '%s'\n", uri_user.c_str());
483 DBG(" uri_host '%s'\n", uri_host.c_str());
484 DBG(" uri_port '%s'\n", uri_port.c_str());
485 DBG(" uri_hdr '%s'\n", uri_headers.c_str());
486 DBG(" uri_param '%s'\n", uri_param.c_str());
487 for (map<string, string>::const_iterator it = params.begin();
488 it != params.end(); it++) {
489
490 if (it->second.empty())
491 DBG(" param '%s'\n", it->first.c_str());
492 else
493 DBG(" param '%s'='%s'\n", it->first.c_str(), it->second.c_str());
494 }
495 DBG("-------------------- \n");
496 }
497
uri_str() const498 string AmUriParser::uri_str() const
499 {
500 string res = canon_uri_str();
501
502 if(!uri_param.empty()) {
503 res += ";" + uri_param;
504 }
505
506 if (!uri_headers.empty()) {
507 res+="?" + uri_headers;
508 }
509
510 return res;
511 }
512
canon_uri_str() const513 string AmUriParser::canon_uri_str() const
514 {
515 string res = "sip:"; // fixme: always SIP...
516 if(!uri_user.empty()) {
517 res += uri_user + "@";
518 }
519 res += uri_host;
520
521 if(!uri_port.empty()) {
522 res += ":" + uri_port;
523 }
524
525 return res;
526 }
527
nameaddr_str() const528 string AmUriParser::nameaddr_str() const
529 {
530 string res = "<" + uri_str() + ">";
531
532 if(!display_name.empty())
533 res = "\"" + display_name + "\" " + res;
534
535 for (map<string, string>::const_iterator it = params.begin();
536 it != params.end(); it++) {
537
538 res += ";"+it->first;
539 if (!it->second.empty())
540 res += "="+it->second;
541 }
542
543 return res;
544 }
545
print() const546 string AmUriParser::print() const {
547 return nameaddr_str();
548 }
549