From: Naveen Albert Date: Mon, 21 Jun 2021 12:49:16 +0000 (-0400) Subject: func_evalexten: Extension evaluation function. X-Git-Tag: 19.4.0-rc1~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=942db8c58d432f005121c1646c0d4fa6382654c2;p=thirdparty%2Fasterisk.git func_evalexten: Extension evaluation function. This adds the EVAL_EXTEN function, which may be used to retrieve the variable-substituted data at any extension. ASTERISK-29486 Change-Id: Iad81019689674c9f4ac77d235f5d7234adbb1432 --- diff --git a/doc/CHANGES-staging/func_evalexten.txt b/doc/CHANGES-staging/func_evalexten.txt new file mode 100644 index 0000000000..f912bbeb5f --- /dev/null +++ b/doc/CHANGES-staging/func_evalexten.txt @@ -0,0 +1,4 @@ +Subject: func_evalexten + +This adds the EVAL_EXTEN function which may be +used to evaluate data at dialplan extensions. diff --git a/funcs/func_evalexten.c b/funcs/func_evalexten.c new file mode 100644 index 0000000000..6a7d28bc90 --- /dev/null +++ b/funcs/func_evalexten.c @@ -0,0 +1,147 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2021, Naveen Albert + * + * 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 Dialplan extension evaluation function + * + * \author Naveen Albert + * + * \ingroup functions + */ + +/*** MODULEINFO + extended + ***/ + +#include "asterisk.h" + +#include "asterisk/module.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/utils.h" +#include "asterisk/app.h" + +/*** DOCUMENTATION + + + Evaluates the contents of a dialplan extension and returns it as a string. + + + + + + + + The EVAL_EXTEN function looks up a dialplan entry by context,extension,priority, + evaluates the contents of a Return statement to resolve any variable or function + references, and returns the result as a string. + You can use this function to create simple user-defined lookup tables or + user-defined functions. + + [call-types] + exten => _1NNN,1,Return(internal) + exten => _NXXNXXXXXX,1,Return(external) + + [udf] + exten => calleridlen,1,Return(${LEN(${CALLERID(num)})}) + + [default] + exten => _X!,1,Verbose(Call type ${EVAL_EXTEN(call-types,${EXTEN},1)} - ${EVAL_EXTEN(udf,calleridlen,1)}) + + Any variables in the evaluated data will be resolved in the context of + that extension. For example, ${EXTEN} would refer to the + EVAL_EXTEN extension, not the extension in the context invoking the function. + This behavior is similar to other applications, e.g. Gosub. + + same => n,Read(input,${EVAL_EXTEN(prompts,${CALLERID(num)},1)}) + + [prompts] + exten => _X!,1,Return(default) + exten => _20X,1,Return(welcome) + exten => _2XX,1,Return(${DB(promptsettings/${EXTEN})}) + exten => _3XX,1,Return(${ODBC_MYFUNC(${EXTEN})}) + + Extensions on which EVAL_EXTEN is invoked are not different from other + extensions. However, the application at that extension is not executed. + Only the application data is parsed and evaluated. + A limitation of this function is that the application at the specified + extension isn't actually executed, and thus unlike a Gosub, you can't pass + arguments in the EVAL_EXTEN function. + + + EVAL + + + ***/ + +static int eval_exten_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + char *exten, *pri, *context, *parse; + int ipri; + char tmpbuf[len]; + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "The EVAL_EXTEN function requires an extension\n"); + return -1; + } + + parse = ast_strdupa(data); + /* Split context,exten,pri */ + context = strsep(&parse, ","); + exten = strsep(&parse, ","); + pri = strsep(&parse, ","); + + if (pbx_parse_location(chan, &context, &exten, &pri, &ipri, NULL, NULL)) { + return -1; + } + + if (ast_strlen_zero(exten) || ast_strlen_zero(context)) { /* only lock if we really need to */ + ast_channel_lock(chan); + if (ast_strlen_zero(exten)) { + exten = ast_strdupa(ast_channel_exten(chan)); + } + if (ast_strlen_zero(context)) { + context = ast_strdupa(ast_channel_context(chan)); + } + ast_channel_unlock(chan); + } + + if (ast_get_extension_data(tmpbuf, len, chan, context, exten, ipri)) { + return -1; /* EVAL_EXTEN failed */ + } + + pbx_substitute_variables_helper_full_location(chan, (chan) ? ast_channel_varshead(chan) : NULL, tmpbuf, buf, len, NULL, context, exten, ipri); + + return 0; +} + +static struct ast_custom_function eval_exten_function = { + .name = "EVAL_EXTEN", + .read = eval_exten_read, +}; + +static int unload_module(void) +{ + return ast_custom_function_unregister(&eval_exten_function); +} + +static int load_module(void) +{ + return ast_custom_function_register(&eval_exten_function); +} + +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Extension evaluation function"); diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index f5f0691869..d531b440dc 100644 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -1501,6 +1501,25 @@ int ast_explicit_goto(struct ast_channel *chan, const char *context, const char */ int ast_async_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority); +/*! + * \brief Parses a dialplan location into context, extension, priority + * + * \param chan Channel to execute on + * \param context Pointer to initial value for context. + * \param exten Pointer to initial value for exten. + * \param pri Pointer to initial value for pri + * \param ipri Pointer to integer value of priority + * \param mode Pointer to mode (or NULL if mode is not used) + * \param rest Pointer to buffer to capture rest of parsing (or NULL if not used) + * + * strsep should be used to initially populate context, exten, and pri prior + * to calling this function. All arguments are modified in place. + * + * \retval 0 success + * \retval non-zero failure + */ +int pbx_parse_location(struct ast_channel *chan, char **context, char **exten, char **pri, int *ipri, int *mode, char *rest); + struct ast_custom_function* ast_custom_function_find(const char *name); /*! diff --git a/main/pbx.c b/main/pbx.c index 00c653fcad..1e08f233dc 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -8826,6 +8826,47 @@ int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, con return __ast_goto_if_exists(chan, context, exten, priority, 1); } +int pbx_parse_location(struct ast_channel *chan, char **contextp, char **extenp, char **prip, int *ipri, int *mode, char *rest) +{ + char *context, *exten, *pri; + /* do the strsep before here, so we don't have to alloc and free */ + if (!*extenp) { + /* Only a priority in this one */ + *prip = *contextp; + *extenp = NULL; + *contextp = NULL; + } else if (!*prip) { + /* Only an extension and priority in this one */ + *prip = *extenp; + *extenp = *contextp; + *contextp = NULL; + } + context = *contextp; + exten = *extenp; + pri = *prip; + if (mode) { + if (*pri == '+') { + *mode = 1; + pri++; + } else if (*pri == '-') { + *mode = -1; + pri++; + } + } + if ((rest && sscanf(pri, "%30d%1s", ipri, rest) != 1) || sscanf(pri, "%30d", ipri) != 1) { + *ipri = ast_findlabel_extension(chan, context ? context : ast_channel_context(chan), + exten ? exten : ast_channel_exten(chan), pri, + S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)); + if (*ipri < 1) { + ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri); + return -1; + } else if (mode) { + *mode = 0; + } + } + return 0; +} + static int pbx_parseable_goto(struct ast_channel *chan, const char *goto_string, int async) { char *exten, *pri, *context; @@ -8842,31 +8883,9 @@ static int pbx_parseable_goto(struct ast_channel *chan, const char *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++; - } - if (sscanf(pri, "%30d%1s", &ipri, rest) != 1) { - ipri = ast_findlabel_extension(chan, context ? context : ast_channel_context(chan), - exten ? exten : ast_channel_exten(chan), pri, - S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)); - if (ipri < 1) { - ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri); - return -1; - } else - mode = 0; + + if (pbx_parse_location(chan, &context, &exten, &pri, &ipri, &mode, rest)) { + return -1; } /* At this point we have a priority and maybe an extension and a context */ diff --git a/main/pbx_variables.c b/main/pbx_variables.c index a060bf59dc..1f78a59956 100644 --- a/main/pbx_variables.c +++ b/main/pbx_variables.c @@ -736,7 +736,7 @@ void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct /* Substitute if necessary */ if (needsub) { - pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL); + pbx_substitute_variables_helper_full_location(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL, context, exten, pri); vars = ltmp; } else { vars = var; @@ -770,10 +770,13 @@ void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct /* For dialplan location, if we were told what to substitute explicitly, use that instead */ if (exten && !strcmp(vars, "EXTEN")) { ast_copy_string(workspace, exten, VAR_BUF_SIZE); + cp4 = workspace; } else if (context && !strcmp(vars, "CONTEXT")) { ast_copy_string(workspace, context, VAR_BUF_SIZE); + cp4 = workspace; } else if (pri && !strcmp(vars, "PRIORITY")) { snprintf(workspace, VAR_BUF_SIZE, "%d", pri); + cp4 = workspace; } else { pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp); } @@ -829,7 +832,7 @@ void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct /* Substitute if necessary */ if (needsub) { - pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL); + pbx_substitute_variables_helper_full_location(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL, context, exten, pri); vars = ltmp; } else { vars = var;