[asterisk-commits] mmichelson: trunk r368435 - in /trunk: ./ apps/ channels/ channels/sip/includ...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jun 4 15:26:26 CDT 2012


Author: mmichelson
Date: Mon Jun  4 15:26:12 2012
New Revision: 368435

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=368435
Log:
Merge changes dealing with support for Digium phones.

Presence support has been added. This is accomplished by
allowing for presence hints in addition to device state
hints. A dialplan function called PRESENCE_STATE has been
added to allow for setting and reading presence. Presence
can be transmitted to Digium phones using custom XML
elements in a PIDF presence document.

Voicemail has new APIs that allow for moving, removing,
forwarding, and playing messages. Messages have had a new
unique message ID added to them so that the APIs will work
reliably. The state of a voicemail mailbox can be obtained
using an API that allows one to get a snapshot of the mailbox.
A voicemail Dialplan App called VoiceMailPlayMsg has been
added to be able to play back a specific message.

Configuration hooks have been added. Configuration hooks
allow for a piece of code to be executed when a specific
configuration file is loaded by a specific module. This is
useful for modules that are dependent on the configuration
of other modules.

chan_sip now has a public method that allows for a custom
SIP INFO request to be sent mid-dialog. Digium phones use
this in order to display progress bars when files are played.

Messaging support has been expanded a bit. The main
visible difference is the addition of an AMI action
MessageSend.

Finally, a ParkingLots manager action has been added in order
to get a list of parking lots.


Added:
    trunk/channels/chan_sip.exports.in
      - copied unchanged from r368179, team/mmichelson/trunk-digiumphones/channels/chan_sip.exports.in
    trunk/funcs/func_presencestate.c
      - copied, changed from r368179, team/mmichelson/trunk-digiumphones/funcs/func_presencestate.c
    trunk/include/asterisk/app_voicemail.h
      - copied unchanged from r368179, team/mmichelson/trunk-digiumphones/include/asterisk/app_voicemail.h
    trunk/include/asterisk/presencestate.h
      - copied, changed from r368179, team/mmichelson/trunk-digiumphones/include/asterisk/presencestate.h
    trunk/include/asterisk/sip_api.h
      - copied unchanged from r368179, team/mmichelson/trunk-digiumphones/include/asterisk/sip_api.h
    trunk/main/presencestate.c
      - copied, changed from r368179, team/mmichelson/trunk-digiumphones/main/presencestate.c
    trunk/tests/test_voicemail_api.c
      - copied unchanged from r368179, team/mmichelson/trunk-digiumphones/tests/test_voicemail_api.c
Modified:
    trunk/   (props changed)
    trunk/apps/app_mixmonitor.c
    trunk/apps/app_queue.c
    trunk/apps/app_voicemail.c
    trunk/apps/app_voicemail.exports.in
    trunk/channels/chan_sip.c
    trunk/channels/chan_skinny.c
    trunk/channels/sip/include/sip.h
    trunk/configs/manager.conf.sample
    trunk/contrib/realtime/mysql/voicemail_messages.sql
    trunk/include/asterisk/app.h
    trunk/include/asterisk/callerid.h
    trunk/include/asterisk/config.h
    trunk/include/asterisk/event_defs.h
    trunk/include/asterisk/file.h
    trunk/include/asterisk/manager.h
    trunk/include/asterisk/message.h
    trunk/include/asterisk/pbx.h
    trunk/main/app.c
    trunk/main/asterisk.c
    trunk/main/callerid.c
    trunk/main/channel.c
    trunk/main/config.c
    trunk/main/event.c
    trunk/main/features.c
    trunk/main/file.c
    trunk/main/manager.c
    trunk/main/message.c
    trunk/main/pbx.c
    trunk/tests/test_config.c

Propchange: trunk/
------------------------------------------------------------------------------
    automerge = *

Propchange: trunk/
------------------------------------------------------------------------------
    automerge-email = mmichelson at digium.com

Propchange: trunk/
------------------------------------------------------------------------------
    branch-10-digiumphones-merged = /branches/10-digiumphones:364766,365396

