Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 <synopsis>
41 Evaluates the contents of a dialplan extension and returns it as a string.
42 </synopsis>
43 <syntax>
44 <parameter name="context" />
45 <parameter name="extensions" />
46 <parameter name="priority" required="true" />
47 </syntax>
48 <description>
49 <para>The EVAL_EXTEN function looks up a dialplan entry by context,extension,priority,
50 evaluates the contents of a Return statement to resolve any variable or function
51 references, and returns the result as a string.</para>
52 <para>You can use this function to create simple user-defined lookup tables or
53 user-defined functions.</para>
54 <example title="Custom dialplan functions">
55 [call-types]
56 exten => _1NNN,1,Return(internal)
57 exten => _NXXNXXXXXX,1,Return(external)
58
59 [udf]
60 exten => calleridlen,1,Return(${LEN(${CALLERID(num)})})
61
62 [default]
63 exten => _X!,1,Verbose(Call type ${EVAL_EXTEN(call-types,${EXTEN},1)} - ${EVAL_EXTEN(udf,calleridlen,1)})
64 </example>
65 <para>Any variables in the evaluated data will be resolved in the context of
66 that extension. For example, <literal>${EXTEN}</literal> would refer to the
67 EVAL_EXTEN extension, not the extension in the context invoking the function.
68 This behavior is similar to other applications, e.g. <literal>Gosub</literal>.</para>
69 <example title="Choosing which prompt to use">
70 same => n,Read(input,${EVAL_EXTEN(prompts,${CALLERID(num)},1)})
71
72 [prompts]
73 exten => _X!,1,Return(default)
74 exten => _20X,1,Return(welcome)
75 exten => _2XX,1,Return(${DB(promptsettings/${EXTEN})})
76 exten => _3XX,1,Return(${ODBC_MYFUNC(${EXTEN})})
77 </example>
78 <para>Extensions on which EVAL_EXTEN is invoked are not different from other
79 extensions. However, the application at that extension is not executed.
80 Only the application data is parsed and evaluated.</para>
81 <para>A limitation of this function is that the application at the specified
82 extension isn't actually executed, and thus unlike a Gosub, you can't pass
83 arguments in the EVAL_EXTEN function.</para>
84 <para>If you need the ability to evaluate more complex logic that cannot be done
85 purely using functions, see <literal>EVAL_SUB</literal>.</para>
86 </description>
87 <see-also>
88 <ref type="function">EVAL</ref>
89 <ref type="function">EVAL_SUB</ref>
90 </see-also>
91 </function>
92 <function name="EVAL_SUB" language="en_US">
93 <synopsis>
94 Executes a Gosub and provides its return value as a string
95 </synopsis>
96 <syntax>
97 <parameter name="context" />
98 <parameter name="extensions" />
99 <parameter name="priority" required="true" />
100 </syntax>
101 <description>
102 <para>The EVAL_SUB function executes up a dialplan location by context,extension,priority, with optional arguments
103 and returns the contents of the Return statement. The arguments to <literal>EVAL_SUB</literal>
104 are exactly like they are with <literal>Gosub</literal>.</para>
105 <para>This function is complementary to <literal>EVAL_EXTEN</literal>. However, it is more powerful,
106 since it allows executing arbitrary dialplan and capturing some outcome as a dialplan function's
107 return value, allowing it to be used in a variety of scenarios that do not allow executing dialplan
108 directly but allow variables and functions to be used, and where using <literal>EVAL_EXTEN</literal>
109 would be difficult or impossible.</para>
110 <para>Consequently, this function also allows you to implement your own arbitrary functions
111 in dialplan, which can then be wrapped using the Asterisk function interface using <literal>EVAL_SUB</literal>.</para>
112 <para>While this function is primarily intended to be used for executing Gosub routines that are quick
113 and do not interact with the channel, it is safe to execute arbitrary, even blocking, dialplan in the
114 called subroutine. That said, this kind of usage is not recommended.</para>
115 <para>This function will always return, even if the channel is hung up.</para>
116 <example title="Record whether a PSTN call is local">
117 [islocal]
118 exten => _X!,1,ExecIf($[${LEN(${EXTEN})}&lt;10]?Return(1))
119 same => n,Set(LOCAL(npanxx)=${EXTEN:-10:6})
120 same => n,ReturnIf(${EXISTS(${DB(localcall/${npanxx})})}?${DB(localcall/${npanxx})})
121 same => n,Set(LOCAL(islocal)=${SHELL(curl "https://example.com/islocal?npanxx=${EXTEN:-10:6}")})
122 same => n,Set(LOCAL(islocal)=${FILTER(A-Z,${islocal})})
123 same => n,Set(DB(localcall/${npanxx})=${islocal})
124 same => n,Return(${islocal})
125
126 [outgoing]
127 exten => _1NXXNXXXXXX,1,Set(CDR(toll)=${IF($["${EVAL_SUB(islocal,${EXTEN},1)}"="Y"]?0:1)})
128 same => n,Dial(DAHDI/1/${EXTEN})
129 same => n,Hangup()
130 </example>
131 <para>This example illustrates an example of logic that would be difficult to capture
132 in a way that a single call to <literal>EVAL_EXTEN</literal> would return the same result. For one, conditionals
133 are involved, and due to the way Asterisk parses dialplan, all functions in an application call are evaluated all the
134 time, which may be undesirable if they cause side effects (e.g. making a cURL request) that should only happen in certain circumstances.</para>
135 <para>The above example, of course, does not require the use of this function, as it could have been invoked
136 using the Gosub application directly. However, if constrained to just using variables or functions,
137 <literal>EVAL_SUB</literal> would be required.</para>
138 </description>
139 <see-also>
140 <ref type="function">EVAL_EXTEN</ref>
141 <ref type="application">Return</ref>
142 </see-also>
143 </function>
144 ***/
145
146static int eval_exten_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
147{
148 char *exten, *pri, *context, *parse;
149 int ipri;
150 char tmpbuf[len];
151
152 if (ast_strlen_zero(data)) {
153 ast_log(LOG_WARNING, "The EVAL_EXTEN function requires an extension\n");
154 return -1;
155 }
156
157 parse = ast_strdupa(data);
158 /* Split context,exten,pri */
159 context = strsep(&parse, ",");
160 exten = strsep(&parse, ",");
161 pri = strsep(&parse, ",");
162
163 if (pbx_parse_location(chan, &context, &exten, &pri, &ipri, NULL, NULL)) {
164 return -1;
165 }
166
167 if (ast_strlen_zero(exten) || ast_strlen_zero(context)) { /* only lock if we really need to */
168 ast_channel_lock(chan);
169 if (ast_strlen_zero(exten)) {
170 exten = ast_strdupa(ast_channel_exten(chan));
171 }
174 }
175 ast_channel_unlock(chan);
176 }
177
178 if (ast_get_extension_data(tmpbuf, len, chan, context, exten, ipri)) {
179 return -1; /* EVAL_EXTEN failed */
180 }
181
182 pbx_substitute_variables_helper_full_location(chan, (chan) ? ast_channel_varshead(chan) : NULL, tmpbuf, buf, len, NULL, context, exten, ipri);
183
184 return 0;
185}
186
187static int eval_sub_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
188{
189 int gosub_res;
190 const char *retval;
191
192 if (ast_strlen_zero(data)) {
193 ast_log(LOG_WARNING, "The EVAL_SUB function requires an extension\n");
194 *buf = '\0';
195 return -1;
196 }
197
198 /* Ignore hangups since we want to retrieve a value, and this function could be called at hangup time */
199 gosub_res = ast_app_exec_sub(NULL, chan, data, 1);
200 if (gosub_res) {
201 ast_log(LOG_WARNING, "Failed to execute Gosub(%s)\n", data);
202 *buf = '\0';
203 return -1;
204 }
205
206 ast_channel_lock(chan);
207 retval = pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL");
208 ast_copy_string(buf, S_OR(retval, ""), len); /* Overwrite, even if empty, to ensure a stale GOSUB_RETVAL isn't returned as our value */
209 ast_channel_unlock(chan);
210
211 return 0;
212}
213
215 .name = "EVAL_EXTEN",
216 .read = eval_exten_read,
217};
218
220 .name = "EVAL_SUB",
221 .read = eval_sub_read,
222};
223
224static int unload_module(void)
225{
226 int res = 0;
229 return res;
230}
231
232static int load_module(void)
233{
234 int res = 0;
237 return res;
238}
239
240AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Extension evaluation function");
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:2968
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:2969
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)
static ENTRY retval
Definition: hsearch.c:50
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
char * strsep(char **str, const char *delims)
#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:1558
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:8567
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:8791
#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.