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