[asterisk-commits] mmichelson: branch 10-digiumphones r361748 - in /branches/10-digiumphones: ap...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Apr 9 16:32:21 CDT 2012
Author: mmichelson
Date: Mon Apr 9 16:32:17 2012
New Revision: 361748
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=361748
Log:
Fix bugs in voicemail APIs and add unit tests.
Added:
branches/10-digiumphones/tests/test_voicemail_api.c (with props)
Modified:
branches/10-digiumphones/apps/app_voicemail.c
branches/10-digiumphones/apps/app_voicemail.exports.in
branches/10-digiumphones/include/asterisk/app_voicemail.h
branches/10-digiumphones/main/manager.c
Modified: branches/10-digiumphones/apps/app_voicemail.c
URL: http://svnview.digium.com/svn/asterisk/branches/10-digiumphones/apps/app_voicemail.c?view=diff&rev=361748&r1=361747&r2=361748
==============================================================================
--- branches/10-digiumphones/apps/app_voicemail.c (original)
+++ branches/10-digiumphones/apps/app_voicemail.c Mon Apr 9 16:32:17 2012
@@ -2511,7 +2511,7 @@
*
* \return zero on success, -1 on error.
*/
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag)
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag, const char *dest_folder)
{
struct vm_state *sendvms = NULL, *destvms = NULL;
char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
@@ -2529,7 +2529,7 @@
}
snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
ast_mutex_lock(&sendvms->lock);
- if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
+ if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, dest_folder)) == T)) {
ast_mutex_unlock(&sendvms->lock);
return 0;
}
@@ -5336,7 +5336,7 @@
*
* \return zero on success, -1 on error.
*/
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag, const char *dest_folder)
{
char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
const char *frombox = mbox(vmu, imbox);
@@ -5348,6 +5348,8 @@
if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
userfolder = "Urgent";
+ } else if (!ast_strlen_zero(dest_folder)) {
+ userfolder = dest_folder;
} else {
userfolder = "INBOX";
}
@@ -6391,7 +6393,7 @@
cntx++;
}
if ((recip = find_user(&recipu, cntx, exten))) {
- copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
+ copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
free_user(recip);
}
}
@@ -7712,7 +7714,7 @@
vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
NULL, urgent_str);
#else
- copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
+ copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
#endif
saved_messages++;
AST_LIST_REMOVE_CURRENT(list);
@@ -14121,6 +14123,39 @@
return NULL;
}
+#ifdef TEST_FRAMEWORK
+
+int ast_vm_test_destroy_user(const char *context, const char *mailbox)
+{
+ struct ast_vm_user *vmu;
+
+ AST_LIST_LOCK(&users);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&users, vmu, list) {
+ if (!strncmp(context, vmu->context, sizeof(context))
+ && !strncmp(mailbox, vmu->mailbox, sizeof(mailbox))) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(vmu);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&users);
+ return 0;
+}
+
+int ast_vm_test_create_user(const char *context, const char *mailbox)
+{
+ struct ast_vm_user *vmu;
+
+ if (!(vmu = find_or_create(context, mailbox))) {
+ return -1;
+ }
+ populate_defaults(vmu);
+ return 0;
+}
+
+#endif
+
/*!
* \brief Create and store off all the msgs in an open mailbox
*
@@ -14255,6 +14290,11 @@
int inbox_index = 0;
int old_index = 1;
+ if (ast_strlen_zero(mailbox)) {
+ ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
+ return NULL;
+ }
+
memset(&vmus, 0, sizeof(vmus));
if (!(ast_strlen_zero(folder))) {
@@ -14386,6 +14426,51 @@
return str;
}
+/*!
+ * \brief common bounds checking and existence check for Voicemail API functions.
+ *
+ * \details
+ * This is called by ast_vm_msg_move, ast_vm_msg_remove, and ast_vm_msg_forward to
+ * ensure that data passed in are valid. This tests the following:
+ *
+ * 1. No negative indexes are given.
+ * 2. No index greater than the highest message index for the folder is given.
+ * 3. All message indexes given point to messages that exist.
+ *
+ * \param vms The voicemail state corresponding to an open mailbox
+ * \param msg_ids An array of message identifiers
+ * \param num_msgs The number of identifiers in msg_ids
+ *
+ * \retval -1 Failure
+ * \retval 0 Success
+ */
+static int message_range_and_existence_check(struct vm_state *vms, int *msg_ids, size_t num_msgs)
+{
+ int i;
+ int res = 0;
+ for (i = 0; i < num_msgs; ++i) {
+ int cur_msg = msg_ids[i];
+ if (cur_msg < 0) {
+ ast_log(LOG_WARNING, "Message has negative index\n");
+ res = -1;
+ break;
+ }
+ if (vms->lastmsg < cur_msg) {
+ ast_log(LOG_WARNING, "Message %d is out of range. Last message is %d\n", cur_msg, vms->lastmsg);
+ res = -1;
+ break;
+ }
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, cur_msg);
+ if (!EXISTS(vms->curdir, cur_msg, vms->fn, NULL)) {
+ ast_log(LOG_WARNING, "Message %d does not exist.\n", cur_msg);
+ res = -1;
+ break;
+ }
+ }
+
+ return res;
+}
+
static void notify_new_state(struct ast_vm_user *vmu)
{
int new = 0, old = 0, urgent = 0;
@@ -14413,17 +14498,36 @@
struct ast_config *msg_cfg;
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
char filename[PATH_MAX];
- int from_folder_index = get_folder_by_name(from_folder);
- int to_folder_index = get_folder_by_name(to_folder);
+ int from_folder_index;
int open = 0;
int res = 0;
int i;
+ if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
+ ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
+ return -1;
+ }
+
+ if (!num_msgs) {
+ ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
+ return -1;
+ }
+
+ if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
+ ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
+ return -1;
+ }
+
memset(&vmus, 0, sizeof(vmus));
memset(&to_vmus, 0, sizeof(to_vmus));
memset(&from_vms, 0, sizeof(from_vms));
- if (to_folder_index == -1 || from_folder_index == -1) {
+ from_folder_index = get_folder_by_name(from_folder);
+ if (from_folder_index == -1) {
+ return -1;
+ }
+
+ if (get_folder_by_name(to_folder) == -1) {
return -1;
}
@@ -14450,20 +14554,30 @@
open = 1;
+ if ((from_vms.lastmsg + 1) < num_msgs) {
+ ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
+ res = -1;
+ goto vm_forward_cleanup;
+ }
+
+ if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs) < 0)) {
+ goto vm_forward_cleanup;
+ }
+
+ /* Now we actually forward the messages */
for (i = 0; i < num_msgs; i++) {
int cur_msg = msg_ids[i];
int duration = 0;
const char *value;
- if (cur_msg >= 0 && from_vms.lastmsg < cur_msg) {
- /* msg does not exist */
- ast_log(LOG_WARNING, "msg %d does not exist to forward. Last msg is %d\n", cur_msg, from_vms.lastmsg);
- continue;
- }
make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
msg_cfg = ast_config_load(filename, config_flags);
+ /* XXX This likely will not fail since we previously ensured that the
+ * message we are looking for exists. However, there still could be some
+ * circumstance where this fails, so atomicity is not guaranteed.
+ */
if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
DISPOSE(from_vms.curdir, cur_msg);
continue;
@@ -14472,7 +14586,7 @@
duration = atoi(value);
}
- copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "");
+ copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
if (delete_old) {
from_vms.deleted[cur_msg] = 1;
@@ -14515,11 +14629,29 @@
{
struct vm_state vms;
struct ast_vm_user *vmu = NULL, vmus;
- int old_folder_index = get_folder_by_name(oldfolder);
- int new_folder_index = get_folder_by_name(newfolder);
+ int old_folder_index;
+ int new_folder_index;
int open = 0;
int res = 0;
int i;
+
+ if (ast_strlen_zero(mailbox)) {
+ ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
+ return -1;
+ }
+
+ if (!num_msgs) {
+ ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
+ return -1;
+ }
+
+ if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
+ ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
+ return -1;
+ }
+
+ old_folder_index = get_folder_by_name(oldfolder);
+ new_folder_index = get_folder_by_name(newfolder);
memset(&vmus, 0, sizeof(vmus));
memset(&vms, 0, sizeof(vms));
@@ -14545,13 +14677,13 @@
open = 1;
- for (i = 0; i < num_msgs; i++) {
- if (vms.lastmsg < old_msg_nums[i]) {
- /* msg does not exist */
- res = -1;
- goto vm_move_cleanup;
- }
- if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, (new_msg_nums + i))) {
+ if ((res = message_range_and_existence_check(&vms, old_msg_nums, num_msgs)) < 0) {
+ goto vm_move_cleanup;
+ }
+
+ /* Now actually move the message */
+ for (i = 0; i < num_msgs; ++i) {
+ if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, new_msg_nums ? (new_msg_nums + i) : NULL)) {
res = -1;
goto vm_move_cleanup;
}
@@ -14590,14 +14722,30 @@
{
struct vm_state vms;
struct ast_vm_user *vmu = NULL, vmus;
- int folder_index = get_folder_by_name(folder);
+ int folder_index;
int open = 0;
int res = 0;
int i;
+ if (ast_strlen_zero(mailbox)) {
+ ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
+ return -1;
+ }
+
+ if (!num_msgs) {
+ ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
+ return -1;
+ }
+
+ if (ast_strlen_zero(folder)) {
+ ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
+ return -1;
+ }
+
memset(&vmus, 0, sizeof(vmus));
memset(&vms, 0, sizeof(vms));
+ folder_index = get_folder_by_name(folder);
if (folder_index == -1) {
ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
return -1;
@@ -14621,13 +14769,17 @@
open = 1;
+ if ((vms.lastmsg + 1) < num_msgs) {
+ ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
+ res = -1;
+ goto vm_remove_cleanup;
+ }
+
+ if ((res = message_range_and_existence_check(&vms, msgs, num_msgs)) < 0) {
+ goto vm_remove_cleanup;
+ }
+
for (i = 0; i < num_msgs; i++) {
- if (vms.lastmsg < msgs[i]) {
- /* msg does not exist */
- ast_log(AST_LOG_ERROR, "Could not remove msg %d from folder %s because it does not exist.\n", msgs[i], folder);
- res = -1;
- goto vm_remove_cleanup;
- }
vms.deleted[msgs[i]] = 1;
}
@@ -14676,6 +14828,26 @@
int res = 0;
int open = 0;
int i;
+ char filename[PATH_MAX];
+ struct ast_config *msg_cfg;
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+ int duration = 0;
+ const char *value;
+
+ if (ast_strlen_zero(mailbox)) {
+ ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(folder)) {
+ ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(msg_num)) {
+ ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
+ return -1;
+ }
memset(&vmus, 0, sizeof(vmus));
memset(&vms, 0, sizeof(vms));
@@ -14685,69 +14857,60 @@
}
if (!(vmu = find_user(&vmus, context, mailbox))) {
+ return -1;
+ }
+
+ i = get_folder_by_name(folder);
+ ast_copy_string(vms.username, mailbox, sizeof(vms.username));
+ vms.lastmsg = -1;
+ if ((res = open_mailbox(&vms, vmu, i)) < 0) {
+ ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
goto play2_msg_cleanup;
}
-
- if (!ast_strlen_zero(msg_num) && !ast_strlen_zero(folder)) {
- char filename[PATH_MAX];
- struct ast_config *msg_cfg;
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
- int duration = 0;
- const char *value;
-
- i = get_folder_by_name(folder);
- ast_copy_string(vms.username, mailbox, sizeof(vms.username));
- vms.lastmsg = -1;
- if ((res = open_mailbox(&vms, vmu, i)) < 0) {
- ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
- res = -1;
- goto play2_msg_cleanup;
- }
- open = 1;
-
- vms.curmsg = atoi(msg_num);
- if (vms.curmsg > vms.lastmsg) {
- res = -1;
- goto play2_msg_cleanup;
- }
-
- /* Find the msg */
- make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
- snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
- RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
-
- msg_cfg = ast_config_load(filename, config_flags);
- if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
- DISPOSE(vms.curdir, vms.curmsg);
- res = -1;
- goto play2_msg_cleanup;
- }
- if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
- duration = atoi(value);
- }
- ast_config_destroy(msg_cfg);
-
- vms.heard[vms.curmsg] = 1;
+ open = 1;
+
+ vms.curmsg = atoi(msg_num);
+ if (vms.curmsg > vms.lastmsg || vms.curmsg < 0) {
+ res = -1;
+ goto play2_msg_cleanup;
+ }
+
+ /* Find the msg */
+ make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
+ snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
+ RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
+
+ msg_cfg = ast_config_load(filename, config_flags);
+ if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
+ DISPOSE(vms.curdir, vms.curmsg);
+ res = -1;
+ goto play2_msg_cleanup;
+ }
+ if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
+ duration = atoi(value);
+ }
+ ast_config_destroy(msg_cfg);
#ifdef IMAP_STORAGE
- /*IMAP storage stores any prepended message from a forward
- * as a separate file from the rest of the message
- */
- if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
- wait_file(chan, &vms, vms.introfn);
- }
+ /*IMAP storage stores any prepended message from a forward
+ * as a separate file from the rest of the message
+ */
+ if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
+ wait_file(chan, &vms, vms.introfn);
+ }
#endif
- if (cb) {
- cb(chan, vms.fn, duration);
- } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
- ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
- } else {
- res = 0;
- }
-
- /* cleanup configs and msg */
- DISPOSE(vms.curdir, vms.curmsg);
- }
+ if (cb) {
+ cb(chan, vms.fn, duration);
+ } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
+ ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
+ } else {
+ res = 0;
+ }
+
+ vms.heard[vms.curmsg] = 1;
+
+ /* cleanup configs and msg */
+ DISPOSE(vms.curdir, vms.curmsg);
play2_msg_cleanup:
if (vmu && open) {
Modified: branches/10-digiumphones/apps/app_voicemail.exports.in
URL: http://svnview.digium.com/svn/asterisk/branches/10-digiumphones/apps/app_voicemail.exports.in?view=diff&rev=361748&r1=361747&r2=361748
==============================================================================
--- branches/10-digiumphones/apps/app_voicemail.exports.in (original)
+++ branches/10-digiumphones/apps/app_voicemail.exports.in Mon Apr 9 16:32:17 2012
@@ -22,6 +22,8 @@
LINKER_SYMBOL_PREFIXast_vm_msg_forward;
LINKER_SYMBOL_PREFIXast_vm_index_to_foldername;
LINKER_SYMBOL_PREFIXast_vm_msg_play;
+ LINKER_SYMBOL_PREFIXast_vm_test_create_user;
+ LINKER_SYMBOL_PREFIXast_vm_test_destroy_user;
local:
*;
};
Modified: branches/10-digiumphones/include/asterisk/app_voicemail.h
URL: http://svnview.digium.com/svn/asterisk/branches/10-digiumphones/include/asterisk/app_voicemail.h?view=diff&rev=361748&r1=361747&r2=361748
==============================================================================
--- branches/10-digiumphones/include/asterisk/app_voicemail.h (original)
+++ branches/10-digiumphones/include/asterisk/app_voicemail.h Mon Apr 9 16:32:17 2012
@@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 2011-2012, Digium, Inc.
+ * Copyright (C) 2011, Digium, Inc.
*
* David Vossel <dvossel at digium.com>
*
@@ -190,5 +190,25 @@
* \retval other The name of the mailbox
*/
const char *ast_vm_index_to_foldername(unsigned int index);
+
+#ifdef TEST_FRAMEWORK
+/*!
+ * \brief Add a user to the voicemail system for test purposes
+ * \param context The context of the mailbox
+ * \param mailbox The mailbox for the user
+ * \retval 0 success
+ * \retval other failure
+ */
+int ast_vm_test_create_user(const char *context, const char *mailbox);
+
+/*!
+ * \brief Dispose of a user. This should be used to destroy a user that was
+ * previously created using ast_vm_test_create_user
+ * \param context The context of the mailbox
+ * \param mailbox The mailbox for the user to destroy
+ */
+int ast_vm_test_destroy_user(const char *context, const char *mailbox);
+
#endif
+#endif
Modified: branches/10-digiumphones/main/manager.c
URL: http://svnview.digium.com/svn/asterisk/branches/10-digiumphones/main/manager.c?view=diff&rev=361748&r1=361747&r2=361748
==============================================================================
--- branches/10-digiumphones/main/manager.c (original)
+++ branches/10-digiumphones/main/manager.c Mon Apr 9 16:32:17 2012
@@ -2404,6 +2404,7 @@
struct ast_sockaddr addr;
if (ast_strlen_zero(username)) { /* missing username */
+ ast_log(LOG_NOTICE, "Missing username?\n");
return -1;
}
@@ -2444,8 +2445,10 @@
}
} else if (user->secret) {
if (!strcmp(password, user->secret)) {
+ ast_log(LOG_NOTICE, "Seems to have passed...\n");
error = 0;
} else {
+ ast_log(LOG_NOTICE, "They didn't equal? %s != %s ?\n", password, user->secret);
report_inval_password(s, username);
}
}
Added: branches/10-digiumphones/tests/test_voicemail_api.c
URL: http://svnview.digium.com/svn/asterisk/branches/10-digiumphones/tests/test_voicemail_api.c?view=auto&rev=361748
==============================================================================
--- branches/10-digiumphones/tests/test_voicemail_api.c (added)
+++ branches/10-digiumphones/tests/test_voicemail_api.c Mon Apr 9 16:32:17 2012
@@ -1,0 +1,1415 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2012, Matt Jordan
+ *
+ * Matt Jordan <mjordan at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Skeleton Test
+ *
+ * \author\verbatim Matt Jordan <mjordan at digium.com> \endverbatim
+ *
+ * Tests for the publicly exposed Voicemail API
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/paths.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/app_voicemail.h"
+
+/*! \internal \brief Permissions to set on the voicemail directories we create
+ * - taken from app_voicemail */
+#define VOICEMAIL_DIR_MODE 0777
+
+/*! \internal \brief Permissions to set on the voicemail files we create
+ * - taken from app_voicemail */
+#define VOICEMAIL_FILE_MODE 0666
+
+/*! \internal \brief The number of mock snapshot objects we use for tests */
+#define TOTAL_SNAPSHOTS 4
+
+/*! \internal \brief Create and populate the mock message objects and create the
+ * envelope files on the file system */
+#define VM_API_TEST_SETUP do { \
+ if (test_vm_api_test_setup()) { \
+ VM_API_TEST_CLEANUP; \
+ ast_test_status_update(test, "Failed to set up necessary mock objects for voicemail API test\n"); \
+ return AST_TEST_FAIL; \
+ } else { \
+ int i = 0; \
+ for (; i < TOTAL_SNAPSHOTS; i++) { \
+ ast_test_status_update(test, "Created message in %s/%s with ID %s\n", \
+ test_snapshots[i]->exten, test_snapshots[i]->folder_name, test_snapshots[i]->msg_id); \
+ } \
+} } while (0)
+
+/*! \internal \brief Safely cleanup after a test run. This should be called both when a
+ * test fails and when it passes */
+#define VM_API_TEST_CLEANUP test_vm_api_test_teardown()
+
+/*! \internal \brief Safely cleanup a snapshot and a test run. Note that it assumes
+ * that the mailbox snapshot object is test_mbox_snapshot */
+#define VM_API_SNAPSHOT_TEST_CLEANUP \
+ if (test_mbox_snapshot) { \
+ test_mbox_snapshot = ast_vm_mailbox_snapshot_destroy(test_mbox_snapshot); \
+ } \
+ VM_API_TEST_CLEANUP; \
+
+/*! \internal \brief Verify the expected result from two string values obtained
+ * from a mailbox snapshot. Note that it assumes the mailbox snapshot
+ * object is test_mbox_snapshot
+ */
+#define VM_API_STRING_FIELD_VERIFY(expected, actual) do { \
+ if (strncmp((expected), (actual), sizeof((expected)))) { \
+ ast_test_status_update(test, "Test failed for parameter %s: Expected [%s], Actual [%s]\n", #actual, expected, actual); \
+ VM_API_SNAPSHOT_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Verify the expected result from two integer values. Note
+ * that it assumes the mailbox snapshot object is test_mbox_snapshot */
+#define VM_API_INT_VERIFY(expected, actual) do { \
+ if ((expected) != (actual)) { \
+ ast_test_status_update(test, "Test failed for parameter %s: Expected [%d], Actual [%d]\n", #actual, expected, actual); \
+ VM_API_SNAPSHOT_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Verify that a mailbox snapshot contains the expected message
+ * snapshot, in the correct position, with the expected values. Note
+ * that it assumes the mailbox snapshot object is test_mbox_snapshot
+ */
+#define VM_API_SNAPSHOT_MSG_VERIFY(expected, actual, expected_folder, expected_index) do { \
+ struct ast_vm_msg_snapshot *msg; \
+ int found = 0; \
+ int counter = 0; \
+ AST_LIST_TRAVERSE(&((actual)->snapshots[get_folder_by_name(expected_folder)]), msg, msg) { \
+ if (!(strcmp(msg->msg_id, (expected)->msg_id))) { \
+ ast_test_status_update(test, "Found message %s in snapshot\n", msg->msg_id); \
+ found = 1; \
+ if ((expected_index) != counter) { \
+ ast_test_status_update(test, "Expected message %s at index %d; Actual [%d]\n", \
+ (expected)->msg_id, (expected_index), counter); \
+ VM_API_SNAPSHOT_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } \
+ VM_API_STRING_FIELD_VERIFY((expected)->callerid, msg->callerid); \
+ VM_API_STRING_FIELD_VERIFY((expected)->callerchan, msg->callerchan); \
+ VM_API_STRING_FIELD_VERIFY((expected)->exten, msg->exten); \
+ VM_API_STRING_FIELD_VERIFY((expected)->origdate, msg->origdate); \
+ VM_API_STRING_FIELD_VERIFY((expected)->origtime, msg->origtime); \
+ VM_API_STRING_FIELD_VERIFY((expected)->duration, msg->duration); \
+ VM_API_STRING_FIELD_VERIFY((expected)->folder_name, msg->folder_name); \
+ /* We are currently not going to check folder_dir, since its never written out. */ \
+ /* VM_API_STRING_FIELD_VERIFY((expected)->folder_dir, msg->folder_dir); \ */ \
+ VM_API_STRING_FIELD_VERIFY((expected)->flag, msg->flag); \
+ VM_API_INT_VERIFY((expected)->msg_number, msg->msg_number); \
+ break; \
+ } \
+ ++counter; \
+ } \
+ if (!found) { \
+ ast_test_status_update(test, "Test failed for message snapshot %s: not found in mailbox snapshot\n", (expected)->msg_id); \
+ VM_API_SNAPSHOT_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+} } while (0)
+
+
+/*! \internal \brief Create a message snapshot, failing the test if the snapshot could not be created.
+ * This requires having a snapshot named test_mbox_snapshot.
+ */
+#define VM_API_SNAPSHOT_CREATE(mailbox, context, folder, desc, sort, old_and_inbox) do { \
+ if (!(test_mbox_snapshot = ast_vm_mailbox_snapshot_create( \
+ (mailbox), (context), (folder), (desc), (sort), (old_and_inbox)))) { \
+ ast_test_status_update(test, "Failed to create voicemail mailbox snapshot\n"); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Create a message snapshot, failing the test if the snapshot could be created.
+ * This is used to test off nominal conditions.
+ * This requires having a snapshot named test_mbox_snapshot.
+ */
+#define VM_API_SNAPSHOT_OFF_NOMINAL_TEST(mailbox, context, folder, desc, sort, old_and_inbox) do { \
+ if ((test_mbox_snapshot = ast_vm_mailbox_snapshot_create( \
+ (mailbox), (context), (folder), (desc), (sort), (old_and_inbox)))) { \
+ ast_test_status_update(test, "Created mailbox snapshot when none was expected\n"); \
+ test_mbox_snapshot = ast_vm_mailbox_snapshot_destroy(test_mbox_snapshot); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Move a voicemail message, failing the test if the message could not be moved */
+#define VM_API_MOVE_MESSAGE(mailbox, context, number_of_messages, source, message_numbers_in, dest, message_numbers_out) do { \
+ if (ast_vm_msg_move((mailbox), (context), (number_of_messages), (source), (message_numbers_in), (dest), (message_numbers_out))) { \
+ ast_test_status_update(test, "Failed to move message %s@%s from %s to %s\n", \
+ (mailbox) ? (mailbox): "(NULL)", (context) ? (context) : "(NULL)", (source) ? (source) : "(NULL)", (dest) ? (dest) : "(NULL)"); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+ /*! \internal \brief Attempt to move a voicemail message, failing the test if the message could be moved */
+#define VM_API_MOVE_MESSAGE_OFF_NOMINAL(mailbox, context, number_of_messages, source, message_numbers_in, dest, message_numbers_out) do { \
+ if (!ast_vm_msg_move((mailbox), (context), (number_of_messages), (source), (message_numbers_in), (dest), (message_numbers_out))) { \
+ ast_test_status_update(test, "Succeeded to move message %s@%s from %s to %s when we really shouldn't\n", \
+ (mailbox) ? (mailbox): "(NULL)", (context) ? (context) : "(NULL)", (source) ? (source) : "(NULL)", (dest) ? (dest) : "(NULL)"); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Remove a message, failing the test if the method failed or if the message is still present. */
+#define VM_API_REMOVE_MESSAGE(mailbox, context, number_of_messages, folder, message_numbers_in) do { \
+ if (ast_vm_msg_remove((mailbox), (context), (number_of_messages), (folder), (message_numbers_in))) { \
+ ast_test_status_update(test, "Failed to remove message from mailbox %s@%s, folder %s", \
+ (mailbox) ? (mailbox): "(NULL)", (context) ? (context) : "(NULL)", (folder) ? (folder) : "(NULL)"); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } \
+ VM_API_SNAPSHOT_CREATE((mailbox), (context), (folder), 0, AST_VM_SNAPSHOT_SORT_BY_TIME, 0); \
+ VM_API_INT_VERIFY(test_mbox_snapshot->total_msg_num, 0); \
+ test_mbox_snapshot = ast_vm_mailbox_snapshot_destroy(test_mbox_snapshot); \
+} while (0)
+
+/*! \internal \brief Remove a message, failing the test if the method succeeds */
+#define VM_API_REMOVE_MESSAGE_OFF_NOMINAL(mailbox, context, number_of_messages, folder, message_numbers_in) do { \
+ if (!ast_vm_msg_remove((mailbox), (context), (number_of_messages), (folder), (message_numbers_in))) { \
+ ast_test_status_update(test, "Succeeded in removing message from mailbox %s@%s, folder %s, when expected result was failure\n", \
+ (mailbox) ? (mailbox): "(NULL)", (context) ? (context) : "(NULL)", (folder) ? (folder) : "(NULL)"); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Forward a message, failing the test if the message could not be forwarded */
+# define VM_API_FORWARD_MESSAGE(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, number_of_messages, message_numbers_in, delete_old) do { \
+ if (ast_vm_msg_forward((from_mailbox), (from_context), (from_folder), (to_mailbox), (to_context), (to_folder), (number_of_messages), (message_numbers_in), (delete_old))) { \
+ ast_test_status_update(test, "Failed to forward message from %s@%s [%s] to %s@%s [%s]\n", \
+ (from_mailbox) ? (from_mailbox) : "(NULL)", (from_context) ? (from_context) : "(NULL)", (from_folder) ? (from_folder) : "(NULL)", \
+ (to_mailbox) ? (to_mailbox) : "(NULL)", (to_context) ? (to_context) : "(NULL)", (to_folder) ? (to_folder) : "(NULL)"); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+ /*! \internal \brief Forward a message, failing the test if the message was successfully forwarded */
+#define VM_API_FORWARD_MESSAGE_OFF_NOMINAL(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, number_of_messages, message_numbers_in, delete_old) do { \
+ if (!ast_vm_msg_forward((from_mailbox), (from_context), (from_folder), (to_mailbox), (to_context), (to_folder), (number_of_messages), (message_numbers_in), (delete_old))) { \
+ ast_test_status_update(test, "Succeeded in forwarding message from %s@%s [%s] to %s@%s [%s] when expected result was fail\n", \
+ (from_mailbox) ? (from_mailbox) : "(NULL)", (from_context) ? (from_context) : "(NULL)", (from_folder) ? (from_folder) : "(NULL)", \
+ (to_mailbox) ? (to_mailbox) : "(NULL)", (to_context) ? (to_context) : "(NULL)", (to_folder) ? (to_folder) : "(NULL)"); \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Playback a message on a channel or callback function. Note that the channel name must be test_channel.
+ * Fail the test if the message could not be played. */
+#define VM_API_PLAYBACK_MESSAGE(channel, mailbox, context, folder, message, callback_fn) do { \
+ if (ast_vm_msg_play((channel), (mailbox), (context), (folder), (message), (callback_fn))) { \
+ ast_test_status_update(test, "Failed nominal playback message test\n"); \
+ if (test_channel) { \
+ ast_hangup(test_channel); \
+ } \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+/*! \internal \brief Playback a message on a channel or callback function. Note that the channel name must be test_channel.
+ * Fail the test if the message is successfully played */
+#define VM_API_PLAYBACK_MESSAGE_OFF_NOMINAL(channel, mailbox, context, folder, message, callback_fn) do { \
+ if (!ast_vm_msg_play((channel), (mailbox), (context), (folder), (message), (callback_fn))) { \
+ ast_test_status_update(test, "Succeeded in playing back of message when expected result was to fail\n"); \
+ if (test_channel) { \
+ ast_hangup(test_channel); \
+ } \
+ VM_API_TEST_CLEANUP; \
+ return AST_TEST_FAIL; \
+ } } while (0)
+
+
+/*! \internal \brief Possible names of folders. Taken from app_voicemail */
+static const char * const mailbox_folders[] = {
+ "INBOX",
+ "Old",
+ "Work",
+ "Family",
+ "Friends",
+ "Cust1",
+ "Cust2",
+ "Cust3",
+ "Cust4",
+ "Cust5",
+ "Deleted",
+ "Urgent",
+};
+
+/*! \internal \brief Message snapshots representing the messages that are used by the various tests */
+static struct ast_vm_msg_snapshot *test_snapshots[TOTAL_SNAPSHOTS];
+
+/*! \internal \brief Tracks whether or not we entered into the message playback callback function */
+static int global_entered_playback_callback = 0;
+
+/*! \internal \brief Get a folder index by its name */
+static int get_folder_by_name(const char *folder)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
+ if (strcasecmp(folder, mailbox_folders[i]) == 0) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*! \internal \brief Get a mock snapshot object
+ * \param context The mailbox context
+ * \param exten The mailbox extension
+ * \param callerid The caller ID of the person leaving the message
+ * \returns an ast_vm_msg_snapshot object on success
+ * \returns NULL on error
+ */
+static struct ast_vm_msg_snapshot *test_vm_api_create_mock_snapshot(const char *context, const char *exten, const char *callerid)
+{
+ char msg_id_hash[AST_MAX_CONTEXT + AST_MAX_EXTENSION + sizeof(callerid) + 1];
+ char msg_id_buf[256];
+ struct ast_vm_msg_snapshot *snapshot;
+
+ snprintf(msg_id_hash, sizeof(msg_id_hash), "%s%s%s", exten, context, callerid);
+ snprintf(msg_id_buf, sizeof(msg_id_buf), "%ld-%d", (long)time(NULL), ast_str_hash(msg_id_hash));
+
+ if ((snapshot = ast_calloc(1, sizeof(*snapshot)))) {
+ ast_string_field_init(snapshot, 128);
+ ast_string_field_set(snapshot, msg_id, msg_id_buf);
+ ast_string_field_set(snapshot, exten, exten);
+ ast_string_field_set(snapshot, callerid, callerid);
+ }
+ return snapshot;
+}
+
+/*! \internal \brief Make a voicemail mailbox folder based on the values provided in a message snapshot
+ * \param snapshot The snapshot containing the information to create the folder from
+ * \returns 0 on success
+ * \returns 1 on failure
+ */
+static int test_vm_api_create_voicemail_folder(struct ast_vm_msg_snapshot *snapshot)
+{
+ mode_t mode = VOICEMAIL_DIR_MODE;
+ int res;
+
+ if ((res = ast_mkdir(snapshot->folder_dir, mode))) {
+ ast_log(AST_LOG_ERROR, "ast_mkdir '%s' failed: %s\n", snapshot->folder_dir, strerror(res));
+ return 1;
+ }
+ return 0;
+}
+
+/*! \internal \brief Create the voicemail files specified by a snapshot
+ * \param context The context of the mailbox
+ * \param mailbox The actual mailbox
+ * \param snapshot The message snapshot object containing the relevant envelope data
+ * \note This will symbolic link the sound file 'beep.gsm' to act as the 'sound' portion of the voicemail.
+ * Certain actions in app_voicemail will fail if an actual sound file does not exist
+ * \returns 0 on success
+ * \returns 1 on any failure
+ */
+static int test_vm_api_create_voicemail_files(const char *context, const char *mailbox, struct ast_vm_msg_snapshot *snapshot)
+{
+ FILE *msg_file;
+ char folder_path[PATH_MAX];
+ char msg_path[PATH_MAX];
+ char snd_path[PATH_MAX];
+ char beep_path[PATH_MAX];
+
+ /* Note that we create both the text and a dummy sound file here. Without
+ * the sound file, a number of the voicemail operations 'silently' fail, as it
+ * does not believe that an actual voicemail exists
+ */
+ snprintf(folder_path, sizeof(folder_path), "%s/voicemail/%s/%s/%s",
+ ast_config_AST_SPOOL_DIR, context, mailbox, snapshot->folder_name);
+ ast_string_field_set(snapshot, folder_dir, folder_path);
+ snprintf(msg_path, sizeof(msg_path), "%s/msg%04d.txt",
+ snapshot->folder_dir, snapshot->msg_number);
+ snprintf(snd_path, sizeof(snd_path), "%s/msg%04d.gsm",
+ snapshot->folder_dir, snapshot->msg_number);
+ snprintf(beep_path, sizeof(beep_path), "%s/sounds/en/beep.gsm", ast_config_AST_VAR_DIR);
+
+ if (test_vm_api_create_voicemail_folder(snapshot)) {
+ return 1;
+ }
+
+ if (ast_lock_path(snapshot->folder_dir) == AST_LOCK_FAILURE) {
+ ast_log(AST_LOG_ERROR, "Unable to lock directory %s\n", snapshot->folder_dir);
+ return 1;
+ }
+
+ if (symlink(beep_path, snd_path)) {
+ ast_unlock_path(snapshot->folder_dir);
+ ast_log(AST_LOG_ERROR, "Failed to create a symbolic link from %s to %s: %s\n",
+ beep_path, snd_path, strerror(errno));
+ return 1;
+ }
+
+ if (!(msg_file = fopen(msg_path, "w"))) {
+ /* Attempt to remove the sound file */
+ unlink(snd_path);
+ ast_unlock_path(snapshot->folder_dir);
+ ast_log(AST_LOG_ERROR, "Failed to open %s for writing\n", msg_path);
+ return 1;
+ }
+
+ fprintf(msg_file, ";\n; Message Information file\n;\n"
+ "[message]\n"
+ "origmailbox=%s\n"
+ "context=%s\n"
+ "macrocontext=%s\n"
+ "exten=%s\n"
+ "rdnis=%s\n"
+ "priority=%d\n"
+ "callerchan=%s\n"
+ "callerid=%s\n"
+ "origdate=%s\n"
+ "origtime=%s\n"
+ "category=%s\n"
+ "msg_id=%s\n"
+ "flag=%s\n"
+ "duration=%s\n",
+ mailbox,
+ context,
+ "",
+ snapshot->exten,
+ "unknown",
+ 1,
+ snapshot->callerchan,
+ snapshot->callerid,
+ snapshot->origdate,
+ snapshot->origtime,
+ "",
+ snapshot->msg_id,
+ snapshot->flag,
+ snapshot->duration);
+ fclose(msg_file);
+
+ if (chmod(msg_path, VOICEMAIL_FILE_MODE) < 0) {
+ ast_unlock_path(snapshot->folder_dir);
+ ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", msg_path, strerror(errno));
+ return 1;
+ }
+ ast_unlock_path(snapshot->folder_dir);
+
+ return 0;
+}
+
+/*! \internal \brief Destroy the voicemail on the file system associated with a snapshot
+ * \param snapshot The snapshot describing the voicemail
+ */
+static void test_vm_api_remove_voicemail(struct ast_vm_msg_snapshot *snapshot)
+{
+ char msg_path[PATH_MAX];
+ char snd_path[PATH_MAX];
+ char folder_path[PATH_MAX];
+
+ if (!snapshot) {
+ return;
+ }
+
+ if (ast_strlen_zero(snapshot->folder_dir)) {
+ snprintf(folder_path, sizeof(folder_path), "%s/voicemail/%s/%s/%s",
+ ast_config_AST_SPOOL_DIR, "default", snapshot->exten, snapshot->folder_name);
+ ast_string_field_set(snapshot, folder_dir, folder_path);
+ }
+
+ snprintf(msg_path, sizeof(msg_path), "%s/msg%04d.txt",
+ snapshot->folder_dir, snapshot->msg_number);
+ snprintf(snd_path, sizeof(snd_path), "%s/msg%04d.gsm",
+ snapshot->folder_dir, snapshot->msg_number);
+ unlink(msg_path);
+ unlink(snd_path);
+
+ return;
+}
+
+/*! \internal \brief Destroy the voicemails associated with a mailbox snapshot
+ * \param mailbox The actual mailbox name
+ * \param mailbox_snapshot The mailbox snapshot containing the voicemails to destroy
+ * \note Its necessary to specify not just the snapshot, but the mailbox itself. The
+ * message snapshots contained in the snapshot may have originated from a different mailbox
+ * then the one we're destroying, which means that we can't determine the files to delete
+ * without knowing the actual mailbox they exist in.
+ */
+static void test_vm_api_destroy_mailbox_voicemails(const char *mailbox, struct ast_vm_mailbox_snapshot *mailbox_snapshot)
+{
+ struct ast_vm_msg_snapshot *msg;
+ int i;
+
+ for (i = 0; i < 12; ++i) {
+ AST_LIST_TRAVERSE(&mailbox_snapshot->snapshots[i], msg, msg) {
+ ast_string_field_set(msg, exten, mailbox);
+ test_vm_api_remove_voicemail(msg);
+ }
+ }
+}
+
+/*! \internal \brief Use snapshots to remove all messages in the mailboxes */
+static void test_vm_api_remove_all_messages(void)
+{
+ struct ast_vm_mailbox_snapshot *mailbox_snapshot;
+
+ /* Take a snapshot of each mailbox and remove the contents. Note that we need to use
+ * snapshots of the mailboxes in addition to our tracked test snapshots, as there's a good chance
+ * we've created copies of the snapshots */
[... 942 lines stripped ...]
More information about the asterisk-commits
mailing list