32 #include "asterisk/res_geolocation.h"
35 #include "asterisk/res_pjsip.h"
36 #include "asterisk/res_pjsip_session.h"
38 static pj_str_t GEOLOCATION_HDR;
39 static pj_str_t GEOLOCATION_ROUTING_HDR;
41 static int find_pidf(
const char *session_name,
struct pjsip_rx_data *rdata,
char *geoloc_uri,
42 char **pidf_body,
unsigned int *pidf_len)
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);
56 if (local_uri[0] ==
'<') {
59 ra = strchr(local_uri,
'>');
70 if (ast_sip_are_media_types_equal(&rdata->msg_info.ctype->media,
71 &pjsip_media_type_application_pidf_xml)) {
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,
75 &pjsip_media_type_multipart_mixed)) {
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);
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);
86 *pidf_body = mp->body->data;
87 *pidf_len = mp->body->len;
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);
101 const char *session_name = (session ? ast_sip_session_get_name(session) :
"NULL_SESSION");
104 SCOPE_ENTER(4,
"%s\n", session_name);
106 ds = ast_geoloc_datastore_create(session_name);
108 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING,
109 "%s: Couldn't allocate a geoloc datastore\n", session_name);
116 ast_geoloc_datastore_set_inheritance(ds, 1);
118 rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);
121 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING,
122 "%s: Couldn't add eprofile '%s' to datastore\n", session_name,
126 ast_channel_lock(session->
channel);
128 ast_channel_unlock(session->
channel);
130 SCOPE_EXIT_RTN_VALUE(0,
"%s: eprofile: '%s' EffectiveLoc: %s\n",
135 static int handle_incoming_request(
struct ast_sip_session *session,
struct pjsip_rx_data *rdata)
137 const char *session_name = (session ? ast_sip_session_get_name(session) :
"NULL_SESSION");
144 char *geoloc_hdr_value = NULL;
145 char *geoloc_routing_hdr_value = NULL;
146 char *geoloc_uri = NULL;
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);
154 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
"%s: session is NULL!!!. Skipping.\n",
158 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
"%s: Session has no endpoint. Skipping.\n",
163 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
"%s: Session has no channel. Skipping.\n",
168 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
"%s: Session has no rdata. Skipping.\n",
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,
178 &GEOLOCATION_ROUTING_HDR, NULL);
181 ast_trace(4,
"%s: Message has no Geolocation header\n", session_name);
183 ast_trace(4,
"%s: Geolocation: " PJSTR_PRINTF_SPEC
"\n", session_name,
184 PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
187 if (ast_strlen_zero(endpoint->geoloc_incoming_call_profile)) {
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));
194 SCOPE_EXIT_RTN_VALUE(0,
"%s: Endpoint has no geoloc_incoming_call_profile. "
195 "Done.\n", session_name);
199 config_profile = ast_geoloc_get_profile(endpoint->geoloc_incoming_call_profile);
200 if (!config_profile) {
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));
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);
214 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
"%s: Unable to allocate buf\n", session_name);
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 "
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);
228 if (!geoloc_hdr || config_profile->precedence == AST_GEOLOC_PRECED_DISCARD_INCOMING ||
229 config_profile->precedence == AST_GEOLOC_PRECED_PREFER_CONFIG) {
231 ast_trace(4,
"%s: config eprofile '%s' is being used\n",
232 session_name, config_eprofile->id);
241 rc = add_eprofile_to_channel(session, config_eprofile, buf);
243 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
244 "%s: Couldn't add config eprofile '%s' to datastore. Fail.\n", session_name,
245 config_eprofile->id);
248 SCOPE_EXIT_RTN_VALUE(0,
"%s: Added geoloc datastore with eprofile from config. Done.\n",
256 ast_trace(4,
"%s: Either config_eprofile didn't exist or it had no effective location\n",
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",
274 if (geoloc_hdr && config_profile->precedence != AST_GEOLOC_PRECED_DISCARD_INCOMING) {
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);
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;
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>"));
310 ast_trace(4,
"Processing URI '%s'\n", geoloc_uri);
313 ast_trace(4,
"Processing URI '%s'\n", geoloc_uri);
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);
322 ast_trace(4,
"Processing PIDF-LO '%s'\n", geoloc_uri);
324 rc = find_pidf(session_name, rdata, geoloc_uri, &pidf_body, &pidf_len);
325 if (rc != 0 || !pidf_body || pidf_len == 0) {
328 ast_trace(5,
"Processing PIDF-LO "PJSTR_PRINTF_SPEC
"\n", (
int)pidf_len, pidf_body);
332 ast_log(LOG_WARNING,
"%s: Unable to parse pidf document for URI '%s'\n",
333 session_name, geoloc_uri);
337 incoming_eprofile = ast_geoloc_eprofile_create_from_pidf(incoming_doc, geoloc_uri, session_name);
340 if (!incoming_eprofile) {
342 "%s: Couldn't create incoming_eprofile from pidf\n", session_name);
349 if (!incoming_eprofile) {
351 incoming_eprofile = config_eprofile;
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);
363 if (incoming_eprofile) {
364 rc = add_eprofile_to_channel(session, incoming_eprofile, buf);
366 SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
367 "%s: Couldn't add eprofile '%s' to channel. Fail.\n", session_name,
368 incoming_eprofile->id);
371 SCOPE_EXIT_RTN_VALUE(0,
"%s: Added eprofile '%s' to channel. Done.\n",
372 session_name, incoming_eprofile->id);
375 SCOPE_EXIT_RTN_VALUE(0,
"%s: No eprofiles to add to channel. Done.\n", session_name);
379 struct pjsip_tx_data *tdata,
struct ast_str **buf,
const char *session_name)
381 static const pj_str_t from_name = {
"From", 4};
382 static const pj_str_t cid_name = {
"Content-ID", 10 };
384 pjsip_sip_uri *sip_uri;
385 pjsip_generic_string_hdr *cid;
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;
394 RAII_VAR(
char *, base_cid, NULL, ast_free);
395 const char *final_doc;
397 SCOPE_ENTER(3,
"%s\n", session_name);
404 final_doc = ast_geoloc_eprofile_to_pidf(eprofile, channel, buf, session_name);
405 ast_trace(5,
"Final pidf: \n%s\n", 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);
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);
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",
436 multipart_body = pjsip_multipart_create(tdata->pool, &pjsip_media_type_multipart_mixed, NULL);
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);
444 pjsip_multipart_add_part(tdata->pool, multipart_body, pidf_part);
446 sip_uri = (pjsip_sip_uri *)pjsip_uri_get_uri(from->uri);
447 alloc_size =
sizeof(id) + pj_strlen(&sip_uri->host) + 2;
449 sprintf(base_cid,
"%s@%.*s",
451 (
int) pj_strlen(&sip_uri->host), pj_strbuf(&sip_uri->host));
454 ast_trace(4,
"cid: '%s' uri: '%s'\n", base_cid,
ast_str_buffer(*buf));
456 cid_value.ptr = pj_pool_alloc(tdata->pool, alloc_size);
457 cid_value.slen = sprintf(cid_value.ptr,
"<%s>", base_cid);
459 cid = pjsip_generic_string_hdr_create(tdata->pool, &cid_name, &cid_value);
461 pj_list_insert_after(&pidf_part->hdr, cid);
463 tdata->msg->body = multipart_body;
465 SCOPE_EXIT_RTN_VALUE(
ast_str_buffer(*buf),
"%s: PIDF-LO added with cid '%s'\n", session_name, base_cid);
468 static void handle_outgoing_request(
struct ast_sip_session *session,
struct pjsip_tx_data *tdata)
470 const char *session_name = ast_sip_session_get_name(session);
479 pjsip_msg_body *orig_body = NULL;
480 pjsip_generic_string_hdr *geoloc_hdr = NULL;
481 int eprofile_count = 0;
484 SCOPE_ENTER(3,
"%s\n", session_name);
487 SCOPE_EXIT_LOG_RTN(LOG_WARNING,
"%s: Session has no endpoint. Skipping.\n",
492 SCOPE_EXIT_LOG_RTN(LOG_WARNING,
"%s: Session has no channel. Skipping.\n",
496 if (ast_strlen_zero(endpoint->geoloc_outgoing_call_profile)) {
497 SCOPE_EXIT_RTN(
"%s: Endpoint has no geoloc_outgoing_call_profile. Skipping.\n",
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);
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 "
513 if (!config_eprofile->effective_location) {
518 ast_trace(4,
"%s: There was no effective location for config profile '%s'\n",
521 config_eprofile = NULL;
524 ds = ast_geoloc_datastore_find(channel);
526 ast_trace(4,
"%s: There was no geoloc datastore on the channel\n", session_name);
528 eprofile_count = ast_geoloc_datastore_size(ds);
529 ast_trace(4,
"%s: There are %d geoloc profiles on this channel\n", session_name,
535 incoming_eprofile = ast_geoloc_datastore_get_eprofile(ds, 0);
538 ast_trace(4,
"%s: Profile precedence: %s\n\n", session_name,
539 ast_geoloc_precedence_to_name(config_profile->precedence));
541 switch (config_profile->precedence) {
542 case AST_GEOLOC_PRECED_DISCARD_INCOMING:
543 final_eprofile = config_eprofile;
544 ao2_cleanup(incoming_eprofile);
545 incoming_eprofile = NULL;
547 case AST_GEOLOC_PRECED_PREFER_INCOMING:
548 if (incoming_eprofile) {
549 final_eprofile = incoming_eprofile;
550 ao2_cleanup(config_eprofile);
551 config_eprofile = NULL;
553 final_eprofile = config_eprofile;
556 case AST_GEOLOC_PRECED_DISCARD_CONFIG:
557 final_eprofile = incoming_eprofile;
558 ao2_cleanup(config_eprofile);
559 config_eprofile = NULL;
561 case AST_GEOLOC_PRECED_PREFER_CONFIG:
562 if (config_eprofile) {
563 final_eprofile = config_eprofile;
564 ao2_cleanup(incoming_eprofile);
565 incoming_eprofile = NULL;
567 final_eprofile = incoming_eprofile;
572 if (!final_eprofile) {
573 SCOPE_EXIT_RTN(
"%s: No eprofiles to send. Done.\n",
577 if (!final_eprofile->effective_location) {
578 ast_geoloc_eprofile_refresh_location(final_eprofile);
583 SCOPE_EXIT_LOG_RTN(LOG_WARNING,
"%s: Unable to allocate buf\n", session_name);
586 if (final_eprofile->format == AST_GEOLOC_FORMAT_URI) {
587 uri = ast_geoloc_eprofile_to_uri(final_eprofile, channel, &buf, session_name);
589 SCOPE_EXIT_LOG_RTN(LOG_ERROR,
"%s: Unable to create URI from eprofile '%s'\n",
590 session_name, final_eprofile->id);
593 orig_body = tdata->msg->body;
594 uri = add_eprofile_to_tdata(final_eprofile, channel, tdata, &buf, session_name);
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);
607 ast_trace(4,
"%s: Using URI '%s'\n", session_name, uri);
610 geoloc_hdr = ast_sip_add_header2(tdata,
"Geolocation", uri);
611 if (geoloc_hdr == NULL) {
613 tdata->msg->body = orig_body;
615 SCOPE_EXIT_LOG_RTN(LOG_ERROR,
"%s: Unable to add Geolocation header\n", session_name);
617 rc = ast_sip_add_header(tdata,
"Geolocation-Routing", final_eprofile->allow_routing_use ?
"yes" :
"no");
620 tdata->msg->body = orig_body;
622 pj_list_erase(geoloc_hdr);
623 SCOPE_EXIT_LOG_RTN(LOG_ERROR,
"%s: Unable to add Geolocation-Routing header\n", session_name);
625 SCOPE_EXIT_RTN(
"%s: Geolocation: %s\n", session_name, uri);
630 .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 10,
631 .incoming_request = handle_incoming_request,
632 .outgoing_request = handle_outgoing_request,
635 static int reload_module(
void)
640 static int unload_module(
void)
643 ast_sip_session_unregister_supplement(&geolocation_supplement);
648 static int load_module(
void)
651 GEOLOCATION_HDR = pj_str(
"Geolocation");
652 GEOLOCATION_ROUTING_HDR = pj_str(
"Geolocation-Routing");
654 ast_sip_session_register_supplement(&geolocation_supplement);
659 AST_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,
662 .unload = unload_module,
663 .reload = reload_module,
665 .requires =
"res_geolocation,res_pjsip,res_pjsip_session,chan_pjsip",
Main Channel structure associated with a channel.
struct ast_sip_endpoint * endpoint
Asterisk main include file. File version handling, generic pbx functions.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Structure for a data store object.
A structure describing a SIP session.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
struct ast_channel * channel
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
#define ast_malloc(len)
A wrapper for malloc()
An entity with which Asterisk communicates.
Asterisk XML abstraction layer.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Support for dynamic strings.
char * ast_generate_random_string(char *buf, size_t size)
Create a pseudo-random string of a fixed length.
void ast_xml_close(struct ast_xml_doc *doc)
Close an already open document and free the used structure.
A supplement to SIP message processing.
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
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.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
struct ast_xml_doc * ast_xml_read_memory(char *buffer, size_t size)
Open an XML document that resides in memory.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.