[asterisk-commits] markm: trunk r359705 - in /trunk: ./ apps/ include/asterisk/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Mar 15 13:58:31 CDT 2012


Author: markm
Date: Thu Mar 15 13:58:25 2012
New Revision: 359705

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=359705
Log:
Add options PreDial options 'b' and 'B' to app_dial

* Added 'b' and 'B' options to Dial.  These options will allow you to run
  last-minute dialplan on the caller and callee channels while the Dial
  application is executing, but before the call is started.  For example you
  can use the 'b' option to run dialplan on the callee channel to get the name
  of the newly created channel right away.

Review: https://reviewboard.asterisk.org/r/1229/

(closes issue: ASTERISK-19548)
Reported by: Mark Murawski
Tested by: Mark Murawski, Stefan Schmidt


Modified:
    trunk/CHANGES
    trunk/apps/app_dial.c
    trunk/include/asterisk/pbx.h
    trunk/main/pbx.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=359705&r1=359704&r2=359705
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Thu Mar 15 13:58:25 2012
@@ -130,6 +130,12 @@
    when using multiple options (so that j option could be used without having to
    manually specify timezone and format) There are other beneftis eg. format can
    now be used without specifying time zone as well.
+
+ * Added 'b' and 'B' options to Dial.  These options will allow you to run
+   last-minute dialplan on the caller and callee channels while the Dial
+   application is executing, but before the call is started.  For example you
+   can use the 'b' option to run dialplan on the callee channel to get the name
+   of the newly created channel right away.
 
 Parking
 ------------

Modified: trunk/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_dial.c?view=diff&rev=359705&r1=359704&r2=359705
==============================================================================
--- trunk/apps/app_dial.c (original)
+++ trunk/apps/app_dial.c Thu Mar 15 13:58:25 2012
@@ -107,6 +107,21 @@
 					announcement) are completed.  This option can be used to answer the calling
 					channel before doing anything on the called channel. You will rarely need to use
 					this option, the default behavior is adequate in most cases.</para>
+				</option>
+				<option name="b" argsep=",">
+					<para>Before initiating the actual call, Gosub to the specified
+					context,exten,priority using the newly created channel(s).</para>
+                                        <para>The Gosub will be executed for each destination channel</para>
+					<argument name="context" required="true"/>
+					<argument name="exten" required="true"/>
+					<argument name="priority" required="true"/>
+				</option>
+				<option name="B" argsep=",">
+					<para>Before initiating the actual call, Gosub to the specified
+					context,exten,priority using the current channel</para>
+					<argument name="context" required="true"/>
+					<argument name="exten" required="true"/>
+					<argument name="priority" required="true"/>
 				</option>
 				<option name="C">
 					<para>Reset the call detail record (CDR) for this call.</para>
@@ -590,6 +605,8 @@
 #define OPT_FORCE_CID_TAG    (1LLU << 38)
 #define OPT_FORCE_CID_PRES   (1LLU << 39)
 #define OPT_CALLER_ANSWER    (1LLU << 40)
+#define OPT_PREDIAL_CALLEE_GOSUB  (1LLU << 41)
+#define OPT_PREDIAL_CALLER_GOSUB  (1LLU << 42)
 
 enum {
 	OPT_ARG_ANNOUNCE = 0,
@@ -609,6 +626,8 @@
 	OPT_ARG_FORCECLID,
 	OPT_ARG_FORCE_CID_TAG,
 	OPT_ARG_FORCE_CID_PRES,
+	OPT_ARG_PREDIAL_CALLER_GOSUB,
+	OPT_ARG_PREDIAL_CALLEE_GOSUB,
 	/* note: this entry _MUST_ be the last one in the enum */
 	OPT_ARG_ARRAY_SIZE,
 };
@@ -652,12 +671,14 @@
 	AST_APP_OPTION('x', OPT_CALLEE_MIXMONITOR),
 	AST_APP_OPTION('X', OPT_CALLER_MIXMONITOR),
 	AST_APP_OPTION('z', OPT_CANCEL_TIMEOUT),
