[Asterisk-code-review] main/pbx: Split pbx builtin functions to pbx builtins.c (asterisk[master])

George Joseph asteriskteam at digium.com
Mon Dec 28 19:45:45 CST 2015


George Joseph has uploaded a new change for review.

  https://gerrit.asterisk.org/1879

Change subject: main/pbx: Split pbx_builtin functions to pbx_builtins.c
......................................................................

main/pbx: Split pbx_builtin functions to pbx_builtins.c

We joked about splitting pbx.c into multiple files but this
first step was fairly easy.  All of the pbx_builtin functions
have been moved into pbx_functions.c and a new pbx_private.h
file was added.  pbx.c:load_pbx calls load_pbx_builtins with
globals and globalslock as parameters so the pbx_builtins
can use them without making them 'globals'.  Only a few other
functions needed to be cross-exposed.

Change-Id: I87066be3dbf7f5822942ac1449d98cc43fc7561a
---
M main/pbx.c
A main/pbx_builtins.c
A main/pbx_private.h
3 files changed, 1,182 insertions(+), 1,086 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/79/1879/1

diff --git a/main/pbx.c b/main/pbx.c
index 202940e..9e39f1d 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -73,6 +73,7 @@
 #include "asterisk/stasis_channels.h"
 #include "asterisk/dial.h"
 #include "asterisk/vector.h"
+#include "pbx_private.h"
 
 /*!
  * \note I M P O R T A N T :
@@ -850,31 +851,9 @@
 
 #define SWITCH_DATA_LENGTH 256
 
-#define VAR_BUF_SIZE 4096
-
 #define	VAR_NORMAL		1
 #define	VAR_SOFTTRAN	2
 #define	VAR_HARDTRAN	3
-
-#define BACKGROUND_SKIP		(1 << 0)
-#define BACKGROUND_NOANSWER	(1 << 1)
-#define BACKGROUND_MATCHEXTEN	(1 << 2)
-#define BACKGROUND_PLAYBACK	(1 << 3)
-
-AST_APP_OPTIONS(background_opts, {
-	AST_APP_OPTION('s', BACKGROUND_SKIP),
-	AST_APP_OPTION('n', BACKGROUND_NOANSWER),
-	AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
-	AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
-});
-
-#define WAITEXTEN_MOH		(1 << 0)
-#define WAITEXTEN_DIALTONE	(1 << 1)
-
-AST_APP_OPTIONS(waitexten_opts, {
-	AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
-	AST_APP_OPTION_ARG('d', WAITEXTEN_DIALTONE, 0),
-});
 
 struct ast_context;
 struct ast_app;
@@ -1238,7 +1217,6 @@
 	return 0;
 }
 
-
 static const struct cfextension_states {
 	int extension_state;
 	const char * const text;
@@ -1263,34 +1241,10 @@
 	int priority;				/*!< Priority associated with this exception */
 };
 
-static int pbx_builtin_answer(struct ast_channel *, const char *);
-static int pbx_builtin_goto(struct ast_channel *, const char *);
-static int pbx_builtin_hangup(struct ast_channel *, const char *);
-static int pbx_builtin_background(struct ast_channel *, const char *);
-static int pbx_builtin_wait(struct ast_channel *, const char *);
-static int pbx_builtin_waitexten(struct ast_channel *, const char *);
-static int pbx_builtin_incomplete(struct ast_channel *, const char *);
-static int pbx_builtin_setamaflags(struct ast_channel *, const char *);
-static int pbx_builtin_ringing(struct ast_channel *, const char *);
-static int pbx_builtin_proceeding(struct ast_channel *, const char *);
-static int pbx_builtin_progress(struct ast_channel *, const char *);
-static int pbx_builtin_congestion(struct ast_channel *, const char *);
-static int pbx_builtin_busy(struct ast_channel *, const char *);
-static int pbx_builtin_noop(struct ast_channel *, const char *);
-static int pbx_builtin_gotoif(struct ast_channel *, const char *);
-static int pbx_builtin_gotoiftime(struct ast_channel *, const char *);
-static int pbx_builtin_execiftime(struct ast_channel *, const char *);
-static int pbx_builtin_saynumber(struct ast_channel *, const char *);
-static int pbx_builtin_saydigits(struct ast_channel *, const char *);
-static int pbx_builtin_saycharacters(struct ast_channel *, const char *);
-static int pbx_builtin_saycharacters_case(struct ast_channel *, const char *);
-static int pbx_builtin_sayphonetic(struct ast_channel *, const char *);
 static int matchcid(const char *cidpattern, const char *callerid);
 #ifdef NEED_DEBUG
 static void log_match_char_tree(struct match_char *node, char *prefix); /* for use anywhere */
 #endif
-static int pbx_builtin_importvar(struct ast_channel *, const char *);
-static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
 static void new_find_extension(const char *str, struct scoreboard *score,
 		struct match_char *tree, int length, int spec, const char *callerid,
 		const char *label, enum ext_match_t action);
@@ -1418,7 +1372,6 @@
 	return ast_hashtab_hash_string(S_OR(ac->label, ""));
 }
 
-
 AST_RWLOCK_DEFINE_STATIC(globalslock);
 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
 
@@ -1441,43 +1394,6 @@
  * It is sorted by function name.
  */
 static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
-
-/*! \brief Declaration of builtin applications */
-static struct pbx_builtin {
-	char name[AST_MAX_APP];
-	int (*execute)(struct ast_channel *chan, const char *data);
-} builtins[] =
-{
-	/* These applications are built into the PBX core and do not
-	   need separate modules */
-
-	{ "Answer",         pbx_builtin_answer },
-	{ "BackGround",     pbx_builtin_background },
-	{ "Busy",           pbx_builtin_busy },
-	{ "Congestion",     pbx_builtin_congestion },
-	{ "ExecIfTime",     pbx_builtin_execiftime },
-	{ "Goto",           pbx_builtin_goto },
-	{ "GotoIf",         pbx_builtin_gotoif },
-	{ "GotoIfTime",     pbx_builtin_gotoiftime },
-	{ "ImportVar",      pbx_builtin_importvar },
-	{ "Hangup",         pbx_builtin_hangup },
-	{ "Incomplete",     pbx_builtin_incomplete },
-	{ "NoOp",           pbx_builtin_noop },
-	{ "Proceeding",     pbx_builtin_proceeding },
-	{ "Progress",       pbx_builtin_progress },
-	{ "RaiseException", pbx_builtin_raise_exception },
-	{ "Ringing",        pbx_builtin_ringing },
-	{ "SayAlpha",       pbx_builtin_saycharacters },
-	{ "SayAlphaCase",   pbx_builtin_saycharacters_case },
-	{ "SayDigits",      pbx_builtin_saydigits },
-	{ "SayNumber",      pbx_builtin_saynumber },
-	{ "SayPhonetic",    pbx_builtin_sayphonetic },
-	{ "Set",            pbx_builtin_setvar },
-	{ "MSet",           pbx_builtin_setvar_multiple },
-	{ "SetAMAFlags",    pbx_builtin_setamaflags },
-	{ "Wait",           pbx_builtin_wait },
-	{ "WaitExten",      pbx_builtin_waitexten }
-};
 
 static struct ast_context *contexts;
 static struct ast_hashtab *contexts_table = NULL;
@@ -3822,7 +3738,7 @@
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int raise_exception(struct ast_channel *chan, const char *reason, int priority)
+int raise_exception(struct ast_channel *chan, const char *reason, int priority)
 {
 	struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
 	struct pbx_exception *exception = NULL;
@@ -3846,12 +3762,6 @@
 	exception->priority = ast_channel_priority(chan);
 	set_ext_pri(chan, "e", priority);
 	return 0;
-}
-
-int pbx_builtin_raise_exception(struct ast_channel *chan, const char *reason)
-{
-	/* Priority will become 1, next time through the AUTOLOOP */
-	return raise_exception(chan, reason, 0);
 }
 
 static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
@@ -6463,7 +6373,7 @@
 }
 
 /*! helper function to set extension and priority */
-static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
+void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
 {
 	ast_channel_lock(c);
 	ast_channel_exten_set(c, exten);
@@ -11047,7 +10957,7 @@
 	ast_unlock_contexts();
 }
 
