From b6be24372a79c5fe8568a831bab188db9ea98e8c Mon Sep 17 00:00:00 2001 From: matteo Date: Sun, 23 Feb 2003 06:00:11 +0000 Subject: Sun Feb 23 07:00:00 CET 2003 git-svn-id: http://svn.digium.com/svn/asterisk/trunk@621 f38db490-d61c-443f-a65b-d21fe96a405b --- CHANGES | 1 + agi/.cvsignore | 2 + agi/Makefile | 8 +- agi/eagi-sphinx-test.c | 216 ++++++++++++++++++++++++++ agi/eagi-test.c | 160 +++++++++++++++++++ app.c | 18 +++ apps/Makefile | 3 +- apps/app_agi.c | 377 ++++++++++++++++++++++++--------------------- apps/app_waitforring.c | 130 ++++++++++++++++ channel.c | 74 +++++++++ channels/chan_sip.c | 29 +++- contrib/scripts/vmail.cgi | 5 +- file.c | 65 ++++++++ include/asterisk/app.h | 3 + include/asterisk/channel.h | 8 +- include/asterisk/file.h | 4 + include/asterisk/say.h | 5 + pbx.c | 123 ++++++++------- say.c | 90 ++++++++++- vmail.cgi | 5 +- 20 files changed, 1088 insertions(+), 238 deletions(-) create mode 100755 agi/.cvsignore create mode 100755 agi/eagi-sphinx-test.c create mode 100755 agi/eagi-test.c create mode 100755 apps/app_waitforring.c diff --git a/CHANGES b/CHANGES index 3cad385ce..008975059 100755 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,4 @@ + -- Add "Enhanced" AGI with audio pass-through (voice recognition anyone?) -- Choose best priority from codec from allow/disallow -- Reject SIP calls to self -- Allow SIP registration to provide an alternative contact diff --git a/agi/.cvsignore b/agi/.cvsignore new file mode 100755 index 000000000..f0f87f249 --- /dev/null +++ b/agi/.cvsignore @@ -0,0 +1,2 @@ +eagi-test +eagi-sphinx-test diff --git a/agi/Makefile b/agi/Makefile index 8044516f7..bb12d1597 100755 --- a/agi/Makefile +++ b/agi/Makefile @@ -11,7 +11,7 @@ # the GNU General Public License # -AGIS=agi-test.agi +AGIS=agi-test.agi eagi-test eagi-sphinx-test CFLAGS+= @@ -20,6 +20,12 @@ all: $(AGIS) install: all for x in $(AGIS); do $(INSTALL) -m 755 $$x $(AGI_DIR) ; done +eagi-test: eagi-test.o + $(CC) -o eagi-test eagi-test.o + +eagi-sphinx-test: eagi-sphinx-test.o + $(CC) -o eagi-sphinx-test eagi-sphinx-test.o + clean: rm -f *.so *.o look diff --git a/agi/eagi-sphinx-test.c b/agi/eagi-sphinx-test.c new file mode 100755 index 000000000..ae80654e8 --- /dev/null +++ b/agi/eagi-sphinx-test.c @@ -0,0 +1,216 @@ +/* + * Extended AGI test application + * + * Copyright (C) 2003, Digium, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUDIO_FILENO (STDERR_FILENO + 1) + +#define SPHINX_HOST "192.168.1.108" +#define SPHINX_PORT 3460 + +static int sphinx_sock = -1; + +static int connect_sphinx(void) +{ + struct hostent *hp; + struct sockaddr_in sin; + int res; + hp = gethostbyname(SPHINX_HOST); + if (!hp) { + fprintf(stderr, "Unable to resolve '%s'\n", SPHINX_HOST); + return -1; + } + sphinx_sock = socket(PF_INET, SOCK_STREAM, 0); + if (sphinx_sock < 0) { + fprintf(stderr, "Unable to allocate socket: %s\n", strerror(errno)); + return -1; + } + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(SPHINX_PORT); + memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + if (connect(sphinx_sock, &sin, sizeof(sin))) { + fprintf(stderr, "Unable to connect on socket: %s\n", strerror(errno)); + close(sphinx_sock); + sphinx_sock = -1; + return -1; + } + res = fcntl(sphinx_sock, F_GETFL); + if ((res < 0) || (fcntl(sphinx_sock, F_SETFL, res | O_NONBLOCK) < 0)) { + fprintf(stderr, "Unable to set flags on socket: %s\n", strerror(errno)); + close(sphinx_sock); + sphinx_sock = -1; + return -1; + } + return 0; +} + +static int read_environment(void) +{ + char buf[256]; + char *val; + /* Read environment */ + for(;;) { + fgets(buf, sizeof(buf), stdin); + if (feof(stdin)) + return -1; + buf[strlen(buf) - 1] = '\0'; + /* Check for end of environment */ + if (!strlen(buf)) + return 0; + val = strchr(buf, ':'); + if (!val) { + fprintf(stderr, "Invalid environment: '%s'\n", buf); + return -1; + } + *val = '\0'; + val++; + val++; + /* Skip space */ + fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val); + + /* Load into normal environment */ + setenv(buf, val, 1); + + } + /* Never reached */ + return 0; +} + +static char *wait_result(void) +{ + fd_set fds; + int res; + int max; + static char astresp[256]; + static char sphinxresp[256]; + char audiobuf[4096]; + for (;;) { + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + FD_SET(AUDIO_FILENO, &fds); + max = AUDIO_FILENO; + if (sphinx_sock > -1) { + FD_SET(sphinx_sock, &fds); + if (sphinx_sock > max) + max = sphinx_sock; + } + /* Wait for *some* sort of I/O */ + res = select(max + 1, &fds, NULL, NULL, NULL); + if (res < 0) { + fprintf(stderr, "Error in select: %s\n", strerror(errno)); + return NULL; + } + if (FD_ISSET(STDIN_FILENO, &fds)) { + fgets(astresp, sizeof(astresp), stdin); + if (feof(stdin)) { + fprintf(stderr, "Got hungup on apparently\n"); + return NULL; + } + astresp[strlen(astresp) - 1] = '\0'; + fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); + return astresp; + } + if (FD_ISSET(AUDIO_FILENO, &fds)) { + res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf)); + if (res > 0) { + if (sphinx_sock > -1) + write(sphinx_sock, audiobuf, res); + } + } + if ((sphinx_sock > -1) && FD_ISSET(sphinx_sock, &fds)) { + res = read(sphinx_sock, sphinxresp, sizeof(sphinxresp)); + if (res > 0) { + fprintf(stderr, "Oooh, Sphinx found a token: '%s'\n", sphinxresp); + return sphinxresp; + } else if (res == 0) { + fprintf(stderr, "Hrm, lost sphinx, guess we're on our own\n"); + close(sphinx_sock); + sphinx_sock = -1; + } + } + } + +} + +static char *run_command(char *command) +{ + fprintf(stdout, "%s\n", command); + return wait_result(); +} + +static int run_script(void) +{ + char *res; + res = run_command("STREAM FILE demo-enterkeywords 0123456789*#"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "1. Result is '%s'\n", res); + res = run_command("STREAM FILE demo-nomatch 0123456789*#"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "2. Result is '%s'\n", res); + res = run_command("SAY NUMBER 23452345 0123456789*#"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "3. Result is '%s'\n", res); + res = run_command("GET DATA demo-enterkeywords"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "4. Result is '%s'\n", res); + res = run_command("STREAM FILE auth-thankyou \"\""); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "5. Result is '%s'\n", res); + return 0; +} + +int main(int argc, char *argv[]) +{ + char *tmp; + int ver = 0; + int subver = 0; + /* Setup stdin/stdout for line buffering */ + setlinebuf(stdin); + setlinebuf(stdout); + if (read_environment()) { + fprintf(stderr, "Failed to read environment: %s\n", strerror(errno)); + exit(1); + } + connect_sphinx(); + tmp = getenv("agi_enhanced"); + if (tmp) { + if (sscanf(tmp, "%d.%d", &ver, &subver) != 2) + ver = 0; + } + if (ver < 1) { + fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n"); + exit(1); + } + if (run_script()) + return -1; + exit(0); +} diff --git a/agi/eagi-test.c b/agi/eagi-test.c new file mode 100755 index 000000000..7d1e95e92 --- /dev/null +++ b/agi/eagi-test.c @@ -0,0 +1,160 @@ +/* + * Extended AGI test application + * + * Copyright (C) 2003, Digium, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#define AUDIO_FILENO (STDERR_FILENO + 1) + +static int read_environment(void) +{ + char buf[256]; + char *val; + /* Read environment */ + for(;;) { + fgets(buf, sizeof(buf), stdin); + if (feof(stdin)) + return -1; + buf[strlen(buf) - 1] = '\0'; + /* Check for end of environment */ + if (!strlen(buf)) + return 0; + val = strchr(buf, ':'); + if (!val) { + fprintf(stderr, "Invalid environment: '%s'\n", buf); + return -1; + } + *val = '\0'; + val++; + val++; + /* Skip space */ + fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val); + + /* Load into normal environment */ + setenv(buf, val, 1); + + } + /* Never reached */ + return 0; +} + +static char *wait_result(void) +{ + fd_set fds; + int res; + int bytes = 0; + static char astresp[256]; + char audiobuf[4096]; + for (;;) { + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + FD_SET(AUDIO_FILENO, &fds); + /* Wait for *some* sort of I/O */ + res = select(AUDIO_FILENO + 1, &fds, NULL, NULL, NULL); + if (res < 0) { + fprintf(stderr, "Error in select: %s\n", strerror(errno)); + return NULL; + } + if (FD_ISSET(STDIN_FILENO, &fds)) { + fgets(astresp, sizeof(astresp), stdin); + if (feof(stdin)) { + fprintf(stderr, "Got hungup on apparently\n"); + return NULL; + } + astresp[strlen(astresp) - 1] = '\0'; + fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); + return astresp; + } + if (FD_ISSET(AUDIO_FILENO, &fds)) { + res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf)); + if (res > 0) { + /* XXX Process the audio with sphinx here XXX */ +#if 0 + fprintf(stderr, "Got %d/%d bytes of audio\n", res, bytes); +#endif + bytes += res; + /* Prentend we detected some audio after 3 seconds */ + if (bytes > 16000 * 3) { + return "Sample Message"; + bytes = 0; + } + } + } + } + +} + +static char *run_command(char *command) +{ + fprintf(stdout, "%s\n", command); + return wait_result(); +} + +static int run_script(void) +{ + char *res; + res = run_command("STREAM FILE demo-enterkeywords 0123456789*#"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "1. Result is '%s'\n", res); + res = run_command("STREAM FILE demo-nomatch 0123456789*#"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "2. Result is '%s'\n", res); + res = run_command("SAY NUMBER 23452345 0123456789*#"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "3. Result is '%s'\n", res); + res = run_command("GET DATA demo-enterkeywords"); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "4. Result is '%s'\n", res); + res = run_command("STREAM FILE auth-thankyou \"\""); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + fprintf(stderr, "5. Result is '%s'\n", res); + return 0; +} + +int main(int argc, char *argv[]) +{ + char *tmp; + int ver = 0; + int subver = 0; + /* Setup stdin/stdout for line buffering */ + setlinebuf(stdin); + setlinebuf(stdout); + if (read_environment()) { + fprintf(stderr, "Failed to read environment: %s\n", strerror(errno)); + exit(1); + } + tmp = getenv("agi_enhanced"); + if (tmp) { + if (sscanf(tmp, "%d.%d", &ver, &subver) != 2) + ver = 0; + } + if (ver < 1) { + fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n"); + exit(1); + } + if (run_script()) + return -1; + exit(0); +} diff --git a/app.c b/app.c index 482c2ee3a..2a8c12ef9 100755 --- a/app.c +++ b/app.c @@ -34,6 +34,7 @@ int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout) { int res,to,fto; + /* XXX Merge with full version? XXX */ if (prompt) { res = ast_streamfile(c, prompt, c->language); if (res < 0) @@ -47,6 +48,23 @@ int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, in return res; } + +int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd) +{ + int res,to,fto; + if (prompt) { + res = ast_streamfile(c, prompt, c->language); + if (res < 0) + return res; + } + fto = 6000; + to = 2000; + if (timeout > 0) fto = to = timeout; + if (timeout < 0) fto = to = 1000000000; + res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd); + return res; +} + int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec) { int res; diff --git a/apps/Makefile b/apps/Makefile index 3ea4494a3..7a7d93771 100755 --- a/apps/Makefile +++ b/apps/Makefile @@ -18,7 +18,8 @@ APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_intercom. app_zapateller.so app_datetime.so app_setcallerid.so app_festival.so \ app_queue.so app_senddtmf.so app_parkandannounce.so app_striplsd.so \ app_setcidname.so app_lookupcidname.so app_substring.so app_macro.so \ - app_authenticate.so app_softhangup.so app_lookupblacklist.so + app_authenticate.so app_softhangup.so app_lookupblacklist.so \ + app_waitforring.so #APPS+=app_sql_postgres.so #APPS+=app_sql_odbc.so diff --git a/apps/app_agi.c b/apps/app_agi.c index a616e814d..dde76a271 100755 --- a/apps/app_agi.c +++ b/apps/app_agi.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "../asterisk.h" #include "../astconf.h" @@ -42,12 +43,18 @@ /* Recycle some stuff from the CLI interface */ #define fdprintf ast_cli +typedef struct agi_state { + int fd; /* FD for general output */ + int audio; /* FD for audio output */ + int ctrl; /* FD for input control */ +} AGI; + typedef struct agi_command { /* Null terminated list of the words of the command */ char *cmda[AST_MAX_CMD_LEN]; - /* Handler for the command (fd for output, # of arguments, argument list). + /* Handler for the command (channel, AGI state, # of arguments, argument list). Returns RESULT_SHOWUSAGE for improper arguments */ - int (*handler)(struct ast_channel *chan, int fd, int argc, char *argv[]); + int (*handler)(struct ast_channel *chan, AGI *agi, int argc, char *argv[]); /* Summary of the command (< 60 characters) */ char *summary; /* Detailed usage information */ @@ -58,15 +65,18 @@ static char *tdesc = "Asterisk Gateway Interface (AGI)"; static char *app = "AGI"; +static char *eapp = "EAGI"; + static char *synopsis = "Executes an AGI compliant application"; static char *descrip = -" AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" +" [E]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" "program on a channel. AGI allows Asterisk to launch external programs\n" "written in any language to control a telephony channel, play audio,\n" "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n" "and stdout. Returns -1 on hangup or if application requested hangup, or\n" -"0 on non-hangup exit.\n"; +"0 on non-hangup exit. Using 'EAGI' provides enhanced AGI, with audio\n" +"available out of band on file descriptor 3\n"; STANDARD_LOCAL_USER; @@ -75,33 +85,15 @@ LOCAL_USER_DECL; #define TONE_BLOCK_SIZE 200 -static float loudness = 8192.0; - -unsigned char linear2ulaw(short sample); -static void make_tone_block(unsigned char *data, float f1, int *x); - -static void make_tone_block(unsigned char *data, float f1, int *x) -{ -int i; -float val; - - for(i = 0; i < TONE_BLOCK_SIZE; i++) - { - val = loudness * sin((f1 * 2.0 * M_PI * (*x)++)/8000.0); - data[i] = linear2ulaw((int)val); - } - /* wrap back around from 8000 */ - if (*x >= 8000) *x = 0; - return; -} - -static int launch_script(char *script, char *args, int *fds, int *opid) +static int launch_script(char *script, char *args, int *fds, int *efd, int *opid) { char tmp[256]; int pid; int toast[2]; int fromast[2]; + int audio[2]; int x; + int res; if (script[0] != '/') { snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script); script = tmp; @@ -116,17 +108,45 @@ static int launch_script(char *script, char *args, int *fds, int *opid) close(toast[1]); return -1; } + if (efd) { + if (pipe(audio)) { + ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno)); + close(fromast[0]); + close(fromast[1]); + close(toast[0]); + close(toast[1]); + return -1; + } + res = fcntl(audio[1], F_GETFL); + if (res > -1) + res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK); + if (res < 0) { + ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno)); + close(fromast[0]); + close(fromast[1]); + close(toast[0]); + close(toast[1]); + close(audio[0]); + close(audio[1]); + return -1; + } + } pid = fork(); if (pid < 0) { ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); return -1; } if (!pid) { - /* Redirect stdin and out */ + /* Redirect stdin and out, provide enhanced audio channel if desired */ dup2(fromast[0], STDIN_FILENO); dup2(toast[1], STDOUT_FILENO); + if (efd) { + dup2(audio[0], STDERR_FILENO + 1); + } else { + close(STDERR_FILENO + 1); + } /* Close everything but stdin/out/error */ - for (x=STDERR_FILENO + 1;x<1024;x++) + for (x=STDERR_FILENO + 2;x<1024;x++) close(x); /* Execute script */ execl(script, script, args, NULL); @@ -138,6 +158,9 @@ static int launch_script(char *script, char *args, int *fds, int *opid) ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script); fds[0] = toast[0]; fds[1] = fromast[1]; + if (efd) { + *efd = audio[1]; + } /* close what we're not using in the parent */ close(toast[1]); close(fromast[0]); @@ -146,7 +169,7 @@ static int launch_script(char *script, char *args, int *fds, int *opid) } -static void setup_env(struct ast_channel *chan, char *request, int fd) +static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced) { /* Print initial environment, with agi_request always being the first thing */ @@ -164,12 +187,13 @@ static void setup_env(struct ast_channel *chan, char *request, int fd) fdprintf(fd, "agi_context: %s\n", chan->context); fdprintf(fd, "agi_extension: %s\n", chan->exten); fdprintf(fd, "agi_priority: %d\n", chan->priority); + fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0"); /* End with empty return */ fdprintf(fd, "\n"); } -static int handle_answer(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; res = 0; @@ -177,14 +201,14 @@ static int handle_answer(struct ast_channel *chan, int fd, int argc, char *argv[ /* Answer the chan */ res = ast_answer(chan); } - fdprintf(fd, "200 result=%d\n", res); + fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_waitfordigit(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int to; @@ -192,15 +216,15 @@ static int handle_waitfordigit(struct ast_channel *chan, int fd, int argc, char return RESULT_SHOWUSAGE; if (sscanf(argv[3], "%i", &to) != 1) return RESULT_SHOWUSAGE; - res = ast_waitfordigit(chan, to); - fdprintf(fd, "200 result=%d\n", res); + res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl); + fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_sendtext(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) @@ -213,34 +237,34 @@ static int handle_sendtext(struct ast_channel *chan, int fd, int argc, char *arg parsing, then here, add a newline at the end of the string before sending it to ast_sendtext --DUDE */ res = ast_sendtext(chan, argv[2]); - fdprintf(fd, "200 result=%d\n", res); + fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_recvchar(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) return RESULT_SHOWUSAGE; res = ast_recvchar(chan,atoi(argv[2])); if (res == 0) { - fdprintf(fd, "200 result=%d (timeout)\n", res); + fdprintf(agi->fd, "200 result=%d (timeout)\n", res); return RESULT_SUCCESS; } if (res > 0) { - fdprintf(fd, "200 result=%d\n", res); + fdprintf(agi->fd, "200 result=%d\n", res); return RESULT_SUCCESS; } else { - fdprintf(fd, "200 result=%d (hangup)\n", res); + fdprintf(agi->fd, "200 result=%d (hangup)\n", res); return RESULT_FAILURE; } } -static int handle_tddmode(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res,x; if (argc != 3) @@ -249,14 +273,14 @@ static int handle_tddmode(struct ast_channel *chan, int fd, int argc, char *argv if (!strncasecmp(argv[2],"mate",4)) x = 2; if (!strncasecmp(argv[2],"tdd",3)) x = 1; res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0); - fdprintf(fd, "200 result=%d\n", res); + fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_sendimage(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) @@ -264,14 +288,14 @@ static int handle_sendimage(struct ast_channel *chan, int fd, int argc, char *ar res = ast_send_image(chan, argv[2]); if (!ast_check_hangup(chan)) res = 0; - fdprintf(fd, "200 result=%d\n", res); + fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_streamfile(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; struct ast_filestream *fs; @@ -287,7 +311,7 @@ static int handle_streamfile(struct ast_channel *chan, int fd, int argc, char *a fs = ast_openstream(chan, argv[2], chan->language); if(!fs){ - fdprintf(fd, "200 result=%d endpos=%ld\n", 0, sample_offset); + fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset); ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]); return RESULT_FAILURE; } @@ -297,25 +321,29 @@ static int handle_streamfile(struct ast_channel *chan, int fd, int argc, char *a res = ast_applystream(chan, fs); res = ast_playstream(fs); if (res) { - fdprintf(fd, "200 result=%d endpos=%ld\n", res, sample_offset); + fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); if (res >= 0) return RESULT_SHOWUSAGE; else return RESULT_FAILURE; } - res = ast_waitstream(chan, argv[3]); + res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); /* this is to check for if ast_waitstream closed the stream, we probably are at * the end of the stream, return that amount, else check for the amount */ sample_offset = (chan->stream)?ast_tellstream(fs):max_length; ast_stopstream(chan); - fdprintf(fd, "200 result=%d endpos=%ld\n", res, sample_offset); + if (res == 1) { + /* Stop this command, don't print a result line, as there is a new command */ + return RESULT_SUCCESS; + } + fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_saynumber(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; @@ -323,15 +351,17 @@ static int handle_saynumber(struct ast_channel *chan, int fd, int argc, char *ar return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%i", &num) != 1) return RESULT_SHOWUSAGE; - res = ast_say_number(chan, num, argv[3], chan->language); - fdprintf(fd, "200 result=%d\n", res); + res = ast_say_number_full(chan, num, argv[3], chan->language, agi->audio, agi->ctrl); + if (res == 1) + return RESULT_SUCCESS; + fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_saydigits(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; @@ -339,17 +369,17 @@ static int handle_saydigits(struct ast_channel *chan, int fd, int argc, char *ar return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%i", &num) != 1) return RESULT_SHOWUSAGE; - res = ast_say_digit_str(chan, argv[2], argv[3], chan->language); - fdprintf(fd, "200 result=%d\n", res); + res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); + if (res == 1) /* New command */ + return RESULT_SUCCESS; + fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout); - -static int handle_getdata(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; char data[1024]; @@ -360,37 +390,39 @@ static int handle_getdata(struct ast_channel *chan, int fd, int argc, char *argv return RESULT_SHOWUSAGE; if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0; if (argc >= 5) max = atoi(argv[4]); else max = 1024; - res = ast_app_getdata(chan, argv[2], data, max, timeout); - if (res == 1) - fdprintf(fd, "200 result=%s (timeout)\n", data); + res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl); + if (res == 2) /* New command */ + return RESULT_SUCCESS; + else if (res == 1) + fdprintf(agi->fd, "200 result=%s (timeout)\n", data); else - fdprintf(fd, "200 result=%s\n", data); + fdprintf(agi->fd, "200 result=%s\n", data); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } -static int handle_setcontext(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; strncpy(chan->context, argv[2], sizeof(chan->context)-1); - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } -static int handle_setextension(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argc != 3) return RESULT_SHOWUSAGE; strncpy(chan->exten, argv[2], sizeof(chan->exten)-1); - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } -static int handle_setpriority(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int pri; if (argc != 3) @@ -398,20 +430,11 @@ static int handle_setpriority(struct ast_channel *chan, int fd, int argc, char * if (sscanf(argv[2], "%i", &pri) != 1) return RESULT_SHOWUSAGE; chan->priority = pri - 1; - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } -static int ms_diff(struct timeval *tv1, struct timeval *tv2) -{ -int ms; - - ms = (tv1->tv_sec - tv2->tv_sec) * 1000; - ms += (tv1->tv_usec - tv2->tv_usec) / 1000; - return(ms); -} - -static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { struct ast_filestream *fs; struct ast_frame *f; @@ -420,6 +443,8 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a int res = 0; int ms; + /* XXX EAGI FIXME XXX */ + if (argc < 6) return RESULT_SHOWUSAGE; if (sscanf(argv[5], "%i", &ms) != 1) @@ -438,7 +463,7 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY, 0, 0644); if (!fs) { res = -1; - fdprintf(fd, "200 result=%d (writefile)\n", res); + fdprintf(agi->fd, "200 result=%d (writefile)\n", res); return RESULT_FAILURE; } @@ -454,12 +479,12 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a res = ast_waitfor(chan, -1); if (res < 0) { ast_closestream(fs); - fdprintf(fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset); + fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset); return RESULT_FAILURE; } f = ast_read(chan); if (!f) { - fdprintf(fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset); + fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset); ast_closestream(fs); return RESULT_FAILURE; } @@ -468,7 +493,7 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a if (strchr(argv[4], f->subclass)) { /* This is an interrupting chracter */ sample_offset = ast_tellstream(fs); - fdprintf(fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset); + fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset); ast_closestream(fs); ast_frfree(f); return RESULT_SUCCESS; @@ -485,14 +510,14 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a ast_frfree(f); gettimeofday(&tv, NULL); } - fdprintf(fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); + fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); ast_closestream(fs); } else - fdprintf(fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset); + fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset); return RESULT_SUCCESS; } -static int handle_autohangup(struct ast_channel *chan, int fd, int argc, char *argv[]) +static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int timeout; @@ -506,17 +531,17 @@ static int handle_autohangup(struct ast_channel *chan, int fd, int argc, char *a chan->whentohangup = time(NULL) + timeout; else chan->whentohangup = 0; - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } -static int handle_hangup(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv) { struct ast_channel *c; if (argc==1) { /* no argument: hangup the current channel */ ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT); - fdprintf(fd, "200 result=1\n"); + fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } else if (argc==2) { /* one argument: look for info on the specified channel */ @@ -525,20 +550,20 @@ static int handle_hangup(struct ast_channel *chan, int fd, int argc, char **argv if (strcasecmp(argv[1],c->name)==0) { /* we have a matching channel */ ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT); - fdprintf(fd, "200 result=1\n"); + fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } c = ast_channel_walk(c); } /* if we get this far no channel name matched the argument given */ - fdprintf(fd, "200 result=-1\n"); + fdprintf(agi->fd, "200 result=-1\n"); return RESULT_SUCCESS; } else { return RESULT_SHOWUSAGE; } } -static int handle_exec(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; struct ast_app *app; @@ -557,68 +582,68 @@ static int handle_exec(struct ast_channel *chan, int fd, int argc, char **argv) ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]); res = -2; } - fdprintf(fd, "200 result=%d\n", res); + fdprintf(agi->fd, "200 result=%d\n", res); return res; } -static int handle_setcallerid(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argv[2]) ast_set_callerid(chan, argv[2], 0); /* strncpy(chan->callerid, argv[2], sizeof(chan->callerid)-1); -*/ fdprintf(fd, "200 result=1\n"); +*/ fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } -static int handle_channelstatus(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv) { struct ast_channel *c; if (argc==2) { /* no argument: supply info on the current channel */ - fdprintf(fd, "200 result=%d\n", chan->_state); + fdprintf(agi->fd, "200 result=%d\n", chan->_state); return RESULT_SUCCESS; } else if (argc==3) { /* one argument: look for info on the specified channel */ c = ast_channel_walk(NULL); while (c) { if (strcasecmp(argv[2],c->name)==0) { - fdprintf(fd, "200 result=%d\n", c->_state); + fdprintf(agi->fd, "200 result=%d\n", c->_state); return RESULT_SUCCESS; } c = ast_channel_walk(c); } /* if we get this far no channel name matched the argument given */ - fdprintf(fd, "200 result=-1\n"); + fdprintf(agi->fd, "200 result=-1\n"); return RESULT_SUCCESS; } else { return RESULT_SHOWUSAGE; } } -static int handle_setvariable(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argv[3]) pbx_builtin_setvar_helper(chan, argv[2], argv[3]); - fdprintf(fd, "200 result=1\n"); + fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } -static int handle_getvariable(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) { char *tempstr; if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])) ) - fdprintf(fd, "200 result=1 (%s)\n", tempstr); + fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr); else - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } -static int handle_verbose(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int level = 0; char *prefix; @@ -648,12 +673,12 @@ static int handle_verbose(struct ast_channel *chan, int fd, int argc, char **arg if (level <= option_verbose) ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]); - fdprintf(fd, "200 result=1\n"); + fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } -static int handle_dbget(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; char tmp[256]; @@ -661,42 +686,42 @@ static int handle_dbget(struct ast_channel *chan, int fd, int argc, char **argv) return RESULT_SHOWUSAGE; res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp)); if (res) - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); else - fdprintf(fd, "200 result=1 (%s)\n", tmp); + fdprintf(agi->fd, "200 result=1 (%s)\n", tmp); return RESULT_SUCCESS; } -static int handle_dbput(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if (argc != 5) return RESULT_SHOWUSAGE; res = ast_db_put(argv[2], argv[3], argv[4]); if (res) - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); else - fdprintf(fd, "200 result=1\n"); + fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } -static int handle_dbdel(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_db_del(argv[2], argv[3]); if (res) - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); else - fdprintf(fd, "200 result=1\n"); + fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } -static int handle_dbdeltree(struct ast_channel *chan, int fd, int argc, char **argv) +static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if ((argc < 3) || (argc > 4)) @@ -707,9 +732,15 @@ static int handle_dbdeltree(struct ast_channel *chan, int fd, int argc, char **a res = ast_db_deltree(argv[2], NULL); if (res) - fdprintf(fd, "200 result=0\n"); + fdprintf(agi->fd, "200 result=0\n"); else - fdprintf(fd, "200 result=1\n"); + fdprintf(agi->fd, "200 result=1\n"); + return RESULT_SUCCESS; +} + +static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[]) +{ + fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } @@ -878,6 +909,10 @@ static char usage_autohangup[] = "future. Of course it can be hungup before then as well. Setting to\n" "0 will cause the autohangup feature to be disabled on this channel.\n"; +static char usage_noop[] = +" Usage: NOOP\n" +" Does nothing.\n"; + agi_command commands[] = { { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer }, { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit }, @@ -904,7 +939,8 @@ agi_command commands[] = { { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget }, { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput }, { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel }, - { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree } + { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree }, + { { "noop", NULL }, handle_noop, "Does nothing", usage_noop } }; static void join(char *s, int len, char *w[]) @@ -1040,7 +1076,7 @@ normal: return 0; } -static int agi_handle_command(struct ast_channel *chan, int fd, char *buf) +static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf) { char *argv[MAX_ARGS]; int argc = 0; @@ -1055,12 +1091,12 @@ static int agi_handle_command(struct ast_channel *chan, int fd, char *buf) #endif c = find_command(argv, 0); if (c) { - res = c->handler(chan, fd, argc, argv); + res = c->handler(chan, agi, argc, argv); switch(res) { case RESULT_SHOWUSAGE: - fdprintf(fd, "520-Invalid command syntax. Proper usage follows:\n"); - fdprintf(fd, c->usage); - fdprintf(fd, "520 End of proper usage.\n"); + fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n"); + fdprintf(agi->fd, c->usage); + fdprintf(agi->fd, "520 End of proper usage.\n"); break; case RESULT_FAILURE: /* They've already given the failure. We've been hung up on so handle this @@ -1068,12 +1104,12 @@ static int agi_handle_command(struct ast_channel *chan, int fd, char *buf) return -1; } } else { - fdprintf(fd, "510 Invalid or unknown command\n"); + fdprintf(agi->fd, "510 Invalid or unknown command\n"); } return 0; } -static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid) +static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid) { struct ast_channel *c; int outfd; @@ -1082,16 +1118,16 @@ static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid) struct ast_frame *f; char buf[2048]; FILE *readf; - if (!(readf = fdopen(fds[0], "r"))) { + if (!(readf = fdopen(agi->ctrl, "r"))) { ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n"); kill(pid, SIGHUP); return -1; } setlinebuf(readf); - setup_env(chan, request, fds[1]); + setup_env(chan, request, agi->fd, (agi->audio > -1)); for (;;) { ms = -1; - c = ast_waitfor_nandfds(&chan, 1, &fds[0], 1, NULL, &outfd, &ms); + c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms); if (c) { /* Idle the channel until we get a command */ f = ast_read(c); @@ -1100,6 +1136,11 @@ static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid) returnstatus = -1; break; } else { + /* If it's voice, write it to the audio pipe */ + if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) { + /* Write, ignoring errors */ + write(agi->audio, f->data, f->datalen); + } ast_frfree(f); } } else if (outfd > -1) { @@ -1117,7 +1158,7 @@ static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid) if (*buf && buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0; - returnstatus |= agi_handle_command(chan, fds[1], buf); + returnstatus |= agi_handle_command(chan, agi, buf); /* If the handle_command returns -1, we need to stop */ if (returnstatus < 0) { break; @@ -1213,21 +1254,24 @@ static int handle_dumpagihtml(int fd, int argc, char *argv[]) { return RESULT_SUCCESS; } -static int agi_exec(struct ast_channel *chan, void *data) +static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced) { int res=0; struct localuser *u; char *args,*ringy; char tmp[256]; int fds[2]; + int efd = -1; int pid; char *stringp=tmp; + AGI agi; if (!data || !strlen(data)) { ast_log(LOG_WARNING, "AGI requires an argument (script)\n"); return -1; } + memset(&agi, 0, sizeof(agi)); strncpy(tmp, data, sizeof(tmp)-1); strsep(&stringp, "|"); args = strsep(&stringp, "|"); @@ -1249,16 +1293,44 @@ static int agi_exec(struct ast_channel *chan, void *data) } } #endif - res = launch_script(tmp, args, fds, &pid); + res = launch_script(tmp, args, fds, enhanced ? &efd : NULL, &pid); if (!res) { - res = run_agi(chan, tmp, fds, pid); + agi.fd = fds[1]; + agi.ctrl = fds[0]; + agi.audio = efd; + res = run_agi(chan, tmp, &agi, pid); close(fds[0]); close(fds[1]); + if (efd > -1) + close(efd); } LOCAL_USER_REMOVE(u); return res; } +static int agi_exec(struct ast_channel *chan, void *data) +{ + return agi_exec_full(chan, data, 0); +} + +static int eagi_exec(struct ast_channel *chan, void *data) +{ + int readformat; + int res; + readformat = chan->readformat; + if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { + ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name); + return -1; + } + res = agi_exec_full(chan, data, 1); + if (!res) { + if (ast_set_read_format(chan, readformat)) { + ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %d\n", chan->name, readformat); + } + } + return res; +} + static char showagi_help[] = "Usage: show agi [topic]\n" " When called with a topic as an argument, displays usage\n" @@ -1281,6 +1353,7 @@ int unload_module(void) STANDARD_HANGUP_LOCALUSERS; ast_cli_unregister(&showagi); ast_cli_unregister(&dumpagihtml); + ast_unregister_application(eapp); return ast_unregister_application(app); } @@ -1288,6 +1361,7 @@ int load_module(void) { ast_cli_register(&showagi); ast_cli_register(&dumpagihtml); + ast_register_application(eapp, eagi_exec, synopsis, descrip); return ast_register_application(app, agi_exec, synopsis, descrip); } @@ -1308,44 +1382,3 @@ char *key() return ASTERISK_GPL_KEY; } -#define CLIP 32635 -#define BIAS 0x84 - -unsigned char -linear2ulaw(sample) -short sample; { - static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; - int sign, exponent, mantissa; - unsigned char ulawbyte; - - /* Get the sample into sign-magnitude. */ - sign = (sample >> 8) & 0x80; /* set aside the sign */ - if (sign != 0) sample = -sample; /* get magnitude */ - if (sample > CLIP) sample = CLIP; /* clip the magnitude */ - - /* Convert from 16 bit linear to ulaw. */ - sample = sample + BIAS; - exponent = exp_lut[(sample >> 7) & 0xFF]; - mantissa = (sample >> (exponent + 3)) & 0x0F; - ulawbyte = ~(sign | (exponent << 4) | mantissa); -#ifdef ZEROTRAP - if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */ -#endif - - return(ulawbyte); -} diff --git a/apps/app_waitforring.c b/apps/app_waitforring.c new file mode 100755 index 000000000..06a18a7d8 --- /dev/null +++ b/apps/app_waitforring.c @@ -0,0 +1,130 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Wait for Ring Application + * + * Copyright (C) 2003, Digium + * + * Mark Spencer + * + * 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 + + +static char *synopsis = "Wait for Ring Application"; + +static char *tdesc = "Waits until first ring after time"; + +static char *desc = " WaitForRing(timeout)\n" +"Returns 0 after waiting at least timeout seconds. and\n" +"only after the next ring has completed. Returns 0 on\n" +"success or -1 on hangup\n"; + +static char *app = "WaitForRing"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static int waitforring_exec(struct ast_channel *chan, void *data) +{ + struct localuser *u; + struct ast_frame *f; + int res = 0; + int ms; + if (!data || (sscanf(data, "%d", &ms) != 1)) { + ast_log(LOG_WARNING, "SoftHangup requires an argument (minimum seconds)\n"); + return 0; + } + ms *= 1000; + LOCAL_USER_ADD(u); + while(ms > 0) { + ms = ast_waitfor(chan, ms); + if (ms < 0) { + res = ms; + break; + } + if (ms > 0) { + f = ast_read(chan); + if (!f) { + res = -1; + break; + } + if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Got a ring but still waiting for timeout\n"); + } + ast_frfree(f); + } + } + /* Now we're really ready for the ring */ + if (!res) { + ms = 99999999; + while(ms > 0) { + ms = ast_waitfor(chan, ms); + if (ms < 0) { + res = ms; + break; + } + if (ms > 0) { + f = ast_read(chan); + if (!f) { + res = -1; + break; + } + if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Got a ring after the timeout\n"); + ast_frfree(f); + break; + } + ast_frfree(f); + } + } + } + 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, waitforring_exec, synopsis, desc); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff --git a/channel.c b/channel.c index e75f74723..6e6638c58 100755 --- a/channel.c +++ b/channel.c @@ -869,6 +869,7 @@ int ast_waitfor(struct ast_channel *c, int ms) char ast_waitfordigit(struct ast_channel *c, int ms) { + /* XXX Should I be merged with waitfordigit_full XXX */ struct ast_frame *f; char result = 0; /* Stop if we're a zombie or need a soft hangup */ @@ -893,6 +894,36 @@ char ast_waitfordigit(struct ast_channel *c, int ms) return result; } +char ast_waitfordigit_full(struct ast_channel *c, int ms, int audio, int ctrl) +{ + struct ast_frame *f; + char result = 0; + struct ast_channel *rchan; + int outfd; + /* Stop if we're a zombie or need a soft hangup */ + if (c->zombie || ast_check_hangup(c)) + return -1; + /* Wait for a digit, no more than ms milliseconds total. */ + while(ms && !result) { + rchan = ast_waitfor_nandfds(&c, 1, &audio, (audio > -1) ? 1 : 0, NULL, &outfd, &ms); + if ((!rchan) && (outfd < 0) && (ms)) /* Error */ + result = -1; + else if (outfd > -1) { + result = 1; + } else if (rchan) { + /* Read something */ + f = ast_read(c); + if (f) { + if (f->frametype == AST_FRAME_DTMF) + result = f->subclass; + ast_frfree(f); + } else + result = -1; + } + } + return result; +} + struct ast_frame *ast_read(struct ast_channel *chan) { struct ast_frame *f = NULL; @@ -1383,6 +1414,7 @@ int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int fti int pos=0; int to = ftimeout; char d; + /* XXX Merge with full version? XXX */ /* Stop if we're a zombie or need a soft hangup */ if (c->zombie || ast_check_hangup(c)) return -1; @@ -1416,6 +1448,48 @@ int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int fti return 0; } +int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd) +{ + int pos=0; + int to = ftimeout; + char d; + /* Stop if we're a zombie or need a soft hangup */ + if (c->zombie || ast_check_hangup(c)) + return -1; + if (!len) + return -1; + do { + if (c->streamid > -1) { + d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd); + ast_stopstream(c); + usleep(1000); + if (!d) + d = ast_waitfordigit_full(c, to, audiofd, ctrlfd); + } else { + d = ast_waitfordigit_full(c, to, audiofd, ctrlfd); + } + if (d < 0) + return -1; + if (d == 0) { + s[pos]='\0'; + return 1; + } + if (d == 1) { + s[pos]='\0'; + return 2; + } + if (!strchr(enders, d)) + s[pos++] = d; + if (strchr(enders, d) || (pos >= len)) { + s[pos]='\0'; + return 0; + } + to = timeout; + } while(1); + /* Never reached */ + return 0; +} + int ast_channel_supports_html(struct ast_channel *chan) { if (chan->pvt->send_html) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 557e83b6a..fa0c1fa81 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -698,13 +698,38 @@ static int sip_hangup(struct ast_channel *ast) static int sip_answer(struct ast_channel *ast) { - int res = 0; + int res = 0,fmt,capability; + char *codec; struct sip_pvt *p = ast->pvt->pvt; + struct sip_codec_pref *oldpref=NULL; + + if (ast->_state != AST_STATE_UP) { + + + + codec=pbx_builtin_getvar_helper(p->owner,"SIP_CODEC"); + if (codec) { + ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC) variable\n",codec); + fmt=ast_getformatbyname(codec); + if (fmt) { + oldpref=prefs; + prefs=NULL; + sip_pref_append(fmt); + capability=p->capability; + p->capability=fmt; + } else ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because of unrecognized codec: %s\n",codec); + } + ast_setstate(ast, AST_STATE_UP); if (option_debug) ast_log(LOG_DEBUG, "sip_answer(%s)\n", ast->name); res = transmit_response_with_sdp(p, "200 OK", &p->initreq); + sip_prefs_free(); + if (oldpref) { + prefs=oldpref; + p->capability=capability; + } } return res; } @@ -1676,7 +1701,7 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * if ((codec = ast2rtp(cur->codec)) > -1) { snprintf(costr, sizeof(costr), " %d", codec); strcat(m, costr); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast2rtpn(x)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast2rtpn(rtp2ast(codec))); strcat(a, costr); } } diff --git a/contrib/scripts/vmail.cgi b/contrib/scripts/vmail.cgi index 7071b34f4..4d5528714 100755 --- a/contrib/scripts/vmail.cgi +++ b/contrib/scripts/vmail.cgi @@ -341,11 +341,12 @@ sub message_audio() # Mailbox and folder are already verified if (open(AUDIO, "<$path")) { + $size = -s $path; $|=1; if ($forcedownload) { - print header(-type=>$formats{$format}->{'mime'}, -attachment => "msg${msgid}.$format"); + print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size, -attachment => "msg${msgid}.$format"); } else { - print header(-type=>$formats{$format}->{'mime'}); + print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size); } while(($amt = sysread(AUDIO, $data, 4096)) > 0) { diff --git a/file.c b/file.c index aeabc77aa..a2e1efb17 100755 --- a/file.c +++ b/file.c @@ -605,6 +605,7 @@ struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, char ast_waitstream(struct ast_channel *c, char *breakon) { + /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */ int res; struct ast_frame *fr; while(c->stream) { @@ -658,3 +659,67 @@ char ast_waitstream(struct ast_channel *c, char *breakon) return (c->_softhangup ? -1 : 0); } +char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int cmdfd) +{ + int res; + int ms; + int outfd; + struct ast_frame *fr; + struct ast_channel *rchan; + + while(c->stream) { + ms = ast_sched_wait(c->sched); + if (ms < 0) { + ast_closestream(c->stream); + break; + } + rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms); + if (!rchan && (outfd < 0) && (ms)) { + ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno)); + return -1; + } else if (outfd > -1) { + /* The FD we were watching has something waiting */ + return 1; + } else if (rchan) { + fr = ast_read(c); + if (!fr) { +#if 0 + ast_log(LOG_DEBUG, "Got hung up\n"); +#endif + return -1; + } + + switch(fr->frametype) { + case AST_FRAME_DTMF: + res = fr->subclass; + if (strchr(breakon, res)) { + ast_frfree(fr); + return res; + } + break; + case AST_FRAME_CONTROL: + switch(fr->subclass) { + case AST_CONTROL_HANGUP: + ast_frfree(fr); + return -1; + case AST_CONTROL_RINGING: + case AST_CONTROL_ANSWER: + /* Unimportant */ + break; + default: + ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass); + } + case AST_FRAME_VOICE: + /* Write audio if appropriate */ + if (audiofd > -1) + write(audiofd, fr->data, fr->datalen); + } + /* Ignore */ + ast_frfree(fr); + } else + ast_sched_runq(c->sched); + + + } + return (c->_softhangup ? -1 : 0); +} diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 44c4186d2..bcbccb726 100755 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -33,6 +33,9 @@ extern "C" { */ extern int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout); +/* Full version with audiofd and controlfd. NOTE: returns '2' on ctrlfd available, not '1' like other full functions */ +extern int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd); + //! Record voice (after playing prompt if specified), waiting for silence (in ms) up to a given timeout (in s) or '#' int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec); diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 9673fb099..b14d3c928 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -465,6 +465,10 @@ struct ast_channel *ast_channel_walk(struct ast_channel *prev); * Wait for a digit. Returns <0 on error, 0 on no entry, and the digit on success. */ char ast_waitfordigit(struct ast_channel *c, int ms); +/* Same as above with audio fd for outputing read audio and ctrlfd to monitor for + reading. Returns 1 if ctrlfd becomes available */ +char ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int ctrlfd); + //! Reads multiple digits /*! * \param c channel to read from @@ -476,8 +480,10 @@ char ast_waitfordigit(struct ast_channel *c, int ms); * Read in a digit string "s", max length "len", maximum timeout between digits "timeout" (-1 for none), terminated by anything in "enders". Give them rtimeout for the first digit. Returns 0 on normal return, or 1 on a timeout. In the case of - a timeout, any digits that were read before the timeout will still be available in s. */ + a timeout, any digits that were read before the timeout will still be available in s. + RETURNS 2 in full version when ctrlfd is available, NOT 1*/ int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders); +int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders, int audiofd, int ctrlfd); /*! Report DTMF on channel 0 */ #define AST_BRIDGE_DTMF_CHANNEL_0 (1 << 0) diff --git a/include/asterisk/file.h b/include/asterisk/file.h index 79febcb48..e7f19825d 100755 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -123,6 +123,10 @@ int ast_filecopy(char *oldname, char *newname, char *fmt); */ char ast_waitstream(struct ast_channel *c, char *breakon); +/* Same as waitstream, but with audio output to fd and monitored fd checking. Returns + 1 if monfd is ready for reading */ +char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int monfd); + //! Starts writing a file /*! * \param filename the name of the file to write to diff --git a/include/asterisk/say.h b/include/asterisk/say.h index c0eb7ec70..7c0d911d7 100755 --- a/include/asterisk/say.h +++ b/include/asterisk/say.h @@ -34,6 +34,9 @@ extern "C" { */ int ast_say_number(struct ast_channel *chan, int num, char *ints, char *lang); +/* Same as above with audiofd for received audio and returns 1 on ctrlfd being readable */ +int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd); + //! says digits /*! * \param chan channel to act upon @@ -44,6 +47,7 @@ int ast_say_number(struct ast_channel *chan, int num, char *ints, char *lang); * Returns 0 on success, dtmf if interrupted, -1 on failure */ int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang); +int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd); //! says digits of a string /*! @@ -55,6 +59,7 @@ int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang); * Returns 0 on success, dtmf if interrupted, -1 on failure */ int ast_say_digit_str(struct ast_channel *chan, char *num, char *ints, char *lang); +int ast_say_digit_str_full(struct ast_channel *chan, char *num, char *ints, char *lang, int audiofd, int ctrlfd); int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang); diff --git a/pbx.c b/pbx.c index bd09b720d..d2c7bf90b 100755 --- a/pbx.c +++ b/pbx.c @@ -727,88 +727,99 @@ static void pbx_substitute_variables_temp(struct ast_channel *c,char *cp3,char * *cp4 = pri; } 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,strlen(cp3))==0) +#if 0 + ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",cp3,ast_var_name(variables)); +#endif + if (strcasecmp(ast_var_name(variables),cp3)==0) *cp4=ast_var_value(variables); } if (!(*cp4)) { /* Try globals */ AST_LIST_TRAVERSE(&globals,variables,entries) { -// ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",cp3,ast_var_name(variables)); - if (strncasecmp(ast_var_name(variables),cp3,strlen(cp3))==0) +#if 0 + ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",cp3,ast_var_name(variables)); +#endif + if (strcasecmp(ast_var_name(variables),cp3)==0) *cp4=ast_var_value(variables); } } } } -static void pbx_substitute_variables_helper(struct ast_channel *c,char *cp1,char **ecp2) +static void pbx_substitute_variables_helper(struct ast_channel *c,char *cp1,char **ecp2,int count) { char *cp4,*cp2; - char *tmp,*wherearewe,*finish; - int length; + char *tmp,*wherearewe,*finish,*ltmp,*lval,*nextvar; + int length,variables=0; wherearewe=tmp=cp1; cp2=*ecp2; *cp2='\0'; do { + char *start,*start2; if (!(*wherearewe)) break; if ((tmp=strstr(wherearewe,"${"))) { + variables++; length=(int)(tmp-wherearewe); strncat(cp2,wherearewe,length); wherearewe=tmp; - if (!strncmp(tmp+2,"${",2)) { - char *ltmp,*lval; - ltmp=malloc(sizeof(char)*256); - finish=strchr(tmp+2,'}'); - /* get the one before the last closing bracket */ - do { - if (strlen(finish)<2) - break; - if (finish[1]=='}' && finish[2]=='}') - finish++; - else break; - } while (1); - - if (!finish) { - ast_log(LOG_WARNING, "Something went wrong with ${VARIABLE}\n"); - *ecp2=""; - break; - } - length=(int)(finish-tmp-1); - wherearewe+=length+3; - lval=strndup(tmp+2,length); - pbx_substitute_variables_helper(c,lval,<mp); - free(lval); - pbx_substitute_variables_temp(c,ltmp,&cp4); - if (cp4) { - length=strlen(cp4); - strncat(cp2,cp4,length); - } - } else { - char value[256]; - finish=strchr(tmp+2,'}'); - if (!finish) { - ast_log(LOG_WARNING, "Something went wrong with ${VARIABLE}\n"); - *ecp2=""; - break; + + ltmp=malloc(sizeof(char)*256); + start=start2=tmp+1; + do { + if (variables==0) { + nextvar=strstr(start2,"${"); + if (nextvar) { + if ((int)(finish-nextvar)>0) { + variables++; + start2=nextvar+1; + } else break; + } else break; } - length=(int)(finish-tmp)-2; - wherearewe+=length+3; - strncpy(value,tmp+2,length); - value[length]='\0'; - pbx_substitute_variables_temp(c,value,&cp4); - if (cp4) { - length=strlen(cp4); - strncat(cp2,cp4,length); + finish=strchr(start,'}'); + if (finish) { + variables--; + start=finish+1; + } else { + if (variables>0) { + ast_log(LOG_NOTICE, "Error in extension logic\n"); + cp2[0]='\0'; + return; + } + break; } + } while (1); + + length=(int)(finish-tmp); + wherearewe+=length+1; + lval=strndup(tmp+2,length-2); + pbx_substitute_variables_helper(c,lval,<mp,count+1); + free(lval); + if (ltmp) { + length=strlen(ltmp); + strncat(cp2,ltmp,length); } } else { - if (*wherearewe) { - length=strlen(wherearewe); - strncat(cp2,wherearewe,length); + if (wherearewe!=cp1) { + if (*wherearewe) { + length=strlen(wherearewe); + strncat(cp2,wherearewe,length); + } + strcat(cp2,"\0"); + + cp1=cp2; + } + + pbx_substitute_variables_temp(c,cp1,&cp4); + + if (cp4) { + /* reset output variable so we could store the result */ + *cp2='\0'; + length=strlen(cp4); + strncat(cp2,cp4,length); + } else { + if (count) cp2[0]='\0'; } - strcat(cp2,"\0"); break; } } while(1); @@ -827,7 +838,7 @@ static void *pbx_substitute_variables(struct ast_channel *c, struct ast_exten *e cp1=e->data; cp2=malloc(sizeof(char)*256); - pbx_substitute_variables_helper(c,cp1,(char **)&cp2); + pbx_substitute_variables_helper(c,cp1,(char **)&cp2,0); /* Second stage, expression evaluation */ if ((strstr(cp2,"$[")==NULL)) { @@ -3527,7 +3538,7 @@ void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value headp=&globals; AST_LIST_TRAVERSE (headp,newvariable,entries) { - if (strncasecmp(ast_var_name(newvariable),name,strlen(name))==0) { + if (strcasecmp(ast_var_name(newvariable),name)==0) { /* there is already such a variable, delete it */ AST_LIST_REMOVE(headp,newvariable,ast_var_t,entries); ast_var_delete(newvariable); diff --git a/say.c b/say.c index 9cfdfc4a8..cee2b4e55 100755 --- a/say.c +++ b/say.c @@ -19,6 +19,7 @@ int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang) { + /* XXX Merge with full version? XXX */ char fn[256] = ""; int num = 0; int res = 0; @@ -33,14 +34,102 @@ int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lan return res; } +int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd) +{ + char fn[256] = ""; + int num = 0; + int res = 0; + while(fn2[num] && !res) { + snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); + res = ast_streamfile(chan, fn, lang); + if (!res) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + ast_stopstream(chan); + num++; + } + return res; +} + int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang) { + /* XXX Should I be merged with say_digits_full XXX */ char fn2[256]; snprintf(fn2, sizeof(fn2), "%d", num); return ast_say_digit_str(chan, fn2, ints, lang); } + +int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd) +{ + char fn2[256]; + snprintf(fn2, sizeof(fn2), "%d", num); + return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd); +} + +int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + char fn[256] = ""; + if (!num) + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + if (0) { + /* XXX Only works for english XXX */ + } else { + /* Use english numbers */ + language = "en"; + while(!res && (num || playh)) { + if (playh) { + snprintf(fn, sizeof(fn), "digits/hundred"); + playh = 0; + } else + if (num < 20) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 100) { + snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10); + num -= ((num / 10) * 10); + } else { + if (num < 1000){ + snprintf(fn, sizeof(fn), "digits/%d", (num/100)); + playh++; + num -= ((num / 100) * 100); + } else { + if (num < 1000000) { + res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd); + if (res) + return res; + num = num % 1000; + snprintf(fn, sizeof(fn), "digits/thousand"); + } else { + if (num < 1000000000) { + res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd); + if (res) + return res; + num = num % 1000000; + snprintf(fn, sizeof(fn), "digits/million"); + } else { + ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); + res = -1; + } + } + } + } + if (!res) { + res = ast_streamfile(chan, fn, language); + if (!res) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + ast_stopstream(chan); + } + + } + } + return res; +} + int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language) { + /* XXX Should I be merged with ast_say_number_full XXX */ int res = 0; int playh = 0; char fn[256] = ""; @@ -100,7 +189,6 @@ int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language } return res; } - int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang) { struct tm *tm; diff --git a/vmail.cgi b/vmail.cgi index 7071b34f4..4d5528714 100755 --- a/vmail.cgi +++ b/vmail.cgi @@ -341,11 +341,12 @@ sub message_audio() # Mailbox and folder are already verified if (open(AUDIO, "<$path")) { + $size = -s $path; $|=1; if ($forcedownload) { - print header(-type=>$formats{$format}->{'mime'}, -attachment => "msg${msgid}.$format"); + print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size, -attachment => "msg${msgid}.$format"); } else { - print header(-type=>$formats{$format}->{'mime'}); + print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size); } while(($amt = sysread(AUDIO, $data, 4096)) > 0) { -- cgit v1.2.3