res_mwi_external.c 23 KB

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