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