Skip to content
Snippets Groups Projects
pbx.c 159 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
    
    			set_ext_pri(chan, "t", 0); /* XXX is the 0 correct ? */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    			ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
    			res = -1;
    		}
    
    
    	if (ast_test_flag(&flags, WAITEXTEN_MOH))
    		ast_moh_stop(chan);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    
    /*!
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_background(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    James Golovich's avatar
    James Golovich committed
    	int res = 0;
    
    	char *parse;
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(filename);
    		AST_APP_ARG(options);
    		AST_APP_ARG(lang);
    		AST_APP_ARG(context);
    	);
    
    	if (ast_strlen_zero(data))
    		ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
    
    	if (!(parse = ast_strdupa(data)))
    		return -1;
    
    	AST_STANDARD_APP_ARGS(args, parse);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		args.lang = (char *)chan->language;	/* XXX this is const */
    
    	if (!args.context)
    		args.context = chan->context;
    
    	if (args.options) {
    		if (!strcasecmp(args.options, "skip"))
    
    		else if (!strcasecmp(args.options, "noanswer"))
    
    			ast_app_parse_options(background_opts, &flags, NULL, args.options);
    
    James Golovich's avatar
    James Golovich committed
    
    	/* Answer if need be */
    	if (chan->_state != AST_STATE_UP) {
    
    		if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
    
    James Golovich's avatar
    James Golovich committed
    			return 0;
    
    		} else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
    
    James Golovich's avatar
    James Golovich committed
    			res = ast_answer(chan);
    		}
    	}
    
    	if (!res) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		char *back = args.filename;
    		char *front;
    		ast_stopstream(chan);		/* Stop anything playing */
    		/* Stream the list of files */
    		while (!res && (front = strsep(&back, "&")) ) {
    			if ( (res = ast_streamfile(chan, front, args.lang)) ) {
    
    				ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
    				res = 0;
    				break;
    			}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
    				res = ast_waitstream(chan, "");
    			} else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
    				res = ast_waitstream_exten(chan, args.context);
    			} else {
    				res = ast_waitstream(chan, AST_DIGIT_ANY);
    			}
    			ast_stopstream(chan);
    
    	if (args.context != chan->context && res) {
    
    		snprintf(chan->exten, sizeof(chan->exten), "%c", res);
    
    		ast_copy_string(chan->context, args.context, sizeof(chan->context));
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		res = 0;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	return res;
    
    /*! Goto
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_goto(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int res = ast_parseable_goto(chan, data);
    
    	if (!res && (option_verbose > 2))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
    
    {
    	struct ast_var_t *variables;
    
    	if (!chan)
    		return 0;
    
    	memset(buf, 0, size);
    
    	AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
    		if(variables &&
    		   (var=ast_var_name(variables)) && (val=ast_var_value(variables)) &&
    		   !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
    			if (ast_build_string(&buf, &size, "%s=%s\n", var, val)) {
    				ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		} else
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_var_t *variables;
    
    	int i;
    	struct varshead *places[2] = { NULL, &globals };
    
    		if (!places[i])
    			continue;
    		if (places[i] == &globals)
    			ast_mutex_lock(&globalslock);
    		AST_LIST_TRAVERSE(places[i], variables, entries) {
    			if (!strcmp(name, ast_var_name(variables))) {
    				ret = ast_var_value(variables);
    				break;
    
    		if (places[i] == &globals)
    			ast_mutex_unlock(&globalslock);
    		if (ret)
    			break;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
    {
    	struct ast_var_t *newvariable;
    	struct varshead *headp;
    
    	if (name[strlen(name)-1] == ')') {
    
    		char *function = ast_strdupa(name);
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
    
    		ast_func_write(chan, function, value);
    		return;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	}
    
    	headp = (chan) ? &chan->varshead : &globals;
    
    	if (value) {
    		if ((option_verbose > 1) && (headp == &globals))
    			ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
    
    		newvariable = ast_var_assign(name, value);
    		if (headp == &globals)
    			ast_mutex_lock(&globalslock);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
    
    		if (headp == &globals)
    			ast_mutex_unlock(&globalslock);
    
    void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_var_t *newvariable;
    
    	struct varshead *headp;
    
    	/* XXX may need locking on the channel ? */
    
    	if (name[strlen(name)-1] == ')') {
    		char *function = ast_strdupa(name);
    
    		ast_func_write(chan, function, value);
    		return;
    	}
    
    	headp = (chan) ? &chan->varshead : &globals;
    
    	/* For comparison purposes, we have to strip leading underscores */
    	if (*nametail == '_') {
    		nametail++;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (*nametail == '_')
    
    	if (headp == &globals)
    		ast_mutex_lock(&globalslock);
    
    	AST_LIST_TRAVERSE (headp, newvariable, entries) {
    
    		if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* there is already such a variable, delete it */
    
    			AST_LIST_REMOVE(headp, newvariable, entries);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_var_delete(newvariable);
    			break;
    		}
    
    	if (value) {
    		if ((option_verbose > 1) && (headp == &globals))
    
    			ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		newvariable = ast_var_assign(name, value);
    
    		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
    
    	if (headp == &globals)
    		ast_mutex_unlock(&globalslock);
    
    int pbx_builtin_setvar(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	char *name, *value, *mydata;
    	int argc;
    	char *argv[24];		/* this will only support a maximum of 24 variables being set in a single operation */
    	int global = 0;
    	int x;
    
    		ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
    
    	argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
    
    
    	/* check for a trailing flags argument */
    	if ((argc > 1) && !strchr(argv[argc-1], '=')) {
    		argc--;
    		if (strchr(argv[argc], 'g'))
    			global = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    	for (x = 0; x < argc; x++) {
    		name = argv[x];
    		if ((value = strchr(name, '='))) {
    
    			pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
    		} else
    			ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
    	}
    
    
    int pbx_builtin_importvar(struct ast_channel *chan, void *data)
    {
    	char *name;
    	char *value;
    	char *channel;
    
    	char tmp[VAR_BUF_SIZE]="";
    
    		ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
    		return 0;
    	}
    
    
    	value = ast_strdupa(data);
    	name = strsep(&value,"=");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	channel = strsep(&value,"|");
    
    	if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
    		struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
    
    		if (chan2) {
    
    			char *s = alloca(strlen(value) + 4);
    
    			if (s) {
    				sprintf(s, "${%s}", value);
    				pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
    			}
    
    			ast_channel_unlock(chan2);
    
    		}
    		pbx_builtin_setvar_helper(chan, name, tmp);
    	}
    
    	return(0);
    }
    
    
    /*! \todo XXX overwrites data ? */
    
    static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
    {
    	char *name;
    
    		ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
    		return 0;
    	}
    
    
    	name = strsep(&stringp, "=");
    
    	/*! \todo XXX watch out, leading whitespace ? */
    	pbx_builtin_setvar_helper(NULL, name, stringp);
    
    static int pbx_builtin_noop(struct ast_channel *chan, void *data)
    {
    	return 0;
    }
    
    void pbx_builtin_clear_globals(void)
    {
    	struct ast_var_t *vardata;
    
    
    	ast_mutex_lock(&globalslock);
    	while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
    
    		ast_var_delete(vardata);
    
    	ast_mutex_unlock(&globalslock);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    int pbx_checkcondition(const char *condition)
    
    	if (ast_strlen_zero(condition))	/* NULL or empty strings are false */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    	else if (*condition >= '0' && *condition <= '9')	/* Numbers are evaluated for truth */
    		return atoi(condition);
    
    Russell Bryant's avatar
    Russell Bryant committed
    	else	/* Strings are true */
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
    {
    
    	char *condition, *branch1, *branch2, *branch;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int rc;
    
    	char *stringp;
    
    		ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	condition = strsep(&stringp,"?");
    	branch1 = strsep(&stringp,":");
    	branch2 = strsep(&stringp,"");
    
    	branch = pbx_checkcondition(condition) ? branch1 : branch2;
    
    		ast_log(LOG_DEBUG, "Not taking any branch\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	rc = pbx_builtin_goto(chan, branch);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
    {
    
    	char *number = tmp;
    	char *options;
    
    		ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
    		return -1;
    	}
    
    	ast_copy_string(tmp, data, sizeof(tmp));
    
    	strsep(&number, "|");
    	options = strsep(&number, "|");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (options) {
    		if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
    
    			strcasecmp(options, "c") && strcasecmp(options, "n") ) {
    
    			ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
    			return -1;
    
    	return ast_say_number(chan, atoi(tmp), "", chan->language, options);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
    {
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (data)
    
    		res = ast_say_digit_str(chan, data, "", chan->language);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
    {
    	int res = 0;
    
    		res = ast_say_character_str(chan, data, "", chan->language);
    
    static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
    {
    	int res = 0;
    
    		res = ast_say_phonetic_str(chan, data, "", chan->language);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int load_pbx(void)
    {
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Initialize the PBX */
    	if (option_verbose) {
    		ast_verbose( "Asterisk PBX Core Initializing\n");
    		ast_verbose( "Registering builtin applications:\n");
    	}
    
    	ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(pbx_cli[0]));
    
    
    	/* Register builtin applications */
    	for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_verbose)
    			ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
    			return -1;
    		}
    	}
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Lock context list functions ...
     */
    int ast_lock_contexts()
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_unlock_contexts()
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*
     * Lock context ...
     */
    int ast_lock_context(struct ast_context *con)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_unlock_context(struct ast_context *con)
    {
    
    	return ast_mutex_unlock(&con->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*
     * Name functions ...
     */
    
    const char *ast_get_context_name(struct ast_context *con)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return con ? con->name : NULL;
    }
    
    
    struct ast_context *ast_get_extension_context(struct ast_exten *exten)
    {
    	return exten ? exten->parent : NULL;
    }
    
    
    const char *ast_get_extension_name(struct ast_exten *exten)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return exten ? exten->exten : NULL;
    }
    
    
    const char *ast_get_extension_label(struct ast_exten *exten)
    {
    	return exten ? exten->label : NULL;
    }
    
    const char *ast_get_include_name(struct ast_include *inc)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return inc ? inc->name : NULL;
    }
    
    
    const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return ip ? ip->pattern : NULL;
    }
    
    int ast_get_extension_priority(struct ast_exten *exten)
    {
    	return exten ? exten->priority : -1;
    }
    
    /*
     * Registrar info functions ...
     */
    
    const char *ast_get_context_registrar(struct ast_context *c)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return c ? c->registrar : NULL;
    }
    
    
    const char *ast_get_extension_registrar(struct ast_exten *e)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return e ? e->registrar : NULL;
    }
    
    
    const char *ast_get_include_registrar(struct ast_include *i)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return i ? i->registrar : NULL;
    }
    
    
    const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return ip ? ip->registrar : NULL;
    }
    
    
    int ast_get_extension_matchcid(struct ast_exten *e)
    {
    	return e ? e->matchcid : 0;
    }
    
    
    const char *ast_get_extension_cidmatch(struct ast_exten *e)
    
    const char *ast_get_extension_app(struct ast_exten *e)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return e ? e->app : NULL;
    }
    
    void *ast_get_extension_app_data(struct ast_exten *e)
    {
    	return e ? e->data : NULL;
    }
    
    
    const char *ast_get_switch_name(struct ast_sw *sw)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return sw ? sw->name : NULL;
    }
    
    
    const char *ast_get_switch_data(struct ast_sw *sw)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return sw ? sw->data : NULL;
    }
    
    
    const char *ast_get_switch_registrar(struct ast_sw *sw)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return sw ? sw->registrar : NULL;
    }
    
    /*
     * Walking functions ...
     */
    struct ast_context *ast_walk_contexts(struct ast_context *con)
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	return con ? con->next : contexts;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
    	struct ast_exten *exten)
    {
    	if (!exten)
    		return con ? con->root : NULL;
    	else
    		return exten->next;
    }
    
    struct ast_sw *ast_walk_context_switches(struct ast_context *con,
    	struct ast_sw *sw)
    {
    	if (!sw)
    
    		return con ? AST_LIST_FIRST(&con->alts) : NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    		return AST_LIST_NEXT(sw, list);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
    	struct ast_exten *priority)
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	return priority ? priority->peer : exten;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    struct ast_include *ast_walk_context_includes(struct ast_context *con,
    	struct ast_include *inc)
    {
    	if (!inc)
    		return con ? con->includes : NULL;
    	else
    		return inc->next;
    }
    
    struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
    	struct ast_ignorepat *ip)
    {
    	if (!ip)
    		return con ? con->ignorepats : NULL;
    	else
    		return ip->next;
    }
    
    
    int ast_context_verify_includes(struct ast_context *con)
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct ast_include *inc = NULL;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ( (inc = ast_walk_context_includes(con, inc)) )
    
    		if (!ast_context_find(inc->rname)) {
    			res = -1;
    
    			ast_log(LOG_WARNING, "Context '%s' tries includes nonexistent context '%s'\n",
    
    					ast_get_context_name(con), inc->rname);
    		}
    	return res;
    }
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
    
    	int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
    
    	if (context == NULL)
    		context = chan->context;
    	if (exten == NULL)
    		exten = chan->exten;
    
    
    	goto_func = (async) ? ast_async_goto : ast_explicit_goto;
    
    	if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
    		return goto_func(chan, context, exten, priority);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	else
    
    int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
    {
    
    	return __ast_goto_if_exists(chan, context, exten, priority, 0);
    }
    
    
    int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
    {
    
    	return __ast_goto_if_exists(chan, context, exten, priority, 1);
    }
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
    
    {
    	char *exten, *pri, *context;
    
    	if (ast_strlen_zero(goto_string)) {
    
    		ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
    		return -1;
    	}
    
    	stringp = ast_strdupa(goto_string);
    	context = strsep(&stringp, "|");	/* guaranteed non-null */
    
    	exten = strsep(&stringp, "|");
    
    	pri = strsep(&stringp, "|");
    	if (!exten) {	/* Only a priority in this one */
    
    		pri = context;
    		exten = NULL;
    		context = NULL;
    
    	} else if (!pri) {	/* Only an extension and priority in this one */
    		pri = exten;
    		exten = context;
    		context = NULL;
    
    	}
    	if (*pri == '+') {
    		mode = 1;
    		pri++;
    	} else if (*pri == '-') {
    		mode = -1;
    		pri++;
    	}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
    
    			pri, chan->cid.cid_num)) < 1) {
    			ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
    			return -1;
    		} else
    			mode = 0;
    
    	/* At this point we have a priority and maybe an extension and a context */
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (mode)
    
    		ipri = chan->priority + (ipri * mode);
    
    
    	ast_explicit_goto(chan, context, exten, ipri);
    
    	ast_cdr_update(chan);
    	return 0;
    
    }