+	AST_APP_OPTION_ARG('b', OPT_PREDIAL_CALLEE_GOSUB, OPT_ARG_PREDIAL_CALLEE_GOSUB),
+	AST_APP_OPTION_ARG('B', OPT_PREDIAL_CALLER_GOSUB, OPT_ARG_PREDIAL_CALLER_GOSUB),
 END_OPTIONS );
 
 #define CAN_EARLY_BRIDGE(flags,chan,peer) (!ast_test_flag64(flags, OPT_CALLEE_HANGUP | \
 	OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
 	OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK |  \
-	OPT_CALLER_PARK | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB) && \
+	OPT_CALLER_PARK | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_PREDIAL_CALLER_GOSUB | OPT_PREDIAL_CALLEE_GOSUB) && \
 	!ast_channel_audiohooks(chan) && !ast_channel_audiohooks(peer) && \
 	ast_framehook_list_is_empty(ast_channel_framehooks(chan)) && ast_framehook_list_is_empty(ast_channel_framehooks(peer)))
 
@@ -2169,14 +2190,22 @@
 
 	ast_channel_lock(chan);
 	if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
-		outbound_group = ast_strdupa(outbound_group);	
+		outbound_group = ast_strdupa(outbound_group);
 		pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
 	} else if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"))) {
 		outbound_group = ast_strdupa(outbound_group);
 	}
-	ast_channel_unlock(chan);	
+	ast_channel_unlock(chan);
 	ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_IGNORE_CONNECTEDLINE |
-			 OPT_CANCEL_TIMEOUT | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID);
+			 OPT_CANCEL_TIMEOUT | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID | OPT_PREDIAL_CALLER_GOSUB | OPT_PREDIAL_CALLEE_GOSUB);
+
+	/* PREDIAL: Run gosub on the caller's channel */
+	if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER_GOSUB])) {
+		struct ast_channel *c = chan;
+		const char *goto_target = opt_args[OPT_ARG_PREDIAL_CALLER_GOSUB];
+
+		ast_pbx_exten_run_parseargs(c, goto_target, 1);
+	}
 
 	/* loop through the list of dial destinations */
 	rest = args.peers;
@@ -2406,6 +2435,22 @@
 
 		ast_channel_unlock(tc);
 		ast_channel_unlock(chan);
+
+		/* PREDIAL: Run gosub on the callee's channel
+		 * We run the callee predial before ast_call() in case the user wishes to do something on the newly created channel
+		 * before the channel does anything important
+		 *
+		 * Inside the target gosub we will be able to do something with the newly created channel name
+		 * ie: now the calling channel can know what channel will be used to call the destination
+		 * ex: now we will know that SIP/abc-123 is calling SIP/def-124
+		 */
+		if (ast_test_flag64(&opts, OPT_PREDIAL_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE_GOSUB])) {
+			struct ast_channel *c = tc;
+			const char *goto_target = opt_args[OPT_ARG_PREDIAL_CALLEE_GOSUB];
+
+			ast_pbx_exten_run_parseargs(c, goto_target, 1);
+		}
+
 		res = ast_call(tc, numsubst, 0); /* Place the call, but don't wait on the answer */
 		ast_channel_lock(chan);
 

Modified: trunk/include/asterisk/pbx.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/pbx.h?view=diff&rev=359705&r1=359704&r2=359705
==============================================================================
--- trunk/include/asterisk/pbx.h (original)
+++ trunk/include/asterisk/pbx.h Thu Mar 15 13:58:25 2012
@@ -331,6 +331,8 @@
 		struct {
 			/*! Do not hangup the channel when the PBX is complete. */
 			unsigned int no_hangup_chan:1;
+			/*! Reuse existing pbx on the channel (used for arbitrarily jumping into dialplan) */
+			unsigned int use_existing_pbx:1;
 		};
 	};
 };