-static void wait_for_hangup(struct ast_channel *chan, const void *data)
+void wait_for_hangup(struct ast_channel *chan, const void *data)
 {
 	int res;
 	struct ast_frame *f;
@@ -11067,188 +10977,6 @@
 		if (f)
 			ast_frfree(f);
 	} while(f);
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_proceeding(struct ast_channel *chan, const char *data)
-{
-	ast_indicate(chan, AST_CONTROL_PROCEEDING);
-	return 0;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_progress(struct ast_channel *chan, const char *data)
-{
-	ast_indicate(chan, AST_CONTROL_PROGRESS);
-	return 0;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_ringing(struct ast_channel *chan, const char *data)
-{
-	ast_indicate(chan, AST_CONTROL_RINGING);
-	return 0;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_busy(struct ast_channel *chan, const char *data)
-{
-	ast_indicate(chan, AST_CONTROL_BUSY);
-	/* Don't change state of an UP channel, just indicate
-	   busy in audio */
-	ast_channel_lock(chan);
-	if (ast_channel_state(chan) != AST_STATE_UP) {
-		ast_channel_hangupcause_set(chan, AST_CAUSE_BUSY);
-		ast_setstate(chan, AST_STATE_BUSY);
-	}
-	ast_channel_unlock(chan);
-	wait_for_hangup(chan, data);
-	return -1;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_congestion(struct ast_channel *chan, const char *data)
-{
-	ast_indicate(chan, AST_CONTROL_CONGESTION);
-	/* Don't change state of an UP channel, just indicate
-	   congestion in audio */
-	ast_channel_lock(chan);
-	if (ast_channel_state(chan) != AST_STATE_UP) {
-		ast_channel_hangupcause_set(chan, AST_CAUSE_CONGESTION);
-		ast_setstate(chan, AST_STATE_BUSY);
-	}
-	ast_channel_unlock(chan);
-	wait_for_hangup(chan, data);
-	return -1;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_answer(struct ast_channel *chan, const char *data)
-{
-	int delay = 0;
-	char *parse;
-	AST_DECLARE_APP_ARGS(args,
-		AST_APP_ARG(delay);
-		AST_APP_ARG(answer_cdr);
-	);
-
-	if (ast_strlen_zero(data)) {
-		return __ast_answer(chan, 0);
-	}
-
-	parse = ast_strdupa(data);
-
-	AST_STANDARD_APP_ARGS(args, parse);
-
-	if (!ast_strlen_zero(args.delay) && (ast_channel_state(chan) != AST_STATE_UP))
-		delay = atoi(data);
-
-	if (delay < 0) {
-		delay = 0;
-	}
-
-	if (!ast_strlen_zero(args.answer_cdr) && !strcasecmp(args.answer_cdr, "nocdr")) {
-		ast_log(AST_LOG_WARNING, "The nocdr option for the Answer application has been removed and is no longer supported.\n");
-	}
-
-	return __ast_answer(chan, delay);
-}
-
-static int pbx_builtin_incomplete(struct ast_channel *chan, const char *data)
-{
-	const char *options = data;
-	int answer = 1;
-
-	/* Some channels can receive DTMF in unanswered state; some cannot */
-	if (!ast_strlen_zero(options) && strchr(options, 'n')) {
-		answer = 0;
-	}
-
-	/* If the channel is hungup, stop waiting */
-	if (ast_check_hangup(chan)) {
-		return -1;
-	} else if (ast_channel_state(chan) != AST_STATE_UP && answer) {
-		__ast_answer(chan, 0);
-	}
-
-	ast_indicate(chan, AST_CONTROL_INCOMPLETE);
-
-	return AST_PBX_INCOMPLETE;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_setamaflags(struct ast_channel *chan, const char *data)
-{
-	ast_log(AST_LOG_WARNING, "The SetAMAFlags application is deprecated. Please use the CHANNEL function instead.\n");
-
-	if (ast_strlen_zero(data)) {
-		ast_log(AST_LOG_WARNING, "No parameter passed to SetAMAFlags\n");
-		return 0;
-	}
-	/* Copy the AMA Flags as specified */
-	ast_channel_lock(chan);
-	if (isdigit(data[0])) {
-		int amaflags;
-		if (sscanf(data, "%30d", &amaflags) != 1) {
-			ast_log(AST_LOG_WARNING, "Unable to set AMA flags on channel %s\n", ast_channel_name(chan));
-			ast_channel_unlock(chan);
-			return 0;
-		}
-		ast_channel_amaflags_set(chan, amaflags);
-	} else {
-		ast_channel_amaflags_set(chan, ast_channel_string2amaflag(data));
-	}
-	ast_channel_unlock(chan);
-	return 0;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_hangup(struct ast_channel *chan, const char *data)
-{
-	int cause;
-
-	ast_set_hangupsource(chan, "dialplan/builtin", 0);
-
-	if (!ast_strlen_zero(data)) {
-		cause = ast_str2cause(data);
-		if (cause <= 0) {
-			if (sscanf(data, "%30d", &cause) != 1 || cause <= 0) {
-				ast_log(LOG_WARNING, "Invalid cause given to Hangup(): \"%s\"\n", data);
-				cause = 0;
-			}
-		}
-	} else {
-		cause = 0;
-	}
-
-	ast_channel_lock(chan);
-	if (cause <= 0) {
-		cause = ast_channel_hangupcause(chan);
-		if (cause <= 0) {
-			cause = AST_CAUSE_NORMAL_CLEARING;
-		}
-	}
-	ast_channel_hangupcause_set(chan, cause);
-	ast_softhangup_nolock(chan, AST_SOFTHANGUP_EXPLICIT);
-	ast_channel_unlock(chan);
-
-	return -1;
 }
 
 /*!
@@ -11282,802 +11010,6 @@
 	.name = "TESTTIME",
 	.write = testtime_write,
 };
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_gotoiftime(struct ast_channel *chan, const char *data)
-{
-	char *s, *ts, *branch1, *branch2, *branch;
-	struct ast_timing timing;
-	const char *ctime;
-	struct timeval tv = ast_tvnow();
-	long timesecs;
-
-	if (!chan) {
-		ast_log(LOG_WARNING, "GotoIfTime requires a channel on which to operate\n");
-		return -1;
-	}
-
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n  <time range>,<days of week>,<days of month>,<months>[,<timezone>]?'labeliftrue':'labeliffalse'\n");
-		return -1;
-	}
-
-	ts = s = ast_strdupa(data);
-
-	ast_channel_lock(chan);
-	if ((ctime = pbx_builtin_getvar_helper(chan, "TESTTIME")) && sscanf(ctime, "%ld", &timesecs) == 1) {
-		tv.tv_sec = timesecs;
-	} else if (ctime) {
-		ast_log(LOG_WARNING, "Using current time to evaluate\n");
-		/* Reset when unparseable */
-		pbx_builtin_setvar_helper(chan, "TESTTIME", NULL);
-	}
-	ast_channel_unlock(chan);
-
-	/* Separate the Goto path */
-	strsep(&ts, "?");
-	branch1 = strsep(&ts,":");
-	branch2 = strsep(&ts,"");
-
-	/* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
-	if (ast_build_timing(&timing, s) && ast_check_timing2(&timing, tv)) {
-		branch = branch1;
-	} else {
-		branch = branch2;
-	}
-	ast_destroy_timing(&timing);
-
-	if (ast_strlen_zero(branch)) {
-		ast_debug(1, "Not taking any branch\n");
-		return 0;
-	}
-
-	return pbx_builtin_goto(chan, branch);
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_execiftime(struct ast_channel *chan, const char *data)
-{
-	char *s, *appname;
-	struct ast_timing timing;
-	struct ast_app *app;
-	static const char * const usage = "ExecIfTime requires an argument:\n  <time range>,<days of week>,<days of month>,<months>[,<timezone>]?<appname>[(<appargs>)]";
-
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "%s\n", usage);
-		return -1;
-	}
-
-	appname = ast_strdupa(data);
-
-	s = strsep(&appname, "?");	/* Separate the timerange and application name/data */
-	if (!appname) {	/* missing application */
-		ast_log(LOG_WARNING, "%s\n", usage);
-		return -1;
-	}
-
-	if (!ast_build_timing(&timing, s)) {
-		ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
-		ast_destroy_timing(&timing);
-		return -1;
-	}
-
-	if (!ast_check_timing(&timing))	{ /* outside the valid time window, just return */
-		ast_destroy_timing(&timing);
-		return 0;
-	}
-	ast_destroy_timing(&timing);
-
-	/* now split appname(appargs) */
-	if ((s = strchr(appname, '('))) {
-		char *e;
-		*s++ = '\0';
-		if ((e = strrchr(s, ')')))
-			*e = '\0';
-		else
-			ast_log(LOG_WARNING, "Failed to find closing parenthesis\n");
-	}
-
-
-	if ((app = pbx_findapp(appname))) {
-		return pbx_exec(chan, app, S_OR(s, ""));
-	} else {
-		ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
-		return -1;
-	}
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_wait(struct ast_channel *chan, const char *data)
-{
-	int ms;
-
-	/* Wait for "n" seconds */
-	if (!ast_app_parse_timelen(data, &ms, TIMELEN_SECONDS) && ms > 0) {
-		return ast_safe_sleep(chan, ms);
-	}
-	return 0;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_waitexten(struct ast_channel *chan, const char *data)
-{
-	int ms, res;
-	struct ast_flags flags = {0};
-	char *opts[1] = { NULL };
-	char *parse;
-	AST_DECLARE_APP_ARGS(args,
-		AST_APP_ARG(timeout);
-		AST_APP_ARG(options);
-	);
-
-	if (!ast_strlen_zero(data)) {
-		parse = ast_strdupa(data);
-		AST_STANDARD_APP_ARGS(args, parse);
-	} else
-		memset(&args, 0, sizeof(args));
-
-	if (args.options)
-		ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
-
-	if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
-		ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
-	} else if (ast_test_flag(&flags, WAITEXTEN_MOH)) {
-		ast_indicate_data(chan, AST_CONTROL_HOLD, S_OR(opts[0], NULL),
-			!ast_strlen_zero(opts[0]) ? strlen(opts[0]) + 1 : 0);
-	} else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE)) {
-		struct ast_tone_zone_sound *ts = ast_get_indication_tone(ast_channel_zone(chan), "dial");
-		if (ts) {
-			ast_playtones_start(chan, 0, ts->data, 0);
-			ts = ast_tone_zone_sound_unref(ts);
-		} else {
-			ast_tonepair_start(chan, 350, 440, 0, 0);
-		}
-	}
-	/* Wait for "n" seconds */
-	if (!ast_app_parse_timelen(args.timeout, &ms, TIMELEN_SECONDS) && ms > 0) {
-		/* Yay! */
-	} else if (ast_channel_pbx(chan)) {
-		ms = ast_channel_pbx(chan)->rtimeoutms;
-	} else {
-		ms = 10000;
-	}
-
-	res = ast_waitfordigit(chan, ms);
-	if (!res) {
-		if (ast_check_hangup(chan)) {
-			/* Call is hungup for some reason. */
-			res = -1;
-		} else if (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan) + 1,
-			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
-			ast_verb(3, "Timeout on %s, continuing...\n", ast_channel_name(chan));
-		} else if (ast_exists_extension(chan, ast_channel_context(chan), "t", 1,
-			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
-			ast_verb(3, "Timeout on %s, going to 't'\n", ast_channel_name(chan));
-			set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
-		} else if (ast_exists_extension(chan, ast_channel_context(chan), "e", 1,
-			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
-			raise_exception(chan, "RESPONSETIMEOUT", 0); /* 0 will become 1, next time through the loop */
-		} else {
-			ast_log(LOG_WARNING, "Timeout but no rule 't' or 'e' in context '%s'\n",
-				ast_channel_context(chan));
-			res = -1;
-		}
-	}
-
-	if (ast_test_flag(&flags, WAITEXTEN_MOH))
-		ast_indicate(chan, AST_CONTROL_UNHOLD);
-	else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE))
-		ast_playtones_stop(chan);
-
-	return res;
-}
-
-/*!
- * \ingroup applications
- */
-static int pbx_builtin_background(struct ast_channel *chan, const char *data)
-{
-	int res = 0;
-	int mres = 0;
-	struct ast_flags flags = {0};
-	char *parse, exten[2] = "";
-	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");
-		return -1;
-	}
-
-	parse = ast_strdupa(data);
-
-	AST_STANDARD_APP_ARGS(args, parse);
-
-	if (ast_strlen_zero(args.lang))
-		args.lang = (char *)ast_channel_language(chan);	/* XXX this is const */
-
-	if (ast_strlen_zero(args.context)) {
-		const char *context;
-		ast_channel_lock(chan);
-		if ((context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"))) {
-			args.context = ast_strdupa(context);
-		} else {
-			args.context = ast_strdupa(ast_channel_context(chan));
-		}
-		ast_channel_unlock(chan);
-	}
-
-	if (args.options) {
-		if (!strcasecmp(args.options, "skip"))
-			flags.flags = BACKGROUND_SKIP;
-		else if (!strcasecmp(args.options, "noanswer"))
-			flags.flags = BACKGROUND_NOANSWER;
-		else
-			ast_app_parse_options(background_opts, &flags, NULL, args.options);
-	}
-
-	/* Answer if need be */
-	if (ast_channel_state(chan) != AST_STATE_UP) {
-		if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
-			goto done;
-		} else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
-			res = ast_answer(chan);
-		}
-	}
-
-	if (!res) {
-		char *back = ast_strip(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", ast_channel_name(chan), (char*)data);
-				res = 0;
-				mres = 1;
-				break;
-			}
-			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 the single digit DTMF is an extension in the specified context, then
-	 * go there and signal no DTMF.  Otherwise, we should exit with that DTMF.
-	 * If we're in Macro, we'll exit and seek that DTMF as the beginning of an
-	 * extension in the Macro's calling context.  If we're not in Macro, then
-	 * we'll simply seek that extension in the calling context.  Previously,
-	 * someone complained about the behavior as it related to the interior of a
-	 * Gosub routine, and the fix (#14011) inadvertently broke FreePBX
-	 * (#14940).  This change should fix both of these situations, but with the
-	 * possible incompatibility that if a single digit extension does not exist
-	 * (but a longer extension COULD have matched), it would have previously
-	 * gone immediately to the "i" extension, but will now need to wait for a
-	 * timeout.
-	 *
-	 * Later, we had to add a flag to disable this workaround, because AGI
-	 * users can EXEC Background and reasonably expect that the DTMF code will
-	 * be returned (see #16434).
-	 */
-	if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_WORKAROUNDS)
-		&& (exten[0] = res)
-		&& ast_canmatch_extension(chan, args.context, exten, 1,
-			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))
-		&& !ast_matchmore_extension(chan, args.context, exten, 1,
-			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
-		char buf[2] = { 0, };
-		snprintf(buf, sizeof(buf), "%c", res);
-		ast_channel_exten_set(chan, buf);
-		ast_channel_context_set(chan, args.context);
-		ast_channel_priority_set(chan, 0);
-		res = 0;
-	}
-done:
-	pbx_builtin_setvar_helper(chan, "BACKGROUNDSTATUS", mres ? "FAILED" : "SUCCESS");
-	return res;
-}
-
-/*! Goto
- * \ingroup applications
- */
-static int pbx_builtin_goto(struct ast_channel *chan, const char *data)
-{
-	int res = ast_parseable_goto(chan, data);
-	if (!res)
-		ast_verb(3, "Goto (%s,%s,%d)\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan) + 1);
-	return res;
-}
-
-
-int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
-{
-	struct ast_var_t *variables;
-	const char *var, *val;
-	int total = 0;
-
-	if (!chan)
-		return 0;
-
-	ast_str_reset(*buf);
-
-	ast_channel_lock(chan);
-
-	AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
-		if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
-		   /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
-		   ) {
-			if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
-				ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
-				break;
-			} else
-				total++;
-		} else
-			break;
-	}
-
-	ast_channel_unlock(chan);
-
-	return total;
-}
-
-const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
-{
-	struct ast_var_t *variables;
-	const char *ret = NULL;
-	int i;
-	struct varshead *places[2] = { NULL, &globals };
-
-	if (!name)
-		return NULL;
-
-	if (chan) {
-		ast_channel_lock(chan);
-		places[0] = ast_channel_varshead(chan);
-	}
-
-	for (i = 0; i < 2; i++) {
-		if (!places[i])
-			continue;
-		if (places[i] == &globals)
-			ast_rwlock_rdlock(&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_rwlock_unlock(&globalslock);
-		if (ret)
-			break;
-	}
-
-	if (chan)
-		ast_channel_unlock(chan);
-
-	return ret;
-}
-
-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);
-
-		ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
-		ast_func_write(chan, function, value);
-		return;
-	}
-
-	if (chan) {
-		ast_channel_lock(chan);
-		headp = ast_channel_varshead(chan);
-	} else {
-		ast_rwlock_wrlock(&globalslock);
-		headp = &globals;
-	}
-
-	if (value && (newvariable = ast_var_assign(name, value))) {
-		if (headp == &globals)
-			ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
-		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
-	}
-
-	if (chan)
-		ast_channel_unlock(chan);
-	else
-		ast_rwlock_unlock(&globalslock);
-}
-
-int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
-{
-	struct ast_var_t *newvariable;
-	struct varshead *headp;
-	const char *nametail = name;
-	/*! True if the old value was not an empty string. */
-	int old_value_existed = 0;
-
-	if (name[strlen(name) - 1] == ')') {
-		char *function = ast_strdupa(name);
-
-		return ast_func_write(chan, function, value);
-	}
-
-	if (chan) {
-		ast_channel_lock(chan);
-		headp = ast_channel_varshead(chan);
-	} else {
-		ast_rwlock_wrlock(&globalslock);
-		headp = &globals;
-	}
-
-	/* For comparison purposes, we have to strip leading underscores */
-	if (*nametail == '_') {
-		nametail++;
-		if (*nametail == '_')
-			nametail++;
-	}
-
-	AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
-		if (strcmp(ast_var_name(newvariable), nametail) == 0) {
-			/* there is already such a variable, delete it */
-			AST_LIST_REMOVE_CURRENT(entries);
-			old_value_existed = !ast_strlen_zero(ast_var_value(newvariable));
-			ast_var_delete(newvariable);
-			break;
-		}
-	}
-	AST_LIST_TRAVERSE_SAFE_END;
-
-	if (value && (newvariable = ast_var_assign(name, value))) {
-		if (headp == &globals) {
-			ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
-		}
-		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
-		ast_channel_publish_varset(chan, name, value);
-	} else if (old_value_existed) {
-		/* We just deleted a non-empty dialplan variable. */
-		ast_channel_publish_varset(chan, name, "");
-	}
-
-	if (chan)
-		ast_channel_unlock(chan);
-	else
-		ast_rwlock_unlock(&globalslock);
-	return 0;
-}
-
-int pbx_builtin_setvar(struct ast_channel *chan, const char *data)
-{
-	char *name, *value, *mydata;
-
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
-		return 0;
-	}
-
-	mydata = ast_strdupa(data);
-	name = strsep(&mydata, "=");
-	value = mydata;
-	if (!value) {
-		ast_log(LOG_WARNING, "Set requires an '=' to be a valid assignment.\n");
-		return 0;
-	}
-
-	if (strchr(name, ' ')) {
-		ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
-	}
-
-	pbx_builtin_setvar_helper(chan, name, value);
-
-	return 0;
-}
-
-int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *vdata)
-{
-	char *data;
-	int x;
-	AST_DECLARE_APP_ARGS(args,
-		AST_APP_ARG(pair)[24];
-	);
-	AST_DECLARE_APP_ARGS(pair,
-		AST_APP_ARG(name);
-		AST_APP_ARG(value);
-	);
-
-	if (ast_strlen_zero(vdata)) {
-		ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
-		return 0;
-	}
-
-	data = ast_strdupa(vdata);
-	AST_STANDARD_APP_ARGS(args, data);
-
-	for (x = 0; x < args.argc; x++) {
-		AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
-		if (pair.argc == 2) {
-			pbx_builtin_setvar_helper(chan, pair.name, pair.value);
-			if (strchr(pair.name, ' '))
-				ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
-		} else if (!chan) {
-			ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '='\n", pair.name);
-		} else {
-			ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan));
-		}
-	}
-
-	return 0;
-}
-
-int pbx_builtin_importvar(struct ast_channel *chan, const char *data)
-{
-	char *name;
-	char *value;
-	char *channel;
-	char tmp[VAR_BUF_SIZE];
-	static int deprecation_warning = 0;
-
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
-		return 0;
-	}
-	tmp[0] = 0;
-	if (!deprecation_warning) {
-		ast_log(LOG_WARNING, "ImportVar is deprecated.  Please use Set(varname=${IMPORT(channel,variable)}) instead.\n");
-		deprecation_warning = 1;
-	}
-
-	value = ast_strdupa(data);
-	name = strsep(&value,"=");
-	channel = strsep(&value,",");
-	if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
-		struct ast_channel *chan2 = ast_channel_get_by_name(channel);
-		if (chan2) {
-			char *s = ast_alloca(strlen(value) + 4);
-			sprintf(s, "${%s}", value);
-			pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
-			chan2 = ast_channel_unref(chan2);
-		}
-		pbx_builtin_setvar_helper(chan, name, tmp);
-	}
-
-	return(0);
-}
-
-static int pbx_builtin_noop(struct ast_channel *chan, const char *data)
-{
-	return 0;
-}
-
-void pbx_builtin_clear_globals(void)
-{
-	struct ast_var_t *vardata;
-
-	ast_rwlock_wrlock(&globalslock);
-	while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
-		ast_var_delete(vardata);
-	ast_rwlock_unlock(&globalslock);
-}
-
-int pbx_checkcondition(const char *condition)
-{
-	int res;
-	if (ast_strlen_zero(condition)) {                /* NULL or empty strings are false */
-		return 0;
-	} else if (sscanf(condition, "%30d", &res) == 1) { /* Numbers are evaluated for truth */
-		return res;
-	} else {                                         /* Strings are true */
-		return 1;
-	}
-}
-
-static int pbx_builtin_gotoif(struct ast_channel *chan, const char *data)
-{
-	char *condition, *branch1, *branch2, *branch;
-	char *stringp;
-
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
-		return 0;
-	}
-
-	stringp = ast_strdupa(data);
-	condition = strsep(&stringp,"?");
-	branch1 = strsep(&stringp,":");
-	branch2 = strsep(&stringp,"");
-	branch = pbx_checkcondition(condition) ? branch1 : branch2;
-
-	if (ast_strlen_zero(branch)) {
-		ast_debug(1, "Not taking any branch\n");
-		return 0;
-	}
-
-	return pbx_builtin_goto(chan, branch);
-}
-
-static int pbx_builtin_saynumber(struct ast_channel *chan, const char *data)
-{
-	char tmp[256];
-	char *number = tmp;
-	int number_val;
-	char *options;
-	int res;
-	int interrupt = 0;
-	const char *interrupt_string;
-
-	ast_channel_lock(chan);
-	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
-	if (ast_true(interrupt_string)) {
-		interrupt = 1;
-	}
-	ast_channel_unlock(chan);
-
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
-		return -1;
-	}
-	ast_copy_string(tmp, data, sizeof(tmp));
-	strsep(&number, ",");
-
-	if (sscanf(tmp, "%d", &number_val) != 1) {
-		ast_log(LOG_WARNING, "argument '%s' to SayNumber could not be parsed as a number.\n", tmp);
-		return 0;
-	}
-
-	options = strsep(&number, ",");
-	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;
-		}
-	}
-
-	res = ast_say_number(chan, number_val, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), options);
-
-	if (res < 0) {
-		ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
-	}
-
-	return interrupt ? res : 0;
-}
-
-static int pbx_builtin_saydigits(struct ast_channel *chan, const char *data)
-{
-	int res = 0;
-	int interrupt = 0;
-	const char *interrupt_string;
-
-	ast_channel_lock(chan);
-	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
-	if (ast_true(interrupt_string)) {
-		interrupt = 1;
-	}
-	ast_channel_unlock(chan);
-
-	if (data) {
-		res = ast_say_digit_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan));
-	}
-
-	return res;
-}
-
-static int pbx_builtin_saycharacters_case(struct ast_channel *chan, const char *data)
-{
-	int res = 0;
-	int sensitivity = 0;
-	char *parse;
-	int interrupt = 0;
-	const char *interrupt_string;
-
-	AST_DECLARE_APP_ARGS(args,
-		AST_APP_ARG(options);
-		AST_APP_ARG(characters);
-	);
-
-	ast_channel_lock(chan);
-	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
-	if (ast_true(interrupt_string)) {
-		interrupt = 1;
-	}
-	ast_channel_unlock(chan);
-
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "SayAlphaCase requires two arguments (options, characters)\n");
-		return 0;
-	}
-
-	parse = ast_strdupa(data);
-	AST_STANDARD_APP_ARGS(args, parse);
-
-	if (!args.options || strlen(args.options) != 1) {
-		ast_log(LOG_WARNING, "SayAlphaCase options are mutually exclusive and required\n");
-		return 0;
-	}
-
-	switch (args.options[0]) {
-	case 'a':
-		sensitivity = AST_SAY_CASE_ALL;
-		break;
-	case 'l':
-		sensitivity = AST_SAY_CASE_LOWER;
-		break;
-	case 'n':
-		sensitivity = AST_SAY_CASE_NONE;
-		break;
-	case 'u':
-		sensitivity = AST_SAY_CASE_UPPER;
-		break;
-	default:
-		ast_log(LOG_WARNING, "Invalid option: '%s'\n", args.options);
-		return 0;
-	}
-
-	res = ast_say_character_str(chan, args.characters, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), sensitivity);
-
-	return res;
-}
-
-static int pbx_builtin_saycharacters(struct ast_channel *chan, const char *data)
-{
-	int res = 0;
-	int interrupt = 0;
-	const char *interrupt_string;
-
-	ast_channel_lock(chan);
-	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
-	if (ast_true(interrupt_string)) {
-		interrupt = 1;
-	}
-	ast_channel_unlock(chan);
-
-	if (data) {
-		res = ast_say_character_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), AST_SAY_CASE_NONE);
-	}
-
-	return res;
-}
-
-static int pbx_builtin_sayphonetic(struct ast_channel *chan, const char *data)
-{
-	int res = 0;
-	int interrupt = 0;
-	const char *interrupt_string;
-
-	ast_channel_lock(chan);
-	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
-	if (ast_true(interrupt_string)) {
-		interrupt = 1;
-	}
-	ast_channel_unlock(chan);
-
-	if (data)
-		res = ast_say_phonetic_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan));
-	return res;
-}
 
 static void presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
