Add a function, CHANNELS(), which retrieves a list of all active channels.
[asterisk-bristuff.git] / apps / app_playback.c
blob561600ad474e7e2e1f4ab83e4114516a56c6cffc
1 /*
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.
19 /*! \file
21 * \brief Trivial application to playback a sound file
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include "asterisk/file.h"
33 #include "asterisk/pbx.h"
34 #include "asterisk/module.h"
35 #include "asterisk/app.h"
36 /* This file provides config-file based 'say' functions, and implenents
37 * some CLI commands.
39 #include "asterisk/say.h" /* provides config-file based 'say' functions */
40 #include "asterisk/cli.h"
42 static char *app = "Playback";
44 static char *synopsis = "Play a file";
46 static char *descrip =
47 " Playback(filename[&filename2...][,option]): Plays back given filenames (do not put\n"
48 "extension). Options may also be included following a comma.\n"
49 "The 'skip' option causes the playback of the message to be skipped if the channel\n"
50 "is not in the 'up' state (i.e. it hasn't been answered yet). If 'skip' is \n"
51 "specified, the application will return immediately should the channel not be\n"
52 "off hook. Otherwise, unless 'noanswer' is specified, the channel will\n"
53 "be answered before the sound is played. Not all channels support playing\n"
54 "messages while still on hook.\n"
55 "This application sets the following channel variable upon completion:\n"
56 " PLAYBACKSTATUS The status of the playback attempt as a text string, one of\n"
57 " SUCCESS | FAILED\n"
58 "See Also: Background (application) -- for playing soundfiles that are interruptible\n"
59 " WaitExten (application) -- wait for digits from caller, optionally play music on hold\n"
63 static struct ast_config *say_cfg = NULL;
64 /* save the say' api calls.
65 * The first entry is NULL if we have the standard source,
66 * otherwise we are sourcing from here.
67 * 'say load [new|old]' will enable the new or old method, or report status
69 static const void *say_api_buf[40];
70 static const char *say_old = "old";
71 static const char *say_new = "new";
73 static void save_say_mode(const void *arg)
75 int i = 0;
76 say_api_buf[i++] = arg;
78 say_api_buf[i++] = ast_say_number_full;
79 say_api_buf[i++] = ast_say_enumeration_full;
80 say_api_buf[i++] = ast_say_digit_str_full;
81 say_api_buf[i++] = ast_say_character_str_full;
82 say_api_buf[i++] = ast_say_phonetic_str_full;
83 say_api_buf[i++] = ast_say_datetime;
84 say_api_buf[i++] = ast_say_time;
85 say_api_buf[i++] = ast_say_date;
86 say_api_buf[i++] = ast_say_datetime_from_now;
87 say_api_buf[i++] = ast_say_date_with_format;
90 static void restore_say_mode(void *arg)
92 int i = 0;
93 say_api_buf[i++] = arg;
95 ast_say_number_full = say_api_buf[i++];
96 ast_say_enumeration_full = say_api_buf[i++];
97 ast_say_digit_str_full = say_api_buf[i++];
98 ast_say_character_str_full = say_api_buf[i++];
99 ast_say_phonetic_str_full = say_api_buf[i++];
100 ast_say_datetime = say_api_buf[i++];
101 ast_say_time = say_api_buf[i++];
102 ast_say_date = say_api_buf[i++];
103 ast_say_datetime_from_now = say_api_buf[i++];
104 ast_say_date_with_format = say_api_buf[i++];
108 * Typical 'say' arguments in addition to the date or number or string
109 * to say. We do not include 'options' because they may be different
110 * in recursive calls, and so they are better left as an external
111 * parameter.
113 typedef struct {
114 struct ast_channel *chan;
115 const char *ints;
116 const char *language;
117 int audiofd;
118 int ctrlfd;
119 } say_args_t;
121 static int s_streamwait3(const say_args_t *a, const char *fn)
123 int res = ast_streamfile(a->chan, fn, a->language);
124 if (res) {
125 ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
126 return res;
128 res = (a->audiofd > -1 && a->ctrlfd > -1) ?
129 ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
130 ast_waitstream(a->chan, a->ints);
131 ast_stopstream(a->chan);
132 return res;
136 * the string is 'prefix:data' or prefix:fmt:data'
137 * with ':' being invalid in strings.
139 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
141 struct ast_variable *v;
142 char *lang, *x, *rule = NULL;
143 int ret = 0;
144 struct varshead head = { .first = NULL, .last = NULL };
145 struct ast_var_t *n;
147 ast_debug(2, "string <%s> depth <%d>\n", s, depth);
148 if (depth++ > 10) {
149 ast_log(LOG_WARNING, "recursion too deep, exiting\n");
150 return -1;
151 } else if (!say_cfg) {
152 ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
153 return -1;
156 /* scan languages same as in file.c */
157 if (a->language == NULL)
158 a->language = "en"; /* default */
159 ast_debug(2, "try <%s> in <%s>\n", s, a->language);
160 lang = ast_strdupa(a->language);
161 for (;;) {
162 for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
163 if (ast_extension_match(v->name, s)) {
164 rule = ast_strdupa(v->value);
165 break;
168 if (rule)
169 break;
170 if ( (x = strchr(lang, '_')) )
171 *x = '\0'; /* try without suffix */
172 else if (strcmp(lang, "en"))
173 lang = "en"; /* last resort, try 'en' if not done yet */
174 else
175 break;
177 if (!rule)
178 return 0;
180 /* skip up to two prefixes to get the value */
181 if ( (x = strchr(s, ':')) )
182 s = x + 1;
183 if ( (x = strchr(s, ':')) )
184 s = x + 1;
185 ast_debug(2, "value is <%s>\n", s);
186 n = ast_var_assign("SAY", s);
187 AST_LIST_INSERT_HEAD(&head, n, entries);
189 /* scan the body, one piece at a time */
190 while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
191 char fn[128];
192 const char *p, *fmt, *data; /* format and data pointers */
194 /* prepare a decent file name */
195 x = ast_skip_blanks(x);
196 ast_trim_blanks(x);
198 /* replace variables */
199 pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
200 ast_debug(2, "doing [%s]\n", fn);
202 /* locate prefix and data, if any */
203 fmt = index(fn, ':');
204 if (!fmt || fmt == fn) { /* regular filename */
205 ret = s_streamwait3(a, fn);
206 continue;
208 fmt++;
209 data = index(fmt, ':'); /* colon before data */
210 if (!data || data == fmt) { /* simple prefix-fmt */
211 ret = do_say(a, fn, options, depth);
212 continue;
214 /* prefix:fmt:data */
215 for (p = fmt; p < data && ret <= 0; p++) {
216 char fn2[sizeof(fn)];
217 if (*p == ' ' || *p == '\t') /* skip blanks */
218 continue;
219 if (*p == '\'') {/* file name - we trim them */
220 char *y;
221 strcpy(fn2, ast_skip_blanks(p+1)); /* make a full copy */
222 y = index(fn2, '\'');
223 if (!y) {
224 p = data; /* invalid. prepare to end */
225 break;
227 *y = '\0';
228 ast_trim_blanks(fn2);
229 p = index(p+1, '\'');
230 ret = s_streamwait3(a, fn2);
231 } else {
232 int l = fmt-fn;
233 strcpy(fn2, fn); /* copy everything */
234 /* after prefix, append the format */
235 fn2[l++] = *p;
236 strcpy(fn2 + l, data);
237 ret = do_say(a, fn2, options, depth);
240 if (ret) {
241 break;
245 ast_var_delete(n);
246 return ret;
249 static int say_full(struct ast_channel *chan, const char *string,
250 const char *ints, const char *lang, const char *options,
251 int audiofd, int ctrlfd)
253 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
254 return do_say(&a, string, options, 0);
257 static int say_number_full(struct ast_channel *chan, int num,
258 const char *ints, const char *lang, const char *options,
259 int audiofd, int ctrlfd)
261 char buf[64];
262 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
263 snprintf(buf, sizeof(buf), "num:%d", num);
264 return do_say(&a, buf, options, 0);
267 static int say_enumeration_full(struct ast_channel *chan, int num,
268 const char *ints, const char *lang, const char *options,
269 int audiofd, int ctrlfd)
271 char buf[64];
272 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
273 snprintf(buf, sizeof(buf), "enum:%d", num);
274 return do_say(&a, buf, options, 0);
277 static int say_date_generic(struct ast_channel *chan, time_t t,
278 const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix)
280 char buf[128];
281 struct ast_tm tm;
282 struct timeval tv = { t, 0 };
283 say_args_t a = { chan, ints, lang, -1, -1 };
284 if (format == NULL)
285 format = "";
287 ast_localtime(&tv, &tm, NULL);
288 snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
289 prefix,
290 format,
291 tm.tm_year+1900,
292 tm.tm_mon+1,
293 tm.tm_mday,
294 tm.tm_hour,
295 tm.tm_min,
296 tm.tm_sec,
297 tm.tm_wday,
298 tm.tm_yday);
299 return do_say(&a, buf, NULL, 0);
302 static int say_date_with_format(struct ast_channel *chan, time_t t,
303 const char *ints, const char *lang, const char *format, const char *timezone)
305 return say_date_generic(chan, t, ints, lang, format, timezone, "datetime");
308 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
310 return say_date_generic(chan, t, ints, lang, "", NULL, "date");
313 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
315 return say_date_generic(chan, t, ints, lang, "", NULL, "time");
318 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
320 return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
324 * remap the 'say' functions to use those in this file
326 static int say_init_mode(const char *mode) {
327 if (!strcmp(mode, say_new)) {
328 if (say_cfg == NULL) {
329 ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
330 return -1;
332 save_say_mode(say_new);
333 ast_say_number_full = say_number_full;
335 ast_say_enumeration_full = say_enumeration_full;
336 #if 0
337 ast_say_digits_full = say_digits_full;
338 ast_say_digit_str_full = say_digit_str_full;
339 ast_say_character_str_full = say_character_str_full;
340 ast_say_phonetic_str_full = say_phonetic_str_full;
341 ast_say_datetime_from_now = say_datetime_from_now;
342 #endif
343 ast_say_datetime = say_datetime;
344 ast_say_time = say_time;
345 ast_say_date = say_date;
346 ast_say_date_with_format = say_date_with_format;
347 } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
348 restore_say_mode(NULL);
349 } else if (strcmp(mode, say_old)) {
350 ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
351 return -1;
354 return 0;
357 static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
359 const char *old_mode = say_api_buf[0] ? say_new : say_old;
360 char *mode;
361 switch (cmd) {
362 case CLI_INIT:
363 e->command = "say load [new|old]";
364 e->usage =
365 "Usage: say load [new|old]\n"
366 " say load\n"
367 " Report status of current say mode\n"
368 " say load new\n"
369 " Set say method, configured in say.conf\n"
370 " say load old\n"
371 " Set old say method, coded in asterisk core\n";
372 return NULL;
373 case CLI_GENERATE:
374 return NULL;
376 if (a->argc == 2) {
377 ast_cli(a->fd, "say mode is [%s]\n", old_mode);
378 return CLI_SUCCESS;
379 } else if (a->argc != e->args)
380 return CLI_SHOWUSAGE;
381 mode = a->argv[2];
382 if (!strcmp(mode, old_mode))
383 ast_cli(a->fd, "say mode is %s already\n", mode);
384 else
385 if (say_init_mode(mode) == 0)
386 ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
388 return CLI_SUCCESS;
391 static struct ast_cli_entry cli_playback[] = {
392 AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
395 static int playback_exec(struct ast_channel *chan, void *data)
397 int res = 0;
398 int mres = 0;
399 char *tmp;
400 int option_skip=0;
401 int option_say=0;
402 int option_noanswer = 0;
404 AST_DECLARE_APP_ARGS(args,
405 AST_APP_ARG(filenames);
406 AST_APP_ARG(options);
409 if (ast_strlen_zero(data)) {
410 ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
411 return -1;
414 tmp = ast_strdupa(data);
415 AST_STANDARD_APP_ARGS(args, tmp);
417 if (args.options) {
418 if (strcasestr(args.options, "skip"))
419 option_skip = 1;
420 if (strcasestr(args.options, "say"))
421 option_say = 1;
422 if (strcasestr(args.options, "noanswer"))
423 option_noanswer = 1;
425 if (chan->_state != AST_STATE_UP) {
426 if (option_skip) {
427 /* At the user's option, skip if the line is not up */
428 goto done;
429 } else if (!option_noanswer)
430 /* Otherwise answer unless we're supposed to send this while on-hook */
431 res = ast_answer(chan);
433 if (!res) {
434 char *back = args.filenames;
435 char *front;
437 ast_stopstream(chan);
438 while (!res && (front = strsep(&back, "&"))) {
439 if (option_say)
440 res = say_full(chan, front, "", chan->language, NULL, -1, -1);
441 else
442 res = ast_streamfile(chan, front, chan->language);
443 if (!res) {
444 res = ast_waitstream(chan, "");
445 ast_stopstream(chan);
446 } else {
447 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
448 res = 0;
449 mres = 1;
453 done:
454 pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
455 return res;
458 static int reload(void)
460 struct ast_variable *v;
461 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
462 struct ast_config *newcfg;
464 if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
465 return 0;
467 if (say_cfg) {
468 ast_config_destroy(say_cfg);
469 ast_log(LOG_NOTICE, "Reloading say.conf\n");
470 say_cfg = newcfg;
473 if (say_cfg) {
474 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
475 if (ast_extension_match(v->name, "mode")) {
476 say_init_mode(v->value);
477 break;
483 * XXX here we should sort rules according to the same order
484 * we have in pbx.c so we have the same matching behaviour.
486 return 0;
489 static int unload_module(void)
491 int res;
493 res = ast_unregister_application(app);
495 ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
497 if (say_cfg)
498 ast_config_destroy(say_cfg);
500 return res;
503 static int load_module(void)
505 struct ast_variable *v;
506 struct ast_flags config_flags = { 0 };
508 say_cfg = ast_config_load("say.conf", config_flags);
509 if (say_cfg) {
510 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
511 if (ast_extension_match(v->name, "mode")) {
512 say_init_mode(v->value);
513 break;
518 ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
519 return ast_register_application(app, playback_exec, synopsis, descrip);
522 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
523 .load = load_module,
524 .unload = unload_module,
525 .reload = reload,