Asterisk - The Open Source Telephony Project GIT-master-abe0018
Functions | Variables
res_pjsip_geolocation.c File Reference
#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/xml.h"
#include "asterisk/res_geolocation.h"
#include <pjsip_ua.h>
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
Include dependency graph for res_pjsip_geolocation.c:

Go to the source code of this file.

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
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)
 
struct ast_moduleAST_MODULE_SELF_SYM (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 int handle_incoming_request (struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 
static void handle_outgoing_request (struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 
static int load_module (void)
 
static int reload_module (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "res_pjsip_geolocation Module for Asterisk" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND - 1, .requires = "res_geolocation,res_pjsip,res_pjsip_session,chan_pjsip", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static pj_str_t GEOLOCATION_HDR
 
static pj_str_t GEOLOCATION_ROUTING_HDR
 
static struct ast_sip_session_supplement geolocation_supplement
 

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 666 of file res_pjsip_geolocation.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 666 of file res_pjsip_geolocation.c.

◆ add_eprofile_to_channel()

static int add_eprofile_to_channel ( struct ast_sip_session session,
struct ast_geoloc_eprofile eprofile,
struct ast_str buf 
)
static

Definition at line 98 of file res_pjsip_geolocation.c.

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}
static struct ast_mansession session
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
#define ast_channel_lock(chan)
Definition: channel.h:2922
#define ast_channel_unlock(chan)
Definition: channel.h:2923
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_VALUE(__return_value,...)
#define SCOPE_EXIT_LOG_RTN_VALUE(__value, __log_level,...)
#define SCOPE_ENTER(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:700
#define LOG_WARNING
struct ast_datastore * ast_geoloc_datastore_create(const char *id)
Create an empty geoloc datastore.
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_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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
Structure for a data store object.
Definition: datastore.h:64
struct ast_variable * effective_location
const ast_string_field id

References ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_datastore_free(), ast_geoloc_datastore_add_eprofile(), ast_geoloc_datastore_create(), ast_geoloc_datastore_set_inheritance(), ast_sip_session_get_name(), ast_str_buffer(), ast_variable_list_join(), buf, ast_geoloc_eprofile::effective_location, ast_geoloc_eprofile::id, LOG_WARNING, NULL, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, and session.

Referenced by handle_incoming_request().

◆ add_eprofile_to_tdata()

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

Definition at line 378 of file res_pjsip_geolocation.c.

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}
enum queue_result id
Definition: app_queue.c:1638
#define ast_free(a)
Definition: astmm.h:180
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_trace(level,...)
#define LOG_ERROR
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.
pjsip_media_type pjsip_media_type_application_pidf_xml
Definition: res_pjsip.c:3911
pjsip_media_type pjsip_media_type_multipart_mixed
Definition: res_pjsip.c:3918
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
char * ast_generate_random_string(char *buf, size_t size)
Create a pseudo-random string of a fixed length.
Definition: strings.c:226
#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

References ast_free, ast_generate_random_string(), ast_geoloc_eprofile_to_pidf(), ast_malloc, ast_str_buffer(), ast_str_set(), ast_trace, buf, id, ast_geoloc_eprofile::id, LOG_ERROR, NULL, pjsip_media_type_application_pidf_xml, pjsip_media_type_multipart_mixed, RAII_VAR, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, and SCOPE_EXIT_RTN_VALUE.

Referenced by handle_outgoing_request().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 666 of file res_pjsip_geolocation.c.

◆ find_pidf()

static int find_pidf ( const char *  session_name,
struct pjsip_rx_data *  rdata,
char *  geoloc_uri,
char **  pidf_body,
unsigned int *  pidf_len 
)
static

Definition at line 41 of file res_pjsip_geolocation.c.

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}
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
int ast_sip_are_media_types_equal(pjsip_media_type *a, pjsip_media_type *b)
Compare pjsip media types.
Definition: res_pjsip.c:2219

References ast_log, ast_sip_are_media_types_equal(), ast_strdupa, LOG_WARNING, NULL, pjsip_media_type_application_pidf_xml, and pjsip_media_type_multipart_mixed.

Referenced by handle_incoming_request().

◆ handle_incoming_request()

static int handle_incoming_request ( struct ast_sip_session session,
struct pjsip_rx_data *  rdata 
)
static

Definition at line 135 of file res_pjsip_geolocation.c.

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}
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define LOG_NOTICE
@ AST_GEOLOC_PRECED_DISCARD_CONFIG
@ AST_GEOLOC_PRECED_PREFER_CONFIG
@ AST_GEOLOC_PRECED_DISCARD_INCOMING
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_geoloc_eprofile * ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile)
Allocate a new effective profile from an existing profile.
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
#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 int add_eprofile_to_channel(struct ast_sip_session *session, struct ast_geoloc_eprofile *eprofile, struct ast_str *buf)
static pj_str_t GEOLOCATION_HDR
static int find_pidf(const char *session_name, struct pjsip_rx_data *rdata, char *geoloc_uri, char **pidf_body, unsigned int *pidf_len)
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
#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
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
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_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.
An entity with which Asterisk communicates.
Definition: res_pjsip.h:963
Support for dynamic strings.
Definition: strings.h:623
void ast_xml_close(struct ast_xml_doc *doc)
Close an already open document and free the used structure.
Definition: xml.c:213
struct ast_xml_doc * ast_xml_read_memory(char *buffer, size_t size)
Open an XML document that resides in memory.
Definition: xml.c:194

