Mon May 14 04:42:51 2007

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$")
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 ;
00069 
00070 
00071 static struct ast_config *say_cfg = NULL;
00072 /* save the say' api calls.
00073  * The first entry is NULL if we have the standard source,
00074  * otherwise we are sourcing from here.
00075  * 'say load [new|old]' will enable the new or old method, or report status
00076  */
00077 static const void * say_api_buf[40];
00078 static const char *say_old = "old";
00079 static const char *say_new = "new";
00080 
00081 static void save_say_mode(const void *arg)
00082 {
00083    int i = 0;
00084    say_api_buf[i++] = arg;
00085 
00086         say_api_buf[i++] = ast_say_number_full;
00087         say_api_buf[i++] = ast_say_enumeration_full;
00088         say_api_buf[i++] = ast_say_digit_str_full;
00089         say_api_buf[i++] = ast_say_character_str_full;
00090         say_api_buf[i++] = ast_say_phonetic_str_full;
00091         say_api_buf[i++] = ast_say_datetime;
00092         say_api_buf[i++] = ast_say_time;
00093         say_api_buf[i++] = ast_say_date;
00094         say_api_buf[i++] = ast_say_datetime_from_now;
00095         say_api_buf[i++] = ast_say_date_with_format;
00096 }
00097 
00098 static void restore_say_mode(void *arg)
00099 {
00100    int i = 0;
00101    say_api_buf[i++] = arg;
00102 
00103         ast_say_number_full = say_api_buf[i++];
00104         ast_say_enumeration_full = say_api_buf[i++];
00105         ast_say_digit_str_full = say_api_buf[i++];
00106         ast_say_character_str_full = say_api_buf[i++];
00107         ast_say_phonetic_str_full = say_api_buf[i++];
00108         ast_say_datetime = say_api_buf[i++];
00109         ast_say_time = say_api_buf[i++];
00110         ast_say_date = say_api_buf[i++];
00111         ast_say_datetime_from_now = say_api_buf[i++];
00112         ast_say_date_with_format = say_api_buf[i++];
00113 }
00114 
00115 /* 
00116  * Typical 'say' arguments in addition to the date or number or string
00117  * to say. We do not include 'options' because they may be different
00118  * in recursive calls, and so they are better left as an external
00119  * parameter.
00120  */
00121 typedef struct {
00122         struct ast_channel *chan;
00123         const char *ints;
00124         const char *language;
00125         int audiofd;
00126         int ctrlfd;
00127 } say_args_t;
00128 
00129 static int s_streamwait3(const say_args_t *a, const char *fn)
00130 {
00131         int res = ast_streamfile(a->chan, fn, a->language);
00132         if (res) {
00133                 ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
00134                 return res;
00135         }
00136         res = (a->audiofd  > -1 && a->ctrlfd > -1) ?
00137                 ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
00138                 ast_waitstream(a->chan, a->ints);
00139         ast_stopstream(a->chan);
00140         return res;  
00141 }
00142 
00143 /*
00144  * the string is 'prefix:data' or prefix:fmt:data'
00145  * with ':' being invalid in strings.
00146  */
00147 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
00148 {
00149    struct ast_variable *v;
00150    char *lang, *x, *rule = NULL;
00151    int ret = 0;   
00152    struct varshead head = { .first = NULL, .last = NULL };
00153    struct ast_var_t *n;
00154 
00155    ast_log(LOG_WARNING, "string <%s> depth <%d>\n", s, depth);
00156    if (depth++ > 10) {
00157       ast_log(LOG_WARNING, "recursion too deep, exiting\n");
00158       return -1;
00159    } else if (!say_cfg) {
00160       ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
00161       return -1;
00162    }
00163 
00164    /* scan languages same as in file.c */
00165    if (a->language == NULL)
00166       a->language = "en";     /* default */
00167    ast_log(LOG_WARNING, "try <%s> in <%s>\n", s, a->language);
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    ast_log(LOG_WARNING, "value is <%s>\n", s);
00194    n = ast_var_assign("SAY", s);
00195    AST_LIST_INSERT_HEAD(&head, n, entries);
00196 
00197    /* scan the body, one piece at a time */
00198    while ( ret <= 0 && (x = strsep(&rule, ",")) ) { /* exit on key */
00199       char fn[128];
00200       const char *p, *fmt, *data; /* format and data pointers */
00201 
00202       /* prepare a decent file name */
00203       x = ast_skip_blanks(x);
00204       ast_trim_blanks(x);
00205 
00206       /* replace variables */
00207       memset(fn, 0, sizeof(fn)); /* XXX why isn't done in pbx_substitute_variables_helper! */
00208       pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
00209       ast_log(LOG_WARNING, "doing [%s]\n", fn);
00210 
00211       /* locate prefix and data, if any */
00212       fmt = index(fn, ':');
00213       if (!fmt || fmt == fn)  {  /* regular filename */
00214          ret = s_streamwait3(a, fn);
00215          continue;
00216       }
00217       fmt++;
00218       data = index(fmt, ':'); /* colon before data */
00219       if (!data || data == fmt) {   /* simple prefix-fmt */
00220          ret = do_say(a, fn, options, depth);
00221          continue;
00222       }
00223       /* prefix:fmt:data */
00224       for (p = fmt; p < data && ret <= 0; p++) {
00225          char fn2[sizeof(fn)];
00226          if (*p == ' ' || *p == '\t')  /* skip blanks */
00227             continue;
00228          if (*p == '\'') {/* file name - we trim them */
00229             char *y;
00230             strcpy(fn2, ast_skip_blanks(p+1));  /* make a full copy */
00231             y = index(fn2, '\'');
00232             if (!y) {
00233                p = data;   /* invalid. prepare to end */
00234                break;
00235             }
00236             *y = '\0';
00237             ast_trim_blanks(fn2);
00238             p = index(p+1, '\'');
00239             ret = s_streamwait3(a, fn2);
00240          } else {
00241             int l = fmt-fn;
00242             strcpy(fn2, fn); /* copy everything */
00243             /* after prefix, append the format */
00244             fn2[l++] = *p;
00245             strcpy(fn2 + l, data);
00246             ret = do_say(a, fn2, options, depth);
00247          }
00248       }
00249    }
00250    ast_var_delete(n);
00251    return ret;
00252 }
00253 
00254 static int say_full(struct ast_channel *chan, const char *string,
00255         const char *ints, const char *lang, const char *options,
00256         int audiofd, int ctrlfd)
00257 {
00258         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00259         return do_say(&a, string, options, 0);
00260 }
00261 
00262 static int say_number_full(struct ast_channel *chan, int num,
00263    const char *ints, const char *lang, const char *options,
00264    int audiofd, int ctrlfd)
00265 {
00266    char buf[64];
00267         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00268    snprintf(buf, sizeof(buf), "num:%d", num);
00269         return do_say(&a, buf, options, 0);
00270 }
00271 
00272 static int say_enumeration_full(struct ast_channel *chan, int num,
00273    const char *ints, const char *lang, const char *options,
00274    int audiofd, int ctrlfd)
00275 {
00276    char buf[64];
00277         say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00278    snprintf(buf, sizeof(buf), "enum:%d", num);
00279         return do_say(&a, buf, options, 0);
00280 }
00281 
00282 static int say_date_generic(struct ast_channel *chan, time_t t,
00283    const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix)
00284 {
00285    char buf[128];
00286    struct tm tm;
00287         say_args_t a = { chan, ints, lang, -1, -1 };
00288    if (format == NULL)
00289       format = "";
00290 
00291    ast_localtime(&t, &tm, NULL);
00292    snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
00293       prefix,
00294       format,
00295       tm.tm_year+1900,
00296       tm.tm_mon+1,
00297       tm.tm_mday,
00298       tm.tm_hour,
00299       tm.tm_min,
00300       tm.tm_sec,
00301       tm.tm_wday,
00302       tm.tm_yday);
00303         return do_say(&a, buf, NULL, 0);
00304 }
00305 
00306 static int say_date_with_format(struct ast_channel *chan, time_t t,
00307    const char *ints, const char *lang, const char *format, const char *timezone)
00308 {
00309    return say_date_generic(chan, t, ints, lang, format, timezone, "datetime");
00310 }
00311 
00312 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00313 {
00314    return say_date_generic(chan, t, ints, lang, "", NULL, "date");
00315 }
00316 
00317 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00318 {
00319    return say_date_generic(chan, t, ints, lang, "", NULL, "time");
00320 }
00321 
00322 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00323 {
00324    return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
00325 }
00326 
00327 /*
00328  * remap the 'say' functions to use those in this file
00329  */
00330 static int __say_init(int fd, int argc, char *argv[])
00331 {
00332    const char *old_mode = say_api_buf[0] ? say_new : say_old;
00333    char *mode;
00334 
00335    if (argc == 2) {
00336       ast_cli(fd, "say mode is [%s]\n", old_mode);
00337       return RESULT_SUCCESS;
00338         } else if (argc != 3)
00339                 return RESULT_SHOWUSAGE;
00340         mode = argv[2];
00341 
00342    ast_log(LOG_WARNING, "init say.c from %s to %s\n", old_mode, mode);
00343 
00344    if (!strcmp(mode, old_mode)) {
00345       ast_log(LOG_WARNING, "say mode is %s already\n", mode);
00346    } else if (!strcmp(mode, say_new)) {
00347       if (say_cfg == NULL)
00348          say_cfg = ast_config_load("say.conf");
00349       save_say_mode(say_new);
00350       ast_say_number_full = say_number_full;
00351 
00352       ast_say_enumeration_full = say_enumeration_full;
00353 #if 0
00354       ast_say_digits_full = say_digits_full;
00355       ast_say_digit_str_full = say_digit_str_full;
00356       ast_say_character_str_full = say_character_str_full;
00357       ast_say_phonetic_str_full = say_phonetic_str_full;
00358       ast_say_datetime_from_now = say_datetime_from_now;
00359 #endif
00360       ast_say_datetime = say_datetime;
00361       ast_say_time = say_time;
00362       ast_say_date = say_date;
00363       ast_say_date_with_format = say_date_with_format;
00364    } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
00365       restore_say_mode(NULL);
00366    } else {
00367       ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
00368    }
00369    return RESULT_SUCCESS;
00370 }
00371 
00372 static struct ast_cli_entry cli_playback[] = {
00373         { { "say", "load", NULL },
00374    __say_init, "set/show the say mode",
00375    "say load new|old" },
00376 };
00377 
00378 static int playback_exec(struct ast_channel *chan, void *data)
00379 {
00380    int res = 0;
00381    int mres = 0;
00382    struct ast_module_user *u;
00383    char *tmp;
00384    int option_skip=0;
00385    int option_say=0;
00386    int option_noanswer = 0;
00387    int priority_jump = 0;
00388 
00389    AST_DECLARE_APP_ARGS(args,
00390       AST_APP_ARG(filenames);
00391       AST_APP_ARG(options);
00392    );
00393    
00394    if (ast_strlen_zero(data)) {
00395       ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
00396       return -1;
00397    }
00398 
00399    tmp = ast_strdupa(data);
00400 
00401    u = ast_module_user_add(chan);
00402    AST_STANDARD_APP_ARGS(args, tmp);
00403 
00404    if (args.options) {
00405       if (strcasestr(args.options, "skip"))
00406          option_skip = 1;
00407       if (strcasestr(args.options, "say"))
00408          option_say = 1;
00409       if (strcasestr(args.options, "noanswer"))
00410          option_noanswer = 1;
00411       if (strchr(args.options, 'j'))
00412          priority_jump = 1;
00413    }
00414    
00415    if (chan->_state != AST_STATE_UP) {
00416       if (option_skip) {
00417          /* At the user's option, skip if the line is not up */
00418          goto done;
00419       } else if (!option_noanswer)
00420          /* Otherwise answer unless we're supposed to send this while on-hook */
00421          res = ast_answer(chan);
00422    }
00423    if (!res) {
00424       char *back = args.filenames;
00425       char *front;
00426 
00427       ast_stopstream(chan);
00428       while (!res && (front = strsep(&back, "&"))) {
00429          if (option_say)
00430             res = say_full(chan, front, "", chan->language, NULL, -1, -1);
00431          else
00432             res = ast_streamfile(chan, front, chan->language);
00433          if (!res) { 
00434             res = ast_waitstream(chan, "");  
00435             ast_stopstream(chan);
00436          } else {
00437             ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
00438             if (priority_jump || ast_opt_priority_jumping)
00439                ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
00440             res = 0;
00441             mres = 1;
00442          }
00443       }
00444    }
00445 done:
00446    pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
00447    ast_module_user_remove(u);
00448    return res;
00449 }
00450 
00451 static int reload(void)
00452 {
00453    if (say_cfg) {
00454       ast_config_destroy(say_cfg);
00455       ast_log(LOG_NOTICE, "Reloading say.conf\n");
00456    }
00457    say_cfg = ast_config_load("say.conf");
00458    /*
00459     * XXX here we should sort rules according to the same order
00460     * we have in pbx.c so we have the same matching behaviour.
00461     */
00462    return 0;
00463 }
00464 
00465 static int unload_module(void)
00466 {
00467    int res;
00468 
00469    res = ast_unregister_application(app);
00470 
00471    ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00472 
00473    ast_module_user_hangup_all();
00474 
00475    if (say_cfg)
00476       ast_config_destroy(say_cfg);
00477 
00478    return res; 
00479 }
00480 
00481 static int load_module(void)
00482 {
00483    say_cfg = ast_config_load("say.conf");
00484         ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00485    return ast_register_application(app, playback_exec, synopsis, descrip);
00486 }
00487 
00488 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
00489       .load = load_module,
00490       .unload = unload_module,
00491       .reload = reload,
00492           );

Generated on Mon May 14 04:42:51 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1