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: 370642 $")
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 int 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 if (pipe(fds)) {
00182 ast_log(LOG_WARNING, "Unable to create pipe\n");
00183 return -1;
00184 }
00185
00186
00187 if (chan->_state != AST_STATE_UP)
00188 ast_answer(chan);
00189 ast_stopstream(chan);
00190 ast_indicate(chan, -1);
00191
00192 owriteformat = chan->writeformat;
00193 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
00194 if (res < 0) {
00195 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
00196 return -1;
00197 }
00198
00199 res = send_waveform_to_fd(waveform, length, fds[1]);
00200 if (res >= 0) {
00201
00202
00203 for (;;) {
00204 res = ast_waitfor(chan, 1000);
00205 if (res < 1) {
00206 res = -1;
00207 break;
00208 }
00209 f = ast_read(chan);
00210 if (!f) {
00211 ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
00212 res = -1;
00213 break;
00214 }
00215 if (f->frametype == AST_FRAME_DTMF) {
00216 ast_debug(1, "User pressed a key\n");
00217 if (intkeys && strchr(intkeys, f->subclass.integer)) {
00218 res = f->subclass.integer;
00219 ast_frfree(f);
00220 break;
00221 }
00222 }
00223 if (f->frametype == AST_FRAME_VOICE) {
00224
00225 needed = f->samples * 2;
00226 if (needed > sizeof(myf.frdata)) {
00227 ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
00228 (int)sizeof(myf.frdata) / 2, needed/2);
00229 needed = sizeof(myf.frdata);
00230 }
00231 res = read(fds[0], myf.frdata, needed);
00232 if (res > 0) {
00233 myf.f.frametype = AST_FRAME_VOICE;
00234 myf.f.subclass.codec = AST_FORMAT_SLINEAR;
00235 myf.f.datalen = res;
00236 myf.f.samples = res / 2;
00237 myf.f.offset = AST_FRIENDLY_OFFSET;
00238 myf.f.src = __PRETTY_FUNCTION__;
00239 myf.f.data.ptr = myf.frdata;
00240 if (ast_write(chan, &myf.f) < 0) {
00241 res = -1;
00242 ast_frfree(f);
00243 break;
00244 }
00245 if (res < needed) {
00246 ast_debug(1, "Last frame\n");
00247 res = 0;
00248 ast_frfree(f);
00249 break;
00250 }
00251 } else {
00252 ast_debug(1, "No more waveform\n");
00253 res = 0;
00254 }
00255 }
00256 ast_frfree(f);
00257 }
00258 }
00259 close(fds[0]);
00260 close(fds[1]);
00261
00262 if (!res && owriteformat)
00263 ast_set_write_format(chan, owriteformat);
00264 return res;
00265 }
00266
00267 static int festival_exec(struct ast_channel *chan, const char *vdata)
00268 {
00269 int usecache;
00270 int res = 0;
00271 struct sockaddr_in serv_addr;
00272 struct hostent *serverhost;
00273 struct ast_hostent ahp;
00274 int fd;
00275 FILE *fs;
00276 const char *host;
00277 const char *cachedir;
00278 const char *temp;
00279 const char *festivalcommand;
00280 int port = 1314;
00281 int n;
00282 char ack[4];
00283 char *waveform;
00284 int filesize;
00285 char bigstring[MAXFESTLEN];
00286 int i;
00287 struct MD5Context md5ctx;
00288 unsigned char MD5Res[16];
00289 char MD5Hex[33] = "";
00290 char koko[4] = "";
00291 char cachefile[MAXFESTLEN]="";
00292 int readcache = 0;
00293 int writecache = 0;
00294 int strln;
00295 int fdesc = -1;
00296 char buffer[16384];
00297 int seekpos = 0;
00298 char *data;
00299 struct ast_config *cfg;
00300 char *newfestivalcommand;
00301 struct ast_flags config_flags = { 0 };
00302 AST_DECLARE_APP_ARGS(args,
00303 AST_APP_ARG(text);
00304 AST_APP_ARG(interrupt);
00305 );
00306
00307 if (ast_strlen_zero(vdata)) {
00308 ast_log(LOG_WARNING, "festival requires an argument (text)\n");
00309 return -1;
00310 }
00311
00312 cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00313 if (!cfg) {
00314 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00315 return -1;
00316 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00317 ast_log(LOG_ERROR, "Config file " FESTIVAL_CONFIG " is in an invalid format. Aborting.\n");
00318 return -1;
00319 }
00320
00321 if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
00322 host = "localhost";
00323 }
00324 if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
00325 port = 1314;
00326 } else {
00327 port = atoi(temp);
00328 }
00329 if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
00330 usecache = 0;
00331 } else {
00332 usecache = ast_true(temp);
00333 }
00334 if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
00335 cachedir = "/tmp/";
00336 }
00337
00338 data = ast_strdupa(vdata);
00339 AST_STANDARD_APP_ARGS(args, data);
00340
00341 if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
00342 const char *startcmd = "(tts_textasterisk \"";
00343 const char *endcmd = "\" 'file)(quit)\n";
00344
00345 strln = strlen(startcmd) + strlen(args.text) + strlen(endcmd) + 1;
00346 newfestivalcommand = ast_alloca(strln);
00347 snprintf(newfestivalcommand, strln, "%s%s%s", startcmd, args.text, endcmd);
00348 festivalcommand = newfestivalcommand;
00349 } else {
00350 int x, j;
00351 newfestivalcommand = ast_alloca(strlen(festivalcommand) + strlen(args.text) + 1);
00352
00353 for (x = 0, j = 0; x < strlen(festivalcommand); x++) {
00354 if (festivalcommand[x] == '\\' && festivalcommand[x + 1] == 'n') {
00355 newfestivalcommand[j++] = '\n';
00356 x++;
00357 } else if (festivalcommand[x] == '\\') {
00358 newfestivalcommand[j++] = festivalcommand[x + 1];
00359 x++;
00360 } else if (festivalcommand[x] == '%' && festivalcommand[x + 1] == 's') {
00361 sprintf(&newfestivalcommand[j], "%s", args.text);
00362 j += strlen(args.text);
00363 x++;
00364 } else
00365 newfestivalcommand[j++] = festivalcommand[x];
00366 }
00367 newfestivalcommand[j] = '\0';
00368 festivalcommand = newfestivalcommand;
00369 }
00370
00371 if (args.interrupt && !strcasecmp(args.interrupt, "any"))
00372 args.interrupt = AST_DIGIT_ANY;
00373
00374 ast_debug(1, "Text passed to festival server : %s\n", args.text);
00375
00376
00377 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00378
00379 if (fd < 0) {
00380 ast_log(LOG_WARNING, "festival_client: can't get socket\n");
00381 ast_config_destroy(cfg);
00382 return -1;
00383 }
00384
00385 memset(&serv_addr, 0, sizeof(serv_addr));
00386
00387 if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
00388
00389 serverhost = ast_gethostbyname(host, &ahp);
00390
00391 if (serverhost == NULL) {
00392 ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
00393 ast_config_destroy(cfg);
00394 return -1;
00395 }
00396 memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
00397 }
00398
00399 serv_addr.sin_family = AF_INET;
00400 serv_addr.sin_port = htons(port);
00401
00402 if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
00403 ast_log(LOG_WARNING, "festival_client: connect to server failed\n");
00404 ast_config_destroy(cfg);
00405 return -1;
00406 }
00407
00408
00409 MD5Init(&md5ctx);
00410 MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text));
00411 MD5Final(MD5Res, &md5ctx);
00412 MD5Hex[0] = '\0';
00413
00414
00415
00416 for (i = 0; i < 16; i++) {
00417 snprintf(koko, sizeof(koko), "%X", MD5Res[i]);
00418 strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1);
00419 }
00420 readcache = 0;
00421 writecache = 0;
00422 if (strlen(cachedir) + strlen(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
00423 snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
00424 fdesc = open(cachefile, O_RDWR);
00425 if (fdesc == -1) {
00426 fdesc = open(cachefile, O_CREAT | O_RDWR, AST_FILE_MODE);
00427 if (fdesc != -1) {
00428 writecache = 1;
00429 strln = strlen(args.text);
00430 ast_debug(1, "line length : %d\n", strln);
00431 if (write(fdesc,&strln,sizeof(int)) < 0) {
00432 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00433 }
00434 if (write(fdesc,data,strln) < 0) {
00435 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00436 }
00437 seekpos = lseek(fdesc, 0, SEEK_CUR);
00438 ast_debug(1, "Seek position : %d\n", seekpos);
00439 }
00440 } else {
00441 if (read(fdesc,&strln,sizeof(int)) != sizeof(int)) {
00442 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00443 }
00444 ast_debug(1, "Cache file exists, strln=%d, strlen=%d\n", strln, (int)strlen(args.text));
00445 if (strlen(args.text) == strln) {
00446 ast_debug(1, "Size OK\n");
00447 if (read(fdesc,&bigstring,strln) != strln) {
00448 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00449 }
00450 bigstring[strln] = 0;
00451 if (strcmp(bigstring, args.text) == 0) {
00452 readcache = 1;
00453 } else {
00454 ast_log(LOG_WARNING, "Strings do not match\n");
00455 }
00456 } else {
00457 ast_log(LOG_WARNING, "Size mismatch\n");
00458 }
00459 }
00460 }
00461
00462 if (readcache == 1) {
00463 close(fd);
00464 fd = fdesc;
00465 ast_debug(1, "Reading from cache...\n");
00466 } else {
00467 ast_debug(1, "Passing text to festival...\n");
00468 fs = fdopen(dup(fd), "wb");
00469
00470 fprintf(fs, "%s", festivalcommand);
00471 fflush(fs);
00472 fclose(fs);
00473 }
00474
00475
00476 if (writecache == 1) {
00477 ast_debug(1, "Writing result to cache...\n");
00478 while ((strln = read(fd, buffer, 16384)) != 0) {
00479 if (write(fdesc,buffer,strln) < 0) {
00480 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00481 }
00482 }
00483 close(fd);
00484 close(fdesc);
00485 fd = open(cachefile, O_RDWR);
00486 lseek(fd, seekpos, SEEK_SET);
00487 }
00488
00489 ast_debug(1, "Passing data to channel...\n");
00490
00491
00492
00493 do {
00494 int read_data;
00495 for (n = 0; n < 3; ) {
00496 read_data = read(fd, ack + n, 3 - n);
00497
00498
00499
00500 if (read_data == -1) {
00501 ast_log(LOG_WARNING, "Unable to read from cache/festival fd\n");
00502 close(fd);
00503 ast_config_destroy(cfg);
00504 return -1;
00505 }
00506 n += read_data;
00507 }
00508 ack[3] = '\0';
00509 if (strcmp(ack, "WV\n") == 0) {
00510 ast_debug(1, "Festival WV command\n");
00511 if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00512 res = send_waveform_to_channel(chan, waveform, filesize, args.interrupt);
00513 ast_free(waveform);
00514 }
00515 break;
00516 } else if (strcmp(ack, "LP\n") == 0) {
00517 ast_debug(1, "Festival LP command\n");
00518 if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
00519 waveform[filesize] = '\0';
00520 ast_log(LOG_WARNING, "Festival returned LP : %s\n", waveform);
00521 ast_free(waveform);
00522 }
00523 } else if (strcmp(ack, "ER\n") == 0) {
00524 ast_log(LOG_WARNING, "Festival returned ER\n");
00525 res = -1;
00526 break;
00527 }
00528 } while (strcmp(ack, "OK\n") != 0);
00529 close(fd);
00530 ast_config_destroy(cfg);
00531 return res;
00532 }
00533
00534 static int unload_module(void)
00535 {
00536 return ast_unregister_application(app);
00537 }
00538
00539 static int load_module(void)
00540 {
00541 struct ast_flags config_flags = { 0 };
00542 struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
00543 if (!cfg) {
00544 ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
00545 return AST_MODULE_LOAD_DECLINE;
00546 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00547 ast_log(LOG_ERROR, "Config file " FESTIVAL_CONFIG " is in an invalid format. Aborting.\n");
00548 return AST_MODULE_LOAD_DECLINE;
00549 }
00550 ast_config_destroy(cfg);
00551 return ast_register_application_xml(app, festival_exec);
00552 }
00553
00554 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Festival Interface");