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