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> &params, 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