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