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