]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
func_evalexten: Extension evaluation function.
authorNaveen Albert <asterisk@phreaknet.org>
Mon, 21 Jun 2021 12:49:16 +0000 (08:49 -0400)
committerGeorge Joseph <gjoseph@digium.com>
Wed, 27 Apr 2022 08:29:54 +0000 (03:29 -0500)
This adds the EVAL_EXTEN function, which may be used to retrieve
the variable-substituted data at any extension.

ASTERISK-29486

Change-Id: Iad81019689674c9f4ac77d235f5d7234adbb1432

doc/CHANGES-staging/func_evalexten.txt [new file with mode: 0644]
funcs/func_evalexten.c [new file with mode: 0644]
include/asterisk/pbx.h
main/pbx.c
main/pbx_variables.c

diff --git a/doc/CHANGES-staging/func_evalexten.txt b/doc/CHANGES-staging/func_evalexten.txt
new file mode 100644 (file)
index 0000000..f912bbe
--- /dev/null
@@ -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 (file)
index 0000000..6a7d28b
--- /dev/null
@@ -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 <asterisk@phreaknet.org>
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+       <support_level>extended</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+/*** DOCUMENTATION
+       <function name="EVAL_EXTEN" language="en_US">
+               <synopsis>
+                       Evaluates the contents of a dialplan extension and returns it as a string.
+               </synopsis>
+               <syntax>
+                       <parameter name="context" />
+                       <parameter name="extensions" />
+                       <parameter name="priority" required="true" />
+               </syntax>
+               <description>
+                       <para>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.</para>
+                       <para>You can use this function to create simple user-defined lookup tables or
+                       user-defined functions.</para>
+                       <example title="Custom dialplan 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)})
+                       </example>
+                       <para>Any variables in the evaluated data will be resolved in the context of
+                       that extension. For example, <literal>${EXTEN}</literal> 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. <literal>Gosub</literal>.</para>
+                       <example title="Choosing which prompt to use">
+                       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})})
+                       </example>
+                       <para>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.</para>
+                       <para>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.</para>
+               </description>
+               <see-also>
+                       <ref type="function">EVAL</ref>
+               </see-also>
+       </function>
+ ***/
+
+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");
index f5f0691869ad7a39dbb2940bdd8b5446d38f9b66..d531b440dceaeb20fa5d67dcc39edfd93094b686 100644 (file)
@@ -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);
 
 /*!
index 00c653fcadd91fdb889694b10a26bbdc16d5d81a..1e08f233dc05ec60b0d689b7f4ecb0f12074a101 100644 (file)
@@ -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 */
 
index a060bf59dcb819b22f2798ac6a0aeab8ee545c71..1f78a599563ff28ddc433aac65f1289dd1029c51 100644 (file)
@@ -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;