00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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
00075
00076
00077
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
00119
00120
00121
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
00147
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
00166 if (a->language == NULL)
00167 a->language = "en";
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';
00180 else if (strcmp(lang, "en"))
00181 lang = "en";
00182 else
00183 break;
00184 }
00185 if (!rule)
00186 return 0;
00187
00188
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
00197 while ( !ret && (x = strsep(&rule, ",")) ) {
00198 char fn[128];
00199 const char *p, *fmt, *data;
00200
00201
00202 x = ast_skip_blanks(x);
00203 ast_trim_blanks(x);
00204
00205
00206 memset(fn, 0, sizeof(fn));
00207 pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
00208
00209
00210 fmt = strchr(fn, ':');
00211 if (!fmt || fmt == fn) {
00212 ret = s_streamwait3(a, fn);
00213 continue;
00214 }
00215 fmt++;
00216 data = strchr(fmt, ':');
00217 if (!data || data == fmt) {
00218 ret = do_say(a, fn, options, depth);
00219 continue;
00220 }
00221
00222 for (p = fmt; p < data && ret <= 0; p++) {
00223 char fn2[sizeof(fn)];
00224 if (*p == ' ' || *p == '\t')
00225 continue;
00226 if (*p == '\'') {
00227 char *y;
00228 strcpy(fn2, ast_skip_blanks(p+1));
00229 y = strchr(fn2, '\'');
00230 if (!y) {
00231 p = data;
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);
00241
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
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
00420 goto done;
00421 } else if (!option_noanswer) {
00422
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
00463
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 );