Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
app_if.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright 2022, Naveen Albert <asterisk@phreaknet.org>
5 *
6 * Naveen Albert <asterisk@phreaknet.org>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief If Branch Implementation
22 *
23 * \author Naveen Albert <asterisk@phreaknet.org>
24 *
25 * \ingroup applications
26 */
27
28/*** MODULEINFO
29 <support_level>extended</support_level>
30 ***/
31
32#include "asterisk.h"
33
34#include "asterisk/pbx.h"
35#include "asterisk/module.h"
36#include "asterisk/channel.h"
37
38/*** DOCUMENTATION
39 <application name="If" language="en_US">
40 <since>
41 <version>18.16.0</version>
42 <version>20.1.0</version>
43 </since>
44 <synopsis>
45 Start an if branch.
46 </synopsis>
47 <syntax>
48 <parameter name="expr" required="true" />
49 </syntax>
50 <description>
51 <para>Start an If branch. Execution will continue inside the branch
52 if expr is true.</para>
53 <note><para>This application (and related applications) set variables
54 internally during execution.</para></note>
55 </description>
56 <see-also>
57 <ref type="application">ElseIf</ref>
58 <ref type="application">Else</ref>
59 <ref type="application">EndIf</ref>
60 <ref type="application">ExitIf</ref>
61 </see-also>
62 </application>
63 <application name="ElseIf" language="en_US">
64 <since>
65 <version>18.16.0</version>
66 <version>20.1.0</version>
67 </since>
68 <synopsis>
69 Start an else if branch.
70 </synopsis>
71 <syntax>
72 <parameter name="expr" required="true" />
73 </syntax>
74 <description>
75 <para>Start an optional ElseIf branch. Execution will continue inside the branch
76 if expr is true and if previous If and ElseIf branches evaluated to false.</para>
77 <para>Please note that execution inside a true If branch will fallthrough into
78 ElseIf unless the If segment is terminated with an ExitIf call. This is only
79 necessary with ElseIf but not with Else.</para>
80 </description>
81 <see-also>
82 <ref type="application">If</ref>
83 <ref type="application">Else</ref>
84 <ref type="application">EndIf</ref>
85 <ref type="application">ExitIf</ref>
86 </see-also>
87 </application>
88 <application name="Else" language="en_US">
89 <since>
90 <version>18.16.0</version>
91 <version>20.1.0</version>
92 </since>
93 <synopsis>
94 Define an optional else branch.
95 </synopsis>
96 <syntax>
97 <parameter name="expr" required="true" />
98 </syntax>
99 <description>
100 <para>Start an Else branch. Execution will jump here if all previous
101 If and ElseIf branches evaluated to false.</para>
102 </description>
103 <see-also>
104 <ref type="application">If</ref>
105 <ref type="application">ElseIf</ref>
106 <ref type="application">EndIf</ref>
107 <ref type="application">ExitIf</ref>
108 </see-also>
109 </application>
110 <application name="EndIf" language="en_US">
111 <since>
112 <version>18.16.0</version>
113 <version>20.1.0</version>
114 </since>
115 <synopsis>
116 End an if branch.
117 </synopsis>
118 <syntax />
119 <description>
120 <para>Ends the branch begun by the preceding <literal>If()</literal> application.</para>
121 </description>
122 <see-also>
123 <ref type="application">If</ref>
124 <ref type="application">ElseIf</ref>
125 <ref type="application">Else</ref>
126 <ref type="application">ExitIf</ref>
127 </see-also>
128 </application>
129 <application name="ExitIf" language="en_US">
130 <since>
131 <version>18.16.0</version>
132 <version>20.1.0</version>
133 </since>
134 <synopsis>
135 End an If branch.
136 </synopsis>
137 <syntax />
138 <description>
139 <para>Exits an <literal>If()</literal> branch, whether or not it has completed.</para>
140 </description>
141 <see-also>
142 <ref type="application">If</ref>
143 <ref type="application">ElseIf</ref>
144 <ref type="application">Else</ref>
145 <ref type="application">EndIf</ref>
146 </see-also>
147 </application>
148 ***/
149
150static char *if_app = "If";
151static char *elseif_app = "ElseIf";
152static char *else_app = "Else";
153static char *stop_app = "EndIf";
154static char *exit_app = "ExitIf";
155
156#define VAR_SIZE 64
157
158static const char *get_index(struct ast_channel *chan, const char *prefix, int idx)
159{
160 char varname[VAR_SIZE];
161
162 snprintf(varname, VAR_SIZE, "%s_%d", prefix, idx);
163 return pbx_builtin_getvar_helper(chan, varname);
164}
165
166static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
167{
168 struct ast_exten *e;
169 struct ast_context *c2;
170 int idx;
171
174 int needmatch = ast_get_extension_matchcid(e);
175 if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
176 (!needmatch)) {
177 /* This is the matching extension we want */
178 struct ast_exten *p;
181 continue;
182 return p;
183 }
184 }
185 }
186 }
187
188 /* No match; run through includes */
189 for (idx = 0; idx < ast_context_includes_count(c); idx++) {
190 const struct ast_include *i = ast_context_includes_get(c, idx);
191
192 for (c2 = ast_walk_contexts(NULL); c2; c2 = ast_walk_contexts(c2)) {
193 if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
194 e = find_matching_priority(c2, exten, priority, callerid);
195 if (e)
196 return e;
197 }
198 }
199 }
200 return NULL;
201}
202
203static int find_matching_endif(struct ast_channel *chan, const char *otherapp)
204{
205 struct ast_context *c;
206 int res = -1;
207
208 if (ast_rdlock_contexts()) {
209 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
210 return -1;
211 }
212
214 struct ast_exten *e;
215
216 if (!ast_rdlock_context(c)) {
217 if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
218 /* This is the matching context we want */
219
220 int cur_priority = ast_channel_priority(chan) + 1, level = 1;
221
222 for (e = find_matching_priority(c, ast_channel_exten(chan), cur_priority,
223 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
224 e;
225 e = find_matching_priority(c, ast_channel_exten(chan), ++cur_priority,
226 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
227
228 if (!strcasecmp(ast_get_extension_app(e), "IF")) {
229 level++;
230 } else if (!strcasecmp(ast_get_extension_app(e), "ENDIF")) {
231 level--;
232 }
233
234 if (!otherapp && level == 0) {
235 res = cur_priority;
236 break;
237 } else if (otherapp && level == 1 && !strcasecmp(ast_get_extension_app(e), otherapp)) {
238 res = cur_priority;
239 break;
240 }
241 }
242 }
244 if (res > 0) {
245 break;
246 }
247 }
248 }
250 return res;
251}
252
253static int if_helper(struct ast_channel *chan, const char *data, int end)
254{
255 int res = 0;
256 const char *if_pri = NULL;
257 char *my_name = NULL;
258 const char *label = NULL;
259 char varname[VAR_SIZE + 3]; /* + IF_ */
260 char end_varname[sizeof(varname) + 4]; /* + END_ + sizeof(varname) */
261 const char *prefix = "IF";
262 size_t size = 0;
263 int used_index_i = -1, x = 0;
264 char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
265
266 if (!chan) {
267 return -1;
268 }
269
270 for (x = 0 ;; x++) {
271 if (get_index(chan, prefix, x)) {
272 used_index_i = x;
273 } else {
274 break;
275 }
276 }
277
278 snprintf(used_index, sizeof(used_index), "%d", used_index_i);
279 snprintf(new_index, sizeof(new_index), "%d", used_index_i + 1);
280
281 size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
282 my_name = ast_alloca(size);
283 memset(my_name, 0, size);
284 snprintf(my_name, size, "%s_%s_%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
285
286 ast_channel_lock(chan);
287 if (end > 1) {
288 label = used_index;
289 } else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
290 label = new_index;
291 pbx_builtin_setvar_helper(chan, my_name, label);
292 }
293 snprintf(varname, sizeof(varname), "%s_%s", prefix, label);
294 if ((if_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
295 if_pri = ast_strdupa(if_pri);
296 snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
297 }
298 ast_channel_unlock(chan);
299
300 if ((end <= 1 && !pbx_checkcondition(ast_strdupa(data))) || (end > 1)) {
301 /* Condition Met (clean up helper vars) */
302 const char *goto_str;
303 int pri, endifpri;
304 pbx_builtin_setvar_helper(chan, varname, NULL);
305 pbx_builtin_setvar_helper(chan, my_name, NULL);
306 snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
307 ast_channel_lock(chan);
308 /* For EndIf, simply go to the next priority.
309 * We do not add 1 to ast_channel_priority because the dialplan will
310 * auto-increment the priority when we return, so just keep the priority as is.
311 * For ExitIf or false If() condition, we need to find the end of the current
312 * If branch (at same indentation) and branch there. */
313 endifpri = end == 2 ? ast_channel_priority(chan) : find_matching_endif(chan, NULL);
314 if ((goto_str = pbx_builtin_getvar_helper(chan, end_varname))) {
315 ast_parseable_goto(chan, goto_str);
316 pbx_builtin_setvar_helper(chan, end_varname, NULL);
317 } else if (end <= 1 && (pri = find_matching_endif(chan, "ElseIf")) > 0 && pri < endifpri) {
318 pri--; /* back up a priority, since it returned the priority after the ElseIf */
319 /* If is false, and ElseIf exists, so jump to ElseIf */
320 ast_verb(3, "Taking conditional false branch, jumping to priority %d\n", pri);
321 ast_channel_priority_set(chan, pri);
322 } else if (end <= 1 && (pri = find_matching_endif(chan, "Else")) > 0 && pri < endifpri) {
323 /* don't need to back up a priority, because we don't actually need to execute Else, just jump to the priority after. Directly executing Else will exit the conditional. */
324 /* If is false, and Else exists, so jump to Else */
325 ast_verb(3, "Taking absolute false branch, jumping to priority %d\n", pri);
326 ast_channel_priority_set(chan, pri);
327 } else {
328 pri = endifpri;
329 if (pri > 0) {
330 ast_verb(3, "Exiting conditional, jumping to priority %d\n", pri);
331 ast_channel_priority_set(chan, pri);
332 } else if (end == 4) { /* Condition added because of end > 0 instead of end == 4 */
333 ast_log(LOG_WARNING, "Couldn't find matching EndIf? (If at %s@%s priority %d)\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
334 }
335 }
336 ast_channel_unlock(chan);
337 return res;
338 }
339
340 if (end <= 1 && !if_pri) {
341 char *goto_str;
342 size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
343 goto_str = ast_alloca(size);
344 memset(goto_str, 0, size);
345 snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
346 pbx_builtin_setvar_helper(chan, varname, goto_str);
347 } else if (end > 1 && if_pri) {
348 /* END of branch */
349 snprintf(end_varname, sizeof(end_varname), "END_%s", varname);
350 if (!pbx_builtin_getvar_helper(chan, end_varname)) {
351 char *goto_str;
352 size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
353 goto_str = ast_alloca(size);
354 memset(goto_str, 0, size);
355 snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan)+1);
356 pbx_builtin_setvar_helper(chan, end_varname, goto_str);
357 }
358 ast_parseable_goto(chan, if_pri);
359 }
360
361 return res;
362}
363
364static int if_exec(struct ast_channel *chan, const char *data) {
365 return if_helper(chan, data, 0);
366}
367
368static int elseif_exec(struct ast_channel *chan, const char *data) {
369 return if_helper(chan, data, 1);
370}
371
372static int end_exec(struct ast_channel *chan, const char *data) {
373 return if_helper(chan, data, 2);
374}
375
376static int else_exec(struct ast_channel *chan, const char *data) {
377 return if_helper(chan, data, 3);
378}
379
380static int exit_exec(struct ast_channel *chan, const char *data) {
381 return if_helper(chan, data, 4);
382}
383
384static int unload_module(void)
385{
386 int res;
387
393
394 return res;
395}
396
397static int load_module(void)
398{
399 int res;
400
406
407 return res;
408}
409
410AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "If Branch and Conditional Execution");
static char * else_app
Definition: app_if.c:152
static const char * get_index(struct ast_channel *chan, const char *prefix, int idx)
Definition: app_if.c:158
static struct ast_exten * find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
Definition: app_if.c:166
static int if_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:364
static char * stop_app
Definition: app_if.c:153
static char * if_app
Definition: app_if.c:150
static int if_helper(struct ast_channel *chan, const char *data, int end)
Definition: app_if.c:253
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "If Branch and Conditional Execution")
static char * exit_app
Definition: app_if.c:154
static int find_matching_endif(struct ast_channel *chan, const char *otherapp)
Definition: app_if.c:203
#define VAR_SIZE
Definition: app_if.c:156
static int elseif_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:368
static int load_module(void)
Definition: app_if.c:397
static char * elseif_app
Definition: app_if.c:151
static int end_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:372
static int unload_module(void)
Definition: app_if.c:384
static int else_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:376
static int exit_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:380
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
static int priority
General Asterisk PBX channel definitions.
#define ast_channel_lock(chan)
Definition: channel.h:2970
int ast_channel_priority(const struct ast_channel *chan)
const char * ast_channel_context(const struct ast_channel *chan)
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_channel_priority_set(struct ast_channel *chan, int value)
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2971
char * end
Definition: eagi_proxy.c:73
static char prefix[MAX_PREFIX]
Definition: http.c:144
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
Asterisk module definitions.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Core PBX routines and definitions.
struct ast_context * ast_walk_contexts(struct ast_context *con)
Definition: extconf.c:4024
int ast_get_extension_priority(struct ast_exten *exten)
Definition: pbx.c:8534
int ast_get_extension_matchcid(struct ast_exten *e)
Definition: pbx.c:8562
const char * ast_get_extension_app(struct ast_exten *e)
Definition: pbx.c:8572
const char * ast_get_extension_cidmatch(struct ast_exten *e)
Definition: pbx.c:8567
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
const char * ast_get_include_name(const struct ast_include *include)
Definition: pbx_include.c:50
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8506
struct ast_exten * ast_walk_extension_priorities(struct ast_exten *exten, struct ast_exten *priority)
Definition: extconf.c:4061
const char * ast_get_context_name(struct ast_context *con)
Definition: ael_main.c:421
int ast_extension_match(const char *pattern, const char *extension)
Determine if a given extension matches a given pattern (in NXX format)
Definition: extconf.c:4295
int ast_rdlock_contexts(void)
Read locks the context list.
Definition: pbx.c:8483
const struct ast_include * ast_context_includes_get(const struct ast_context *con, int idx)
Definition: pbx.c:8699
struct ast_exten * ast_walk_context_extensions(struct ast_context *con, struct ast_exten *priority)
Definition: ael_main.c:427
int pbx_checkcondition(const char *condition)
Evaluate a condition.
Definition: pbx.c:8297
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8488
int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
Definition: pbx.c:8881
int ast_context_includes_count(const struct ast_context *con)
Definition: pbx.c:8694
const char * ast_get_extension_name(struct ast_exten *exten)
Definition: pbx.c:8524
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8501
#define NULL
Definition: resample.c:96
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
Main Channel structure associated with a channel.
ast_context: An extension context
Definition: pbx.c:299
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition: pbx.c:252
char * exten
Definition: pbx.c:253
void * data
Definition: pbx.c:263
const char * label
Definition: pbx.c:259
ast_include: include= support in extensions.conf
Definition: pbx_include.c:37
Number structure.
Definition: app_followme.c:157
static struct test_val c