Propchange: trunk/
------------------------------------------------------------------------------
    certified-branch-1.8.11-merged = /certified/branches/1.8.11:364761,365395

Propchange: trunk/
------------------------------------------------------------------------------
    svn:mergeinfo = /team/mmichelson/private/phones-trunk:358764-361321

Propchange: trunk/
------------------------------------------------------------------------------
    svnmerge-integrated = /trunk:1-368158

Modified: trunk/apps/app_mixmonitor.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_mixmonitor.c?view=diff&rev=368435&r1=368434&r2=368435
==============================================================================
--- trunk/apps/app_mixmonitor.c (original)
+++ trunk/apps/app_mixmonitor.c Mon Jun  4 15:26:12 2012
@@ -42,6 +42,7 @@
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_MONITOR_DIR */
+#include "asterisk/stringfields.h"
 #include "asterisk/file.h"
 #include "asterisk/audiohook.h"
 #include "asterisk/pbx.h"
@@ -51,6 +52,7 @@
 #include "asterisk/channel.h"
 #include "asterisk/autochan.h"
 #include "asterisk/manager.h"
+#include "asterisk/callerid.h"
 #include "asterisk/mod_format.h"
 #include "asterisk/linkedlists.h"
 
@@ -111,6 +113,12 @@
 					<option name="i">
 						<argument name="chanvar" required="true" />
 						<para>Stores the MixMonitor's ID on this channel variable.</para>
+					</option>
+					<option name="m">
+						<argument name="mailbox" required="true" />
+						<para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
+						separated by commas eg. m(1111 at default,2222 at default,...).  Folders can be optionally specified using
+						the syntax: mailbox at context/folder</para>
 					</option>
 				</optionlist>
 			</parameter>
@@ -238,6 +246,17 @@
 
 static const char * const mixmonitor_spy_type = "MixMonitor";
 
+/*!
+ * \internal
+ * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
+ */
+struct vm_recipient {
+	char mailbox[AST_MAX_CONTEXT];
+	char context[AST_MAX_EXTENSION];
+	char folder[80];
+	AST_LIST_ENTRY(vm_recipient) list;
+};
+
 struct mixmonitor {
 	struct ast_audiohook audiohook;
 	struct ast_callid *callid;
@@ -249,6 +268,20 @@
 	unsigned int flags;
 	struct ast_autochan *autochan;
 	struct mixmonitor_ds *mixmonitor_ds;
+
+	/* the below string fields describe data used for creating voicemails from the recording */
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(call_context);
+		AST_STRING_FIELD(call_macrocontext);
+		AST_STRING_FIELD(call_extension);
+		AST_STRING_FIELD(call_callerchan);
+		AST_STRING_FIELD(call_callerid);
+	);
+	int call_priority;
+
+	/* FUTURE DEVELOPMENT NOTICE
+	 * recipient_list will need locks if we make it editable after the monitor is started */
+	AST_LIST_HEAD_NOLOCK(, vm_recipient) recipient_list;
 };
 
 enum mixmonitor_flags {
@@ -260,7 +293,8 @@
 	MUXFLAG_READ = (1 << 6),
 	MUXFLAG_WRITE = (1 << 7),
 	MUXFLAG_COMBINED = (1 << 8),
-        MUXFLAG_UID = (1 << 9),
+	MUXFLAG_UID = (1 << 9),
+	MUXFLAG_VMRECIPIENTS = (1 << 10),
 };
 
 enum mixmonitor_args {
@@ -269,7 +303,8 @@
 	OPT_ARG_VOLUME,
 	OPT_ARG_WRITENAME,
 	OPT_ARG_READNAME,
-        OPT_ARG_UID,
+	OPT_ARG_UID,
+	OPT_ARG_VMRECIPIENTS,
 	OPT_ARG_ARRAY_SIZE,	/* Always last element of the enum */
 };
 
@@ -282,6 +317,7 @@
 	AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
 	AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
 	AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
+	AST_APP_OPTION_ARG('m', MUXFLAG_VMRECIPIENTS, OPT_ARG_VMRECIPIENTS),
 });
 
 struct mixmonitor_ds {
@@ -380,6 +416,70 @@
 		ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);	
 
 	return res;
