Asterisk - The Open Source Telephony Project  18.5.0
app_if.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright 2021, Naveen Albert <[email protected]>
5  *
6  * Naveen Albert <[email protected]>
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 <[email protected]>
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  <synopsis>
41  Start an if branch.
42  </synopsis>
43  <syntax>
44  <parameter name="expr" required="true" />
45  </syntax>
46  <description>
47  <para>Start an If branch. Execution will continue inside the branch
48  if expr is true.</para>
49  </description>
50  <see-also>
51  <ref type="application">EndIf</ref>
52  <ref type="application">ExitIf</ref>
53  </see-also>
54  </application>
55  <application name="EndIf" language="en_US">
56  <synopsis>
57  End an if branch.
58  </synopsis>
59  <syntax />
60  <description>
61  <para>Ends the branch begun by the preceding <literal>If()</literal> application.</para>
62  </description>
63  <see-also>
64  <ref type="application">If</ref>
65  <ref type="application">ExitIf</ref>
66  </see-also>
67  </application>
68  <application name="ExitIf" language="en_US">
69  <synopsis>
70  End an If branch.
71  </synopsis>
72  <syntax />
73  <description>
74  <para>Exits an <literal>If()</literal> branch, whether or not it has completed.</para>
75  </description>
76  <see-also>
77  <ref type="application">If</ref>
78  <ref type="application">EndIf</ref>
79  </see-also>
80  </application>
81  ***/
82 
83 static char *start_app = "If";
84 static char *stop_app = "EndIf";
85 static char *exit_app = "ExitIf";
86 
87 #define VAR_SIZE 64
88 
89 
90 static const char *get_index(struct ast_channel *chan, const char *prefix, int idx) {
91  char varname[VAR_SIZE];
92 
93  snprintf(varname, VAR_SIZE, "%s_%d", prefix, idx);
94  return pbx_builtin_getvar_helper(chan, varname);
95 }
96 
97 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
98 {
99  struct ast_exten *e;
100  struct ast_context *c2;
101  int idx;
102 
105  int needmatch = ast_get_extension_matchcid(e);
106  if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
107  (!needmatch)) {
108  /* This is the matching extension we want */
109  struct ast_exten *p;
111  if (priority != ast_get_extension_priority(p))
112  continue;
113  return p;
114  }
115  }
116  }
117  }
118 
119  /* No match; run through includes */
120  for (idx = 0; idx < ast_context_includes_count(c); idx++) {
121  const struct ast_include *i = ast_context_includes_get(c, idx);
122 
123  for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
124  if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
125  e = find_matching_priority(c2, exten, priority, callerid);
126  if (e)
127  return e;
128  }
129  }
130  }
131  return NULL;
132 }
133 
134 static int find_matching_endif(struct ast_channel *chan)
135 {
136  struct ast_context *c;
137  int res = -1;
138 
139  if (ast_rdlock_contexts()) {
140  ast_log(LOG_ERROR, "Failed to lock contexts list\n");
141  return -1;
142  }
143 
144  for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
145  struct ast_exten *e;
146 
147  if (!ast_rdlock_context(c)) {
148  if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
149  /* This is the matching context we want */
150  int cur_priority = ast_channel_priority(chan) + 1, level=1;
151 
152  for (e = find_matching_priority(c, ast_channel_exten(chan), cur_priority,
153  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
154  e;
155  e = find_matching_priority(c, ast_channel_exten(chan), ++cur_priority,
156  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
157  if (!strcasecmp(ast_get_extension_app(e), "IF")) {
158  level++;
159  } else if (!strcasecmp(ast_get_extension_app(e), "ENDIF")) {
160  level--;
161  }
162 
163  if (level == 0) {
164  res = cur_priority;
165  break;
166  }
167  }
168  }
170  if (res > 0) {
171  break;
172  }
173  }
174  }
176  return res;
177 }
178 
179 static int _if_exec(struct ast_channel *chan, const char *data, int end)
180 {
181  int res=0;
182  const char *if_pri = NULL;
183  char *my_name = NULL;
184  const char *condition = NULL, *label = NULL;
185  char varname[VAR_SIZE], end_varname[VAR_SIZE];
186  const char *prefix = "IF";
187  size_t size=0;
188  int used_index_i = -1, x=0;
189  char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
190 
191  if (!chan) {
192  return -1;
193  }
194 
195  for (x=0;;x++) {
196  if (get_index(chan, prefix, x)) {
197  used_index_i = x;
198  } else
199  break;
200  }
201 
202  snprintf(used_index, VAR_SIZE, "%d", used_index_i);
203  snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
204 
205  if (!end)
206  condition = ast_strdupa(data);
207 
208  size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
209  my_name = ast_alloca(size);
210  memset(my_name, 0, size);
211  snprintf(my_name, size, "%s_%s_%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
212 
213  ast_channel_lock(chan);
214  if (end) {
215  label = used_index;
216  } else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
217  label = new_index;
218  pbx_builtin_setvar_helper(chan, my_name, label);
219  }
220  snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
221  if ((if_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
222  if_pri = ast_strdupa(if_pri);
223  snprintf(end_varname,VAR_SIZE,"END_%s",varname);
224  }
225  ast_channel_unlock(chan);
226 
227  if ((!end && !pbx_checkcondition(condition)) || (end > 0)) {
228  /* Condition Met (clean up helper vars) */
229  const char *goto_str;
230  pbx_builtin_setvar_helper(chan, varname, NULL);
231  pbx_builtin_setvar_helper(chan, my_name, NULL);
232  snprintf(end_varname,VAR_SIZE,"END_%s",varname);
233  ast_channel_lock(chan);
234  if ((goto_str = pbx_builtin_getvar_helper(chan, end_varname))) {
235  ast_parseable_goto(chan, goto_str);
236  pbx_builtin_setvar_helper(chan, end_varname, NULL);
237  } else {
238  int pri = find_matching_endif(chan);
239  if (pri > 0) {
240  ast_verb(3, "Jumping to priority %d\n", pri);
241  ast_channel_priority_set(chan, pri);
242  } else if (end == 2) { /* Condition added because of end > 0 instead of end == 2 */
243  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));
244  }
245  }
246  ast_channel_unlock(chan);
247  return res;
248  }
249 
250  if (!end && !if_pri) {
251  char *goto_str;
252  size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
253  goto_str = ast_alloca(size);
254  memset(goto_str, 0, size);
255  snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
256  pbx_builtin_setvar_helper(chan, varname, goto_str);
257  } else if (end && if_pri) {
258  /* END of branch */
259  snprintf(end_varname, VAR_SIZE, "END_%s", varname);
260  if (! pbx_builtin_getvar_helper(chan, end_varname)) {
261  char *goto_str;
262  size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
263  goto_str = ast_alloca(size);
264  memset(goto_str, 0, size);
265  snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan)+1);
266  pbx_builtin_setvar_helper(chan, end_varname, goto_str);
267  }
268  ast_parseable_goto(chan, if_pri);
269  }
270 
271  return res;
272 }
273 
274 static int if_start_exec(struct ast_channel *chan, const char *data) {
275  return _if_exec(chan, data, 0);
276 }
277 
278 static int if_end_exec(struct ast_channel *chan, const char *data) {
279  return _if_exec(chan, data, 1);
280 }
281 
282 static int if_exit_exec(struct ast_channel *chan, const char *data) {
283  return _if_exec(chan, data, 2);
284 }
285 
286 static int unload_module(void)
287 {
288  int res;
289 
293 
294  return res;
295 }
296 
297 static int load_module(void)
298 {
299  int res;
300 
304 
305  return res;
306 }
307 
308 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "If Branch and Conditional Execution");
const char * label
Definition: pbx.c:244
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
ast_include: include= support in extensions.conf
Definition: pbx_include.c:37
static struct ast_exten * find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
Definition: app_if.c:97
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8530
#define ast_channel_lock(chan)
Definition: channel.h:2945
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:118
Main Channel structure associated with a channel.
static int unload_module(void)
Definition: app_if.c:286
int ast_get_extension_priority(struct ast_exten *exten)
Definition: pbx.c:8558
static char * exit_app
Definition: app_if.c:85
static int if_start_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:274
Asterisk main include file. File version handling, generic pbx functions.
ast_exten: An extension The dialplan is saved as a linked list with each context having it&#39;s own link...
Definition: pbx.c:237
const char * ast_get_context_name(struct ast_context *con)
Definition: ael_main.c:421
static int _if_exec(struct ast_channel *chan, const char *data, int end)
Definition: app_if.c:179
#define LOG_WARNING
Definition: logger.h:274
const char * ast_get_extension_app(struct ast_exten *e)
Definition: pbx.c:8596
int pbx_checkcondition(const char *condition)
Evaluate a condition.
Definition: pbx.c:8321
struct ast_context * ast_walk_contexts(struct ast_context *con)
Definition: conf2ael.c:618
int ast_rdlock_contexts(void)
Read locks the context list.
Definition: pbx.c:8507
static int if_end_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:278
static struct test_val c
#define NULL
Definition: resample.c:96
static int load_module(void)
Definition: app_if.c:297
char * end
Definition: eagi_proxy.c:73
static int priority
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
int ast_channel_priority(const struct ast_channel *chan)
#define ast_verb(level,...)
Definition: logger.h:463
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Number structure.
Definition: app_followme.c:154
#define VAR_SIZE
Definition: app_if.c:87
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "If Branch and Conditional Execution")
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
Definition: pbx.c:8859
#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:85
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
const char * ast_get_extension_cidmatch(struct ast_exten *e)
Definition: pbx.c:8591
const char * ast_channel_exten(const struct ast_channel *chan)
Core PBX routines and definitions.
static int find_matching_endif(struct ast_channel *chan)
Definition: app_if.c:134
static const char * get_index(struct ast_channel *chan, const char *prefix, int idx)
Definition: app_if.c:90
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
int ast_context_includes_count(const struct ast_context *con)
Definition: pbx.c:8697
#define LOG_ERROR
Definition: logger.h:285
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8512
static int if_exit_exec(struct ast_channel *chan, const char *data)
Definition: app_if.c:282
const char * ast_get_extension_name(struct ast_exten *exten)
Definition: pbx.c:8548
struct ast_exten * ast_walk_context_extensions(struct ast_context *con, struct ast_exten *priority)
Definition: ael_main.c:427
#define ast_channel_unlock(chan)
Definition: channel.h:2946
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:4297
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
struct ast_exten * ast_walk_extension_priorities(struct ast_exten *exten, struct ast_exten *priority)
Definition: extconf.c:4063
static char * start_app
Definition: app_if.c:83
const char * ast_channel_context(const struct ast_channel *chan)
static char * stop_app
Definition: app_if.c:84
void * data
Definition: pbx.c:248
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
void ast_channel_priority_set(struct ast_channel *chan, int value)
int ast_get_extension_matchcid(struct ast_exten *e)
Definition: pbx.c:8586
Asterisk module definitions.
ast_context: An extension context - must remain in sync with fake_context
Definition: pbx.c:284
const struct ast_include * ast_context_includes_get(const struct ast_context *con, int idx)
Definition: pbx.c:8702
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8525
static char prefix[MAX_PREFIX]
Definition: http.c:141