Asterisk - The Open Source Telephony Project  21.4.1
app_while.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright 2004 - 2005, Anthony Minessale <anthmct@yahoo.com>
5  *
6  * Anthony Minessale <anthmct@yahoo.com>
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 While Loop Implementation
22  *
23  * \author Anthony Minessale <anthmct@yahoo.com>
24  *
25  * \ingroup applications
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</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="While" language="en_US">
40  <synopsis>
41  Start a while loop.
42  </synopsis>
43  <syntax>
44  <parameter name="expr" required="true" />
45  </syntax>
46  <description>
47  <para>Start a While Loop. Execution will return to this point when
48  <literal>EndWhile()</literal> is called until expr is no longer true.</para>
49  </description>
50  <see-also>
51  <ref type="application">EndWhile</ref>
52  <ref type="application">ExitWhile</ref>
53  <ref type="application">ContinueWhile</ref>
54  </see-also>
55  </application>
56  <application name="EndWhile" language="en_US">
57  <synopsis>
58  End a while loop.
59  </synopsis>
60  <syntax />
61  <description>
62  <para>Return to the previous called <literal>While()</literal>.</para>
63  </description>
64  <see-also>
65  <ref type="application">While</ref>
66  <ref type="application">ExitWhile</ref>
67  <ref type="application">ContinueWhile</ref>
68  </see-also>
69  </application>
70  <application name="ExitWhile" language="en_US">
71  <synopsis>
72  End a While loop.
73  </synopsis>
74  <syntax />
75  <description>
76  <para>Exits a <literal>While()</literal> loop, whether or not the conditional has been satisfied.</para>
77  </description>
78  <see-also>
79  <ref type="application">While</ref>
80  <ref type="application">EndWhile</ref>
81  <ref type="application">ContinueWhile</ref>
82  </see-also>
83  </application>
84  <application name="ContinueWhile" language="en_US">
85  <synopsis>
86  Restart a While loop.
87  </synopsis>
88  <syntax />
89  <description>
90  <para>Returns to the top of the while loop and re-evaluates the conditional.</para>
91  </description>
92  <see-also>
93  <ref type="application">While</ref>
94  <ref type="application">EndWhile</ref>
95  <ref type="application">ExitWhile</ref>
96  </see-also>
97  </application>
98  ***/
99 
100 static char *start_app = "While";
101 static char *stop_app = "EndWhile";
102 static char *exit_app = "ExitWhile";
103 static char *continue_app = "ContinueWhile";
104 
105 #define VAR_SIZE 64
106 
107 
108 static const char *get_index(struct ast_channel *chan, const char *prefix, int idx) {
109  char varname[VAR_SIZE];
110 
111  snprintf(varname, VAR_SIZE, "%s_%d", prefix, idx);
112  return pbx_builtin_getvar_helper(chan, varname);
113 }
114 
115 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
116 {
117  struct ast_exten *e;
118  struct ast_context *c2;
119  int idx;
120 
121  for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
122  if (ast_extension_match(ast_get_extension_name(e), exten)) {
123  int needmatch = ast_get_extension_matchcid(e);
124  if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
125  (!needmatch)) {
126  /* This is the matching extension we want */
127  struct ast_exten *p;
128  for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
129  if (priority != ast_get_extension_priority(p))
130  continue;
131  return p;
132  }
133  }
134  }
135  }
136 
137  /* No match; run through includes */
138  for (idx = 0; idx < ast_context_includes_count(c); idx++) {
139  const struct ast_include *i = ast_context_includes_get(c, idx);
140 
141  for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
142  if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
143  e = find_matching_priority(c2, exten, priority, callerid);
144  if (e)
145  return e;
146  }
147  }
148  }
149  return NULL;
150 }
151 
152 static int find_matching_endwhile(struct ast_channel *chan)
153 {
154  struct ast_context *c;
155  int res=-1;
156 
157  if (ast_rdlock_contexts()) {
158  ast_log(LOG_ERROR, "Failed to lock contexts list\n");
159  return -1;
160  }
161 
162  for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
163  struct ast_exten *e;
164 
165  if (!ast_rdlock_context(c)) {
166  if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
167  /* This is the matching context we want */
168  int cur_priority = ast_channel_priority(chan) + 1, level=1;
169 
170  for (e = find_matching_priority(c, ast_channel_exten(chan), cur_priority,
171  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
172  e;
173  e = find_matching_priority(c, ast_channel_exten(chan), ++cur_priority,
174  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
175  if (!strcasecmp(ast_get_extension_app(e), "WHILE")) {
176  level++;
177  } else if (!strcasecmp(ast_get_extension_app(e), "ENDWHILE")) {
178  level--;
179  }
180 
181  if (level == 0) {
182  res = cur_priority;
183  break;
184  }
185  }
186  }
188  if (res > 0) {
189  break;
190  }
191  }
192  }
194  return res;
195 }
196 
197 static int _while_exec(struct ast_channel *chan, const char *data, int end)
198 {
199  int res=0;
200  const char *while_pri = NULL;
201  char *my_name = NULL;
202  const char *condition = NULL, *label = NULL;
203  char varname[VAR_SIZE], end_varname[VAR_SIZE];
204  const char *prefix = "WHILE";
205  size_t size=0;
206  int used_index_i = -1, x=0;
207  char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
208 
209  if (!chan) {
210  /* huh ? */
211  return -1;
212  }
213 
214 #if 0
215  /* don't want run away loops if the chan isn't even up
216  this is up for debate since it slows things down a tad ......
217 
218  Debate is over... this prevents While/EndWhile from working
219  within the "h" extension. Not good.
220  */
221  if (ast_waitfordigit(chan,1) < 0)
222  return -1;
223 #endif
224 
225  for (x=0;;x++) {
226  if (get_index(chan, prefix, x)) {
227  used_index_i = x;
228  } else
229  break;
230  }
231 
232  snprintf(used_index, VAR_SIZE, "%d", used_index_i);
233  snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
234 
235  if (!end)
236  condition = ast_strdupa(data);
237 
238  size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
239  my_name = ast_alloca(size);
240  memset(my_name, 0, size);
241  snprintf(my_name, size, "%s_%s_%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
242 
243  ast_channel_lock(chan);
244  if (end) {
245  label = used_index;
246  } else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
247  label = new_index;
248  pbx_builtin_setvar_helper(chan, my_name, label);
249  }
250  snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
251  if ((while_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
252  while_pri = ast_strdupa(while_pri);
253  snprintf(end_varname,VAR_SIZE,"END_%s",varname);
254  }
255  ast_channel_unlock(chan);
256 
257 
258  if ((!end && !pbx_checkcondition(condition)) || (end == 2)) {
259  /* Condition Met (clean up helper vars) */
260  const char *goto_str;
261  pbx_builtin_setvar_helper(chan, varname, NULL);
262  pbx_builtin_setvar_helper(chan, my_name, NULL);
263  snprintf(end_varname,VAR_SIZE,"END_%s",varname);
264  ast_channel_lock(chan);
265  if ((goto_str = pbx_builtin_getvar_helper(chan, end_varname))) {
266  ast_parseable_goto(chan, goto_str);
267  pbx_builtin_setvar_helper(chan, end_varname, NULL);
268  } else {
269  int pri = find_matching_endwhile(chan);
270  if (pri > 0) {
271  ast_verb(3, "Jumping to priority %d\n", pri);
272  ast_channel_priority_set(chan, pri);
273  } else {
274  ast_log(LOG_WARNING, "Couldn't find matching EndWhile? (While at %s@%s priority %d)\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
275  }
276  }
277  ast_channel_unlock(chan);
278  return res;
279  }
280 
281  if (!end && !while_pri) {
282  char *goto_str;
283  size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
284  goto_str = ast_alloca(size);
285  memset(goto_str, 0, size);
286  snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
287  pbx_builtin_setvar_helper(chan, varname, goto_str);
288  }
289 
290  else if (end && while_pri) {
291  /* END of loop */
292  snprintf(end_varname, VAR_SIZE, "END_%s", varname);
293  if (! pbx_builtin_getvar_helper(chan, end_varname)) {
294  char *goto_str;
295  size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
296  goto_str = ast_alloca(size);
297  memset(goto_str, 0, size);
298  snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan)+1);
299  pbx_builtin_setvar_helper(chan, end_varname, goto_str);
300  }
301  ast_parseable_goto(chan, while_pri);
302  }
303 
304  return res;
305 }
306 
307 static int while_start_exec(struct ast_channel *chan, const char *data) {
308  return _while_exec(chan, data, 0);
309 }
310 
311 static int while_end_exec(struct ast_channel *chan, const char *data) {
312  return _while_exec(chan, data, 1);
313 }
314 
315 static int while_exit_exec(struct ast_channel *chan, const char *data) {
316  return _while_exec(chan, data, 2);
317 }
318 
319 static int while_continue_exec(struct ast_channel *chan, const char *data)
320 {
321  int x;
322  const char *prefix = "WHILE", *while_pri=NULL;
323 
324  for (x = 0; ; x++) {
325  const char *tmp = get_index(chan, prefix, x);
326  if (tmp)
327  while_pri = tmp;
328  else
329  break;
330  }
331 
332  if (while_pri)
333  ast_parseable_goto(chan, while_pri);
334 
335  return 0;
336 }
337 
338 static int unload_module(void)
339 {
340  int res;
341 
342  res = ast_unregister_application(start_app);
343  res |= ast_unregister_application(stop_app);
344  res |= ast_unregister_application(exit_app);
345  res |= ast_unregister_application(continue_app);
346 
347  return res;
348 }
349 
350 static int load_module(void)
351 {
352  int res;
353 
354  res = ast_register_application_xml(start_app, while_start_exec);
355  res |= ast_register_application_xml(stop_app, while_end_exec);
356  res |= ast_register_application_xml(exit_app, while_exit_exec);
357  res |= ast_register_application_xml(continue_app, while_continue_exec);
358 
359  return res;
360 }
361 
362 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "While Loops and Conditional Execution");
const char * label
Definition: pbx.c:244
ast_include: include= support in extensions.conf
Definition: pbx_include.c:37
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8491
Main Channel structure associated with a channel.
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's own link...
Definition: pbx.c:237
int pbx_checkcondition(const char *condition)
Evaluate a condition.
Definition: pbx.c:8282
int ast_rdlock_contexts(void)
Read locks the context list.
Definition: pbx.c:8468
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
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
int priority
Definition: pbx.c:243
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
Definition: pbx.c:8866
#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
Core PBX routines and definitions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
char * exten
Definition: pbx.c:238
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8473
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 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...
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3175
void * data
Definition: pbx.c:248
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
ast_context: An extension context
Definition: pbx.c:284
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8486