res_pjsip_publish_asterisk.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  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_outbound_publish</depend>
  22. <depend>res_pjsip_pubsub</depend>
  23. <support_level>core</support_level>
  24. ***/
  25. #include "asterisk.h"
  26. #include <regex.h>
  27. #include <pjsip.h>
  28. #include <pjsip_simple.h>
  29. #include "asterisk/res_pjsip.h"
  30. #include "asterisk/res_pjsip_outbound_publish.h"
  31. #include "asterisk/res_pjsip_pubsub.h"
  32. #include "asterisk/module.h"
  33. #include "asterisk/logger.h"
  34. #include "asterisk/app.h"
  35. /*** DOCUMENTATION
  36. <configInfo name="res_pjsip_publish_asterisk" language="en_US">
  37. <synopsis>SIP resource for inbound and outbound Asterisk event publications</synopsis>
  38. <description><para>
  39. <emphasis>Inbound and outbound Asterisk event publication</emphasis>
  40. </para>
  41. <para>This module allows <literal>res_pjsip</literal> to send and receive Asterisk event publications.</para>
  42. </description>
  43. <configFile name="pjsip.conf">
  44. <configObject name="asterisk-publication">
  45. <synopsis>The configuration for inbound Asterisk event publication</synopsis>
  46. <description><para>
  47. Publish is <emphasis>COMPLETELY</emphasis> separate from the rest of
  48. <literal>pjsip.conf</literal>.
  49. </para></description>
  50. <configOption name="devicestate_publish">
  51. <synopsis>Optional name of a publish item that can be used to publish a request for full device state information.</synopsis>
  52. </configOption>
  53. <configOption name="mailboxstate_publish">
  54. <synopsis>Optional name of a publish item that can be used to publish a request for full mailbox state information.</synopsis>
  55. </configOption>
  56. <configOption name="device_state" default="no">
  57. <synopsis>Whether we should permit incoming device state events.</synopsis>
  58. </configOption>
  59. <configOption name="device_state_filter">
  60. <synopsis>Optional regular expression used to filter what devices we accept events for.</synopsis>
  61. </configOption>
  62. <configOption name="mailbox_state" default="no">
  63. <synopsis>Whether we should permit incoming mailbox state events.</synopsis>
  64. </configOption>
  65. <configOption name="mailbox_state_filter">
  66. <synopsis>Optional regular expression used to filter what mailboxes we accept events for.</synopsis>
  67. </configOption>
  68. <configOption name="type">
  69. <synopsis>Must be of type 'asterisk-publication'.</synopsis>
  70. </configOption>
  71. </configObject>
  72. </configFile>
  73. </configInfo>
  74. ***/
  75. /*! \brief Structure which contains Asterisk device state publisher state information */
  76. struct asterisk_devicestate_publisher_state {
  77. /*! \brief The publish client to send PUBLISH messages on */
  78. struct ast_sip_outbound_publish_client *client;
  79. /*! \brief Device state subscription */
  80. struct stasis_subscription *device_state_subscription;
  81. /*! \brief Regex used for filtering outbound device state */
  82. regex_t device_state_regex;
  83. /*! \brief Device state should be filtered */
  84. unsigned int device_state_filter;
  85. };
  86. /*! \brief Structure which contains Asterisk mailbox publisher state information */
  87. struct asterisk_mwi_publisher_state {
  88. /*! \brief The publish client to send PUBLISH messages on */
  89. struct ast_sip_outbound_publish_client *client;
  90. /*! \brief Mailbox state subscription */
  91. struct stasis_subscription *mailbox_state_subscription;
  92. /*! \brief Regex used for filtering outbound mailbox state */
  93. regex_t mailbox_state_regex;
  94. /*! \brief Mailbox state should be filtered */
  95. unsigned int mailbox_state_filter;
  96. };
  97. /*! \brief Structure which contains Asterisk publication information */
  98. struct asterisk_publication_config {
  99. /*! \brief Sorcery object details */
  100. SORCERY_OBJECT(details);
  101. /*! \brief Stringfields */
  102. AST_DECLARE_STRING_FIELDS(
  103. /*! \brief Optional name of a device state publish item, used to request the remote side update us */
  104. AST_STRING_FIELD(devicestate_publish);
  105. /*! \brief Optional name of a mailbox state publish item, used to request the remote side update us */
  106. AST_STRING_FIELD(mailboxstate_publish);
  107. );
  108. /*! \brief Accept inbound device state events */
  109. unsigned int device_state;
  110. /*! \brief Regex used for filtering inbound device state */
  111. regex_t device_state_regex;
  112. /*! \brief Device state should be filtered */
  113. unsigned int device_state_filter;
  114. /*! \brief Accept inbound mailbox state events */
  115. unsigned int mailbox_state;
  116. /*! \brief Regex used for filtering inbound mailbox state */
  117. regex_t mailbox_state_regex;
  118. /*! \brief Mailbox state should be filtered */
  119. unsigned int mailbox_state_filter;
  120. };
  121. /*! \brief Destroy callback for Asterisk devicestate publisher state information from datastore */
  122. static void asterisk_devicestate_publisher_state_destroy(void *obj)
  123. {
  124. struct asterisk_devicestate_publisher_state *publisher_state = obj;
  125. ao2_cleanup(publisher_state->client);
  126. if (publisher_state->device_state_filter) {
  127. regfree(&publisher_state->device_state_regex);
  128. }
  129. }
  130. /*! \brief Datastore for attaching devicestate publisher state information */
  131. static const struct ast_datastore_info asterisk_devicestate_publisher_state_datastore = {
  132. .type = "asterisk-devicestate-publisher",
  133. .destroy = asterisk_devicestate_publisher_state_destroy,
  134. };
  135. /*! \brief Destroy callback for Asterisk mwi publisher state information from datastore */
  136. static void asterisk_mwi_publisher_state_destroy(void *obj)
  137. {
  138. struct asterisk_mwi_publisher_state *publisher_state = obj;
  139. ao2_cleanup(publisher_state->client);
  140. if (publisher_state->mailbox_state_filter) {
  141. regfree(&publisher_state->mailbox_state_regex);
  142. }
  143. }
  144. /*! \brief Datastore for attaching devicestate publisher state information */
  145. static const struct ast_datastore_info asterisk_mwi_publisher_state_datastore = {
  146. .type = "asterisk-mwi-publisher",
  147. .destroy = asterisk_mwi_publisher_state_destroy,
  148. };
  149. /*!
  150. * \brief Callback function for device state events
  151. * \param ast_event
  152. * \param data void pointer to ast_client structure
  153. * \return void
  154. */
  155. static void asterisk_publisher_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
  156. {
  157. struct ast_datastore *datastore = data;
  158. struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
  159. struct ast_device_state_message *dev_state;
  160. char eid_str[20];
  161. struct ast_json *json;
  162. char *text;
  163. struct ast_sip_body body = {
  164. .type = "application",
  165. .subtype = "json",
  166. };
  167. if (!stasis_subscription_is_subscribed(sub) || ast_device_state_message_type() != stasis_message_type(msg)) {
  168. return;
  169. }
  170. dev_state = stasis_message_data(msg);
  171. if (!dev_state->eid || ast_eid_cmp(&ast_eid_default, dev_state->eid)) {
  172. /* If the event is aggregate or didn't originate from this server, don't send it out. */
  173. return;
  174. }
  175. if (publisher_state->device_state_filter && regexec(&publisher_state->device_state_regex, dev_state->device, 0, NULL, 0)) {
  176. /* Outgoing device state has been filtered and the device name does not match */
  177. return;
  178. }
  179. ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
  180. json = ast_json_pack(
  181. "{ s: s, s: s, s: s, s: i, s:s }",
  182. "type", "devicestate",
  183. "device", dev_state->device,
  184. "state", ast_devstate_str(dev_state->state),
  185. "cachable", dev_state->cachable,
  186. "eid", eid_str);
  187. if (!json) {
  188. return;
  189. }
  190. text = ast_json_dump_string(json);
  191. if (!text) {
  192. ast_json_unref(json);
  193. return;
  194. }
  195. body.body_text = text;
  196. ast_sip_publish_client_send(publisher_state->client, &body);
  197. ast_json_free(text);
  198. ast_json_unref(json);
  199. }
  200. /*!
  201. * \brief Callback function for mailbox state events
  202. * \param ast_event
  203. * \param data void pointer to ast_client structure
  204. * \return void
  205. */
  206. static void asterisk_publisher_mwistate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
  207. {
  208. struct ast_datastore *datastore = data;
  209. struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
  210. struct ast_mwi_state *mwi_state;
  211. char eid_str[20];
  212. struct ast_json *json;
  213. char *text;
  214. struct ast_sip_body body = {
  215. .type = "application",
  216. .subtype = "json",
  217. };
  218. if (!stasis_subscription_is_subscribed(sub) || ast_mwi_state_type() != stasis_message_type(msg)) {
  219. return;
  220. }
  221. mwi_state = stasis_message_data(msg);
  222. if (ast_eid_cmp(&ast_eid_default, &mwi_state->eid)) {
  223. /* If the event is aggregate or didn't originate from this server, don't send it out. */
  224. return;
  225. }
  226. if (publisher_state->mailbox_state_filter && regexec(&publisher_state->mailbox_state_regex, mwi_state->uniqueid, 0, NULL, 0)) {
  227. /* Outgoing mailbox state has been filtered and the uniqueid does not match */
  228. return;
  229. }
  230. ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
  231. json = ast_json_pack(
  232. "{ s: s, s: s, s: i, s: i, s:s }",
  233. "type", "mailboxstate",
  234. "uniqueid", mwi_state->uniqueid,
  235. "old", mwi_state->old_msgs,
  236. "new", mwi_state->new_msgs,
  237. "eid", eid_str);
  238. if (!json) {
  239. return;
  240. }
  241. text = ast_json_dump_string(json);
  242. if (!text) {
  243. ast_json_unref(json);
  244. return;
  245. }
  246. body.body_text = text;
  247. ast_sip_publish_client_send(publisher_state->client, &body);
  248. ast_json_free(text);
  249. ast_json_unref(json);
  250. }
  251. static int cached_devstate_cb(void *obj, void *arg, int flags)
  252. {
  253. struct stasis_message *msg = obj;
  254. struct ast_datastore *datastore = arg;
  255. struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
  256. asterisk_publisher_devstate_cb(arg, publisher_state->device_state_subscription, msg);
  257. return 0;
  258. }
  259. static int cached_mwistate_cb(void *obj, void *arg, int flags)
  260. {
  261. struct stasis_message *msg = obj;
  262. struct ast_datastore *datastore = arg;
  263. struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
  264. asterisk_publisher_mwistate_cb(arg, publisher_state->mailbox_state_subscription, msg);
  265. return 0;
  266. }
  267. static int build_regex(regex_t *regex, const char *text)
  268. {
  269. int res;
  270. if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
  271. size_t len = regerror(res, regex, NULL, 0);
  272. char buf[len];
  273. regerror(res, regex, buf, len);
  274. ast_log(LOG_ERROR, "Could not compile regex '%s': %s\n", text, buf);
  275. return -1;
  276. }
  277. return 0;
  278. }
  279. static int asterisk_start_devicestate_publishing(struct ast_sip_outbound_publish *configuration,
  280. struct ast_sip_outbound_publish_client *client)
  281. {
  282. RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
  283. struct asterisk_devicestate_publisher_state *publisher_state;
  284. const char *value;
  285. struct ao2_container *cached;
  286. datastore = ast_sip_publish_client_alloc_datastore(&asterisk_devicestate_publisher_state_datastore,
  287. "asterisk-devicestate-publisher");
  288. if (!datastore) {
  289. return -1;
  290. }
  291. publisher_state = ast_calloc(1, sizeof(struct asterisk_devicestate_publisher_state));
  292. if (!publisher_state) {
  293. return -1;
  294. }
  295. datastore->data = publisher_state;
  296. value = ast_sorcery_object_get_extended(configuration, "device_state_filter");
  297. if (!ast_strlen_zero(value)) {
  298. if (build_regex(&publisher_state->device_state_regex, value)) {
  299. return -1;
  300. }
  301. publisher_state->device_state_filter = 1;
  302. }
  303. publisher_state->client = ao2_bump(client);
  304. if (ast_sip_publish_client_add_datastore(client, datastore)) {
  305. return -1;
  306. }
  307. publisher_state->device_state_subscription = stasis_subscribe(ast_device_state_topic_all(),
  308. asterisk_publisher_devstate_cb, ao2_bump(datastore));
  309. if (!publisher_state->device_state_subscription) {
  310. ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
  311. ao2_ref(datastore, -1);
  312. return -1;
  313. }
  314. stasis_subscription_accept_message_type(publisher_state->device_state_subscription, ast_device_state_message_type());
  315. stasis_subscription_accept_message_type(publisher_state->device_state_subscription, stasis_subscription_change_type());
  316. stasis_subscription_set_filter(publisher_state->device_state_subscription, STASIS_SUBSCRIPTION_FILTER_SELECTIVE);
  317. cached = stasis_cache_dump(ast_device_state_cache(), NULL);
  318. ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
  319. ao2_ref(cached, -1);
  320. return 0;
  321. }
  322. static int asterisk_stop_devicestate_publishing(struct ast_sip_outbound_publish_client *client)
  323. {
  324. RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher"),
  325. ao2_cleanup);
  326. struct asterisk_devicestate_publisher_state *publisher_state;
  327. if (!datastore) {
  328. return 0;
  329. }
  330. publisher_state = datastore->data;
  331. if (publisher_state->device_state_subscription) {
  332. stasis_unsubscribe_and_join(publisher_state->device_state_subscription);
  333. ao2_ref(datastore, -1);
  334. }
  335. ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
  336. return 0;
  337. }
  338. struct ast_sip_event_publisher_handler asterisk_devicestate_publisher_handler = {
  339. .event_name = "asterisk-devicestate",
  340. .start_publishing = asterisk_start_devicestate_publishing,
  341. .stop_publishing = asterisk_stop_devicestate_publishing,
  342. };
  343. static int asterisk_start_mwi_publishing(struct ast_sip_outbound_publish *configuration,
  344. struct ast_sip_outbound_publish_client *client)
  345. {
  346. RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
  347. struct asterisk_mwi_publisher_state *publisher_state;
  348. const char *value;
  349. struct ao2_container *cached;
  350. datastore = ast_sip_publish_client_alloc_datastore(&asterisk_mwi_publisher_state_datastore, "asterisk-mwi-publisher");
  351. if (!datastore) {
  352. return -1;
  353. }
  354. publisher_state = ast_calloc(1, sizeof(struct asterisk_mwi_publisher_state));
  355. if (!publisher_state) {
  356. return -1;
  357. }
  358. datastore->data = publisher_state;
  359. value = ast_sorcery_object_get_extended(configuration, "mailbox_state_filter");
  360. if (!ast_strlen_zero(value)) {
  361. if (build_regex(&publisher_state->mailbox_state_regex, value)) {
  362. return -1;
  363. }
  364. publisher_state->mailbox_state_filter = 1;
  365. }
  366. publisher_state->client = ao2_bump(client);
  367. if (ast_sip_publish_client_add_datastore(client, datastore)) {
  368. return -1;
  369. }
  370. publisher_state->mailbox_state_subscription = stasis_subscribe(ast_mwi_topic_all(),
  371. asterisk_publisher_mwistate_cb, ao2_bump(datastore));
  372. if (!publisher_state->mailbox_state_subscription) {
  373. ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
  374. ao2_ref(datastore, -1);
  375. return -1;
  376. }
  377. stasis_subscription_accept_message_type(publisher_state->mailbox_state_subscription, ast_mwi_state_type());
  378. stasis_subscription_accept_message_type(publisher_state->mailbox_state_subscription, stasis_subscription_change_type());
  379. stasis_subscription_set_filter(publisher_state->mailbox_state_subscription, STASIS_SUBSCRIPTION_FILTER_SELECTIVE);
  380. cached = stasis_cache_dump(ast_mwi_state_cache(), NULL);
  381. ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
  382. ao2_ref(cached, -1);
  383. return 0;
  384. }
  385. static int asterisk_stop_mwi_publishing(struct ast_sip_outbound_publish_client *client)
  386. {
  387. RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher"),
  388. ao2_cleanup);
  389. struct asterisk_mwi_publisher_state *publisher_state;
  390. if (!datastore) {
  391. return 0;
  392. }
  393. publisher_state = datastore->data;
  394. if (publisher_state->mailbox_state_subscription) {
  395. stasis_unsubscribe_and_join(publisher_state->mailbox_state_subscription);
  396. ao2_ref(datastore, -1);
  397. }
  398. ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
  399. return 0;
  400. }
  401. struct ast_sip_event_publisher_handler asterisk_mwi_publisher_handler = {
  402. .event_name = "asterisk-mwi",
  403. .start_publishing = asterisk_start_mwi_publishing,
  404. .stop_publishing = asterisk_stop_mwi_publishing,
  405. };
  406. static int asterisk_publication_new(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
  407. {
  408. RAII_VAR(struct asterisk_publication_config *, config, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "asterisk-publication",
  409. event_configuration), ao2_cleanup);
  410. /* If no inbound Asterisk publication configuration exists reject the PUBLISH */
  411. if (!config) {
  412. return 404;
  413. }
  414. return 200;
  415. }
  416. static int asterisk_publication_devicestate(struct ast_sip_publication *pub, struct asterisk_publication_config *config,
  417. struct ast_eid *pubsub_eid, struct ast_json *json)
  418. {
  419. const char *device = ast_json_string_get(ast_json_object_get(json, "device"));
  420. const char *state = ast_json_string_get(ast_json_object_get(json, "state"));
  421. int cachable = ast_json_integer_get(ast_json_object_get(json, "cachable"));
  422. if (!config->device_state) {
  423. ast_debug(2, "Received device state event for resource '%s' but it is not configured to accept them\n",
  424. ast_sorcery_object_get_id(config));
  425. return 0;
  426. }
  427. if (ast_strlen_zero(device) || ast_strlen_zero(state)) {
  428. ast_debug(1, "Received incomplete device state event for resource '%s'\n",
  429. ast_sorcery_object_get_id(config));
  430. return -1;
  431. }
  432. if (config->device_state_filter && regexec(&config->device_state_regex, device, 0, NULL, 0)) {
  433. ast_debug(2, "Received device state on resource '%s' for device '%s' but it has been filtered out\n",
  434. ast_sorcery_object_get_id(config), device);
  435. return 0;
  436. }
  437. ast_publish_device_state_full(device, ast_devstate_val(state),
  438. cachable == AST_DEVSTATE_CACHABLE ? AST_DEVSTATE_CACHABLE : AST_DEVSTATE_NOT_CACHABLE,
  439. pubsub_eid);
  440. return 0;
  441. }
  442. static int asterisk_publication_mailboxstate(struct ast_sip_publication *pub, struct asterisk_publication_config *config,
  443. struct ast_eid *pubsub_eid, struct ast_json *json)
  444. {
  445. const char *uniqueid = ast_json_string_get(ast_json_object_get(json, "uniqueid"));
  446. int old_msgs = ast_json_integer_get(ast_json_object_get(json, "old"));
  447. int new_msgs = ast_json_integer_get(ast_json_object_get(json, "new"));
  448. char *item_id;
  449. const char *mailbox;
  450. if (!config->mailbox_state) {
  451. ast_debug(2, "Received mailbox state event for resource '%s' but it is not configured to accept them\n",
  452. ast_sorcery_object_get_id(config));
  453. return 0;
  454. }
  455. if (ast_strlen_zero(uniqueid)) {
  456. ast_debug(1, "Received incomplete mailbox state event for resource '%s'\n",
  457. ast_sorcery_object_get_id(config));
  458. return -1;
  459. }
  460. if (config->mailbox_state_filter && regexec(&config->mailbox_state_regex, uniqueid, 0, NULL, 0)) {
  461. ast_debug(2, "Received mailbox state on resource '%s' for uniqueid '%s' but it has been filtered out\n",
  462. ast_sorcery_object_get_id(config), uniqueid);
  463. return 0;
  464. }
  465. item_id = ast_strdupa(uniqueid);
  466. mailbox = strsep(&item_id, "@");
  467. ast_publish_mwi_state_full(mailbox, item_id, new_msgs, old_msgs, NULL, pubsub_eid);
  468. return 0;
  469. }
  470. static int asterisk_publication_devicestate_refresh(struct ast_sip_publication *pub,
  471. struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
  472. {
  473. struct ast_sip_outbound_publish_client *client;
  474. struct ast_datastore *datastore;
  475. struct ao2_container *cached;
  476. if (ast_strlen_zero(config->devicestate_publish)) {
  477. return 0;
  478. }
  479. client = ast_sip_publish_client_get(config->devicestate_publish);
  480. if (!client) {
  481. ast_log(LOG_ERROR, "Received refresh request for devicestate on publication '%s' but publish '%s' is not available\n",
  482. ast_sorcery_object_get_id(config), config->devicestate_publish);
  483. return 0;
  484. }
  485. datastore = ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher");
  486. if (!datastore) {
  487. ao2_ref(client, -1);
  488. return 0;
  489. }
  490. cached = stasis_cache_dump(ast_device_state_cache(), NULL);
  491. if (cached) {
  492. ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
  493. ao2_ref(cached, -1);
  494. }
  495. ao2_ref(client, -1);
  496. ao2_ref(datastore, -1);
  497. return 0;
  498. }
  499. static int asterisk_publication_devicestate_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
  500. enum ast_sip_publish_state state)
  501. {
  502. RAII_VAR(struct asterisk_publication_config *, config, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "asterisk-publication",
  503. ast_sip_publication_get_event_configuration(pub)), ao2_cleanup);
  504. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  505. const char *eid, *type;
  506. struct ast_eid pubsub_eid;
  507. int res = -1;
  508. /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
  509. if (!config) {
  510. return -1;
  511. }
  512. /* If no body exists this is a refresh and can be ignored */
  513. if (!body) {
  514. return 0;
  515. }
  516. /* We only accept JSON for content */
  517. if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
  518. ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
  519. ast_sorcery_object_get_id(config));
  520. return -1;
  521. }
  522. json = ast_json_load_buf(body->data, body->len, NULL);
  523. if (!json) {
  524. ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
  525. ast_sorcery_object_get_id(config));
  526. return -1;
  527. }
  528. eid = ast_json_string_get(ast_json_object_get(json, "eid"));
  529. if (!eid) {
  530. ast_debug(1, "Received event without eid for resource '%s'\n",
  531. ast_sorcery_object_get_id(config));
  532. return -1;
  533. }
  534. ast_str_to_eid(&pubsub_eid, eid);
  535. type = ast_json_string_get(ast_json_object_get(json, "type"));
  536. if (!type) {
  537. ast_debug(1, "Received event without type for resource '%s'\n",
  538. ast_sorcery_object_get_id(config));
  539. return -1;
  540. } else if (!strcmp(type, "devicestate")) {
  541. res = asterisk_publication_devicestate(pub, config, &pubsub_eid, json);
  542. } else if (!strcmp(type, "refresh")) {
  543. res = asterisk_publication_devicestate_refresh(pub, config, &pubsub_eid, json);
  544. }
  545. return res;
  546. }
  547. static int asterisk_publication_mwi_refresh(struct ast_sip_publication *pub,
  548. struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
  549. {
  550. struct ast_sip_outbound_publish_client *client;
  551. struct ast_datastore *datastore;
  552. struct ao2_container *cached;
  553. if (ast_strlen_zero(config->mailboxstate_publish)) {
  554. return 0;
  555. }
  556. client = ast_sip_publish_client_get(config->mailboxstate_publish);
  557. if (!client) {
  558. ast_log(LOG_ERROR, "Received refresh request for mwi state on publication '%s' but publish '%s' is not available\n",
  559. ast_sorcery_object_get_id(config), config->mailboxstate_publish);
  560. return 0;
  561. }
  562. datastore = ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher");
  563. if (!datastore) {
  564. ao2_ref(client, -1);
  565. return 0;
  566. }
  567. cached = stasis_cache_dump(ast_mwi_state_cache(), NULL);
  568. if (cached) {
  569. ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
  570. ao2_ref(cached, -1);
  571. }
  572. ao2_ref(client, -1);
  573. ao2_ref(datastore, -1);
  574. return 0;
  575. }
  576. static int asterisk_publication_mwi_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
  577. enum ast_sip_publish_state state)
  578. {
  579. RAII_VAR(struct asterisk_publication_config *, config, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "asterisk-publication",
  580. ast_sip_publication_get_event_configuration(pub)), ao2_cleanup);
  581. RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
  582. const char *eid, *type;
  583. struct ast_eid pubsub_eid;
  584. int res = -1;
  585. /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
  586. if (!config) {
  587. return -1;
  588. }
  589. /* If no body exists this is a refresh and can be ignored */
  590. if (!body) {
  591. return 0;
  592. }
  593. /* We only accept JSON for content */
  594. if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
  595. ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
  596. ast_sorcery_object_get_id(config));
  597. return -1;
  598. }
  599. json = ast_json_load_buf(body->data, body->len, NULL);
  600. if (!json) {
  601. ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
  602. ast_sorcery_object_get_id(config));
  603. return -1;
  604. }
  605. eid = ast_json_string_get(ast_json_object_get(json, "eid"));
  606. if (!eid) {
  607. ast_debug(1, "Received event without eid for resource '%s'\n",
  608. ast_sorcery_object_get_id(config));
  609. return -1;
  610. }
  611. ast_str_to_eid(&pubsub_eid, eid);
  612. type = ast_json_string_get(ast_json_object_get(json, "type"));
  613. if (!type) {
  614. ast_debug(1, "Received event without type for resource '%s'\n",
  615. ast_sorcery_object_get_id(config));
  616. return -1;
  617. } else if (!strcmp(type, "mailboxstate")) {
  618. res = asterisk_publication_mailboxstate(pub, config, &pubsub_eid, json);
  619. } else if (!strcmp(type, "refresh")) {
  620. res = asterisk_publication_mwi_refresh(pub, config, &pubsub_eid, json);
  621. }
  622. return res;
  623. }
  624. static int send_refresh_cb(void *obj, void *arg, int flags)
  625. {
  626. struct asterisk_publication_config *config = obj;
  627. struct ast_sip_outbound_publish_client *client;
  628. if (!ast_strlen_zero(config->devicestate_publish)) {
  629. client = ast_sip_publish_client_get(config->devicestate_publish);
  630. if (client) {
  631. ast_sip_publish_client_send(client, arg);
  632. ao2_ref(client, -1);
  633. }
  634. }
  635. if (!ast_strlen_zero(config->mailboxstate_publish)) {
  636. client = ast_sip_publish_client_get(config->mailboxstate_publish);
  637. if (client) {
  638. ast_sip_publish_client_send(client, arg);
  639. ao2_ref(client, -1);
  640. }
  641. }
  642. return 0;
  643. }
  644. /*! \brief Internal function to send refresh requests to all publications */
  645. static void asterisk_publication_send_refresh(void)
  646. {
  647. struct ao2_container *publications = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "asterisk-publication", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
  648. char eid_str[20];
  649. struct ast_json *json;
  650. char *text;
  651. struct ast_sip_body body = {
  652. .type = "application",
  653. .subtype = "json",
  654. };
  655. if (!publications) {
  656. return;
  657. }
  658. ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
  659. json = ast_json_pack(
  660. "{ s: s, s: s }",
  661. "type", "refresh",
  662. "eid", eid_str);
  663. if (!json) {
  664. ao2_ref(publications, -1);
  665. return;
  666. }
  667. text = ast_json_dump_string(json);
  668. if (!text) {
  669. ast_json_unref(json);
  670. ao2_ref(publications, -1);
  671. return;
  672. }
  673. body.body_text = text;
  674. ao2_callback(publications, OBJ_NODATA, send_refresh_cb, &body);
  675. ast_json_free(text);
  676. ast_json_unref(json);
  677. ao2_ref(publications, -1);
  678. }
  679. struct ast_sip_publish_handler asterisk_devicestate_publication_handler = {
  680. .event_name = "asterisk-devicestate",
  681. .new_publication = asterisk_publication_new,
  682. .publication_state_change = asterisk_publication_devicestate_state_change,
  683. };
  684. struct ast_sip_publish_handler asterisk_mwi_publication_handler = {
  685. .event_name = "asterisk-mwi",
  686. .new_publication = asterisk_publication_new,
  687. .publication_state_change = asterisk_publication_mwi_state_change,
  688. };
  689. /*! \brief Destructor function for Asterisk publication configuration */
  690. static void asterisk_publication_config_destroy(void *obj)
  691. {
  692. struct asterisk_publication_config *config = obj;
  693. ast_string_field_free_memory(config);
  694. }
  695. /*! \brief Allocator function for Asterisk publication configuration */
  696. static void *asterisk_publication_config_alloc(const char *name)
  697. {
  698. struct asterisk_publication_config *config = ast_sorcery_generic_alloc(sizeof(*config),
  699. asterisk_publication_config_destroy);
  700. if (!config || ast_string_field_init(config, 256)) {
  701. ao2_cleanup(config);
  702. return NULL;
  703. }
  704. return config;
  705. }
  706. static int regex_filter_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
  707. {
  708. struct asterisk_publication_config *config = obj;
  709. int res = -1;
  710. if (ast_strlen_zero(var->value)) {
  711. return 0;
  712. }
  713. if (!strcmp(var->name, "device_state_filter")) {
  714. if (!(res = build_regex(&config->device_state_regex, var->value))) {
  715. config->device_state_filter = 1;
  716. }
  717. } else if (!strcmp(var->name, "mailbox_state_filter")) {
  718. if (!(res = build_regex(&config->mailbox_state_regex, var->value))) {
  719. config->mailbox_state_filter = 1;
  720. }
  721. }
  722. return res;
  723. }
  724. static int load_module(void)
  725. {
  726. CHECK_PJSIP_PUBSUB_MODULE_LOADED();
  727. if (ast_eid_is_empty(&ast_eid_default)) {
  728. ast_log(LOG_ERROR, "Entity ID is not set.\n");
  729. return AST_MODULE_LOAD_DECLINE;
  730. }
  731. ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_publish_asterisk");
  732. ast_sorcery_apply_default(ast_sip_get_sorcery(), "asterisk-publication", "config", "pjsip.conf,criteria=type=asterisk-publication");
  733. if (ast_sorcery_object_register(ast_sip_get_sorcery(), "asterisk-publication", asterisk_publication_config_alloc, NULL, NULL)) {
  734. ast_log(LOG_ERROR, "Unable to register 'asterisk-publication' type with sorcery\n");
  735. return AST_MODULE_LOAD_DECLINE;
  736. }
  737. ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "type", "", OPT_NOOP_T, 0, 0);
  738. ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "devicestate_publish", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct asterisk_publication_config, devicestate_publish));
  739. ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "mailboxstate_publish", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct asterisk_publication_config, mailboxstate_publish));
  740. ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "device_state", "no", OPT_BOOL_T, 1, FLDSET(struct asterisk_publication_config, device_state));
  741. ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "device_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
  742. ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "mailbox_state", "no", OPT_BOOL_T, 1, FLDSET(struct asterisk_publication_config, mailbox_state));
  743. ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "mailbox_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
  744. ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
  745. if (ast_sip_register_publish_handler(&asterisk_devicestate_publication_handler)) {
  746. ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
  747. asterisk_devicestate_publication_handler.event_name);
  748. return AST_MODULE_LOAD_DECLINE;
  749. }
  750. if (ast_sip_register_publish_handler(&asterisk_mwi_publication_handler)) {
  751. ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
  752. asterisk_mwi_publication_handler.event_name);
  753. ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
  754. return AST_MODULE_LOAD_DECLINE;
  755. }
  756. if (ast_sip_register_event_publisher_handler(&asterisk_devicestate_publisher_handler)) {
  757. ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
  758. asterisk_devicestate_publisher_handler.event_name);
  759. ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
  760. ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
  761. return AST_MODULE_LOAD_DECLINE;
  762. }
  763. if (ast_sip_register_event_publisher_handler(&asterisk_mwi_publisher_handler)) {
  764. ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
  765. asterisk_mwi_publisher_handler.event_name);
  766. ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler);
  767. ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
  768. ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
  769. return AST_MODULE_LOAD_DECLINE;
  770. }
  771. asterisk_publication_send_refresh();
  772. return AST_MODULE_LOAD_SUCCESS;
  773. }
  774. static int reload_module(void)
  775. {
  776. ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
  777. asterisk_publication_send_refresh();
  778. return 0;
  779. }
  780. static int unload_module(void)
  781. {
  782. ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
  783. ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
  784. ast_sip_unregister_event_publisher_handler(&asterisk_devicestate_publisher_handler);
  785. ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler);
  786. ast_sorcery_object_unregister(ast_sip_get_sorcery(), "asterisk-publication");
  787. return 0;
  788. }
  789. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Asterisk Event PUBLISH Support",
  790. .support_level = AST_MODULE_SUPPORT_CORE,
  791. .load = load_module,
  792. .reload = reload_module,
  793. .unload = unload_module,
  794. .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
  795. );