+}
+
+/*!
+ * \internal
+ * \brief adds recipients to a mixmonitor's recipient list
+ * \param mixmonitor mixmonitor being affected
+ * \param vm_recipients string containing the desired recipients to add
+ */
+static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
+{
+	/* recipients are in a single string with a format format resembling "mailbox at context/INBOX,mailbox2 at context2,mailbox3 at context3/Work" */
+	char *cur_mailbox = ast_strdupa(vm_recipients);
+	char *cur_context;
+	char *cur_folder;
+	char *next;
+	int elements_processed = 0;
+
+	while (!ast_strlen_zero(cur_mailbox)) {
+		ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
+		if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
+			*(next++) = '\0';
+		}
+
+		if ((cur_folder = strchr(cur_mailbox, '/'))) {
+			*(cur_folder++) = '\0';
+		} else {
+			cur_folder = "INBOX";
+		}
+
+		if ((cur_context = strchr(cur_mailbox, '@'))) {
+			*(cur_context++) = '\0';
+		} else {
+			cur_context = "default";
+		}
+
+		if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
+			struct vm_recipient *recipient;
+			if (!(recipient = ast_malloc(sizeof(*recipient)))) {
+				ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
+				return;
+			}
+			ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
+			ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
+			ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
+
+			/* Add to list */
+			ast_verb(5, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
+			AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
+		} else {
+			ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
+		}
+
+		cur_mailbox = next;
+		elements_processed++;
+	}
+}
+
+static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
+{
+	struct vm_recipient *current;
+	while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
+		/* Clear list element data */
+		ast_free(current);
+	}
 }
 
 #define SAMPLES_PER_FRAME 160
@@ -397,6 +497,12 @@
 			ast_free(mixmonitor->post_process);
 		}
 
+		/* Free everything in the recipient list */
+		clear_mixmonitor_recipient_list(mixmonitor);
+
+		/* clean stringfields */
+		ast_string_field_free_memory(mixmonitor);
+
 		if (mixmonitor->callid) {
 			ast_callid_unref(mixmonitor->callid);
 		}
@@ -404,10 +510,50 @@
 	}
 }
 
-static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag)
+/*!
+ * \internal
+ * \brief Copies the mixmonitor to all voicemail recipients
+ * \param mixmonitor The mixmonitor that needs to forward its file to recipients
+ * \param ext Format of the file that was saved
+ */
+static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
+{
+	struct vm_recipient *recipient = NULL;
+	struct ast_vm_recording_data recording_data;
+	if (ast_string_field_init(&recording_data, 512)) {
+		ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
+		return;
+	}
+
+	/* Copy strings to stringfields that will be used for all recipients */
+	ast_string_field_set(&recording_data, recording_file, filename);
+	ast_string_field_set(&recording_data, recording_ext, ext);
+	ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
+	ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
+	ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
+	ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
+	ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
+	/* and call_priority gets copied too */
+	recording_data.call_priority = mixmonitor->call_priority;
+
+	AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
+		/* context, mailbox, and folder need to be set per recipient */
+		ast_string_field_set(&recording_data, context, recipient->context);
+		ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
+		ast_string_field_set(&recording_data, folder, recipient->folder);
+
+		ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
+			recording_data.context);
+		ast_app_copy_recording_to_vm(&recording_data);
+	}
+
+	/* Free the string fields for recording_data before exiting the function. */
+	ast_string_field_free_memory(&recording_data);
+}
+
+static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
 {
 	/* Initialize the file if not already done so */
-	char *ext = NULL;
 	char *last_slash = NULL;
 	if (!ast_strlen_zero(filename)) {
 		if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
@@ -416,14 +562,19 @@
 
 			last_slash = strrchr(filename, '/');
 
-			if ((ext = strrchr(filename, '.')) && (ext > last_slash)) {
-				*(ext++) = '\0';
+			ast_log(LOG_NOTICE, "!!!!!! File name is %s\n", filename);
+
+			if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
+				ast_log(LOG_NOTICE, "Found a dot. *ext is %s\n", *ext);
+				**ext = '\0';
+				*ext = *ext + 1;
+				ast_log(LOG_NOTICE, "After increment *ext is %s\n", *ext);
 			} else {
-				ext = "raw";
+				*ext = "raw";
 			}
 
-			if (!(*fs = ast_writefile(filename, ext, NULL, *oflags, 0, 0666))) {
-				ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, ext);
+			if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
+				ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
 				*errflag = 1;
 			} else {
 				struct ast_filestream *tmp = *fs;
@@ -436,6 +587,9 @@
 static void *mixmonitor_thread(void *obj) 
 {
 	struct mixmonitor *mixmonitor = obj;
+	char *fs_ext = "";
+	char *fs_read_ext = "";
+	char *fs_write_ext = "";
 
 	struct ast_filestream **fs = NULL;
 	struct ast_filestream **fs_read = NULL;
@@ -457,9 +611,9 @@
 	fs_write = &mixmonitor->mixmonitor_ds->fs_write;
 
 	ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
-	mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag);
-	mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag);
-	mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag);
+	mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
+	mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
+	mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
 
 	ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
 
