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
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 172503 $")
00037
00038 #include <sys/socket.h>
00039 #include <netdb.h>
00040 #include <netinet/in.h>
00041 #include <arpa/inet.h>
00042 #include <signal.h>
00043 #include <fcntl.h>
00044 #include <ctype.h>
00045 #include <errno.h>
00046 #ifdef HAVE_CAP
00047 #include <sys/capability.h>
00048 #endif
00049
00050 #include "asterisk/file.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/md5.h"
00055 #include "asterisk/config.h"
00056 #include "asterisk/utils.h"
00057 #include "asterisk/lock.h"
00058 #include "asterisk/app.h"
00059
00060 #define FESTIVAL_CONFIG "festival.conf"
00061 #define MAXLEN 180
00062 #define MAXFESTLEN 2048
00063
00064 static char *app = "Festival";
00065
00066 static char *synopsis = "Say text to the user";
00067
00068 static char *descrip =
00069 " Festival(text[,intkeys]): Connect to Festival, send the argument, get back the waveform,\n"
00070 "play it to the user, allowing any given interrupt keys to immediately terminate and return\n"
00071 "the value, or 'any' to allow any number back (useful in dialplan)\n";
00072
00073
00074 static char *socket_receive_file_to_buff(int fd, int *size)
00075 {
00076
00077
00078
00079
00080
00081 static char *file_stuff_key = "ft_StUfF_key";
00082 char *buff, *tmp;
00083 int bufflen;
00084 int n,k,i;
00085 char c;
00086
00087 bufflen = 1024;
00088 if (!(buff = ast_malloc(bufflen)))
00089 return NULL;
00090 *size = 0;
00091
00092 for (k = 0; file_stuff_key[k] != '\0';) {
00093 n = read(fd, &c, 1);
00094 if (n == 0)
00095 break;
00096 if ((*size) + k + 1 >= bufflen) {
00097
00098 bufflen += bufflen / 4;
00099 if (!(tmp = ast_realloc(buff, bufflen))) {
00100 ast_free(buff);
00101 return NULL;
00102 }
00103 buff = tmp;
00104 }
00105 if (file_stuff_key[k] == c)
00106 k++;
00107 else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) {
00108
00109 for (i = 0; i < k; i++, (*size)++)
00110 buff[*size] = file_stuff_key[i];
00111 k = 0;
00112
00113 } else {
00114 for (i = 0; i < k; i++, (*size)++)
00115 buff[*size] = file_stuff_key[i];
00116 k = 0;
00117 buff[*size] = c;
00118 (*size)++;
00119 }
00120 }
00121
00122 return buff;
00123 }
00124
00125 static int send_waveform_to_fd(char *waveform, int length, int fd)
00126 {
00127 int res;
00128 int x;
00129 #ifdef __PPC__
00130 char c;
00131 #endif
00132 sigset_t fullset, oldset;
00133 #ifdef HAVE_CAP
00134 cap_t cap;
00135 #endif
00136
00137 sigfillset(&fullset);
00138 pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
00139
00140 res = fork();
00141 if (res < 0)
00142 ast_log(LOG_WARNING, "Fork failed\n");
00143 if (res) {
00144 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
00145 return res;
00146 }
00147 #ifdef HAVE_CAP
00148 cap = cap_from_text("cap_net_admin-eip");
00149
00150 if (cap_set_proc(cap)) {
00151
00152 ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
00153 }
00154 cap_free(cap);
00155 #endif
00156 for (x = 0; x < 256; x++) {
00157 if (x != fd)
00158 close(x);
00159 }
00160 if (ast_opt_high_priority)
00161 ast_set_priority(0);
00162 signal(SIGPIPE, SIG_DFL);
00163 pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
00164 #ifdef __PPC__
00165 for (x = 0; x < length; x += 2) {
00166 c = *(waveform + x + 1);
00167 *(waveform + x + 1) = *(waveform + x);
00168 *(waveform + x) = c;
00169 }
00170 #endif
00171
00172 if (write(fd, waveform, length) < 0) {
00173 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00174 }
00175
00176 close(fd);
00177 exit(0);
00178 }
00179
00180 static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length, char *intkeys)
00181 {
00182 int res = 0;
00183 int fds[2];
00184 int pid = -1;
00185 int needed = 0;
00186 int owriteformat;
00187 struct ast_frame *f;
00188 struct myframe {
00189 struct ast_frame f;
00190 char offset[AST_FRIENDLY_OFFSET];
00191 char frdata[2048];
00192 } myf = {
00193 .f = { 0, },
00194 };
00195
00196 if (pipe(fds)) {
00197 ast_log(LOG_WARNING, "Unable to create pipe\n");
00198 return -1;
00199 }
00200
00201
00202 if (chan->_state != AST_STATE_UP)
00203 ast_answer(chan);
00204 ast_stopstream(chan);
00205 ast_indicate(chan, -1);
00206
00207 owriteformat = chan->writeformat;
00208 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
00209 if (res < 0) {
00210 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
00211 return -1;
00212 }
00213
00214 res = send_waveform_to_fd(waveform, length, fds[1]);
00215 if (res >= 0) {
00216 pid = res;
00217
00218
00219 for (;;) {
00220 res = ast_waitfor(chan, 1000);
00221 if (res < 1) {
00222 res = -1;
00223 break;
00224 }
00225 f = ast_read(chan);
00226 if (!f) {
00227 ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
00228 res = -1;
00229 break;
00230 }
00231 if (f->frametype == AST_FRAME_DTMF) {
00232 ast_debug(1, "User pressed a key\n");
00233 if (intkeys && strchr(intkeys, f->subclass)) {
00234 res = f->subclass;
00235 ast_frfree(f);
00236 break;
00237 }
00238 }
00239 if (f->frametype == AST_FRAME_VOICE) {
00240
00241 needed = f->samples * 2;
00242 if (needed > sizeof(myf.frdata)) {
00243 ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
00244 (int)sizeof(myf.frdata) / 2, needed/2);
00245 needed = sizeof(myf.frdata);
00246 }
00247 res = read(fds[0], myf.frdata, needed);
00248 if (res > 0) {
00249 myf.f.frametype = AST_FRAME_VOICE;
00250 myf.f.subclass = AST_FORMAT_SLINEAR;
00251 myf.f.datalen = res;
00252 myf.f.samples = res / 2;
00253 myf.f.offset = AST_FRIENDLY_OFFSET;
00254 myf.f.src = __PRETTY_FUNCTION__;
00255 myf.f.data = myf.frdata;
00256 if (ast_write(chan, &myf.f) < 0) {
00257 res = -1;
00258 ast_frfree(f);
00259 break;
00260 }
00261 if (res < needed) {
00262 ast_debug(1, "Last frame\n");
00263 res = 0;
00264 ast_frfree(f);
00265 break;
00266 }
00267 } else {
00268 ast_debug(1, "No more waveform\n");
00269 res = 0;
00270 }
00271 }
00272 ast_frfree(f);
00273 }
00274 }
00275 close(fds[0]);
00276 close(fds[1]);
00277
00278 #if 0
00279 if (pid > -1)
00280 kill(pid, SIGKILL);
00281 #endif
00282 if (!res && owriteformat)
00283 ast_set_write_format(chan, owriteformat);
00284 return res;
00285 }
00286
00287 static int festival_exec(struct ast_channel *chan, void *vdata)
00288 {
00289 int usecache;
00290 int res = 0;
00291 struct sockaddr_in serv_addr;
00292 struct hostent *serverhost;
00293 struct ast_hostent ahp;
00294 int fd;
00295 FILE *fs;
00296 const char *host;
00297 const char *cachedir;
00298 const char *temp;
00299 const char *festivalcommand;
00300 int port = 1314;
00301 int n;
00302 char ack[4];
00303 char *waveform;
00304 int filesize;
00305 int wave;
00306 char bigstring[MAXFESTLEN];
00307 int i;
00308 struct MD5Context md5ctx;
00309 unsigned char MD5Res[16];
00310 char MD5Hex[33] = "";
00311 char koko[4] = "";
00312 char cachefile[MAXFESTLEN]="";
00313 int readcache = 0;
00314 int writecache = 0;
00315 int strln;
00316 int fdesc = -1;
00317 char buffer[16384];
00318 int seekpos = 0;
00319 char *data;
00320 struct ast_config *cfg;
00321 char *newfestivalcommand;
00322 struct ast_flags config_flags = { 0 };
00323 AST_DECLARE_APP_ARGS(args,
00324 AST_APP_ARG(text);
00325 AST_APP_ARG(interrupt);
00326 );
00327
00328 if (ast_strlen_zero(vdata)) {
00329 ast_log(LOG_WARNING, "festival requires an argument (text)\n");
00330 return -1;
00331 }
00332
00333 cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00334 if (!cfg) {
00335 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00336 return -1;
00337 }
00338 if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
00339 host = "localhost";
00340 }
00341 if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
00342 port = 1314;
00343 } else {
00344 port = atoi(temp);
00345 }
00346 if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
00347 usecache = 0;
00348 } else {
00349 usecache = ast_true(temp);
00350 }
00351 if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
00352 cachedir = "/tmp/";
00353 }
00354
00355 data = ast_strdupa(vdata);
00356 AST_STANDARD_APP_ARGS(args, data);
00357
00358 if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
00359 const char *startcmd = "(tts_textasterisk \"";
00360 const char *endcmd = "\" 'file)(quit)\n";
00361
00362 strln = strlen(startcmd) + strlen(args.text) + strlen(endcmd) + 1;
00363 newfestivalcommand = alloca(strln);
00364 snprintf(newfestivalcommand, strln, "%s%s%s", startcmd, args.text, endcmd);
00365 festivalcommand = newfestivalcommand;
00366 } else {
00367 int i, j;
00368 newfestivalcommand = alloca(strlen(festivalcommand) + strlen(args.text) + 1);
00369
00370 for (i = 0, j = 0; i < strlen(festivalcommand); i++) {
00371 if (festivalcommand[i] == '\\' && festivalcommand[i + 1] == 'n') {
00372 newfestivalcommand[j++] = '\n';
00373 i++;
00374 } else if (festivalcommand[i] == '\\') {
00375 newfestivalcommand[j++] = festivalcommand[i + 1];
00376 i++;
00377 } else if (festivalcommand[i] == '%' && festivalcommand[i + 1] == 's') {
00378 sprintf(&newfestivalcommand[j], "%s", args.text);
00379 j += strlen(args.text);
00380 i++;
00381 } else
00382 newfestivalcommand[j++] = festivalcommand[i];
00383 }
00384 newfestivalcommand[j] = '\0';
00385 festivalcommand = newfestivalcommand;
00386 }
00387
00388 if (args.interrupt && !strcasecmp(args.interrupt, "any"))
00389 args.interrupt = AST_DIGIT_ANY;
00390
00391 ast_debug(1, "Text passed to festival server : %s\n", args.text);
00392
00393
00394 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00395
00396 if (fd < 0) {
00397 ast_log(LOG_WARNING, "festival_client: can't get socket\n");
00398 ast_config_destroy(cfg);
00399 return -1;
00400 }
00401
00402 memset(&serv_addr, 0, sizeof(serv_addr));
00403
00404 if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
00405
00406 serverhost = ast_gethostbyname(host, &ahp);
00407
00408 if (serverhost == NULL) {
00409 ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
00410 ast_config_destroy(cfg);
00411 return -1;
00412 }
00413 memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
00414 }
00415
00416 serv_addr.sin_family = AF_INET;
00417 serv_addr.sin_port = htons(port);
00418
00419 if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
00420 ast_log(LOG_WARNING, "festival_client: connect to server failed\n");
00421 ast_config_destroy(cfg);
00422 return -1;
00423 }
00424
00425
00426 MD5Init(&md5ctx);
00427 MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text));
00428 MD5Final(MD5Res, &md5ctx);
00429 MD5Hex[0] = '\0';
00430
00431
00432
00433 for (i = 0; i < 16; i++) {
00434 snprintf(koko, sizeof(koko), "%X", MD5Res[i]);
00435 strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1);
00436 }
00437 readcache = 0;
00438 writecache = 0;
00439 if (strlen(cachedir) + strlen(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
00440 snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
00441 fdesc = open(cachefile, O_RDWR);
00442 if (fdesc == -1) {
00443 fdesc = open(cachefile, O_CREAT | O_RDWR, AST_FILE_MODE);
00444 if (fdesc != -1) {
00445 writecache = 1;
00446 strln = strlen(args.text);
00447 ast_debug(1, "line length : %d\n", strln);
00448 if (write(fdesc,&strln,sizeof(int)) < 0) {
00449 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00450 }
00451 if (write(fdesc,data,strln) < 0) {
00452 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00453 }
00454 seekpos = lseek(fdesc, 0, SEEK_CUR);
00455 ast_debug(1, "Seek position : %d\n", seekpos);
00456 }
00457 } else {
00458 if (read(fdesc,&strln,sizeof(int)) != sizeof(int)) {
00459 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00460 }
00461 ast_debug(1, "Cache file exists, strln=%d, strlen=%d\n", strln, (int)strlen(args.text));
00462 if (strlen(args.text) == strln) {
00463 ast_debug(1, "Size OK\n");
00464 if (read(fdesc,&bigstring,strln) != strln) {
00465 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00466 }
00467 bigstring[strln] = 0;
00468 if (strcmp(bigstring, args.text) == 0) {
00469 readcache = 1;
00470 } else {
00471 ast_log(LOG_WARNING, "Strings do not match\n");
00472 }
00473 } else {
00474 ast_log(LOG_WARNING, "Size mismatch\n");
00475 }
00476 }
00477 }
00478
00479 if (readcache == 1) {
00480 close(fd);
00481 fd = fdesc;
00482 ast_debug(1, "Reading from cache...\n");
00483 } else {
00484 ast_debug(1, "Passing text to festival...\n");
00485 fs = fdopen(dup(fd), "wb");
00486
00487 fprintf(fs, "%s", festivalcommand);
00488 fflush(fs);
00489 fclose(fs);
00490 }
00491
00492
00493 if (writecache == 1) {
00494 ast_debug(1, "Writing result to cache...\n");
00495 while ((strln = read(fd, buffer, 16384)) != 0) {
00496 if (write(fdesc,buffer,strln) < 0) {
00497 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00498 }
00499 }
00500 close(fd);
00501 close(fdesc);
00502 fd = open(cachefile, O_RDWR);
00503 lseek(fd, seekpos, SEEK_SET);
00504 }
00505
00506 ast_debug(1, "Passing data to channel...\n");
00507
00508
00509
00510 wave = 0;
00511 do {
00512 int read_data;
00513 for (n = 0; n < 3; ) {
00514 read_data = read(fd, ack + n, 3 - n);
00515
00516
00517
00518 if (read_data == -1) {
00519 ast_log(LOG_WARNING, "Unable to read from cache/festival fd\n");
00520 close(fd);
00521 ast_config_destroy(cfg);
00522 return -1;
00523 }
00524 n += read_data;
00525 }
00526 ack[3] = '\0';
00527 if (strcmp(ack, "WV\n") == 0) {
00528 ast_debug(1, "Festival WV command\n");
00529 if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00530 res = send_waveform_to_channel(chan, waveform, filesize, args.interrupt);
00531 ast_free(waveform);
00532 }
00533 break;
00534 } else if (strcmp(ack, "LP\n") == 0) {
00535 ast_debug(1, "Festival LP command\n");
00536 if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00537 waveform[filesize] = '\0';
00538 ast_log(LOG_WARNING, "Festival returned LP : %s\n", waveform);
00539 ast_free(waveform);
00540 }
00541 } else if (strcmp(ack, "ER\n") == 0) {
00542 ast_log(LOG_WARNING, "Festival returned ER\n");
00543 res = -1;
00544 break;
00545 }
00546 } while (strcmp(ack, "OK\n") != 0);
00547 close(fd);
00548 ast_config_destroy(cfg);
00549 return res;
00550 }
00551
00552 static int unload_module(void)
00553 {
00554 return ast_unregister_application(app);
00555 }
00556
00557 static int load_module(void)
00558 {
00559 struct ast_flags config_flags = { 0 };
00560 struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00561 if (!cfg) {
00562 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00563 return AST_MODULE_LOAD_DECLINE;
00564 }
00565 ast_config_destroy(cfg);
00566 return ast_register_application(app, festival_exec, synopsis, descrip);
00567 }
00568
00569 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Festival Interface");