Version 0.2.0 from FTP

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@523 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.0
Mark Spencer 23 years ago
parent bdecee176e
commit e42d61fac6

@ -0,0 +1,22 @@
app_festival is an application that allows one to send text-to-speech commands
to a background festival server, and to obtain the resulting waveform which
gets sent down to the respective channel. app_festival also employs a waveform
cache, so invariant text-to-speech strings ("Please press 1 for instructions")
do not need to be dynamically generated all the time.
You need :
1) festival, patched to produce 8khz waveforms on output. Patch for Festival
1.4.1 RELEASE are included. The patch adds a new command to festival
(asterisk_tts).
2) My patches to asterisk that provide variable substitution and quoting to
the Asterisk Extension Logic. This is not really a requirement, but without
this, app_festival is mostly useless (you could very well use prerecorded
voices for static information).
3) Before running asterisk, you have to run festival-server with a command
like :
/usr/local/festival/bin/festival --server > /dev/null 2>&1 &

@ -15,7 +15,11 @@
APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_intercom.so app_mp3.so \
app_system.so app_echo.so app_record.so app_image.so app_url.so app_disa.so \
app_agi.so app_qcall.so app_adsiprog.so app_getcpeid.so app_milliwatt.so \
app_zapateller.so
app_zapateller.so app_datetime.so app_setcallerid.so app_festival.so \
app_queue.so
#APPS+=app_sql_postgres.so
#APPS+=app_sql_odbc.so
APPS+=$(shell if [ -f /usr/include/zap.h ]; then echo "app_zapras.so app_meetme.so" ; fi)
@ -38,6 +42,14 @@ app_todd.o: app_todd.c
app_todd.so: app_todd.o
$(CC) -shared -Xlinker -x -o $@ $< -L/usr/local/ssl/lib -lssl -lcrypto
app_sql_postgres.o: app_sql_postgres.c
$(CC) -pipe -I/usr/local/pgsql/include -Wall -Wmissing-prototypes -Wmissing-declarations -O6 -g -Iinclude -I../include -D_REENTRANT -D_GNU_SOURCE -march=i686 -DASTERISK_VERSION=\"CVS-07/21/02-14:49:14\" -DDO_CRASH -DDEBUG_THREADS -c -o app_sql_postgres.o app_sql_postgres.c
app_sql_postgres.so: app_sql_postgres.o
$(CC) -shared -Xlinker -x -o $@ $< -L/usr/local/pgsql/lib -lpq
app_sql_odbc.so: app_sql_odbc.o
$(CC) -shared -Xlinker -x -o $@ $< -lodbc
look: look.c
gcc -pipe -O6 -g look.c -o look -lncurses

