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