From e42d61fac695100d49c816f55ea96e15d29dd5ed Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Wed, 11 Sep 2002 17:09:48 +0000 Subject: [PATCH] Version 0.2.0 from FTP git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@523 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- README.festival | 22 ++ apps/Makefile | 14 +- apps/app_festival.c | 480 ++++++++++++++++++++++++++++ apps/app_sql_postgres.c | 546 ++++++++++++++++++++++++++++++++ configs/festival.conf.sample | 35 +++ contrib/README.festival | 22 ++ contrib/festival-1.4.1-diff | 76 +++++ festival-1.4.1-diff | 76 +++++ pbx.c | 591 +++++++++++++++++++++++++++++++++-- 9 files changed, 1830 insertions(+), 32 deletions(-) create mode 100755 README.festival create mode 100755 apps/app_festival.c create mode 100755 apps/app_sql_postgres.c create mode 100755 configs/festival.conf.sample create mode 100755 contrib/README.festival create mode 100755 contrib/festival-1.4.1-diff create mode 100755 festival-1.4.1-diff diff --git a/README.festival b/README.festival new file mode 100755 index 0000000000..56d985e23f --- /dev/null +++ b/README.festival @@ -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 & diff --git a/apps/Makefile b/apps/Makefile index 0bc6259b2f..ce48c47077 100755 --- a/apps/Makefile +++ b/apps/Makefile @@ -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 diff --git a/apps/app_festival.c b/apps/app_festival.c new file mode 100755 index 0000000000..d45988c991 --- /dev/null +++ b/apps/app_festival.c @@ -0,0 +1,480 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Connect to festival + * + * Copyright (C) 2002, Christos Ricudis + * + * Christos Ricudis + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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;xfds[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;xfds[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; +} diff --git a/apps/app_sql_postgres.c b/apps/app_sql_postgres.c new file mode 100755 index 0000000000..628987ad5d --- /dev/null +++ b/apps/app_sql_postgres.c @@ -0,0 +1,546 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Connect to PostgreSQL + * + * Copyright (C) 2002, Christos Ricudis + * + * Christos Ricudis + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 /dev/null 2>&1 & diff --git a/contrib/festival-1.4.1-diff b/contrib/festival-1.4.1-diff new file mode 100755 index 0000000000..c8543d7418 --- /dev/null +++ b/contrib/festival-1.4.1-diff @@ -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."); + diff --git a/festival-1.4.1-diff b/festival-1.4.1-diff new file mode 100755 index 0000000000..c8543d7418 --- /dev/null +++ b/festival-1.4.1-diff @@ -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."); + diff --git a/pbx.c b/pbx.c index ec52c05983..b0ac72ae5c 100755 --- a/pbx.c +++ b/pbx.c @@ -21,6 +21,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -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;