@ -0,0 +1,480 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Connect to festival
*
* Copyright (C) 2002, Christos Ricudis
*
* Christos Ricudis <ricudis@paiko.gr>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/module.h>
#include <asterisk/md5.h>
#include <asterisk/config.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <pthread.h>
#define FESTIVAL_CONFIG "festival.conf"
static char *tdesc = "Simple Festival Interface";
static char *app = "Festival";
static char *synopsis = "Say text to the user";
static char *descrip =
" Festival(): Connect to Festival, send the argument, get back the waveform,"
"play it to the user.\n";
STANDARD_LOCAL_USER;
LOCAL_USER_DECL;
static char *socket_receive_file_to_buff(int fd,int *size)
{
/* Receive file (probably a waveform file) from socket using */
/* Festival key stuff technique, but long winded I know, sorry */
/* but will receive any file without closeing the stream or */
/* using OOB data */
static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
char *buff;
int bufflen;
int n,k,i;
char c;
bufflen = 1024;
buff = (char *)malloc(bufflen);
*size=0;
for (k=0; file_stuff_key[k] != '\0';)
{
n = read(fd,&c,1);
if (n==0) break; /* hit stream eof before end of file */
if ((*size)+k+1 >= bufflen)
{ /* +1 so you can add a NULL if you want */
bufflen += bufflen/4;
buff = (char *)realloc(buff,bufflen);
}
if (file_stuff_key[k] == c)
k++;
else if ((c == 'X') && (file_stuff_key[k+1] == '\0'))
{ /* It looked like the key but wasn't */
for (i=0; i < k; i++,(*size)++)
buff[*size] = file_stuff_key[i];
k=0;
/* omit the stuffed 'X' */
}
else
{
for (i=0; i < k; i++,(*size)++)
buff[*size] = file_stuff_key[i];
k=0;
buff[*size] = c;
(*size)++;
}
}
return buff;
}
static int send_waveform_to_fd(char *waveform, int length, int fd) {
int res;
int x;
res = fork();
if (res < 0)
ast_log(LOG_WARNING, "Fork failed\n");
if (res)
return res;
for (x=0;x<256;x++) {
if (x != fd)
close(x);
}
write(fd,waveform,length);
write(fd,"a",1);
close(fd);
exit(0);
}
static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length) {
int res=0;
int fds[2];
int rfds[1 + AST_MAX_FDS];
int ms = -1;
int pid = -1;
int us;
int exception;
int owriteformat;
struct timeval tv;
struct timeval last;
struct ast_frame *f;
int x;
struct myframe {
struct ast_frame f;
char offset[AST_FRIENDLY_OFFSET];
char frdata[160];
} myf;
last.tv_usec = 0;
last.tv_sec = 0;
if (pipe(fds)) {
ast_log(LOG_WARNING, "Unable to create pipe\n");
return -1;
}
ast_stopstream(chan);
owriteformat = chan->writeformat;
res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
return -1;
}
res=send_waveform_to_fd(waveform,length,fds[1]);
if (res >= 0) {
pid = res;
/* Order is important -- there's almost always going to be mp3... we want to prioritize the
user */
rfds[AST_MAX_FDS] = fds[0];
for (;;) {
CHECK_BLOCKING(chan);
for (x=0;x<AST_MAX_FDS;x++)
rfds[x] = chan->fds[x];
res = ast_waitfor_n_fd(rfds, AST_MAX_FDS+1, &ms, &exception);
chan->blocking = 0;
if (res < 1) {
ast_log(LOG_DEBUG, "Hangup detected\n");
res = -1;
break;
}
for(x=0;x<AST_MAX_FDS;x++)
if (res == chan->fds[x])
break;
if (x < AST_MAX_FDS) {
if (exception)
chan->exception = 1;
f = ast_read(chan);
if (!f) {
ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
res = -1;
break;
}
if (f->frametype == AST_FRAME_DTMF) {
ast_log(LOG_DEBUG, "User pressed a key\n");
ast_frfree(f);
res = 0;
break;
}
ast_frfree(f);
} else if (res == fds[0]) {
gettimeofday(&tv, NULL);
if (last.tv_sec || last.tv_usec) {
/* We should wait at least a frame length */
us = sizeof(myf.frdata) / 16 * 1000;
/* Subtract 1,000,000 us for each second late we've passed */
us -= (tv.tv_sec - last.tv_sec) * 1000000;
/* And one for each us late we've passed */
us -= (tv.tv_usec - last.tv_usec);
/* Sleep that long if needed */
if (us > 0)
usleep(us);
}
last = tv;
res = read(fds[0], myf.frdata, sizeof(myf.frdata));
if (res > 0) {
myf.f.frametype = AST_FRAME_VOICE;
myf.f.subclass = AST_FORMAT_SLINEAR;
myf.f.datalen = res;
myf.f.timelen = res / 16;
myf.f.mallocd = 0;
myf.f.offset = AST_FRIENDLY_OFFSET;
myf.f.src = __PRETTY_FUNCTION__;
myf.f.data = myf.frdata;
if (ast_write(chan, &myf.f) < 0) {
res = -1;
break;
}
if (res < sizeof(myf.frdata)) { // last frame
ast_log(LOG_WARNING, "Last frame\n");
res=0;
break;
}
} else {
ast_log(LOG_WARNING, "No more waveform\n");
res = 0;
}
} else {
ast_log(LOG_DEBUG, "HuhHHH?\n");
res = -1;
break;
}
}
}
close(fds[0]);
close(fds[1]);
// if (pid > -1)
// kill(pid, SIGKILL);
if (!res && owriteformat)
ast_set_write_format(chan, owriteformat);
return res;
}
#define MAXLEN 180
#define MAXFESTLEN 2048
static int festival_exec(struct ast_channel *chan, void *data)
{
int usecache;
int res=0;
struct localuser *u;
struct sockaddr_in serv_addr;
struct hostent *serverhost;
int fd;
FILE *fs;
char *host;
char *cachedir;
char *temp;
char *festivalcommand;
int port=1314;
int n;
char ack[4];
char *waveform;
int filesize;
int wave;
char bigstring[MAXFESTLEN];
int i;
struct MD5Context md5ctx;
unsigned char MD5Res[16];
char MD5Hex[32];
char koko[4];
char cachefile[MAXFESTLEN];
int readcache=0;
int writecache=0;
int strln;
int fdesc;
char buffer[16384];
int seekpos;
struct ast_config *cfg;
cfg = ast_load(FESTIVAL_CONFIG);
if (!cfg) {
ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
return -1;
}
if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
host = "localhost";
}
if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
port = 1314;
} else {
port = atoi(temp);
}
if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
usecache=0;
} else {
if (strcasecmp(temp,"yes")==0) {
usecache=1;
}
}
if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
cachedir = "/tmp/";
}
if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n";
}
if (!data) {
ast_log(LOG_WARNING, "festival requires an argument (text)\n");
return -1;
}
LOCAL_USER_ADD(u);
ast_log(LOG_WARNING, "Text passed to festival server : %s\n",(char *)data);
/* Connect to local festival server */
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
ast_log(LOG_WARNING,"festival_client: can't get socket\n");
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
/* its a name rather than an ipnum */
serverhost = gethostbyname(host);
if (serverhost == (struct hostent *)0) {
ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n");
return -1;
}
memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
ast_log(LOG_WARNING,"festival_client: connect to server failed\n");
return -1;
}
/* Compute MD5 sum of string */
MD5Init(&md5ctx);
MD5Update(&md5ctx,(unsigned char const *)data,strlen(data));
MD5Final(MD5Res,&md5ctx);
strcpy(MD5Hex,"");
/* Convert to HEX and look if there is any matching file in the cache
directory */
for (i=0;i<16;i++) {
sprintf(koko,"%X",MD5Res[i]);
strcat(MD5Hex,koko);
}
readcache=0;
writecache=0;
if (strlen(cachedir)+strlen(MD5Hex)+1<=MAXFESTLEN && (usecache==1)) {
sprintf(cachefile,"%s/%s",cachedir,MD5Hex);
fdesc=open(cachefile,O_RDWR);
if (fdesc==-1) {
fdesc=open(cachefile,O_CREAT|O_RDWR,0);
if (fdesc!=-1) {
writecache=1;
strln=strlen((char *)data);
ast_log(LOG_WARNING,"line length : %d\n",strln);
write(fdesc,&strln,sizeof(int));
write(fdesc,data,strln);
seekpos=lseek(fdesc,0,SEEK_CUR);
ast_log(LOG_WARNING,"Seek position : %d\n",seekpos);
}
} else {
read(fdesc,&strln,sizeof(int));
ast_log(LOG_WARNING,"Cache file exists, strln=%d, strlen=%d\n",strln,strlen((char *)data));
if (strlen((char *)data)==strln) {
ast_log(LOG_WARNING,"Size OK\n");
read(fdesc,&bigstring,strln);
if (strcmp(bigstring,data)==0) {
readcache=1;
} else {
ast_log(LOG_WARNING,"Strings do not match\n");
}
} else {
ast_log(LOG_WARNING,"Size mismatch\n");
}
}
}
if (readcache==1) {
close(fd);
fd=fdesc;
ast_log(LOG_WARNING,"Reading from cache...\n");
} else {
ast_log(LOG_WARNING,"Passing text to festival...\n");
fs=fdopen(dup(fd),"wb");
fprintf(fs,festivalcommand,(char *)data);
fflush(fs);
fclose(fs);
}
/* Write to cache and then pass it down */
if (writecache==1) {
ast_log(LOG_WARNING,"Writing result to cache...\n");
while ((strln=read(fd,buffer,16384))!=0) {
write(fdesc,buffer,strln);
}
close(fd);
close(fdesc);
fd=open(cachefile,O_RDWR);
lseek(fd,seekpos,SEEK_SET);
}
ast_log(LOG_WARNING,"Passing data to channel...\n");
/* Read back info from server */
/* This assumes only one waveform will come back, also LP is unlikely */
wave = 0;
do {
for (n=0; n < 3; )
n += read(fd,ack+n,3-n);
ack[3] = '\0';
if (strcmp(ack,"WV\n") == 0) { /* receive a waveform */
ast_log(LOG_WARNING,"Festival WV command");
waveform = socket_receive_file_to_buff(fd,&filesize);
send_waveform_to_channel(chan,waveform,filesize);
free(waveform);
res=0;
break;
}
else if (strcmp(ack,"LP\n") == 0) { /* receive an s-expr */
ast_log(LOG_WARNING,"Festival LP command");
waveform = socket_receive_file_to_buff(fd,&filesize);
waveform[filesize]='\0';
ast_log(LOG_WARNING,"Festival returned LP : %s\n",waveform);
free(waveform);
} else if (strcmp(ack,"ER\n") == 0) { /* server got an error */
ast_log(LOG_WARNING,"Festival returned ER\n");
res=-1;
break;
}
} while (strcmp(ack,"OK\n") != 0);
close(fd);
LOCAL_USER_REMOVE(u);
return res;
}
int unload_module(void)
{
STANDARD_HANGUP_LOCALUSERS;
return ast_unregister_application(app);
}
int load_module(void)
{
return ast_register_application(app, festival_exec, synopsis, descrip);
}
char *description(void)
{
return tdesc;
}
int usecount(void)
{
int res;
STANDARD_USECOUNT(res);
return res;
}
char *key()
{
return ASTERISK_GPL_KEY;
}

