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