@@ -554,6 +708,27 @@
 	}
 
 	ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
+
+	if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
+		if (ast_strlen_zero(fs_ext)) {
+			ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
+				mixmonitor -> name);
+		} else {
+			ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
+			copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
+		}
+		if (!ast_strlen_zero(fs_read_ext)) {
+			ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
+			copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
+		}
+		if (!ast_strlen_zero(fs_write_ext)) {
+			ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
+			copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
+		}
+	} else {
+		ast_debug(3, "No recipients to forward monitor to, moving on.\n");
+	}
+
 	mixmonitor_free(mixmonitor);
 	return NULL;
 }
@@ -597,7 +772,8 @@
 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
 				  unsigned int flags, int readvol, int writevol,
 				  const char *post_process, const char *filename_write,
-				  char *filename_read, const char *uid_channel_var)
+				  char *filename_read, const char *uid_channel_var,
+				  const char *recipients)
 {
 	pthread_t thread;
 	struct mixmonitor *mixmonitor;
@@ -623,6 +799,12 @@
 		return;
 	}
 
+	/* Now that the struct has been calloced, go ahead and initialize the string fields. */
+	if (ast_string_field_init(mixmonitor, 512)) {
+		mixmonitor_free(mixmonitor);
+		return;
+	}
+
 	/* Setup the actual spy before creating our thread */
 	if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
 		mixmonitor_free(mixmonitor);
@@ -650,7 +832,6 @@
 	}
 	ast_free(datastore_id);
 
-
 	mixmonitor->name = ast_strdup(ast_channel_name(chan));
 
 	if (!ast_strlen_zero(postprocess2)) {
@@ -667,6 +848,35 @@
 
 	if (!ast_strlen_zero(filename_read)) {
 		mixmonitor->filename_read = ast_strdup(filename_read);
+	}
+
+	if (!ast_strlen_zero(recipients)) {
+		char callerid[256];
+		struct ast_party_connected_line *connected;
+
+		ast_channel_lock(chan);
+
+		/* We use the connected line of the invoking channel for caller ID. */
+
+		connected = ast_channel_connected(chan);
+		ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
+			connected->id.name.str, connected->id.number.valid,
+			connected->id.number.str);
+		ast_callerid_merge(callerid, sizeof(callerid),
+			S_COR(connected->id.name.valid, connected->id.name.str, NULL),
+			S_COR(connected->id.number.valid, connected->id.number.str, NULL),
+			"Unknown");
+
+		ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
+		ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
+		ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
+		ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
+		ast_string_field_set(mixmonitor, call_callerid, callerid);
+		mixmonitor->call_priority = ast_channel_priority(chan);
+
+		ast_channel_unlock(chan);
+
+		add_vm_recipients_from_string(mixmonitor, recipients);
 	}
 
 	ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
@@ -723,6 +933,7 @@
         char *uid_channel_var = NULL;
 
 	struct ast_flags flags = { 0 };
+	char *recipients = NULL;
 	char *parse;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(filename);
@@ -774,6 +985,14 @@
 			}
 		}
 
+		if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
+			if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
+				ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
+			} else {
+				recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
+			}
+		}
+
 		if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
 			filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
 		}
