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: 396279 $")
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 if (!n) {
00215 ast_log(LOG_ERROR, "Memory allocation error in do_say\n");
00216 return -1;
00217 }
00218 AST_LIST_INSERT_HEAD(&head, n, entries);
00219
00220
00221 while ( !ret && (x = strsep(&rule, ",")) ) {
00222 char fn[128];
00223 const char *p, *fmt, *data;
00224
00225
00226 x = ast_skip_blanks(x);
00227 ast_trim_blanks(x);
00228
00229
00230 pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
00231 ast_debug(2, "doing [%s]\n", fn);
00232
00233
00234 fmt = strchr(fn, ':');
00235 if (!fmt || fmt == fn) {
00236 ret = s_streamwait3(a, fn);
00237 continue;
00238 }
00239 fmt++;
00240 data = strchr(fmt, ':');
00241 if (!data || data == fmt) {
00242 ret = do_say(a, fn, options, depth);
00243 continue;
00244 }
00245
00246 for (p = fmt; p < data && ret <= 0; p++) {
00247 char fn2[sizeof(fn)];
00248 if (*p == ' ' || *p == '\t')
00249 continue;
00250 if (*p == '\'') {
00251 char *y;
00252 strcpy(fn2, ast_skip_blanks(p+1));
00253 y = strchr(fn2, '\'');
00254 if (!y) {
00255 p = data;
00256 break;
00257 }
00258 *y = '\0';
00259 ast_trim_blanks(fn2);
00260 p = strchr(p+1, '\'');
00261 ret = s_streamwait3(a, fn2);
00262 } else {
00263 int l = fmt-fn;
00264 strcpy(fn2, fn);
00265
00266 fn2[l++] = *p;
00267 strcpy(fn2 + l, data);
00268 ret = do_say(a, fn2, options, depth);
00269 }
00270
00271 if (ret) {
00272 break;
00273 }
00274 }
00275 }
00276 ast_var_delete(n);
00277 return ret;
00278 }
00279
00280 static int say_full(struct ast_channel *chan, const char *string,
00281 const char *ints, const char *lang, const char *options,
00282 int audiofd, int ctrlfd)
00283 {
00284 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00285 return do_say(&a, string, options, 0);
00286 }
00287
00288 static int say_number_full(struct ast_channel *chan, int num,
00289 const char *ints, const char *lang, const char *options,
00290 int audiofd, int ctrlfd)
00291 {
00292 char buf[64];
00293 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00294 snprintf(buf, sizeof(buf), "num:%d", num);
00295 return do_say(&a, buf, options, 0);
00296 }
00297
00298 static int say_enumeration_full(struct ast_channel *chan, int num,
00299 const char *ints, const char *lang, const char *options,
00300 int audiofd, int ctrlfd)
00301 {
00302 char buf[64];
00303 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00304 snprintf(buf, sizeof(buf), "enum:%d", num);
00305 return do_say(&a, buf, options, 0);
00306 }
00307
00308 static int say_date_generic(struct ast_channel *chan, time_t t,
00309 const char *ints, const char *lang, const char *format, const char *timezonename, const char *prefix)
00310 {
00311 char buf[128];
00312 struct ast_tm tm;
00313 struct timeval when = { t, 0 };
00314 say_args_t a = { chan, ints, lang, -1, -1 };
00315 if (format == NULL)
00316 format = "";
00317
00318 ast_localtime(&when, &tm, NULL);
00319 snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
00320 prefix,
00321 format,
00322 tm.tm_year+1900,
00323 tm.tm_mon+1,
00324 tm.tm_mday,
00325 tm.tm_hour,
00326 tm.tm_min,
00327 tm.tm_sec,
00328 tm.tm_wday,
00329 tm.tm_yday);
00330 return do_say(&a, buf, NULL, 0);
00331 }
00332
00333 static int say_date_with_format(struct ast_channel *chan, time_t t,
00334 const char *ints, const char *lang, const char *format, const char *timezonename)
00335 {
00336 return say_date_generic(chan, t, ints, lang, format, timezonename, "datetime");
00337 }
00338
00339 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00340 {
00341 return say_date_generic(chan, t, ints, lang, "", NULL, "date");
00342 }
00343
00344 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00345 {
00346 return say_date_generic(chan, t, ints, lang, "", NULL, "time");
00347 }
00348
00349 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00350 {
00351 return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
00352 }
00353
00354
00355
00356
00357 static int say_init_mode(const char *mode) {
00358 if (!strcmp(mode, say_new)) {
00359 if (say_cfg == NULL) {
00360 ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
00361 return -1;
00362 }
00363 save_say_mode(say_new);
00364 ast_say_number_full = say_number_full;
00365
00366 ast_say_enumeration_full = say_enumeration_full;
00367 #if 0
00368 ast_say_digits_full = say_digits_full;
00369 ast_say_digit_str_full = say_digit_str_full;
00370 ast_say_character_str_full = say_character_str_full;
00371 ast_say_phonetic_str_full = say_phonetic_str_full;
00372 ast_say_datetime_from_now = say_datetime_from_now;
00373 #endif
00374 ast_say_datetime = say_datetime;
00375 ast_say_time = say_time;
00376 ast_say_date = say_date;
00377 ast_say_date_with_format = say_date_with_format;
00378 } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
00379 restore_say_mode(NULL);
00380 } else if (strcmp(mode, say_old)) {
00381 ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
00382 return -1;
00383 }
00384
00385 return 0;
00386 }
00387
00388 static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00389 {
00390 const char *old_mode = say_api_buf[0] ? say_new : say_old;
00391 const char *mode;
00392 switch (cmd) {
00393 case CLI_INIT:
00394 e->command = "say load [new|old]";
00395 e->usage =
00396 "Usage: say load [new|old]\n"
00397 " say load\n"
00398 " Report status of current say mode\n"
00399 " say load new\n"
00400 " Set say method, configured in say.conf\n"
00401 " say load old\n"
00402 " Set old say method, coded in asterisk core\n";
00403 return NULL;
00404 case CLI_GENERATE:
00405 return NULL;
00406 }
00407 if (a->argc == 2) {
00408 ast_cli(a->fd, "say mode is [%s]\n", old_mode);
00409 return CLI_SUCCESS;
00410 } else if (a->argc != e->args)
00411 return CLI_SHOWUSAGE;
00412 mode = a->argv[2];
00413 if (!strcmp(mode, old_mode))
00414 ast_cli(a->fd, "say mode is %s already\n", mode);
00415 else
00416 if (say_init_mode(mode) == 0)
00417 ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
00418
00419 return CLI_SUCCESS;
00420 }
00421
00422 static struct ast_cli_entry cli_playback[] = {
00423 AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
00424 };
00425
00426 static int playback_exec(struct ast_channel *chan, const char *data)
00427 {
00428 int res = 0;
00429 int mres = 0;
00430 char *tmp;
00431 int option_skip=0;
00432 int option_say=0;
00433 int option_noanswer = 0;
00434
00435 AST_DECLARE_APP_ARGS(args,
00436 AST_APP_ARG(filenames);
00437 AST_APP_ARG(options);
00438 );
00439
00440 if (ast_strlen_zero(data)) {
00441 ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
00442 return -1;
00443 }
00444
00445 tmp = ast_strdupa(data);
00446 AST_STANDARD_APP_ARGS(args, tmp);
00447
00448 if (args.options) {
00449 if (strcasestr(args.options, "skip"))
00450 option_skip = 1;
00451 if (strcasestr(args.options, "say"))
00452 option_say = 1;
00453 if (strcasestr(args.options, "noanswer"))
00454 option_noanswer = 1;
00455 }
00456 if (chan->_state != AST_STATE_UP) {
00457 if (option_skip) {
00458
00459 goto done;
00460 } else if (!option_noanswer) {
00461
00462 res = ast_answer(chan);
00463 }
00464 }
00465 if (!res) {
00466 char *back = args.filenames;
00467 char *front;
00468
00469 ast_stopstream(chan);
00470 while (!res && (front = strsep(&back, "&"))) {
00471 if (option_say)
00472 res = say_full(chan, front, "", chan->language, NULL, -1, -1);
00473 else
00474 res = ast_streamfile(chan, front, chan->language);
00475 if (!res) {
00476 res = ast_waitstream(chan, "");
00477 ast_stopstream(chan);
00478 } else {
00479 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
00480 res = 0;
00481 mres = 1;
00482 }
00483 }
00484 }
00485 done:
00486 pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
00487 return res;
00488 }
00489
00490 static int reload(void)
00491 {
00492 struct ast_variable *v;
00493 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00494 struct ast_config *newcfg;
00495
00496 if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
00497 return 0;
00498 } else if (newcfg == CONFIG_STATUS_FILEINVALID) {
00499 ast_log(LOG_ERROR, "Config file say.conf is in an invalid format. Aborting.\n");
00500 return 0;
00501 }
00502
00503 if (say_cfg) {
00504 ast_config_destroy(say_cfg);
00505 ast_log(LOG_NOTICE, "Reloading say.conf\n");
00506 }
00507 say_cfg = newcfg;
00508
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
00519
00520
00521
00522 return 0;
00523 }
00524
00525 static int unload_module(void)
00526 {
00527 int res;
00528
00529 res = ast_unregister_application(app);
00530
00531 ast_cli_unregister_multiple(cli_playback, ARRAY_LEN(cli_playback));
00532
00533 if (say_cfg)
00534 ast_config_destroy(say_cfg);
00535
00536 return res;
00537 }
00538
00539 static int load_module(void)
00540 {
00541 struct ast_variable *v;
00542 struct ast_flags config_flags = { 0 };
00543
00544 say_cfg = ast_config_load("say.conf", config_flags);
00545 if (say_cfg && say_cfg != CONFIG_STATUS_FILEINVALID) {
00546 for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
00547 if (ast_extension_match(v->name, "mode")) {
00548 say_init_mode(v->value);
00549 break;
00550 }
00551 }
00552 }
00553
00554 ast_cli_register_multiple(cli_playback, ARRAY_LEN(cli_playback));
00555 return ast_register_application_xml(app, playback_exec);
00556 }
00557
00558 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
00559 .load = load_module,
00560 .unload = unload_module,
00561 .reload = reload,
00562 );