Asterisk - The Open Source Telephony Project GIT-master-3dae2cf
res_pjsip_geolocation.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2022, Sangoma Technologies Corporation
5 *
6 * George Joseph <gjoseph@sangoma.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <depend>res_geolocation</depend>
21 <depend>pjproject</depend>
22 <depend>res_pjsip</depend>
23 <depend>res_pjsip_session</depend>
24 <depend>chan_pjsip</depend>
25 <depend>libxml2</depend>
26 <support_level>core</support_level>
27 ***/
28
29#include "asterisk.h"
30#include "asterisk/module.h"
31#include "asterisk/xml.h"
33
34#include <pjsip_ua.h>
35#include "asterisk/res_pjsip.h"
37
38static pj_str_t GEOLOCATION_HDR;
40
41static int find_pidf(const char *session_name, struct pjsip_rx_data *rdata, char *geoloc_uri,
42 char **pidf_body, unsigned int *pidf_len)
43{
44 char *local_uri = ast_strdupa(geoloc_uri);
45 char *ra = NULL;
46 /*
47 * If the URI is "cid" then we're going to search for a pidf document
48 * in the body of the message. If there's no body, there's no point.
49 */
50 if (!rdata->msg_info.msg->body) {
51 ast_log(LOG_WARNING, "%s: There's no message body in which to search for '%s'. Skipping\n",
52 session_name, geoloc_uri);
53 return -1;
54 }
55
56 if (local_uri[0] == '<') {
57 local_uri++;
58 }
59 ra = strchr(local_uri, '>');
60 if (ra) {
61 *ra = '\0';
62 }
63
64 /*
65 * If the message content type is 'application/pidf+xml', then the pidf is
66 * the only document in the message and we'll just parse the entire body
67 * as xml. If it's 'multipart/mixed' then we have to find the part that
68 * has a Content-ID header value matching the URI.
69 */
70 if (ast_sip_are_media_types_equal(&rdata->msg_info.ctype->media,
72 *pidf_body = rdata->msg_info.msg->body->data;
73 *pidf_len = rdata->msg_info.msg->body->len;
74 } else if (ast_sip_are_media_types_equal(&rdata->msg_info.ctype->media,
76 pj_str_t cid = pj_str(local_uri);
77 pjsip_multipart_part *mp = pjsip_multipart_find_part_by_cid_str(
78 rdata->tp_info.pool, rdata->msg_info.msg->body, &cid);
79
80 if (!mp) {
81 ast_log(LOG_WARNING, "%s: A Geolocation header was found with URI '%s'"
82 " but the associated multipart part was not found in the message body. Skipping URI",
83 session_name, geoloc_uri);
84 return -1;
85 }
86 *pidf_body = mp->body->data;
87 *pidf_len = mp->body->len;
88 } else {
89 ast_log(LOG_WARNING, "%s: A Geolocation header was found with URI '%s'"
90 " but no pidf document with that content id was found. Skipping URI",
91 session_name, geoloc_uri);
92 return -1;
93 }
94
95 return 0;
96}
97
99 struct ast_geoloc_eprofile *eprofile, struct ast_str * buf)
100{
101 const char *session_name = (session ? ast_sip_session_get_name(session) : "NULL_SESSION");
102 struct ast_datastore *ds = NULL;
103 int rc = 0;
104 SCOPE_ENTER(4, "%s\n", session_name);
105
106 ds = ast_geoloc_datastore_create(session_name);
107 if (!ds) {
109 "%s: Couldn't allocate a geoloc datastore\n", session_name);
110 }
111
112 /*
113 * We want the datastore to pass through the dialplan and the core
114 * so we need to turn inheritance on.
115 */
117
118 rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);
119 if (rc <= 0) {
122 "%s: Couldn't add eprofile '%s' to datastore\n", session_name,
123 eprofile->id);
124 }
125
126 ast_channel_lock(session->channel);
128 ast_channel_unlock(session->channel);
129
130 SCOPE_EXIT_RTN_VALUE(0, "%s: eprofile: '%s' EffectiveLoc: %s\n",
131 session_name, eprofile->id, ast_str_buffer(
132 ast_variable_list_join(eprofile->effective_location, ",", "=", NULL, &buf)));
133}
134
135static int handle_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
136{
137 const char *session_name = (session ? ast_sip_session_get_name(session) : "NULL_SESSION");
138 struct ast_sip_endpoint *endpoint = (session ? session->endpoint : NULL);
139 struct ast_channel *channel = (session ? session->channel : NULL);
140 RAII_VAR(struct ast_geoloc_profile *, config_profile, NULL, ao2_cleanup);
141 RAII_VAR(struct ast_geoloc_eprofile *, config_eprofile, NULL, ao2_cleanup);
143 RAII_VAR(struct ast_geoloc_eprofile *, incoming_eprofile, NULL, ao2_cleanup);
144 char *geoloc_hdr_value = NULL;
145 char *geoloc_routing_hdr_value = NULL;
146 char *geoloc_uri = NULL;
147 int rc = 0;
148 RAII_VAR(struct ast_str *, buf, NULL, ast_free);
149 pjsip_generic_string_hdr *geoloc_hdr = NULL;
150 pjsip_generic_string_hdr *geoloc_routing_hdr = NULL;
151 SCOPE_ENTER(3, "%s\n", session_name);
152
153 if (!session) {
154 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: session is NULL!!!. Skipping.\n",
155 session_name);
156 }
157 if (!endpoint) {
158 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no endpoint. Skipping.\n",
159 session_name);
160 }
161
162 if (!channel) {
163 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no channel. Skipping.\n",
164 session_name);
165 }
166
167 if (!rdata) {
168 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no rdata. Skipping.\n",
169 session_name);
170 }
171
172 /*
173 * We don't need geoloc_hdr or geoloc_routing_hdr for a while but we get it now
174 * for trace purposes.
175 */
176 geoloc_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &GEOLOCATION_HDR, NULL);
177 geoloc_routing_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
179
180 if (!geoloc_hdr) {
181 ast_trace(4, "%s: Message has no Geolocation header\n", session_name);
182 } else {
183 ast_trace(4, "%s: Geolocation: " PJSTR_PRINTF_SPEC "\n", session_name,
184 PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
185 }
186
187 if (ast_strlen_zero(endpoint->geoloc_incoming_call_profile)) {
188 if (geoloc_hdr) {
189 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has Geolocation header '"
190 PJSTR_PRINTF_SPEC "' but endpoint has no geoloc_incoming_call_profile. "
191 "Done.\n", session_name,
192 PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
193 } else {
194 SCOPE_EXIT_RTN_VALUE(0, "%s: Endpoint has no geoloc_incoming_call_profile. "
195 "Done.\n", session_name);
196 }
197 }
198
199 config_profile = ast_geoloc_get_profile(endpoint->geoloc_incoming_call_profile);
200 if (!config_profile) {
201 if (geoloc_hdr) {
202 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has Geolocation header '"
203 PJSTR_PRINTF_SPEC "' but endpoint's geoloc_incoming_call_profile doesn't exist. "
204 "Done.\n", session_name,
205 PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
206 } else {
207 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has no Geolocation header and endpoint has "
208 " an invalid geoloc_incoming_call_profile. Done.\n", session_name);
209 }
210 }
211
212 buf = ast_str_create(1024);
213 if (!buf) {
214 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Unable to allocate buf\n", session_name);
215 }
216
217 if (config_profile->precedence != AST_GEOLOC_PRECED_DISCARD_CONFIG) {
218 config_eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);
219 if (!config_eprofile) {
220 ast_log(LOG_WARNING, "%s: Unable to create config_eprofile from "
221 "profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));
222 }
223
224 if (config_eprofile && config_eprofile->effective_location) {
225 ast_trace(4, "%s: config eprofile '%s' has effective location\n",
226 session_name, config_eprofile->id);
227
228 if (!geoloc_hdr || config_profile->precedence == AST_GEOLOC_PRECED_DISCARD_INCOMING ||
229 config_profile->precedence == AST_GEOLOC_PRECED_PREFER_CONFIG) {
230
231 ast_trace(4, "%s: config eprofile '%s' is being used\n",
232 session_name, config_eprofile->id);
233
234 /*
235 * If we have an effective location and there's no geolocation header,
236 * or the action is either DISCARD_INCOMING or PREFER_CONFIG,
237 * we don't need to even look for a Geolocation header so just add the
238 * config eprofile to the channel and exit.
239 */
240
241 rc = add_eprofile_to_channel(session, config_eprofile, buf);
242 if (rc != 0) {
244 "%s: Couldn't add config eprofile '%s' to datastore. Fail.\n", session_name,
245 config_eprofile->id);
246 }
247
248 SCOPE_EXIT_RTN_VALUE(0, "%s: Added geoloc datastore with eprofile from config. Done.\n",
249 session_name);
250 }
251 } else {
252 /*
253 * If the config eprofile has no effective location, just get rid
254 * of it.
255 */
256 ast_trace(4, "%s: Either config_eprofile didn't exist or it had no effective location\n",
257 session_name);
258
259 ao2_cleanup(config_eprofile);
260 config_eprofile = NULL;
261 if (config_profile->precedence == AST_GEOLOC_PRECED_DISCARD_INCOMING) {
262 SCOPE_EXIT_RTN_VALUE(0, "%s: DISCARD_INCOMING set and no config eprofile. Done.\n",
263 session_name);
264 }
265 }
266 }
267
268 /*
269 * At this point, if we have a config_eprofile, then the action was
270 * PREFER_INCOMING so we're going to keep it as a backup if we can't
271 * get a profile from the incoming message.
272 */
273
274 if (geoloc_hdr && config_profile->precedence != AST_GEOLOC_PRECED_DISCARD_INCOMING) {
275
276 /*
277 * From RFC-6442:
278 * Geolocation-header = "Geolocation" HCOLON locationValue
279 * *( COMMA locationValue )
280 * locationValue = LAQUOT locationURI RAQUOT
281 * *(SEMI geoloc-param)
282 * locationURI = sip-URI / sips-URI / pres-URI
283 * / http-URI / https-URI
284 * / cid-url ; (from RFC 2392)
285 * / absoluteURI ; (from RFC 3261)
286 */
287
288 geoloc_hdr_value = ast_alloca(geoloc_hdr->hvalue.slen + 1);
289 ast_copy_pj_str(geoloc_hdr_value, &geoloc_hdr->hvalue, geoloc_hdr->hvalue.slen + 1);
290
291 /*
292 * We're going to scan the header value for URIs until we find
293 * one that processes successfully or we run out of URIs.
294 * I.E. The first good one wins.
295 */
296 while (geoloc_hdr_value && !incoming_eprofile) {
297 char *pidf_body = NULL;
298 unsigned int pidf_len = 0;
299 struct ast_xml_doc *incoming_doc = NULL;
300 int rc = 0;
301
302 /* We're only going to consider the first URI in the header for now */
303 geoloc_uri = ast_strsep(&geoloc_hdr_value, ',', AST_STRSEP_TRIM);
304 if (ast_strlen_zero(geoloc_uri) || geoloc_uri[0] != '<' || strchr(geoloc_uri, '>') == NULL) {
305 ast_log(LOG_WARNING, "%s: Geolocation header has no or bad URI '%s'. Skipping\n", session_name,
306 S_OR(geoloc_uri, "<empty>"));
307 continue;
308 }
309
310 ast_trace(4, "Processing URI '%s'\n", geoloc_uri);
311
312 if (!ast_begins_with(geoloc_uri, "<cid:")) {
313 ast_trace(4, "Processing URI '%s'\n", geoloc_uri);
314
315 incoming_eprofile = ast_geoloc_eprofile_create_from_uri(geoloc_uri, session_name);
316 if (!incoming_eprofile) {
317 ast_log(LOG_WARNING, "%s: Unable to create effective profile for URI '%s'. Skipping\n",
318 session_name, geoloc_uri);
319 continue;
320 }
321 } else {
322 ast_trace(4, "Processing PIDF-LO '%s'\n", geoloc_uri);
323
324 rc = find_pidf(session_name, rdata, geoloc_uri, &pidf_body, &pidf_len);
325 if (rc != 0 || !pidf_body || pidf_len == 0) {
326 continue;
327 }
328 ast_trace(5, "Processing PIDF-LO "PJSTR_PRINTF_SPEC "\n", (int)pidf_len, pidf_body);
329
330 incoming_doc = ast_xml_read_memory(pidf_body, pidf_len);
331 if (!incoming_doc) {
332 ast_log(LOG_WARNING, "%s: Unable to parse pidf document for URI '%s'\n",
333 session_name, geoloc_uri);
334 continue;
335 }
336
337 incoming_eprofile = ast_geoloc_eprofile_create_from_pidf(incoming_doc, geoloc_uri, session_name);
338 ast_xml_close(incoming_doc);
339
340 if (!incoming_eprofile) {
342 "%s: Couldn't create incoming_eprofile from pidf\n", session_name);
343 continue;
344 }
345 }
346 }
347 }
348
349 if (!incoming_eprofile) {
350 /* Use the config_eprofile as a backup if there was one */
351 incoming_eprofile = config_eprofile;
352 } else {
353 ao2_cleanup(config_eprofile);
354 config_eprofile = NULL;
355 if (geoloc_routing_hdr) {
356 geoloc_routing_hdr_value = ast_alloca(geoloc_routing_hdr->hvalue.slen + 1);
357 ast_copy_pj_str(geoloc_routing_hdr_value, &geoloc_routing_hdr->hvalue,
358 geoloc_routing_hdr->hvalue.slen + 1);
359 incoming_eprofile->allow_routing_use = ast_true(geoloc_routing_hdr_value);
360 }
361 }
362
363 if (incoming_eprofile) {
364 rc = add_eprofile_to_channel(session, incoming_eprofile, buf);
365 if (rc != 0) {
367 "%s: Couldn't add eprofile '%s' to channel. Fail.\n", session_name,
368 incoming_eprofile->id);
369 }
370
371 SCOPE_EXIT_RTN_VALUE(0, "%s: Added eprofile '%s' to channel. Done.\n",
372 session_name, incoming_eprofile->id);
373 }
374
375 SCOPE_EXIT_RTN_VALUE(0, "%s: No eprofiles to add to channel. Done.\n", session_name);
376}
377
378static const char *add_eprofile_to_tdata(struct ast_geoloc_eprofile *eprofile, struct ast_channel *channel,
379 struct pjsip_tx_data *tdata, struct ast_str **buf, const char *session_name)
380{
381 static const pj_str_t from_name = { "From", 4};
382 static const pj_str_t cid_name = { "Content-ID", 10 };
383
384 pjsip_sip_uri *sip_uri;
385 pjsip_generic_string_hdr *cid;
386 pj_str_t cid_value;
387 pjsip_from_hdr *from = pjsip_msg_find_hdr_by_name(tdata->msg, &from_name, NULL);
388 pjsip_sdp_info *tdata_sdp_info;
389 pjsip_msg_body *multipart_body = NULL;
390 pjsip_multipart_part *pidf_part;
391 pj_str_t pidf_body_text;
392 char id[6];
393 size_t alloc_size;
394 RAII_VAR(char *, base_cid, NULL, ast_free);
395 const char *final_doc;
396 int rc = 0;
397 SCOPE_ENTER(3, "%s\n", session_name);
398
399 /*
400 * ast_geoloc_eprofiles_to_pidf() takes the datastore with all of the eprofiles
401 * in it, skips over the ones not needing PIDF processing and combines the
402 * rest into one document.
403 */
404 final_doc = ast_geoloc_eprofile_to_pidf(eprofile, channel, buf, session_name);
405 ast_trace(5, "Final pidf: \n%s\n", final_doc);
406
407 if (!final_doc) {
408 SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create pidf document from"
409 " eprofile '%s'\n\n", session_name, eprofile->id);
410 }
411
412 /*
413 * There _should_ be an SDP already attached to the tdata at this point
414 * but maybe not. If we can find an existing one, we'll convert the tdata
415 * body into a multipart body and add the SDP as the first part. Then we'll
416 * create another part to hold the PIDF.
417 *
418 * If we don't find one, we're going to create an empty multipart body
419 * and add the PIDF part to it.
420 *
421 * Technically, if we only have the PIDF, we don't need a multipart
422 * body to hold it but that means we'd have to add the Content-ID header
423 * to the main SIP message. Since it's unlikely, it's just better to
424 * add the multipart body and leave the rest of the processing unchanged.
425 */
426 tdata_sdp_info = pjsip_tdata_get_sdp_info(tdata);
427 if (tdata_sdp_info->sdp) {
428 ast_trace(4, "body: %p %u\n", tdata_sdp_info->sdp, (unsigned)tdata_sdp_info->sdp_err);
429
430 rc = pjsip_create_multipart_sdp_body(tdata->pool, tdata_sdp_info->sdp, &multipart_body);
431 if (rc != PJ_SUCCESS) {
432 SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create sdp multipart body\n",
433 session_name);
434 }
435 } else {
436 multipart_body = pjsip_multipart_create(tdata->pool, &pjsip_media_type_multipart_mixed, NULL);
437 }
438
439 pidf_part = pjsip_multipart_create_part(tdata->pool);
440 pj_cstr(&pidf_body_text, final_doc);
441 pidf_part->body = pjsip_msg_body_create(tdata->pool, &pjsip_media_type_application_pidf_xml.type,
442 &pjsip_media_type_application_pidf_xml.subtype, &pidf_body_text);
443
444 pjsip_multipart_add_part(tdata->pool, multipart_body, pidf_part);
445
446 sip_uri = (pjsip_sip_uri *)pjsip_uri_get_uri(from->uri);
447 alloc_size = sizeof(id) + pj_strlen(&sip_uri->host) + 2;
448 base_cid = ast_malloc(alloc_size);
449 sprintf(base_cid, "%s@%.*s",
450 ast_generate_random_string(id, sizeof(id)),
451 (int) pj_strlen(&sip_uri->host), pj_strbuf(&sip_uri->host));
452
453 ast_str_set(buf, 0, "cid:%s", base_cid);
454 ast_trace(4, "cid: '%s' uri: '%s'\n", base_cid, ast_str_buffer(*buf));
455
456 cid_value.ptr = pj_pool_alloc(tdata->pool, alloc_size);
457 cid_value.slen = sprintf(cid_value.ptr, "<%s>", base_cid);
458
459 cid = pjsip_generic_string_hdr_create(tdata->pool, &cid_name, &cid_value);
460
461 pj_list_insert_after(&pidf_part->hdr, cid);
462
463 tdata->msg->body = multipart_body;
464
465 SCOPE_EXIT_RTN_VALUE(ast_str_buffer(*buf), "%s: PIDF-LO added with cid '%s'\n", session_name, base_cid);
466}
467
468static void handle_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
469{
470 const char *session_name = ast_sip_session_get_name(session);
471 struct ast_sip_endpoint *endpoint = session->endpoint;
472 struct ast_channel *channel = session->channel;
473 RAII_VAR(struct ast_geoloc_profile *, config_profile, NULL, ao2_cleanup);
474 RAII_VAR(struct ast_geoloc_eprofile *, config_eprofile, NULL, ao2_cleanup);
475 RAII_VAR(struct ast_geoloc_eprofile *, incoming_eprofile, NULL, ao2_cleanup);
476 struct ast_geoloc_eprofile *final_eprofile = NULL;
477 RAII_VAR(struct ast_str *, buf, NULL, ast_free);
478 struct ast_datastore *ds = NULL; /* The channel cleans up ds */
479 pjsip_msg_body *orig_body = NULL;
480 pjsip_generic_string_hdr *geoloc_hdr = NULL;
481 int eprofile_count = 0;
482 int rc = 0;
483 const char *uri;
484 SCOPE_ENTER(3, "%s\n", session_name);
485
486 if (!endpoint) {
487 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Session has no endpoint. Skipping.\n",
488 session_name);
489 }
490
491 if (!channel) {
492 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Session has no channel. Skipping.\n",
493 session_name);
494 }
495
496 if (ast_strlen_zero(endpoint->geoloc_outgoing_call_profile)) {
497 SCOPE_EXIT_RTN("%s: Endpoint has no geoloc_outgoing_call_profile. Skipping.\n",
498 session_name);
499 }
500
501 config_profile = ast_geoloc_get_profile(endpoint->geoloc_outgoing_call_profile);
502 if (!config_profile) {
503 SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Endpoint's geoloc_outgoing_call_profile doesn't exist. "
504 "Geolocation info discarded.\n", session_name);
505 }
506
507 config_eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);
508 if (!config_eprofile) {
509 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Unable to create eprofile from "
510 "profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));
511 }
512
513 if (!config_eprofile->effective_location) {
514 /*
515 * If there's no effective location on the eprofile
516 * we don't need to keep it.
517 */
518 ast_trace(4, "%s: There was no effective location for config profile '%s'\n",
519 session_name, ast_sorcery_object_get_id(config_profile));
520 ao2_ref(config_eprofile, -1);
521 config_eprofile = NULL;
522 }
523
524 ds = ast_geoloc_datastore_find(channel);
525 if (!ds) {
526 ast_trace(4, "%s: There was no geoloc datastore on the channel\n", session_name);
527 } else {
528 eprofile_count = ast_geoloc_datastore_size(ds);
529 ast_trace(4, "%s: There are %d geoloc profiles on this channel\n", session_name,
530 eprofile_count);
531 /*
532 * There'd better be a max of 1 at this time. In the future
533 * we may allow more than 1.
534 */
535 incoming_eprofile = ast_geoloc_datastore_get_eprofile(ds, 0);
536 }
537
538 ast_trace(4, "%s: Profile precedence: %s\n\n", session_name,
539 ast_geoloc_precedence_to_name(config_profile->precedence));
540
541 switch (config_profile->precedence) {
543 final_eprofile = config_eprofile;
544 ao2_cleanup(incoming_eprofile);
545 incoming_eprofile = NULL;
546 break;
548 if (incoming_eprofile) {
549 final_eprofile = incoming_eprofile;
550 ao2_cleanup(config_eprofile);
551 config_eprofile = NULL;
552 } else {
553 final_eprofile = config_eprofile;
554 }
555 break;
557 final_eprofile = incoming_eprofile;
558 ao2_cleanup(config_eprofile);
559 config_eprofile = NULL;
560 break;
562 if (config_eprofile) {
563 final_eprofile = config_eprofile;
564 ao2_cleanup(incoming_eprofile);
565 incoming_eprofile = NULL;
566 } else {
567 final_eprofile = incoming_eprofile;
568 }
569 break;
570 }
571
572 if (!final_eprofile) {
573 SCOPE_EXIT_RTN("%s: No eprofiles to send. Done.\n",
574 session_name);
575 }
576
577 if (!final_eprofile->effective_location) {
579 }
580
581 buf = ast_str_create(1024);
582 if (!buf) {
583 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Unable to allocate buf\n", session_name);
584 }
585
586 if (final_eprofile->format == AST_GEOLOC_FORMAT_URI) {
587 uri = ast_geoloc_eprofile_to_uri(final_eprofile, channel, &buf, session_name);
588 if (!uri) {
589 SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Unable to create URI from eprofile '%s'\n",
590 session_name, final_eprofile->id);
591 }
592 } else {
593 orig_body = tdata->msg->body;
594 uri = add_eprofile_to_tdata(final_eprofile, channel, tdata, &buf, session_name);
595 if (!uri) {
596 tdata->msg->body = orig_body;
597 SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Unable to add eprofile '%s' to tdata\n",
598 session_name, final_eprofile->id);
599 }
600 }
601
604 ast_str_set(&buf, 0, "<%s>", uri);
606
607 ast_trace(4, "%s: Using URI '%s'\n", session_name, uri);
608
609 /* It's almost impossible for add header to fail but you never know */
610 geoloc_hdr = ast_sip_add_header2(tdata, "Geolocation", uri);
611 if (geoloc_hdr == NULL) {
612 if (orig_body) {
613 tdata->msg->body = orig_body;
614 }
615 SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Unable to add Geolocation header\n", session_name);
616 }
617 rc = ast_sip_add_header(tdata, "Geolocation-Routing", final_eprofile->allow_routing_use ? "yes" : "no");
618 if (rc != 0) {
619 if (orig_body) {
620 tdata->msg->body = orig_body;
621 }
622 pj_list_erase(geoloc_hdr);
623 SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Unable to add Geolocation-Routing header\n", session_name);
624 }
625 SCOPE_EXIT_RTN("%s: Geolocation: %s\n", session_name, uri);
626}
627
629 .method = "INVITE",
631 .incoming_request = handle_incoming_request,
632 .outgoing_request = handle_outgoing_request,
633};
634
635static int reload_module(void)
636{
637 return 0;
638}
639
640static int unload_module(void)
641{
642 int res = 0;
644
645 return res;
646}
647
648static int load_module(void)
649{
650 int res = 0;
651 GEOLOCATION_HDR = pj_str("Geolocation");
652 GEOLOCATION_ROUTING_HDR = pj_str("Geolocation-Routing");
653
655
656 return res;
657}
658
659AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "res_pjsip_geolocation Module for Asterisk",
660 .support_level = AST_MODULE_SUPPORT_CORE,
661 .load = load_module,
662 .unload = unload_module,
664 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 1,
665 .requires = "res_geolocation,res_pjsip,res_pjsip_session,chan_pjsip",
enum queue_result id
Definition: app_queue.c:1638
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_mansession session
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2404
#define ast_channel_lock(chan)
Definition: channel.h:2968
#define ast_channel_unlock(chan)
Definition: channel.h:2969
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define SCOPE_EXIT_RTN(...)
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_EXIT_LOG_RTN_VALUE(__value, __log_level,...)
#define SCOPE_ENTER(level,...)
#define SCOPE_EXIT_LOG_RTN(__log_level,...)
#define ast_trace(level,...)
struct ast_str * ast_variable_list_join(const struct ast_variable *head, const char *item_separator, const char *name_value_separator, const char *quote_char, struct ast_str **str)
Join an ast_variable list with specified separators and quoted values.
Definition: main/config.c:701
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
static int reload(void)
@ AST_GEOLOC_PRECED_DISCARD_CONFIG
@ AST_GEOLOC_PRECED_PREFER_CONFIG
@ AST_GEOLOC_PRECED_DISCARD_INCOMING
@ AST_GEOLOC_PRECED_PREFER_INCOMING
int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_eprofile *eprofile)
Refresh the effective profile with any changed info.
struct ast_geoloc_eprofile * ast_geoloc_datastore_get_eprofile(struct ast_datastore *ds, int ix)
Retrieve a specific eprofile from a datastore by index.
const char * ast_geoloc_eprofile_to_uri(struct ast_geoloc_eprofile *eprofile, struct ast_channel *chan, struct ast_str **buf, const char *ref_string)
Convert a URI eprofile to a URI string.
struct ast_geoloc_eprofile * ast_geoloc_eprofile_create_from_pidf(struct ast_xml_doc *pidf_xmldoc, const char *geoloc_uri, const char *reference_string)
Allocate a new effective profile from an XML PIDF-LO document.
struct ast_geoloc_eprofile * ast_geoloc_eprofile_create_from_uri(const char *uri, const char *reference_string)
Allocate a new effective profile from a URI.
struct ast_geoloc_profile * ast_geoloc_get_profile(const char *id)
Retrieve a geolocation profile by id.
struct ast_datastore * ast_geoloc_datastore_create(const char *id)
Create an empty geoloc datastore.
@ AST_GEOLOC_FORMAT_URI
int ast_geoloc_datastore_set_inheritance(struct ast_datastore *ds, int inherit)
Sets the inheritance flag on the datastore.
int ast_geoloc_datastore_add_eprofile(struct ast_datastore *ds, struct ast_geoloc_eprofile *eprofile)
Add an eprofile to a datastore.
const char * ast_geoloc_eprofile_to_pidf(struct ast_geoloc_eprofile *eprofile, struct ast_channel *chan, struct ast_str **buf, const char *ref_string)
Convert a single eprofile to a PIDF-LO document.
int ast_geoloc_datastore_size(struct ast_datastore *ds)
Retrieves the number of eprofiles in the datastore.
struct ast_datastore * ast_geoloc_datastore_find(struct ast_channel *chan)
Retrieves the geoloc datastore from a channel, if any.
struct ast_geoloc_eprofile * ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile)
Allocate a new effective profile from an existing profile.
pjsip_generic_string_hdr * ast_sip_add_header2(pjsip_tx_data *tdata, const char *name, const char *value)
Add a header to an outbound SIP message, returning a pointer to the header.
Definition: res_pjsip.c:2023
pjsip_media_type pjsip_media_type_application_pidf_xml
Definition: res_pjsip.c:3911
@ AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL
Definition: res_pjsip.h:3185
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition: res_pjsip.c:2201
int ast_sip_are_media_types_equal(pjsip_media_type *a, pjsip_media_type *b)
Compare pjsip media types.
Definition: res_pjsip.c:2219
pjsip_media_type pjsip_media_type_multipart_mixed
Definition: res_pjsip.c:3918
int ast_sip_add_header(pjsip_tx_data *tdata, const char *name, const char *value)
Add a header to an outbound SIP message.
Definition: res_pjsip.c:2008
#define PJSTR_PRINTF_VAR(_v)
Definition: res_pjsip.h:72
#define PJSTR_PRINTF_SPEC
Definition: res_pjsip.h:71
static pj_str_t GEOLOCATION_ROUTING_HDR
static void handle_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
static int add_eprofile_to_channel(struct ast_sip_session *session, struct ast_geoloc_eprofile *eprofile, struct ast_str *buf)
static const char * add_eprofile_to_tdata(struct ast_geoloc_eprofile *eprofile, struct ast_channel *channel, struct pjsip_tx_data *tdata, struct ast_str **buf, const char *session_name)
static int handle_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
static pj_str_t GEOLOCATION_HDR
static int reload_module(void)
static int find_pidf(const char *session_name, struct pjsip_rx_data *rdata, char *geoloc_uri, char **pidf_body, unsigned int *pidf_len)
static struct ast_sip_session_supplement geolocation_supplement
static int load_module(void)
static int unload_module(void)
#define ast_sip_session_register_supplement(supplement)
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.
Definition: pjsip_session.c:63
const char * ast_sip_session_get_name(const struct ast_sip_session *session)
Get the channel or endpoint name associated with the session.
#define NULL
Definition: resample.c:96
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
@ AST_STRSEP_TRIM
Definition: strings.h:256
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
char * ast_generate_random_string(char *buf, size_t size)
Create a pseudo-random string of a fixed length.
Definition: strings.c:226
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: utils.c:1835
Main Channel structure associated with a channel.
Structure for a data store object.
Definition: datastore.h:64
struct ast_variable * effective_location
enum ast_geoloc_format format
const ast_string_field id
An entity with which Asterisk communicates.
Definition: res_pjsip.h:958
A supplement to SIP message processing.
A structure describing a SIP session.
Support for dynamic strings.
Definition: strings.h:623
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
Asterisk XML abstraction layer.
void ast_xml_close(struct ast_xml_doc *doc)
Close an already open document and free the used structure.
Definition: xml.c:211
struct ast_xml_doc * ast_xml_read_memory(char *buffer, size_t size)
Open an XML document that resides in memory.
Definition: xml.c:192