res_mwi_external.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Richard Mudgett <rmudgett@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*!
  19. * \file
  20. * \brief Core external MWI support.
  21. *
  22. * \details
  23. * The module manages the persistent message counts cache and supplies
  24. * an API to allow the protocol specific modules to control the counts
  25. * or a subset.
  26. *
  27. * \author Richard Mudgett <rmudgett@digium.com>
  28. *
  29. * See Also:
  30. * \arg \ref AstCREDITS
  31. */
  32. /*** MODULEINFO
  33. <defaultenabled>no</defaultenabled>
  34. <support_level>core</support_level>
  35. ***/
  36. /*** DOCUMENTATION
  37. <configInfo name="res_mwi_external" language="en_US">
  38. <synopsis>Core external MWI support</synopsis>
  39. <configFile name="sorcery.conf">
  40. <configObject name="mailboxes">
  41. <synopsis>Persistent cache of external MWI Mailboxs.</synopsis>
  42. <description>
  43. <para>Allows the alteration of sorcery backend mapping for
  44. the persistent cache of external MWI mailboxes.</para>
  45. </description>
  46. </configObject>
  47. </configFile>
  48. </configInfo>
  49. ***/
  50. #include "asterisk.h"
  51. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  52. #include "asterisk/app.h"
  53. #include "asterisk/module.h"
  54. #include "asterisk/res_mwi_external.h"
  55. #include "asterisk/sorcery.h"
  56. #include "asterisk/cli.h"
  57. /* ------------------------------------------------------------------- */
  58. /*!
  59. * Define to include CLI commands to manipulate the external MWI mailboxes.
  60. * Useful for testing the module functionality.
  61. */
  62. //#define MWI_DEBUG_CLI 1
  63. #define MWI_ASTDB_PREFIX "mwi_external"
  64. #define MWI_MAILBOX_TYPE "mailboxes"
  65. struct ast_mwi_mailbox_object {
  66. SORCERY_OBJECT(details);
  67. /*! Number of new messages in mailbox. */
  68. unsigned int msgs_new;
  69. /*! Number of old messages in mailbox. */
  70. unsigned int msgs_old;
  71. };
  72. static struct ast_sorcery *mwi_sorcery;
  73. void ast_mwi_external_ref(void)
  74. {
  75. ast_module_ref(ast_module_info->self);
  76. }
  77. void ast_mwi_external_unref(void)
  78. {
  79. ast_module_unref(ast_module_info->self);
  80. }
  81. /*!
  82. * \internal
  83. * \brief Post an update event to the MWI counts.
  84. * \since 12.1.0
  85. *
  86. * \return Nothing
  87. */
  88. static void mwi_post_event(const struct ast_mwi_mailbox_object *mailbox)
  89. {
  90. ast_publish_mwi_state(ast_sorcery_object_get_id(mailbox), NULL,
  91. mailbox->msgs_new, mailbox->msgs_old);
  92. }
  93. static void mwi_observe_update(const void *obj)
  94. {
  95. mwi_post_event(obj);
  96. }
  97. /*!
  98. * \internal
  99. * \brief Post a count clearing event to the MWI counts.
  100. * \since 12.1.0
  101. *
  102. * \return Nothing
  103. */
  104. static void mwi_observe_delete(const void *obj)
  105. {
  106. const struct ast_mwi_mailbox_object *mailbox = obj;
  107. if (mailbox->msgs_new || mailbox->msgs_old) {
  108. /* Post a count clearing event. */
  109. ast_publish_mwi_state(ast_sorcery_object_get_id(mailbox), NULL, 0, 0);
  110. }
  111. /* Post a cache remove event. */
  112. ast_delete_mwi_state(ast_sorcery_object_get_id(mailbox), NULL);
  113. }
  114. static const struct ast_sorcery_observer mwi_observers = {
  115. .created = mwi_observe_update,
  116. .updated = mwi_observe_update,
  117. .deleted = mwi_observe_delete,
  118. };
  119. /*! \brief Internal function to allocate a mwi object */
  120. static void *mwi_sorcery_object_alloc(const char *id)
  121. {
  122. return ast_sorcery_generic_alloc(sizeof(struct ast_mwi_mailbox_object), NULL);
  123. }
  124. /*!
  125. * \internal
  126. * \brief Initialize sorcery for external MWI.
  127. * \since 12.1.0
  128. *
  129. * \retval 0 on success.
  130. * \retval -1 on error.
  131. */
  132. static int mwi_sorcery_init(void)
  133. {
  134. int res;
  135. mwi_sorcery = ast_sorcery_open();
  136. if (!mwi_sorcery) {
  137. ast_log(LOG_ERROR, "MWI external: Sorcery failed to open.\n");
  138. return -1;
  139. }
  140. /* Map the external MWI wizards. */
  141. if (ast_sorcery_apply_default(mwi_sorcery, MWI_MAILBOX_TYPE, "astdb",
  142. MWI_ASTDB_PREFIX) == AST_SORCERY_APPLY_FAIL) {
  143. ast_log(LOG_ERROR, "MWI external: Sorcery could not setup wizards.\n");
  144. return -1;
  145. }
  146. res = ast_sorcery_object_register(mwi_sorcery, MWI_MAILBOX_TYPE,
  147. mwi_sorcery_object_alloc, NULL, NULL);
  148. if (res) {
  149. ast_log(LOG_ERROR, "MWI external: Sorcery could not register object type '%s'.\n",
  150. MWI_MAILBOX_TYPE);
  151. return -1;
  152. }
  153. /* Define the MWI_MAILBOX_TYPE object fields. */
  154. res |= ast_sorcery_object_field_register_nodoc(mwi_sorcery, MWI_MAILBOX_TYPE,
  155. "msgs_new", "0", OPT_UINT_T, 0, FLDSET(struct ast_mwi_mailbox_object, msgs_new));
  156. res |= ast_sorcery_object_field_register_nodoc(mwi_sorcery, MWI_MAILBOX_TYPE,
  157. "msgs_old", "0", OPT_UINT_T, 0, FLDSET(struct ast_mwi_mailbox_object, msgs_old));
  158. return res ? -1 : 0;
  159. }
  160. struct ao2_container *ast_mwi_mailbox_get_all(void)
  161. {
  162. return ast_sorcery_retrieve_by_fields(mwi_sorcery, MWI_MAILBOX_TYPE,
  163. AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
  164. }
  165. struct ao2_container *ast_mwi_mailbox_get_by_regex(const char *regex)
  166. {
  167. return ast_sorcery_retrieve_by_regex(mwi_sorcery, MWI_MAILBOX_TYPE, regex ?: "");
  168. }
  169. const struct ast_mwi_mailbox_object *ast_mwi_mailbox_get(const char *mailbox_id)
  170. {
  171. if (ast_strlen_zero(mailbox_id)) {
  172. return NULL;
  173. }
  174. return ast_sorcery_retrieve_by_id(mwi_sorcery, MWI_MAILBOX_TYPE, mailbox_id);
  175. }
  176. struct ast_mwi_mailbox_object *ast_mwi_mailbox_alloc(const char *mailbox_id)
  177. {
  178. if (ast_strlen_zero(mailbox_id)) {
  179. return NULL;
  180. }
  181. return ast_sorcery_alloc(mwi_sorcery, MWI_MAILBOX_TYPE, mailbox_id);
  182. }
  183. struct ast_mwi_mailbox_object *ast_mwi_mailbox_copy(const struct ast_mwi_mailbox_object *mailbox)
  184. {
  185. return ast_sorcery_copy(mwi_sorcery, mailbox);
  186. }
  187. const char *ast_mwi_mailbox_get_id(const struct ast_mwi_mailbox_object *mailbox)
  188. {
  189. return ast_sorcery_object_get_id(mailbox);
  190. }
  191. unsigned int ast_mwi_mailbox_get_msgs_new(const struct ast_mwi_mailbox_object *mailbox)
  192. {
  193. return mailbox->msgs_new;
  194. }
  195. unsigned int ast_mwi_mailbox_get_msgs_old(const struct ast_mwi_mailbox_object *mailbox)
  196. {
  197. return mailbox->msgs_old;
  198. }
  199. void ast_mwi_mailbox_set_msgs_new(struct ast_mwi_mailbox_object *mailbox, unsigned int num_msgs)
  200. {
  201. mailbox->msgs_new = num_msgs;
  202. }
  203. void ast_mwi_mailbox_set_msgs_old(struct ast_mwi_mailbox_object *mailbox, unsigned int num_msgs)
  204. {
  205. mailbox->msgs_old = num_msgs;
  206. }
  207. int ast_mwi_mailbox_update(struct ast_mwi_mailbox_object *mailbox)
  208. {
  209. const struct ast_mwi_mailbox_object *exists;
  210. int res;
  211. exists = ast_sorcery_retrieve_by_id(mwi_sorcery, MWI_MAILBOX_TYPE,
  212. ast_sorcery_object_get_id(mailbox));
  213. if (exists) {
  214. res = ast_sorcery_update(mwi_sorcery, mailbox);
  215. ast_mwi_mailbox_unref(exists);
  216. } else {
  217. res = ast_sorcery_create(mwi_sorcery, mailbox);
  218. }
  219. return res;
  220. }
  221. /*!
  222. * \internal
  223. * \brief Delete a mailbox.
  224. * \since 12.1.0
  225. *
  226. * \param mailbox Mailbox object to delete from sorcery.
  227. *
  228. * \return Nothing
  229. */
  230. static void mwi_mailbox_delete(struct ast_mwi_mailbox_object *mailbox)
  231. {
  232. ast_sorcery_delete(mwi_sorcery, mailbox);
  233. }
  234. /*!
  235. * \internal
  236. * \brief Delete all mailboxes in container.
  237. * \since 12.1.0
  238. *
  239. * \param mailboxes Mailbox objects to delete from sorcery.
  240. *
  241. * \return Nothing
  242. */
  243. static void mwi_mailbox_delete_all(struct ao2_container *mailboxes)
  244. {
  245. struct ast_mwi_mailbox_object *mailbox;
  246. struct ao2_iterator iter;
  247. iter = ao2_iterator_init(mailboxes, AO2_ITERATOR_UNLINK);
  248. for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) {
  249. mwi_mailbox_delete(mailbox);
  250. }
  251. ao2_iterator_destroy(&iter);
  252. }
  253. int ast_mwi_mailbox_delete_all(void)
  254. {
  255. struct ao2_container *mailboxes;
  256. mailboxes = ast_mwi_mailbox_get_all();
  257. if (mailboxes) {
  258. mwi_mailbox_delete_all(mailboxes);
  259. ao2_ref(mailboxes, -1);
  260. }
  261. return 0;
  262. }
  263. int ast_mwi_mailbox_delete_by_regex(const char *regex)
  264. {
  265. struct ao2_container *mailboxes;
  266. mailboxes = ast_mwi_mailbox_get_by_regex(regex);
  267. if (mailboxes) {
  268. mwi_mailbox_delete_all(mailboxes);
  269. ao2_ref(mailboxes, -1);
  270. }
  271. return 0;
  272. }
  273. int ast_mwi_mailbox_delete(const char *mailbox_id)
  274. {
  275. const struct ast_mwi_mailbox_object *mailbox;
  276. if (ast_strlen_zero(mailbox_id)) {
  277. return -1;
  278. }
  279. mailbox = ast_mwi_mailbox_get(mailbox_id);
  280. if (mailbox) {
  281. mwi_mailbox_delete((struct ast_mwi_mailbox_object *) mailbox);
  282. ast_mwi_mailbox_unref(mailbox);
  283. }
  284. return 0;
  285. }
  286. enum folder_map {
  287. FOLDER_INVALID = 0,
  288. FOLDER_INBOX = 1,
  289. FOLDER_OLD = 2,
  290. };
  291. /*!
  292. * \internal
  293. * \brief Determine if the requested folder is valid for external MWI support.
  294. * \since 12.1.0
  295. *
  296. * \param folder Folder name to check (NULL is valid).
  297. *
  298. * \return Enum of the supported folder.
  299. */
  300. static enum folder_map mwi_folder_map(const char *folder)
  301. {
  302. enum folder_map which_folder;
  303. if (ast_strlen_zero(folder) || !strcasecmp(folder, "INBOX")) {
  304. which_folder = FOLDER_INBOX;
  305. } else if (!strcasecmp(folder, "Old")) {
  306. which_folder = FOLDER_OLD;
  307. } else {
  308. which_folder = FOLDER_INVALID;
  309. }
  310. return which_folder;
  311. }
  312. /*!
  313. * \internal
  314. * \brief Gets the number of messages that exist in a mailbox folder.
  315. * \since 12.1.0
  316. *
  317. * \param mailbox_id The mailbox name.
  318. * \param folder The folder to look in. Default is INBOX if not provided.
  319. *
  320. * \return The number of messages in the mailbox folder (zero or more).
  321. */
  322. static int mwi_messagecount(const char *mailbox_id, const char *folder)
  323. {
  324. const struct ast_mwi_mailbox_object *mailbox;
  325. int num_msgs;
  326. enum folder_map which_folder;
  327. which_folder = mwi_folder_map(folder);
  328. if (which_folder == FOLDER_INVALID) {
  329. return 0;
  330. }
  331. mailbox = ast_mwi_mailbox_get(mailbox_id);
  332. if (!mailbox) {
  333. return 0;
  334. }
  335. num_msgs = 0;
  336. switch (which_folder) {
  337. case FOLDER_INVALID:
  338. break;
  339. case FOLDER_INBOX:
  340. num_msgs = mailbox->msgs_new;
  341. break;
  342. case FOLDER_OLD:
  343. num_msgs = mailbox->msgs_old;
  344. break;
  345. }
  346. ast_mwi_mailbox_unref(mailbox);
  347. return num_msgs;
  348. }
  349. /*!
  350. * \internal
  351. * \brief Determines if the given folder has messages.
  352. * \since 12.1.0
  353. *
  354. * \param mailboxes Comma or & delimited list of mailboxes.
  355. * \param folder The folder to look in. Default is INBOX if not provided.
  356. *
  357. * \retval 1 if the folder has one or more messages.
  358. * \retval 0 otherwise.
  359. */
  360. static int mwi_has_voicemail(const char *mailboxes, const char *folder)
  361. {
  362. char *parse;
  363. char *mailbox_id;
  364. enum folder_map which_folder;
  365. which_folder = mwi_folder_map(folder);
  366. if (which_folder == FOLDER_INVALID) {
  367. return 0;
  368. }
  369. /* For each mailbox in the list. */
  370. parse = ast_strdupa(mailboxes);
  371. while ((mailbox_id = strsep(&parse, ",&"))) {
  372. const struct ast_mwi_mailbox_object *mailbox;
  373. int num_msgs;
  374. /* Get the specified mailbox. */
  375. mailbox = ast_mwi_mailbox_get(mailbox_id);
  376. if (!mailbox) {
  377. continue;
  378. }
  379. /* Done if the found mailbox has any messages. */
  380. num_msgs = 0;
  381. switch (which_folder) {
  382. case FOLDER_INVALID:
  383. break;
  384. case FOLDER_INBOX:
  385. num_msgs = mailbox->msgs_new;
  386. break;
  387. case FOLDER_OLD:
  388. num_msgs = mailbox->msgs_old;
  389. break;
  390. }
  391. ast_mwi_mailbox_unref(mailbox);
  392. if (num_msgs) {
  393. return 1;
  394. }
  395. }
  396. return 0;
  397. }
  398. /*!
  399. * \internal
  400. * \brief Gets the number of messages that exist for the mailbox list.
  401. * \since 12.1.0
  402. *
  403. * \param mailboxes Comma or space delimited list of mailboxes.
  404. * \param newmsgs Where to put the count of new messages. (Can be NULL)
  405. * \param oldmsgs Where to put the count of old messages. (Can be NULL)
  406. *
  407. * \details
  408. * Simultaneously determines the count of new and old
  409. * messages. The total messages would then be the sum of these.
  410. *
  411. * \retval 0 on success
  412. * \retval -1 on failure
  413. */
  414. static int mwi_inboxcount(const char *mailboxes, int *newmsgs, int *oldmsgs)
  415. {
  416. char *parse;
  417. char *mailbox_id;
  418. if (!newmsgs && !oldmsgs) {
  419. /* Nowhere to accumulate counts */
  420. return 0;
  421. }
  422. /* For each mailbox in the list. */
  423. parse = ast_strdupa(mailboxes);
  424. while ((mailbox_id = strsep(&parse, ", "))) {
  425. const struct ast_mwi_mailbox_object *mailbox;
  426. /* Get the specified mailbox. */
  427. mailbox = ast_mwi_mailbox_get(mailbox_id);
  428. if (!mailbox) {
  429. continue;
  430. }
  431. /* Accumulate the counts. */
  432. if (newmsgs) {
  433. *newmsgs += mailbox->msgs_new;
  434. }
  435. if (oldmsgs) {
  436. *oldmsgs += mailbox->msgs_old;
  437. }
  438. ast_mwi_mailbox_unref(mailbox);
  439. }
  440. return 0;
  441. }
  442. /*!
  443. * \internal
  444. * \brief Gets the number of messages that exist for the mailbox list.
  445. * \since 12.1.0
  446. *
  447. * \param mailboxes Comma or space delimited list of mailboxes.
  448. * \param urgentmsgs Where to put the count of urgent messages. (Can be NULL)
  449. * \param newmsgs Where to put the count of new messages. (Can be NULL)
  450. * \param oldmsgs Where to put the count of old messages. (Can be NULL)
  451. *
  452. * \details
  453. * Simultaneously determines the count of new, old, and urgent
  454. * messages. The total messages would then be the sum of these
  455. * three.
  456. *
  457. * \retval 0 on success
  458. * \retval -1 on failure
  459. */
  460. static int mwi_inboxcount2(const char *mailboxes, int *urgentmsgs, int *newmsgs, int *oldmsgs)
  461. {
  462. /*
  463. * This module does not support urgentmsgs. Just ignore them.
  464. * The global API call has already set the count to zero.
  465. */
  466. return mwi_inboxcount(mailboxes, newmsgs, oldmsgs);
  467. }
  468. static const struct ast_vm_functions vm_table = {
  469. .module_version = VM_MODULE_VERSION,
  470. .module_name = AST_MODULE,
  471. .has_voicemail = mwi_has_voicemail,
  472. .inboxcount = mwi_inboxcount,
  473. .inboxcount2 = mwi_inboxcount2,
  474. .messagecount = mwi_messagecount,
  475. };
  476. #if defined(MWI_DEBUG_CLI)
  477. static char *complete_mailbox(const char *word, int state)
  478. {
  479. struct ao2_iterator iter;
  480. int wordlen = strlen(word);
  481. int which = 0;
  482. char *ret = NULL;
  483. char *regex;
  484. const struct ast_mwi_mailbox_object *mailbox;
  485. RAII_VAR(struct ao2_container *, mailboxes, NULL, ao2_cleanup);
  486. regex = ast_alloca(2 + wordlen);
  487. sprintf(regex, "^%s", word);/* Safe */
  488. mailboxes = ast_mwi_mailbox_get_by_regex(regex);
  489. if (!mailboxes) {
  490. return NULL;
  491. }
  492. iter = ao2_iterator_init(mailboxes, 0);
  493. for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) {
  494. if (++which > state) {
  495. ret = ast_strdup(ast_sorcery_object_get_id(mailbox));
  496. ast_mwi_mailbox_unref(mailbox);
  497. break;
  498. }
  499. }
  500. ao2_iterator_destroy(&iter);
  501. return ret;
  502. }
  503. #endif /* defined(MWI_DEBUG_CLI) */
  504. #if defined(MWI_DEBUG_CLI)
  505. static char *handle_mwi_delete_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  506. {
  507. switch (cmd) {
  508. case CLI_INIT:
  509. e->command = "mwi delete all";
  510. e->usage =
  511. "Usage: mwi delete all\n"
  512. " Delete all external MWI mailboxes.\n";
  513. return NULL;
  514. case CLI_GENERATE:
  515. return NULL;
  516. }
  517. ast_mwi_mailbox_delete_all();
  518. ast_cli(a->fd, "Deleted all external MWI mailboxes.\n");
  519. return CLI_SUCCESS;
  520. }
  521. #endif /* defined(MWI_DEBUG_CLI) */
  522. #if defined(MWI_DEBUG_CLI)
  523. static char *handle_mwi_delete_like(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  524. {
  525. const char *regex;
  526. switch (cmd) {
  527. case CLI_INIT:
  528. e->command = "mwi delete like";
  529. e->usage =
  530. "Usage: mwi delete like <pattern>\n"
  531. " Delete external MWI mailboxes matching a regular expression.\n";
  532. return NULL;
  533. case CLI_GENERATE:
  534. return NULL;
  535. }
  536. if (a->argc != 4) {
  537. return CLI_SHOWUSAGE;
  538. }
  539. regex = a->argv[3];
  540. ast_mwi_mailbox_delete_by_regex(regex);
  541. ast_cli(a->fd, "Deleted external MWI mailboxes matching '%s'.\n", regex);
  542. return CLI_SUCCESS;
  543. }
  544. #endif /* defined(MWI_DEBUG_CLI) */
  545. #if defined(MWI_DEBUG_CLI)
  546. static char *handle_mwi_delete_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  547. {
  548. const char *mailbox_id;
  549. switch (cmd) {
  550. case CLI_INIT:
  551. e->command = "mwi delete mailbox";
  552. e->usage =
  553. "Usage: mwi delete mailbox <mailbox_id>\n"
  554. " Delete a specific external MWI mailbox.\n";
  555. return NULL;
  556. case CLI_GENERATE:
  557. if (a->pos == 3) {
  558. return complete_mailbox(a->word, a->n);
  559. }
  560. return NULL;
  561. }
  562. if (a->argc != 4) {
  563. return CLI_SHOWUSAGE;
  564. }
  565. mailbox_id = a->argv[3];
  566. ast_mwi_mailbox_delete(mailbox_id);
  567. ast_cli(a->fd, "Deleted external MWI mailbox '%s'.\n", mailbox_id);
  568. return CLI_SUCCESS;
  569. }
  570. #endif /* defined(MWI_DEBUG_CLI) */
  571. #define FORMAT_MAILBOX_HDR "%6s %6s %s\n"
  572. #define FORMAT_MAILBOX_ROW "%6u %6u %s\n"
  573. #if defined(MWI_DEBUG_CLI)
  574. /*!
  575. * \internal
  576. * \brief Print a mailbox list line to CLI.
  577. * \since 12.1.0
  578. *
  579. * \param cli_fd File descriptor for CLI output.
  580. * \param mailbox What to list.
  581. *
  582. * \return Nothing
  583. */
  584. static void mwi_cli_print_mailbox(int cli_fd, const struct ast_mwi_mailbox_object *mailbox)
  585. {
  586. ast_cli(cli_fd, FORMAT_MAILBOX_ROW, mailbox->msgs_new, mailbox->msgs_old,
  587. ast_sorcery_object_get_id(mailbox));
  588. }
  589. #endif /* defined(MWI_DEBUG_CLI) */
  590. #if defined(MWI_DEBUG_CLI)
  591. /*!
  592. * \internal
  593. * \brief List all mailboxes in the given container.
  594. * \since 12.1.0
  595. *
  596. * \param cli_fd File descriptor for CLI output.
  597. * \param mailboxes What to list.
  598. *
  599. * \return Nothing
  600. */
  601. static void mwi_cli_list_mailboxes(int cli_fd, struct ao2_container *mailboxes)
  602. {
  603. struct ao2_iterator iter;
  604. const struct ast_mwi_mailbox_object *mailbox;
  605. ast_cli(cli_fd, FORMAT_MAILBOX_HDR, "New", "Old", "Mailbox");
  606. iter = ao2_iterator_init(mailboxes, 0);
  607. for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) {
  608. mwi_cli_print_mailbox(cli_fd, mailbox);
  609. }
  610. ao2_iterator_destroy(&iter);
  611. }
  612. #endif /* defined(MWI_DEBUG_CLI) */
  613. #undef FORMAT_MAILBOX_HDR
  614. #undef FORMAT_MAILBOX_ROW
  615. #if defined(MWI_DEBUG_CLI)
  616. static char *handle_mwi_list_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  617. {
  618. struct ao2_container *mailboxes;
  619. switch (cmd) {
  620. case CLI_INIT:
  621. e->command = "mwi list all";
  622. e->usage =
  623. "Usage: mwi list all\n"
  624. " List all external MWI mailboxes.\n";
  625. return NULL;
  626. case CLI_GENERATE:
  627. return NULL;
  628. }
  629. mailboxes = ast_mwi_mailbox_get_all();
  630. if (!mailboxes) {
  631. ast_cli(a->fd, "Failed to retrieve external MWI mailboxes.\n");
  632. return CLI_SUCCESS;
  633. }
  634. mwi_cli_list_mailboxes(a->fd, mailboxes);
  635. ao2_ref(mailboxes, -1);
  636. return CLI_SUCCESS;
  637. }
  638. #endif /* defined(MWI_DEBUG_CLI) */
  639. #if defined(MWI_DEBUG_CLI)
  640. static char *handle_mwi_list_like(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  641. {
  642. struct ao2_container *mailboxes;
  643. const char *regex;
  644. switch (cmd) {
  645. case CLI_INIT:
  646. e->command = "mwi list like";
  647. e->usage =
  648. "Usage: mwi list like <pattern>\n"
  649. " List external MWI mailboxes matching a regular expression.\n";
  650. return NULL;
  651. case CLI_GENERATE:
  652. return NULL;
  653. }
  654. if (a->argc != 4) {
  655. return CLI_SHOWUSAGE;
  656. }
  657. regex = a->argv[3];
  658. mailboxes = ast_mwi_mailbox_get_by_regex(regex);
  659. if (!mailboxes) {
  660. ast_cli(a->fd, "Failed to retrieve external MWI mailboxes.\n");
  661. return CLI_SUCCESS;
  662. }
  663. mwi_cli_list_mailboxes(a->fd, mailboxes);
  664. ao2_ref(mailboxes, -1);
  665. return CLI_SUCCESS;
  666. }
  667. #endif /* defined(MWI_DEBUG_CLI) */
  668. #if defined(MWI_DEBUG_CLI)
  669. static char *handle_mwi_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  670. {
  671. const struct ast_mwi_mailbox_object *mailbox;
  672. const char *mailbox_id;
  673. switch (cmd) {
  674. case CLI_INIT:
  675. e->command = "mwi show mailbox";
  676. e->usage =
  677. "Usage: mwi show mailbox <mailbox_id>\n"
  678. " Show a specific external MWI mailbox.\n";
  679. return NULL;
  680. case CLI_GENERATE:
  681. if (a->pos == 3) {
  682. return complete_mailbox(a->word, a->n);
  683. }
  684. return NULL;
  685. }
  686. if (a->argc != 4) {
  687. return CLI_SHOWUSAGE;
  688. }
  689. mailbox_id = a->argv[3];
  690. mailbox = ast_mwi_mailbox_get(mailbox_id);
  691. if (mailbox) {
  692. ast_cli(a->fd,
  693. "Mailbox: %s\n"
  694. "NewMessages: %u\n"
  695. "OldMessages: %u\n",
  696. ast_sorcery_object_get_id(mailbox),
  697. mailbox->msgs_new,
  698. mailbox->msgs_old);
  699. ast_mwi_mailbox_unref(mailbox);
  700. } else {
  701. ast_cli(a->fd, "External MWI mailbox '%s' not found.\n", mailbox_id);
  702. }
  703. return CLI_SUCCESS;
  704. }
  705. #endif /* defined(MWI_DEBUG_CLI) */
  706. #if defined(MWI_DEBUG_CLI)
  707. static char *handle_mwi_update_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  708. {
  709. struct ast_mwi_mailbox_object *mailbox;
  710. const char *mailbox_id;
  711. unsigned int num_new;
  712. unsigned int num_old;
  713. switch (cmd) {
  714. case CLI_INIT:
  715. e->command = "mwi update mailbox";
  716. e->usage =
  717. "Usage: mwi update mailbox <mailbox_id> [<new> [<old>]]\n"
  718. " Update a specific external MWI mailbox.\n";
  719. return NULL;
  720. case CLI_GENERATE:
  721. if (a->pos == 3) {
  722. return complete_mailbox(a->word, a->n);
  723. }
  724. return NULL;
  725. }
  726. if (a->argc < 4 || 6 < a->argc) {
  727. return CLI_SHOWUSAGE;
  728. }
  729. mailbox_id = a->argv[3];
  730. num_new = 0;
  731. if (4 < a->argc) {
  732. const char *count_new = a->argv[4];
  733. if (sscanf(count_new, "%u", &num_new) != 1) {
  734. ast_cli(a->fd, "Invalid NewMessages: '%s'.\n", count_new);
  735. return CLI_SHOWUSAGE;
  736. }
  737. }
  738. num_old = 0;
  739. if (5 < a->argc) {
  740. const char *count_old = a->argv[5];
  741. if (sscanf(count_old, "%u", &num_old) != 1) {
  742. ast_cli(a->fd, "Invalid OldMessages: '%s'.\n", count_old);
  743. return CLI_SHOWUSAGE;
  744. }
  745. }
  746. mailbox = ast_mwi_mailbox_alloc(mailbox_id);
  747. if (mailbox) {
  748. ast_mwi_mailbox_set_msgs_new(mailbox, num_new);
  749. ast_mwi_mailbox_set_msgs_old(mailbox, num_old);
  750. if (ast_mwi_mailbox_update(mailbox)) {
  751. ast_cli(a->fd, "Could not update mailbox %s.\n",
  752. ast_sorcery_object_get_id(mailbox));
  753. } else {
  754. ast_cli(a->fd, "Updated mailbox %s.\n", ast_sorcery_object_get_id(mailbox));
  755. }
  756. ast_mwi_mailbox_unref(mailbox);
  757. }
  758. return CLI_SUCCESS;
  759. }
  760. #endif /* defined(MWI_DEBUG_CLI) */
  761. #if defined(MWI_DEBUG_CLI)
  762. static struct ast_cli_entry mwi_cli[] = {
  763. AST_CLI_DEFINE(handle_mwi_delete_all, "Delete all external MWI mailboxes"),
  764. AST_CLI_DEFINE(handle_mwi_delete_like, "Delete external MWI mailboxes matching regex"),
  765. AST_CLI_DEFINE(handle_mwi_delete_mailbox, "Delete a specific external MWI mailbox"),
  766. AST_CLI_DEFINE(handle_mwi_list_all, "List all external MWI mailboxes"),
  767. AST_CLI_DEFINE(handle_mwi_list_like, "List external MWI mailboxes matching regex"),
  768. AST_CLI_DEFINE(handle_mwi_show_mailbox, "Show a specific external MWI mailbox"),
  769. AST_CLI_DEFINE(handle_mwi_update_mailbox, "Update a specific external MWI mailbox"),
  770. };
  771. #endif /* defined(MWI_DEBUG_CLI) */
  772. /*!
  773. * \internal
  774. * \brief Post initial MWI count events.
  775. * \since 12.1.0
  776. *
  777. * \return Nothing
  778. */
  779. static void mwi_initial_events(void)
  780. {
  781. struct ao2_container *mailboxes;
  782. const struct ast_mwi_mailbox_object *mailbox;
  783. struct ao2_iterator iter;
  784. /* Get all mailbox counts. */
  785. mailboxes = ast_mwi_mailbox_get_all();
  786. if (!mailboxes) {
  787. return;
  788. }
  789. /* Post all mailbox counts. */
  790. iter = ao2_iterator_init(mailboxes, AO2_ITERATOR_UNLINK);
  791. for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) {
  792. mwi_post_event(mailbox);
  793. }
  794. ao2_iterator_destroy(&iter);
  795. ao2_ref(mailboxes, -1);
  796. }
  797. static int unload_module(void)
  798. {
  799. ast_vm_unregister(vm_table.module_name);
  800. #if defined(MWI_DEBUG_CLI)
  801. ast_cli_unregister_multiple(mwi_cli, ARRAY_LEN(mwi_cli));
  802. #endif /* defined(MWI_DEBUG_CLI) */
  803. ast_sorcery_observer_remove(mwi_sorcery, MWI_MAILBOX_TYPE, &mwi_observers);
  804. ast_sorcery_unref(mwi_sorcery);
  805. mwi_sorcery = NULL;
  806. return 0;
  807. }
  808. static int load_module(void)
  809. {
  810. int res;
  811. if (mwi_sorcery_init()
  812. || ast_sorcery_observer_add(mwi_sorcery, MWI_MAILBOX_TYPE, &mwi_observers)
  813. #if defined(MWI_DEBUG_CLI)
  814. || ast_cli_register_multiple(mwi_cli, ARRAY_LEN(mwi_cli))
  815. #endif /* defined(MWI_DEBUG_CLI) */
  816. ) {
  817. unload_module();
  818. return AST_MODULE_LOAD_DECLINE;
  819. }
  820. /* ast_vm_register may return DECLINE if another module registered for vm */
  821. res = ast_vm_register(&vm_table);
  822. if (res) {
  823. ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
  824. unload_module();
  825. return AST_MODULE_LOAD_DECLINE;
  826. }
  827. /* Post initial MWI count events. */
  828. mwi_initial_events();
  829. return AST_MODULE_LOAD_SUCCESS;
  830. }
  831. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Core external MWI resource",
  832. .support_level = AST_MODULE_SUPPORT_CORE,
  833. .load = load_module,
  834. .unload = unload_module,
  835. .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,
  836. );