summaryrefslogtreecommitdiff
path: root/main/pbx.c
diff options
context:
space:
mode:
authorMark Murawki <markm@intellasoft.net>2012-03-15 18:58:25 +0000
committerMark Murawki <markm@intellasoft.net>2012-03-15 18:58:25 +0000
commitc65b41f57af918f7c6f5bb18c23347d7727a7fbc (patch)
tree702017b489118a6f009c2bd3423e123052398c2e /main/pbx.c
parentc61d49d5cc592e07208fb6d85ba6e6edae455aff (diff)
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 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@359705 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/pbx.c')
-rw-r--r--main/pbx.c269
1 files changed, 254 insertions, 15 deletions
diff --git a/main/pbx.c b/main/pbx.c
index 905310ccf..630ec6c83 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -5105,19 +5105,21 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
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 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
}
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 @@ int ast_pbx_init(void)
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;
+}