Asterisk - The Open Source Telephony Project  21.4.1
pbx_variables.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2016, CFWare, LLC
5  *
6  * Corey Farrell <git@cfware.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 PBX variables routines.
22  *
23  * \author Corey Farrell <git@cfware.com>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/_private.h"
33 #include "asterisk/app.h"
34 #include "asterisk/ast_expr.h"
35 #include "asterisk/chanvars.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/linkedlists.h"
38 #include "asterisk/lock.h"
39 #include "asterisk/module.h"
40 #include "asterisk/paths.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/stasis_channels.h"
43 #include "asterisk/test.h"
44 #include "pbx_private.h"
45 
46 /*** DOCUMENTATION
47  <application name="Set" language="en_US">
48  <synopsis>
49  Set channel variable or function value.
50  </synopsis>
51  <syntax argsep="=">
52  <parameter name="name" required="true" />
53  <parameter name="value" required="true" />
54  </syntax>
55  <description>
56  <para>This function can be used to set the value of channel variables or dialplan functions.
57  When setting variables, if the variable name is prefixed with <literal>_</literal>,
58  the variable will be inherited into channels created from the current channel.
59  If the variable name is prefixed with <literal>__</literal>, the variable will be
60  inherited into channels created from the current channel and all children channels.</para>
61  <note><para>If (and only if), in <filename>/etc/asterisk/asterisk.conf</filename>, you have
62  a <literal>[compat]</literal> category, and you have <literal>app_set = 1.4</literal> under that, then
63  the behavior of this app changes, and strips surrounding quotes from the right hand side as
64  it did previously in 1.4.
65  The advantages of not stripping out quoting, and not caring about the separator characters (comma and vertical bar)
66  were sufficient to make these changes in 1.6. Confusion about how many backslashes would be needed to properly
67  protect separators and quotes in various database access strings has been greatly
68  reduced by these changes.</para></note>
69  </description>
70  <see-also>
71  <ref type="application">MSet</ref>
72  <ref type="function">GLOBAL</ref>
73  <ref type="function">SET</ref>
74  <ref type="function">ENV</ref>
75  </see-also>
76  </application>
77  <application name="MSet" language="en_US">
78  <synopsis>
79  Set channel variable(s) or function value(s).
80  </synopsis>
81  <syntax>
82  <parameter name="set1" required="true" argsep="=">
83  <argument name="name1" required="true" />
84  <argument name="value1" required="true" />
85  </parameter>
86  <parameter name="set2" multiple="true" argsep="=">
87  <argument name="name2" required="true" />
88  <argument name="value2" required="true" />
89  </parameter>
90  </syntax>
91  <description>
92  <para>This function can be used to set the value of channel variables or dialplan functions.
93  When setting variables, if the variable name is prefixed with <literal>_</literal>,
94  the variable will be inherited into channels created from the current channel
95  If the variable name is prefixed with <literal>__</literal>, the variable will be
96  inherited into channels created from the current channel and all children channels.
97  MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus
98  prone to doing things that you may not expect. For example, it strips surrounding
99  double-quotes from the right-hand side (value). If you need to put a separator
100  character (comma or vert-bar), you will need to escape them by inserting a backslash
101  before them. Avoid its use if possible.</para>
102  <para>This application allows up to 99 variables to be set at once.</para>
103  </description>
104  <see-also>
105  <ref type="application">Set</ref>
106  </see-also>
107  </application>
108  ***/
109 
110 AST_RWLOCK_DEFINE_STATIC(globalslock);
111 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
112 
113 /*!
114  * \brief extract offset:length from variable name.
115  * \return 1 if there is a offset:length part, which is
116  * trimmed off (values go into variables)
117  */
118 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
119 {
120  int parens = 0;
121 
122  *offset = 0;
123  *length = INT_MAX;
124  *isfunc = 0;
125  for (; *var; var++) {
126  if (*var == '(') {
127  (*isfunc)++;
128  parens++;
129  } else if (*var == ')') {
130  parens--;
131  } else if (*var == ':' && parens == 0) {
132  *var++ = '\0';
133  sscanf(var, "%30d:%30d", offset, length);
134  return 1; /* offset:length valid */
135  }
136  }
137  return 0;
138 }
139 
140 /*!
141  *\brief takes a substring. It is ok to call with value == workspace.
142  * \param value
143  * \param offset < 0 means start from the end of the string and set the beginning
144  * to be that many characters back.
145  * \param length is the length of the substring, a value less than 0 means to leave
146  * that many off the end.
147  * \param workspace
148  * \param workspace_len
149  * Always return a copy in workspace.
150  */
151 static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
152 {
153  char *ret = workspace;
154  int lr; /* length of the input string after the copy */
155 
156  ast_copy_string(workspace, value, workspace_len); /* always make a copy */
157 
158  lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
159 
160  /* Quick check if no need to do anything */
161  if (offset == 0 && length >= lr) /* take the whole string */
162  return ret;
163 
164  if (offset < 0) { /* translate negative offset into positive ones */
165  offset = lr + offset;
166  if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
167  offset = 0;
168  }
169 
170  /* too large offset result in empty string so we know what to return */
171  if (offset >= lr)
172  return ret + lr; /* the final '\0' */
173 
174  ret += offset; /* move to the start position */
175  if (length >= 0 && length < lr - offset) /* truncate if necessary */
176  ret[length] = '\0';
177  else if (length < 0) {
178  if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
179  ret[lr + length - offset] = '\0';
180  else
181  ret[0] = '\0';
182  }
183 
184  return ret;
185 }
186 
187 static const char *ast_str_substring(struct ast_str *value, int offset, int length)
188 {
189  int lr; /* length of the input string after the copy */
190 
191  lr = ast_str_strlen(value); /* compute length after copy, so we never go out of the workspace */
192 
193  ast_assert(lr == strlen(ast_str_buffer(value))); /* ast_str_strlen should always agree with strlen */
194 
195  /* Quick check if no need to do anything */
196  if (offset == 0 && length >= lr) /* take the whole string */
197  return ast_str_buffer(value);
198 
199  if (offset < 0) { /* translate negative offset into positive ones */
200  offset = lr + offset;
201  if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
202  offset = 0;
203  }
204 
205  /* too large offset result in empty string so we know what to return */
206  if (offset >= lr) {
207  ast_str_reset(value);
208  return ast_str_buffer(value);
209  }
210 
211  if (offset > 0) {
212  /* Go ahead and chop off the beginning */
213  memmove(ast_str_buffer(value), ast_str_buffer(value) + offset, ast_str_strlen(value) - offset + 1);
214  lr -= offset;
215  }
216 
217  if (length >= 0 && length < lr) { /* truncate if necessary */
218  ast_str_truncate(value, length);
219  } else if (length < 0) {
220  if (lr > -length) { /* After we remove from the front and from the rear, is there anything left? */
221  ast_str_truncate(value, lr + length);
222  } else {
223  ast_str_reset(value);
224  }
225  } else {
226  /* Nothing to do, but update the buffer length */
227  ast_str_update(value);
228  }
229 
230  return ast_str_buffer(value);
231 }
232 
233 /*! \brief Support for Asterisk built-in variables in the dialplan
234 
235 \note See also
236  - \ref AstVar Channel variables
237  - \ref AstCauses The HANGUPCAUSE variable
238  */
239 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
240 {
241  struct ast_str *str = ast_str_create(16);
242  const char *cret;
243 
244  cret = ast_str_retrieve_variable(&str, 0, c, headp, var);
245  ast_copy_string(workspace, ast_str_buffer(str), workspacelen);
246  *ret = cret ? workspace : NULL;
247  ast_free(str);
248 }
249 
250 const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
251 {
252  const char not_found = '\0';
253  char *tmpvar;
254  const char *ret;
255  const char *s; /* the result */
256  int offset, length;
257  int i, need_substring;
258  struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
259  char workspace[20];
260 
261  if (c) {
262  ast_channel_lock(c);
263  places[0] = ast_channel_varshead(c);
264  }
265  /*
266  * Make a copy of var because parse_variable_name() modifies the string.
267  * Then if called directly, we might need to run substring() on the result;
268  * remember this for later in 'need_substring', 'offset' and 'length'
269  */
270  tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
271  need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
272 
273  /*
274  * Look first into predefined variables, then into variable lists.
275  * Variable 's' points to the result, according to the following rules:
276  * s == &not_found (set at the beginning) means that we did not find a
277  * matching variable and need to look into more places.
278  * If s != &not_found, s is a valid result string as follows:
279  * s = NULL if the variable does not have a value;
280  * you typically do this when looking for an unset predefined variable.
281  * s = workspace if the result has been assembled there;
282  * typically done when the result is built e.g. with an snprintf(),
283  * so we don't need to do an additional copy.
284  * s != workspace in case we have a string, that needs to be copied
285  * (the ast_copy_string is done once for all at the end).
286  * Typically done when the result is already available in some string.
287  */
288  s = &not_found; /* default value */
289  if (c) { /* This group requires a valid channel */
290  /* Names with common parts are looked up a piece at a time using strncmp. */
291  if (!strncmp(var, "CALL", 4)) {
292  if (!strncmp(var + 4, "ING", 3)) {
293  if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
294  ast_str_set(str, maxlen, "%d",
295  ast_party_id_presentation(&ast_channel_caller(c)->id));
296  s = ast_str_buffer(*str);
297  } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
298  ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->ani2);
299  s = ast_str_buffer(*str);
300  } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
301  ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->id.number.plan);
302  s = ast_str_buffer(*str);
303  } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
304  ast_str_set(str, maxlen, "%d", ast_channel_dialed(c)->transit_network_select);
305  s = ast_str_buffer(*str);
306  }
307  }
308  } else if (!strcmp(var, "HINT")) {
309  s = ast_str_get_hint(str, maxlen, NULL, 0, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
310  } else if (!strcmp(var, "HINTNAME")) {
311  s = ast_str_get_hint(NULL, 0, str, maxlen, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
312  } else if (!strcmp(var, "EXTEN")) {
313  s = ast_channel_exten(c);
314  } else if (!strcmp(var, "CONTEXT")) {
315  s = ast_channel_context(c);
316  } else if (!strcmp(var, "PRIORITY")) {
317  ast_str_set(str, maxlen, "%d", ast_channel_priority(c));
318  s = ast_str_buffer(*str);
319  } else if (!strcmp(var, "CHANNEL")) {
320  s = ast_channel_name(c);
321  } else if (!strcmp(var, "UNIQUEID")) {
322  s = ast_channel_uniqueid(c);
323  } else if (!strcmp(var, "HANGUPCAUSE")) {
324  ast_str_set(str, maxlen, "%d", ast_channel_hangupcause(c));
325  s = ast_str_buffer(*str);
326  }
327  }
328  if (s == &not_found) { /* look for more */
329  if (!strcmp(var, "EPOCH")) {
330  ast_str_set(str, maxlen, "%d", (int) time(NULL));
331  s = ast_str_buffer(*str);
332  } else if (!strcmp(var, "SYSTEMNAME")) {
333  s = ast_config_AST_SYSTEM_NAME;
334  } else if (!strcmp(var, "ASTCACHEDIR")) {
335  s = ast_config_AST_CACHE_DIR;
336  } else if (!strcmp(var, "ASTETCDIR")) {
337  s = ast_config_AST_CONFIG_DIR;
338  } else if (!strcmp(var, "ASTMODDIR")) {
339  s = ast_config_AST_MODULE_DIR;
340  } else if (!strcmp(var, "ASTVARLIBDIR")) {
341  s = ast_config_AST_VAR_DIR;
342  } else if (!strcmp(var, "ASTDBDIR")) {
343  s = ast_config_AST_DB;
344  } else if (!strcmp(var, "ASTKEYDIR")) {
345  s = ast_config_AST_KEY_DIR;
346  } else if (!strcmp(var, "ASTDATADIR")) {
347  s = ast_config_AST_DATA_DIR;
348  } else if (!strcmp(var, "ASTAGIDIR")) {
349  s = ast_config_AST_AGI_DIR;
350  } else if (!strcmp(var, "ASTSPOOLDIR")) {
351  s = ast_config_AST_SPOOL_DIR;
352  } else if (!strcmp(var, "ASTRUNDIR")) {
353  s = ast_config_AST_RUN_DIR;
354  } else if (!strcmp(var, "ASTLOGDIR")) {
355  s = ast_config_AST_LOG_DIR;
356  } else if (!strcmp(var, "ASTSBINDIR")) {
357  s = ast_config_AST_SBIN_DIR;
358  } else if (!strcmp(var, "ENTITYID")) {
359  ast_eid_to_str(workspace, sizeof(workspace), &ast_eid_default);
360  s = workspace;
361  }
362  }
363  /* if not found, look into chanvars or global vars */
364  for (i = 0; s == &not_found && i < ARRAY_LEN(places); i++) {
365  struct ast_var_t *variables;
366  if (!places[i])
367  continue;
368  if (places[i] == &globals)
369  ast_rwlock_rdlock(&globalslock);
370  AST_LIST_TRAVERSE(places[i], variables, entries) {
371  if (!strcmp(ast_var_name(variables), var)) {
372  s = ast_var_value(variables);
373  break;
374  }
375  }
376  if (places[i] == &globals)
377  ast_rwlock_unlock(&globalslock);
378  }
379  if (s == &not_found || s == NULL) {
380  ast_debug(5, "Result of '%s' is NULL\n", var);
381  ret = NULL;
382  } else {
383  ast_debug(5, "Result of '%s' is '%s'\n", var, s);
384  if (s != ast_str_buffer(*str)) {
385  ast_str_set(str, maxlen, "%s", s);
386  }
387  ret = ast_str_buffer(*str);
388  if (need_substring) {
389  ret = ast_str_substring(*str, offset, length);
390  ast_debug(2, "Final result of '%s' is '%s'\n", var, ret);
391  }
392  }
393 
394  if (c) {
395  ast_channel_unlock(c);
396  }
397  return ret;
398 }
399 
400 void ast_str_substitute_variables_full2(struct ast_str **buf, ssize_t maxlen,
401  struct ast_channel *c, struct varshead *headp, const char *templ,
402  size_t *used, int use_both)
403 {
404  /* Substitutes variables into buf, based on string templ */
405  const char *whereweare;
406  struct ast_str *substr1 = ast_str_create(16);
407  struct ast_str *substr2 = NULL;
408  struct ast_str *substr3 = ast_str_create(16);
409 
410  ast_str_reset(*buf);
411 
412  if (!substr1 || !substr3) {
413  if (used) {
414  *used = ast_str_strlen(*buf);
415  }
416  ast_free(substr1);
417  ast_free(substr3);
418  return;
419  }
420 
421  whereweare = templ;
422  while (!ast_strlen_zero(whereweare)) {
423  const char *nextvar = NULL;
424  const char *nextexp = NULL;
425  const char *nextthing;
426  const char *vars;
427  const char *vare;
428  char *finalvars;
429  int pos;
430  int brackets;
431  int needsub;
432  int len;
433 
434  /* reset our buffer */
435  ast_str_reset(substr3);
436 
437  /* Determine how much simply needs to be copied to the output buf. */
438  nextthing = strchr(whereweare, '$');
439  if (nextthing) {
440  pos = nextthing - whereweare;
441  switch (nextthing[1]) {
442  case '{':
443  /* Variable substitution */
444  nextvar = nextthing;
445  break;
446  case '[':
447  /* Expression substitution */
448  nextexp = nextthing;
449  break;
450  default:
451  /* '$' is not part of a substitution so include it too. */
452  ++pos;
453  break;
454  }
455  } else {
456  /* We're copying the whole remaining string */
457  pos = strlen(whereweare);
458  }
459 
460  if (pos) {
461  /* Copy that many bytes */
462  ast_str_append_substr(buf, maxlen, whereweare, pos);
463 
464  whereweare += pos;
465  }
466 
467  if (nextvar) {
468  int offset;
469  int offset2;
470  int isfunction;
471  int res;
472 
473  /* We have a variable. Find the start and end, and determine
474  if we are going to have to recursively call ourselves on the
475  contents */
476  vars = vare = nextvar + 2;
477  brackets = 1;
478  needsub = 0;
479 
480  /* Find the end of it */
481  while (brackets && *vare) {
482  if ((vare[0] == '$') && (vare[1] == '{')) {
483  needsub++;
484  brackets++;
485  vare++;
486  } else if (vare[0] == '{') {
487  brackets++;
488  } else if (vare[0] == '}') {
489  brackets--;
490  } else if ((vare[0] == '$') && (vare[1] == '[')) {
491  needsub++;
492  vare++;
493  }
494  vare++;
495  }
496  len = vare - vars;
497  if (brackets) {
498  ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
499  } else {
500  /* Don't count the closing '}' in the length. */
501  --len;
502  }
503 
504  /* Skip totally over variable string */
505  whereweare = vare;
506 
507  /* Store variable name expression to lookup. */
508  ast_str_set_substr(&substr1, 0, vars, len);
509  ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n",
510  ast_str_buffer(substr1), vars, len);
511 
512  /* Substitute if necessary */
513  if (needsub) {
514  if (!substr2) {
515  substr2 = ast_str_create(16);
516  if (!substr2) {
517  continue;
518  }
519  }
520  ast_str_substitute_variables_full2(&substr2, 0, c, headp,
521  ast_str_buffer(substr1), NULL, use_both);
522  finalvars = ast_str_buffer(substr2);
523  } else {
524  finalvars = ast_str_buffer(substr1);
525  }
526 
527  parse_variable_name(finalvars, &offset, &offset2, &isfunction);
528  if (isfunction) {
529  /* Evaluate function */
530  res = -1;
531  if (c) {
532  res = ast_func_read2(c, finalvars, &substr3, 0);
533  ast_debug(2, "Function %s result is '%s' from channel\n",
534  finalvars, res ? "" : ast_str_buffer(substr3));
535  }
536  if (!c || (c && res < 0 && use_both)) {
537  struct varshead old;
538  struct ast_channel *bogus;
539 
540  bogus = ast_dummy_channel_alloc();
541  if (bogus) {
542  old = *ast_channel_varshead(bogus);
543  if (headp) {
544  *ast_channel_varshead(bogus) = *headp;
545  }
546  res = ast_func_read2(bogus, finalvars, &substr3, 0);
547  /* Don't deallocate the varshead that was passed in */
548  if (headp) {
549  *ast_channel_varshead(bogus) = old;
550  }
551  ast_channel_unref(bogus);
552  } else {
553  ast_log(LOG_ERROR, "Unable to allocate bogus channel for function value substitution.\n");
554  res = -1;
555  }
556  ast_debug(2, "Function %s result is '%s' from headp\n",
557  finalvars, res ? "" : ast_str_buffer(substr3));
558  }
559  } else {
560  const char *result;
561  if (c) {
562  result = ast_str_retrieve_variable(&substr3, 0, c, NULL, finalvars);
563  ast_debug(2, "Variable %s result is '%s' from channel\n",
564  finalvars, S_OR(result, ""));
565  }
566  if (!c || (c && !result && use_both)) {
567  result = ast_str_retrieve_variable(&substr3, 0, NULL, headp, finalvars);
568  ast_debug(2, "Variable %s result is '%s' from headp\n",
569  finalvars, S_OR(result, ""));
570  }
571  res = (result ? 0 : -1);
572  }
573  if (!res) {
574  ast_str_substring(substr3, offset, offset2);
575  ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
576  }
577  } else if (nextexp) {
578  /* We have an expression. Find the start and end, and determine
579  if we are going to have to recursively call ourselves on the
580  contents */
581  vars = vare = nextexp + 2;
582  brackets = 1;
583  needsub = 0;
584 
585  /* Find the end of it */
586  while (brackets && *vare) {
587  if ((vare[0] == '$') && (vare[1] == '[')) {
588  needsub++;
589  brackets++;
590  vare++;
591  } else if (vare[0] == '[') {
592  brackets++;
593  } else if (vare[0] == ']') {
594  brackets--;
595  } else if ((vare[0] == '$') && (vare[1] == '{')) {
596  needsub++;
597  vare++;
598  }
599  vare++;
600  }
601  len = vare - vars;
602  if (brackets) {
603  ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
604  } else {
605  /* Don't count the closing ']' in the length. */
606  --len;
607  }
608 
609  /* Skip totally over expression */
610  whereweare = vare;
611 
612  /* Store expression to evaluate. */
613  ast_str_set_substr(&substr1, 0, vars, len);
614 
615  /* Substitute if necessary */
616  if (needsub) {
617  if (!substr2) {
618  substr2 = ast_str_create(16);
619  if (!substr2) {
620  continue;
621  }
622  }
623  ast_str_substitute_variables_full2(&substr2, 0, c, headp,
624  ast_str_buffer(substr1), NULL, use_both);
625  finalvars = ast_str_buffer(substr2);
626  } else {
627  finalvars = ast_str_buffer(substr1);
628  }
629 
630  if (ast_str_expr(&substr3, 0, c, finalvars)) {
631  ast_debug(2, "Expression result is '%s'\n", ast_str_buffer(substr3));
632  }
633  ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
634  }
635  }
636  if (used) {
637  *used = ast_str_strlen(*buf);
638  }
639  ast_free(substr1);
640  ast_free(substr2);
641  ast_free(substr3);
642 }
643 
644 void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen,
645  struct ast_channel *chan, struct varshead *headp, const char *templ, size_t *used)
646 {
647  ast_str_substitute_variables_full2(buf, maxlen, chan, headp, templ, used, 0);
648 }
649 
650 void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
651 {
652  ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, NULL);
653 }
654 
655 void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
656 {
657  ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, NULL);
658 }
659 
660 void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
661 {
662  pbx_substitute_variables_helper_full_location(c, headp, cp1, cp2, count, used, NULL, NULL, 0);
663 }
664 
665 /*! \brief Thread local keeping track of recursion depth */
666 AST_THREADSTORAGE(varsub_recurse_level);
667 
668 #define MAX_VARIABLE_SUB_RECURSE_DEPTH 15
669 
670 void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used, const char *context, const char *exten, int pri)
671 {
672  /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
673  const char *whereweare;
674  const char *orig_cp2 = cp2;
675  char ltmp[VAR_BUF_SIZE];
676  char var[VAR_BUF_SIZE];
677  int *recurse_depth;
678 
679  *cp2 = 0; /* just in case nothing ends up there */
680 
681  /* It is possible to craft dialplan that will recurse indefinitely and cause a stack overflow.
682  * This is symptomatic of a dialplan bug, so abort substitution rather than crash. */
683  recurse_depth = ast_threadstorage_get(&varsub_recurse_level, sizeof(*recurse_depth));
684  if (!recurse_depth) {
685  return;
686  }
687  if ((*recurse_depth)++ >= MAX_VARIABLE_SUB_RECURSE_DEPTH) {
688  ast_log(LOG_ERROR, "Exceeded maximum variable substitution recursion depth (%d) - possible infinite recursion in dialplan?\n", MAX_VARIABLE_SUB_RECURSE_DEPTH);
689  (*recurse_depth)--;
690  return;
691  }
692 
693  whereweare = cp1;
694  while (!ast_strlen_zero(whereweare) && count) {
695  char *nextvar = NULL;
696  char *nextexp = NULL;
697  char *nextthing;
698  char *vars;
699  char *vare;
700  int length;
701  int pos;
702  int brackets;
703  int needsub;
704  int len;
705 
706  /* Determine how much simply needs to be copied to the output buf. */
707  nextthing = strchr(whereweare, '$');
708  if (nextthing) {
709  pos = nextthing - whereweare;
710  switch (nextthing[1]) {
711  case '{':
712  /* Variable substitution */
713  nextvar = nextthing;
714  break;
715  case '[':
716  /* Expression substitution */
717  nextexp = nextthing;
718  break;
719  default:
720  /* '$' is not part of a substitution so include it too. */
721  ++pos;
722  break;
723  }
724  } else {
725  /* We're copying the whole remaining string */
726  pos = strlen(whereweare);
727  }
728 
729  if (pos) {
730  /* Can't copy more than 'count' bytes */
731  if (pos > count)
732  pos = count;
733 
734  /* Copy that many bytes */
735  memcpy(cp2, whereweare, pos);
736 
737  count -= pos;
738  cp2 += pos;
739  whereweare += pos;
740  *cp2 = 0;
741  }
742 
743  if (nextvar) {
744  int offset;
745  int offset2;
746  int isfunction;
747  char *cp4 = NULL;
748  char workspace[VAR_BUF_SIZE] = "";
749 
750  /* We have a variable. Find the start and end, and determine
751  if we are going to have to recursively call ourselves on the
752  contents */
753  vars = vare = nextvar + 2;
754  brackets = 1;
755  needsub = 0;
756 
757  /* Find the end of it */
758  while (brackets && *vare) {
759  if ((vare[0] == '$') && (vare[1] == '{')) {
760  needsub++;
761  brackets++;
762  vare++;
763  } else if (vare[0] == '{') {
764  brackets++;
765  } else if (vare[0] == '}') {
766  brackets--;
767  } else if ((vare[0] == '$') && (vare[1] == '[')) {
768  needsub++;
769  vare++;
770  }
771  vare++;
772  }
773  len = vare - vars;
774  if (brackets) {
775  ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
776  } else {
777  /* Don't count the closing '}' in the length. */
778  --len;
779  }
780 
781  /* Skip totally over variable string */
782  whereweare = vare;
783 
784  /* Store variable name expression to lookup (and truncate). */
785  ast_copy_string(var, vars, len + 1);
786 
787  /* Substitute if necessary */
788  if (needsub) {
789  pbx_substitute_variables_helper_full_location(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL, context, exten, pri);
790  vars = ltmp;
791  } else {
792  vars = var;
793  }
794 
795  parse_variable_name(vars, &offset, &offset2, &isfunction);
796  if (isfunction) {
797  /* Evaluate function */
798  if (c || !headp)
799  cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
800  else {
801  struct varshead old;
802  struct ast_channel *bogus;
803 
804  bogus = ast_dummy_channel_alloc();
805  if (bogus) {
806  old = *ast_channel_varshead(bogus);
807  *ast_channel_varshead(bogus) = *headp;
808  cp4 = ast_func_read(bogus, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
809  /* Don't deallocate the varshead that was passed in */
810  *ast_channel_varshead(bogus) = old;
811  ast_channel_unref(bogus);
812  } else {
813  ast_log(LOG_ERROR, "Unable to allocate bogus channel for function value substitution.\n");
814  cp4 = NULL;
815  }
816  }
817  ast_debug(2, "Function %s result is '%s'\n", vars, cp4 ? cp4 : "(null)");
818  } else {
819  /* Retrieve variable value */
820  /* For dialplan location, if we were told what to substitute explicitly, use that instead */
821  if (exten && !strcmp(vars, "EXTEN")) {
822  ast_copy_string(workspace, exten, VAR_BUF_SIZE);
823  cp4 = workspace;
824  } else if (context && !strcmp(vars, "CONTEXT")) {
825  ast_copy_string(workspace, context, VAR_BUF_SIZE);
826  cp4 = workspace;
827  } else if (pri && !strcmp(vars, "PRIORITY")) {
828  snprintf(workspace, VAR_BUF_SIZE, "%d", pri);
829  cp4 = workspace;
830  } else {
831  pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
832  }
833  }
834  if (cp4) {
835  cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
836 
837  length = strlen(cp4);
838  if (length > count)
839  length = count;
840  memcpy(cp2, cp4, length);
841  count -= length;
842  cp2 += length;
843  *cp2 = 0;
844  }
845  } else if (nextexp) {
846  /* We have an expression. Find the start and end, and determine
847  if we are going to have to recursively call ourselves on the
848  contents */
849  vars = vare = nextexp + 2;
850  brackets = 1;
851  needsub = 0;
852 
853  /* Find the end of it */
854  while (brackets && *vare) {
855  if ((vare[0] == '$') && (vare[1] == '[')) {
856  needsub++;
857  brackets++;
858  vare++;
859  } else if (vare[0] == '[') {
860  brackets++;
861  } else if (vare[0] == ']') {
862  brackets--;
863  } else if ((vare[0] == '$') && (vare[1] == '{')) {
864  needsub++;
865  vare++;
866  }
867  vare++;
868  }
869  len = vare - vars;
870  if (brackets) {
871  ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
872  } else {
873  /* Don't count the closing ']' in the length. */
874  --len;
875  }
876 
877  /* Skip totally over expression */
878  whereweare = vare;
879 
880  /* Store expression to evaluate (and truncate). */
881  ast_copy_string(var, vars, len + 1);
882 
883  /* Substitute if necessary */
884  if (needsub) {
885  pbx_substitute_variables_helper_full_location(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL, context, exten, pri);
886  vars = ltmp;
887  } else {
888  vars = var;
889  }
890 
891  length = ast_expr(vars, cp2, count, c);
892  if (length) {
893  ast_debug(1, "Expression result is '%s'\n", cp2);
894  count -= length;
895  cp2 += length;
896  *cp2 = 0;
897  }
898  }
899  }
900  if (used) {
901  *used = cp2 - orig_cp2;
902  }
903  (*recurse_depth)--;
904 }
905 
906 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
907 {
908  pbx_substitute_variables_helper_full(c, (c) ? ast_channel_varshead(c) : NULL, cp1, cp2, count, NULL);
909 }
910 
911 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
912 {
913  pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, NULL);
914 }
915 
916 /*! \brief CLI support for listing global variables in a parseable way */
917 static char *handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
918 {
919  int i = 0;
920  struct ast_var_t *newvariable;
921 
922  switch (cmd) {
923  case CLI_INIT:
924  e->command = "dialplan show globals";
925  e->usage =
926  "Usage: dialplan show globals\n"
927  " List current global dialplan variables and their values\n";
928  return NULL;
929  case CLI_GENERATE:
930  return NULL;
931  }
932 
933  ast_rwlock_rdlock(&globalslock);
934  AST_LIST_TRAVERSE (&globals, newvariable, entries) {
935  i++;
936  ast_cli(a->fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
937  }
938  ast_rwlock_unlock(&globalslock);
939  ast_cli(a->fd, "\n -- %d variable(s)\n", i);
940 
941  return CLI_SUCCESS;
942 }
943 
944 /*! \brief CLI support for listing chanvar's variables in a parseable way */
945 static char *handle_show_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
946 {
947  struct ast_channel *chan;
948  struct ast_var_t *var;
949 
950  switch (cmd) {
951  case CLI_INIT:
952  e->command = "dialplan show chanvar";
953  e->usage =
954  "Usage: dialplan show chanvar <channel>\n"
955  " List current channel variables and their values\n";
956  return NULL;
957  case CLI_GENERATE:
958  return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
959  }
960 
961  if (a->argc != e->args + 1) {
962  return CLI_SHOWUSAGE;
963  }
964 
965  chan = ast_channel_get_by_name(a->argv[e->args]);
966  if (!chan) {
967  ast_cli(a->fd, "Channel '%s' not found\n", a->argv[e->args]);
968  return CLI_FAILURE;
969  }
970 
971  ast_channel_lock(chan);
972  AST_LIST_TRAVERSE(ast_channel_varshead(chan), var, entries) {
973  ast_cli(a->fd, "%s=%s\n", ast_var_name(var), ast_var_value(var));
974  }
975  ast_channel_unlock(chan);
976 
977  ast_channel_unref(chan);
978  return CLI_SUCCESS;
979 }
980 
981 /*! \brief CLI support for executing function */
982 static char *handle_eval_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
983 {
984  struct ast_channel *c = NULL;
985  char *fn, *substituted;
986  int ret;
987  char workspace[1024];
988 
989  switch (cmd) {
990  case CLI_INIT:
991  e->command = "dialplan eval function";
992  e->usage =
993  "Usage: dialplan eval function <name(args)>\n"
994  " Evaluate a dialplan function call\n"
995  " A dummy channel is used to evaluate\n"
996  " the function call, so only global\n"
997  " variables should be used.\n";
998  return NULL;
999  case CLI_GENERATE:
1000  return NULL;
1001  }
1002 
1003  if (a->argc != e->args + 1) {
1004  return CLI_SHOWUSAGE;
1005  }
1006 
1008  if (!c) {
1009  ast_cli(a->fd, "Unable to allocate bogus channel for function evaluation.\n");
1010  return CLI_FAILURE;
1011  }
1012 
1013  fn = (char *) a->argv[3];
1014  pbx_substitute_variables_helper(c, fn, workspace, sizeof(workspace));
1015  substituted = ast_strdupa(workspace);
1016  workspace[0] = '\0';
1017  ret = ast_func_read(c, substituted, workspace, sizeof(workspace));
1018 
1019  c = ast_channel_unref(c);
1020 
1021  ast_cli(a->fd, "Return Value: %s (%d)\n", ret ? "Failure" : "Success", ret);
1022  ast_cli(a->fd, "Result: %s\n", workspace);
1023 
1024  return CLI_SUCCESS;
1025 }
1026 
1027 static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1028 {
1029  switch (cmd) {
1030  case CLI_INIT:
1031  e->command = "dialplan set global";
1032  e->usage =
1033  "Usage: dialplan set global <name> <value>\n"
1034  " Set global dialplan variable <name> to <value>\n";
1035  return NULL;
1036  case CLI_GENERATE:
1037  return NULL;
1038  }
1039 
1040  if (a->argc != e->args + 2)
1041  return CLI_SHOWUSAGE;
1042 
1043  pbx_builtin_setvar_helper(NULL, a->argv[3], a->argv[4]);
1044  ast_cli(a->fd, "\n -- Global variable '%s' set to '%s'\n", a->argv[3], a->argv[4]);
1045 
1046  return CLI_SUCCESS;
1047 }
1048 
1049 static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1050 {
1051  struct ast_channel *chan;
1052  const char *chan_name, *var_name, *var_value;
1053 
1054  switch (cmd) {
1055  case CLI_INIT:
1056  e->command = "dialplan set chanvar";
1057  e->usage =
1058  "Usage: dialplan set chanvar <channel> <varname> <value>\n"
1059  " Set channel variable <varname> to <value>\n";
1060  return NULL;
1061  case CLI_GENERATE:
1062  return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
1063  }
1064 
1065  if (a->argc != e->args + 3)
1066  return CLI_SHOWUSAGE;
1067 
1068  chan_name = a->argv[e->args];
1069  var_name = a->argv[e->args + 1];
1070  var_value = a->argv[e->args + 2];
1071 
1072  if (!(chan = ast_channel_get_by_name(chan_name))) {
1073  ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
1074  return CLI_FAILURE;
1075  }
1076 
1077  pbx_builtin_setvar_helper(chan, var_name, var_value);
1078 
1079  chan = ast_channel_unref(chan);
1080 
1081  ast_cli(a->fd, "\n -- Channel variable '%s' set to '%s' for '%s'\n", var_name, var_value, chan_name);
1082 
1083  return CLI_SUCCESS;
1084 }
1085 
1086 int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
1087 {
1088  struct ast_var_t *variables;
1089  const char *var, *val;
1090  int total = 0;
1091 
1092  if (!chan)
1093  return 0;
1094 
1095  ast_str_reset(*buf);
1096 
1097  ast_channel_lock(chan);
1098 
1099  AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
1100  if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
1101  /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
1102  ) {
1103  if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
1104  ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
1105  break;
1106  } else
1107  total++;
1108  } else
1109  break;
1110  }
1111 
1112  ast_channel_unlock(chan);
1113 
1114  return total;
1115 }
1116 
1117 const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
1118 {
1119  struct ast_var_t *variables;
1120  const char *ret = NULL;
1121  int i;
1122  struct varshead *places[2] = { NULL, &globals };
1123 
1124  if (!name)
1125  return NULL;
1126 
1127  if (chan) {
1128  ast_channel_lock(chan);
1129  places[0] = ast_channel_varshead(chan);
1130  }
1131 
1132  for (i = 0; i < 2; i++) {
1133  if (!places[i])
1134  continue;
1135  if (places[i] == &globals)
1136  ast_rwlock_rdlock(&globalslock);
1137  AST_LIST_TRAVERSE(places[i], variables, entries) {
1138  if (!strcmp(name, ast_var_name(variables))) {
1139  ret = ast_var_value(variables);
1140  break;
1141  }
1142  }
1143  if (places[i] == &globals)
1144  ast_rwlock_unlock(&globalslock);
1145  if (ret)
1146  break;
1147  }
1148 
1149  if (chan)
1150  ast_channel_unlock(chan);
1151 
1152  return ret;
1153 }
1154 
1155 void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
1156 {
1157  struct ast_var_t *newvariable;
1158  struct varshead *headp;
1159 
1160  if (name[strlen(name)-1] == ')') {
1161  char *function = ast_strdupa(name);
1162 
1163  ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
1164  ast_func_write(chan, function, value);
1165  return;
1166  }
1167 
1168  if (chan) {
1169  ast_channel_lock(chan);
1170  headp = ast_channel_varshead(chan);
1171  } else {
1172  ast_rwlock_wrlock(&globalslock);
1173  headp = &globals;
1174  }
1175 
1176  if (value && (newvariable = ast_var_assign(name, value))) {
1177  if (headp == &globals)
1178  ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
1179  AST_LIST_INSERT_HEAD(headp, newvariable, entries);
1180  }
1181 
1182  if (chan)
1183  ast_channel_unlock(chan);
1184  else
1185  ast_rwlock_unlock(&globalslock);
1186 }
1187 
1188 int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
1189 {
1190  struct ast_var_t *newvariable;
1191  struct varshead *headp;
1192  const char *nametail = name;
1193  /*! True if the old value was not an empty string. */
1194  int old_value_existed = 0;
1195 
1196  if (name[strlen(name) - 1] == ')') {
1197  char *function = ast_strdupa(name);
1198 
1199  return ast_func_write(chan, function, value);
1200  }
1201 
1202  if (chan) {
1203  ast_channel_lock(chan);
1204  headp = ast_channel_varshead(chan);
1205  } else {
1206  ast_rwlock_wrlock(&globalslock);
1207  headp = &globals;
1208  }
1209 
1210  /* For comparison purposes, we have to strip leading underscores */
1211  if (*nametail == '_') {
1212  nametail++;
1213  if (*nametail == '_')
1214  nametail++;
1215  }
1216 
1217  AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
1218  if (strcmp(ast_var_name(newvariable), nametail) == 0) {
1219  /* there is already such a variable, delete it */
1220  AST_LIST_REMOVE_CURRENT(entries);
1221  old_value_existed = !ast_strlen_zero(ast_var_value(newvariable));
1222  ast_var_delete(newvariable);
1223  break;
1224  }
1225  }
1227 
1228  if (value && (newvariable = ast_var_assign(name, value))) {
1229  if (headp == &globals) {
1230  ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
1231  }
1232  AST_LIST_INSERT_HEAD(headp, newvariable, entries);
1233  ast_channel_publish_varset(chan, name, value);
1234  } else if (old_value_existed) {
1235  /* We just deleted a non-empty dialplan variable. */
1236  ast_channel_publish_varset(chan, name, "");
1237  }
1238 
1239  if (chan)
1240  ast_channel_unlock(chan);
1241  else
1242  ast_rwlock_unlock(&globalslock);
1243  return 0;
1244 }
1245 
1246 int pbx_builtin_setvar(struct ast_channel *chan, const char *data)
1247 {
1248  char *name, *value, *mydata;
1249 
1250  if (ast_strlen_zero(data)) {
1251  ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
1252  return 0;
1253  }
1254 
1255  mydata = ast_strdupa(data);
1256  name = strsep(&mydata, "=");
1257  value = mydata;
1258  if (!value) {
1259  ast_log(LOG_WARNING, "Set requires an '=' to be a valid assignment.\n");
1260  return 0;
1261  }
1262 
1263  if (strchr(name, ' ')) {
1264  ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
1265  }
1266 
1267  pbx_builtin_setvar_helper(chan, name, value);
1268 
1269  return 0;
1270 }
1271 
1272 int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *vdata)
1273 {
1274  char *data;
1275  int x;
1276  AST_DECLARE_APP_ARGS(args,
1277  AST_APP_ARG(pair)[99]; /* parse up to 99 variables */
1278  );
1280  AST_APP_ARG(name);
1281  AST_APP_ARG(value);
1282  );
1283 
1284  if (ast_strlen_zero(vdata)) {
1285  ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
1286  return 0;
1287  }
1288 
1289  data = ast_strdupa(vdata);
1290  AST_STANDARD_APP_ARGS(args, data);
1291 
1292  for (x = 0; x < args.argc; x++) {
1293  AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
1294  if (pair.argc == 2) {
1295  pbx_builtin_setvar_helper(chan, pair.name, pair.value);
1296  if (strchr(pair.name, ' '))
1297  ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
1298  } else if (!chan) {
1299  ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '='\n", pair.name);
1300  } else {
1301  ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan));
1302  }
1303  }
1304 
1305  return 0;
1306 }
1307 
1308 void pbx_builtin_clear_globals(void)
1309 {
1310  struct ast_var_t *vardata;
1311 
1312  ast_rwlock_wrlock(&globalslock);
1313  while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
1314  ast_var_delete(vardata);
1315  ast_rwlock_unlock(&globalslock);
1316 }
1317 
1318 #ifdef TEST_FRAMEWORK
1319 AST_TEST_DEFINE(test_variable_substrings)
1320 {
1321  int i, res = AST_TEST_PASS;
1322  struct ast_channel *chan; /* dummy channel */
1323  struct ast_str *str; /* fancy string for holding comparing value */
1324 
1325  const char *test_strings[][5] = {
1326  {"somevaluehere", "CALLERID(num):0:25", "somevaluehere"},
1327  {"somevaluehere", "CALLERID(num):0:5", "somev"},
1328  {"somevaluehere", "CALLERID(num):4:5", "value"},
1329  {"somevaluehere", "CALLERID(num):0:-4", "somevalue"},
1330  {"somevaluehere", "CALLERID(num):-4", "here"},
1331  };
1332 
1333  switch (cmd) {
1334  case TEST_INIT:
1335  info->name = "variable_substrings";
1336  info->category = "/main/pbx/";
1337  info->summary = "Test variable substring resolution";
1338  info->description = "Verify that variable substrings are calculated correctly";
1339  return AST_TEST_NOT_RUN;
1340  case TEST_EXECUTE:
1341  break;
1342  }
1343 
1344  if (!(chan = ast_dummy_channel_alloc())) {
1345  ast_test_status_update(test, "Unable to allocate dummy channel\n");
1346  return AST_TEST_FAIL;
1347  }
1348 
1349  if (!(str = ast_str_create(64))) {
1350  ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1351  ast_channel_release(chan);
1352  return AST_TEST_FAIL;
1353  }
1354 
1355  for (i = 0; i < ARRAY_LEN(test_strings); i++) {
1356  char substituted[512], tmp[512] = "";
1357 
1358  ast_set_callerid(chan, test_strings[i][0], NULL, test_strings[i][0]);
1359 
1360  snprintf(tmp, sizeof(tmp), "${%s}", test_strings[i][1]);
1361 
1362  /* test ast_str_substitute_variables */
1363  ast_str_substitute_variables(&str, 0, chan, tmp);
1364  ast_debug(1, "Comparing STR %s with %s\n", ast_str_buffer(str), test_strings[i][2]);
1365  if (strcmp(test_strings[i][2], ast_str_buffer(str))) {
1366  ast_test_status_update(test, "Format string '%s' substituted to '%s' using str sub. Expected '%s'.\n", test_strings[i][0], ast_str_buffer(str), test_strings[i][2]);
1367  res = AST_TEST_FAIL;
1368  }
1369 
1370  /* test pbx_substitute_variables_helper */
1371  pbx_substitute_variables_helper(chan, tmp, substituted, sizeof(substituted) - 1);
1372  ast_debug(1, "Comparing PBX %s with %s\n", substituted, test_strings[i][2]);
1373  if (strcmp(test_strings[i][2], substituted)) {
1374  ast_test_status_update(test, "Format string '%s' substituted to '%s' using pbx sub. Expected '%s'.\n", test_strings[i][0], substituted, test_strings[i][2]);
1375  res = AST_TEST_FAIL;
1376  }
1377  }
1378  ast_free(str);
1379 
1380  ast_channel_release(chan);
1381 
1382  return res;
1383 }
1384 #endif
1385 
1386 static struct ast_cli_entry vars_cli[] = {
1387  AST_CLI_DEFINE(handle_show_globals, "Show global dialplan variables"),
1388  AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"),
1389  AST_CLI_DEFINE(handle_eval_function, "Evaluate dialplan function"),
1390  AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable"),
1391  AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"),
1392 };
1393 
1394 static void unload_pbx_variables(void)
1395 {
1396  ast_cli_unregister_multiple(vars_cli, ARRAY_LEN(vars_cli));
1399  pbx_builtin_clear_globals();
1400  AST_TEST_UNREGISTER(test_variable_substrings);
1401 }
1402 
1404 {
1405  int res = 0;
1406 
1407  res |= ast_cli_register_multiple(vars_cli, ARRAY_LEN(vars_cli));
1408  res |= ast_register_application2("Set", pbx_builtin_setvar, NULL, NULL, NULL);
1409  res |= ast_register_application2("MSet", pbx_builtin_setvar_multiple, NULL, NULL, NULL);
1410  ast_register_cleanup(unload_pbx_variables);
1411  AST_TEST_REGISTER(test_variable_substrings);
1412 
1413  return res;
1414 }
static char * substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
takes a substring. It is ok to call with value == workspace.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
Set caller ID number, name and ANI and generate AMI event.
Definition: channel.c:7334
int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
executes a read operation on a function
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
Private include file for pbx.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: utils.c:2839
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, without removing any previously set value...
int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
executes a read operation on a function
Channel Variables.
char context[AST_MAX_CONTEXT]
struct ast_channel * ast_channel_release(struct ast_channel *chan)
Unlink and release reference to a channel.
Definition: channel.c:1584
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
extract offset:length from variable name.
descriptor for a cli entry.
Definition: cli.h:171
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, struct varshead *headp, const char *templ, size_t *used)
int ast_party_id_presentation(const struct ast_party_id *id)
Determine the overall presentation value for the given party.
Definition: channel.c:1821
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
Test Framework API.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, 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 ast_str_get_hint(struct ast_str **hint, ssize_t hintsize, struct ast_str **name, ssize_t namesize, struct ast_channel *c, const char *context, const char *exten)
If an extension hint exists, return non-zero.
Definition: pbx.c:4155
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
Create a human-readable string, specifying all variables and their corresponding values.
void ast_str_substitute_variables_full2(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used, int use_both)
Perform variable/function/expression substitution on an ast_str.
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:786
Number structure.
Definition: app_followme.c:154
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
Asterisk file paths, configured in asterisk.conf.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1282
char * ast_str_append_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string.
Definition: strings.h:1062
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
A set of macros to manage forward-linked lists.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
Core PBX routines and definitions.
static char * handle_show_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI support for listing chanvar's variables in a parseable way.
int load_pbx_variables(void)
Support for dynamic strings.
Definition: strings.h:623
void ast_channel_publish_varset(struct ast_channel *chan, const char *variable, const char *value)
Publish a ast_channel_publish_varset for a channel.
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
char * command
Definition: cli.h:186
int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *vdata)
Parse and set multiple channel variables, where the pairs are separated by the ',' character...
Prototypes for public functions only of internal interest,.
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
Definition: linkedlists.h:252
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 * usage
Definition: cli.h:177
int ast_str_expr(struct ast_str **str, ssize_t maxlen, struct ast_channel *chan, char *expr)
Evaluate the given expression.
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:703
#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
char * ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
Command completion for the list of active channels.
Definition: main/cli.c:1865
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
int ast_register_application2(const char *app, int(*execute)(struct ast_channel *, const char *), const char *synopsis, const char *description, void *mod)
Register an application.
Definition: pbx_app.c:103
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
const char * ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
char * ast_str_set_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Set a dynamic string to a non-NULL terminated substring.
Definition: strings.h:1055
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
Support for Asterisk built-in variables in the dialplan.
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
Asterisk module definitions.
static char * handle_eval_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI support for executing function.
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
executes a write operation on a function
static char * substituted(struct ast_channel *channel, const char *string)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
Evaluate the given expression.
static char * handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI support for listing global variables in a parseable way.
char exten[AST_MAX_EXTENSION]
int pbx_builtin_setvar(struct ast_channel *chan, const char *data)
Parse and set a single channel variable, where the name and value are separated with an '=' character...
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
#define AST_APP_ARG(name)
Define an application argument.