@@ -12315,15 +11247,11 @@
  */
 static void unload_pbx(void)
 {
-	int x;
-
 	presence_state_sub = stasis_unsubscribe_and_join(presence_state_sub);
 	device_state_sub = stasis_unsubscribe_and_join(device_state_sub);
 
-	/* Unregister builtin applications */
-	for (x = 0; x < ARRAY_LEN(builtins); x++) {
-		ast_unregister_application(builtins[x].name);
-	}
+	unload_pbx_builtins();
+
 	ast_manager_unregister("ShowDialPlan");
 	ast_manager_unregister("ExtensionStateList");
 	ast_cli_unregister_multiple(pbx_cli, ARRAY_LEN(pbx_cli));
@@ -12335,7 +11263,6 @@
 int load_pbx(void)
 {
 	int res = 0;
-	int x;
 
 	ast_register_cleanup(unload_pbx);
 
@@ -12348,13 +11275,8 @@
 	__ast_custom_function_register(&exception_function, NULL);
 	__ast_custom_function_register(&testtime_function, NULL);
 
-	/* Register builtin applications */
-	for (x = 0; x < ARRAY_LEN(builtins); x++) {
-		if (ast_register_application2(builtins[x].name, builtins[x].execute, NULL, NULL, NULL)) {
-			ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
-			return -1;
-		}
-	}
+	/* Register builtin functions */
+	res |= load_pbx_builtins(&globals, &globalslock);
 
 	/* Register manager application */
 	res |= ast_manager_register_xml_core("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan);
diff --git a/main/pbx_builtins.c b/main/pbx_builtins.c
new file mode 100644
index 0000000..402f5c9
--- /dev/null
+++ b/main/pbx_builtins.c
@@ -0,0 +1,1134 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015 Fairview 5 Engineering, LLC
+ *
+ * George Joseph <george.joseph at fairview5.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Core PBX builtin routines.
+ *
+ * \author George Joseph <george.joseph at fairview5.com>
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+
+#include "asterisk/pbx.h"
+#include "asterisk/causes.h"
+#include "asterisk/indications.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/say.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "pbx_private.h"
+
+#define BACKGROUND_SKIP		(1 << 0)
+#define BACKGROUND_NOANSWER	(1 << 1)
+#define BACKGROUND_MATCHEXTEN	(1 << 2)
+#define BACKGROUND_PLAYBACK	(1 << 3)
+
+AST_APP_OPTIONS(background_opts, {
+	AST_APP_OPTION('s', BACKGROUND_SKIP),
+	AST_APP_OPTION('n', BACKGROUND_NOANSWER),
+	AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
+	AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
+});
+
+#define WAITEXTEN_MOH		(1 << 0)
+#define WAITEXTEN_DIALTONE	(1 << 1)
+
+AST_APP_OPTIONS(waitexten_opts, {
+	AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
+	AST_APP_OPTION_ARG('d', WAITEXTEN_DIALTONE, 0),
+});
+
+static ast_rwlock_t *globalslock;
+struct varshead *globals;
+
+static int pbx_builtin_answer(struct ast_channel *, const char *);
+static int pbx_builtin_goto(struct ast_channel *, const char *);
+static int pbx_builtin_hangup(struct ast_channel *, const char *);
+static int pbx_builtin_background(struct ast_channel *, const char *);
+static int pbx_builtin_wait(struct ast_channel *, const char *);
+static int pbx_builtin_waitexten(struct ast_channel *, const char *);
+static int pbx_builtin_incomplete(struct ast_channel *, const char *);
+static int pbx_builtin_setamaflags(struct ast_channel *, const char *);
+static int pbx_builtin_ringing(struct ast_channel *, const char *);
+static int pbx_builtin_proceeding(struct ast_channel *, const char *);
+static int pbx_builtin_progress(struct ast_channel *, const char *);
+static int pbx_builtin_noop(struct ast_channel *, const char *);
+static int pbx_builtin_gotoif(struct ast_channel *, const char *);
+static int pbx_builtin_gotoiftime(struct ast_channel *, const char *);
+static int pbx_builtin_execiftime(struct ast_channel *, const char *);
+static int pbx_builtin_saynumber(struct ast_channel *, const char *);
+static int pbx_builtin_saydigits(struct ast_channel *, const char *);
+static int pbx_builtin_saycharacters(struct ast_channel *, const char *);
+static int pbx_builtin_saycharacters_case(struct ast_channel *, const char *);
+static int pbx_builtin_sayphonetic(struct ast_channel *, const char *);
+static int pbx_builtin_importvar(struct ast_channel *, const char *);
+
+/*! \brief Declaration of builtin applications */
+struct pbx_builtin {
+	char name[AST_MAX_APP];
+	int (*execute)(struct ast_channel *chan, const char *data);
+} builtins[] =
+{
+	/* These applications are built into the PBX core and do not
+	   need separate modules */
+
+	{ "Answer",         pbx_builtin_answer },
+	{ "BackGround",     pbx_builtin_background },
+	{ "Busy",           pbx_builtin_busy },
+	{ "Congestion",     pbx_builtin_congestion },
+	{ "ExecIfTime",     pbx_builtin_execiftime },
+	{ "Goto",           pbx_builtin_goto },
+	{ "GotoIf",         pbx_builtin_gotoif },
+	{ "GotoIfTime",     pbx_builtin_gotoiftime },
+	{ "ImportVar",      pbx_builtin_importvar },
+	{ "Hangup",         pbx_builtin_hangup },
+	{ "Incomplete",     pbx_builtin_incomplete },
+	{ "NoOp",           pbx_builtin_noop },
+	{ "Proceeding",     pbx_builtin_proceeding },
+	{ "Progress",       pbx_builtin_progress },
+	{ "RaiseException", pbx_builtin_raise_exception },
+	{ "Ringing",        pbx_builtin_ringing },
+	{ "SayAlpha",       pbx_builtin_saycharacters },
+	{ "SayAlphaCase",   pbx_builtin_saycharacters_case },
+	{ "SayDigits",      pbx_builtin_saydigits },
+	{ "SayNumber",      pbx_builtin_saynumber },
+	{ "SayPhonetic",    pbx_builtin_sayphonetic },
+	{ "Set",            pbx_builtin_setvar },
+	{ "MSet",           pbx_builtin_setvar_multiple },
+	{ "SetAMAFlags",    pbx_builtin_setamaflags },
+	{ "Wait",           pbx_builtin_wait },
+	{ "WaitExten",      pbx_builtin_waitexten }
+};
+
+int pbx_builtin_raise_exception(struct ast_channel *chan, const char *reason)
+{
+	/* Priority will become 1, next time through the AUTOLOOP */
+	return raise_exception(chan, reason, 0);
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_proceeding(struct ast_channel *chan, const char *data)
+{
+	ast_indicate(chan, AST_CONTROL_PROCEEDING);
+	return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_progress(struct ast_channel *chan, const char *data)
+{
+	ast_indicate(chan, AST_CONTROL_PROGRESS);
+	return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_ringing(struct ast_channel *chan, const char *data)
+{
+	ast_indicate(chan, AST_CONTROL_RINGING);
+	return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+int pbx_builtin_busy(struct ast_channel *chan, const char *data)
+{
+	ast_indicate(chan, AST_CONTROL_BUSY);
+	/* Don't change state of an UP channel, just indicate
+	   busy in audio */
+	ast_channel_lock(chan);
+	if (ast_channel_state(chan) != AST_STATE_UP) {
+		ast_channel_hangupcause_set(chan, AST_CAUSE_BUSY);
+		ast_setstate(chan, AST_STATE_BUSY);
+	}
+	ast_channel_unlock(chan);
+	wait_for_hangup(chan, data);
+	return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+int pbx_builtin_congestion(struct ast_channel *chan, const char *data)
+{
+	ast_indicate(chan, AST_CONTROL_CONGESTION);
+	/* Don't change state of an UP channel, just indicate
+	   congestion in audio */
+	ast_channel_lock(chan);
+	if (ast_channel_state(chan) != AST_STATE_UP) {
+		ast_channel_hangupcause_set(chan, AST_CAUSE_CONGESTION);
+		ast_setstate(chan, AST_STATE_BUSY);
+	}
+	ast_channel_unlock(chan);
+	wait_for_hangup(chan, data);
+	return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_answer(struct ast_channel *chan, const char *data)
+{
+	int delay = 0;
+	char *parse;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(delay);
+		AST_APP_ARG(answer_cdr);
+	);
+
+	if (ast_strlen_zero(data)) {
+		return __ast_answer(chan, 0);
+	}
+
+	parse = ast_strdupa(data);
+
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (!ast_strlen_zero(args.delay) && (ast_channel_state(chan) != AST_STATE_UP))
+		delay = atoi(data);
+
+	if (delay < 0) {
+		delay = 0;
+	}
+
+	if (!ast_strlen_zero(args.answer_cdr) && !strcasecmp(args.answer_cdr, "nocdr")) {
+		ast_log(AST_LOG_WARNING, "The nocdr option for the Answer application has been removed and is no longer supported.\n");
+	}
+
+	return __ast_answer(chan, delay);
+}
+
+static int pbx_builtin_incomplete(struct ast_channel *chan, const char *data)
+{
+	const char *options = data;
+	int answer = 1;
+
+	/* Some channels can receive DTMF in unanswered state; some cannot */
+	if (!ast_strlen_zero(options) && strchr(options, 'n')) {
+		answer = 0;
+	}
+
+	/* If the channel is hungup, stop waiting */
+	if (ast_check_hangup(chan)) {
+		return -1;
+	} else if (ast_channel_state(chan) != AST_STATE_UP && answer) {
+		__ast_answer(chan, 0);
+	}
+
+	ast_indicate(chan, AST_CONTROL_INCOMPLETE);
+
+	return AST_PBX_INCOMPLETE;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_setamaflags(struct ast_channel *chan, const char *data)
+{
+	ast_log(AST_LOG_WARNING, "The SetAMAFlags application is deprecated. Please use the CHANNEL function instead.\n");
+
+	if (ast_strlen_zero(data)) {
+		ast_log(AST_LOG_WARNING, "No parameter passed to SetAMAFlags\n");
+		return 0;
+	}
+	/* Copy the AMA Flags as specified */
+	ast_channel_lock(chan);
+	if (isdigit(data[0])) {
+		int amaflags;
+		if (sscanf(data, "%30d", &amaflags) != 1) {
+			ast_log(AST_LOG_WARNING, "Unable to set AMA flags on channel %s\n", ast_channel_name(chan));
+			ast_channel_unlock(chan);
+			return 0;
+		}
+		ast_channel_amaflags_set(chan, amaflags);
+	} else {
+		ast_channel_amaflags_set(chan, ast_channel_string2amaflag(data));
+	}
+	ast_channel_unlock(chan);
+	return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_hangup(struct ast_channel *chan, const char *data)
+{
+	int cause;
+
+	ast_set_hangupsource(chan, "dialplan/builtin", 0);
+
+	if (!ast_strlen_zero(data)) {
+		cause = ast_str2cause(data);
+		if (cause <= 0) {
+			if (sscanf(data, "%30d", &cause) != 1 || cause <= 0) {
+				ast_log(LOG_WARNING, "Invalid cause given to Hangup(): \"%s\"\n", data);
+				cause = 0;
+			}
+		}
+	} else {
+		cause = 0;
+	}
+
+	ast_channel_lock(chan);
+	if (cause <= 0) {
+		cause = ast_channel_hangupcause(chan);
+		if (cause <= 0) {
+			cause = AST_CAUSE_NORMAL_CLEARING;
+		}
+	}
+	ast_channel_hangupcause_set(chan, cause);
+	ast_softhangup_nolock(chan, AST_SOFTHANGUP_EXPLICIT);
+	ast_channel_unlock(chan);
+
+	return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_gotoiftime(struct ast_channel *chan, const char *data)
+{
+	char *s, *ts, *branch1, *branch2, *branch;
+	struct ast_timing timing;
+	const char *ctime;
+	struct timeval tv = ast_tvnow();
+	long timesecs;
+
+	if (!chan) {
+		ast_log(LOG_WARNING, "GotoIfTime requires a channel on which to operate\n");
+		return -1;
+	}
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n  <time range>,<days of week>,<days of month>,<months>[,<timezone>]?'labeliftrue':'labeliffalse'\n");
+		return -1;
+	}
+
+	ts = s = ast_strdupa(data);
+
+	ast_channel_lock(chan);
+	if ((ctime = pbx_builtin_getvar_helper(chan, "TESTTIME")) && sscanf(ctime, "%ld", &timesecs) == 1) {
+		tv.tv_sec = timesecs;
+	} else if (ctime) {
+		ast_log(LOG_WARNING, "Using current time to evaluate\n");
+		/* Reset when unparseable */
+		pbx_builtin_setvar_helper(chan, "TESTTIME", NULL);
+	}
+	ast_channel_unlock(chan);
+
+	/* Separate the Goto path */
+	strsep(&ts, "?");
+	branch1 = strsep(&ts,":");
+	branch2 = strsep(&ts,"");
+
+	/* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
+	if (ast_build_timing(&timing, s) && ast_check_timing2(&timing, tv)) {
+		branch = branch1;
+	} else {
+		branch = branch2;
+	}
+	ast_destroy_timing(&timing);
+
+	if (ast_strlen_zero(branch)) {
+		ast_debug(1, "Not taking any branch\n");
+		return 0;
+	}
+
+	return pbx_builtin_goto(chan, branch);
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_execiftime(struct ast_channel *chan, const char *data)
+{
+	char *s, *appname;
+	struct ast_timing timing;
+	struct ast_app *app;
+	static const char * const usage = "ExecIfTime requires an argument:\n  <time range>,<days of week>,<days of month>,<months>[,<timezone>]?<appname>[(<appargs>)]";
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "%s\n", usage);
+		return -1;
+	}
+
+	appname = ast_strdupa(data);
+
+	s = strsep(&appname, "?");	/* Separate the timerange and application name/data */
+	if (!appname) {	/* missing application */
+		ast_log(LOG_WARNING, "%s\n", usage);
+		return -1;
+	}
+
+	if (!ast_build_timing(&timing, s)) {
+		ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
+		ast_destroy_timing(&timing);
+		return -1;
+	}
+
+	if (!ast_check_timing(&timing))	{ /* outside the valid time window, just return */
+		ast_destroy_timing(&timing);
+		return 0;
+	}
+	ast_destroy_timing(&timing);
+
+	/* now split appname(appargs) */
+	if ((s = strchr(appname, '('))) {
+		char *e;
+		*s++ = '\0';
+		if ((e = strrchr(s, ')')))
+			*e = '\0';
+		else
+			ast_log(LOG_WARNING, "Failed to find closing parenthesis\n");
+	}
+
+
+	if ((app = pbx_findapp(appname))) {
+		return pbx_exec(chan, app, S_OR(s, ""));
+	} else {
+		ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
+		return -1;
+	}
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_wait(struct ast_channel *chan, const char *data)
+{
+	int ms;
+
+	/* Wait for "n" seconds */
+	if (!ast_app_parse_timelen(data, &ms, TIMELEN_SECONDS) && ms > 0) {
+		return ast_safe_sleep(chan, ms);
+	}
+	return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_waitexten(struct ast_channel *chan, const char *data)
+{
+	int ms, res;
+	struct ast_flags flags = {0};
+	char *opts[1] = { NULL };
+	char *parse;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(timeout);
+		AST_APP_ARG(options);
+	);
+
+	if (!ast_strlen_zero(data)) {
+		parse = ast_strdupa(data);
+		AST_STANDARD_APP_ARGS(args, parse);
+	} else
+		memset(&args, 0, sizeof(args));
+
+	if (args.options)
+		ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
+
+	if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
+		ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
+	} else if (ast_test_flag(&flags, WAITEXTEN_MOH)) {
+		ast_indicate_data(chan, AST_CONTROL_HOLD, S_OR(opts[0], NULL),
+			!ast_strlen_zero(opts[0]) ? strlen(opts[0]) + 1 : 0);
+	} else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE)) {
+		struct ast_tone_zone_sound *ts = ast_get_indication_tone(ast_channel_zone(chan), "dial");
+		if (ts) {
+			ast_playtones_start(chan, 0, ts->data, 0);
+			ts = ast_tone_zone_sound_unref(ts);
+		} else {
+			ast_tonepair_start(chan, 350, 440, 0, 0);
+		}
+	}
+	/* Wait for "n" seconds */
+	if (!ast_app_parse_timelen(args.timeout, &ms, TIMELEN_SECONDS) && ms > 0) {
+		/* Yay! */
+	} else if (ast_channel_pbx(chan)) {
+		ms = ast_channel_pbx(chan)->rtimeoutms;
+	} else {
+		ms = 10000;
+	}
+
+	res = ast_waitfordigit(chan, ms);
+	if (!res) {
+		if (ast_check_hangup(chan)) {
+			/* Call is hungup for some reason. */
+			res = -1;
+		} else if (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan) + 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
+			ast_verb(3, "Timeout on %s, continuing...\n", ast_channel_name(chan));
+		} else if (ast_exists_extension(chan, ast_channel_context(chan), "t", 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
+			ast_verb(3, "Timeout on %s, going to 't'\n", ast_channel_name(chan));
+			set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
+		} else if (ast_exists_extension(chan, ast_channel_context(chan), "e", 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
+			raise_exception(chan, "RESPONSETIMEOUT", 0); /* 0 will become 1, next time through the loop */
+		} else {
+			ast_log(LOG_WARNING, "Timeout but no rule 't' or 'e' in context '%s'\n",
+				ast_channel_context(chan));
+			res = -1;
+		}
+	}
+
+	if (ast_test_flag(&flags, WAITEXTEN_MOH))
+		ast_indicate(chan, AST_CONTROL_UNHOLD);
+	else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE))
+		ast_playtones_stop(chan);
+
+	return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_background(struct ast_channel *chan, const char *data)
+{
+	int res = 0;
+	int mres = 0;
+	struct ast_flags flags = {0};
+	char *parse, exten[2] = "";
+	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");
+		return -1;
+	}
+
+	parse = ast_strdupa(data);
+
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (ast_strlen_zero(args.lang))
+		args.lang = (char *)ast_channel_language(chan);	/* XXX this is const */
+
+	if (ast_strlen_zero(args.context)) {
+		const char *context;
+		ast_channel_lock(chan);
+		if ((context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"))) {
+			args.context = ast_strdupa(context);
+		} else {
+			args.context = ast_strdupa(ast_channel_context(chan));
+		}
+		ast_channel_unlock(chan);
+	}
+
+	if (args.options) {
+		if (!strcasecmp(args.options, "skip"))
+			flags.flags = BACKGROUND_SKIP;
+		else if (!strcasecmp(args.options, "noanswer"))
+			flags.flags = BACKGROUND_NOANSWER;
+		else
+			ast_app_parse_options(background_opts, &flags, NULL, args.options);
+	}
+
+	/* Answer if need be */
+	if (ast_channel_state(chan) != AST_STATE_UP) {
+		if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
+			goto done;
+		} else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
+			res = ast_answer(chan);
+		}
+	}
+
+	if (!res) {
+		char *back = ast_strip(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", ast_channel_name(chan), (char*)data);
+				res = 0;
+				mres = 1;
+				break;
+			}
+			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 the single digit DTMF is an extension in the specified context, then
+	 * go there and signal no DTMF.  Otherwise, we should exit with that DTMF.
+	 * If we're in Macro, we'll exit and seek that DTMF as the beginning of an
+	 * extension in the Macro's calling context.  If we're not in Macro, then
+	 * we'll simply seek that extension in the calling context.  Previously,
+	 * someone complained about the behavior as it related to the interior of a
+	 * Gosub routine, and the fix (#14011) inadvertently broke FreePBX
+	 * (#14940).  This change should fix both of these situations, but with the
+	 * possible incompatibility that if a single digit extension does not exist
+	 * (but a longer extension COULD have matched), it would have previously
+	 * gone immediately to the "i" extension, but will now need to wait for a
+	 * timeout.
+	 *
+	 * Later, we had to add a flag to disable this workaround, because AGI
+	 * users can EXEC Background and reasonably expect that the DTMF code will
+	 * be returned (see #16434).
+	 */
+	if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_WORKAROUNDS)
+		&& (exten[0] = res)
+		&& ast_canmatch_extension(chan, args.context, exten, 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))
+		&& !ast_matchmore_extension(chan, args.context, exten, 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
+		char buf[2] = { 0, };
+		snprintf(buf, sizeof(buf), "%c", res);
+		ast_channel_exten_set(chan, buf);
+		ast_channel_context_set(chan, args.context);
+		ast_channel_priority_set(chan, 0);
+		res = 0;
+	}
+done:
+	pbx_builtin_setvar_helper(chan, "BACKGROUNDSTATUS", mres ? "FAILED" : "SUCCESS");
+	return res;
+}
+
+/*! Goto
+ * \ingroup applications
+ */
+static int pbx_builtin_goto(struct ast_channel *chan, const char *data)
+{
+	int res = ast_parseable_goto(chan, data);
+	if (!res)
+		ast_verb(3, "Goto (%s,%s,%d)\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan) + 1);
+	return res;
+}
+
+
+int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
+{
+	struct ast_var_t *variables;
+	const char *var, *val;
+	int total = 0;
+
+	if (!chan)
+		return 0;
+
+	ast_str_reset(*buf);
+
+	ast_channel_lock(chan);
+
+	AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
+		if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
+		   /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
+		   ) {
+			if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
+				ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+				break;
+			} else
+				total++;
+		} else
+			break;
+	}
+
+	ast_channel_unlock(chan);
+
+	return total;
+}
+
+const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
+{
+	struct ast_var_t *variables;
+	const char *ret = NULL;
+	int i;
+	struct varshead *places[2] = { NULL, globals };
+
+	if (!name)
+		return NULL;
+
+	if (chan) {
+		ast_channel_lock(chan);
+		places[0] = ast_channel_varshead(chan);
+	}
+
+	for (i = 0; i < 2; i++) {
+		if (!places[i])
+			continue;
+		if (places[i] == globals)
+			ast_rwlock_rdlock(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_rwlock_unlock(globalslock);
+		if (ret)
+			break;
+	}
+
+	if (chan)
+		ast_channel_unlock(chan);
+
+	return ret;
+}
+
+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);
+
+		ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
+		ast_func_write(chan, function, value);
+		return;
+	}
+
+	if (chan) {
+		ast_channel_lock(chan);
+		headp = ast_channel_varshead(chan);
+	} else {
+		ast_rwlock_wrlock(globalslock);
+		headp = globals;
+	}
+
+	if (value && (newvariable = ast_var_assign(name, value))) {
+		if (headp == globals)
+			ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+	}
+
+	if (chan)
+		ast_channel_unlock(chan);
+	else
+		ast_rwlock_unlock(globalslock);
+}
+
+int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+	struct ast_var_t *newvariable;
+	struct varshead *headp;
+	const char *nametail = name;
+	/*! True if the old value was not an empty string. */
+	int old_value_existed = 0;
+
+	if (name[strlen(name) - 1] == ')') {
+		char *function = ast_strdupa(name);
+
+		return ast_func_write(chan, function, value);
+	}
+
+	if (chan) {
+		ast_channel_lock(chan);
+		headp = ast_channel_varshead(chan);
+	} else {
+		ast_rwlock_wrlock(globalslock);
+		headp = globals;
+	}
+
+	/* For comparison purposes, we have to strip leading underscores */
+	if (*nametail == '_') {
+		nametail++;
+		if (*nametail == '_')
+			nametail++;
+	}
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
+		if (strcmp(ast_var_name(newvariable), nametail) == 0) {
+			/* there is already such a variable, delete it */
+			AST_LIST_REMOVE_CURRENT(entries);
+			old_value_existed = !ast_strlen_zero(ast_var_value(newvariable));
+			ast_var_delete(newvariable);
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	if (value && (newvariable = ast_var_assign(name, value))) {
+		if (headp == globals) {
+			ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+		}
+		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+		ast_channel_publish_varset(chan, name, value);
+	} else if (old_value_existed) {
+		/* We just deleted a non-empty dialplan variable. */
+		ast_channel_publish_varset(chan, name, "");
+	}
+
+	if (chan)
+		ast_channel_unlock(chan);
+	else
+		ast_rwlock_unlock(globalslock);
+	return 0;
+}
+
+int pbx_builtin_setvar(struct ast_channel *chan, const char *data)
+{
+	char *name, *value, *mydata;
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
+		return 0;
+	}
+
+	mydata = ast_strdupa(data);
+	name = strsep(&mydata, "=");
+	value = mydata;
+	if (!value) {
+		ast_log(LOG_WARNING, "Set requires an '=' to be a valid assignment.\n");
+		return 0;
+	}
+
+	if (strchr(name, ' ')) {
+		ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
+	}
+
+	pbx_builtin_setvar_helper(chan, name, value);
+
+	return 0;
+}
+
+int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *vdata)
+{
+	char *data;
+	int x;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(pair)[24];
+	);
+	AST_DECLARE_APP_ARGS(pair,
+		AST_APP_ARG(name);
+		AST_APP_ARG(value);
+	);
+
+	if (ast_strlen_zero(vdata)) {
+		ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
+		return 0;
+	}
+
+	data = ast_strdupa(vdata);
+	AST_STANDARD_APP_ARGS(args, data);
+
+	for (x = 0; x < args.argc; x++) {
+		AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
+		if (pair.argc == 2) {
+			pbx_builtin_setvar_helper(chan, pair.name, pair.value);
+			if (strchr(pair.name, ' '))
+				ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
+		} else if (!chan) {
+			ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '='\n", pair.name);
+		} else {
+			ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan));
+		}
+	}
+
+	return 0;
+}
+
+static int pbx_builtin_importvar(struct ast_channel *chan, const char *data)
+{
+	char *name;
+	char *value;
+	char *channel;
+	char tmp[VAR_BUF_SIZE];
+	static int deprecation_warning = 0;
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
+		return 0;
+	}
+	tmp[0] = 0;
+	if (!deprecation_warning) {
+		ast_log(LOG_WARNING, "ImportVar is deprecated.  Please use Set(varname=${IMPORT(channel,variable)}) instead.\n");
+		deprecation_warning = 1;
+	}
+
+	value = ast_strdupa(data);
+	name = strsep(&value,"=");
+	channel = strsep(&value,",");
+	if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
+		struct ast_channel *chan2 = ast_channel_get_by_name(channel);
+		if (chan2) {
+			char *s = ast_alloca(strlen(value) + 4);
+			sprintf(s, "${%s}", value);
+			pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
+			chan2 = ast_channel_unref(chan2);
+		}
+		pbx_builtin_setvar_helper(chan, name, tmp);
+	}
+
+	return(0);
+}
+
+static int pbx_builtin_noop(struct ast_channel *chan, const char *data)
+{
+	return 0;
+}
+
+void pbx_builtin_clear_globals(void)
+{
+	struct ast_var_t *vardata;
+
+	ast_rwlock_wrlock(globalslock);
+	while ((vardata = AST_LIST_REMOVE_HEAD(globals, entries)))
+		ast_var_delete(vardata);
+	ast_rwlock_unlock(globalslock);
+}
+
+int pbx_checkcondition(const char *condition)
+{
+	int res;
+	if (ast_strlen_zero(condition)) {                /* NULL or empty strings are false */
+		return 0;
+	} else if (sscanf(condition, "%30d", &res) == 1) { /* Numbers are evaluated for truth */
+		return res;
+	} else {                                         /* Strings are true */
+		return 1;
+	}
+}
+
+static int pbx_builtin_gotoif(struct ast_channel *chan, const char *data)
+{
+	char *condition, *branch1, *branch2, *branch;
+	char *stringp;
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
+		return 0;
+	}
+
+	stringp = ast_strdupa(data);
+	condition = strsep(&stringp,"?");
+	branch1 = strsep(&stringp,":");
+	branch2 = strsep(&stringp,"");
+	branch = pbx_checkcondition(condition) ? branch1 : branch2;
+
+	if (ast_strlen_zero(branch)) {
+		ast_debug(1, "Not taking any branch\n");
+		return 0;
+	}
+
+	return pbx_builtin_goto(chan, branch);
+}
+
+static int pbx_builtin_saynumber(struct ast_channel *chan, const char *data)
+{
+	char tmp[256];
+	char *number = tmp;
+	int number_val;
+	char *options;
+	int res;
+	int interrupt = 0;
+	const char *interrupt_string;
+
+	ast_channel_lock(chan);
+	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
+	if (ast_true(interrupt_string)) {
+		interrupt = 1;
+	}
+	ast_channel_unlock(chan);
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
+		return -1;
+	}
+	ast_copy_string(tmp, data, sizeof(tmp));
+	strsep(&number, ",");
+
+	if (sscanf(tmp, "%d", &number_val) != 1) {
+		ast_log(LOG_WARNING, "argument '%s' to SayNumber could not be parsed as a number.\n", tmp);
+		return 0;
+	}
+
+	options = strsep(&number, ",");
+	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;
+		}
+	}
+
+	res = ast_say_number(chan, number_val, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), options);
+
+	if (res < 0) {
+		ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
+	}
+
+	return interrupt ? res : 0;
+}
+
+static int pbx_builtin_saydigits(struct ast_channel *chan, const char *data)
+{
+	int res = 0;
+	int interrupt = 0;
+	const char *interrupt_string;
+
+	ast_channel_lock(chan);
+	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
+	if (ast_true(interrupt_string)) {
+		interrupt = 1;
+	}
+	ast_channel_unlock(chan);
+
+	if (data) {
+		res = ast_say_digit_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan));
+	}
+
+	return res;
+}
+
+static int pbx_builtin_saycharacters_case(struct ast_channel *chan, const char *data)
+{
+	int res = 0;
+	int sensitivity = 0;
+	char *parse;
+	int interrupt = 0;
+	const char *interrupt_string;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(options);
+		AST_APP_ARG(characters);
+	);
+
+	ast_channel_lock(chan);
+	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
+	if (ast_true(interrupt_string)) {
+		interrupt = 1;
+	}
+	ast_channel_unlock(chan);
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "SayAlphaCase requires two arguments (options, characters)\n");
+		return 0;
+	}
+
+	parse = ast_strdupa(data);
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (!args.options || strlen(args.options) != 1) {
+		ast_log(LOG_WARNING, "SayAlphaCase options are mutually exclusive and required\n");
+		return 0;
+	}
+
+	switch (args.options[0]) {
+	case 'a':
+		sensitivity = AST_SAY_CASE_ALL;
+		break;
+	case 'l':
+		sensitivity = AST_SAY_CASE_LOWER;
+		break;
+	case 'n':
+		sensitivity = AST_SAY_CASE_NONE;
+		break;
+	case 'u':
+		sensitivity = AST_SAY_CASE_UPPER;
+		break;
+	default:
+		ast_log(LOG_WARNING, "Invalid option: '%s'\n", args.options);
+		return 0;
+	}
+
+	res = ast_say_character_str(chan, args.characters, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), sensitivity);
+
+	return res;
+}
+
+static int pbx_builtin_saycharacters(struct ast_channel *chan, const char *data)
+{
+	int res = 0;
+	int interrupt = 0;
+	const char *interrupt_string;
+
+	ast_channel_lock(chan);
+	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
+	if (ast_true(interrupt_string)) {
+		interrupt = 1;
+	}
+	ast_channel_unlock(chan);
+
+	if (data) {
+		res = ast_say_character_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), AST_SAY_CASE_NONE);
+	}
+
+	return res;
+}
+
+static int pbx_builtin_sayphonetic(struct ast_channel *chan, const char *data)
+{
+	int res = 0;
+	int interrupt = 0;
+	const char *interrupt_string;
+
+	ast_channel_lock(chan);
+	interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
+	if (ast_true(interrupt_string)) {
+		interrupt = 1;
+	}
+	ast_channel_unlock(chan);
+
+	if (data)
+		res = ast_say_phonetic_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan));
+	return res;
+}
+
+void unload_pbx_builtins(void)
+{
+	int x;
+	/* Unregister builtin applications */
+	for (x = 0; x < ARRAY_LEN(builtins); x++) {
+		ast_unregister_application(builtins[x].name);
+	}
+}
+
+int load_pbx_builtins(struct varshead *g, ast_rwlock_t *l)
+{
+	int x;
+
+	globals = g;
+	globalslock = l;
+
+	/* Register builtin applications */
+	for (x = 0; x < ARRAY_LEN(builtins); x++) {
+		if (ast_register_application2(builtins[x].name, builtins[x].execute, NULL, NULL, NULL)) {
+			ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
+			return -1;
+		}
+	}
+	return 0;
+}
diff --git a/main/pbx_private.h b/main/pbx_private.h
new file mode 100644
index 0000000..c57dd54
--- /dev/null
+++ b/main/pbx_private.h
@@ -0,0 +1,40 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015 Fairview 5 Engineering, LLC
+ *
+ * George Joseph <george.joseph at fairview5.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Private include file for pbx
+ */
+
+#ifndef _PBX_PRIVATE_H
+#define _PBX_PRIVATE_H
+
+/*! pbx.c functions needed by pbx_builtinns */
+int raise_exception(struct ast_channel *chan, const char *reason, int priority);
+void wait_for_hangup(struct ast_channel *chan, const void *data);
+void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
+
+/*! pbx_builtins.c functions needed by pbx.c */
+void unload_pbx_builtins(void);
+int load_pbx_builtins(struct varshead *g, ast_rwlock_t *l);
+int pbx_builtin_congestion(struct ast_channel *, const char *);
+int pbx_builtin_busy(struct ast_channel *, const char *);
+
+#define VAR_BUF_SIZE 4096
+
+
+#endif /* _PBX_PRIVATE_H */

-- 
To view, visit https://gerrit.asterisk.org/1879
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I87066be3dbf7f5822942ac1449d98cc43fc7561a
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: George Joseph <george.joseph at fairview5.com>



More information about the asterisk-code-review mailing list