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