References add_eprofile_to_channel(), ao2_cleanup, ast_alloca, ast_begins_with(), ast_copy_pj_str(), ast_datastore_free(), ast_free, ast_geoloc_eprofile_create_from_pidf(), ast_geoloc_eprofile_create_from_profile(), ast_geoloc_eprofile_create_from_uri(), ast_geoloc_get_profile(), AST_GEOLOC_PRECED_DISCARD_CONFIG, AST_GEOLOC_PRECED_DISCARD_INCOMING, AST_GEOLOC_PRECED_PREFER_CONFIG, ast_log, ast_sip_session_get_name(), ast_sorcery_object_get_id(), ast_str_create, ast_strlen_zero(), ast_strsep(), AST_STRSEP_TRIM, ast_trace, ast_true(), ast_xml_close(), ast_xml_read_memory(), buf, find_pidf(), GEOLOCATION_HDR, GEOLOCATION_ROUTING_HDR, LOG_NOTICE, LOG_WARNING, NULL, PJSTR_PRINTF_SPEC, PJSTR_PRINTF_VAR, RAII_VAR, S_OR, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, and session.

◆ handle_outgoing_request()

static void handle_outgoing_request ( struct ast_sip_session session,
struct pjsip_tx_data *  tdata 
)
static

Definition at line 468 of file res_pjsip_geolocation.c.

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}
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define SCOPE_EXIT_RTN(...)
#define SCOPE_EXIT_LOG_RTN(__log_level,...)
@ 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.
@ AST_GEOLOC_FORMAT_URI
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.
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
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
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)
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
enum ast_geoloc_format format

References add_eprofile_to_tdata(), ast_geoloc_eprofile::allow_routing_use, ao2_cleanup, ao2_ref, ast_free, ast_geoloc_datastore_find(), ast_geoloc_datastore_get_eprofile(), ast_geoloc_datastore_size(), ast_geoloc_eprofile_create_from_profile(), ast_geoloc_eprofile_refresh_location(), ast_geoloc_eprofile_to_uri(), AST_GEOLOC_FORMAT_URI, ast_geoloc_get_profile(), AST_GEOLOC_PRECED_DISCARD_CONFIG, AST_GEOLOC_PRECED_DISCARD_INCOMING, AST_GEOLOC_PRECED_PREFER_CONFIG, AST_GEOLOC_PRECED_PREFER_INCOMING, ast_sip_add_header(), ast_sip_add_header2(), ast_sip_session_get_name(), ast_sorcery_object_get_id(), ast_str_buffer(), ast_str_create, ast_str_reset(), ast_str_set(), ast_strdupa, ast_strlen_zero(), ast_trace, buf, ast_geoloc_eprofile::effective_location, ast_geoloc_eprofile::format, ast_geoloc_eprofile::id, LOG_ERROR, LOG_WARNING, NULL, RAII_VAR, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN, SCOPE_EXIT_RTN, and session.

◆ load_module()

static int load_module ( void  )
static

Definition at line 648 of file res_pjsip_geolocation.c.

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}
static struct ast_sip_session_supplement geolocation_supplement
#define ast_sip_session_register_supplement(supplement)

References ast_sip_session_register_supplement, GEOLOCATION_HDR, GEOLOCATION_ROUTING_HDR, and geolocation_supplement.

◆ reload_module()

static int reload_module ( void  )
static

Definition at line 635 of file res_pjsip_geolocation.c.

636{
637 return 0;
638}

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 640 of file res_pjsip_geolocation.c.

641{
642 int res = 0;
644
645 return res;
646}
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

References ast_sip_session_unregister_supplement(), and geolocation_supplement.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "res_pjsip_geolocation Module for Asterisk" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND - 1, .requires = "res_geolocation,res_pjsip,res_pjsip_session,chan_pjsip", }
static

Definition at line 666 of file res_pjsip_geolocation.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 666 of file res_pjsip_geolocation.c.

◆ GEOLOCATION_HDR

pj_str_t GEOLOCATION_HDR
static

Definition at line 38 of file res_pjsip_geolocation.c.

Referenced by handle_incoming_request(), and load_module().

◆ GEOLOCATION_ROUTING_HDR

pj_str_t GEOLOCATION_ROUTING_HDR
static

Definition at line 39 of file res_pjsip_geolocation.c.

Referenced by handle_incoming_request(), and load_module().

◆ geolocation_supplement

struct ast_sip_session_supplement geolocation_supplement
static

Definition at line 628 of file res_pjsip_geolocation.c.

Referenced by load_module(), and unload_module().