Add ControlPlayback manager action
[asterisk/asterisk.git] / apps / app_playback.c
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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
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
41  * some CLI commands.
42  */
43 #include "asterisk/say.h"       /*!< provides config-file based 'say' functions */
44 #include "asterisk/cli.h"
45
46 /*** DOCUMENTATION
47         <application name="Playback" language="en_US">
48                 <synopsis>
49                         Play a file.
50                 </synopsis>
51                 <syntax>
52                         <parameter name="filenames" required="true" argsep="&amp;">
53                                 <argument name="filename" required="true" />
54                                 <argument name="filename2" multiple="true" />
55                         </parameter>
56                         <parameter name="options">
57                                 <para>Comma separated list of options</para>
58                                 <optionlist>
59                                         <option name="skip">
60                                                 <para>Do not play if not answered</para>
61                                         </option>
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>
66                                         </option>
67                                 </optionlist>
68                         </parameter>
69                 </syntax>
70                 <description>
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>
75                         <variablelist>
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"/>
80                                 </variable>
81                         </variablelist>
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>
84                 </description>
85                 <see-also>
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>
92                 </see-also>
93         </application>
94  ***/
95
96 static char *app = "Playback";
97
98 static struct ast_config *say_cfg = NULL;
99
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
104  */
105 static const void *say_api_buf[40];
106 static const char * const say_old = "old";
107 static const char * const say_new = "new";
108
109 static void save_say_mode(const void *arg)
110 {
111         int i = 0;
112         say_api_buf[i++] = arg;
113
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;
124 }
125
126 static void restore_say_mode(void *arg)
127 {
128         int i = 0;
129         say_api_buf[i++] = arg;
130
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++];
141 }
142
143 /*! \brief
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
147  * parameter.
148  */
149 typedef struct {
150         struct ast_channel *chan;
151         const char *ints;
152         const char *language;
153         int audiofd;
154         int ctrlfd;
155 } say_args_t;
156
157 static int s_streamwait3(const say_args_t *a, const char *fn)
158 {
159         int res = ast_streamfile(a->chan, fn, a->language);
160         if (res) {
161                 ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
162                 return res;
163         }
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);
168         return res;  
169 }
170
171 /*! \brief
172  * the string is 'prefix:data' or prefix:fmt:data'
173  * with ':' being invalid in strings.
174  */
175 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
176 {
177         struct ast_variable *v;
178         char *lang, *x, *rule = NULL;
179         int ret = 0;   
180         struct varshead head = { .first = NULL, .last = NULL };
181         struct ast_var_t *n;
182
183         ast_debug(2, "string <%s> depth <%d>\n", s, depth);
184         if (depth++ > 10) {
185                 ast_log(LOG_WARNING, "recursion too deep, exiting\n");
186                 return -1;
187         } else if (!say_cfg) {
188                 ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
189                 return -1;
190         }
191
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);
197         for (;;) {
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);
201                                 break;
202                         }
203                 }
204                 if (rule)
205                         break;
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 */
210                 else
211                         break;
212         }
213         if (!rule)
214                 return 0;
215
216         /* skip up to two prefixes to get the value */
217         if ( (x = strchr(s, ':')) )
218                 s = x + 1;
219         if ( (x = strchr(s, ':')) )
220                 s = x + 1;
221         ast_debug(2, "value is <%s>\n", s);
222         n = ast_var_assign("SAY", s);
223         AST_LIST_INSERT_HEAD(&head, n, entries);
224
225         /* scan the body, one piece at a time */
226         while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
227                 char fn[128];
228                 const char *p, *fmt, *data; /* format and data pointers */
229
230                 /* prepare a decent file name */
231                 x = ast_skip_blanks(x);
232                 ast_trim_blanks(x);
233
234                 /* replace variables */
235                 pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
236                 ast_debug(2, "doing [%s]\n", fn);
237
238                 /* locate prefix and data, if any */
239                 fmt = strchr(fn, ':');
240                 if (!fmt || fmt == fn)  {       /* regular filename */
241                         ret = s_streamwait3(a, fn);
242                         continue;
243                 }
244                 fmt++;
245                 data = strchr(fmt, ':');        /* colon before data */
246                 if (!data || data == fmt) {     /* simple prefix-fmt */
247                         ret = do_say(a, fn, options, depth);
248                         continue;
249                 }
250                 /* prefix:fmt:data */
251                 for (p = fmt; p < data && ret <= 0; p++) {
252                         char fn2[sizeof(fn)];
253                         if (*p == ' ' || *p == '\t')    /* skip blanks */
254                                 continue;
255                         if (*p == '\'') {/* file name - we trim them */
256                                 char *y;
257                                 strcpy(fn2, ast_skip_blanks(p+1));      /* make a full copy */
258                                 y = strchr(fn2, '\'');
259                                 if (!y) {
260                                         p = data;       /* invalid. prepare to end */
261                                         break;
262                                 }
263                                 *y = '\0';
264                                 ast_trim_blanks(fn2);
265                                 p = strchr(p+1, '\'');
266                                 ret = s_streamwait3(a, fn2);
267                         } else {
268                                 int l = fmt-fn;
269                                 strcpy(fn2, fn); /* copy everything */
270                                 /* after prefix, append the format */
271                                 fn2[l++] = *p;
272                                 strcpy(fn2 + l, data);
273                                 ret = do_say(a, fn2, options, depth);
274                         }
275                         
276                         if (ret) {
277                                 break;
278                         }
279                 }
280         }
281         ast_var_delete(n);
282         return ret;
283 }
284
285 static int say_full(struct ast_channel *chan, const char *string,
286         const char *ints, const char *lang, const char *options,
287         int audiofd, int ctrlfd)
288 {
289         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
290         return do_say(&a, string, options, 0);
291 }
292
293 static int say_number_full(struct ast_channel *chan, int num,
294         const char *ints, const char *lang, const char *options,
295         int audiofd, int ctrlfd)
296 {
297         char buf[64];
298         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
299         snprintf(buf, sizeof(buf), "num:%d", num);
300         return do_say(&a, buf, options, 0);
301 }
302
303 static int say_enumeration_full(struct ast_channel *chan, int num,
304         const char *ints, const char *lang, const char *options,
305         int audiofd, int ctrlfd)
306 {
307         char buf[64];
308         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
309         snprintf(buf, sizeof(buf), "enum:%d", num);
310         return do_say(&a, buf, options, 0);
311 }
312
313 static int say_date_generic(struct ast_channel *chan, time_t t,
314         const char *ints, const char *lang, const char *format, const char *timezonename, const char *prefix)
315 {
316         char buf[128];
317         struct ast_tm tm;
318         struct timeval when = { t, 0 };
319         say_args_t a = { chan, ints, lang, -1, -1 };
320         if (format == NULL)
321                 format = "";
322
323         ast_localtime(&when, &tm, NULL);
324         snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
325                 prefix,
326                 format,
327                 tm.tm_year+1900,
328                 tm.tm_mon+1,
329                 tm.tm_mday,
330                 tm.tm_hour,
331                 tm.tm_min,
332                 tm.tm_sec,
333                 tm.tm_wday,
334                 tm.tm_yday);
335         return do_say(&a, buf, NULL, 0);
336 }
337
338 static int say_date_with_format(struct ast_channel *chan, time_t t,
339         const char *ints, const char *lang, const char *format, const char *timezonename)
340 {
341         return say_date_generic(chan, t, ints, lang, format, timezonename, "datetime");
342 }
343
344 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
345 {
346         return say_date_generic(chan, t, ints, lang, "", NULL, "date");
347 }
348
349 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
350 {
351         return say_date_generic(chan, t, ints, lang, "", NULL, "time");
352 }
353
354 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
355 {
356         return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
357 }
358
359 /*! \brief
360  * remap the 'say' functions to use those in this file
361  */
362 static int say_init_mode(const char *mode) {
363         if (!strcmp(mode, say_new)) {
364                 if (say_cfg == NULL) {
365                         ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
366                         return -1;
367                 }
368                 save_say_mode(say_new);
369                 ast_say_number_full = say_number_full;
370
371                 ast_say_enumeration_full = say_enumeration_full;
372 #if 0
373                 /*! \todo XXX 
374                    These functions doesn't exist.
375                    say.conf.sample indicates this is working... 
376                 */
377                 ast_say_digits_full = say_digits_full;
378                 ast_say_digit_str_full = say_digit_str_full;
379                 ast_say_character_str_full = say_character_str_full;
380                 ast_say_phonetic_str_full = say_phonetic_str_full;
381                 ast_say_datetime_from_now = say_datetime_from_now;
382 #endif
383                 ast_say_datetime = say_datetime;
384                 ast_say_time = say_time;
385                 ast_say_date = say_date;
386                 ast_say_date_with_format = say_date_with_format;
387         } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
388                 restore_say_mode(NULL);
389         } else if (strcmp(mode, say_old)) {
390                 ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
391                 return -1;
392         }
393         
394         return 0;
395 }
396
397 static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
398 {
399         const char *old_mode = say_api_buf[0] ? say_new : say_old;
400         const char *mode;
401         switch (cmd) {
402         case CLI_INIT:
403                 e->command = "say load [new|old]";
404                 e->usage = 
405                         "Usage: say load [new|old]\n"
406                         "       say load\n"
407                         "           Report status of current say mode\n"
408                         "       say load new\n"
409                         "           Set say method, configured in say.conf\n"
410                         "       say load old\n"
411                         "           Set old say method, coded in asterisk core\n";
412                 return NULL;
413         case CLI_GENERATE:
414                 return NULL;
415         }
416         if (a->argc == 2) {
417                 ast_cli(a->fd, "say mode is [%s]\n", old_mode);
418                 return CLI_SUCCESS;
419         } else if (a->argc != e->args)
420                 return CLI_SHOWUSAGE;
421         mode = a->argv[2];
422         if (!strcmp(mode, old_mode))
423                 ast_cli(a->fd, "say mode is %s already\n", mode);
424         else
425                 if (say_init_mode(mode) == 0)
426                         ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
427
428         return CLI_SUCCESS;
429 }
430
431 static struct ast_cli_entry cli_playback[] = {
432         AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
433 };
434
435 static int playback_exec(struct ast_channel *chan, const char *data)
436 {
437         int res = 0;
438         int mres = 0;
439         char *tmp;
440         int option_skip=0;
441         int option_say=0;
442         int option_noanswer = 0;
443
444         AST_DECLARE_APP_ARGS(args,
445                 AST_APP_ARG(filenames);
446                 AST_APP_ARG(options);
447         );
448         
449         if (ast_strlen_zero(data)) {
450                 ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
451                 return -1;
452         }
453
454         tmp = ast_strdupa(data);
455         AST_STANDARD_APP_ARGS(args, tmp);
456
457         if (args.options) {
458                 if (strcasestr(args.options, "skip"))
459                         option_skip = 1;
460                 if (strcasestr(args.options, "say"))
461                         option_say = 1;
462                 if (strcasestr(args.options, "noanswer"))
463                         option_noanswer = 1;
464         } 
465         if (ast_channel_state(chan) != AST_STATE_UP) {
466                 if (option_skip) {
467                         /* At the user's option, skip if the line is not up */
468                         goto done;
469                 } else if (!option_noanswer) {
470                         /* Otherwise answer unless we're supposed to send this while on-hook */
471                         res = ast_answer(chan);
472                 }
473         }
474         if (!res) {
475                 char *back = args.filenames;
476                 char *front;
477
478                 ast_stopstream(chan);
479                 while (!res && (front = strsep(&back, "&"))) {
480                         if (option_say)
481                                 res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
482                         else
483                                 res = ast_streamfile(chan, front, ast_channel_language(chan));
484                         if (!res) {
485                                 res = ast_waitstream(chan, "");
486                                 ast_stopstream(chan);
487                         }
488                         if (res) {
489                                 ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
490                                 res = 0;
491                                 mres = 1;
492                         }
493                 }
494         }
495 done:
496         pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
497         return res;
498 }
499
500 static int reload(void)
501 {
502         struct ast_variable *v;
503         struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
504         struct ast_config *newcfg;
505
506         if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
507                 return 0;
508         } else if (newcfg == CONFIG_STATUS_FILEINVALID) {
509                 ast_log(LOG_ERROR, "Config file say.conf is in an invalid format.  Aborting.\n");
510                 return 0;
511         }
512
513         if (say_cfg) {
514                 ast_config_destroy(say_cfg);
515                 ast_log(LOG_NOTICE, "Reloading say.conf\n");
516                 say_cfg = newcfg;
517         }
518
519         if (say_cfg) {
520                 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
521                         if (ast_extension_match(v->name, "mode")) {
522                                 say_init_mode(v->value);
523                                 break;
524                         }
525                 }
526         }
527         
528         /*! \todo
529          * XXX here we should sort rules according to the same order
530          * we have in pbx.c so we have the same matching behaviour.
531          */
532         return 0;
533 }
534
535 static int unload_module(void)
536 {
537         int res;
538
539         res = ast_unregister_application(app);
540
541         ast_cli_unregister_multiple(cli_playback, ARRAY_LEN(cli_playback));
542
543         if (say_cfg)
544                 ast_config_destroy(say_cfg);
545
546         return res;     
547 }
548
549 static int load_module(void)
550 {
551         struct ast_variable *v;
552         struct ast_flags config_flags = { 0 };
553
554         say_cfg = ast_config_load("say.conf", config_flags);
555         if (say_cfg && say_cfg != CONFIG_STATUS_FILEINVALID) {
556                 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
557                         if (ast_extension_match(v->name, "mode")) {
558                                 say_init_mode(v->value);
559                                 break;
560                         }
561                 }
562         }
563
564         ast_cli_register_multiple(cli_playback, ARRAY_LEN(cli_playback));
565         return ast_register_application_xml(app, playback_exec);
566 }
567
568 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
569                 .load = load_module,
570                 .unload = unload_module,
571                 .reload = reload,
572                );