434d1dfdc1bbb489d2d0110124500dd6d2ff5580
[sip-router] / src / modules / sca / sca_util.c
1 /*\r
2  * Copyright (C) 2012 Andrew Mortensen\r
3  *\r
4  * This file is part of the sca module for Kamailio, a free SIP server.\r
5  *\r
6  * The sca module is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version\r
10  *\r
11  * The sca module is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA. 02110-1301 USA\r
19  */\r
20 #include "sca_common.h"\r
21 #include "sca.h"\r
22 #include <assert.h>\r
23 \r
24 #include "sca_util.h"\r
25 #include "../../core/dset.h"\r
26 #include "../../core/parser/sdp/sdp.h"\r
27 \r
28 int sca_get_msg_method(sip_msg_t *msg)\r
29 {\r
30         assert(msg != NULL);\r
31 \r
32         if (msg->first_line.type == SIP_REQUEST) {\r
33                 return (msg->REQ_METHOD);\r
34         }\r
35 \r
36         return (sca_get_msg_cseq_method(msg));\r
37 }\r
38 \r
39 int sca_get_msg_contact_uri(sip_msg_t *msg, str *contact_uri)\r
40 {\r
41         contact_body_t *contact_body;\r
42 \r
43         assert(msg != NULL);\r
44         assert(contact_uri != NULL);\r
45 \r
46         if (SCA_HEADER_EMPTY(msg->contact)) {\r
47                 LM_DBG("Empty Contact header\n");\r
48                 contact_uri->s = NULL;\r
49                 contact_uri->len = 0;\r
50 \r
51                 return (0);\r
52         }\r
53 \r
54         if (parse_contact(msg->contact) < 0) {\r
55                 LM_ERR("Failed to parse Contact header: %.*s\n",\r
56                                 STR_FMT(&msg->contact->body));\r
57                 return (-1);\r
58         }\r
59         if ((contact_body = (contact_body_t *) msg->contact->parsed) == NULL) {\r
60                 LM_ERR("Invalid Contact header: %.*s\n", STR_FMT(&msg->contact->body));\r
61                 return (-1);\r
62         }\r
63         if (contact_body->star) {\r
64                 LM_ERR("Invalid Contact header: SCA Contact must not be \"*\"\n");\r
65                 return (-1);\r
66         }\r
67         if (contact_body->contacts == NULL) {\r
68                 LM_ERR("Invalid Contact header: parser found no contacts\n");\r
69                 return (-1);\r
70         }\r
71         if (contact_body->contacts->next) {\r
72                 LM_ERR("Invalid Contact header: Contact may only contain one URI\n");\r
73                 return (-1);\r
74         }\r
75 \r
76         contact_uri->s = contact_body->contacts->uri.s;\r
77         contact_uri->len = contact_body->contacts->uri.len;\r
78 \r
79         return (1);\r
80 }\r
81 \r
82 int sca_get_msg_cseq_number(sip_msg_t *msg)\r
83 {\r
84         int cseq;\r
85 \r
86         assert(msg != NULL);\r
87 \r
88         if (SCA_HEADER_EMPTY(msg->cseq)) {\r
89                 LM_ERR("Empty Cseq header\n");\r
90                 return (-1);\r
91         }\r
92         if (str2int(&(get_cseq(msg)->number), (unsigned int *) &cseq) != 0) {\r
93                 LM_ERR("Bad Cseq header: %.*s\n", STR_FMT(&msg->cseq->body));\r
94                 return (-1);\r
95         }\r
96 \r
97         return (cseq);\r
98 }\r
99 \r
100 /*\r
101  *  assumes cseq header in msg is already parsed\r
102  */\r
103 int sca_get_msg_cseq_method(sip_msg_t *msg)\r
104 {\r
105         assert(msg != NULL);\r
106 \r
107         if (SCA_HEADER_EMPTY(msg->cseq)) {\r
108                 LM_ERR("Empty Cseq header\n");\r
109                 return (-1);\r
110         }\r
111 \r
112         return (get_cseq(msg)->method_id);\r
113 }\r
114 \r
115 int sca_get_msg_from_header(sip_msg_t *msg, struct to_body **from)\r
116 {\r
117         struct to_body *f;\r
118 \r
119         assert(msg != NULL);\r
120         assert(from != NULL);\r
121 \r
122         if (SCA_HEADER_EMPTY(msg->from)) {\r
123                 LM_ERR("Empty From header\n");\r
124                 return (-1);\r
125         }\r
126         if (parse_from_header(msg) < 0) {\r
127                 LM_ERR("Bad From header\n");\r
128                 return (-1);\r
129         }\r
130         f = get_from(msg);\r
131         if (SCA_STR_EMPTY(&f->tag_value)) {\r
132                 LM_ERR("Bad From header: no tag parameter\n");\r
133                 return (-1);\r
134         }\r
135 \r
136         // ensure the URI is parsed for future use\r
137         if (parse_uri(f->uri.s, f->uri.len, GET_FROM_PURI(msg)) < 0) {\r
138                 LM_ERR("Failed to parse From URI %.*s\n", STR_FMT(&f->uri));\r
139                 return (-1);\r
140         }\r
141 \r
142         *from = f;\r
143 \r
144         return (0);\r
145 }\r
146 \r
147 int sca_get_msg_to_header(sip_msg_t *msg, struct to_body **to)\r
148 {\r
149         struct to_body parsed_to;\r
150         struct to_body *t = NULL;\r
151 \r
152         assert(msg != NULL);\r
153         assert(to != NULL);\r
154 \r
155         if (SCA_HEADER_EMPTY(msg->to)) {\r
156                 LM_ERR("Empty To header\n");\r
157                 return (-1);\r
158         }\r
159         t = get_to(msg);\r
160         if (t == NULL) {\r
161                 parse_to(msg->to->body.s, msg->to->body.s + msg->to->body.len + 1, // end of buffer\r
162                 &parsed_to);\r
163                 if (parsed_to.error != PARSE_OK) {\r
164                         LM_ERR("Bad To header\n");\r
165                         return (-1);\r
166                 }\r
167                 t = &parsed_to;\r
168         }\r
169 \r
170         // ensure the URI is parsed for future use\r
171         if (parse_uri(t->uri.s, t->uri.len, GET_TO_PURI(msg)) < 0) {\r
172                 LM_ERR("Failed to parse To URI %.*s\n", STR_FMT(&t->uri));\r
173                 return (-1);\r
174         }\r
175 \r
176         *to = t;\r
177 \r
178         return (0);\r
179 }\r
180 \r
181 /*\r
182  * caller needs to call free_to for *body\r
183  */\r
184 int sca_build_to_body_from_uri(sip_msg_t *msg, struct to_body **body, str *uri)\r
185 {\r
186         assert(msg != NULL);\r
187         assert(body != NULL);\r
188         assert(uri != NULL);\r
189 \r
190         *body = pkg_malloc(sizeof(struct to_body));\r
191         if(*body == NULL) {\r
192                 LM_ERR("cannot allocate pkg memory\n");\r
193                 return(-1);\r
194         }\r
195 \r
196         parse_to(uri->s, uri->s + uri->len + 1, *body);\r
197         if ((*body)->error != PARSE_OK) {\r
198                 LM_ERR("Bad uri value[%.*s]\n", STR_FMT(uri));\r
199                 free_to(*body);\r
200                 return(-1);\r
201         }\r
202         return (0);\r
203 }\r
204 \r
205 /*\r
206  *  count characters requiring escape as defined by escape_common\r
207  */\r
208 int sca_uri_display_escapes_count(str *display) {\r
209         int c = 0;\r
210         int i;\r
211 \r
212         if (SCA_STR_EMPTY(display)) {\r
213                 return (0);\r
214         }\r
215 \r
216         for (i = 0; i < display->len; i++) {\r
217                 switch (display->s[i]) {\r
218                 case '\'':\r
219                 case '"':\r
220                 case '\\':\r
221                 case '\0':\r
222                         c++;\r
223 \r
224                 default:\r
225                         break;\r
226                 }\r
227         }\r
228 \r
229         return (c);\r
230 }\r
231 \r
232 int sca_uri_extract_aor(str *uri, str *aor)\r
233 {\r
234         char *semi;\r
235 \r
236         assert(aor != NULL);\r
237 \r
238         if (uri == NULL) {\r
239                 aor->s = NULL;\r
240                 aor->len = 0;\r
241                 return (-1);\r
242         }\r
243 \r
244         aor->s = uri->s;\r
245         semi = memchr(uri->s, ';', uri->len);\r
246         if (semi != NULL) {\r
247                 aor->len = semi - uri->s;\r
248         } else {\r
249                 aor->len = uri->len;\r
250         }\r
251 \r
252         return (0);\r
253 }\r
254 \r
255 int sca_uri_build_aor(str *aor, int maxlen, str *contact_uri, str *domain_uri)\r
256 {\r
257         char *p;\r
258         char *dp;\r
259         int len;\r
260 \r
261         assert(aor != NULL);\r
262         assert(contact_uri != NULL);\r
263         assert(domain_uri != NULL);\r
264 \r
265         if (contact_uri->len + domain_uri->len >= maxlen) {\r
266                 return (-1);\r
267         }\r
268 \r
269         p = memchr(contact_uri->s, '@', contact_uri->len);\r
270         if (p == NULL) {\r
271                 // no username, by definition can't be an SCA line\r
272                 aor->s = NULL;\r
273                 aor->len = 0;\r
274 \r
275                 return (0);\r
276         }\r
277         dp = memchr(domain_uri->s, '@', domain_uri->len);\r
278         if (dp == NULL) {\r
279                 // may be nameless URI\r
280                 dp = memchr(domain_uri->s, ':', domain_uri->len);\r
281                 if (dp == NULL) {\r
282                         // bad domain URI\r
283                         return (-1);\r
284                 }\r
285         }\r
286         dp++;\r
287 \r
288         len = p - contact_uri->s;\r
289         memcpy(aor->s, contact_uri->s, len);\r
290         aor->s[len] = '@';\r
291         len += 1;\r
292         aor->len = len;\r
293 \r
294         len = domain_uri->len - (dp - domain_uri->s);\r
295         memcpy(aor->s + aor->len, dp, len);\r
296         aor->len += len;\r
297 \r
298         return (aor->len);\r
299 }\r
300 \r
301 int sca_aor_create_from_info(str *aor, uri_type type, str *user, str *domain,\r
302                 str *port)\r
303 {\r
304         str scheme = STR_NULL;\r
305         int len = 0;\r
306 \r
307         assert(aor != NULL);\r
308 \r
309         uri_type_to_str(type, &scheme);\r
310 \r
311         // +1 for ':', +1 for '@'\r
312         len = scheme.len + 1 + user->len + 1 + domain->len;\r
313         if (!SCA_STR_EMPTY(port)) {\r
314                 // +1 for ':'\r
315                 len += 1 + port->len;\r
316         }\r
317 \r
318         aor->s = (char *) pkg_malloc(len);\r
319         if (aor->s == NULL) {\r
320                 LM_ERR("sca_aor_create_from_info: pkg_malloc %d bytes failed\n", len);\r
321                 return (-1);\r
322         }\r
323 \r
324         len = 0;\r
325         SCA_STR_COPY(aor, &scheme);\r
326         len += scheme.len;\r
327 \r
328         *(aor->s + len) = ':';\r
329         aor->len++;\r
330         len++;\r
331 \r
332         SCA_STR_APPEND(aor, user);\r
333         len += user->len;\r
334 \r
335         *(aor->s + len) = '@';\r
336         aor->len++;\r
337         len++;\r
338 \r
339         SCA_STR_APPEND(aor, domain);\r
340         len += domain->len;\r
341 \r
342         if (!SCA_STR_EMPTY(port)) {\r
343                 *(aor->s + len) = ':';\r
344                 len += 1;\r
345 \r
346                 SCA_STR_APPEND(aor, port);\r
347                 len += port->len;\r
348         }\r
349 \r
350         return (aor->len);\r
351 }\r
352 \r
353 int sca_create_canonical_aor_for_ua(sip_msg_t *msg, str *c_aor, int ua_opts)\r
354 {\r
355         struct to_body *tf = NULL;\r
356         sip_uri_t c_uri;\r
357         str tf_aor = STR_NULL;\r
358         str contact_uri = STR_NULL;\r
359         int rc = -1;\r
360 \r
361         assert(msg != NULL);\r
362         assert(c_aor != NULL);\r
363 \r
364         memset(c_aor, 0, sizeof(str));\r
365 \r
366         if ((ua_opts & SCA_AOR_TYPE_AUTO)) {\r
367                 if (msg->first_line.type == SIP_REQUEST) {\r
368                         ua_opts = SCA_AOR_TYPE_UAC;\r
369                 } else {\r
370                         ua_opts = SCA_AOR_TYPE_UAS;\r
371                 }\r
372         }\r
373 \r
374         if ((ua_opts & SCA_AOR_TYPE_UAC)) {\r
375                 if (sca_get_msg_from_header(msg, &tf) < 0) {\r
376                         LM_ERR("sca_create_canonical_aor: failed to get From header\n");\r
377                         goto done;\r
378                 }\r
379         } else {\r
380                 if (sca_get_msg_to_header(msg, &tf) < 0) {\r
381                         LM_ERR("sca_create_canonical_aor: failed to get To header\n");\r
382                         goto done;\r
383                 }\r
384         }\r
385 \r
386         if (sca_uri_extract_aor(&tf->uri, &tf_aor) < 0) {\r
387                 LM_ERR("sca_create_canonical_aor: failed to extract AoR from "\r
388                                 "URI <%.*s>\n", STR_FMT(&tf->uri));\r
389                 goto done;\r
390         }\r
391 \r
392         memset(&c_uri, 0, sizeof(sip_uri_t));\r
393         if ((rc = sca_get_msg_contact_uri(msg, &contact_uri)) < 0) {\r
394                 LM_ERR("sca_create_canonical_aor: failed to get contact URI from "\r
395                                 "Contact <%.*s>\n", STR_FMT(&msg->contact->body));\r
396                 goto done;\r
397         }\r
398         if (rc > 0) {\r
399                 if (parse_uri(contact_uri.s, contact_uri.len, &c_uri) < 0) {\r
400                         LM_ERR("sca_create_canonical_aor: failed to parse Contact URI "\r
401                                         "<%.*s>\n", STR_FMT(&contact_uri));\r
402                         rc = -1;\r
403                         goto done;\r
404                 }\r
405         }\r
406 \r
407         if (SCA_STR_EMPTY(&c_uri.user) ||\r
408         SCA_STR_EQ(&c_uri.user, &tf->parsed_uri.user)) {\r
409                 // empty contact header or Contact user matches To/From AoR\r
410                 c_aor->s = (char *) pkg_malloc(tf_aor.len);\r
411                 c_aor->len = tf_aor.len;\r
412                 memcpy(c_aor->s, tf_aor.s, tf_aor.len);\r
413         } else {\r
414                 // Contact user and To/From user mismatch\r
415                 if (sca_aor_create_from_info(c_aor, c_uri.type, &c_uri.user,\r
416                                 &tf->parsed_uri.host, &tf->parsed_uri.port) < 0) {\r
417                         LM_ERR("sca_create_canonical_aor: failed to create AoR from "\r
418                                         "Contact <%.*s> and URI <%.*s>\n",\r
419                                         STR_FMT(&contact_uri), STR_FMT(&tf_aor));\r
420                         goto done;\r
421                 }\r
422         }\r
423 \r
424         rc = 1;\r
425 \r
426         done: return (rc);\r
427 }\r
428 \r
429 int sca_create_canonical_aor(sip_msg_t *msg, str *c_aor)\r
430 {\r
431         return (sca_create_canonical_aor_for_ua(msg, c_aor, SCA_AOR_TYPE_AUTO));\r
432 }\r
433 \r
434 /*\r
435  * XXX this considers any held stream to mean the call is on hold. correct?\r
436  */\r
437 int sca_call_is_held(sip_msg_t *msg)\r
438 {\r
439         sdp_session_cell_t *session;\r
440         sdp_stream_cell_t *stream;\r
441         int n_sess;\r
442         int n_str;\r
443         int is_held = 0;\r
444         int rc;\r
445 \r
446         if(sca->cfg->onhold_bflag >= 0) {\r
447                 if (isbflagset(0, (flag_t)sca->cfg->onhold_bflag)==1) {\r
448                         LM_DBG("onhold_bflag set, skip parse_sdp and set held\n");\r
449                         return ( 1 );\r
450                 }\r
451         }\r
452         rc = parse_sdp(msg);\r
453         if (rc < 0) {\r
454                 LM_ERR("sca_call_is_held: parse_sdp body failed\n");\r
455                 return (0);\r
456         } else if (rc > 0) {\r
457                 LM_DBG("sca_call_is_held: parse_sdp returned %d, no SDP body\n", rc);\r
458                 return (0);\r
459         }\r
460 \r
461         // Cf. modules_k/textops's exported is_audio_on_hold\r
462         for (n_sess = 0, session = get_sdp_session(msg, n_sess); session != NULL;\r
463                         n_sess++, session = get_sdp_session(msg, n_sess)) {\r
464 \r
465                 for (n_str = 0, stream = get_sdp_stream(msg, n_sess, n_str);\r
466                                 stream != NULL;\r
467                                 n_str++, stream = get_sdp_stream(msg, n_sess, n_str)) {\r
468                         if (stream->is_on_hold) {\r
469                                 LM_DBG("sca_call_is_held: parse_sdp detected stream is on hold\n");\r
470                                 is_held = 1;\r
471                                 goto done;\r
472                         }\r
473                 }\r
474         }\r
475 \r
476         done: return (is_held);\r
477 }\r