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