@@ -1112,6 +1114,23 @@
 int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority);
 
 /*!
+ * \note This function will check the validity of a goto target, see
+ *       if it's reachable given the current channel state, and save the
+ *       parsed tokens to the given buffers.
+ */
+int ast_pbx_exten_parse(struct ast_channel *chan, const char *goto_target, struct ast_str *context, struct ast_str *exten, struct ast_str *priority, struct varshead *varshead);
+
+/*!
+ * \note This function will run dialplan on a channel at context,exten,priority
+ */
+enum ast_pbx_result ast_pbx_exten_run_parseargs(struct ast_channel *chan, const char *gosub_args, int restore_dialplan_location);
+
+/*!
+ * \note This function will run dialplan on a channel at context,exten,priority and set also ARG
+ */
+enum ast_pbx_result ast_pbx_exten_run(struct ast_channel *chan, const char *context, const char *exten, int priority, struct varshead *varshead, int restore_dialplan_location);
+
+/*!
  * \note This function will handle locking the channel as needed.
  */
 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string);

Modified: trunk/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/pbx.c?view=diff&rev=359705&r1=359704&r2=359705
==============================================================================
--- trunk/main/pbx.c (original)
+++ trunk/main/pbx.c Thu Mar 15 13:58:25 2012
@@ -5105,19 +5105,21 @@
 	int error = 0;		/* set an error conditions */
 	struct ast_pbx *pbx;
 
-	/* A little initial setup here */
-	if (ast_channel_pbx(c)) {
-		ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c));
-		/* XXX and now what ? */
-		ast_free(ast_channel_pbx(c));
-	}
-	if (!(pbx = ast_calloc(1, sizeof(*pbx)))) {
-		return -1;
-	}
-	ast_channel_pbx_set(c, pbx);
-	/* Set reasonable defaults */
-	ast_channel_pbx(c)->rtimeoutms = 10000;
-	ast_channel_pbx(c)->dtimeoutms = 5000;
+	if (!args || !args->use_existing_pbx) {
+		/* A little initial setup here */
+		if (ast_channel_pbx(c)) {
+			ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c));
+			/* XXX and now what ? */
+			ast_free(ast_channel_pbx(c));
+		}
+		if (!(pbx = ast_calloc(1, sizeof(*pbx)))) {
+			return -1;
+		}
+		ast_channel_pbx_set(c, pbx);
+		/* Set reasonable defaults */
+		ast_channel_pbx(c)->rtimeoutms = 10000;
+		ast_channel_pbx(c)->dtimeoutms = 5000;
+        }
 
 	autoloopflag = ast_test_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP);	/* save value to restore at the end */
 	ast_set_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP);
@@ -5390,8 +5392,11 @@
 	}
 	ast_set2_flag(ast_channel_flags(c), autoloopflag, AST_FLAG_IN_AUTOLOOP);
 	ast_clear_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */
-	pbx_destroy(ast_channel_pbx(c));
-	ast_channel_pbx_set(c, NULL);
+
+        if (!args || !args->use_existing_pbx) {
+		pbx_destroy(ast_channel_pbx(c));
+		ast_channel_pbx_set(c, NULL);
+        }
 
 	if (!args || !args->no_hangup_chan) {
 		ast_hangup(c);
@@ -10844,3 +10849,237 @@
 
 	return (hints && hintdevices && statecbs) ? 0 : -1;
 }
