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: 89618 $")
00031
00032 #include <string.h>
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035
00036 #include "asterisk/lock.h"
00037 #include "asterisk/file.h"
00038 #include "asterisk/logger.h"
00039 #include "asterisk/channel.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/module.h"
00042 #include "asterisk/translate.h"
00043 #include "asterisk/utils.h"
00044 #include "asterisk/options.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/localtime.h"
00048 #include "asterisk/say.h"
00049
00050 static char *app = "Playback";
00051
00052 static char *synopsis = "Play a file";
00053
00054 static char *descrip =
00055 " Playback(filename[&filename2...][|option]): Plays back given filenames (do not put\n"
00056 "extension). Options may also be included following a pipe symbol. The 'skip'\n"
00057 "option causes the playback of the message to be skipped if the channel\n"
00058 "is not in the 'up' state (i.e. it hasn't been answered yet). If 'skip' is \n"
00059 "specified, the application will return immediately should the channel not be\n"
00060 "off hook. Otherwise, unless 'noanswer' is specified, the channel will\n"
00061 "be answered before the sound is played. Not all channels support playing\n"
00062 "messages while still on hook. If 'j' is specified, the application\n"
00063 "will jump to priority n+101 if present when a file specified to be played\n"
00064 "does not exist.\n"
00065 "This application sets the following channel variable upon completion:\n"
00066 " PLAYBACKSTATUS The status of the playback attempt as a text string, one of\n"
00067 " SUCCESS | FAILED\n"
00068 ;
00069
00070
00071 static struct ast_config *say_cfg = NULL;
00072
00073
00074
00075
00076
00077 static const void * say_api_buf[40];
00078 static const char *say_old = "old";
00079 static const char *say_new = "new";
00080
00081 static void save_say_mode(const void *arg)
00082 {
00083 int i = 0;
00084 say_api_buf[i++] = arg;
00085
00086 say_api_buf[i++] = ast_say_number_full;
00087 say_api_buf[i++] = ast_say_enumeration_full;
00088 say_api_buf[i++] = ast_say_digit_str_full;
00089 say_api_buf[i++] = ast_say_character_str_full;
00090 say_api_buf[i++] = ast_say_phonetic_str_full;
00091 say_api_buf[i++] = ast_say_datetime;
00092 say_api_buf[i++] = ast_say_time;
00093 say_api_buf[i++] = ast_say_date;
00094 say_api_buf[i++] = ast_say_datetime_from_now;
00095 say_api_buf[i++] = ast_say_date_with_format;
00096 }
00097
00098 static void restore_say_mode(void *arg)
00099 {
00100 int i = 0;
00101 say_api_buf[i++] = arg;
00102
00103 ast_say_number_full = say_api_buf[i++];
00104 ast_say_enumeration_full = say_api_buf[i++];
00105 ast_say_digit_str_full = say_api_buf[i++];
00106 ast_say_character_str_full = say_api_buf[i++];
00107 ast_say_phonetic_str_full = say_api_buf[i++];
00108 ast_say_datetime = say_api_buf[i++];
00109 ast_say_time = say_api_buf[i++];
00110 ast_say_date = say_api_buf[i++];
00111 ast_say_datetime_from_now = say_api_buf[i++];
00112 ast_say_date_with_format = say_api_buf[i++];
00113 }
00114
00115
00116
00117
00118
00119
00120
00121 typedef struct {
00122 struct ast_channel *chan;
00123 const char *ints;
00124 const char *language;
00125 int audiofd;
00126 int ctrlfd;
00127 } say_args_t;
00128
00129 static int s_streamwait3(const say_args_t *a, const char *fn)
00130 {
00131 int res = ast_streamfile(a->chan, fn, a->language);
00132 if (res) {
00133 ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
00134 return res;
00135 }
00136 res = (a->audiofd > -1 && a->ctrlfd > -1) ?
00137 ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
00138 ast_waitstream(a->chan, a->ints);
00139 ast_stopstream(a->chan);
00140 return res;
00141 }
00142
00143
00144
00145
00146
00147 static int do_say(say_args_t *a, const char *s, const char *options, int depth)
00148 {
00149 struct ast_variable *v;
00150 char *lang, *x, *rule = NULL;
00151 int ret = 0;
00152 struct varshead head = { .first = NULL, .last = NULL };
00153 struct ast_var_t *n;
00154
00155 if (depth++ > 10) {
00156 ast_log(LOG_WARNING, "recursion too deep, exiting\n");
00157 return -1;
00158 } else if (!say_cfg) {
00159 ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
00160 return -1;
00161 }
00162
00163
00164 if (a->language == NULL)
00165 a->language = "en";
00166 lang = ast_strdupa(a->language);
00167 for (;;) {
00168 for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
00169 if (ast_extension_match(v->name, s)) {
00170 rule = ast_strdupa(v->value);
00171 break;
00172 }
00173 }
00174 if (rule)
00175 break;
00176 if ( (x = strchr(lang, '_')) )
00177 *x = '\0';
00178 else if (strcmp(lang, "en"))
00179 lang = "en";
00180 else
00181 break;
00182 }
00183 if (!rule)
00184 return 0;
00185
00186
00187 if ( (x = strchr(s, ':')) )
00188 s = x + 1;
00189 if ( (x = strchr(s, ':')) )
00190 s = x + 1;
00191 n = ast_var_assign("SAY", s);
00192 AST_LIST_INSERT_HEAD(&head, n, entries);
00193
00194
00195 while ( !ret && (x = strsep(&rule, ",")) ) {
00196 char fn[128];
00197 const char *p, *fmt, *data;
00198
00199
00200 x = ast_skip_blanks(x);
00201 ast_trim_blanks(x);
00202
00203
00204 memset(fn, 0, sizeof(fn));
00205 pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
00206
00207
00208 fmt = index(fn, ':');
00209 if (!fmt || fmt == fn) {
00210 ret = s_streamwait3(a, fn);
00211 continue;
00212 }
00213 fmt++;
00214 data = index(fmt, ':');
00215 if (!data || data == fmt) {
00216 ret = do_say(a, fn, options, depth);
00217 continue;
00218 }
00219
00220 for (p = fmt; p < data && ret <= 0; p++) {
00221 char fn2[sizeof(fn)];
00222 if (*p == ' ' || *p == '\t')
00223 continue;
00224 if (*p == '\'') {
00225 char *y;
00226 strcpy(fn2, ast_skip_blanks(p+1));
00227 y = index(fn2, '\'');
00228 if (!y) {
00229 p = data;
00230 break;
00231 }
00232 *y = '\0';
00233 ast_trim_blanks(fn2);
00234 p = index(p+1, '\'');
00235 ret = s_streamwait3(a, fn2);
00236 } else {
00237 int l = fmt-fn;
00238 strcpy(fn2, fn);
00239
00240 fn2[l++] = *p;
00241 strcpy(fn2 + l, data);
00242 ret = do_say(a, fn2, options, depth);
00243 }
00244
00245 if (ret) {
00246 break;
00247 }
00248 }
00249 }
00250 ast_var_delete(n);
00251 return ret;
00252 }
00253
00254 static int say_full(struct ast_channel *chan, const char *string,
00255 const char *ints, const char *lang, const char *options,
00256 int audiofd, int ctrlfd)
00257 {
00258 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00259 return do_say(&a, string, options, 0);
00260 }
00261
00262 static int say_number_full(struct ast_channel *chan, int num,
00263 const char *ints, const char *lang, const char *options,
00264 int audiofd, int ctrlfd)
00265 {
00266 char buf[64];
00267 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00268 snprintf(buf, sizeof(buf), "num:%d", num);
00269 return do_say(&a, buf, options, 0);
00270 }
00271
00272 static int say_enumeration_full(struct ast_channel *chan, int num,
00273 const char *ints, const char *lang, const char *options,
00274 int audiofd, int ctrlfd)
00275 {
00276 char buf[64];
00277 say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
00278 snprintf(buf, sizeof(buf), "enum:%d", num);
00279 return do_say(&a, buf, options, 0);
00280 }
00281
00282 static int say_date_generic(struct ast_channel *chan, time_t t,
00283 const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix)
00284 {
00285 char buf[128];
00286 struct tm tm;
00287 say_args_t a = { chan, ints, lang, -1, -1 };
00288 if (format == NULL)
00289 format = "";
00290
00291 ast_localtime(&t, &tm, NULL);
00292 snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
00293 prefix,
00294 format,
00295 tm.tm_year+1900,
00296 tm.tm_mon+1,
00297 tm.tm_mday,
00298 tm.tm_hour,
00299 tm.tm_min,
00300 tm.tm_sec,
00301 tm.tm_wday,
00302 tm.tm_yday);
00303 return do_say(&a, buf, NULL, 0);
00304 }
00305
00306 static int say_date_with_format(struct ast_channel *chan, time_t t,
00307 const char *ints, const char *lang, const char *format, const char *timezone)
00308 {
00309 return say_date_generic(chan, t, ints, lang, format, timezone, "datetime");
00310 }
00311
00312 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00313 {
00314 return say_date_generic(chan, t, ints, lang, "", NULL, "date");
00315 }
00316
00317 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00318 {
00319 return say_date_generic(chan, t, ints, lang, "", NULL, "time");
00320 }
00321
00322 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
00323 {
00324 return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
00325 }
00326
00327
00328
00329
00330 static int __say_init(int fd, int argc, char *argv[])
00331 {
00332 const char *old_mode = say_api_buf[0] ? say_new : say_old;
00333 char *mode;
00334
00335 if (argc == 2) {
00336 ast_cli(fd, "say mode is [%s]\n", old_mode);
00337 return RESULT_SUCCESS;
00338 } else if (argc != 3)
00339 return RESULT_SHOWUSAGE;
00340 mode = argv[2];
00341
00342 ast_log(LOG_WARNING, "init say.c from %s to %s\n", old_mode, mode);
00343
00344 if (!strcmp(mode, old_mode)) {
00345 ast_log(LOG_WARNING, "say mode is %s already\n", mode);
00346 } else if (!strcmp(mode, say_new)) {
00347 if (say_cfg == NULL)
00348 say_cfg = ast_config_load("say.conf");
00349 save_say_mode(say_new);
00350 ast_say_number_full = say_number_full;
00351
00352 ast_say_enumeration_full = say_enumeration_full;
00353 #if 0
00354 ast_say_digits_full = say_digits_full;
00355 ast_say_digit_str_full = say_digit_str_full;
00356 ast_say_character_str_full = say_character_str_full;
00357 ast_say_phonetic_str_full = say_phonetic_str_full;
00358 ast_say_datetime_from_now = say_datetime_from_now;
00359 #endif
00360 ast_say_datetime = say_datetime;
00361 ast_say_time = say_time;
00362 ast_say_date = say_date;
00363 ast_say_date_with_format = say_date_with_format;
00364 } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
00365 restore_say_mode(NULL);
00366 } else {
00367 ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
00368 }
00369 return RESULT_SUCCESS;
00370 }
00371
00372 static struct ast_cli_entry cli_playback[] = {
00373 { { "say", "load", NULL },
00374 __say_init, "set/show the say mode",
00375 "say load new|old" },
00376 };
00377
00378 static int playback_exec(struct ast_channel *chan, void *data)
00379 {
00380 int res = 0;
00381 int mres = 0;
00382 struct ast_module_user *u;
00383 char *tmp;
00384 int option_skip=0;
00385 int option_say=0;
00386 int option_noanswer = 0;
00387 int priority_jump = 0;
00388
00389 AST_DECLARE_APP_ARGS(args,
00390 AST_APP_ARG(filenames);
00391 AST_APP_ARG(options);
00392 );
00393
00394 if (ast_strlen_zero(data)) {
00395 ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
00396 return -1;
00397 }
00398
00399 tmp = ast_strdupa(data);
00400
00401 u = ast_module_user_add(chan);
00402 AST_STANDARD_APP_ARGS(args, tmp);
00403
00404 if (args.options) {
00405 if (strcasestr(args.options, "skip"))
00406 option_skip = 1;
00407 if (strcasestr(args.options, "say"))
00408 option_say = 1;
00409 if (strcasestr(args.options, "noanswer"))
00410 option_noanswer = 1;
00411 if (strchr(args.options, 'j'))
00412 priority_jump = 1;
00413 }
00414
00415 if (chan->_state != AST_STATE_UP) {
00416 if (option_skip) {
00417
00418 goto done;
00419 } else if (!option_noanswer)
00420
00421 res = ast_answer(chan);
00422 }
00423 if (!res) {
00424 char *back = args.filenames;
00425 char *front;
00426
00427 ast_stopstream(chan);
00428 while (!res && (front = strsep(&back, "&"))) {
00429 if (option_say)
00430 res = say_full(chan, front, "", chan->language, NULL, -1, -1);
00431 else
00432 res = ast_streamfile(chan, front, chan->language);
00433 if (!res) {
00434 res = ast_waitstream(chan, "");
00435 ast_stopstream(chan);
00436 } else {
00437 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
00438 if (priority_jump || ast_opt_priority_jumping)
00439 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
00440 res = 0;
00441 mres = 1;
00442 }
00443 }
00444 }
00445 done:
00446 pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
00447 ast_module_user_remove(u);
00448 return res;
00449 }
00450
00451 static int reload(void)
00452 {
00453 if (say_cfg) {
00454 ast_config_destroy(say_cfg);
00455 ast_log(LOG_NOTICE, "Reloading say.conf\n");
00456 }
00457 say_cfg = ast_config_load("say.conf");
00458
00459
00460
00461
00462 return 0;
00463 }
00464
00465 static int unload_module(void)
00466 {
00467 int res;
00468
00469 res = ast_unregister_application(app);
00470
00471 ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00472
00473 ast_module_user_hangup_all();
00474
00475 if (say_cfg)
00476 ast_config_destroy(say_cfg);
00477
00478 return res;
00479 }
00480
00481 static int load_module(void)
00482 {
00483 say_cfg = ast_config_load("say.conf");
00484 ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
00485 return ast_register_application(app, playback_exec, synopsis, descrip);
00486 }
00487
00488 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
00489 .load = load_module,
00490 .unload = unload_module,
00491 .reload = reload,
00492 );