res_pjsip_publish_asterisk.c 30 KB

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