@ -0,0 +1,546 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Connect to PostgreSQL
*
* Copyright (C) 2002, Christos Ricudis
*
* Christos Ricudis <ricudis@paiko.gr>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/module.h>
#include <asterisk/linkedlists.h>
#include <asterisk/chanvars.h>
#include <asterisk/lock.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include "libpq-fe.h"
static char *tdesc = "Simple PostgreSQL Interface";
static char *app = "PGSQL";
static char *synopsis = "Do several SQLy things";
static char *descrip =
" PGSQL(): Do several SQLy things\n";
/*
Syntax of SQL commands :
Connect #var option-string
Connects to a database using the option-string and stores the
connection identifier in $var
Query var connection-identifier query-string
Submits query-string to database backend and stores the result
identifier in ${var}
Fetch statusvar result-identifier var1 var2 var3 ... varn
Fetches a row from the query and stores end-of-table status in
${statusvar} and columns in ${var1}..${varn}
Clear result-identifier
Clears data structures associated with result-identifier
Disconnect connection-identifier
Disconnects from named connection
EXAMPLES OF USE :
(
$2 = Connection Identifier
$3 = Result Identifier
$4 = Fetch Status Identifier (0 = no more rows)
$5, $6 = Data variables
)
exten => s,2,PGSQL,"Connect connid host=localhost user=asterisk dbname=credit";
exten => s,3,PGSQL,"Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${callerid}";
exten => s,4,PGSQL,"Fetch fetchid ${resultid} datavar1 datavar2";
exten => s,5,GotoIf,"${fetchid}=1?s|6:s|8";
exten => s,6,blablabla ${datavar1} ${datavar2} (does blablabla, datavar1 = username, datavar2 = credit);
exten => s,7,Goto,s|4
exten => s,8,PGSQL,"Clear ${resultid}";
exten => s,9,PGSQL,"Disconnect ${connid}";
*/
STANDARD_LOCAL_USER;
LOCAL_USER_DECL;
extern void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value);
#define AST_PGSQL_ID_DUMMY 0
#define AST_PGSQL_ID_CONNID 1
#define AST_PGSQL_ID_RESID 2
#define AST_PGSQL_ID_FETCHID 3
struct ast_PGSQL_id {
int identifier_type; /* 0=dummy, 1=connid, 2=resultid */
int identifier;
void *data;
AST_LIST_ENTRY(ast_PGSQL_id) entries;
} *ast_PGSQL_id;
AST_LIST_HEAD(PGSQLidshead,ast_PGSQL_id) PGSQLidshead;
static void *find_identifier(int identifier,int identifier_type) {
struct PGSQLidshead *headp;
struct ast_PGSQL_id *i;
void *res=NULL;
int found=0;
headp=&PGSQLidshead;
if (AST_LIST_LOCK(headp)) {
ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
} else {
AST_LIST_TRAVERSE(headp,i,entries) {
if ((i->identifier==identifier) && (i->identifier_type==identifier_type)) {
found=1;
res=i->data;
break;
}
}
if (!found) {
ast_log(LOG_WARNING,"Identifier %d, identifier_type %d not found in identifier list\n",identifier,identifier_type);
}
AST_LIST_UNLOCK(headp);
}
return(res);
}
static int add_identifier(int identifier_type,void *data) {
struct ast_PGSQL_id *i,*j;
struct PGSQLidshead *headp;
int maxidentifier=0;
headp=&PGSQLidshead;
i=NULL;
j=NULL;
if (AST_LIST_LOCK(headp)) {
ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
return(-1);
} else {
i=malloc(sizeof(struct ast_PGSQL_id));
AST_LIST_TRAVERSE(headp,j,entries) {
if (j->identifier>maxidentifier) {
maxidentifier=j->identifier;
}
}
i->identifier=maxidentifier+1;
i->identifier_type=identifier_type;
i->data=data;
AST_LIST_INSERT_HEAD(headp,i,entries);
AST_LIST_UNLOCK(headp);
}
return(i->identifier);
}
static int del_identifier(int identifier,int identifier_type) {
struct ast_PGSQL_id *i;
struct PGSQLidshead *headp;
int found=0;
headp=&PGSQLidshead;
if (AST_LIST_LOCK(headp)) {
ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
} else {
AST_LIST_TRAVERSE(headp,i,entries) {
if ((i->identifier==identifier) &&
(i->identifier_type==identifier_type)) {
AST_LIST_REMOVE(headp,i,ast_PGSQL_id,entries);
free(i);
found=1;
break;
}
}
AST_LIST_UNLOCK(headp);
}
if (found==0) {
ast_log(LOG_WARNING,"Could not find identifier %d, identifier_type %d in list to delete\n",identifier,identifier_type);
return(-1);
} else {
return(0);
}
}
static int aPGSQL_connect(struct ast_channel *chan, void *data) {
char *ptrptr;
char *s1,*s4;
char s[100];
char *optionstring;
char *var;
int l;
int res;
PGconn *karoto;
int id;
res=0;
l=strlen(data)+2;
s1=malloc(l);
strncpy(s1,data,l);
strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P
var=strtok_r(NULL," ",&ptrptr);
optionstring=strtok_r(NULL,"\n",&ptrptr);
karoto = PQconnectdb(optionstring);
if (PQstatus(karoto) == CONNECTION_BAD) {
ast_log(LOG_WARNING,"Connection to database using '%s' failed. postgress reports : %s\n", optionstring,
PQerrorMessage(karoto));
res=-1;
} else {
ast_log(LOG_WARNING,"adding identifier\n");
id=add_identifier(AST_PGSQL_ID_CONNID,karoto);
s4=&s[0];
sprintf(s4,"%d",id);
pbx_builtin_setvar_helper(chan,var,s);
}
free(s1);
return res;
}
static int aPGSQL_query(struct ast_channel *chan, void *data) {
char *ptrptr;
char *s1,*s2,*s3,*s4,*s5;
char s[100];
char *querystring;
char *var;
int l;
int res,nres;
PGconn *karoto;
PGresult *PGSQLres;
int id,id1;
res=0;
l=strlen(data)+2;
s1=malloc(l);
s2=malloc(l);
strcpy(s1,data);
strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P
s3=strtok_r(NULL," ",&ptrptr);
while (1) { // ugly trick to make branches with break;
var=s3;
s4=strtok_r(NULL," ",&ptrptr);
id=atoi(s4);
querystring=strtok_r(NULL,"\n",&ptrptr);
if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) {
ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_query\n",id);
res=-1;
break;
}
PGSQLres=PQexec(karoto,querystring);
if (PGSQLres==NULL) {
ast_log(LOG_WARNING,"aPGSQL_query: Connection Error (connection identifier = %d, error message : %s)\n",id,PQerrorMessage(karoto));
res=-1;
break;
}
if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
ast_log(LOG_WARNING,"aPGSQL_query: Query Error (connection identifier : %d, error message : %s)\n",id,PQcmdStatus(PGSQLres));
res=-1;
break;
}
nres=PQnfields(PGSQLres);
id1=add_identifier(AST_PGSQL_ID_RESID,PGSQLres);
s5=&s[0];
sprintf(s5,"%d",id1);
pbx_builtin_setvar_helper(chan,var,s);
break;
}
free(s1);
free(s2);
return(res);
}
static int aPGSQL_fetch(struct ast_channel *chan, void *data) {
char *ptrptr;
char *s1,*s2,*s3,*s4,*s5,*s6,*s7;
char s[100];
char *var;
int l;
int res;
PGresult *PGSQLres;
int id,id1,i,j,fnd;
int *lalares=NULL;
int nres;
struct ast_var_t *variables;
struct varshead *headp;
headp=&chan->varshead;
res=0;
l=strlen(data)+2;
s7=NULL;
s1=malloc(l);
s2=malloc(l);
strcpy(s1,data);
strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P
s3=strtok_r(NULL," ",&ptrptr);
while (1) { // ugly trick to make branches with break;
var=s3; // fetchid
fnd=0;
AST_LIST_TRAVERSE(headp,variables,entries) {
if (strncasecmp(ast_var_name(variables),s3,strlen(s3))==0) {
s7=ast_var_value(variables);
fnd=1;
break;
}
}
if (fnd==0) {
s7="0";
pbx_builtin_setvar_helper(chan,s3,s7);
}
s4=strtok_r(NULL," ",&ptrptr);
id=atoi(s4); // resultid
if ((PGSQLres=find_identifier(id,AST_PGSQL_ID_RESID))==NULL) {
ast_log(LOG_WARNING,"Invalid result identifier %d passed in aPGSQL_fetch\n",id);
res=-1;
break;
}
id=atoi(s7); //fetchid
if ((lalares=find_identifier(id,AST_PGSQL_ID_FETCHID))==NULL) {
i=0;
} else {
i=*lalares;
free(lalares);
del_identifier(id,AST_PGSQL_ID_FETCHID);
}
nres=PQnfields(PGSQLres);
ast_log(LOG_WARNING,"ast_PGSQL_fetch : nres = %d i = %d ;\n",nres,i);
for (j=0;j<nres;j++) {
s5=strtok_r(NULL," ",&ptrptr);
if (s5==NULL) {
ast_log(LOG_WARNING,"ast_PGSQL_fetch : More tuples (%d) than variables (%d)\n",nres,j);
break;
}
s6=PQgetvalue(PGSQLres,i,j);
if (s6==NULL) {
ast_log(LOG_WARNING,"PWgetvalue(res,%d,%d) returned NULL in ast_PGSQL_fetch\n",i,j);
break;
}
ast_log(LOG_WARNING,"===setting variable '%s' to '%s'\n",s5,s6);
pbx_builtin_setvar_helper(chan,s5,s6);
}
i++;
if (i<PQntuples(PGSQLres)) {
lalares=malloc(sizeof(int));
*lalares=i;
id1=add_identifier(AST_PGSQL_ID_FETCHID,lalares);
} else {
id1=0;
}
s5=&s[0];
sprintf(s5,"%d",id1);
ast_log(LOG_WARNING,"Setting var '%s' to value '%s'\n",s3,s);
pbx_builtin_setvar_helper(chan,s3,s);
break;
}
free(s1);
free(s2);
return(res);
}
static int aPGSQL_reset(struct ast_channel *chan, void *data) {
char *ptrptr;
char *s1,*s3;
int l;
PGconn *karoto;
int id;
l=strlen(data)+2;
s1=malloc(l);
strcpy(s1,data);
strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P
s3=strtok_r(NULL," ",&ptrptr);
id=atoi(s3);
if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) {
ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_reset\n",id);
} else {
PQreset(karoto);
}
free(s1);
return(0);
}
static int aPGSQL_clear(struct ast_channel *chan, void *data) {
char *ptrptr;
char *s1,*s3;
int l;
PGresult *karoto;
int id;
l=strlen(data)+2;
s1=malloc(l);
strcpy(s1,data);
strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P
s3=strtok_r(NULL," ",&ptrptr);
id=atoi(s3);
if ((karoto=find_identifier(id,AST_PGSQL_ID_RESID))==NULL) {
ast_log(LOG_WARNING,"Invalid result identifier %d passed in aPGSQL_clear\n",id);
} else {
PQclear(karoto);
del_identifier(id,AST_PGSQL_ID_RESID);
}
free(s1);
return(0);
}
static int aPGSQL_disconnect(struct ast_channel *chan, void *data) {
char *ptrptr;
char *s1,*s3;
int l;
PGconn *karoto;
int id;
l=strlen(data)+2;
s1=malloc(l);
strcpy(s1,data);
strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P
s3=strtok_r(NULL," ",&ptrptr);
id=atoi(s3);
if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) {
ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_disconnect\n",id);
} else {
PQfinish(karoto);
del_identifier(id,AST_PGSQL_ID_CONNID);
}
free(s1);
return(0);
}
static int aPGSQL_debug(struct ast_channel *chan, void *data) {
ast_log(LOG_WARNING,"Debug : %s\n",(char *)data);
return(0);
}
static int PGSQL_exec(struct ast_channel *chan, void *data)
{
struct localuser *u;
int result;
if (!data) {
ast_log(LOG_WARNING, "APP_PGSQL requires an argument (see manual)\n");
return -1;
}
LOCAL_USER_ADD(u);
result=0;
if (strncasecmp("connect",data,strlen("connect"))==0) {
result=(aPGSQL_connect(chan,data));
} else if (strncasecmp("query",data,strlen("query"))==0) {
result=(aPGSQL_query(chan,data));
} else if (strncasecmp("fetch",data,strlen("fetch"))==0) {
result=(aPGSQL_fetch(chan,data));
} else if (strncasecmp("reset",data,strlen("reset"))==0) {
result=(aPGSQL_reset(chan,data));
} else if (strncasecmp("clear",data,strlen("clear"))==0) {
result=(aPGSQL_clear(chan,data));
} else if (strncasecmp("debug",data,strlen("debug"))==0) {
result=(aPGSQL_debug(chan,data));
} else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) {
result=(aPGSQL_disconnect(chan,data));
} else {
ast_log(LOG_WARNING, "Unknown APP_PGSQL argument : %s\n",(char *)data);
result=-1;
}
LOCAL_USER_REMOVE(u);
return result;
}
int unload_module(void)
{
STANDARD_HANGUP_LOCALUSERS;
return ast_unregister_application(app);
}
int load_module(void)
{
struct PGSQLidshead *headp;
headp=&PGSQLidshead;
AST_LIST_HEAD_INIT(headp);
return ast_register_application(app, PGSQL_exec, synopsis, descrip);
}
char *description(void)
{
return tdesc;
}
int usecount(void)
{
int res;
STANDARD_USECOUNT(res);
return res;
}
char *key()
{
return ASTERISK_GPL_KEY;
}

