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