@@ -799,7 +1018,16 @@
 	}
 
 	pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
-	launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process, filename_write, filename_read, uid_channel_var);
+	launch_monitor_thread(chan,
+			args.filename,
+			flags.flags,
+			readvol,
+			writevol,
+			args.post_process,
+			filename_write,
+			filename_read,
+			uid_channel_var,
+			recipients);
 
 	return 0;
 }

Modified: trunk/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_queue.c?view=diff&rev=368435&r1=368434&r2=368435
==============================================================================
--- trunk/apps/app_queue.c (original)
+++ trunk/apps/app_queue.c Mon Jun  4 15:26:12 2012
@@ -1703,12 +1703,18 @@
 	return state;
 }
 
-static int extension_state_cb(const char *context, const char *exten, enum ast_extension_states state, void *data)
+static int extension_state_cb(char *context, char *exten, struct ast_state_cb_info *info, void *data)
 {
 	struct ao2_iterator miter, qiter;
 	struct member *m;
 	struct call_queue *q;
+	int state = info->exten_state;
 	int found = 0, device_state = extensionstate2devicestate(state);
+
+	/* only interested in extension state updates involving device states */
+	if (info->reason != AST_HINT_UPDATE_DEVICE) {
+		return 0;
+	}
 
 	qiter = ao2_iterator_init(queues, 0);
 	while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {

Modified: trunk/apps/app_voicemail.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_voicemail.c?view=diff&rev=368435&r1=368434&r2=368435
==============================================================================
--- trunk/apps/app_voicemail.c (original)
+++ trunk/apps/app_voicemail.c Mon Jun  4 15:26:12 2012
@@ -113,12 +113,14 @@
 #include "asterisk/module.h"
 #include "asterisk/adsi.h"
 #include "asterisk/app.h"
+#include "asterisk/app_voicemail.h"
 #include "asterisk/manager.h"
 #include "asterisk/dsp.h"
 #include "asterisk/localtime.h"
 #include "asterisk/cli.h"
 #include "asterisk/utils.h"
 #include "asterisk/stringfields.h"
+#include "asterisk/strings.h"
 #include "asterisk/smdi.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/event.h"
@@ -334,6 +336,30 @@
 			</enumlist>
 		</description>
 	</application>
+	<application name="VoiceMailPlayMsg" language="en_US">
+		<synopsis>
+			Play a single voice mail msg from a mailbox by msg id.
+		</synopsis>
+		<syntax>
+			<parameter name="mailbox" required="true" argsep="@">
+				<argument name="mailbox" />
+				<argument name="context" />
+			</parameter>
+			<parameter name="msg_id" required="true">
+				<para>The msg id of the msg to play back. </para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This application sets the following channel variable upon completion:</para>
+			<variablelist>
+				<variable name="VOICEMAIL_PLAYBACKSTATUS">
+					<para>The status of the playback attempt as a text string.</para>
+					<value name="SUCCESS"/>
+					<value name="FAILED"/>
+				</variable>
+			</variablelist>
+		</description>
+	</application>
 	<application name="VMSayName" language="en_US">
 		<synopsis>
 			Play the name of a voicemail user
@@ -469,7 +495,8 @@
 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
-static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
+static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id);
+static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder);
 static void update_messages_by_imapuser(const char *user, unsigned long number);
 static int vm_delete(char *file);
 
@@ -552,7 +579,6 @@
 #define ERROR_LOCK_PATH  -100
 #define OPERATOR_EXIT     300
 
-
 enum vm_box {
 	NEW_FOLDER,
 	OLD_FOLDER,
@@ -599,6 +625,25 @@
 	AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
 	AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
 });
+
+static const char * const mailbox_folders[] = {
+#ifdef IMAP_STORAGE
+	imapfolder,
+#else
+	"INBOX",
+#endif
+	"Old",
+	"Work",
+	"Family",
+	"Friends",
+	"Cust1",
+	"Cust2",
+	"Cust3",
+	"Cust4",
+	"Cust5",
+	"Deleted",
+	"Urgent",
+};
 
 static int load_config(int reload);
 #ifdef TEST_FRAMEWORK
