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