Sat Aug 6 00:39:20 2011

Asterisk developer's documentation


app_playback.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Trivial application to playback a sound file
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  * 
00025  * \ingroup applications
00026  */
00027  
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 220288 $")
00031 
00032 #include <string.h>
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 
00036 #include "asterisk/lock.h"
00037 #include "asterisk/file.h"
00038 #include "asterisk/logger.h"
00039 #include "asterisk/channel.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/module.h"
00042 #include "asterisk/translate.h"
00043 #include "asterisk/utils.h"
00044 #include "asterisk/options.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/localtime.h"
00048 #include "asterisk/say.h"
00049 
00050 static char *app = "Playback";
00051 
00052 static char *synopsis = "Play a file";
00053 
00054 static char *descrip = 
00055 "  Playback(filename[&filename2...][|option]):  Plays back given filenames (do not put\n"
00056 "extension). Options may also be included following a pipe symbol. The 'skip'\n"
00057 "option causes the playback of the message to be skipped if the channel\n"
00058 "is not in the 'up' state (i.e. it hasn't been  answered  yet). If 'skip' is \n"
00059 "specified, the application will return immediately should the channel not be\n"
00060 "off hook.  Otherwise, unless 'noanswer' is specified, the channel will\n"
00061 "be answered before the sound is played. Not all channels support playing\n"
00062 "messages while still on hook. If 'j' is specified, the application\n"
00063 "will jump to priority n+101 if present when a file specified to be played\n"
00064 "does not exist.\n"
00065 "This application sets the following channel variable upon completion:\n"
00066 " PLAYBACKSTATUS    The status of the playback attempt as a text string, one of\n"
00067 "               SUCCESS | FAILED\n"
00068 "See Also: Background (application) -- for playing soundfiles that are interruptible\n"
00069 "          WaitExten (application) -- wait for digits from caller, optionally play music on hold\n"
00070 ;
00071 
00072 
00073 static struct ast_config *say_cfg = NULL;
00074 /* save the say' api calls.
00075  * The first entry is NULL if we have the standard source,
00076  * otherwise we are sourcing from here.
00077  * 'say load [new|old]' will enable the new or old method, or report status
00078  */
00079 static const void * say_api_buf[40];
00080 static const char *say_old = "old";
00081 static const char *say_new = "new";
00082 
00083 static void save_say_mode(const void *arg)
00084 {
00085    int i = 0;
00086    say_api_buf[i++] = arg;
00087 
00088         say_api_buf[i++] = ast_say_number_full;
00089         say_api_buf[i++] = ast_say_enumeration_full;
00090         say_api_buf[i++] = ast_say_digit_str_full;
00091         say_api_buf[i++] = ast_say_character_str_full;
00092         say_api_buf[i++] = ast_say_phonetic_str_full;
00093         say_api_buf[i++] = ast_say_datetime;
00094         say_api_buf[i++] = ast_say_time;
00095         say_api_buf[i++] = ast_say_date;
00096         say_api_buf[i++] = ast_say_datetime_from_now;
00097         say_api_buf[i++] = ast_say_date_with_format;
00098 }
00099 
00100 static void restore_say_mode(void *arg)
00101 {
00102    int i = 0;
00103    say_api_buf[i++] = arg;
00104 
00105         ast_say_number_full = say_api_buf[i++];
00106         ast_say_enumeration_full = say_api_buf[i++];
00107         ast_say_digit_str_full = say_api_buf[i++];
00108         ast_say_character_str_full = say_api_buf[i++];
00109         ast_say_phonetic_str_full = say_api_buf[i++];
00110         ast_say_datetime = say_api_buf[i++];
00111         ast_say_time = say_api_buf[i++];
00112         ast_say_date = say_api_buf[i++];
00113         ast_say_datetime_from_now = say_api_buf[i++];
00114         ast_say_date_with_format = say_api_buf[i++];
00115 }
00116 
00117 /* 
00118  * Typical 'say' arguments in addition to the date or number or string
00119  * to say. We do not include 'options' because they may be different
00120  * in recursive calls, and so they are better left as an external
00121  * parameter.
00122  */
00123 typedef struct {
00124         struct ast_channel *chan;
00125         const char *ints;
00126         const char *language;
00127         int audiofd;
00128         int ctrlfd;
00129 } say_args_t;
00130 
00131 static int s_streamwait3(const say_args_t *a, const char *fn)
00132 {
00133         int res = ast_streamfile(a->chan, fn, a->language);
00134         if (res) {
00135                 ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
00136                 return res;
00137         }
00138         res = (a->audiofd  > -1 && a->ctrlfd > -1) ?
00139                 ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
00140                 ast_waitstream(a->chan, a->ints);
00141         ast_stopstream(a->chan);
00142         return res;  
00143 }
00144 
00145 /*
00146  * the string is 'prefix:data' or prefix:fmt:data'
00147  * with ':' being invalid in strings.
00148  */
00149 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
00150 {
00151    struct ast_variable *v;
00152    char *lang, *x, *rule = NULL;
00153    int ret = 0;   
00154    struct varshead head = { .first = NULL, .last = NULL };
00155    struct ast_var_t *n;
00156 
00157    if (depth++ > 10) {
00158       ast_log(LOG_WARNING, "recursion too deep, exiting\n");
00159       return -1;
00160    } else if (!say_cfg) {
00161       ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
00162       return -1;
00163    }
00164 
00165    /* scan languages same as in file.c */
00166    if (a->language == NULL)
00167       a->language = "en";     /* default */
00168    lang = ast_strdupa(a->language);
00169    for (;;) {
00170       for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
00171          if (ast_extension_match(v->name, s)) {
00172             rule = ast_strdupa(v->value);
00173             break;
00174          }
00175       }
00176       if (rule)
00177          break;
00178       if ( (x = strchr(lang, '_')) )
00179          *x = '\0';      /* try without suffix */
00180       else if (strcmp(lang, "en"))
00181          lang = "en";    /* last resort, try 'en' if not done yet */
00182       else
00183          break;
00184    }
00185    if (!rule)
00186       return 0;
00187 
00188    /* skip up to two prefixes to get the value */
00189    if ( (x = strchr(s, ':')) )
00190       s = x + 1;
00191    if ( (x = strchr(s, ':')) )
00192       s = x + 1;
00193    n = ast_var_assign("SAY", s);
00194    AST_LIST_INSERT_HEAD(&head, n, entries);
00195 
00196    /* scan the body, one piece at a time */
00197    while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
00198       char fn[128];
00199       const char *p, *fmt, *data; /* format and data pointers */
00200 
00201       /* prepare a decent file name */
00202       x = ast_skip_blanks(x);
00203       ast_trim_blanks(x);
00204 
00205       /* replace variables */
00206       memset(fn, 0, sizeof(fn)); /* XXX why isn't done in pbx_substitute_variables_helper! */
00207       pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
00208 
00209       /* locate prefix and data, if any */
00210       fmt = strchr(fn, ':');
00211       if (!fmt || fmt == fn)  {  /* regular filename */
00212          ret = s_streamwait3(a, fn);
00213          continue;
00214       }
00215       fmt++;
00216       data = strchr(fmt, ':');   /* colon before data */
00217       if (!data || data == fmt) {   /* simple prefix-fmt */
00218          ret = do_say(a, fn, options, depth);
00219          continue;
00220       }
00221       /* prefix:fmt:data */
00222       for (p = fmt; p < data && ret <= 0; p++) {
00223          char fn2[sizeof(fn)];
00224          if (*p == ' ' || *p == '\t')  /* skip blanks */
00225             continue;
00226          if (*p == '\'') {/* file name - we trim them */
00227             char *y;
00228             strcpy(fn2, ast_skip_blanks(p+1));  /* make a full copy */
00229             y = strchr(fn2, '\'');
00230             if (!y) {
00231                p = data;   /* invalid. prepare to end */
00232                break;
00233             }
00234             *y = '\0';
00235             ast_trim_blanks(fn2);
00236             p = strchr(p + 1, '\'');
00237             ret = s_streamwait3(a, fn2);
00238          } else {
00239             int l = fmt-fn;
00240             strcpy(fn2, fn); /* copy everything */
00241             /* after prefix, append the format */
00242             fn2[l++] = *p;
00243             strcpy(fn2 + l, data);
00244             ret = do_say(a, fn2, options, depth);
00245          }
00246          
00247          if (ret) {
00248             break;
00249          }
00250       }
00251    }
00252    ast_var_delete(n);
00253    return ret;
00254 }
00255 
00256 static int say_full(struct ast_channel *chan, const char *string,
00257         const char *ints, const char *lang, const char *options,
00258         int audiofd, int ctrlfd)
00259 {
00260         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00261         return do_say(&a, string, options, 0);
00262 }
00263 
00264 static int say_number_full(struct ast_channel *chan, int num,
00265    const char *ints, const char *lang, const char *options,
00266    int audiofd, int ctrlfd)
00267 {
00268    char buf[64];
00269         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00270    snprintf(buf, sizeof(buf), "num:%d", num);
00271         return do_say(&a, buf, options, 0);
00272 }
00273 
00274 static int say_enumeration_full(struct ast_channel *chan, int num,
00275    const char *ints, const char *lang, const char *options,
00276    int audiofd, int ctrlfd)
00277 {
00278    char buf[64];
00279         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00280    snprintf(buf, sizeof(buf), "enum:%d", num);
00281         return do_say(&a, buf, options, 0);
00282 }
00283 
00284 static int say_date_generic(struct ast_channel *chan, time_t t,
00285    const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix)
00286 {
00287    char buf[128];
00288    struct tm tm;
00289         say_args_t a = { chan, ints, lang, -1, -1 };
00290    if (format == NULL)
00291       format = "";
00292 
00293    ast_localtime(&t, &tm, NULL);
00294    snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
00295       prefix,
00296       format,
00297       tm.tm_year+1900,
00298       tm.tm_mon+1,
00299       tm.tm_mday,
00300       tm.tm_hour,
00301       tm.tm_min,
00302       tm.tm_sec,
00303       tm.tm_wday,
00304       tm.tm_yday);
00305         return do_say(&a, buf, NULL, 0);
00306 }
00307 
00308 static int say_date_with_format(struct ast_channel *chan, time_t t,
00309    const char *ints, const char *lang, const char *format, const char *timezone)
00310 {
00311    return say_date_generic(chan, t, ints, lang, format, timezone, "datetime");
00312 }
00313 
00314 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00315 {
00316    return say_date_generic(chan, t, ints, lang, "", NULL, "date");
00317 }
00318 
00319 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00320 {
00321    return say_date_generic(chan, t, ints, lang, "", NULL, "time");
00322 }
00323 
00324 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00325 {
00326    return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
00327 }
00328 
00329 /*
00330  * remap the 'say' functions to use those in this file
00331  */
00332 static int __say_init(int fd, int argc, char *argv[])
00333 {
00334    const char *old_mode = say_api_buf[0] ? say_new : say_old;
00335    char *mode;
00336 
00337    if (argc == 2) {
00338       ast_cli(fd, "say mode is [%s]\n", old_mode);
00339       return RESULT_SUCCESS;
00340         } else if (argc != 3)
00341                 return RESULT_SHOWUSAGE;
00342         mode = argv[2];
00343 
00344    ast_log(LOG_WARNING, "init say.c from %s to %s\n", old_mode, mode);
00345 
00346    if (!strcmp(mode, old_mode)) {
00347       ast_log(LOG_WARNING, "say mode is %s already\n", mode);
00348    } else if (!strcmp(mode, say_new)) {
00349       if (say_cfg == NULL)
00350          say_cfg = ast_config_load("say.conf");
00351       save_say_mode(say_new);
00352       ast_say_number_full = say_number_full;
00353 
00354       ast_say_enumeration_full = say_enumeration_full;
00355 #if 0
00356       ast_say_digits_full = say_digits_full;
00357       ast_say_digit_str_full = say_digit_str_full;
00358       ast_say_character_str_full = say_character_str_full;
00359       ast_say_phonetic_str_full = say_phonetic_str_full;
00360       ast_say_datetime_from_now = say_datetime_from_now;
00361 #endif
00362       ast_say_datetime = say_datetime;
00363       ast_say_time = say_time;
00364       ast_say_date = say_date;
00365       ast_say_date_with_format = say_date_with_format;
00366    } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
00367       restore_say_mode(NULL);
00368    } else {
00369       ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
00370    }
00371    return RESULT_SUCCESS;
00372 }
00373 
00374 static struct ast_cli_entry cli_playback[] = {
00375         { { "say", "load", NULL },
00376    __say_init, "set/show the say mode",
00377    "Usage: say load [new|old]\n    Set/show the say mode\n" },
00378 };
00379 
00380 static int playback_exec(struct ast_channel *chan, void *data)
00381 {
00382    int res = 0;
00383    int mres = 0;
00384    struct ast_module_user *u;
00385    char *tmp;
00386    int option_skip=0;
00387    int option_say=0;
00388    int option_noanswer = 0;
00389    int priority_jump = 0;
00390 
00391    AST_DECLARE_APP_ARGS(args,
00392       AST_APP_ARG(filenames);
00393       AST_APP_ARG(options);
00394    );
00395    
00396    if (ast_strlen_zero(data)) {
00397       ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
00398       return -1;
00399    }
00400 
00401    tmp = ast_strdupa(data);
00402 
00403    u = ast_module_user_add(chan);
00404    AST_STANDARD_APP_ARGS(args, tmp);
00405 
00406    if (args.options) {
00407       if (strcasestr(args.options, "skip"))
00408          option_skip = 1;
00409       if (strcasestr(args.options, "say"))
00410          option_say = 1;
00411       if (strcasestr(args.options, "noanswer"))
00412          option_noanswer = 1;
00413       if (strchr(args.options, 'j'))
00414          priority_jump = 1;
00415    }
00416    
00417    if (chan->_state != AST_STATE_UP) {
00418       if (option_skip) {
00419          /* At the user's option, skip if the line is not up */
00420          goto done;
00421       } else if (!option_noanswer) {
00422          /* Otherwise answer unless we're supposed to send this while on-hook */
00423          res = ast_answer(chan);
00424       }
00425    }
00426    if (!res) {
00427       char *back = args.filenames;
00428       char *front;
00429 
00430       ast_stopstream(chan);
00431       while (!res && (front = strsep(&back, "&"))) {
00432          if (option_say)
00433             res = say_full(chan, front, "", chan->language, NULL, -1, -1);
00434          else
00435             res = ast_streamfile(chan, front, chan->language);
00436          if (!res) { 
00437             res = ast_waitstream(chan, "");  
00438             ast_stopstream(chan);
00439          } else {
00440             ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
00441             if (priority_jump || ast_opt_priority_jumping)
00442                ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
00443             res = 0;
00444             mres = 1;
00445          }
00446       }
00447    }
00448 done:
00449    pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
00450    ast_module_user_remove(u);
00451    return res;
00452 }
00453 
00454 static int reload(void)
00455 {
00456    if (say_cfg) {
00457       ast_config_destroy(say_cfg);
00458       ast_log(LOG_NOTICE, "Reloading say.conf\n");
00459    }
00460    say_cfg = ast_config_load("say.conf");
00461    /*
00462     * XXX here we should sort rules according to the same order
00463     * we have in pbx.c so we have the same matching behaviour.
00464     */
00465    return 0;
00466 }
00467 
00468 static int unload_module(void)
00469 {
00470    int res;
00471 
00472    res = ast_unregister_application(app);
00473 
00474    ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00475 
00476    ast_module_user_hangup_all();
00477 
00478    if (say_cfg)
00479       ast_config_destroy(say_cfg);
00480 
00481    return res; 
00482 }
00483 
00484 static int load_module(void)
00485 {
00486    say_cfg = ast_config_load("say.conf");
00487         ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00488    return ast_register_application(app, playback_exec, synopsis, descrip);
00489 }
00490 
00491 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
00492       .load = load_module,
00493       .unload = unload_module,
00494       .reload = reload,
00495           );

Generated on Sat Aug 6 00:39:20 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7