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