+
+/*! \brief return the pieces of a goto style target if it's valid.
+ *
+ *  \param chan         Channel on which to test target validity
+ *  \param goto_target  Goto target string. ([[context,]extension,]priority) see below for examples
+ *  \param context      Parsed context (ast_str must be pre created)
+ *  \param exten        Parsed exten (ast_str must be pre created)
+ *  \param priority     Parsed priority (ast_str must be pre created) (can be null if not needed)
+ *  \param varshead     Parsed variables from the gosub (can be null if not needed)
+ *
+ *  \return -1 on failure
+ *  \return parsed priority integer on success (> 0)
+ *
+ *  \note lock channel before calling
+ *
+ *  \example goto_target valid format: priority
+ *  \example goto_target valid format: label
+ *  \example goto_target valid format: exten,priority
+ *  \example goto_target valid format: exten,label
+ *  \example goto_target valid format: context,exten,priority
+ *  \example goto_target valid format: context,exten,label
+ *  \example goto_target <valid_format>(args)
+ */
+int ast_pbx_exten_parse(struct ast_channel *chan, const char *goto_target, struct ast_str *context, struct ast_str *exten, struct ast_str *priority, struct varshead *varshead)
+{
+	int parse_args = 0;
+	char *target = ast_strdupa(goto_target); /* Target must be writable for AST_STANDARD_RAW_ARGS */
+	char *start_args = target;
+	int ipriority;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(context);
+		AST_APP_ARG(exten);
+		AST_APP_ARG(priority);
+	);
+	AST_DECLARE_APP_ARGS(args2,
+		AST_APP_ARG(argval)[100];
+	);
+
+	ast_str_truncate(context, 0);
+	ast_str_truncate(exten,	  0);
+
+	/* Find the args (if any) */
+	if (varshead && ((start_args = strchr(start_args, '(')) != NULL)) {
+		char *end_args;
+
+		parse_args = 1;
+		*start_args = 0;
+		start_args++;
+
+		if ((end_args = strchr(start_args, ')')) != NULL) {
+			*end_args = 0;
+		}
+		else {
+			ast_log(LOG_WARNING, "Ouch.  No closing paren for Gosub parameters: '%s'?\n", goto_target);
+		}
+	}
+
+	AST_STANDARD_RAW_ARGS(args, target);
+
+	if (priority) {
+		ast_str_truncate(priority, 0);
+	}
+
+	if (ast_strlen_zero(goto_target)) {
+		ast_log(LOG_WARNING, "goto_target cannot be empty ([[context,]extension,]priority)\n");
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.exten)) {
+		/* Only a priority in this one */
+		args.priority = args.context;
+		args.exten    = (char *) ast_channel_exten(chan);
+		args.context  = (char *) ast_channel_context(chan);
+	} else if (ast_strlen_zero(args.priority)) {
+		/* Only an extension and priority in this one */
+		args.priority = args.exten;
+		args.exten    = args.context;
+		args.context  = (char *) ast_channel_context(chan);
+	}
+
+	if (sscanf(args.priority, "%d", &ipriority) > 0) {
+		if (!ast_exists_extension(chan, args.context, args.exten, ipriority, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
+		      ast_log(LOG_WARNING, "priority based goto target not found: %s\n", goto_target);
+		      ipriority = 0;
+		      return -1;
+		}
+	} else {
+		if (!(ipriority = ast_findlabel_extension(chan, args.context, args.exten, args.priority, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)))) {
+			ast_log(LOG_WARNING, "label based goto target not found: %s\n", goto_target);
+			ipriority = 0;
+			return -1;
+		}
+	}
+
+	ast_str_set(&context, 0, "%s", args.context);
+	ast_str_set(&exten,   0, "%s", args.exten);
+
+	if (priority) {
+		ast_str_set(&priority, 0, "%s", args.priority);
+	}
+
+	if (parse_args) {
+		int i;
+		struct ast_str *argument_name = ast_str_create(64);
+		struct ast_var_t *variable;
+		int argument_num = 1;
+
+		AST_STANDARD_RAW_ARGS(args2, start_args);
+
+		for (i = 0; i < args2.argc; i++) {
+			ast_str_truncate(argument_name, 0);
+			ast_str_append(&argument_name, 0, "ARG%d", argument_num);
+
+			variable = ast_var_assign(ast_str_buffer(argument_name), args2.argval[i]);
+			AST_LIST_INSERT_TAIL(varshead, variable, entries);
+			argument_num++;
+
+			ast_log(LOG_WARNING, "Setting '%s' to '%s' %p\n", ast_str_buffer(argument_name), args2.argval[i], variable);
+			ast_debug(1, "Setting '%s' to '%s'\n", ast_str_buffer(argument_name), args2.argval[i]);
+		}
+
+		ast_free(argument_name);
+	}
+
+	return ipriority;
+}
+
+/*! \brief run dialplan on a channel, optionally restoring the channel to the previous dialplan location
+ *
+ *  \param chan                       Channel on which to run dialplan
+ *  \param args                       Gosub style args in the form of context,exten,priority(arg1,arg2,argn,...)
+ *  \param restore_dialplan_location  1/0 whether to restore the channel's dialplan location to where it was before we were called
+ *
+ *  \retval AST_PBX_SUCCESS on success
+ *  \retval AST_PBX_ERROR   on error
+ *
+ */
+enum ast_pbx_result ast_pbx_exten_run_parseargs(struct ast_channel *chan, const char *gosub_args, int restore_dialplan_location) {
+	struct varshead varshead;
+	struct ast_var_t *variable;
+	struct ast_str *context = ast_str_create(64);
+	struct ast_str *exten = ast_str_create(64);
+	int ipriority;
+	enum ast_pbx_result res = AST_PBX_ERROR;
+
+        memset(&varshead, 0, sizeof(varshead));
+
+	if ((ipriority = ast_pbx_exten_parse(chan, gosub_args, context, exten, NULL, &varshead)) > 0) {
+		res = ast_pbx_exten_run(chan, ast_str_buffer(context), ast_str_buffer(exten), ipriority, &varshead, 1);
+	}
+
+        AST_LIST_TRAVERSE_SAFE_BEGIN(&varshead, variable, entries) {
+		AST_LIST_REMOVE_CURRENT(entries);
+		ast_var_delete(variable);
+	}
+        AST_LIST_TRAVERSE_SAFE_END;
+
+	ast_free(context);
+	ast_free(exten);
+
+	return res;
+}
+
+/*! \brief run dialplan on a channel, optionally restoring the channel to the previous dialplan location
+ *
+ *  \param chan                       Channel on which to run dialplan
+ *  \param context                    Context in which to execute
+ *  \param exten                      Exten within the context
+ *  \param priority                   Priority within the exten
+ *  \param varshead                   Array of arguments to pass to destination.  Args will be set in the form of ARG1,ARG2,ARGn,...
+ *  \param restore_dialplan_location  1/0 whether to restore the channel's dialplan location to where it was before we were called
+ *
+ *  \retval AST_PBX_HANGUP on error
+ *  \retval AST_PBX_OK on success
+ *
+ */
+enum ast_pbx_result ast_pbx_exten_run(struct ast_channel *chan, const char *context, const char *exten, int priority, struct varshead *varshead, int restore_dialplan_location)
+{
+	struct ast_str *backup_context;
+	struct ast_str *backup_exten;
+	int backup_priority;
+	enum ast_pbx_result res;
+	struct ast_pbx_args run_args;
+	struct ast_var_t *variable;
+
+	/* Back up current dialplan location */
+	if (restore_dialplan_location) {
+		backup_context = ast_str_create(64);
+		backup_exten   = ast_str_create(64);
+
+		ast_str_set(&backup_context, 0, "%s", ast_channel_context(chan));
+		ast_str_set(&backup_exten,   0, "%s", ast_channel_exten(chan));
+		backup_priority = ast_channel_priority(chan);
+	}
+
+	/* New dialplan location */
+	ast_channel_context_set(chan, context);
+	ast_channel_exten_set(chan, exten);
+	ast_channel_priority_set(chan, priority);
+
+	/* set args, if any */
+	if (varshead) {
+		AST_LIST_TRAVERSE(varshead, variable, entries) {
+			pbx_builtin_pushvar_helper(chan, ast_var_name(variable), ast_var_value(variable));
+		}
+	}
+
+        memset(&run_args, 0, sizeof(run_args));
+	if (ast_channel_pbx(chan)) {
+		run_args.use_existing_pbx = 1;
+	}
+	run_args.no_hangup_chan = 1;
+	res = __ast_pbx_run(chan, &run_args);
+
+	/* Allow use of previously set variables.  Ie: if there was previously ARG1,ARG2,etc set on the channel
+	   we want access to those old values since the dialplan we ran is now finished */
+	if (varshead) {
+		AST_LIST_TRAVERSE(varshead, variable, entries) {
+			pbx_builtin_setvar_helper(chan, ast_var_name(variable), NULL);
+		}
+	}
+
+	if (restore_dialplan_location) {
+		/* Restore current dialplan location */
+		ast_channel_context_set(chan, ast_str_buffer(backup_context));
+		ast_channel_exten_set(chan, ast_str_buffer(backup_exten));
+		ast_channel_priority_set(chan, backup_priority);
+
+		ast_free(backup_context);
+		ast_free(backup_exten);
+	}
+
+	return res;
+}




More information about the asterisk-commits mailing list