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: 144578 $")
00031
00032 #include "asterisk/file.h"
00033 #include "asterisk/pbx.h"
00034 #include "asterisk/module.h"
00035 #include "asterisk/app.h"
00036
00037
00038
00039 #include "asterisk/say.h"
00040 #include "asterisk/cli.h"
00041
00042 static char *app = "Playback";
00043
00044 static char *synopsis = "Play a file";
00045
00046 static char *descrip =
00047 " Playback(filename[&filename2...][,option]): Plays back given filenames (do not put\n"
00048 "extension). Options may also be included following a comma.\n"
00049 "The 'skip' option causes the playback of the message to be skipped if the channel\n"
00050 "is not in the 'up' state (i.e. it hasn't been answered yet). If 'skip' is \n"
00051 "specified, the application will return immediately should the channel not be\n"
00052 "off hook. Otherwise, unless 'noanswer' is specified, the channel will\n"
00053 "be answered before the sound is played. Not all channels support playing\n"
00054 "messages while still on hook.\n"
00055 "This application sets the following channel variable upon completion:\n"
00056 " PLAYBACKSTATUS The status of the playback attempt as a text string, one of\n"
00057 " SUCCESS | FAILED\n"
00058 "See Also: Background (application) -- for playing soundfiles that are interruptible\n"
00059 " WaitExten (application) -- wait for digits from caller, optionally play music on hold\n"
00060 ;
00061
00062
00063 static struct ast_config *say_cfg = NULL;
00064
00065
00066
00067
00068
00069 static const void *say_api_buf[40];
00070 static const char *say_old = "old";
00071 static const char *say_new = "new";
00072
00073 static void save_say_mode(const void *arg)
00074 {
00075 int i = 0;
00076 say_api_buf[i++] = arg;
00077
00078 say_api_buf[i++] = ast_say_number_full;
00079 say_api_buf[i++] = ast_say_enumeration_full;
00080 say_api_buf[i++] = ast_say_digit_str_full;
00081 say_api_buf[i++] = ast_say_character_str_full;
00082 say_api_buf[i++] = ast_say_phonetic_str_full;
00083 say_api_buf[i++] = ast_say_datetime;
00084 say_api_buf[i++] = ast_say_time;
00085 say_api_buf[i++] = ast_say_date;
00086 say_api_buf[i++] = ast_say_datetime_from_now;
00087 say_api_buf[i++] = ast_say_date_with_format;
00088 }
00089
00090 static void restore_say_mode(void *arg)
00091 {
00092 int i = 0;
00093 say_api_buf[i++] = arg;
00094
00095 ast_say_number_full = say_api_buf[i++];
00096 ast_say_enumeration_full = say_api_buf[i++];
00097 ast_say_digit_str_full = say_api_buf[i++];
00098 ast_say_character_str_full = say_api_buf[i++];
00099 ast_say_phonetic_str_full = say_api_buf[i++];
00100 ast_say_datetime = say_api_buf[i++];
00101 ast_say_time = say_api_buf[i++];
00102 ast_say_date = say_api_buf[i++];
00103 ast_say_datetime_from_now = say_api_buf[i++];
00104 ast_say_date_with_format = say_api_buf[i++];
00105 }
00106
00107
00108
00109
00110
00111
00112
00113 typedef struct {
00114 struct ast_channel *chan;
00115 const char *ints;
00116 const char *language;
00117 int audiofd;
00118 int ctrlfd;
00119 } say_args_t;
00120
00121 static int s_streamwait3(const say_args_t *a, const char *fn)
00122 {
00123 int res = ast_streamfile(a->chan, fn, a->language);
00124 if (res) {
00125 ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
00126 return res;
00127 }
00128 res = (a->audiofd > -1 && a->ctrlfd > -1) ?
00129 ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
00130 ast_waitstream(a->chan, a->ints);
00131 ast_stopstream(a->chan);
00132 return res;
00133 }
00134
00135
00136
00137
00138
00139 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
00140 {
00141 struct ast_variable *v;
00142 char *lang, *x, *rule = NULL;
00143 int ret = 0;
00144 struct varshead head = { .first = NULL, .last = NULL };
00145 struct ast_var_t *n;
00146
00147 ast_debug(2, "string <%s> depth <%d>\n", s, depth);
00148 if (depth++ > 10) {
00149 ast_log(LOG_WARNING, "recursion too deep, exiting\n");
00150 return -1;
00151 } else if (!say_cfg) {
00152 ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
00153 return -1;
00154 }
00155
00156
00157 if (a->language == NULL)
00158 a->language = "en";
00159 ast_debug(2, "try <%s> in <%s>\n", s, a->language);
00160 lang = ast_strdupa(a->language);
00161 for (;;) {
00162 for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
00163 if (ast_extension_match(v->name, s)) {
00164 rule = ast_strdupa(v->value);
00165 break;
00166 }
00167 }
00168 if (rule)
00169 break;
00170 if ( (x = strchr(lang, '_')) )
00171 *x = '\0';
00172 else if (strcmp(lang, "en"))
00173 lang = "en";
00174 else
00175 break;
00176 }
00177 if (!rule)
00178 return 0;
00179
00180
00181 if ( (x = strchr(s, ':')) )
00182 s = x + 1;
00183 if ( (x = strchr(s, ':')) )
00184 s = x + 1;
00185 ast_debug(2, "value is <%s>\n", s);
00186 n = ast_var_assign("SAY", s);
00187 AST_LIST_INSERT_HEAD(&head, n, entries);
00188
00189
00190 while ( !ret && (x = strsep(&rule, ",")) ) {
00191 char fn[128];
00192 const char *p, *fmt, *data;
00193
00194
00195 x = ast_skip_blanks(x);
00196 ast_trim_blanks(x);
00197
00198
00199 pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
00200 ast_debug(2, "doing [%s]\n", fn);
00201
00202
00203 fmt = strchr(fn, ':');
00204 if (!fmt || fmt == fn) {
00205 ret = s_streamwait3(a, fn);
00206 continue;
00207 }
00208 fmt++;
00209 data = strchr(fmt, ':');
00210 if (!data || data == fmt) {
00211 ret = do_say(a, fn, options, depth);
00212 continue;
00213 }
00214
00215 for (p = fmt; p < data && ret <= 0; p++) {
00216 char fn2[sizeof(fn)];
00217 if (*p == ' ' || *p == '\t')
00218 continue;
00219 if (*p == '\'') {
00220 char *y;
00221 strcpy(fn2, ast_skip_blanks(p+1));
00222 y = strchr(fn2, '\'');
00223 if (!y) {
00224 p = data;
00225 break;
00226 }
00227 *y = '\0';
00228 ast_trim_blanks(fn2);
00229 p = strchr(p+1, '\'');
00230 ret = s_streamwait3(a, fn2);
00231 } else {
00232 int l = fmt-fn;
00233 strcpy(fn2, fn);
00234
00235 fn2[l++] = *p;
00236 strcpy(fn2 + l, data);
00237 ret = do_say(a, fn2, options, depth);
00238 }
00239
00240 if (ret) {
00241 break;
00242 }
00243 }
00244 }
00245 ast_var_delete(n);
00246 return ret;
00247 }
00248
00249 static int say_full(struct ast_channel *chan, const char *string,
00250 const char *ints, const char *lang, const char *options,
00251 int audiofd, int ctrlfd)
00252 {
00253 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00254 return do_say(&a, string, options, 0);
00255 }
00256
00257 static int say_number_full(struct ast_channel *chan, int num,
00258 const char *ints, const char *lang, const char *options,
00259 int audiofd, int ctrlfd)
00260 {
00261 char buf[64];
00262 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00263 snprintf(buf, sizeof(buf), "num:%d", num);
00264 return do_say(&a, buf, options, 0);
00265 }
00266
00267 static int say_enumeration_full(struct ast_channel *chan, int num,
00268 const char *ints, const char *lang, const char *options,
00269 int audiofd, int ctrlfd)
00270 {
00271 char buf[64];
00272 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00273 snprintf(buf, sizeof(buf), "enum:%d", num);
00274 return do_say(&a, buf, options, 0);
00275 }
00276
00277 static int say_date_generic(struct ast_channel *chan, time_t t,
00278 const char *ints, const char *lang, const char *format, const char *timezonename, const char *prefix)
00279 {
00280 char buf[128];
00281 struct ast_tm tm;
00282 struct timeval when = { t, 0 };
00283 say_args_t a = { chan, ints, lang, -1, -1 };
00284 if (format == NULL)
00285 format = "";
00286
00287 ast_localtime(&when, &tm, NULL);
00288 snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
00289 prefix,
00290 format,
00291 tm.tm_year+1900,
00292 tm.tm_mon+1,
00293 tm.tm_mday,
00294 tm.tm_hour,
00295 tm.tm_min,
00296 tm.tm_sec,
00297 tm.tm_wday,
00298 tm.tm_yday);
00299 return do_say(&a, buf, NULL, 0);
00300 }
00301
00302 static int say_date_with_format(struct ast_channel *chan, time_t t,
00303 const char *ints, const char *lang, const char *format, const char *timezonename)
00304 {
00305 return say_date_generic(chan, t, ints, lang, format, timezonename, "datetime");
00306 }
00307
00308 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00309 {
00310 return say_date_generic(chan, t, ints, lang, "", NULL, "date");
00311 }
00312
00313 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00314 {
00315 return say_date_generic(chan, t, ints, lang, "", NULL, "time");
00316 }
00317
00318 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00319 {
00320 return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
00321 }
00322
00323
00324
00325
00326 static int say_init_mode(const char *mode) {
00327 if (!strcmp(mode, say_new)) {
00328 if (say_cfg == NULL) {
00329 ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
00330 return -1;
00331 }
00332 save_say_mode(say_new);
00333 ast_say_number_full = say_number_full;
00334
00335 ast_say_enumeration_full = say_enumeration_full;
00336 #if 0
00337 ast_say_digits_full = say_digits_full;
00338 ast_say_digit_str_full = say_digit_str_full;
00339 ast_say_character_str_full = say_character_str_full;
00340 ast_say_phonetic_str_full = say_phonetic_str_full;
00341 ast_say_datetime_from_now = say_datetime_from_now;
00342 #endif
00343 ast_say_datetime = say_datetime;
00344 ast_say_time = say_time;
00345 ast_say_date = say_date;
00346 ast_say_date_with_format = say_date_with_format;
00347 } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
00348 restore_say_mode(NULL);
00349 } else if (strcmp(mode, say_old)) {
00350 ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
00351 return -1;
00352 }
00353
00354 return 0;
00355 }
00356
00357 static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00358 {
00359 const char *old_mode = say_api_buf[0] ? say_new : say_old;
00360 char *mode;
00361 switch (cmd) {
00362 case CLI_INIT:
00363 e->command = "say load [new|old]";
00364 e->usage =
00365 "Usage: say load [new|old]\n"
00366 " say load\n"
00367 " Report status of current say mode\n"
00368 " say load new\n"
00369 " Set say method, configured in say.conf\n"
00370 " say load old\n"
00371 " Set old say method, coded in asterisk core\n";
00372 return NULL;
00373 case CLI_GENERATE:
00374 return NULL;
00375 }
00376 if (a->argc == 2) {
00377 ast_cli(a->fd, "say mode is [%s]\n", old_mode);
00378 return CLI_SUCCESS;
00379 } else if (a->argc != e->args)
00380 return CLI_SHOWUSAGE;
00381 mode = a->argv[2];
00382 if (!strcmp(mode, old_mode))
00383 ast_cli(a->fd, "say mode is %s already\n", mode);
00384 else
00385 if (say_init_mode(mode) == 0)
00386 ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
00387
00388 return CLI_SUCCESS;
00389 }
00390
00391 static struct ast_cli_entry cli_playback[] = {
00392 AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
00393 };
00394
00395 static int playback_exec(struct ast_channel *chan, void *data)
00396 {
00397 int res = 0;
00398 int mres = 0;
00399 char *tmp;
00400 int option_skip=0;
00401 int option_say=0;
00402 int option_noanswer = 0;
00403
00404 AST_DECLARE_APP_ARGS(args,
00405 AST_APP_ARG(filenames);
00406 AST_APP_ARG(options);
00407 );
00408
00409 if (ast_strlen_zero(data)) {
00410 ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
00411 return -1;
00412 }
00413
00414 tmp = ast_strdupa(data);
00415 AST_STANDARD_APP_ARGS(args, tmp);
00416
00417 if (args.options) {
00418 if (strcasestr(args.options, "skip"))
00419 option_skip = 1;
00420 if (strcasestr(args.options, "say"))
00421 option_say = 1;
00422 if (strcasestr(args.options, "noanswer"))
00423 option_noanswer = 1;
00424 }
00425 if (chan->_state != AST_STATE_UP) {
00426 if (option_skip) {
00427
00428 goto done;
00429 } else if (!option_noanswer)
00430
00431 res = ast_answer(chan);
00432 }
00433 if (!res) {
00434 char *back = args.filenames;
00435 char *front;
00436
00437 ast_stopstream(chan);
00438 while (!res && (front = strsep(&back, "&"))) {
00439 if (option_say)
00440 res = say_full(chan, front, "", chan->language, NULL, -1, -1);
00441 else
00442 res = ast_streamfile(chan, front, chan->language);
00443 if (!res) {
00444 res = ast_waitstream(chan, "");
00445 ast_stopstream(chan);
00446 } else {
00447 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
00448 res = 0;
00449 mres = 1;
00450 }
00451 }
00452 }
00453 done:
00454 pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
00455 return res;
00456 }
00457
00458 static int reload(void)
00459 {
00460 struct ast_variable *v;
00461 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00462 struct ast_config *newcfg;
00463
00464 if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
00465 return 0;
00466
00467 if (say_cfg) {
00468 ast_config_destroy(say_cfg);
00469 ast_log(LOG_NOTICE, "Reloading say.conf\n");
00470 say_cfg = newcfg;
00471 }
00472
00473 if (say_cfg) {
00474 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
00475 if (ast_extension_match(v->name, "mode")) {
00476 say_init_mode(v->value);
00477 break;
00478 }
00479 }
00480 }
00481
00482
00483
00484
00485
00486 return 0;
00487 }
00488
00489 static int unload_module(void)
00490 {
00491 int res;
00492
00493 res = ast_unregister_application(app);
00494
00495 ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00496
00497 if (say_cfg)
00498 ast_config_destroy(say_cfg);
00499
00500 return res;
00501 }
00502
00503 static int load_module(void)
00504 {
00505 struct ast_variable *v;
00506 struct ast_flags config_flags = { 0 };
00507
00508 say_cfg = ast_config_load("say.conf", config_flags);
00509 if (say_cfg) {
00510 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
00511 if (ast_extension_match(v->name, "mode")) {
00512 say_init_mode(v->value);
00513 break;
00514 }
00515 }
00516 }
00517
00518 ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00519 return ast_register_application(app, playback_exec, synopsis, descrip);
00520 }
00521
00522 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
00523 .load = load_module,
00524 .unload = unload_module,
00525 .reload = reload,
00526 );