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