[svn-commits] branch oej/voicemail-ng r12490 -
/team/oej/voicemail-ng/apps/app_voicemail.c
svn-commits at lists.digium.com
svn-commits at lists.digium.com
Fri Mar 10 01:10:19 MST 2006
Author: oej
Date: Fri Mar 10 02:10:17 2006
New Revision: 12490
URL: http://svn.digium.com/view/asterisk?rev=12490&view=rev
Log:
Issue #6512 - ast_vm_message abstraction
Modified:
team/oej/voicemail-ng/apps/app_voicemail.c
Modified: team/oej/voicemail-ng/apps/app_voicemail.c
URL: http://svn.digium.com/view/asterisk/team/oej/voicemail-ng/apps/app_voicemail.c?rev=12490&r1=12489&r2=12490&view=diff
==============================================================================
--- team/oej/voicemail-ng/apps/app_voicemail.c (original)
+++ team/oej/voicemail-ng/apps/app_voicemail.c Fri Mar 10 02:10:17 2006
@@ -288,11 +288,107 @@
int (*has_voicemail)(const char *mailbox, const char *folder);
};
+/* Message abstraction */
+
+/* flags */
+#define VM_MSG_FILE_RETRIEVED 0x01 /*!< file has been retrieved from storage */
+#define VM_MSG_IS_NEW 0x02 /*!< message is newly created */
+
+/*! Message handle. This should be a mostly opaque type; direct
+ access to its members is strongly discouraged except from within the
+ ast_vm_message_* functions.
+
+ Instances of this structure shouldn't stay around for long; the user
+ object stored in them is owned by the calling code (for now), and the
+ directory where the voicemail is stored is locked for the lifetime of
+ this object. These objects should be instantiated, used, and then
+ immediately released.
+ */
+
+struct ast_vm_message
+{
+ char context[256]; /*!< The context of this voicemail box */
+ char mailbox[256]; /*!< The mailbox for this voicemail */
+ char dir[256]; /*!< The directory where the voicemail files are stored */
+ char fn[256]; /*!< The base filename (without .txt, .gsm,
+ etc.) for the voicemail files. Each voicemail is
+ stored in several files, which are formed by
+ adding various extensions to this member. */
+ char fmt[80]; /*!< A |-separated list of the formats this voicemail is stored in */
+ int msgnum; /*!< The index number of this message within its folder */
+ struct ast_vm_user *user; /*!< The user structure this voicemail is for */
+ int flags; /*!< Flags; see VM_MSG_* above */
+
+ struct ast_channel *chan; /*!< HACK: The channel associated with a new message */
+ int duration; /*!< HACK: The duration of a new message, once it's known */
+};
+
+/*! Create a new voicemail message. Only used when an actual new voicemail
+ is being recorded.
+
+ \param vmu The user for whom the voicemail's being left. Do not free this
+ while the message handle still exists.
+ \param chan The channel from which the voicemail's being recorded
+ \param fmt A |-separated list of formats in which the message should be stored
+ \return A new message handle; call ast_vm_message_release() on it when you're done.
+ */
+static struct ast_vm_message *ast_vm_message_create(struct ast_vm_user *vmu, struct ast_channel *chan, const char *fmt);
+
+/*! Create a new voicemail message from another.
+
+ Warning: Using this to make another copy of a message within the same folder
+ will cause a deadlock.
+
+ \param src The message to copy
+ \param recip The user to send the copy of the voicemail to
+ \param chan The channel from which the original voicemail was recorded (for
+ caller ID data)
+ \param fmt A |-separated list of formats in which the message should be stored
+*/
+static struct ast_vm_message *ast_vm_message_create_copy(struct ast_vm_message *src, struct ast_vm_user *recip, struct ast_channel *chan, char *fmt);
+
+/*! Release a voicemail message handle, unlocking the directory and freeing
+ the in-memory storage.
+
+ \param message The message to release
+*/
+static void ast_vm_message_release(struct ast_vm_message *message);
+
+/*! Delete a voicemail message, and release the handle which was being used
+ to access the message.
+
+ Currently this only works properly for canceling new messages.
+
+ \param message The message to delete
+*/
+static void ast_vm_message_delete_and_release(struct ast_vm_message *message);
+
+/*! Get the sound file associated with a message.
+
+ \param message The message to get the data for
+ \param filename A buffer to store the filename in
+ \param filenamelen The size of the buffer
+ \param fmt The format desired. NULL means any format. "" means
+ to return the raw filename (before appending the
+ format extension).
+ \return 0 on success, nonzero on failure
+*/
+static int ast_vm_message_get_data(struct ast_vm_message *message, char *filename, int filenamelen, char *fmt);
+
+/*! Write metadata associated with a message.
+
+ \param message The message to write metadata for
+ \param fmt printf arguments to print to the metadata file.
+*/
+static void ast_vm_message_printf_metadata(struct ast_vm_message *message, const char *fmt, ...);
+
+/* End message abstraction */
+
static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
int option, signed char record_gain);
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
- char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
+ char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration,
signed char record_gain);
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
@@ -2292,42 +2388,6 @@
return 0;
}
-static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
-
-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 fromdir[256], todir[256], frompath[256], topath[256];
- char *frombox = mbox(imbox);
- int recipmsgnum;
-
- ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
-
- create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
-
- make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
- make_file(frompath, sizeof(frompath), fromdir, msgnum);
-
- if (vm_lock_path(todir))
- return ERROR_LOCK_PATH;
-
- recipmsgnum = 0;
- do {
- make_file(topath, sizeof(topath), todir, recipmsgnum);
- if (!storage->message_exists(todir, recipmsgnum, topath, chan->language))
- break;
- recipmsgnum++;
- } while (recipmsgnum < recip->maxmsg);
- if (recipmsgnum < recip->maxmsg) {
- storage->copy_file(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
- } else {
- ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
- }
- ast_unlock_path(todir);
- notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
-
- return 0;
-}
-
static void run_externnotify(char *context, char *extension)
{
char arguments[255];
@@ -2381,16 +2441,11 @@
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
{
- char txtfile[256];
char callerid[256];
- FILE *txt;
int res = 0;
- int msgnum;
- int duration = 0;
int ausemacro = 0;
int ousemacro = 0;
char date[256];
- char dir[256];
char fn[256];
char prefile[256]="";
char tempfile[256]="";
@@ -2402,6 +2457,8 @@
struct ast_vm_user *vmu;
struct ast_vm_user svm;
const char *category = NULL;
+ struct ast_vm_message *message = NULL;
+ char duration_string[256];
ast_copy_string(tmp, ext, sizeof(tmp));
ext = tmp;
@@ -2443,8 +2500,6 @@
if (ast_fileexists(tempfile, NULL, NULL) > 0)
ast_copy_string(prefile, tempfile, sizeof(prefile));
storage->dispose_file(tempfile, -1);
- /* It's easier just to try to make it than to check for its existence */
- create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
/* Check current or macro-calling context for special extensions */
if (!ast_strlen_zero(vmu->exit)) {
@@ -2543,28 +2598,17 @@
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
return -1;
}
- /* The meat of recording the message... All the announcements and beeps have been played*/
+ /* The meat of recording the message... All the announcements have been played, but not the beep */
ast_copy_string(fmt, vmfmts, sizeof(fmt));
if (!ast_strlen_zero(fmt)) {
- msgnum = 0;
-
- if (vm_lock_path(dir)) {
- free_user(vmu);
- return ERROR_LOCK_PATH;
- }
-
/*
* This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
* in the folder. So we should get this first so we don't cut off the first few seconds of the
* message.
*/
- do {
- make_file(fn, sizeof(fn), dir, msgnum);
- if (!storage->message_exists(dir,msgnum,fn,chan->language))
- break;
- msgnum++;
- } while (msgnum < vmu->maxmsg);
-
+
+ message = ast_vm_message_create(vmu, chan, fmt);
+
/* Now play the beep once we have the message number for our next message. */
if (res >= 0) {
/* Unless we're *really* silent, try to send the beep */
@@ -2572,16 +2616,24 @@
if (!res)
res = ast_waitstream(chan, "");
}
- if (msgnum < vmu->maxmsg) {
+ if (message != NULL) {
+ get_date(date, sizeof(date));
+
+ ast_vm_message_get_data(message, fn, sizeof(fn), "");
+
/* assign a variable with the name of the voicemail file */
pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
+ /* duration is a HACK */
+ res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &(message->duration), options->record_gain);
+
+ if (res != '0')
+ snprintf(duration_string, sizeof(duration_string), "duration=%d\n", message->duration);
+ else
+ duration_string[0] = '0';
+
/* Store information */
- snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
- txt = fopen(txtfile, "w+");
- if (txt) {
- get_date(date, sizeof(date));
- fprintf(txt,
+ ast_vm_message_printf_metadata(message,
";\n"
"; Message Information file\n"
";\n"
@@ -2595,7 +2647,8 @@
"callerid=%s\n"
"origdate=%s\n"
"origtime=%ld\n"
- "category=%s\n",
+ "category=%s\n"
+ "%s",
ext,
chan->context,
chan->macrocontext,
@@ -2604,26 +2657,19 @@
chan->name,
ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
date, (long)time(NULL),
- category ? category : "");
- } else
- ast_log(LOG_WARNING, "Error opening text file for output\n");
- res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
- if (res == '0') {
- if (txt)
- fclose(txt);
+ category ? category : "",
+ duration_string);
+
+ if (res == '0')
goto transfer;
- }
if (res > 0)
res = 0;
- if (txt) {
- fprintf(txt, "duration=%d\n", duration);
- fclose(txt);
- }
- if (duration < vmminmessage) {
+ if (message->duration < vmminmessage) {
if (option_verbose > 2)
- ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
- storage->delete_file(dir,msgnum,fn);
+ ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", message->duration, vmminmessage);
+ ast_vm_message_delete_and_release(message);
+
/* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
goto leave_vm_out;
@@ -2640,18 +2686,20 @@
context++;
}
if ((recip = find_user(&recipu, context, exten))) {
- copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
+ struct ast_vm_message *copy;
+ copy = ast_vm_message_create_copy(message, recip, chan, fmt);
+ if (copy)
+ ast_vm_message_release(copy);
+ else
+ ast_log(LOG_WARNING, "Couldn't copy message to %s\n", recip->mailbox);
free_user(recip);
}
}
- if (ast_fileexists(fn, NULL, NULL)) {
- storage->store_file(dir, vmu->mailbox, vmu->context, msgnum);
- notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
- storage->dispose_file(dir, msgnum);
- }
+ ast_vm_message_release(message);
pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
} else {
- ast_unlock_path(dir);
+ /* FIXME -- we assume that any problem is a full mailbox, which isn't always true */
+
res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
if (!res)
res = ast_waitstream(chan, "");
@@ -4724,7 +4772,7 @@
/* If forcename is set, have the user record their name */
if (ast_test_flag(vmu, VM_FORCENAME)) {
snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
if (cmd < 0 || cmd == 't' || cmd == '#')
return cmd;
}
@@ -4732,11 +4780,11 @@
/* If forcegreetings is set, have the user record their greetings */
if (ast_test_flag(vmu, VM_FORCEGREET)) {
snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
if (cmd < 0 || cmd == 't' || cmd == '#')
return cmd;
snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
if (cmd < 0 || cmd == 't' || cmd == '#')
return cmd;
}
@@ -4770,15 +4818,15 @@
switch (cmd) {
case '1':
snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
break;
case '2':
snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
break;
case '3':
snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
break;
case '4':
cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
@@ -4864,12 +4912,12 @@
retries = 0;
storage->retrieve_file(prefile, -1);
if (ast_fileexists(prefile, NULL, NULL) <= 0) {
- play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
cmd = 't';
} else {
switch (cmd) {
case '1':
- cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
+ cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, record_gain);
break;
case '2':
storage->delete_file(prefile, -1, prefile);
@@ -6720,7 +6768,7 @@
}
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
- int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
+ int outsidecaller, struct ast_vm_user *vmu, int *duration,
signed char record_gain)
{
/* Record message & let caller review or re-record it, or set options if applicable */
@@ -6779,7 +6827,7 @@
/* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
if (record_gain)
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
- cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir);
+ cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, NULL);
if (record_gain)
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
if (cmd == -1) {
@@ -6896,6 +6944,129 @@
}
+struct ast_vm_message *ast_vm_message_create(struct ast_vm_user *vmu, struct ast_channel *chan, const char *fmt)
+{
+ struct ast_vm_message *message;
+
+ message = (struct ast_vm_message *) malloc(sizeof(struct ast_vm_message));
+
+ strncpy(message->context, vmu->context, sizeof(message->context));
+ strncpy(message->mailbox, vmu->mailbox, sizeof(message->mailbox));
+ strncpy(message->fmt, fmt, sizeof(message->fmt));
+ message->chan = chan;
+
+ /* It's easier just to try to make it than to check for its existence */
+ if (!create_dirpath(message->dir, sizeof(message->dir), vmu->context, vmu->mailbox, "INBOX")) {
+ free(message);
+ return NULL;
+ }
+
+ if (vm_lock_path(message->dir)) {
+ ast_log(LOG_WARNING, "Can't lock directory %s\n", message->dir);
+ free(message);
+ return NULL;
+ }
+
+ message->msgnum = 0;
+ do {
+ make_file(message->fn, sizeof(message->fn), message->dir, message->msgnum);
+ if (!storage->message_exists(message->dir,message->msgnum,message->fn,chan->language))
+ break;
+ message->msgnum++;
+ } while (message->msgnum < vmu->maxmsg);
+ if (message->msgnum >= vmu->maxmsg) {
+ ast_log(LOG_WARNING, "Mailbox full writing message to %s\n", message->dir);
+ ast_unlock_path(message->dir);
+ free(message);
+ return NULL;
+ }
+
+ make_file(message->fn, sizeof(message->fn), message->dir, message->msgnum);
+ message->user = vmu;
+ message->flags = (VM_MSG_FILE_RETRIEVED | VM_MSG_IS_NEW);
+ message->duration = 0; /* HACK */
+
+ return message;
+}
+
+/* FIXME deadlock when copying a message to the same directory as the source */
+struct ast_vm_message *ast_vm_message_create_copy(struct ast_vm_message *src, struct ast_vm_user *recip, struct ast_channel *chan, char *fmt)
+{
+ struct ast_vm_message *dest;
+
+ ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", src->mailbox, src->context, recip->mailbox, recip->context);
+
+ dest = ast_vm_message_create(recip, chan, fmt);
+ if (dest) {
+ storage->copy_file(src->dir, src->msgnum, dest->dir, dest->msgnum, recip->mailbox, recip->context, src->fn, dest->fn);
+ dest->duration = src->duration;
+ } else
+ ast_log(LOG_ERROR, "Can't copy message from %s@%s to %s@%s\n", src->mailbox, src->context, recip->mailbox, recip->context);
+
+ return dest;
+}
+
+void ast_vm_message_release(struct ast_vm_message *message)
+{
+ if (message->flags & VM_MSG_IS_NEW) {
+ /* FIXME eliminating ast_fileexists test... do we need it? */
+ storage->store_file(message->dir, message->mailbox, message->context, message->msgnum);
+ notify_new_message(message->chan, message->user, message->msgnum, message->duration, message->fmt, message->chan->cid.cid_num, message->chan->cid.cid_name);
+ }
+ if (message->flags & VM_MSG_FILE_RETRIEVED)
+ storage->dispose_file(message->dir, message->msgnum);
+ ast_unlock_path(message->dir);
+ free(message);
+}
+
+void ast_vm_message_delete_and_release(struct ast_vm_message *message)
+{
+ /* FIXME only works for newly-created messages for now */
+ if (message->flags & VM_MSG_IS_NEW)
+ storage->delete_file(message->dir,message->msgnum,message->fn);
+ ast_unlock_path(message->dir);
+ free(message);
+}
+
+/* FIXME format handling could be better */
+int ast_vm_message_get_data(struct ast_vm_message *message, char *filename, int filenamelen, char *fmt)
+{
+ if (!(message->flags & VM_MSG_FILE_RETRIEVED)) {
+ storage->retrieve_file(message->dir, message->msgnum);
+ message->flags |= VM_MSG_FILE_RETRIEVED;
+ }
+ if (fmt == NULL)
+ fmt = message->fmt;
+ if (fmt == "") {
+ strncpy(filename, message->fn, filenamelen);
+ return 0;
+ } else {
+ char one_fmt[80];
+ char *p, *q;
+ strncpy(one_fmt, fmt, sizeof(one_fmt));
+ q = strsep(&p, "|");
+ snprintf(filename, filenamelen, "%s.%s", message->fn, q);
+ return 0;
+ }
+}
+
+void ast_vm_message_printf_metadata(struct ast_vm_message *message, const char *fmt, ...)
+{
+ va_list vars;
+ FILE *txt;
+ char txtfile[256];
+
+ snprintf(txtfile, sizeof(txtfile), "%s.txt", message->fn);
+ txt = fopen(txtfile, "w+");
+ if (txt) {
+ va_start(vars, fmt);
+ vfprintf(txt, fmt, vars);
+ va_end(vars);
+ fclose(txt);
+ } else
+ ast_log(LOG_WARNING, "Error opening text file %s for output\n", txtfile);
+}
+
int usecount(void)
{
int res;
More information about the svn-commits
mailing list