Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 */
46 static 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 
57 static 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  */
68 static 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  }
87  ast_channel_unlock(device_state->causing_channel);
88  }
89  ao2_iterator_destroy(&citer);
90  return c ? ast_channel_ref(c) : NULL;
91 }
92 
93 static 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  pjsip_sip_uri *dlg_remote_fromhdr = pjsip_uri_get_uri(dlg->local.info->uri);
187  char remote_target[PJSIP_MAX_URL_SIZE + 32];
188  char dlg_remote_uri[PJSIP_MAX_URL_SIZE];
189  char *from_domain_stripped;
190  char from_domain_sanitized[PJSIP_MAX_URL_SIZE];
191  pj_xml_node *remote_node, *remote_identity_node, *remote_target_node;
192 
193  /* We use the local dialog URI to determine the domain to use in the XML itself */
194  ast_copy_pj_str(dlg_remote_uri, &dlg_remote_fromhdr->host, sizeof(dlg_remote_uri));
195  from_domain_stripped = ast_strip_quoted(dlg_remote_uri, "<", ">");
196  ast_sip_sanitize_xml(from_domain_stripped, from_domain_sanitized, sizeof(from_domain_sanitized));
197 
198  ast_channel_lock(callee);
199 
200  /* The remote node uses the connected line information, so who is calling the
201  * monitored endpoint.
202  */
203  remote_cid_name = S_COR(ast_channel_connected(callee)->id.name.valid,
204  S_COR((ast_channel_connected(callee)->id.name.presentation &
206  ast_channel_connected(callee)->id.name.str), "");
207 
208  remote_connected_num_restricted = (ast_channel_connected(callee)->id.number.presentation &
210  remote_connected_num = S_COR(ast_channel_connected(callee)->id.number.valid,
211  S_COR(remote_connected_num_restricted, anonymous,
212  ast_channel_connected(callee)->id.number.str), "invalid");
213 
214  snprintf(remote_target, sizeof(remote_target), "sip:%s@%s", remote_connected_num,
215  remote_connected_num_restricted ? invalid : from_domain_sanitized);
216 
217  /* The local node uses the callerid information, so what the callerid would be
218  * if the monitored endpoint was calling.
219  */
220  local_cid_name = S_COR(ast_channel_caller(callee)->id.name.valid,
221  ast_channel_caller(callee)->id.name.str, "");
222  local_caller_num = S_COR(ast_channel_caller(callee)->id.number.valid,
223  ast_channel_caller(callee)->id.number.str, "invalid");
224 
225  snprintf(local_target, sizeof(local_target), "sip:%s@%s", local_caller_num,
226  from_domain_sanitized);
227 
228  ast_channel_unlock(callee);
229  callee = ast_channel_unref(callee);
230 
231  remote_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "remote");
232  remote_identity_node = ast_sip_presence_xml_create_node(state_data->pool, remote_node, "identity");
233  remote_target_node = ast_sip_presence_xml_create_node(state_data->pool, remote_node, "target");
234 
235  pj_strdup2(state_data->pool, &remote_identity_node->content, remote_target);
236  if (!ast_strlen_zero(remote_cid_name)) {
237  ast_sip_presence_xml_create_attr(state_data->pool, remote_identity_node, "display", remote_cid_name);
238  }
239  ast_sip_presence_xml_create_attr(state_data->pool, remote_target_node, "uri", remote_target);
240  }
241 
242  if (state_data->device_state_info) {
243  local_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "local");
244  local_identity_node = ast_sip_presence_xml_create_node(state_data->pool, local_node, "identity");
245  local_target_node = ast_sip_presence_xml_create_node(state_data->pool, local_node, "target");
246 
247  /* If a channel is not available we fall back to the sanitized local URI instead */
248  pj_strdup2(state_data->pool, &local_identity_node->content, S_OR(local_target, sanitized));
249  if (!ast_strlen_zero(local_cid_name)) {
250  ast_sip_presence_xml_create_attr(state_data->pool, local_identity_node, "display", local_cid_name);
251  }
252  ast_sip_presence_xml_create_attr(state_data->pool, local_target_node, "uri", S_OR(local_target, sanitized));
253  }
254  }
255 
256  state = ast_sip_presence_xml_create_node(state_data->pool, dialog, "state");
257  pj_strdup2(state_data->pool, &state->content, statestring);
258 
259  if (state_data->exten_state == AST_EXTENSION_ONHOLD) {
260  pj_xml_node *local_node, *target, *param;
261 
262  local_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "local");
263  target = ast_sip_presence_xml_create_node(state_data->pool, local_node, "target");
264  ast_sip_presence_xml_create_attr(state_data->pool, target, "uri", sanitized);
265  param = ast_sip_presence_xml_create_node(state_data->pool, target, "param");
266  ast_sip_presence_xml_create_attr(state_data->pool, param, "pname", "+sip.rendering");
267  ast_sip_presence_xml_create_attr(state_data->pool, param, "pvalue", "no");
268  }
269 
270  ao2_ref(datastore, -1);
271 
272  return 0;
273 }
274 
275 /* The maximum number of times the ast_str() for the body text can grow before we declare an XML body
276  * too large to send.
277  */
278 #define MAX_STRING_GROWTHS 6
279 
280 static void dialog_info_to_string(void *body, struct ast_str **str)
281 {
282  pj_xml_node *dialog_info = body;
283  int growths = 0;
284  int size;
285 
286  do {
287  size = pj_xml_print(dialog_info, ast_str_buffer(*str), ast_str_size(*str) - 1, PJ_TRUE);
288  if (size <= AST_PJSIP_XML_PROLOG_LEN) {
289  ast_str_make_space(str, ast_str_size(*str) * 2);
290  ++growths;
291  }
292  } while (size <= AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
293  if (size <= AST_PJSIP_XML_PROLOG_LEN) {
294  ast_log(LOG_WARNING, "dialog-info+xml body text too large\n");
295  return;
296  }
297 
298  *(ast_str_buffer(*str) + size) = '\0';
299  ast_str_update(*str);
300 }
301 
303  .type = "application",
304  .subtype = "dialog-info+xml",
305  .body_type = AST_SIP_EXTEN_STATE_DATA,
306  .allocate_body = dialog_info_allocate_body,
307  .generate_body_content = dialog_info_generate_body_content,
308  .to_string = dialog_info_to_string,
309  /* No need for a destroy_body callback since we use a pool */
310 };
311 
312 static int load_module(void)
313 {
314  if (ast_sip_pubsub_register_body_generator(&dialog_info_body_generator)) {
316  }
317 
319 }
320 
321 static int unload_module(void)
322 {
323  ast_sip_pubsub_unregister_body_generator(&dialog_info_body_generator);
324  return 0;
325 }
326 
327 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Dialog Info+XML Provider",
328  .support_level = AST_MODULE_SUPPORT_CORE,
329  .load = load_module,
330  .unload = unload_module,
331  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
332  .requires = "res_pjsip,res_pjsip_pubsub",
333 );
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
const char * type
Definition: datastore.h:32
static const struct ast_datastore_info dialog_info_xml_datastore
Datastore for attaching dialog-info+xml state information.
enum sip_cc_notify_state state
Definition: chan_sip.c:959
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
static void dialog_info_to_string(void *body, struct ast_str **str)
int presentation
Q.931 presentation-indicator and screening-indicator encoded fields.
Definition: channel.h:296
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
struct ast_party_id id
Connected party ID.
Definition: channel.h:459
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2981
Pubsub body generator.
const char * type
Content type In "plain/text", "plain" is the type.
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:699
#define LOG_WARNING
Definition: logger.h:274
#define ast_str_make_space(buf, new_len)
Definition: strings.h:780
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static void * dialog_info_allocate_body(void *data)
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
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:108
int ast_datastores_add(struct ao2_container *datastores, struct ast_datastore *datastore)
Add a data store to a container.
Definition: datastore.c:101
Structure for a data store type.
Definition: datastore.h:31
Structure which contains dialog-info+xml state information.
enum ast_extension_states exten_state
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static struct test_val c
Structure for a data store object.
Definition: datastore.h:68
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:5240
const char * str
Definition: app_jack.c:147
int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator *generator)
Register a body generator with the pubsub core.
#define NULL
Definition: resample.c:96
struct ast_sip_subscription * sub
static int dialog_info_generate_body_content(void *body, void *data)
#define AST_PRES_RESTRICTED
Definition: callerid.h:325
struct ast_datastore * ast_datastores_find(struct ao2_container *datastores, const char *name)
Find a data store in a container.
Definition: datastore.c:119
#define ast_strlen_zero(foo)
Definition: strings.h:52
Number structure.
Definition: app_followme.c:154
#define AST_SIP_EXTEN_STATE_DATA
struct ast_channel * causing_channel
Definition: pbx.h:98
char * ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
Strip leading/trailing whitespace and quotes from a string.
Definition: main/utils.c:1639
#define ast_log
Definition: astobj2.c:42
void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
Set persistence data for a subscription.
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
struct ast_party_connected_line * ast_channel_connected(struct ast_channel *chan)
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#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:85
unsigned int notify_early_inuse_ringing
Definition: res_pjsip.h:897
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
enum ast_device_state device_state
Definition: pbx.h:97
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:138
An entity with which Asterisk communicates.
Definition: res_pjsip.h:812
#define AST_PJSIP_XML_PROLOG_LEN
Length of the XML prolog when printing presence or other XML in PJSIP.
struct ao2_container * device_state_info
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compres two struct timeval instances returning -1, 0, 1 if the first arg is smaller, equal or greater to the second.
Definition: time.h:128
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
struct timeval ast_channel_creationtime(struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static struct ast_sip_pubsub_body_generator dialog_info_body_generator
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
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
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
static struct ast_channel * find_ringing_channel(struct ao2_container *device_state_info)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
static int unload_module(void)
#define AST_PRES_RESTRICTION
Definition: callerid.h:323
struct ast_sip_endpoint * ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
Get the endpoint that is associated with this subscription.
pjsip_dialog * ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub)
Get the pjsip dialog that is associated with this subscription.
static int load_module(void)
structure used for presence XML bodies
void * data
Definition: datastore.h:70
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2970
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
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:663
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
Abstract JSON element (object, array, string, int, ...).
const struct ast_json * ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription)
Retrieve persistence data for a subscription.
Generic container type.
ast_sip_pidf_state
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static void dialog_info_xml_state_destroy(void *obj)
Destructor for dialog-info+xml information.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:322
unsigned int version
Version to place into the next NOTIFY.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void ast_sip_pubsub_unregister_body_generator(struct ast_sip_pubsub_body_generator *generator)
Unregister a body generator with the pubsub core.
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.
Definition: json.c:317
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:343