@ -0,0 +1,35 @@
;
; Festival Configuration
;
[general]
;
; Host which runs the festival server (default : localhost);
;
;host=localhost
;
; Port on host where the festival server runs (default : 1314)
;
;port=1314
;
; Use cache (yes, no - defaults to no)
;
;usecache=yes
;
; If usecache=yes, a directory to store waveform cache files.
; The cache is never cleared (yet), so you must take care of cleaning it
; yourself (just delete any or all files from the cache).
; THIS DIRECTORY *MUST* EXIST and must be writable from the asterisk process.
; Defaults to /tmp/
;
;cachedir=/var/lib/asterisk/festivalcache/
;
; Festival command to send to the server.
; Defaults to: (tts_textasterisk "%s" 'file)(quit)\n
; %s is replaced by the desired text to say. The command MUST end with a
; (quit) directive, or the cache handling mechanism will hang. Do not
; forget the \n at the end.
;
;festivalcommand=(tts_textasterisk "%s" 'file)(quit)\n
;
;

@ -0,0 +1,22 @@
app_festival is an application that allows one to send text-to-speech commands
to a background festival server, and to obtain the resulting waveform which
gets sent down to the respective channel. app_festival also employs a waveform
cache, so invariant text-to-speech strings ("Please press 1 for instructions")
do not need to be dynamically generated all the time.
You need :
1) festival, patched to produce 8khz waveforms on output. Patch for Festival
1.4.1 RELEASE are included. The patch adds a new command to festival
(asterisk_tts).
2) My patches to asterisk that provide variable substitution and quoting to
the Asterisk Extension Logic. This is not really a requirement, but without
this, app_festival is mostly useless (you could very well use prerecorded
voices for static information).
3) Before running asterisk, you have to run festival-server with a command
like :
/usr/local/festival/bin/festival --server > /dev/null 2>&1 &