@@ -793,28 +838,31 @@
 static char odbc_table[80];
 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
 #define DISPOSE(a,b) remove_file(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
+#define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
 #define EXISTS(a,b,c,d) (message_exists(a,b))
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
 #define DELETE(a,b,c,d) (delete_file(a,b))
+#define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
 #else
 #ifdef IMAP_STORAGE
 #define DISPOSE(a,b) (imap_remove_file(a,b))
-#define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
+#define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
+#define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
 #else
 #define RETRIEVE(a,b,c,d)
 #define DISPOSE(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i,j)
+#define STORE(a,b,c,d,e,f,g,h,i,j,k)
 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
 #define DELETE(a,b,c,d) (vm_delete(c))
+#define UPDATE_MSG_ID(a, b, c, d, e, f)
 #endif
 #endif
 
@@ -851,6 +899,8 @@
 
 static char *app3 = "MailboxExists";
 static char *app4 = "VMAuthenticate";
+
+static char *playmsg_app = "VoiceMailPlayMsg";
 
 static char *sayname_app = "VMSayName";
 
@@ -976,21 +1026,39 @@
 
 /* Forward declarations - generic */
 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
+static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
 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, int *sound_duration, const char *unlockdir,
-			signed char record_gain, struct vm_state *vms, char *flag);
+			signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id);
 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);
 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
+static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag, const char *msg_id);
 static void apply_options(struct ast_vm_user *vmu, const char *options);
 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
 static int is_valid_dtmf(const char *key);
 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
 static int write_password_to_file(const char *secretfn, const char *password);
+struct ast_str *vm_mailbox_snapshot_str(const char *mailbox, const char *context);
 static const char *substitute_escapes(const char *value);
+static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
+/*!
+ * Place a message in the indicated folder
+ *
+ * \param vmu Voicemail user
+ * \param vms Current voicemail state for the user
+ * \param msg The message number to save
+ * \param box The folder into which the message should be saved
+ * \param[out] newmsg The new message number of the saved message
+ * \param move Tells whether to copy or to move the message
+ *
+ * \note the "move" parameter is only honored for IMAP voicemail presently
+ * \retval 0 Success
+ * \revval other Failure
+ */
+static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
 
 struct ao2_container *inprocess_container;
 
@@ -1790,25 +1858,6 @@
 	return 0;
 }
 
-static const char * const mailbox_folders[] = {
-#ifdef IMAP_STORAGE
-	imapfolder,
-#else
-	"INBOX",
-#endif
-	"Old",
-	"Work",
-	"Family",
-	"Friends",
-	"Cust1",
-	"Cust2",
-	"Cust3",
-	"Cust4",
-	"Cust5",
-	"Deleted",
-	"Urgent",
-};
-
 static const char *mbox(struct ast_vm_user *vmu, int id)
 {
 #ifdef IMAP_STORAGE
@@ -1907,6 +1956,79 @@
 	mail_setflag (vms->mailstream, arg, "\\DELETED");
 	mail_expunge(vms->mailstream);
 	ast_mutex_unlock(&vms->lock);
+}
+
+static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder)
+{
+	struct ast_channel *chan;
+	char *cid;
+	char *cid_name;
+	char *cid_num;
+	struct vm_state *vms;
+	const char *duration_str;
+	int duration = 0;
+
+	/*
+	 * First, get things initially set up. If any of this fails, then
+	 * back out before doing anything substantial
+	 */
+	vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
+	if (!vms) {
+		return;
+	}
+
+	if (open_mailbox(vms, vmu, folder)) {
+		return;
+	}
+
+	chan = ast_dummy_channel_alloc();
+	if (!chan) {
+		close_mailbox(vms, vmu);
+		return;
+	}
+
+	/*
+	 * We need to make sure the new message we save has the same
+	 * callerid, flag, and duration as the original message
+	 */
+	cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
+
+	if (!ast_strlen_zero(cid)) {
+		ast_callerid_parse(cid, &cid_name, &cid_num);
+		ast_party_caller_init(ast_channel_caller(chan));
+		if (!ast_strlen_zero(cid_name)) {
+			ast_channel_caller(chan)->id.name.valid = 1;
+			ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
+		}
+		if (!ast_strlen_zero(cid_num)) {
+			ast_channel_caller(chan)->id.number.valid = 1;
+			ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
+		}
+	}
+
+	duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
+
+	if (!ast_strlen_zero(duration_str)) {
+		sscanf(duration_str, "%30d", &duration);
+	}
+
+	/*
+	 * IMAP messages cannot be altered once delivered. So we have to delete the
+	 * current message and then re-add it with the updated message ID.
+	 *
+	 * Furthermore, there currently is no atomic way to create a new message and to
+	 * store it in an arbitrary folder. So we have to save it to the INBOX and then
+	 * move to the appropriate folder.
+	 */
+	if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
+			duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
+		if (folder != NEW_FOLDER) {
+			save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
+		}
+		vm_imap_delete(dir, msgnum, vmu);
+	}
+	close_mailbox(vms, vmu);
+	ast_channel_unref(chan);
 }
 
 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
