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