2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Trivial application to playback a sound file
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
29 <support_level>core</support_level>
34 ASTERISK_REGISTER_FILE()
36 #include "asterisk/file.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/module.h"
39 #include "asterisk/app.h"
40 /* This file provides config-file based 'say' functions, and implenents
43 #include "asterisk/say.h" /*!< provides config-file based 'say' functions */
44 #include "asterisk/cli.h"
47 <application name="Playback" language="en_US">
52 <parameter name="filenames" required="true" argsep="&">
53 <argument name="filename" required="true" />
54 <argument name="filename2" multiple="true" />
56 <parameter name="options">
57 <para>Comma separated list of options</para>
60 <para>Do not play if not answered</para>
62 <option name="noanswer">
63 <para>Playback without answering, otherwise the channel will
64 be answered before the sound is played.</para>
65 <note><para>Not all channel types support playing messages while still on hook.</para></note>
71 <para>Plays back given filenames (do not put extension of wav/alaw etc).
72 The playback command answer the channel if no options are specified.
73 If the file is non-existant it will fail</para>
74 <para>This application sets the following channel variable upon completion:</para>
76 <variable name="PLAYBACKSTATUS">
77 <para>The status of the playback attempt as a text string.</para>
78 <value name="SUCCESS"/>
79 <value name="FAILED"/>
82 <para>See Also: Background (application) -- for playing sound files that are interruptible</para>
83 <para>WaitExten (application) -- wait for digits from caller, optionally play music on hold</para>
86 <ref type="application">Background</ref>
87 <ref type="application">WaitExten</ref>
88 <ref type="application">ControlPlayback</ref>
89 <ref type="agi">stream file</ref>
90 <ref type="agi">control stream file</ref>
91 <ref type="manager">ControlPlayback</ref>
96 static char *app = "Playback";
98 static struct ast_config *say_cfg = NULL;
100 /*! \brief save the say' api calls.
101 * The first entry is NULL if we have the standard source,
102 * otherwise we are sourcing from here.
103 * 'say load [new|old]' will enable the new or old method, or report status
105 static const void *say_api_buf[40];
106 static const char * const say_old = "old";
107 static const char * const say_new = "new";
109 static void save_say_mode(const void *arg)
112 say_api_buf[i++] = arg;
114 say_api_buf[i++] = ast_say_number_full;
115 say_api_buf[i++] = ast_say_enumeration_full;
116 say_api_buf[i++] = ast_say_digit_str_full;
117 say_api_buf[i++] = ast_say_character_str_full;
118 say_api_buf[i++] = ast_say_phonetic_str_full;
119 say_api_buf[i++] = ast_say_datetime;
120 say_api_buf[i++] = ast_say_time;
121 say_api_buf[i++] = ast_say_date;
122 say_api_buf[i++] = ast_say_datetime_from_now;
123 say_api_buf[i++] = ast_say_date_with_format;
126 static void restore_say_mode(void *arg)
129 say_api_buf[i++] = arg;
131 ast_say_number_full = say_api_buf[i++];
132 ast_say_enumeration_full = say_api_buf[i++];
133 ast_say_digit_str_full = say_api_buf[i++];
134 ast_say_character_str_full = say_api_buf[i++];
135 ast_say_phonetic_str_full = say_api_buf[i++];
136 ast_say_datetime = say_api_buf[i++];
137 ast_say_time = say_api_buf[i++];
138 ast_say_date = say_api_buf[i++];
139 ast_say_datetime_from_now = say_api_buf[i++];
140 ast_say_date_with_format = say_api_buf[i++];
144 * Typical 'say' arguments in addition to the date or number or string
145 * to say. We do not include 'options' because they may be different
146 * in recursive calls, and so they are better left as an external
150 struct ast_channel *chan;
152 const char *language;
157 static int s_streamwait3(const say_args_t *a, const char *fn)
159 int res = ast_streamfile(a->chan, fn, a->language);
161 ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
164 res = (a->audiofd > -1 && a->ctrlfd > -1) ?
165 ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
166 ast_waitstream(a->chan, a->ints);
167 ast_stopstream(a->chan);
172 * the string is 'prefix:data' or prefix:fmt:data'
173 * with ':' being invalid in strings.
175 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
177 struct ast_variable *v;
178 char *lang, *x, *rule = NULL;
180 struct varshead head = { .first = NULL, .last = NULL };
183 ast_debug(2, "string <%s> depth <%d>\n", s, depth);
185 ast_log(LOG_WARNING, "recursion too deep, exiting\n");
187 } else if (!say_cfg) {
188 ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
192 /* scan languages same as in file.c */
193 if (a->language == NULL)
194 a->language = "en"; /* default */
195 ast_debug(2, "try <%s> in <%s>\n", s, a->language);
196 lang = ast_strdupa(a->language);
198 for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
199 if (ast_extension_match(v->name, s)) {
200 rule = ast_strdupa(v->value);
206 if ( (x = strchr(lang, '_')) )
207 *x = '\0'; /* try without suffix */
208 else if (strcmp(lang, "en"))
209 lang = "en"; /* last resort, try 'en' if not done yet */
216 /* skip up to two prefixes to get the value */
217 if ( (x = strchr(s, ':')) )
219 if ( (x = strchr(s, ':')) )
221 ast_debug(2, "value is <%s>\n", s);
222 n = ast_var_assign("SAY", s);
224 ast_log(LOG_ERROR, "Memory allocation error in do_say\n");
227 AST_LIST_INSERT_HEAD(&head, n, entries);
229 /* scan the body, one piece at a time */
230 while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
232 const char *p, *fmt, *data; /* format and data pointers */
234 /* prepare a decent file name */
235 x = ast_skip_blanks(x);
238 /* replace variables */
239 pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
240 ast_debug(2, "doing [%s]\n", fn);
242 /* locate prefix and data, if any */
243 fmt = strchr(fn, ':');
244 if (!fmt || fmt == fn) { /* regular filename */
245 ret = s_streamwait3(a, fn);
249 data = strchr(fmt, ':'); /* colon before data */
250 if (!data || data == fmt) { /* simple prefix-fmt */
251 ret = do_say(a, fn, options, depth);
254 /* prefix:fmt:data */
255 for (p = fmt; p < data && ret <= 0; p++) {
256 char fn2[sizeof(fn)];
257 if (*p == ' ' || *p == '\t') /* skip blanks */
259 if (*p == '\'') {/* file name - we trim them */
261 strcpy(fn2, ast_skip_blanks(p+1)); /* make a full copy */
262 y = strchr(fn2, '\'');
264 p = data; /* invalid. prepare to end */
268 ast_trim_blanks(fn2);
269 p = strchr(p+1, '\'');
270 ret = s_streamwait3(a, fn2);
273 strcpy(fn2, fn); /* copy everything */
274 /* after prefix, append the format */
276 strcpy(fn2 + l, data);
277 ret = do_say(a, fn2, options, depth);
289 static int say_full(struct ast_channel *chan, const char *string,
290 const char *ints, const char *lang, const char *options,
291 int audiofd, int ctrlfd)
293 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
294 return do_say(&a, string, options, 0);
297 static int say_number_full(struct ast_channel *chan, int num,
298 const char *ints, const char *lang, const char *options,
299 int audiofd, int ctrlfd)
302 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
303 snprintf(buf, sizeof(buf), "num:%d", num);
304 return do_say(&a, buf, options, 0);
307 static int say_enumeration_full(struct ast_channel *chan, int num,
308 const char *ints, const char *lang, const char *options,
309 int audiofd, int ctrlfd)
312 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
313 snprintf(buf, sizeof(buf), "enum:%d", num);
314 return do_say(&a, buf, options, 0);
317 static int say_date_generic(struct ast_channel *chan, time_t t,
318 const char *ints, const char *lang, const char *format, const char *timezonename, const char *prefix)
322 struct timeval when = { t, 0 };
323 say_args_t a = { chan, ints, lang, -1, -1 };
327 ast_localtime(&when, &tm, NULL);
328 snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
339 return do_say(&a, buf, NULL, 0);
342 static int say_date_with_format(struct ast_channel *chan, time_t t,
343 const char *ints, const char *lang, const char *format, const char *timezonename)
345 return say_date_generic(chan, t, ints, lang, format, timezonename, "datetime");
348 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
350 return say_date_generic(chan, t, ints, lang, "", NULL, "date");
353 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
355 return say_date_generic(chan, t, ints, lang, "", NULL, "time");
358 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
360 return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
364 * remap the 'say' functions to use those in this file
366 static int say_init_mode(const char *mode) {
367 if (!strcmp(mode, say_new)) {
368 if (say_cfg == NULL) {
369 ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
372 save_say_mode(say_new);
373 ast_say_number_full = say_number_full;
375 ast_say_enumeration_full = say_enumeration_full;
378 These functions doesn't exist.
379 say.conf.sample indicates this is working...
381 ast_say_digits_full = say_digits_full;
382 ast_say_digit_str_full = say_digit_str_full;
383 ast_say_character_str_full = say_character_str_full;
384 ast_say_phonetic_str_full = say_phonetic_str_full;
385 ast_say_datetime_from_now = say_datetime_from_now;
387 ast_say_datetime = say_datetime;
388 ast_say_time = say_time;
389 ast_say_date = say_date;
390 ast_say_date_with_format = say_date_with_format;
391 } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
392 restore_say_mode(NULL);
393 } else if (strcmp(mode, say_old)) {
394 ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
401 static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
403 const char *old_mode = say_api_buf[0] ? say_new : say_old;
407 e->command = "say load [new|old]";
409 "Usage: say load [new|old]\n"
411 " Report status of current say mode\n"
413 " Set say method, configured in say.conf\n"
415 " Set old say method, coded in asterisk core\n";
421 ast_cli(a->fd, "say mode is [%s]\n", old_mode);
423 } else if (a->argc != e->args)
424 return CLI_SHOWUSAGE;
426 if (!strcmp(mode, old_mode))
427 ast_cli(a->fd, "say mode is %s already\n", mode);
429 if (say_init_mode(mode) == 0)
430 ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
435 static struct ast_cli_entry cli_playback[] = {
436 AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
439 static int playback_exec(struct ast_channel *chan, const char *data)
446 int option_noanswer = 0;
448 AST_DECLARE_APP_ARGS(args,
449 AST_APP_ARG(filenames);
450 AST_APP_ARG(options);
453 if (ast_strlen_zero(data)) {
454 ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
458 tmp = ast_strdupa(data);
459 AST_STANDARD_APP_ARGS(args, tmp);
462 if (strcasestr(args.options, "skip"))
464 if (strcasestr(args.options, "say"))
466 if (strcasestr(args.options, "noanswer"))
469 if (ast_channel_state(chan) != AST_STATE_UP) {
471 /* At the user's option, skip if the line is not up */
473 } else if (!option_noanswer) {
474 /* Otherwise answer unless we're supposed to send this while on-hook */
475 res = ast_answer(chan);
479 char *back = args.filenames;
482 ast_stopstream(chan);
483 while (!res && (front = strsep(&back, "&"))) {
485 res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
487 res = ast_streamfile(chan, front, ast_channel_language(chan));
489 res = ast_waitstream(chan, "");
490 ast_stopstream(chan);
493 if (!ast_check_hangup(chan)) {
494 ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
502 pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
506 static int reload(void)
508 struct ast_variable *v;
509 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
510 struct ast_config *newcfg;
512 if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
514 } else if (newcfg == CONFIG_STATUS_FILEINVALID) {
515 ast_log(LOG_ERROR, "Config file say.conf is in an invalid format. Aborting.\n");
520 ast_config_destroy(say_cfg);
521 ast_log(LOG_NOTICE, "Reloading say.conf\n");
526 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
527 if (ast_extension_match(v->name, "mode")) {
528 say_init_mode(v->value);
535 * XXX here we should sort rules according to the same order
536 * we have in pbx.c so we have the same matching behaviour.
541 static int unload_module(void)
545 res = ast_unregister_application(app);
547 ast_cli_unregister_multiple(cli_playback, ARRAY_LEN(cli_playback));
550 ast_config_destroy(say_cfg);
555 static int load_module(void)
557 struct ast_variable *v;
558 struct ast_flags config_flags = { 0 };
560 say_cfg = ast_config_load("say.conf", config_flags);
561 if (say_cfg && say_cfg != CONFIG_STATUS_FILEINVALID) {
562 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
563 if (ast_extension_match(v->name, "mode")) {
564 say_init_mode(v->value);
570 ast_cli_register_multiple(cli_playback, ARRAY_LEN(cli_playback));
571 return ast_register_application_xml(app, playback_exec);
574 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
575 .support_level = AST_MODULE_SUPPORT_CORE,
577 .unload = unload_module,