#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 540 of file app_festival.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 540 of file app_festival.c.
static int festival_exec | ( | struct ast_channel * | chan, | |
void * | vdata | |||
) | [static] |
Definition at line 258 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().
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 { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */ 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); /* we know it is big enough */ 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 /* Connect to local festival server */ 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 /* its a name rather than an ipnum */ 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 /* Compute MD5 sum of string */ 00397 MD5Init(&md5ctx); 00398 MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text)); 00399 MD5Final(MD5Res, &md5ctx); 00400 MD5Hex[0] = '\0'; 00401 00402 /* Convert to HEX and look if there is any matching file in the cache 00403 directory */ 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 /* Write to cache and then pass it down */ 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 /* Read back info from server */ 00480 /* This assumes only one waveform will come back, also LP is unlikely */ 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 /* this avoids falling in infinite loop 00487 * in case that festival server goes down 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) { /* receive a waveform */ 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) { /* receive an s-expr */ 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) { /* server got an error */ 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 }
static int load_module | ( | void | ) | [static] |
Definition at line 528 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.
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 }
static int send_waveform_to_channel | ( | struct ast_channel * | chan, | |
char * | waveform, | |||
int | length, | |||
char * | intkeys | |||
) | [static] |
Definition at line 151 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().
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 /* Answer if it's not already going */ 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 /* Order is important -- there's almost always going to be mp3... we want to prioritize the 00189 user */ 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 /* Treat as a generator */ 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) { /* last frame */ 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 }
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 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 }
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 523 of file app_festival.c.
References ast_unregister_application().
00524 { 00525 return ast_unregister_application(app); 00526 }
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 540 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 540 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.