Asterisk - The Open Source Telephony Project GIT-master-a358458
res_pjsip_dialog_info_body_generator.c
Go to the documentation of this file.
1/*
2 * asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2014, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@digium.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>pjproject</depend>
21 <depend>res_pjsip</depend>
22 <depend>res_pjsip_pubsub</depend>
23 <support_level>core</support_level>
24 ***/
25
26#include "asterisk.h"
27
28#include <pjsip.h>
29#include <pjsip_simple.h>
30#include <pjlib.h>
31
32#include "asterisk/module.h"
33#include "asterisk/callerid.h"
34#include "asterisk/res_pjsip.h"
38
39/*! \brief Structure which contains dialog-info+xml state information */
41 /*! \brief Version to place into the next NOTIFY */
42 unsigned int version;
43};
44
45/*! \brief Destructor for dialog-info+xml information */
46static void dialog_info_xml_state_destroy(void *obj)
47{
48 ast_free(obj);
49}
50
51/*! \brief Datastore for attaching dialog-info+xml state information */
53 .type = "dialog-info+xml",
55};
56
57static void *dialog_info_allocate_body(void *data)
58{
59 struct ast_sip_exten_state_data *state_data = data;
60
61 return ast_sip_presence_xml_create_node(state_data->pool, NULL, "dialog-info");
62}
63
64/*!
65 * \internal
66 * \brief Find the channel that is causing the RINGING update, ref'd
67 */
68static struct ast_channel *find_ringing_channel(struct ao2_container *device_state_info)
69{
70 struct ao2_iterator citer;
72 struct ast_channel *c = NULL;
73 struct timeval tv = {0,};
74
75 /* iterate ringing devices and get the oldest of all causing channels */
76 citer = ao2_iterator_init(device_state_info, 0);
77 for (; (device_state = ao2_iterator_next(&citer)); ao2_ref(device_state, -1)) {
78 if (!device_state->causing_channel || (device_state->device_state != AST_DEVICE_RINGING &&
79 device_state->device_state != AST_DEVICE_RINGINUSE)) {
80 continue;
81 }
82 ast_channel_lock(device_state->causing_channel);
83 if (ast_tvzero(tv) || ast_tvcmp(ast_channel_creationtime(device_state->causing_channel), tv) < 0) {
84 c = device_state->causing_channel;
86 }
88 }
90 return c ? ast_channel_ref(c) : NULL;
91}
92
93static int dialog_info_generate_body_content(void *body, void *data)
94{
95 pj_xml_node *dialog_info = body, *dialog, *state;
96 struct ast_datastore *datastore;
97 struct dialog_info_xml_state *datastore_state;
98 struct ast_sip_exten_state_data *state_data = data;
99 char *local = ast_strdupa(state_data->local), *stripped, *statestring = NULL;
100 char *remote = ast_strdupa(state_data->remote);
101 char *pidfstate = NULL, *pidfnote = NULL;
102 enum ast_sip_pidf_state local_state;
103 char version_str[32], sanitized[PJSIP_MAX_URL_SIZE];
104 struct ast_sip_endpoint *endpoint = NULL;
105 unsigned int notify_early_inuse_ringing = 0;
106
107 if (!local || !remote || !state_data->datastores) {
108 return -1;
109 }
110
111 datastore = ast_datastores_find(state_data->datastores, "dialog-info+xml");
112 if (!datastore) {
113 const struct ast_json *version_json = NULL;
114
115 datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");
116 if (!datastore) {
117 return -1;
118 }
119
120 datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));
121 if (!datastore->data || ast_datastores_add(state_data->datastores, datastore)) {
122 ao2_ref(datastore, -1);
123 return -1;
124 }
125 datastore_state = datastore->data;
126
127 if (state_data->sub) {
128 version_json = ast_sip_subscription_get_persistence_data(state_data->sub);
129 }
130
131 if (version_json) {
132 datastore_state->version = ast_json_integer_get(version_json);
133 datastore_state->version++;
134 } else {
135 datastore_state->version = 0;
136 }
137 } else {
138 datastore_state = datastore->data;
139 datastore_state->version++;
140 }
141
142 stripped = ast_strip_quoted(local, "<", ">");
143 ast_sip_sanitize_xml(stripped, sanitized, sizeof(sanitized));
144
145 if (state_data->sub && (endpoint = ast_sip_subscription_get_endpoint(state_data->sub))) {
146 notify_early_inuse_ringing = endpoint->notify_early_inuse_ringing;
147 ao2_cleanup(endpoint);
148 }
149 ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
150 &pidfstate, &pidfnote, &local_state, notify_early_inuse_ringing);
151
152 ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "xmlns", "urn:ietf:params:xml:ns:dialog-info");
153
154 snprintf(version_str, sizeof(version_str), "%u", datastore_state->version);
155 ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "version", version_str);
156
157 if (state_data->sub) {
159 }
160
161 ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "state", "full");
162 ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "entity", sanitized);
163
164 dialog = ast_sip_presence_xml_create_node(state_data->pool, dialog_info, "dialog");
165 ast_sip_presence_xml_create_attr(state_data->pool, dialog, "id", state_data->exten);
166 if (!ast_strlen_zero(statestring) && !strcmp(statestring, "early")) {
167 struct ast_channel *callee = NULL;
168 char local_target[PJSIP_MAX_URL_SIZE + 32] = "";
169 char *local_cid_name = NULL;
170 pj_xml_node *local_node, *local_identity_node, *local_target_node;
171
172 ast_sip_presence_xml_create_attr(state_data->pool, dialog, "direction", "recipient");
173
174 if (state_data->device_state_info) {
175 callee = find_ringing_channel(state_data->device_state_info);
176 }
177
178 if (callee) {
179 static char *anonymous = "anonymous";
180 static char *invalid = "anonymous.invalid";
181 char *remote_cid_name;
182 char *remote_connected_num;
183 int remote_connected_num_restricted;
184 char *local_caller_num;
185 pjsip_dialog *dlg = ast_sip_subscription_get_dialog(state_data->sub);
186 char remote_target[PJSIP_MAX_URL_SIZE + 32];
187 char dlg_remote_uri[PJSIP_MAX_URL_SIZE];
188 char *from_domain_stripped;
189 char from_domain_sanitized[PJSIP_MAX_URL_SIZE];
190 pj_xml_node *remote_node, *remote_identity_node, *remote_target_node;
191
192 /* We use the local dialog URI to determine the domain to use in the XML itself */
193 ast_copy_pj_str(dlg_remote_uri, ast_sip_pjsip_uri_get_hostname(dlg->local.info->uri), sizeof(dlg_remote_uri));
194 from_domain_stripped = ast_strip_quoted(dlg_remote_uri, "<", ">");
195 ast_sip_sanitize_xml(from_domain_stripped, from_domain_sanitized, sizeof(from_domain_sanitized));
196
197 ast_channel_lock(callee);
198
199 /* The remote node uses the connected line information, so who is calling the
200 * monitored endpoint.
201 */
202 remote_cid_name = S_COR(ast_channel_connected(callee)->id.name.valid,
203 S_COR((ast_channel_connected(callee)->id.name.presentation &
205 ast_channel_connected(callee)->id.name.str), "");
206
207 remote_connected_num_restricted = (ast_channel_connected(callee)->id.number.presentation &
209 remote_connected_num = S_COR(ast_channel_connected(callee)->id.number.valid,
210 S_COR(remote_connected_num_restricted, anonymous,
211 ast_channel_connected(callee)->id.number.str), "invalid");
212
213 snprintf(remote_target, sizeof(remote_target), "sip:%s@%s", remote_connected_num,
214 remote_connected_num_restricted ? invalid : from_domain_sanitized);
215
216 /* The local node uses the callerid information, so what the callerid would be
217 * if the monitored endpoint was calling.
218 */
219 local_cid_name = S_COR(ast_channel_caller(callee)->id.name.valid,
220 ast_channel_caller(callee)->id.name.str, "");
221 local_caller_num = S_COR(ast_channel_caller(callee)->id.number.valid,
222 ast_channel_caller(callee)->id.number.str, "invalid");
223
224 snprintf(local_target, sizeof(local_target), "sip:%s@%s", local_caller_num,
225 from_domain_sanitized);
226
227 ast_channel_unlock(callee);
228 callee = ast_channel_unref(callee);
229
230 remote_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "remote");
231 remote_identity_node = ast_sip_presence_xml_create_node(state_data->pool, remote_node, "identity");
232 remote_target_node = ast_sip_presence_xml_create_node(state_data->pool, remote_node, "target");
233
234 pj_strdup2(state_data->pool, &remote_identity_node->content, remote_target);
235 if (!ast_strlen_zero(remote_cid_name)) {
236 char display_sanitized[PJSIP_MAX_URL_SIZE];
237
238 ast_sip_sanitize_xml(remote_cid_name, display_sanitized, sizeof(display_sanitized));
239 ast_sip_presence_xml_create_attr(state_data->pool, remote_identity_node, "display", display_sanitized);
240 }
241 ast_sip_presence_xml_create_attr(state_data->pool, remote_target_node, "uri", remote_target);
242 }
243
244 if (state_data->device_state_info) {
245 local_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "local");
246 local_identity_node = ast_sip_presence_xml_create_node(state_data->pool, local_node, "identity");
247 local_target_node = ast_sip_presence_xml_create_node(state_data->pool, local_node, "target");
248
249 /* If a channel is not available we fall back to the sanitized local URI instead */
250 pj_strdup2(state_data->pool, &local_identity_node->content, S_OR(local_target, sanitized));
251 if (!ast_strlen_zero(local_cid_name)) {
252 char display_sanitized[PJSIP_MAX_URL_SIZE];
253
254 ast_sip_sanitize_xml(local_cid_name, display_sanitized, sizeof(display_sanitized));
255 ast_sip_presence_xml_create_attr(state_data->pool, local_identity_node, "display", display_sanitized);
256 }
257
258 ast_sip_presence_xml_create_attr(state_data->pool, local_target_node, "uri", sanitized);
259 }
260 }
261
262 state = ast_sip_presence_xml_create_node(state_data->pool, dialog, "state");
263 pj_strdup2(state_data->pool, &state->content, statestring);
264
265 if (state_data->exten_state == AST_EXTENSION_ONHOLD) {
266 pj_xml_node *local_node, *target, *param;
267
268 local_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "local");
269 target = ast_sip_presence_xml_create_node(state_data->pool, local_node, "target");
270 ast_sip_presence_xml_create_attr(state_data->pool, target, "uri", sanitized);
271 param = ast_sip_presence_xml_create_node(state_data->pool, target, "param");
272 ast_sip_presence_xml_create_attr(state_data->pool, param, "pname", "+sip.rendering");
273 ast_sip_presence_xml_create_attr(state_data->pool, param, "pvalue", "no");
274 }
275
276 ao2_ref(datastore, -1);
277
278 return 0;
279}
280
281/* The maximum number of times the ast_str() for the body text can grow before we declare an XML body
282 * too large to send.
283 */
284#define MAX_STRING_GROWTHS 6
285
286static void dialog_info_to_string(void *body, struct ast_str **str)
287{
288 pj_xml_node *dialog_info = body;
289 int growths = 0;
290 int size;
291
292 do {
293 size = pj_xml_print(dialog_info, ast_str_buffer(*str), ast_str_size(*str) - 1, PJ_TRUE);
294 if (size <= AST_PJSIP_XML_PROLOG_LEN) {
296 ++growths;
297 }
298 } while (size <= AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
299 if (size <= AST_PJSIP_XML_PROLOG_LEN) {
300 ast_log(LOG_WARNING, "dialog-info+xml body text too large\n");
301 return;
302 }
303
304 *(ast_str_buffer(*str) + size) = '\0';
306}
307
309 .type = "application",
310 .subtype = "dialog-info+xml",
311 .body_type = AST_SIP_EXTEN_STATE_DATA,
312 .allocate_body = dialog_info_allocate_body,
313 .generate_body_content = dialog_info_generate_body_content,
314 .to_string = dialog_info_to_string,
315 /* No need for a destroy_body callback since we use a pool */
316};
317
318static int load_module(void)
319{
322 }
323
325}
326
327static int unload_module(void)
328{
330 return 0;
331}
332
333AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Dialog Info+XML Provider",
334 .support_level = AST_MODULE_SUPPORT_CORE,
335 .load = load_module,
336 .unload = unload_module,
337 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
338 .requires = "res_pjsip,res_pjsip_pubsub",
const char * str
Definition: app_jack.c:147
Asterisk main include file. File version handling, generic pbx functions.
#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_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
#define AST_PRES_RESTRICTED
Definition: callerid.h:419
#define AST_PRES_RESTRICTION
Definition: callerid.h:417
enum cc_state state
Definition: ccss.c:393
#define ast_channel_lock(chan)
Definition: channel.h:2922
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2947
struct ast_party_connected_line * ast_channel_connected(struct ast_channel *chan)
struct timeval ast_channel_creationtime(struct ast_channel *chan)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2923
int ast_datastores_add(struct ao2_container *datastores, struct ast_datastore *datastore)
Add a data store to a container.
Definition: datastore.c:105
struct ast_datastore * ast_datastores_find(struct ao2_container *datastores, const char *name)
Find a data store in a container.
Definition: datastore.c:123
struct ast_datastore * ast_datastores_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Allocate a datastore for use with the datastores container.
Definition: datastore.c:142
@ AST_DEVICE_RINGINUSE
Definition: devicestate.h:60
@ AST_DEVICE_RINGING
Definition: devicestate.h:59
static const char name[]
Definition: format_mp3.c:68
#define LOG_WARNING
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.
Definition: json.c:327
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:332
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
@ AST_EXTENSION_ONHOLD
Definition: pbx.h:69
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
const pj_str_t * ast_sip_pjsip_uri_get_hostname(pjsip_uri *uri)
Get the host portion of the pjsip_uri.
Definition: res_pjsip.c:3496
static void dialog_info_to_string(void *body, struct ast_str **str)
static struct ast_channel * find_ringing_channel(struct ao2_container *device_state_info)
static int dialog_info_generate_body_content(void *body, void *data)
static void dialog_info_xml_state_destroy(void *obj)
Destructor for dialog-info+xml information.
static struct ast_sip_pubsub_body_generator dialog_info_body_generator
static int load_module(void)
static int unload_module(void)
static const struct ast_datastore_info dialog_info_xml_datastore
Datastore for attaching dialog-info+xml state information.
static void * dialog_info_allocate_body(void *data)
void ast_sip_sanitize_xml(const char *input, char *output, size_t len)
Replace offensive XML characters with XML entities.
Definition: presence_xml.c:29
#define AST_PJSIP_XML_PROLOG_LEN
Length of the XML prolog when printing presence or other XML in PJSIP.
ast_sip_pidf_state
void ast_sip_presence_exten_state_to_str(int state, char **statestring, char **pidfstate, char **pidfnote, enum ast_sip_pidf_state *local_state, unsigned int notify_early_inuse_ringing)
Convert extension state to relevant PIDF strings.
Definition: presence_xml.c:84
pj_xml_node * ast_sip_presence_xml_create_node(pj_pool_t *pool, pj_xml_node *parent, const char *name)
Create XML node.
Definition: presence_xml.c:152
pj_xml_attr * ast_sip_presence_xml_create_attr(pj_pool_t *pool, pj_xml_node *node, const char *name, const char *value)
Create XML attribute.
Definition: presence_xml.c:140
struct ast_sip_endpoint * ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
Get the endpoint that is associated with this subscription.
void ast_sip_pubsub_unregister_body_generator(struct ast_sip_pubsub_body_generator *generator)
Unregister a body generator with the pubsub core.
const struct ast_json * ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription)
Retrieve persistence data for a subscription.
void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
Set persistence data for a subscription.
int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator *generator)
Register a body generator with the pubsub core.
pjsip_dialog * ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub)
Get the pjsip dialog that is associated with this subscription.
#define AST_SIP_EXTEN_STATE_DATA
#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
#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
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
Strip leading/trailing whitespace and quotes from a string.
Definition: utils.c:1818
#define ast_str_make_space(buf, new_len)
Definition: strings.h:828
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:703
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:742
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Main Channel structure associated with a channel.
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
struct ast_channel * causing_channel
Definition: pbx.h:98
enum ast_device_state device_state
Definition: pbx.h:97
Abstract JSON element (object, array, string, int, ...).
struct ast_party_id id
Connected party ID.
Definition: channel.h:458
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
int presentation
Q.931 presentation-indicator and screening-indicator encoded fields.
Definition: channel.h:295
An entity with which Asterisk communicates.
Definition: res_pjsip.h:963
unsigned int notify_early_inuse_ringing
Definition: res_pjsip.h:1052
structure used for presence XML bodies
struct ast_sip_subscription * sub
enum ast_extension_states exten_state
struct ao2_container * device_state_info
Pubsub body generator.
const char * type
Content type In "plain/text", "plain" is the type.
Support for dynamic strings.
Definition: strings.h:623
Structure which contains dialog-info+xml state information.
unsigned int version
Version to place into the next NOTIFY.
Number structure.
Definition: app_followme.c:154
static struct test_val c
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compress two struct timeval instances returning -1, 0, 1 if the first arg is smaller,...
Definition: time.h:137