Merge "app_voicemail: Add Mailbox Aliases"

This commit is contained in:
Joshua C. Colp
2019-01-24 05:56:34 -06:00
committed by Gerrit Code Review
3 changed files with 288 additions and 45 deletions

View File

@@ -93,6 +93,12 @@ Features
The previous behavior has been restored so both channels receive the
channel variable when one of these features is invoked.
app_voicemail
------------------
* You can now specify a special context with the "aliasescontext" parameter
in voicemail.conf which will allow you to create aliases for physical
mailboxes.
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 16.0.0 to Asterisk 16.1.0 ------------
------------------------------------------------------------------------------

View File

@@ -999,6 +999,7 @@ static int skipms;
static int maxlogins;
static int minpassword;
static int passwordlocation;
static char aliasescontext[MAX_VM_CONTEXT_LEN];
/*! Poll mailboxes for changes since there is something external to
* app_voicemail that may change them. */
@@ -1051,6 +1052,27 @@ static struct ast_taskprocessor *mwi_subscription_tps;
static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
struct alias_mailbox_mapping {
char *alias;
char *mailbox;
char buf[0];
};
struct mailbox_alias_mapping {
char *alias;
char *mailbox;
char buf[0];
};
#define MAPPING_BUCKETS 511
static struct ao2_container *alias_mailbox_mappings;
AO2_STRING_FIELD_HASH_FN(alias_mailbox_mapping, alias);
AO2_STRING_FIELD_CMP_FN(alias_mailbox_mapping, alias);
static struct ao2_container *mailbox_alias_mappings;
AO2_STRING_FIELD_HASH_FN(mailbox_alias_mapping, mailbox);
AO2_STRING_FIELD_CMP_FN(mailbox_alias_mapping, mailbox);
/* custom audio control prompts for voicemail playback */
static char listen_control_forward_key[12];
static char listen_control_reverse_key[12];
@@ -1765,9 +1787,31 @@ static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *contex
ast_set2_flag(vmu, !ivm, VM_ALLOCED);
AST_LIST_NEXT(vmu, list) = NULL;
}
} else
vmu = find_user_realtime(ivm, context, mailbox);
}
AST_LIST_UNLOCK(&users);
if (!vmu) {
vmu = find_user_realtime(ivm, context, mailbox);
}
if (!vmu && !ast_strlen_zero(aliasescontext)) {
struct alias_mailbox_mapping *mapping;
char *search_string = ast_alloca(MAX_VM_MAILBOX_LEN);
snprintf(search_string, MAX_VM_MAILBOX_LEN, "%s%s%s",
mailbox,
ast_strlen_zero(context) ? "" : "@",
S_OR(context, ""));
mapping = ao2_find(alias_mailbox_mappings, search_string, OBJ_SEARCH_KEY);
if (mapping) {
char *search_mailbox = NULL;
char *search_context = NULL;
separate_mailbox(ast_strdupa(mapping->mailbox), &search_mailbox, &search_context);
ao2_ref(mapping, -1);
vmu = find_user(ivm, search_mailbox, search_context);
}
}
return vmu;
}
@@ -6056,6 +6100,9 @@ static int __has_voicemail(const char *context, const char *mailbox, const char
struct dirent *de;
char fn[256];
int ret = 0;
struct alias_mailbox_mapping *mapping;
char *c;
char *m;
/* If no mailbox, return immediately */
if (ast_strlen_zero(mailbox))
@@ -6066,7 +6113,21 @@ static int __has_voicemail(const char *context, const char *mailbox, const char
if (ast_strlen_zero(context))
context = "default";
snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
c = (char *)context;
m = (char *)mailbox;
if (!ast_strlen_zero(aliasescontext)) {
char tmp[MAX_VM_MAILBOX_LEN];
snprintf(tmp, MAX_VM_MAILBOX_LEN, "%s@%s", mailbox, context);
mapping = ao2_find(alias_mailbox_mappings, tmp, OBJ_SEARCH_KEY);
if (mapping) {
separate_mailbox(ast_strdupa(mapping->mailbox), &m, &c);
ao2_ref(mapping, -1);
}
}
snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, c, m, folder);
if (!(dir = opendir(fn)))
return 0;
@@ -8096,7 +8157,24 @@ static void queue_mwi_event(const char *channel_id, const char *box, int urgent,
return;
}
ast_debug(3, "Queueing event for mailbox %s New: %d Old: %d\n", box, new + urgent, old);
ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
if (!ast_strlen_zero(aliasescontext)) {
struct ao2_iterator *aliases;
struct mailbox_alias_mapping *mapping;
aliases = ao2_find(mailbox_alias_mappings, box, OBJ_SEARCH_KEY | OBJ_MULTIPLE);
while ((mapping = ao2_iterator_next(aliases))) {
mailbox = NULL;
context = NULL;
ast_debug(3, "Found alias mapping: %s -> %s\n", mapping->alias, box);
separate_mailbox(ast_strdupa(mapping->alias), &mailbox, &context);
ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
ao2_ref(mapping, -1);
}
ao2_iterator_destroy(aliases);
}
}
/*!
@@ -13000,6 +13078,46 @@ static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struc
return res;
}
/*! \brief Show a list of voicemail zones in the CLI */
static char *handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_iterator aliases;
struct alias_mailbox_mapping *mapping;
#define ALIASES_OUTPUT_FORMAT "%-32s %-32s\n"
char *res = CLI_SUCCESS;
switch (cmd) {
case CLI_INIT:
e->command = "voicemail show aliases";
e->usage =
"Usage: voicemail show aliases\n"
" Lists mailbox aliases\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 3)
return CLI_SHOWUSAGE;
if (ast_strlen_zero(aliasescontext)) {
ast_cli(a->fd, "Aliases are not enabled\n");
return res;
}
ast_cli(a->fd, "Aliases context: %s\n", aliasescontext);
ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, "Alias", "Mailbox");
aliases = ao2_iterator_init(alias_mailbox_mappings, 0);
while ((mapping = ao2_iterator_next(&aliases))) {
ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, mapping->alias, mapping->mailbox);
ao2_ref(mapping, -1);
}
ao2_iterator_destroy(&aliases);
return res;
}
/*! \brief Reload voicemail configuration from the CLI */
static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -13026,6 +13144,7 @@ static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct as
static struct ast_cli_entry cli_voicemail[] = {
AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
AST_CLI_DEFINE(handle_voicemail_show_aliases, "List mailbox aliases"),
AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
};
@@ -13244,7 +13363,6 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct
if (stasis_message_type(msg) != stasis_subscription_change_type()) {
return;
}
change = stasis_message_data(msg);
if (change->topic == ast_mwi_topic_all()) {
return;
@@ -13662,11 +13780,98 @@ static int load_config_from_memory(int reload, struct ast_config *cfg, struct as
}
#endif
static struct alias_mailbox_mapping *alias_mailbox_mapping_create(const char *alias, const char *mailbox)
{
struct alias_mailbox_mapping *mapping;
size_t from_len = strlen(alias) + 1;
size_t to_len = strlen(mailbox) + 1;
mapping = ao2_alloc(sizeof(*mapping) + from_len + to_len, NULL);
if (!mapping) {
return NULL;
}
mapping->alias = mapping->buf;
mapping->mailbox = mapping->buf + from_len;
strcpy(mapping->alias, alias); /* Safe */
strcpy(mapping->mailbox, mailbox); /* Safe */
return mapping;
}
static void load_aliases(struct ast_config *cfg)
{
struct ast_variable *var;
if (ast_strlen_zero(aliasescontext)) {
return;
}
var = ast_variable_browse(cfg, aliasescontext);
while (var) {
struct alias_mailbox_mapping *mapping = alias_mailbox_mapping_create(var->name, var->value);
if (mapping) {
ao2_link(alias_mailbox_mappings, mapping);
ao2_link(mailbox_alias_mappings, mapping);
ao2_ref(mapping, -1);
}
var = var->next;
}
}
static void load_zonemessages(struct ast_config *cfg)
{
struct ast_variable *var;
var = ast_variable_browse(cfg, "zonemessages");
while (var) {
struct vm_zone *z;
char *msg_format, *tzone;
z = ast_malloc(sizeof(*z));
if (!z) {
return;
}
msg_format = ast_strdupa(var->value);
tzone = strsep(&msg_format, "|,");
if (msg_format) {
ast_copy_string(z->name, var->name, sizeof(z->name));
ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
AST_LIST_LOCK(&zones);
AST_LIST_INSERT_HEAD(&zones, z, list);
AST_LIST_UNLOCK(&zones);
} else {
ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
ast_free(z);
}
var = var->next;
}
}
static void load_users(struct ast_config *cfg)
{
struct ast_variable *var;
char *cat = NULL;
while ((cat = ast_category_browse(cfg, cat))) {
if (strcasecmp(cat, "general") == 0
|| strcasecmp(cat, aliasescontext) == 0
|| strcasecmp(cat, "zonemessages") == 0) {
continue;
}
var = ast_variable_browse(cfg, cat);
while (var) {
append_mailbox(cat, var->name, var->value);
var = var->next;
}
}
}
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
{
struct ast_vm_user *current;
char *cat;
struct ast_variable *var;
const char *val;
char *q, *stringp, *tmp;
int x;
@@ -13695,6 +13900,10 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
/* Free all the zones structure */
free_vm_zones();
/* Remove all aliases */
ao2_callback(alias_mailbox_mappings, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
ao2_callback(mailbox_alias_mappings, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
AST_LIST_LOCK(&users);
memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
@@ -13706,6 +13915,11 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
val = "default";
ast_copy_string(userscontext, val, sizeof(userscontext));
aliasescontext[0] = '\0';
val = ast_variable_retrieve(cfg, "general", "aliasescontext");
ast_copy_string(aliasescontext, S_OR(val, ""), sizeof(aliasescontext));
/* Attach voice message to mail message ? */
if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
val = "yes";
@@ -14307,45 +14521,16 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
}
/* load mailboxes from voicemail.conf */
cat = ast_category_browse(cfg, NULL);
while (cat) {
if (strcasecmp(cat, "general")) {
var = ast_variable_browse(cfg, cat);
if (strcasecmp(cat, "zonemessages")) {
/* Process mailboxes in this context */
while (var) {
append_mailbox(cat, var->name, var->value);
var = var->next;
}
} else {
/* Timezones in this context */
while (var) {
struct vm_zone *z;
if ((z = ast_malloc(sizeof(*z)))) {
char *msg_format, *tzone;
msg_format = ast_strdupa(var->value);
tzone = strsep(&msg_format, "|,");
if (msg_format) {
ast_copy_string(z->name, var->name, sizeof(z->name));
ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
AST_LIST_LOCK(&zones);
AST_LIST_INSERT_HEAD(&zones, z, list);
AST_LIST_UNLOCK(&zones);
} else {
ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
ast_free(z);
}
} else {
AST_LIST_UNLOCK(&users);
return -1;
}
var = var->next;
}
}
}
cat = ast_category_browse(cfg, cat);
}
/*
* Aliases must be loaded before users or the aliases won't be notified
* if there's existing voicemail in the user mailbox.
*/
load_aliases(cfg);
load_zonemessages(cfg);
load_users(cfg);
AST_LIST_UNLOCK(&users);
@@ -15096,6 +15281,16 @@ static int unload_module(void)
return res;
}
static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
{
struct alias_mailbox_mapping *mapping = v_obj;
if (!mapping) {
return;
}
prnt(where, "Alias: %s Mailbox: %s", mapping->alias, mapping->mailbox);
}
/*!
* \brief Load the module
*
@@ -15122,6 +15317,38 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
alias_mailbox_mappings = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAPPING_BUCKETS,
alias_mailbox_mapping_hash_fn, NULL, alias_mailbox_mapping_cmp_fn);
if (!alias_mailbox_mappings) {
ast_log(LOG_ERROR, "Unable to create alias_mailbox_mappings container\n");
ao2_cleanup(inprocess_container);
return AST_MODULE_LOAD_DECLINE;
}
res = ao2_container_register("voicemail_alias_mailbox_mappings", alias_mailbox_mappings, print_mappings);
if (res) {
ast_log(LOG_ERROR, "Unable to register alias_mailbox_mappings container\n");
ao2_cleanup(inprocess_container);
ao2_cleanup(alias_mailbox_mappings);
return AST_MODULE_LOAD_DECLINE;
}
mailbox_alias_mappings = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAPPING_BUCKETS,
mailbox_alias_mapping_hash_fn, NULL, mailbox_alias_mapping_cmp_fn);
if (!mailbox_alias_mappings) {
ast_log(LOG_ERROR, "Unable to create mailbox_alias_mappings container\n");
ao2_cleanup(inprocess_container);
ao2_cleanup(alias_mailbox_mappings);
return AST_MODULE_LOAD_DECLINE;
}
res = ao2_container_register("voicemail_mailbox_alias_mappings", mailbox_alias_mappings, print_mappings);
if (res) {
ast_log(LOG_ERROR, "Unable to register mailbox_alias_mappings container\n");
ao2_cleanup(inprocess_container);
ao2_cleanup(alias_mailbox_mappings);
ao2_cleanup(mailbox_alias_mappings);
return AST_MODULE_LOAD_DECLINE;
}
/* compute the location of the voicemail spool directory */
snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);

View File

@@ -73,6 +73,10 @@ maxlogins=3
;
;userscontext=default
;
; Aliases allow a mailbox to be referenced by an alias. The aliases are
; specified in the special context named here. There is no default.
;aliasescontext=myaliases
;
; If you need to have an external program, i.e. /usr/bin/myapp
; called when a voicemail is left, delivered, or your voicemailbox
; is checked, uncomment this.
@@ -233,7 +237,6 @@ pagerdateformat=%A, %B %d, %Y at %r
; Default: no
; -----------------------------------------------------------------------------
;
; Each mailbox is listed in the form <mailbox>=<password>,<name>,<email>,<pager_email>,<options>
; If email is specified, a message will be sent when a voicemail is received, to
@@ -451,6 +454,13 @@ european=Europe/Copenhagen|'vm-received' a d b 'digits/at' HM
;4110 => 3443,Rob Flynn,rflynn@blueridge.net
;4235 => 1234,Jim Holmes,jim@astricon.ips,,Tz=european
;
; Aliases allow alternate references to mailboxes. See the "aliasescontext"
; parameter in the "general" section.
;
[myaliases]
1234@devices => 1234@default
;6200@devices => 4200@default
;
; Mailboxes may be organized into multiple contexts for