#include "asterisk.h"
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/md5.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
Go to the source code of this file.
Defines | |
#define | FESTIVAL_CONFIG "festival.conf" |
#define | MAXFESTLEN 2048 |
#define | MAXLEN 180 |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | festival_exec (struct ast_channel *chan, void *vdata) |
static int | load_module (void) |
static int | send_waveform_to_channel (struct ast_channel *chan, char *waveform, int length, char *intkeys) |
static int | send_waveform_to_fd (char *waveform, int length, int fd) |
static char * | socket_receive_file_to_buff (int fd, int *size) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple Festival Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } |
static char * | app = "Festival" |
static struct ast_module_info * | ast_module_info = &__mod_info |
static char * | descrip |
static char * | synopsis = "Say text to the user" |
Definition in file app_festival.c.
#define FESTIVAL_CONFIG "festival.conf" |
#define MAXFESTLEN 2048 |
#define MAXLEN 180 |
Definition at line 54 of file app_festival.c.
static void __reg_module | ( | void | ) | [static] |
Definition at line 541 of file app_festival.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 541 of file app_festival.c.
static int festival_exec | ( | struct ast_channel * | chan, | |
void * | vdata | |||
) | [static] |
Definition at line 259 of file app_festival.c.
References ahp, AST_APP_ARG, ast_config_destroy(), ast_config_load, ast_debug, AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, AST_FILE_MODE, ast_free, ast_gethostbyname(), ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_true(), ast_variable_retrieve(), chan, config_flags, errno, FESTIVAL_CONFIG, LOG_WARNING, MAXFESTLEN, MD5Final(), MD5Init(), MD5Update(), send_waveform_to_channel(), socket_receive_file_to_buff(), text, and wave.
Referenced by load_module().
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 { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */ 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); /* we know it is big enough */ 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 /* Connect to local festival server */ 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 /* its a name rather than an ipnum */ 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 /* Compute MD5 sum of string */ 00398 MD5Init(&md5ctx); 00399 MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text)); 00400 MD5Final(MD5Res, &md5ctx); 00401 MD5Hex[0] = '\0'; 00402 00403 /* Convert to HEX and look if there is any matching file in the cache 00404 directory */ 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 /* Write to cache and then pass it down */ 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 /* Read back info from server */ 00481 /* This assumes only one waveform will come back, also LP is unlikely */ 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 /* this avoids falling in infinite loop 00488 * in case that festival server goes down 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) { /* receive a waveform */ 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) { /* receive an s-expr */ 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) { /* server got an error */ 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 }
static int load_module | ( | void | ) | [static] |
Definition at line 529 of file app_festival.c.
References ast_config_destroy(), ast_config_load, ast_log(), AST_MODULE_LOAD_DECLINE, ast_register_application, config_flags, FESTIVAL_CONFIG, festival_exec(), and LOG_WARNING.
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 }
static int send_waveform_to_channel | ( | struct ast_channel * | chan, | |
char * | waveform, | |||
int | length, | |||
char * | intkeys | |||
) | [static] |
Definition at line 152 of file app_festival.c.
References ast_channel::_state, ast_answer(), ast_debug, AST_FORMAT_SLINEAR, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, AST_FRIENDLY_OFFSET, ast_indicate(), ast_log(), ast_read(), ast_set_write_format(), AST_STATE_UP, ast_stopstream(), ast_waitfor(), ast_write(), chan, f, LOG_WARNING, ast_frame::offset, and send_waveform_to_fd().
Referenced by festival_exec().
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 /* Answer if it's not already going */ 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 /* Order is important -- there's almost always going to be mp3... we want to prioritize the 00190 user */ 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 /* Treat as a generator */ 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) { /* last frame */ 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 }
static int send_waveform_to_fd | ( | char * | waveform, | |
int | length, | |||
int | fd | |||
) | [static] |
Definition at line 118 of file app_festival.c.
References ast_close_fds_above_n(), ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), errno, and LOG_WARNING.
Referenced by send_waveform_to_channel().
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 }
static char* socket_receive_file_to_buff | ( | int | fd, | |
int * | size | |||
) | [static] |
Definition at line 67 of file app_festival.c.
References ast_free, ast_malloc, ast_realloc, and buff.
Referenced by festival_exec().
00068 { 00069 /* Receive file (probably a waveform file) from socket using 00070 * Festival key stuff technique, but long winded I know, sorry 00071 * but will receive any file without closing the stream or 00072 * using OOB data 00073 */ 00074 static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's 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; /* hit stream eof before end of file */ 00089 if ((*size) + k + 1 >= bufflen) { 00090 /* +1 so you can add a terminating NULL if you want */ 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 /* It looked like the key but wasn't */ 00102 for (i = 0; i < k; i++, (*size)++) 00103 buff[*size] = file_stuff_key[i]; 00104 k = 0; 00105 /* omit the stuffed 'X' */ 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 }
static int unload_module | ( | void | ) | [static] |
Definition at line 524 of file app_festival.c.
References ast_unregister_application().
00525 { 00526 return ast_unregister_application(app); 00527 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple Festival Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 541 of file app_festival.c.
char* app = "Festival" [static] |
Definition at line 57 of file app_festival.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 541 of file app_festival.c.
char* descrip [static] |
Initial value:
" Festival(text[,intkeys]): Connect to Festival, send the argument, get back the waveform,\n" "play it to the user, allowing any given interrupt keys to immediately terminate and return\n" "the value, or 'any' to allow any number back (useful in dialplan)\n"
Definition at line 61 of file app_festival.c.
Definition at line 59 of file app_festival.c.