diff --git a/CHANGES b/CHANGES index 3554d45cf750a9cc6c4f716f873e848da80cd00d..cbde5635361163b323d18911fd365633db33b5e1 100644 --- a/CHANGES +++ b/CHANGES @@ -74,6 +74,13 @@ Core changes affect the core console verbosity, 'remote set verbose' will now set a separate level for each remote console without affecting any other console. +Dialplan functions +------------------ + * Addition of the VM_INFO function that can be used to retrieve voicemail + user information, such as the email address and full name. + The MAILBOX_EXISTS dialplan function has been deprecated in favour of + VM_INFO. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 1.8 to Asterisk 10 ------------------- ------------------------------------------------------------------------------ diff --git a/UPGRADE.txt b/UPGRADE.txt index ac88afddf9c4f05d1e06d60f1921ace40143f36f..ee804275be505be7a627257ed7adf5cb0a949e7f 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -22,6 +22,10 @@ From 10 to 11: +Dialplan Functions: + - MAILBOX_EXISTS has been deprecated. Use VM_INFO with the 'exists' parameter + instead. + func_enum: - ENUM query functions now return a count of -1 on lookup error to differentiate between a failed query and a successful query with 0 results diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index ed94bc21a269998ba9c2766d4f5c319e9ef66826..a62a39b87305b11e5478f3555424d0de90809a95 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -284,6 +284,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </parameter> </syntax> <description> + <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note> <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail <replaceable>context</replaceable> is specified, the <literal>default</literal> context will be used.</para> @@ -297,6 +298,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </variable> </variablelist> </description> + <see-also> + <ref type="function">VM_INFO</ref> + </see-also> </application> <application name="VMAuthenticate" language="en_US"> <synopsis> @@ -354,10 +358,67 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <parameter name="context" /> </syntax> <description> + <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note> <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists. If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal> context.</para> </description> + <see-also> + <ref type="function">VM_INFO</ref> + </see-also> + </function> + <function name="VM_INFO" language="en_US"> + <synopsis> + Returns the selected attribute from a mailbox. + </synopsis> + <syntax argsep=","> + <parameter name="mailbox" argsep="@" required="true"> + <argument name="mailbox" required="true" /> + <argument name="context" /> + </parameter> + <parameter name="attribute" required="true"> + <optionlist> + <option name="count"> + <para>Count of messages in specified <replaceable>folder</replaceable>. + If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para> + </option> + <option name="email"> + <para>E-mail address associated with the mailbox.</para> + </option> + <option name="exists"> + <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para> + </option> + <option name="fullname"> + <para>Full name associated with the mailbox.</para> + </option> + <option name="language"> + <para>Mailbox language if overridden, otherwise the language of the channel.</para> + </option> + <option name="locale"> + <para>Mailbox locale if overridden, otherwise global locale.</para> + </option> + <option name="pager"> + <para>Pager e-mail address associated with the mailbox.</para> + </option> + <option name="password"> + <para>Mailbox access password.</para> + </option> + <option name="tz"> + <para>Mailbox timezone if overridden, otherwise global timezone</para> + </option> + </optionlist> + </parameter> + <parameter name="folder" required="false"> + <para>If not specified, <literal>INBOX</literal> is assumed.</para> + </parameter> + </syntax> + <description> + <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>. + If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal> + context. Where the <replaceable>folder</replaceable> can be specified, common folders + include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>, + <literal>Family</literal> and <literal>Friends</literal>.</para> + </description> </function> <manager name="VoicemailUsersList" language="en_US"> <synopsis> @@ -5223,11 +5284,15 @@ static int messagecount(const char *context, const char *mailbox, const char *fo char sql[PATH_MAX]; char rowdata[20]; struct generic_prepare_struct gps = { .sql = sql, .argc = 0 }; - if (!folder) - folder = "INBOX"; + /* If no mailbox, return immediately */ - if (ast_strlen_zero(mailbox)) + if (ast_strlen_zero(mailbox)) { return 0; + } + + if (ast_strlen_zero(folder)) { + folder = "INBOX"; + } obj = ast_odbc_request_obj(odbc_database, 0); if (obj) { @@ -10975,7 +11040,7 @@ static int vm_box_exists(struct ast_channel *chan, const char *data) if (!dep_warning) { dep_warning = 1; - ast_log(AST_LOG_WARNING, "MailboxExists is deprecated. Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data); + ast_log(AST_LOG_WARNING, "MailboxExists is deprecated. Please use ${VM_INFO(%s,exists)} instead.\n", data); } box = ast_strdupa(data); @@ -11005,6 +11070,7 @@ static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *a AST_APP_ARG(mbox); AST_APP_ARG(context); ); + static int dep_warning = 0; AST_NONSTANDARD_APP_ARGS(arg, args, '@'); @@ -11013,15 +11079,99 @@ static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *a return -1; } + if (!dep_warning) { + dep_warning = 1; + ast_log(AST_LOG_WARNING, "MAILBOX_EXISTS is deprecated. Please use ${VM_INFO(%s,exists)} instead.\n", args); + } + ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len); return 0; } +static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len) +{ + struct ast_vm_user *vmu = NULL; + char *tmp, *mailbox, *context, *parse; + int res = 0; + + AST_DECLARE_APP_ARGS(arg, + AST_APP_ARG(mailbox_context); + AST_APP_ARG(attribute); + AST_APP_ARG(folder); + ); + + buf[0] = '\0'; + + if (ast_strlen_zero(args)) { + ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n"); + return -1; + } + + parse = ast_strdupa(args); + AST_STANDARD_APP_ARGS(arg, parse); + + if (ast_strlen_zero(arg.mailbox_context) || ast_strlen_zero(arg.attribute)) { + ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n"); + return -1; + } + + tmp = ast_strdupa(arg.mailbox_context); + mailbox = strsep(&tmp, "@"); + context = strsep(&tmp, ""); + + if (ast_strlen_zero(context)) { + context = "default"; + } + + vmu = find_user(NULL, context, mailbox); + + if (!strncasecmp(arg.attribute, "exists", 5)) { + ast_copy_string(buf, vmu ? "1" : "0", len); + return 0; + } + + if (vmu) { + if (!strncasecmp(arg.attribute, "password", 8)) { + ast_copy_string(buf, vmu->password, len); + } else if (!strncasecmp(arg.attribute, "fullname", 8)) { + ast_copy_string(buf, vmu->fullname, len); + } else if (!strncasecmp(arg.attribute, "email", 5)) { + ast_copy_string(buf, vmu->email, len); + } else if (!strncasecmp(arg.attribute, "pager", 5)) { + ast_copy_string(buf, vmu->pager, len); + } else if (!strncasecmp(arg.attribute, "language", 8)) { + ast_copy_string(buf, S_OR(vmu->language, chan->language), len); + } else if (!strncasecmp(arg.attribute, "locale", 6)) { + ast_copy_string(buf, vmu->locale, len); + } else if (!strncasecmp(arg.attribute, "tz", 2)) { + ast_copy_string(buf, vmu->zonetag, len); + } else if (!strncasecmp(arg.attribute, "count", 5)) { + /* If mbxfolder is empty messagecount will default to INBOX */ + res = messagecount(context, mailbox, arg.folder); + if (res < 0) { + ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context); + return -1; + } + snprintf(buf, len, "%d", res); + } else { + ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute); + return -1; + } + } + + return 0; +} + static struct ast_custom_function mailbox_exists_acf = { .name = "MAILBOX_EXISTS", .read = acf_mailbox_exists, }; +static struct ast_custom_function vm_info_acf = { + .name = "VM_INFO", + .read = acf_vm_info, +}; + static int vmauthenticate(struct ast_channel *chan, const char *data) { char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = ""; @@ -13018,6 +13168,88 @@ cleanup: return res; } +AST_TEST_DEFINE(test_voicemail_vm_info) +{ + struct ast_vm_user *vmu; + struct ast_channel *chan = NULL; + const char testcontext[] = "test"; + const char testmailbox[] = "00000000"; + const char vminfo_cmd[] = "VM_INFO"; + char vminfo_buf[256], vminfo_args[256]; + int res = AST_TEST_PASS; + int test_ret = 0; + int test_counter = 0; + + struct { + char *vminfo_test_args; + char *vminfo_expected; + int vminfo_ret; + } test_items[] = { + { "", "", -1 }, /* Missing argument */ + { "00000000@test,badparam", "", -1 }, /* Wrong argument */ + { "00000000@test", "", -1 }, /* Missing argument */ + { "00000000@test,exists", "1", 0 }, + { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */ + { "00000000@test,email", "vm-info-test@example.net", 0 }, + { "11111111@test,email", "", 0 }, /* Invalid mailbox */ + { "00000000@test,fullname", "Test Framework Mailbox", 0 }, + { "00000000@test,pager", "vm-info-pager-test@example.net", 0 }, + { "00000000@test,locale", "en_US", 0 }, + { "00000000@test,tz", "central", 0 }, + { "00000000@test,language", "en", 0 }, + { "00000000@test,password", "9876", 0 }, + }; + + switch (cmd) { + case TEST_INIT: + info->name = "test_voicemail_vm_info"; + info->category = "/apps/app_voicemail/"; + info->summary = "VM_INFO unit test"; + info->description = + "This tests passing various parameters to VM_INFO"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + if (!(chan = ast_dummy_channel_alloc())) { + ast_test_status_update(test, "Unable to create dummy channel\n"); + return AST_TEST_FAIL; + } + + if (!(vmu = find_user(NULL, testcontext, testmailbox)) && + !(vmu = find_or_create(testcontext, testmailbox))) { + ast_test_status_update(test, "Cannot create vmu structure\n"); + chan = ast_channel_unref(chan); + return AST_TEST_FAIL; + } + + populate_defaults(vmu); + + ast_copy_string(vmu->email, "vm-info-test@example.net", sizeof(vmu->email)); + ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname)); + ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager)); + ast_copy_string(vmu->language, "en", sizeof(vmu->language)); + ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag)); + ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag)); + ast_copy_string(vmu->password, "9876", sizeof(vmu->password)); + + for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) { + ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args)); + test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf)); + if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) { + ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected); + res = AST_TEST_FAIL; + } + if (!(test_ret == test_items[test_counter].vminfo_ret)) { + ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret); + res = AST_TEST_FAIL; + } + } + + chan = ast_channel_unref(chan); + return res; +} #endif /* defined(TEST_FRAMEWORK) */ static int reload(void) @@ -13035,6 +13267,7 @@ static int unload_module(void) res |= ast_unregister_application(app4); res |= ast_unregister_application(sayname_app); res |= ast_custom_function_unregister(&mailbox_exists_acf); + res |= ast_custom_function_unregister(&vm_info_acf); res |= ast_manager_unregister("VoicemailUsersList"); res |= ast_data_unregister(NULL); #ifdef TEST_FRAMEWORK @@ -13043,6 +13276,7 @@ static int unload_module(void) res |= AST_TEST_UNREGISTER(test_voicemail_vmuser); res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl); res |= AST_TEST_UNREGISTER(test_voicemail_load_config); + res |= AST_TEST_UNREGISTER(test_voicemail_vm_info); #endif ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail)); ast_uninstall_vm_functions(); @@ -13086,6 +13320,7 @@ static int load_module(void) res |= ast_register_application_xml(app4, vmauthenticate); res |= ast_register_application_xml(sayname_app, vmsayname_exec); res |= ast_custom_function_register(&mailbox_exists_acf); + res |= ast_custom_function_register(&vm_info_acf); res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users); #ifdef TEST_FRAMEWORK res |= AST_TEST_REGISTER(test_voicemail_vmsayname); @@ -13093,6 +13328,7 @@ static int load_module(void) res |= AST_TEST_REGISTER(test_voicemail_vmuser); res |= AST_TEST_REGISTER(test_voicemail_notify_endl); res |= AST_TEST_REGISTER(test_voicemail_load_config); + res |= AST_TEST_REGISTER(test_voicemail_vm_info); #endif if (res)