res_pjsip_dialog_info_body_generator.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. /*** MODULEINFO
  19. <depend>pjproject</depend>
  20. <depend>res_pjsip</depend>
  21. <depend>res_pjsip_pubsub</depend>
  22. <support_level>core</support_level>
  23. ***/
  24. #include "asterisk.h"
  25. #include <pjsip.h>
  26. #include <pjsip_simple.h>
  27. #include <pjlib.h>
  28. #include "asterisk/module.h"
  29. #include "asterisk/callerid.h"
  30. #include "asterisk/res_pjsip.h"
  31. #include "asterisk/res_pjsip_pubsub.h"
  32. #include "asterisk/res_pjsip_presence_xml.h"
  33. #include "asterisk/res_pjsip_body_generator_types.h"
  34. /*! \brief Structure which contains dialog-info+xml state information */
  35. struct dialog_info_xml_state {
  36. /*! \brief Version to place into the next NOTIFY */
  37. unsigned int version;
  38. };
  39. /*! \brief Destructor for dialog-info+xml information */
  40. static void dialog_info_xml_state_destroy(void *obj)
  41. {
  42. ast_free(obj);
  43. }
  44. /*! \brief Datastore for attaching dialog-info+xml state information */
  45. static const struct ast_datastore_info dialog_info_xml_datastore = {
  46. .type = "dialog-info+xml",
  47. .destroy = dialog_info_xml_state_destroy,
  48. };
  49. static void *dialog_info_allocate_body(void *data)
  50. {
  51. struct ast_sip_exten_state_data *state_data = data;
  52. return ast_sip_presence_xml_create_node(state_data->pool, NULL, "dialog-info");
  53. }
  54. /*!
  55. * \internal
  56. * \brief Find the channel that is causing the RINGING update, ref'd
  57. */
  58. static struct ast_channel *find_ringing_channel(struct ao2_container *device_state_info)
  59. {
  60. struct ao2_iterator citer;
  61. struct ast_device_state_info *device_state;
  62. struct ast_channel *c = NULL;
  63. struct timeval tv = {0,};
  64. /* iterate ringing devices and get the oldest of all causing channels */
  65. citer = ao2_iterator_init(device_state_info, 0);
  66. for (; (device_state = ao2_iterator_next(&citer)); ao2_ref(device_state, -1)) {
  67. if (!device_state->causing_channel || (device_state->device_state != AST_DEVICE_RINGING &&
  68. device_state->device_state != AST_DEVICE_RINGINUSE)) {
  69. continue;
  70. }
  71. ast_channel_lock(device_state->causing_channel);
  72. if (ast_tvzero(tv) || ast_tvcmp(ast_channel_creationtime(device_state->causing_channel), tv) < 0) {
  73. c = device_state->causing_channel;
  74. tv = ast_channel_creationtime(c);
  75. }
  76. ast_channel_unlock(device_state->causing_channel);
  77. }
  78. ao2_iterator_destroy(&citer);
  79. return c ? ast_channel_ref(c) : NULL;
  80. }
  81. static int dialog_info_generate_body_content(void *body, void *data)
  82. {
  83. pj_xml_node *dialog_info = body, *dialog, *state;
  84. struct ast_datastore *datastore;
  85. struct dialog_info_xml_state *datastore_state;
  86. struct ast_sip_exten_state_data *state_data = data;
  87. char *local = ast_strdupa(state_data->local), *stripped, *statestring = NULL;
  88. char *remote = ast_strdupa(state_data->remote);
  89. char *pidfstate = NULL, *pidfnote = NULL;
  90. enum ast_sip_pidf_state local_state;
  91. char version_str[32], sanitized[PJSIP_MAX_URL_SIZE];
  92. struct ast_sip_endpoint *endpoint = NULL;
  93. unsigned int notify_early_inuse_ringing = 0;
  94. if (!local || !remote || !state_data->datastores) {
  95. return -1;
  96. }
  97. datastore = ast_datastores_find(state_data->datastores, "dialog-info+xml");
  98. if (!datastore) {
  99. const struct ast_json *version_json = NULL;
  100. datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");
  101. if (!datastore) {
  102. return -1;
  103. }
  104. datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));
  105. if (!datastore->data || ast_datastores_add(state_data->datastores, datastore)) {
  106. ao2_ref(datastore, -1);
  107. return -1;
  108. }
  109. datastore_state = datastore->data;
  110. if (state_data->sub) {
  111. version_json = ast_sip_subscription_get_persistence_data(state_data->sub);
  112. }
  113. if (version_json) {
  114. datastore_state->version = ast_json_integer_get(version_json);
  115. datastore_state->version++;
  116. } else {
  117. datastore_state->version = 0;
  118. }
  119. } else {
  120. datastore_state = datastore->data;
  121. datastore_state->version++;
  122. }
  123. stripped = ast_strip_quoted(local, "<", ">");
  124. ast_sip_sanitize_xml(stripped, sanitized, sizeof(sanitized));
  125. if (state_data->sub && (endpoint = ast_sip_subscription_get_endpoint(state_data->sub))) {
  126. notify_early_inuse_ringing = endpoint->notify_early_inuse_ringing;
  127. ao2_cleanup(endpoint);
  128. }
  129. ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
  130. &pidfstate, &pidfnote, &local_state, notify_early_inuse_ringing);
  131. ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "xmlns", "urn:ietf:params:xml:ns:dialog-info");
  132. snprintf(version_str, sizeof(version_str), "%u", datastore_state->version);
  133. ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "version", version_str);
  134. if (state_data->sub) {
  135. ast_sip_subscription_set_persistence_data(state_data->sub, ast_json_integer_create(datastore_state->version));
  136. }
  137. ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "state", "full");
  138. ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "entity", sanitized);
  139. dialog = ast_sip_presence_xml_create_node(state_data->pool, dialog_info, "dialog");
  140. ast_sip_presence_xml_create_attr(state_data->pool, dialog, "id", state_data->exten);
  141. if (!ast_strlen_zero(statestring) && !strcmp(statestring, "early")) {
  142. struct ast_channel *callee = NULL;
  143. char local_target[PJSIP_MAX_URL_SIZE + 32] = "";
  144. char *local_cid_name = NULL;
  145. pj_xml_node *local_node, *local_identity_node, *local_target_node;
  146. ast_sip_presence_xml_create_attr(state_data->pool, dialog, "direction", "recipient");
  147. if (state_data->device_state_info) {
  148. callee = find_ringing_channel(state_data->device_state_info);
  149. }
  150. if (callee) {
  151. static char *anonymous = "anonymous";
  152. static char *invalid = "anonymous.invalid";
  153. char *remote_cid_name;
  154. char *remote_connected_num;
  155. int remote_connected_num_restricted;
  156. char *local_caller_num;
  157. pjsip_dialog *dlg = ast_sip_subscription_get_dialog(state_data->sub);
  158. char remote_target[PJSIP_MAX_URL_SIZE + 32];
  159. char dlg_remote_uri[PJSIP_MAX_URL_SIZE];
  160. char *from_domain_stripped;
  161. char from_domain_sanitized[PJSIP_MAX_URL_SIZE];
  162. pj_xml_node *remote_node, *remote_identity_node, *remote_target_node;
  163. /* We use the local dialog URI to determine the domain to use in the XML itself */
  164. ast_copy_pj_str(dlg_remote_uri, ast_sip_pjsip_uri_get_hostname(dlg->local.info->uri), sizeof(dlg_remote_uri));
  165. from_domain_stripped = ast_strip_quoted(dlg_remote_uri, "<", ">");
  166. ast_sip_sanitize_xml(from_domain_stripped, from_domain_sanitized, sizeof(from_domain_sanitized));
  167. ast_channel_lock(callee);
  168. /* The remote node uses the connected line information, so who is calling the
  169. * monitored endpoint.
  170. */
  171. remote_cid_name = S_COR(ast_channel_connected(callee)->id.name.valid,
  172. S_COR((ast_channel_connected(callee)->id.name.presentation &
  173. AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED, anonymous,
  174. ast_channel_connected(callee)->id.name.str), "");
  175. remote_connected_num_restricted = (ast_channel_connected(callee)->id.number.presentation &
  176. AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED;
  177. remote_connected_num = S_COR(ast_channel_connected(callee)->id.number.valid,
  178. S_COR(remote_connected_num_restricted, anonymous,
  179. ast_channel_connected(callee)->id.number.str), "invalid");
  180. snprintf(remote_target, sizeof(remote_target), "sip:%s@%s", remote_connected_num,
  181. remote_connected_num_restricted ? invalid : from_domain_sanitized);
  182. /* The local node uses the callerid information, so what the callerid would be
  183. * if the monitored endpoint was calling.
  184. */
  185. local_cid_name = S_COR(ast_channel_caller(callee)->id.name.valid,
  186. ast_channel_caller(callee)->id.name.str, "");
  187. local_caller_num = S_COR(ast_channel_caller(callee)->id.number.valid,
  188. ast_channel_caller(callee)->id.number.str, "invalid");
  189. snprintf(local_target, sizeof(local_target), "sip:%s@%s", local_caller_num,
  190. from_domain_sanitized);
  191. ast_channel_unlock(callee);
  192. callee = ast_channel_unref(callee);
  193. remote_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "remote");
  194. remote_identity_node = ast_sip_presence_xml_create_node(state_data->pool, remote_node, "identity");
  195. remote_target_node = ast_sip_presence_xml_create_node(state_data->pool, remote_node, "target");
  196. pj_strdup2(state_data->pool, &remote_identity_node->content, remote_target);
  197. if (!ast_strlen_zero(remote_cid_name)) {
  198. char display_sanitized[PJSIP_MAX_URL_SIZE];
  199. ast_sip_sanitize_xml(remote_cid_name, display_sanitized, sizeof(display_sanitized));
  200. ast_sip_presence_xml_create_attr(state_data->pool, remote_identity_node, "display", display_sanitized);
  201. }
  202. ast_sip_presence_xml_create_attr(state_data->pool, remote_target_node, "uri", remote_target);
  203. }
  204. if (state_data->device_state_info) {
  205. local_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "local");
  206. local_identity_node = ast_sip_presence_xml_create_node(state_data->pool, local_node, "identity");
  207. local_target_node = ast_sip_presence_xml_create_node(state_data->pool, local_node, "target");
  208. /* If a channel is not available we fall back to the sanitized local URI instead */
  209. pj_strdup2(state_data->pool, &local_identity_node->content, S_OR(local_target, sanitized));
  210. if (!ast_strlen_zero(local_cid_name)) {
  211. char display_sanitized[PJSIP_MAX_URL_SIZE];
  212. ast_sip_sanitize_xml(local_cid_name, display_sanitized, sizeof(display_sanitized));
  213. ast_sip_presence_xml_create_attr(state_data->pool, local_identity_node, "display", display_sanitized);
  214. }
  215. ast_sip_presence_xml_create_attr(state_data->pool, local_target_node, "uri", sanitized);
  216. }
  217. }
  218. state = ast_sip_presence_xml_create_node(state_data->pool, dialog, "state");
  219. pj_strdup2(state_data->pool, &state->content, statestring);
  220. if (state_data->exten_state == AST_EXTENSION_ONHOLD) {
  221. pj_xml_node *local_node, *target, *param;
  222. local_node = ast_sip_presence_xml_create_node(state_data->pool, dialog, "local");
  223. target = ast_sip_presence_xml_create_node(state_data->pool, local_node, "target");
  224. ast_sip_presence_xml_create_attr(state_data->pool, target, "uri", sanitized);
  225. param = ast_sip_presence_xml_create_node(state_data->pool, target, "param");
  226. ast_sip_presence_xml_create_attr(state_data->pool, param, "pname", "+sip.rendering");
  227. ast_sip_presence_xml_create_attr(state_data->pool, param, "pvalue", "no");
  228. }
  229. ao2_ref(datastore, -1);
  230. return 0;
  231. }
  232. /* The maximum number of times the ast_str() for the body text can grow before we declare an XML body
  233. * too large to send.
  234. */
  235. #define MAX_STRING_GROWTHS 6
  236. static void dialog_info_to_string(void *body, struct ast_str **str)
  237. {
  238. pj_xml_node *dialog_info = body;
  239. int growths = 0;
  240. int size;
  241. do {
  242. size = pj_xml_print(dialog_info, ast_str_buffer(*str), ast_str_size(*str) - 1, PJ_TRUE);
  243. if (size <= AST_PJSIP_XML_PROLOG_LEN) {
  244. ast_str_make_space(str, ast_str_size(*str) * 2);
  245. ++growths;
  246. }
  247. } while (size <= AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
  248. if (size <= AST_PJSIP_XML_PROLOG_LEN) {
  249. ast_log(LOG_WARNING, "dialog-info+xml body text too large\n");
  250. return;
  251. }
  252. *(ast_str_buffer(*str) + size) = '\0';
  253. ast_str_update(*str);
  254. }
  255. static struct ast_sip_pubsub_body_generator dialog_info_body_generator = {
  256. .type = "application",
  257. .subtype = "dialog-info+xml",
  258. .body_type = AST_SIP_EXTEN_STATE_DATA,
  259. .allocate_body = dialog_info_allocate_body,
  260. .generate_body_content = dialog_info_generate_body_content,
  261. .to_string = dialog_info_to_string,
  262. /* No need for a destroy_body callback since we use a pool */
  263. };
  264. static int load_module(void)
  265. {
  266. if (ast_sip_pubsub_register_body_generator(&dialog_info_body_generator)) {
  267. return AST_MODULE_LOAD_DECLINE;
  268. }
  269. return AST_MODULE_LOAD_SUCCESS;
  270. }
  271. static int unload_module(void)
  272. {
  273. ast_sip_pubsub_unregister_body_generator(&dialog_info_body_generator);
  274. return 0;
  275. }
  276. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Dialog Info+XML Provider",
  277. .support_level = AST_MODULE_SUPPORT_CORE,
  278. .load = load_module,
  279. .unload = unload_module,
  280. .load_pri = AST_MODPRI_CHANNEL_DEPEND,
  281. .requires = "res_pjsip,res_pjsip_pubsub",
  282. );