@ -0,0 +1,76 @@
diff -ruN festival/lib/tts.scm myfestival/lib/tts.scm
--- festival/lib/tts.scm Sun May 30 16:40:00 1999
+++ myfestival/lib/tts.scm Wed Apr 17 22:29:34 2002
@@ -200,6 +200,15 @@
(utt.synth
(eval (list 'Utterance 'Text string)))))
+(define (tts_textasterisk string mode)
+ "(tts_textasterisk STRING MODE)
+Apply tts to STRING. This function is specifically designed for
+use in server mode so a single function call may synthesize the string.
+This function name maybe added to the server safe functions."
+ (utt.send.wave.asterisk
+ (utt.synth
+ (eval (list 'Utterance 'Text string)))))
+
(define (tts_return_to_client)
"(tts_return_to_client)
This function is called by clients who wish to return waveforms of
diff -ruN festival/src/arch/festival/wave.cc myfestival/src/arch/festival/wave.cc
--- festival/src/arch/festival/wave.cc Sat Jun 12 10:30:30 1999
+++ myfestival/src/arch/festival/wave.cc Thu Apr 18 10:55:32 2002
@@ -375,6 +375,38 @@
type = "nist";
else
type = get_c_string(ltype);
+
+ w->save(tmpfile,type);
+ write(ft_server_socket,"WV\n",3);
+ socket_send_file(ft_server_socket,tmpfile);
+ unlink(tmpfile);
+
+ return utt;
+}
+
+static LISP utt_send_wave_asterisk(LISP utt)
+{
+ // Send the waveform to a client (must be acting as server)
+ EST_Utterance *u = utterance(utt);
+ EST_Wave *w;
+ EST_String tmpfile = make_tmp_filename();
+ LISP ltype;
+ EST_String type;
+
+ w = get_utt_wave(u);
+ if (ft_server_socket == -1)
+ {
+ cerr << "utt_send_wave_client: not in server mode" << endl;
+ festival_error();
+ }
+
+ ltype = ft_get_param("Wavefiletype");
+ if (ltype == NIL)
+ type = "nist";
+ else
+ type = get_c_string(ltype);
+ w->resample(8000);
+ w->rescale(5);
w->save(tmpfile,type);
write(ft_server_socket,"WV\n",3);
socket_send_file(ft_server_socket,tmpfile);
@@ -434,6 +466,13 @@
"(utt.send.wave.client UTT)\n\
Sends wave in UTT to client. If not in server mode gives an error\n\
Note the client must be expecting to receive the waveform.");
+
+ init_subr_1("utt.send.wave.asterisk",utt_send_wave_asterisk,
+ "(utt.send.wave.asterisk UTT)\n\
+ Sends wave in UTT to client. If not in server mode gives an error\n\
+ Note the client must be expecting to receive the waveform. The waveform
+ is rescaled and resampled according to what asterisk needs");
+
init_subr_2("utt.save.f0",utt_save_f0,
"(utt.save.f0 UTT FILENAME)\n\
Save F0 of UTT as esps track file in FILENAME.");

@ -0,0 +1,76 @@
diff -ruN festival/lib/tts.scm myfestival/lib/tts.scm
--- festival/lib/tts.scm Sun May 30 16:40:00 1999
+++ myfestival/lib/tts.scm Wed Apr 17 22:29:34 2002
@@ -200,6 +200,15 @@
(utt.synth
(eval (list 'Utterance 'Text string)))))
+(define (tts_textasterisk string mode)
+ "(tts_textasterisk STRING MODE)
+Apply tts to STRING. This function is specifically designed for
+use in server mode so a single function call may synthesize the string.
+This function name maybe added to the server safe functions."
+ (utt.send.wave.asterisk
+ (utt.synth
+ (eval (list 'Utterance 'Text string)))))
+
(define (tts_return_to_client)
"(tts_return_to_client)
This function is called by clients who wish to return waveforms of
diff -ruN festival/src/arch/festival/wave.cc myfestival/src/arch/festival/wave.cc
--- festival/src/arch/festival/wave.cc Sat Jun 12 10:30:30 1999
+++ myfestival/src/arch/festival/wave.cc Thu Apr 18 10:55:32 2002
@@ -375,6 +375,38 @@
type = "nist";
else
type = get_c_string(ltype);
+
+ w->save(tmpfile,type);
+ write(ft_server_socket,"WV\n",3);
+ socket_send_file(ft_server_socket,tmpfile);
+ unlink(tmpfile);
+
+ return utt;
+}
+
+static LISP utt_send_wave_asterisk(LISP utt)
+{
+ // Send the waveform to a client (must be acting as server)
+ EST_Utterance *u = utterance(utt);
+ EST_Wave *w;
+ EST_String tmpfile = make_tmp_filename();
+ LISP ltype;
+ EST_String type;
+
+ w = get_utt_wave(u);
+ if (ft_server_socket == -1)
+ {
+ cerr << "utt_send_wave_client: not in server mode" << endl;
+ festival_error();
+ }
+
+ ltype = ft_get_param("Wavefiletype");
+ if (ltype == NIL)
+ type = "nist";
+ else
+ type = get_c_string(ltype);
+ w->resample(8000);
+ w->rescale(5);
w->save(tmpfile,type);
write(ft_server_socket,"WV\n",3);
socket_send_file(ft_server_socket,tmpfile);
@@ -434,6 +466,13 @@
"(utt.send.wave.client UTT)\n\
Sends wave in UTT to client. If not in server mode gives an error\n\
Note the client must be expecting to receive the waveform.");
+
+ init_subr_1("utt.send.wave.asterisk",utt_send_wave_asterisk,
+ "(utt.send.wave.asterisk UTT)\n\
+ Sends wave in UTT to client. If not in server mode gives an error\n\
+ Note the client must be expecting to receive the waveform. The waveform
+ is rescaled and resampled according to what asterisk needs");
+
init_subr_2("utt.save.f0",utt_save_f0,
"(utt.save.f0 UTT FILENAME)\n\
Save F0 of UTT as esps track file in FILENAME.");

591
pbx.c

@ -21,6 +21,10 @@
#include <asterisk/callerid.h>
#include <asterisk/cdr.h>
#include <asterisk/term.h>
#include <asterisk/manager.h>
#include <asterisk/ast_expr.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/linkedlists.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
@ -140,6 +144,10 @@ static int pbx_builtin_setlanguage(struct ast_channel *, void *);
static int pbx_builtin_ringing(struct ast_channel *, void *);
static int pbx_builtin_congestion(struct ast_channel *, void *);
static int pbx_builtin_busy(struct ast_channel *, void *);
static int pbx_builtin_setvar(struct ast_channel *, void *);
static int pbx_builtin_gotoif(struct ast_channel *, void *);
void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value);
static struct pbx_builtin {
char name[AST_MAX_APP];
@ -248,6 +256,17 @@ static struct pbx_builtin {
" Busy(): Requests that the channel indicate busy condition and then waits\n"
"for the user to hang up. Always returns -1." },
{ "Setvar", pbx_builtin_setvar,
"Set variable to value",
" Setvar(#n=value): Sets variable n to value" },
{ "GotoIf", pbx_builtin_gotoif,
"Conditional goto",
" GotoIf(Condition?label1:label2): Go to label 1 if condition is\n"
"true, to label2 if condition is false. Either label1 or label2 may be\n"
"omitted (in that case, we just don't take the particular branch) but not\n"
"both. Look for the condition syntax in examples or documentation." },
};
/* Lock for the application list */
@ -623,12 +642,224 @@ static struct ast_exten *pbx_find_extension(struct ast_channel *chan, char *cont
return NULL;
}
static void *pbx_substitute_variables(struct ast_channel *c, struct ast_exten *e) {
char *cp1,*cp3,*cp4,*cp5;
void *cp2;
char c1,c2;
int m,mve,origlen,quoted,dolsign,docopy;
struct ast_var_t *variables;
struct varshead *headp;
char pri[80];
headp=&c->varshead;
origlen=strlen(e->data)+1;
cp2=malloc(origlen);
memset(cp2,0,origlen);
if ((strchr(e->data,'$')==NULL) && (strchr(e->data,'[')==NULL)) {
strncpy(cp2,e->data,strlen(e->data));
return(cp2);
/* No variables or expressions in e->data, so why scan it? */
}
cp4=NULL;
cp1=e->data;
quoted=0;
dolsign=0;
docopy=1;
/* First stage, variable substitution */
do {
c1=*cp1;
mve=0;
switch (c1) {
case '\\' :
dolsign=0;
if (quoted==1) {
quoted=0;
docopy=1;
} else {
quoted=1;
docopy=0;
}
break;
case '$' :
if (quoted==1) {
quoted=0;
docopy=1;
dolsign=0;
} else {
docopy=0;
dolsign=1;
}
break;
case '{' :
if (quoted==1) {
quoted=0;
dolsign=0;
docopy=1;
break;
}
if (dolsign==0) {
docopy=1;
break;
}
docopy=0;
dolsign=0;
m=0;
cp1++;
while (((c2=*(cp1+m))!='}') && (c2!='\0')) {
m++;
}
mve=1;
cp3=malloc(m+2);
strncpy(cp3,cp1,m);
cp3[m]='\0';
cp1+=m;
/* Now we have the variable name on cp3 */
if (!strcmp(cp3, "CALLERID")) {
cp4 = c->callerid;
break;
} else if (!strcmp(cp3, "EXTEN")) {
cp4 = c->exten;
break;
} else if (!strcmp(cp3, "CONTEXT")) {
cp4 = c->context;
break;
} else if (!strcmp(cp3, "PRIORITY")) {
snprintf(pri, sizeof(pri), "%d", c->priority);
cp4 = pri;
break;
} else {
AST_LIST_TRAVERSE(headp,variables,entries) {
// ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",cp3,ast_var_name(variables));
if (strncasecmp(ast_var_name(variables),cp3,m)==0) {
cp4=ast_var_value(variables);
break;
}
}
}
free(cp3);
break;
default :
if (dolsign==1) {
strncat((char *)cp2,"$",1);
}
if (quoted==1) {
quoted=0;
}
mve=0;
dolsign=0;
docopy=1;
break;
}
if (cp1!='\0') {
if (mve==0) {
if (docopy==1) {
strncat((char *)cp2,&c1,1);
}
} else {
if (cp4!=NULL) {
cp2=realloc(cp2,origlen+strlen(cp4)+1);
strncat((char *)cp2,cp4,strlen(cp4));
} else {
ast_log(LOG_WARNING,"mve!=0 and cp4=NULL, something gone astray\n");
}
}
}
} while (*cp1++!='\0');
/* Second stage, expression evaluation */
if ((strstr(cp2,"$[")==NULL)) {
/* No expressions in cp2, return it */
return(cp2);
}
/* else, do expression evaluation */
dolsign=0;
docopy=1;
origlen=strlen(cp2)+1;
cp5=malloc(origlen);
memset(cp5,0,origlen);
cp4=NULL;
cp1=cp2;
quoted=0;
do {
c1=*cp1;
mve=0;
switch (c1) {
case '$' :
dolsign=1;
docopy=0;
break;
case '[' :
if (dolsign==0) {
docopy=1;
dolsign=0;
break;
}
dolsign=0;
docopy=0;
m=0;
mve=1;
cp1++;
while (((c2=*(cp1+m))!=']') && (c2!='\0')) {
m++;
}
cp3=malloc(m+2);
strncpy(cp3,cp1,m);
cp3[m]='\0';
cp1+=m;
/* Now we have the expression to evaluate on cp3 */
cp4=ast_expr(cp3);
free(cp3);
break;
default :
if (dolsign==1) {
strncat((char *)cp5,"$",1);
}
dolsign=0;
docopy=1;
mve=0;
break;
}
if (cp1!='\0') {
if (mve==0) {
if (docopy==1) {
strncat((char *)cp5,&c1,1);
}
} else {
if (cp4!=NULL) {
cp5=realloc(cp5,origlen+strlen(cp4)+1);
strncat((char *)cp5,cp4,strlen(cp4));
free(cp4);
} else {
ast_log(LOG_WARNING,"mve!=0 and cp4=NULL, something gone astray\n");
}
}
}
} while (*cp1++!='\0');
free(cp2);
return(cp5);
}
static int pbx_extension_helper(struct ast_channel *c, char *context, char *exten, int priority, char *callerid, int action)
{
struct ast_exten *e;
struct ast_app *app;
struct ast_switch *sw;
char *data;
char *newdata;
int newstack = 0;
int res;
int status = 0;
@ -663,23 +894,24 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
strncpy(c->context, context, sizeof(c->context-1));
strncpy(c->exten, exten, sizeof(c->exten)-1);
c->priority = priority;
newdata=pbx_substitute_variables(c,e);
if (option_debug)
ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
else if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n",
term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
term_color(tmp3, (e->data ? (char *)e->data : NULL), COLOR_BRMAGENTA, 0, sizeof(tmp3)),
term_color(tmp3, (newdata ? (char *)newdata : NULL), COLOR_BRMAGENTA, 0, sizeof(tmp3)),
(newstack ? "in new stack" : "in same stack"));
res = pbx_exec(c, app, e->data, newstack);
res = pbx_exec(c, app, newdata, newstack);
free(newdata);
return res;
} else {
ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
return -1;
}
default:
ast_log(LOG_WARNING, "Huh (%d)?\n", action);
return -1;
ast_log(LOG_WARNING, "Huh (%d)?\n", action); return -1;
}
} else if (sw) {
switch(action) {
@ -804,6 +1036,12 @@ int ast_pbx_run(struct ast_channel *c)
digit = 0;
while(ast_exists_extension(c, c->context, c->exten, c->priority, c->callerid)) {
memset(exten, 0, sizeof(exten));
manager_event(EVENT_FLAG_CALL, "Newexten",
"Channel: %s\r\n"
"Context: %s\r\n"
"Extension: %s\r\n"
"Priority: %d\r\n",
c->name, c->context, c->exten, c->priority);
if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->callerid))) {
/* Something bad happened, or a hangup has been requested. */
switch(res) {
@ -812,17 +1050,22 @@ int ast_pbx_run(struct ast_channel *c)
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
break;
goto out;
break;
default:
if (option_debug)
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
c->_softhangup =0;
break;
}
goto out;
}
goto out;
}
if (c->softhangup) {
ast_log(LOG_WARNING, "Extension %s, priority %d returned normally even though call was hung up\n",
if (c->_softhangup) {
ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
c->exten, c->priority);
goto out;
}
@ -830,15 +1073,19 @@ int ast_pbx_run(struct ast_channel *c)
if (c->stream) {
digit = ast_waitstream(c, AST_DIGIT_ANY);
ast_stopstream(c);
/* Hang up if something goes wrong */
if (digit < 0) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Lost connection on %s\n", c->name);
goto out;
}
else if (digit) {
exten[pos++] = digit;
break;
if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
c->_softhangup = 0;
} else {
/* Hang up if something goes wrong */
if (digit < 0) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Lost connection on %s\n", c->name);
goto out;
}
else if (digit) {
exten[pos++] = digit;
break;
}
}
}
firstpass = 0;
@ -867,14 +1114,18 @@ int ast_pbx_run(struct ast_channel *c)
/* As long as we're willing to wait, and as long as it's not defined,
keep reading digits until we can't possibly get a right answer anymore. */
digit = ast_waitfordigit(c, waittime * 1000);
if (!digit)
/* No entry */
break;
if (digit < 0)
/* Error, maybe a hangup */
goto out;
exten[pos++] = digit;
waittime = c->pbx->dtimeout;
if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
c->_softhangup = 0;
} else {
if (!digit)
/* No entry */
break;
if (digit < 0)
/* Error, maybe a hangup */
goto out;
exten[pos++] = digit;
waittime = c->pbx->dtimeout;
}
}
if (ast_exists_extension(c, c->context, exten, 1, c->callerid)) {
/* Prepare the next cycle */
@ -955,6 +1206,7 @@ int ast_pbx_start(struct ast_channel *c)
ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
return -1;
}
/* Start a new thread, and get something handling this channel. */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@ -1864,7 +2116,7 @@ int ast_context_add_include(char *context, char *include, char *registrar)
do { \
c = info; \
while(*c && (*c != '|')) c++; \
if (*c) *c = '\0'; else c = NULL; \
if (*c) { *c = '\0'; c++; } else c = NULL; \
} while(0)
static void get_timerange(struct ast_include *i, char *times)
@ -1935,7 +2187,7 @@ static unsigned int get_dow(char *dow)
char *c;
/* The following line is coincidence, really! */
int s, e, x;
unsigned mask;
unsigned int mask;
/* Check for all days */
if (!strlen(dow) || !strcmp(dow, "*"))
return (1 << 7) - 1;
@ -1944,7 +2196,8 @@ static unsigned int get_dow(char *dow)
if (c) {
*c = '\0';
c++;
}
} else
c = NULL;
/* Find the start */
s = 0;
while((s < 7) && strcasecmp(dow, days[s])) s++;
@ -1954,7 +2207,7 @@ static unsigned int get_dow(char *dow)
}
if (c) {
e = 0;
while((e < 7) && strcasecmp(dow, days[e])) e++;
while((e < 7) && strcasecmp(c, days[e])) e++;
if (e >= 7) {
ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
return 0;
@ -1979,6 +2232,7 @@ static unsigned int get_day(char *day)
/* Check for all days */
if (!strlen(day) || !strcmp(day, "*")) {
mask = (1 << 30) + ((1 << 30) - 1);
return mask;
}
/* Get start and ending days */
c = strchr(day, '-');
@ -2085,7 +2339,6 @@ static void build_timing(struct ast_include *i, char *info)
i->dowmask = (1 << 7) - 1;
/* Avoid using strtok */
FIND_NEXT;
/* Info has the time range, start with that */
get_timerange(i, info);
info = c;
@ -2440,6 +2693,86 @@ int ast_add_extension(char *context, int replace, char *extension, int priority,
return -1;
}
int ast_async_goto(struct ast_channel *chan, char *context, char *exten, int priority, int needlock)
{
int res = 0;
if (needlock)
ast_pthread_mutex_lock(&chan->lock);
if (chan->pbx) {
/* This channel is currently in the PBX */
if (context && strlen(context))
strncpy(chan->context, context, sizeof(chan->context) - 1);
if (exten && strlen(exten))
strncpy(chan->exten, exten, sizeof(chan->context) - 1);
if (priority)
chan->priority = priority - 1;
ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
if (needlock)
ast_pthread_mutex_unlock(&chan->lock);
} else {
/* In order to do it when the channel doesn't really exist within
the PBX, we have to make a new channel, masquerade, and start the PBX
at the new location */
struct ast_channel *tmpchan;
struct ast_frame *f;
tmpchan = ast_channel_alloc(0);
if (tmpchan) {
snprintf(tmpchan->name, sizeof(tmpchan->name), "AsyncGoto/%s", chan->name);
/* Make formats okay */
tmpchan->readformat = chan->readformat;
tmpchan->writeformat = chan->writeformat;
/* Setup proper location */
if (context && strlen(context))
strncpy(tmpchan->context, context, sizeof(tmpchan->context) - 1);
else
strncpy(tmpchan->context, chan->context, sizeof(tmpchan->context) - 1);
if (exten && strlen(exten))
strncpy(tmpchan->exten, exten, sizeof(tmpchan->exten) - 1);
else
strncpy(tmpchan->exten, chan->exten, sizeof(tmpchan->exten) - 1);
if (priority)
tmpchan->priority = priority;
else
tmpchan->priority = chan->priority;
if (needlock)
ast_pthread_mutex_unlock(&chan->lock);
/* Masquerade into temp channel */
ast_channel_masquerade(tmpchan, chan);
/* Make the masquerade happen by reading a frame from the tmp channel */
f = ast_read(tmpchan);
if (f)
ast_frfree(f);
/* Start the PBX going on our stolen channel */
if (ast_pbx_start(tmpchan)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
ast_hangup(tmpchan);
res = -1;
}
} else {
res = -1;
if (needlock)
ast_pthread_mutex_unlock(&chan->lock);
}
}
return res;
}
int ast_async_goto_by_name(char *channame, char *context, char *exten, int priority)
{
struct ast_channel *chan;
chan = ast_channel_walk(NULL);
while(chan) {
if (!strcasecmp(channame, chan->name))
break;
chan = ast_channel_walk(chan);
}
if (chan)
return ast_async_goto(chan, context, exten, priority, 1);
return -1;
}
static void ext_strncpy(char *dst, char *src, int len)
{
int count=0;
@ -2640,6 +2973,112 @@ int ast_add_extension2(struct ast_context *con,
return 0;
}
struct async_stat {
pthread_t p;
struct ast_channel *chan;
char context[AST_MAX_EXTENSION];
char exten[AST_MAX_EXTENSION];
int priority;
int timeout;
char app[AST_MAX_EXTENSION];
char data[1024];
};
static void *async_wait(void *data)
{
struct async_stat *as = data;
struct ast_channel *chan = as->chan;
int timeout = as->timeout;
int res;
struct ast_frame *f;
while(timeout && (chan->_state != AST_STATE_UP)) {
res = ast_waitfor(chan, timeout);
if (res < 1)
break;
if (timeout > -1)
timeout = res;
f = ast_read(chan);
if (!f)
break;
if (f->frametype == AST_FRAME_CONTROL) {
if ((f->subclass == AST_CONTROL_BUSY) ||
(f->subclass == AST_CONTROL_CONGESTION) )
break;
}
ast_frfree(f);
}
if (chan->_state == AST_STATE_UP) {
if (strlen(as->context))
strncpy(chan->context, as->context, sizeof(chan->context) - 1);
if (strlen(as->exten))
strncpy(chan->exten, as->exten, sizeof(chan->exten) - 1);
if (as->priority > 0)
chan->priority = as->priority;
/* Run the PBX */
if (ast_pbx_run(chan)) {
ast_log(LOG_WARNING, "Failed to start PBX on %s\n", chan->name);
} else {
/* PBX will have taken care of this */
chan = NULL;
}
}
free(as);
if (chan)
ast_hangup(chan);
return NULL;
}
int ast_pbx_outgoing_exten(char *type, int format, void *data, int timeout, char *context, char *exten, int priority, int *reason, int sync)
{
struct ast_channel *chan;
struct async_stat *as;
int res = -1;
if (sync) {
chan = ast_request_and_dial(type, format, data, timeout, reason);
if (chan) {
if (chan->_state == AST_STATE_UP) {
res = 0;
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
if (ast_pbx_start(chan)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", chan->name);
ast_hangup(chan);
res = -1;
}
} else {
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
ast_hangup(chan);
}
}
} else {
as = malloc(sizeof(struct async_stat));
if (!as)
return -1;
memset(as, 0, sizeof(struct async_stat));
chan = ast_request_and_dial(type, format, data, 0, reason);
if (!chan) {
free(as);
return -1;
}
as->chan = chan;
strncpy(as->context, context, sizeof(as->context) - 1);
strncpy(as->exten, exten, sizeof(as->exten) - 1);
as->priority = priority;
as->timeout = timeout;
if (pthread_create(&as->p, NULL, async_wait, as)) {
ast_log(LOG_WARNING, "Failed to start async wait\n");
free(as);
ast_hangup(chan);
return -1;
}
res = 0;
}
return res;
}
void ast_context_destroy(struct ast_context *con, char *registrar)
{
struct ast_context *tmp, *tmpl=NULL;
@ -2788,7 +3227,7 @@ static int pbx_builtin_background(struct ast_channel *chan, void *data)
{
int res;
/* Answer if need be */
if (chan->state != AST_STATE_UP)
if (chan->_state != AST_STATE_UP)
if (ast_answer(chan))
return -1;
/* Stop anything playing */
@ -2867,6 +3306,96 @@ static int pbx_builtin_goto(struct ast_channel *chan, void *data)
return 0;
}
void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value) {
struct ast_var_t *newvariable;
struct varshead *headp;
headp=&chan->varshead;
AST_LIST_TRAVERSE (headp,newvariable,entries) {
if (strncasecmp(ast_var_name(newvariable),name,strlen(name))==0) {
/* there is already such a variable, delete it */
AST_LIST_REMOVE(headp,newvariable,ast_var_t,entries);
ast_var_delete(newvariable);
break;
}
}
newvariable=ast_var_assign(name,value);
AST_LIST_INSERT_HEAD(headp,newvariable,entries);
return;
}
static int pbx_builtin_setvar(struct ast_channel *chan, void *data)
{
char *name;
char *value;
char *ptrptr;
if (!data || !strlen(data)) {
ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
return 0;
}
name=strtok_r(data,"=",&ptrptr);
value=strtok_r(NULL,"\0",&ptrptr);
pbx_builtin_setvar_helper(chan,name,value);
return(0);
}
static int pbx_checkcondition(char *condition) {
char *s;
int ret;
s=strdup(condition);
ret=1;
if ((strcasecmp(s,"0")) || (strlen(s)==0)) {
ret=0;
}
free(s);
return(ret);
}
static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
{
char *condition,*branch1,*branch2,*branch;
char *s;
char *ptrptr;
int rc;
if (!data || !strlen(data)) {
ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
return 0;
}
s=strdup(data);
condition=strtok_r(s,"?",&ptrptr);
branch1=strtok_r(NULL,":",&ptrptr);
branch2=strtok_r(NULL,"",&ptrptr);
if (pbx_checkcondition(condition)) {
branch=branch2;
} else {
branch=branch1;
}
if ((branch==NULL) || (strlen(branch)==0)) {
ast_log(LOG_WARNING, "Not taking any branch\n");
return(0);
}
rc=pbx_builtin_goto(chan,branch);
free(s);
return(rc);
}
int load_pbx(void)
{
int x;

Loading…
Cancel
Save