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