Asterisk - The Open Source Telephony Project  21.4.1
app_playback.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.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 Trivial application to playback a sound file
22  *
23  * \author Mark Spencer <markster@digium.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/file.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/module.h"
37 #include "asterisk/app.h"
38 /* This file provides config-file based 'say' functions, and implements
39  * some CLI commands.
40  */
41 #include "asterisk/say.h" /*!< provides config-file based 'say' functions */
42 #include "asterisk/cli.h"
43 
44 /*** DOCUMENTATION
45  <application name="Playback" language="en_US">
46  <synopsis>
47  Play a file.
48  </synopsis>
49  <syntax>
50  <parameter name="filenames" required="true" argsep="&amp;">
51  <para>Ampersand separated list of filenames. If the filename
52  is a relative filename (it does not begin with a slash), it
53  will be searched for in the Asterisk sounds directory. If the
54  filename is able to be parsed as a URL, Asterisk will
55  download the file and then begin playback on it. To include a
56  literal <literal>&amp;</literal> in the URL you can enclose
57  the URL in single quotes.</para>
58  <argument name="filename" required="true" />
59  <argument name="filename2" multiple="true" />
60  </parameter>
61  <parameter name="options">
62  <para>Comma separated list of options</para>
63  <optionlist>
64  <option name="skip">
65  <para>Do not play if not answered</para>
66  </option>
67  <option name="noanswer">
68  <para>Playback without answering, otherwise the channel will
69  be answered before the sound is played.</para>
70  <note><para>Not all channel types support playing messages while still on hook.</para></note>
71  </option>
72  <option name="say">
73  <para>Play using the say.conf file.</para>
74  </option>
75  <option name="mix">
76  <para>Play using a mix of filename and the say.conf file.</para>
77  </option>
78  </optionlist>
79  </parameter>
80  </syntax>
81  <description>
82  <para>Plays back given filenames (do not put extension of wav/alaw etc).
83  The Playback application answers the channel if no options are specified.
84  If the file is non-existent it will fail.</para>
85  <para>This application sets the following channel variable upon completion:</para>
86  <variablelist>
87  <variable name="PLAYBACKSTATUS">
88  <para>The status of the playback attempt as a text string.</para>
89  <value name="SUCCESS"/>
90  <value name="FAILED"/>
91  </variable>
92  </variablelist>
93  <para>See Also: Background (application) -- for playing sound files that are interruptible</para>
94  <para>WaitExten (application) -- wait for digits from caller, optionally play music on hold</para>
95  </description>
96  <see-also>
97  <ref type="application">Background</ref>
98  <ref type="application">WaitExten</ref>
99  <ref type="application">ControlPlayback</ref>
100  <ref type="agi">stream file</ref>
101  <ref type="agi">control stream file</ref>
102  <ref type="manager">ControlPlayback</ref>
103  </see-also>
104  </application>
105  ***/
106 
107 static char *app = "Playback";
108 
109 static struct ast_config *say_cfg = NULL;
110 
111 /*! \brief save the say' api calls.
112  * The first entry is NULL if we have the standard source,
113  * otherwise we are sourcing from here.
114  * 'say load [new|old]' will enable the new or old method, or report status
115  */
116 static const void *say_api_buf[40];
117 static const char * const say_old = "old";
118 static const char * const say_new = "new";
119 
120 static void save_say_mode(const void *arg)
121 {
122  int i = 0;
123  say_api_buf[i++] = arg;
124 
128  say_api_buf[i++] = ast_say_character_str_full;
129  say_api_buf[i++] = ast_say_phonetic_str_full;
130  say_api_buf[i++] = ast_say_datetime;
131  say_api_buf[i++] = ast_say_time;
132  say_api_buf[i++] = ast_say_date;
133  say_api_buf[i++] = ast_say_datetime_from_now;
134  say_api_buf[i++] = ast_say_date_with_format;
135 }
136 
137 static void restore_say_mode(void *arg)
138 {
139  int i = 0;
140  say_api_buf[i++] = arg;
141 
145  ast_say_character_str_full = say_api_buf[i++];
146  ast_say_phonetic_str_full = say_api_buf[i++];
147  ast_say_datetime = say_api_buf[i++];
148  ast_say_time = say_api_buf[i++];
149  ast_say_date = say_api_buf[i++];
150  ast_say_datetime_from_now = say_api_buf[i++];
151  ast_say_date_with_format = say_api_buf[i++];
152 }
153 
154 /*! \brief
155  * Typical 'say' arguments in addition to the date or number or string
156  * to say. We do not include 'options' because they may be different
157  * in recursive calls, and so they are better left as an external
158  * parameter.
159  */
160 typedef struct {
161  struct ast_channel *chan;
162  const char *ints;
163  const char *language;
164  int audiofd;
165  int ctrlfd;
166 } say_args_t;
167 
168 static int s_streamwait3(const say_args_t *a, const char *fn)
169 {
170  int res = ast_streamfile(a->chan, fn, a->language);
171  if (res) {
172  ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
173  return res;
174  }
175  res = (a->audiofd > -1 && a->ctrlfd > -1) ?
176  ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
177  ast_waitstream(a->chan, a->ints);
178  ast_stopstream(a->chan);
179  return res;
180 }
181 
182 /*! \brief
183  * the string is 'prefix:data' or prefix:fmt:data'
184  * with ':' being invalid in strings.
185  */
186 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
187 {
188  struct ast_variable *v;
189  char *lang;
190  char *x;
191  char *rule = NULL;
192  char *rule_head = NULL;
193  int ret = 0;
194  struct varshead head = { .first = NULL, .last = NULL };
195  struct ast_var_t *n;
196 
197  ast_debug(2, "string <%s> depth <%d>\n", s, depth);
198  if (depth++ > 10) {
199  ast_log(LOG_WARNING, "recursion too deep, exiting\n");
200  return -1;
201  } else if (!say_cfg) {
202  ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
203  return -1;
204  }
205 
206  /* scan languages same as in file.c */
207  if (a->language == NULL)
208  a->language = "en"; /* default */
209  ast_debug(2, "try <%s> in <%s>\n", s, a->language);
210  lang = ast_strdupa(a->language);
211  for (;;) {
212  for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
213  if (ast_extension_match(v->name, s)) {
214  rule_head = rule = ast_strdup(v->value);
215  break;
216  }
217  }
218  if (rule)
219  break;
220  if ( (x = strchr(lang, '_')) )
221  *x = '\0'; /* try without suffix */
222  else if (strcmp(lang, "en"))
223  lang = "en"; /* last resort, try 'en' if not done yet */
224  else
225  break;
226  }
227  if (!rule)
228  return 0;
229 
230  /* skip up to two prefixes to get the value */
231  if ( (x = strchr(s, ':')) )
232  s = x + 1;
233  if ( (x = strchr(s, ':')) )
234  s = x + 1;
235  ast_debug(2, "value is <%s>\n", s);
236  n = ast_var_assign("SAY", s);
237  if (!n) {
238  ast_log(LOG_ERROR, "Memory allocation error in do_say\n");
239  ast_free(rule_head);
240  return -1;
241  }
242  AST_LIST_INSERT_HEAD(&head, n, entries);
243 
244  /* scan the body, one piece at a time */
245  while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
246  char fn[128];
247  const char *p, *fmt, *data; /* format and data pointers */
248 
249  /* prepare a decent file name */
250  x = ast_skip_blanks(x);
251  ast_trim_blanks(x);
252 
253  /* replace variables */
254  pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
255  ast_debug(2, "doing [%s]\n", fn);
256 
257  /* locate prefix and data, if any */
258  fmt = strchr(fn, ':');
259  if (!fmt || fmt == fn) { /* regular filename */
260  ret = s_streamwait3(a, fn);
261  continue;
262  }
263  fmt++;
264  data = strchr(fmt, ':'); /* colon before data */
265  if (!data || data == fmt) { /* simple prefix-fmt */
266  ret = do_say(a, fn, options, depth);
267  continue;
268  }
269  /* prefix:fmt:data */
270  for (p = fmt; p < data && ret <= 0; p++) {
271  char fn2[sizeof(fn)];
272  if (*p == ' ' || *p == '\t') /* skip blanks */
273  continue;
274  if (*p == '\'') {/* file name - we trim them */
275  char *y;
276  strcpy(fn2, ast_skip_blanks(p+1)); /* make a full copy */
277  y = strchr(fn2, '\'');
278  if (!y) {
279  p = data; /* invalid. prepare to end */
280  break;
281  }
282  *y = '\0';
283  ast_trim_blanks(fn2);
284  p = strchr(p+1, '\'');
285  ret = s_streamwait3(a, fn2);
286  } else {
287  int l = fmt-fn;
288  strcpy(fn2, fn); /* copy everything */
289  /* after prefix, append the format */
290  fn2[l++] = *p;
291  strcpy(fn2 + l, data);
292  ret = do_say(a, fn2, options, depth);
293  }
294 
295  if (ret) {
296  break;
297  }
298  }
299  }
300  ast_var_delete(n);
301  ast_free(rule_head);
302  return ret;
303 }
304 
305 static int say_full(struct ast_channel *chan, const char *string,
306  const char *ints, const char *lang, const char *options,
307  int audiofd, int ctrlfd)
308 {
309  say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
310  return do_say(&a, string, options, 0);
311 }
312 
313 static int say_number_full(struct ast_channel *chan, int num,
314  const char *ints, const char *lang, const char *options,
315  int audiofd, int ctrlfd)
316 {
317  char buf[64];
318  say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
319  snprintf(buf, sizeof(buf), "num:%d", num);
320  return do_say(&a, buf, options, 0);
321 }
322 
323 static int say_enumeration_full(struct ast_channel *chan, int num,
324  const char *ints, const char *lang, const char *options,
325  int audiofd, int ctrlfd)
326 {
327  char buf[64];
328  say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
329  snprintf(buf, sizeof(buf), "enum:%d", num);
330  return do_say(&a, buf, options, 0);
331 }
332 
333 static int say_date_generic(struct ast_channel *chan, time_t t,
334  const char *ints, const char *lang, const char *format, const char *timezonename, const char *prefix)
335 {
336  char buf[128];
337  struct ast_tm tm;
338  struct timeval when = { t, 0 };
339  say_args_t a = { chan, ints, lang, -1, -1 };
340  if (format == NULL)
341  format = "";
342 
343  ast_localtime(&when, &tm, timezonename);
344  snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
345  prefix,
346  format,
347  tm.tm_year+1900,
348  tm.tm_mon+1,
349  tm.tm_mday,
350  tm.tm_hour,
351  tm.tm_min,
352  tm.tm_sec,
353  tm.tm_wday,
354  tm.tm_yday);
355  return do_say(&a, buf, NULL, 0);
356 }
357 
358 static int say_date_with_format(struct ast_channel *chan, time_t t,
359  const char *ints, const char *lang, const char *format, const char *timezonename)
360 {
361  return say_date_generic(chan, t, ints, lang, format, timezonename, "datetime");
362 }
363 
364 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
365 {
366  return say_date_generic(chan, t, ints, lang, "", NULL, "date");
367 }
368 
369 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
370 {
371  return say_date_generic(chan, t, ints, lang, "", NULL, "time");
372 }
373 
374 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
375 {
376  return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
377 }
378 
379 /*! \brief
380  * remap the 'say' functions to use those in this file
381  */
382 static int say_init_mode(const char *mode) {
383  if (!strcmp(mode, say_new)) {
384  if (say_cfg == NULL) {
385  ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
386  return -1;
387  }
388  save_say_mode(say_new);
390 
392 #if 0
393  /*! \todo XXX
394  These functions doesn't exist.
395  say.conf.sample indicates this is working...
396  */
397  ast_say_digits_full = say_digits_full;
398  ast_say_digit_str_full = say_digit_str_full;
399  ast_say_character_str_full = say_character_str_full;
400  ast_say_phonetic_str_full = say_phonetic_str_full;
401  ast_say_datetime_from_now = say_datetime_from_now;
402 #endif
403  ast_say_datetime = say_datetime;
404  ast_say_time = say_time;
405  ast_say_date = say_date;
406  ast_say_date_with_format = say_date_with_format;
407  } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
408  restore_say_mode(NULL);
409  } else if (strcmp(mode, say_old)) {
410  ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
411  return -1;
412  }
413 
414  return 0;
415 }
416 
417 static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
418 {
419  const char *old_mode = say_api_buf[0] ? say_new : say_old;
420  const char *mode;
421  switch (cmd) {
422  case CLI_INIT:
423  e->command = "say load [new|old]";
424  e->usage =
425  "Usage: say load [new|old]\n"
426  " say load\n"
427  " Report status of current say mode\n"
428  " say load new\n"
429  " Set say method, configured in say.conf\n"
430  " say load old\n"
431  " Set old say method, coded in asterisk core\n";
432  return NULL;
433  case CLI_GENERATE:
434  return NULL;
435  }
436  if (a->argc == 2) {
437  ast_cli(a->fd, "say mode is [%s]\n", old_mode);
438  return CLI_SUCCESS;
439  } else if (a->argc != e->args)
440  return CLI_SHOWUSAGE;
441  mode = a->argv[2];
442  if (!strcmp(mode, old_mode))
443  ast_cli(a->fd, "say mode is %s already\n", mode);
444  else
445  if (say_init_mode(mode) == 0)
446  ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
447 
448  return CLI_SUCCESS;
449 }
450 
451 static struct ast_cli_entry cli_playback[] = {
452  AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
453 };
454 
455 static int playback_exec(struct ast_channel *chan, const char *data)
456 {
457  int res = 0;
458  int mres = 0;
459  char *tmp;
460  int option_skip=0;
461  int option_say=0;
462  int option_mix=0;
463  int option_noanswer = 0;
464 
466  AST_APP_ARG(filenames);
467  AST_APP_ARG(options);
468  );
469 
470  if (ast_strlen_zero(data)) {
471  ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
472  return -1;
473  }
474 
475  tmp = ast_strdupa(data);
477 
478  if (args.options) {
479  if (strcasestr(args.options, "skip"))
480  option_skip = 1;
481  if (strcasestr(args.options, "say"))
482  option_say = 1;
483  if (strcasestr(args.options, "mix"))
484  option_mix = 1;
485  if (strcasestr(args.options, "noanswer"))
486  option_noanswer = 1;
487  }
488  if (ast_channel_state(chan) != AST_STATE_UP) {
489  if (option_skip) {
490  /* At the user's option, skip if the line is not up */
491  goto done;
492  } else if (!option_noanswer) {
493  /* Otherwise answer unless we're supposed to send this while on-hook */
494  res = ast_answer(chan);
495  }
496  }
497  if (!res) {
498  char *back = args.filenames;
499  char *front;
500 
501  ast_stopstream(chan);
502  while (!res && (front = ast_strsep(&back, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
503  if (option_say)
504  res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
505  else if (option_mix){
506  /* Check if it is in say format but not remote audio file */
507  if (strcasestr(front, ":") && !strcasestr(front, "://"))
508  res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
509  else
510  res = ast_streamfile(chan, front, ast_channel_language(chan));
511  }
512  else
513  res = ast_streamfile(chan, front, ast_channel_language(chan));
514  if (!res) {
515  res = ast_waitstream(chan, "");
516  ast_stopstream(chan);
517  } else {
518  if (!ast_check_hangup(chan)) {
519  ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
520  }
521  res = 0;
522  mres = 1;
523  }
524  }
525  }
526 done:
527  pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
528  return res;
529 }
530 
531 static int reload(void)
532 {
533  struct ast_variable *v;
534  struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
535  struct ast_config *newcfg;
536 
537  if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
538  return 0;
539  } else if (newcfg == CONFIG_STATUS_FILEINVALID) {
540  ast_log(LOG_ERROR, "Config file say.conf is in an invalid format. Aborting.\n");
541  return 0;
542  }
543 
544  if (say_cfg) {
545  ast_config_destroy(say_cfg);
546  ast_log(LOG_NOTICE, "Reloading say.conf\n");
547  }
548  say_cfg = newcfg;
549 
550  if (say_cfg) {
551  for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
552  if (ast_extension_match(v->name, "mode")) {
553  say_init_mode(v->value);
554  break;
555  }
556  }
557  }
558 
559  /*! \todo
560  * XXX here we should sort rules according to the same order
561  * we have in pbx.c so we have the same matching behaviour.
562  */
563  return 0;
564 }
565 
566 static int unload_module(void)
567 {
568  int res;
569 
571 
572  ast_cli_unregister_multiple(cli_playback, ARRAY_LEN(cli_playback));
573 
574  if (say_cfg)
575  ast_config_destroy(say_cfg);
576 
577  return res;
578 }
579 
580 static int load_module(void)
581 {
582  struct ast_variable *v;
583  struct ast_flags config_flags = { 0 };
584 
585  say_cfg = ast_config_load("say.conf", config_flags);
586  if (say_cfg && say_cfg != CONFIG_STATUS_FILEINVALID) {
587  for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
588  if (ast_extension_match(v->name, "mode")) {
589  say_init_mode(v->value);
590  break;
591  }
592  }
593  }
594 
595  ast_cli_register_multiple(cli_playback, ARRAY_LEN(cli_playback));
596  return ast_register_application_xml(app, playback_exec);
597 }
598 
599 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
600  .support_level = AST_MODULE_SUPPORT_CORE,
601  .load = load_module,
602  .unload = unload_module,
603  .reload = reload,
604 );
struct ast_variable * next
Main Channel structure associated with a channel.
SAY_EXTERN int(* ast_say_number_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_number_full)
Same as ast_say_number() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: say.h:86
static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full: call language-specific functions
Definition: say.c:874
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
Asterisk main include file. File version handling, generic pbx functions.
SAY_EXTERN int(* ast_say_enumeration_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_enumeration_full)
Same as ast_say_enumeration() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: say.h:125
static int do_say(say_args_t *a, const char *s, const char *options, int depth)
the string is 'prefix:data' or prefix:fmt:data' with ':' being invalid in strings.
Definition: app_playback.c:186
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
descriptor for a cli entry.
Definition: cli.h:171
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_say_digits_full(struct ast_channel *chan, int num, const char *ints, const char *lang, int audiofd, int ctrlfd)
Same as ast_say_digits() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: channel.c:8283
Structure for variables, used for configurations and for channel variables.
ast_channel_state
ast_channel states
Definition: channelstate.h:35
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
int args
This gets set in ast_cli_register()
Definition: cli.h:185
static int reload(void)
Definition: app_playback.c:531
#define ast_config_load(filename, flags)
Load a config file.
SAY_EXTERN int(* ast_say_digit_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_digit_str_full)
Same as ast_say_digit_str() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: say.h:162
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_enumeration_full: call language-specific functions
Definition: say.c:3229
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: utils.c:1835
#define ast_debug(level,...)
Log a DEBUG message.
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition: strings.h:186
Typical 'say' arguments in addition to the date or number or string to say. We do not include 'option...
Definition: app_playback.c:160
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
static const void * say_api_buf[40]
save the say' api calls. The first entry is NULL if we have the standard source, otherwise we are sou...
Definition: app_playback.c:116
char * command
Definition: cli.h:186
static char * app
Definition: app_playback.c:107
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
Structure used to handle boolean flags.
Definition: utils.h:199
const char * usage
Definition: cli.h:177
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_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int monfd)
Definition: file.c:1849
Standard Command Line Interface.
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static int say_init_mode(const char *mode)
remap the 'say' functions to use those in this file
Definition: app_playback.c:382
#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...
const ast_string_field language
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define AST_APP_ARG(name)
Define an application argument.