@@ -2086,20 +2208,30 @@
 
 	fprintf(text_file_ptr, "%s\n", "[message]");
 
-	get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
-	fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
-	get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
-	fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
-	get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
-	fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
-	get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
-	fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
-	get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
-	fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
-	get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
-	fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
-	get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
-	fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
+	}
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
+	}
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
+	}
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
+	}
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
+	}
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
+	}
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
+	}
+	if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
+		fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
+	}
 	fclose(text_file_ptr);
 
 exit:
@@ -2266,7 +2398,9 @@
 	check_quota(vms, vmu->imapfolder);
 	if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
 		ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
-		ast_play_and_wait(chan, "vm-mailboxfull");
+		if (chan) {
+			ast_play_and_wait(chan, "vm-mailboxfull");
+		}
 		return -1;
 	}
 
@@ -2274,8 +2408,10 @@
 	ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
 	if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
 		ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
-		ast_play_and_wait(chan, "vm-mailboxfull");
-		pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+		if (chan) {
+			ast_play_and_wait(chan, "vm-mailboxfull");
+			pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+		}
 		return -1;
 	}
 
@@ -2300,7 +2436,7 @@
 	}
 }
 
-static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
+static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id)
 {
 	char *myserveremail = serveremail;
 	char fn[PATH_MAX];
@@ -2389,7 +2525,7 @@
 	make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
 		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
 		S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
-		fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
+		fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
 	/* read mail file to memory */
 	len = ftell(p);
 	rewind(p);
@@ -2567,7 +2703,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;
 	char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
@@ -3826,6 +3962,7 @@
 	const char *mailboxcontext;
 	const char *category;
 	const char *flag;
+	const char *msg_id;
 };
 
 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
@@ -3852,8 +3989,9 @@
 	SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
 	SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
 	SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
+	SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
 	if (!ast_strlen_zero(data->category)) {
-		SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
+		SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
 	}
 	res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
@@ -3894,7 +4032,7 @@
 	struct ast_config *cfg = NULL;
 	struct odbc_obj *obj;
 	struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
-		.context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
+		.context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
 	struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
 
 	delete_file(dir, msgnum);
@@ -3946,6 +4084,9 @@
 			if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
 				idata.flag = "";
 			}
+			if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
+				idata.msg_id = "";
+			}
 		}
 		fdlen = lseek(fd, 0, SEEK_END);
 		if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
@@ -3963,9 +4104,9 @@
 		idata.datalen = idata.indlen = fdlen;
 
 		if (!ast_strlen_zero(idata.category)) 
-			snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
+			snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
 		else
-			snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
+			snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
 
 		if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
 			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
@@ -3984,6 +4125,33 @@
 	if (fd > -1)
 		close(fd);
 	return res;
+}
+
+static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
+{
+	SQLHSTMT stmt;
+	char sql[PATH_MAX];
+	struct odbc_obj *obj;
+	char msg_num_str[20];
+	char *argv[] = { msg_id, dir, msg_num_str };

[... 5024 lines stripped ...]



More information about the asterisk-commits mailing list