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