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