Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
func_evalexten.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2021, 2024, Naveen Albert
5 *
6 * See http://www.asterisk.org for more information about
7 * the Asterisk project. Please do not directly contact
8 * any of the maintainers of this project for assistance;
9 * the project provides a web site, mailing lists and IRC
10 * channels for your use.
11 *
12 * This program is free software, distributed under the terms of
13 * the GNU General Public License Version 2. See the LICENSE file
14 * at the top of the source tree.
15 */
16
17/*! \file
18 *
19 * \brief Dialplan extension evaluation function
20 *
21 * \author Naveen Albert <asterisk@phreaknet.org>
22 *
23 * \ingroup functions
24 */
25
26/*** MODULEINFO
27 <support_level>extended</support_level>
28 ***/
29
30#include "asterisk.h"
31
32#include "asterisk/module.h"
33#include "asterisk/channel.h"
34#include "asterisk/pbx.h"
35#include "asterisk/utils.h"
36#include "asterisk/app.h"
37
38/*** DOCUMENTATION
39 <function name="EVAL_EXTEN" language="en_US">
40 <since>
41 <version>16.26.0</version>
42 <version>18.12.0</version>
43 <version>19.4.0</version>
44 </since>
45 <synopsis>
46 Evaluates the contents of a dialplan extension and returns it as a string.
47 </synopsis>
48 <syntax>
49 <parameter name="context" />
50 <parameter name="extensions" />
51 <parameter name="priority" required="true" />
52 </syntax>
53 <description>
54 <para>The EVAL_EXTEN function looks up a dialplan entry by context,extension,priority,
55 evaluates the contents of a Return statement to resolve any variable or function
56 references, and returns the result as a string.</para>
57 <para>You can use this function to create simple user-defined lookup tables or
58 user-defined functions.</para>
59 <example title="Custom dialplan functions">
60 [call-types]
61 exten => _1NNN,1,Return(internal)
62 exten => _NXXNXXXXXX,1,Return(external)
63
64 [udf]
65 exten => calleridlen,1,Return(${LEN(${CALLERID(num)})})
66
67 [default]
68 exten => _X!,1,Verbose(Call type ${EVAL_EXTEN(call-types,${EXTEN},1)} - ${EVAL_EXTEN(udf,calleridlen,1)})
69 </example>
70 <para>Any variables in the evaluated data will be resolved in the context of
71 that extension. For example, <literal>${EXTEN}</literal> would refer to the
72 EVAL_EXTEN extension, not the extension in the context invoking the function.
73 This behavior is similar to other applications, e.g. <literal>Gosub</literal>.</para>
74 <example title="Choosing which prompt to use">
75 same => n,Read(input,${EVAL_EXTEN(prompts,${CALLERID(num)},1)})
76
77 [prompts]
78 exten => _X!,1,Return(default)
79 exten => _20X,1,Return(welcome)
80 exten => _2XX,1,Return(${DB(promptsettings/${EXTEN})})
81 exten => _3XX,1,Return(${ODBC_MYFUNC(${EXTEN})})
82 </example>
83 <para>Extensions on which EVAL_EXTEN is invoked are not different from other
84 extensions. However, the application at that extension is not executed.
85 Only the application data is parsed and evaluated.</para>
86 <para>A limitation of this function is that the application at the specified
87 extension isn't actually executed, and thus unlike a Gosub, you can't pass
88 arguments in the EVAL_EXTEN function.</para>
89 <para>If you need the ability to evaluate more complex logic that cannot be done
90 purely using functions, see <literal>EVAL_SUB</literal>.</para>
91 </description>
92 <see-also>
93 <ref type="function">EVAL</ref>
94 <ref type="function">EVAL_SUB</ref>
95 </see-also>
96 </function>
97 <function name="EVAL_SUB" language="en_US">
98 <since>
99 <version>20.11.0</version>
100 <version>21.6.0</version>
101 <version>22.1.0</version>
102 </since>
103 <synopsis>
104 Executes a Gosub and provides its return value as a string
105 </synopsis>
106 <syntax>
107 <parameter name="context" />
108 <parameter name="extensions" />
109 <parameter name="priority" required="true" />
110 </syntax>
111 <description>
112 <para>The EVAL_SUB function executes up a dialplan location by context,extension,priority, with optional arguments
113 and returns the contents of the Return statement. The arguments to <literal>EVAL_SUB</literal>
114 are exactly like they are with <literal>Gosub</literal>.</para>
115 <para>This function is complementary to <literal>EVAL_EXTEN</literal>. However, it is more powerful,
116 since it allows executing arbitrary dialplan and capturing some outcome as a dialplan function's
117 return value, allowing it to be used in a variety of scenarios that do not allow executing dialplan
118 directly but allow variables and functions to be used, and where using <literal>EVAL_EXTEN</literal>
119 would be difficult or impossible.</para>
120 <para>Consequently, this function also allows you to implement your own arbitrary functions
121 in dialplan, which can then be wrapped using the Asterisk function interface using <literal>EVAL_SUB</literal>.</para>
122 <para>While this function is primarily intended to be used for executing Gosub routines that are quick
123 and do not interact with the channel, it is safe to execute arbitrary, even blocking, dialplan in the
124 called subroutine. That said, this kind of usage is not recommended.</para>
125 <para>This function will always return, even if the channel is hung up.</para>
126 <example title="Record whether a PSTN call is local">
127 [islocal]
128 exten => _X!,1,ExecIf($[${LEN(${EXTEN})}&lt;10]?Return(1))
129 same => n,Set(LOCAL(npanxx)=${EXTEN:-10:6})
130 same => n,ReturnIf(${EXISTS(${DB(localcall/${npanxx})})}?${DB(localcall/${npanxx})})
131 same => n,Set(LOCAL(islocal)=${SHELL(curl "https://example.com/islocal?npanxx=${EXTEN:-10:6}")})
132 same => n,Set(LOCAL(islocal)=${FILTER(A-Z,${islocal})})
133 same => n,Set(DB(localcall/${npanxx})=${islocal})
134 same => n,Return(${islocal})
135
136 [outgoing]
137 exten => _1NXXNXXXXXX,1,Set(CDR(toll)=${IF($["${EVAL_SUB(islocal,${EXTEN},1)}"="Y"]?0:1)})
138 same => n,Dial(DAHDI/1/${EXTEN})
139 same => n,Hangup()
140 </example>
141 <para>This example illustrates an example of logic that would be difficult to capture
142 in a way that a single call to <literal>EVAL_EXTEN</literal> would return the same result. For one, conditionals
143 are involved, and due to the way Asterisk parses dialplan, all functions in an application call are evaluated all the
144 time, which may be undesirable if they cause side effects (e.g. making a cURL request) that should only happen in certain circumstances.</para>
145 <para>The above example, of course, does not require the use of this function, as it could have been invoked
146 using the Gosub application directly. However, if constrained to just using variables or functions,
147 <literal>EVAL_SUB</literal> would be required.</para>
148 </description>
149 <see-also>
150 <ref type="function">EVAL_EXTEN</ref>
151 <ref type="application">Return</ref>
152 </see-also>
153 </function>
154 ***/
155
156static int eval_exten_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
157{
158 char *exten, *pri, *context, *parse;
159 int ipri;
160 char tmpbuf[len];
161
162 if (ast_strlen_zero(data)) {
163 ast_log(LOG_WARNING, "The EVAL_EXTEN function requires an extension\n");
164 return -1;
165 }
166
167 parse = ast_strdupa(data);
168 /* Split context,exten,pri */
169 context = strsep(&parse, ",");
170 exten = strsep(&parse, ",");
171 pri = strsep(&parse, ",");
172
173 if (pbx_parse_location(chan, &context, &exten, &pri, &ipri, NULL, NULL)) {
174 return -1;
175 }
176
177 if (ast_strlen_zero(exten) || ast_strlen_zero(context)) { /* only lock if we really need to */
178 ast_channel_lock(chan);
179 if (ast_strlen_zero(exten)) {
180 exten = ast_strdupa(ast_channel_exten(chan));
181 }
184 }
185 ast_channel_unlock(chan);
186 }
187
188 if (ast_get_extension_data(tmpbuf, len, chan, context, exten, ipri)) {
189 return -1; /* EVAL_EXTEN failed */
190 }
191
192 pbx_substitute_variables_helper_full_location(chan, (chan) ? ast_channel_varshead(chan) : NULL, tmpbuf, buf, len, NULL, context, exten, ipri);
193
194 return 0;
195}
196
197static int eval_sub_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
198{
199 int gosub_res;
200 const char *retval;
201
202 if (ast_strlen_zero(data)) {
203 ast_log(LOG_WARNING, "The EVAL_SUB function requires an extension\n");
204 *buf = '\0';
205 return -1;
206 }
207
208 /* Ignore hangups since we want to retrieve a value, and this function could be called at hangup time */
209 gosub_res = ast_app_exec_sub(NULL, chan, data, 1);
210 if (gosub_res) {
211 ast_log(LOG_WARNING, "Failed to execute Gosub(%s)\n", data);
212 *buf = '\0';
213 return -1;
214 }
215
216 ast_channel_lock(chan);
217 retval = pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL");
218 ast_copy_string(buf, S_OR(retval, ""), len); /* Overwrite, even if empty, to ensure a stale GOSUB_RETVAL isn't returned as our value */
219 ast_channel_unlock(chan);
220
221 return 0;
222}
223
225 .name = "EVAL_EXTEN",
226 .read = eval_exten_read,
227};
228
230 .name = "EVAL_SUB",
231 .read = eval_sub_read,
232};
233
234static int unload_module(void)
235{
236 int res = 0;
239 return res;
240}
241
242static int load_module(void)
243{
244 int res = 0;
247 return res;
248}
249
250AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Extension evaluation function");
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
struct varshead * ast_channel_varshead(struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2970
const char * ast_channel_context(const struct ast_channel *chan)
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2971
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int eval_exten_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int eval_sub_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Extension evaluation function")
static struct ast_custom_function eval_exten_function
static int load_module(void)
static int unload_module(void)
static struct ast_custom_function eval_sub_function
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
Run a subroutine on a channel, placing an optional second channel into autoservice.
Definition: main/app.c:297
#define LOG_WARNING
Asterisk module definitions.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_get_extension_data(char *buf, int bufsize, struct ast_channel *c, const char *context, const char *exten, int priority)
Fill a string buffer with the data at a dialplan extension.
Definition: pbx.c:8582
void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used, const char *context, const char *exten, int pri)
Substitutes variables, similar to pbx_substitute_variables_helper_full, but allows passing the contex...
int pbx_parse_location(struct ast_channel *chan, char **context, char **exten, char **pri, int *ipri, int *mode, char *rest)
Parses a dialplan location into context, extension, priority.
Definition: pbx.c:8806
#define NULL
Definition: resample.c:96
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Utility functions.