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