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