--- a/README +++ b/README @@ -4,6 +4,8 @@ and the Asterisk.org developer community Copyright (C) 2001-2006 Digium, Inc. and other copyright holders. +Copyright (C) 2002-2005 Junghanns.NET GmbH +and other copyright holders. ================================================================ * SECURITY --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ -Asterisk is distributed under the GNU General Public License version 2 -and is also available under alternative licenses negotiated directly -with Digium, Inc. If you obtained Asterisk under the GPL, then the GPL -applies to all loadable Asterisk modules used on your system as well, +BRIstuffed Asterisk is distributed under the GNU General Public License version 2 +and is not available under any alternative licenses. +If you obtained BRIstuffed Asterisk under the GPL, then the GPL +applies to all loadable BRIstuffed Asterisk modules used on your system as well, except as defined below. The GPL (version 2) is included in this source tree in the file COPYING. --- a/doc/hardware.txt +++ b/doc/hardware.txt @@ -31,6 +31,19 @@ Zaptel compatible hardware * Wildcard TE410P - Quad T1/E1 switchable interface. Supports PRI and RBS signalling, as well as PPP, FR, and HDLC data modes. +-- Junghanns.NET (Primary author of BRIstuff) + http://www.junghanns.net + + * quadBRI PCI ISDN - 4port BRI ISDN interface, supports NT and TE mode + + * octoBRI PCI ISDN - 8port BRI ISDN interface, supports NT and TE mode + + * singleE1 PCI ISDN - Single E1 interface + + * doubleE1 PCI ISDN - Double E1 interface + + * uno/duo/quad GSM PCI - 1/2/4 channel GSM interface cards + Non-zaptel compatible hardware ============================== --- a/build_tools/make_defaults_h +++ b/build_tools/make_defaults_h @@ -17,6 +17,7 @@ cat << END #define AST_KEY_DIR "${INSTALL_PATH}${ASTDATADIR}/keys" #define AST_DB "${INSTALL_PATH}${ASTVARLIBDIR}/astdb" #define AST_TMP_DIR "${INSTALL_PATH}${ASTSPOOLDIR}/tmp" +#define AST_SYSTEM_NAME "asterisk" #define AST_CONFIG_FILE "${INSTALL_PATH}${ASTCONFPATH}" --- a/main/asterisk.c +++ b/main/asterisk.c @@ -2387,6 +2387,7 @@ static void ast_readconfig(void) ast_copy_string(ast_config_AST_PID, AST_PID, sizeof(ast_config_AST_PID)); ast_copy_string(ast_config_AST_SOCKET, AST_SOCKET, sizeof(ast_config_AST_SOCKET)); ast_copy_string(ast_config_AST_RUN_DIR, AST_RUN_DIR, sizeof(ast_config_AST_RUN_DIR)); + ast_copy_string(ast_config_AST_SYSTEM_NAME, AST_SYSTEM_NAME, sizeof(ast_config_AST_SYSTEM_NAME)); /* no asterisk.conf? no problem, use buildtime config! */ if (!cfg) { @@ -2511,6 +2512,8 @@ static void ast_readconfig(void) ast_copy_string(ast_config_AST_RUN_GROUP, v->value, sizeof(ast_config_AST_RUN_GROUP)); } else if (!strcasecmp(v->name, "systemname")) { ast_copy_string(ast_config_AST_SYSTEM_NAME, v->value, sizeof(ast_config_AST_SYSTEM_NAME)); + } else if (!strcasecmp(v->name, "uniquename")) { + ast_copy_string(ast_config_AST_SYSTEM_NAME, v->value, sizeof(ast_config_AST_SYSTEM_NAME)); } else if (!strcasecmp(v->name, "languageprefix")) { ast_language_is_prefix = ast_true(v->value); } --- a/include/asterisk/agi.h +++ b/include/asterisk/agi.h @@ -29,7 +29,8 @@ extern "C" { typedef struct agi_state { int fd; /* FD for general output */ - int audio; /* FD for audio output */ + int audio_out; /* FD for audio output */ + int audio_in; /* FD for audio output */ int ctrl; /* FD for input control */ unsigned int fast:1; /* flag for fast agi or not */ } AGI; --- a/res/res_agi.c +++ b/res/res_agi.c @@ -11,6 +11,9 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2005 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -75,16 +78,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi static char *app = "AGI"; +static char *xapp = "XAGI"; + static char *eapp = "EAGI"; static char *deadapp = "DeadAGI"; static char *synopsis = "Executes an AGI compliant application"; +static char *xsynopsis = "Executes an XAGI compliant application"; static char *esynopsis = "Executes an EAGI compliant application"; static char *deadsynopsis = "Executes AGI on a hungup channel"; static char *descrip = -" [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" +" [E|Dead|X]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" @@ -97,6 +103,8 @@ static char *descrip = "variable to \"no\" before executing the AGI application.\n" " Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n" "on file descriptor 3\n\n" +"Using 'XAGI' provides enhanced AGI, with incoming audio available out of band" +" on file descriptor 3 and outgoing audio available out of band on file descriptor 4\n\n" " Use the CLI command 'agi show' to list available agi commands\n" " This application sets the following channel variable upon completion:\n" " AGISTATUS The status of the attempt to the run the AGI script\n" @@ -236,13 +244,14 @@ static enum agi_result launch_netscript( return AGI_RESULT_SUCCESS_FAST; } -static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid) +static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *efd2, int *opid) { char tmp[256]; int pid; int toast[2]; int fromast[2]; int audio[2]; + int audio2[2]; int x; int res; sigset_t signal_set, old_set; @@ -287,6 +296,33 @@ static enum agi_result launch_script(cha return AGI_RESULT_FAILURE; } } + if (efd2) { + if (pipe(audio2)) { + 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]); + close(audio[0]); + close(audio[1]); + return AGI_RESULT_FAILURE; + } + res = fcntl(audio2[0], F_GETFL); + if (res > -1) + res = fcntl(audio2[0], 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]); + close(audio2[0]); + close(audio2[1]); + return AGI_RESULT_FAILURE; + } + } /* Block SIGHUP during the fork - prevents a race */ sigfillset(&signal_set); @@ -322,6 +358,11 @@ static enum agi_result launch_script(cha } else { close(STDERR_FILENO + 1); } + if (efd2) { + dup2(audio2[1], STDERR_FILENO + 2); + } else { + close(STDERR_FILENO + 2); + } /* Before we unblock our signals, return our trapped signals back to the defaults */ signal(SIGHUP, SIG_DFL); @@ -339,7 +380,7 @@ static enum agi_result launch_script(cha } /* Close everything but stdin/out/error */ - for (x=STDERR_FILENO + 2;x<1024;x++) + for (x=STDERR_FILENO + 3;x<1024;x++) close(x); /* Execute script */ @@ -357,12 +398,19 @@ static enum agi_result launch_script(cha if (efd) { *efd = audio[1]; } + if (efd2) { + *efd2 = audio2[0]; + } /* close what we're not using in the parent */ close(toast[1]); close(fromast[0]); - if (efd) + if (efd) { close(audio[0]); + } + if (efd2) { + close(audio2[1]); + } *opid = pid; return AGI_RESULT_SUCCESS; @@ -392,7 +440,7 @@ static void setup_env(struct ast_channel 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"); + fdprintf(fd, "agi_enhanced: %d%s\n", enhanced, ".0"); /* User information */ fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : ""); @@ -421,7 +469,7 @@ static int handle_waitfordigit(struct as return RESULT_SHOWUSAGE; if (sscanf(argv[3], "%d", &to) != 1) return RESULT_SHOWUSAGE; - res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl); + res = ast_waitfordigit_full(chan, to, agi->audio_out, agi->ctrl); fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } @@ -596,7 +644,7 @@ static int handle_streamfile(struct ast_ if (vfs) ast_playstream(vfs); - res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); + res = ast_waitstream_full(chan, argv[3], agi->audio_out, 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; @@ -657,7 +705,7 @@ static int handle_getoption(struct ast_c if (vfs) ast_playstream(vfs); - res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); + res = ast_waitstream_full(chan, argv[3], agi->audio_out, 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; @@ -669,7 +717,7 @@ static int handle_getoption(struct ast_c /* If the user didnt press a key, wait for digitTimeout*/ if (res == 0 ) { - res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl); + res = ast_waitfordigit_full(chan, timeout, agi->audio_out, agi->ctrl); /* Make sure the new result is in the escape digits of the GET OPTION */ if ( !strchr(edigits,res) ) res=0; @@ -693,7 +741,7 @@ static int handle_saynumber(struct ast_c return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; - res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl); + res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio_out, agi->ctrl); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -710,7 +758,7 @@ static int handle_saydigits(struct ast_c if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; - res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); + res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio_out, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -724,7 +772,7 @@ static int handle_sayalpha(struct ast_ch if (argc != 4) return RESULT_SHOWUSAGE; - res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); + res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio_out, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -802,7 +850,7 @@ static int handle_sayphonetic(struct ast if (argc != 4) return RESULT_SHOWUSAGE; - res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); + res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio_out, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -826,7 +874,7 @@ static int handle_getdata(struct ast_cha max = atoi(argv[4]); else max = 1024; - res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl); + res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio_out, agi->ctrl); if (res == 2) /* New command */ return RESULT_SUCCESS; else if (res == 1) @@ -1833,8 +1881,13 @@ static enum agi_result run_agi(struct as int ms; enum agi_result returnstatus = AGI_RESULT_SUCCESS; struct ast_frame *f; + struct ast_frame fr; char buf[AGI_BUF_LEN]; + char audiobuf[AGI_BUF_LEN]; char *res = NULL; + int audiobytes; + int fds[2]; + int enhanced = 0; FILE *readf; /* how many times we'll retry if ast_waitfor_nandfs will return without either channel or file descriptor in case select is interrupted by a system call (EINTR) */ @@ -1848,10 +1901,22 @@ static enum agi_result run_agi(struct as return AGI_RESULT_FAILURE; } setlinebuf(readf); - setup_env(chan, request, agi->fd, (agi->audio > -1)); + if (agi->audio_out > -1) { + enhanced = 1; + } + if (agi->audio_in > -1) { + enhanced++; + } + setup_env(chan, request, agi->fd, enhanced); + fds[0] = agi->ctrl; + fds[1] = agi->audio_in; for (;;) { ms = -1; - c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms); + if (agi->audio_in > -1) { + c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, fds, 2, NULL, &outfd, &ms); + } else { + c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms); + } if (c) { retry = AGI_NANDFS_RETRY; /* Idle the channel until we get a command */ @@ -1862,13 +1927,23 @@ static enum agi_result run_agi(struct as break; } else { /* If it's voice, write it to the audio pipe */ - if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) { + if ((agi->audio_out > -1) && (f->frametype == AST_FRAME_VOICE)) { /* Write, ignoring errors */ - write(agi->audio, f->data, f->datalen); + write(agi->audio_out, f->data, f->datalen); } ast_frfree(f); } } else if (outfd > -1) { + if ((agi->audio_in > -1) && (outfd == agi->audio_in)) { + audiobytes = read(agi->audio_in, audiobuf, sizeof(audiobuf)); + if (audiobytes > 0) { + fr.frametype = AST_FRAME_VOICE; + fr.subclass = AST_FORMAT_SLINEAR; + fr.datalen = audiobytes; + fr.data = audiobuf; + ast_write(chan, &fr); + } + } else { size_t len = sizeof(buf); size_t buflen = 0; @@ -1914,6 +1989,7 @@ static enum agi_result run_agi(struct as if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) { break; } + } } else { if (--retry <= 0) { ast_log(LOG_WARNING, "No channel, no fd?\n"); @@ -2022,6 +2098,7 @@ static int agi_exec_full(struct ast_chan int argc = 0; int fds[2]; int efd = -1; + int efd2 = -1; int pid; char *stringp; AGI agi; @@ -2047,12 +2124,13 @@ static int agi_exec_full(struct ast_chan } } #endif - res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid); + res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, (enhanced == 2) ? &efd2 : NULL, &pid); if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) { int status = 0; agi.fd = fds[1]; agi.ctrl = fds[0]; - agi.audio = efd; + agi.audio_out = efd; + agi.audio_in = efd2; agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0; res = run_agi(chan, argv[0], &agi, pid, &status, dead); /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */ @@ -2062,6 +2140,8 @@ static int agi_exec_full(struct ast_chan close(fds[1]); if (efd > -1) close(efd); + if (efd2 > -1) + close(efd2); ast_unreplace_sigchld(); } ast_module_user_remove(u); @@ -2110,6 +2190,35 @@ static int eagi_exec(struct ast_channel return res; } +static int xagi_exec(struct ast_channel *chan, void *data) +{ + int readformat, writeformat; + int res; + + if (chan->_softhangup) + ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n"); + 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; + } + writeformat = chan->writeformat; + if (ast_set_write_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, 2, 0); + if (!res) { + if (ast_set_read_format(chan, readformat)) { + ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat)); + } + if (ast_set_write_format(chan, writeformat)) { + ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(writeformat)); + } + } + return res; +} + static int deadagi_exec(struct ast_channel *chan, void *data) { if (!ast_check_hangup(chan)) @@ -2165,6 +2274,7 @@ static int unload_module(void) { ast_module_user_hangup_all(); ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry)); + ast_unregister_application(xapp); ast_unregister_application(eapp); ast_unregister_application(deadapp); return ast_unregister_application(app); @@ -2175,6 +2285,7 @@ static int load_module(void) ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry)); ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip); ast_register_application(eapp, eagi_exec, esynopsis, descrip); + ast_register_application(xapp, xagi_exec, xsynopsis, descrip); return ast_register_application(app, agi_exec, synopsis, descrip); } --- /dev/null +++ b/agi/xagi-test.c @@ -0,0 +1,175 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * XAGI sample script + * + * Copyright (C) 2005 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * + * based on eagi-test.c + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#ifdef SOLARIS +#include +#endif + +#define AUDIO_FILENO_IN (STDERR_FILENO + 1) +#define AUDIO_FILENO_OUT (STDERR_FILENO + 2) + +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 void app_echo(void) +{ + fd_set fds; + int res; + int bytes = 0; + static char astresp[256]; + char audiobuf[16000]; /* 1 second of audio */ + for (;;) { + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + FD_SET(AUDIO_FILENO_IN, &fds); + /* Wait for *some* sort of I/O */ + res = select(AUDIO_FILENO_IN + 1, &fds, NULL, NULL, NULL); + if (res < 0) { + fprintf(stderr, "Error in select: %s\n", strerror(errno)); + return; + } + if (FD_ISSET(STDIN_FILENO, &fds)) { + fgets(astresp, sizeof(astresp), stdin); + if (feof(stdin)) { + return; + } + astresp[strlen(astresp) - 1] = '\0'; + fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); + return; + } + if (FD_ISSET(AUDIO_FILENO_IN, &fds)) { + /* what goes in.... */ + res = read(AUDIO_FILENO_IN, audiobuf, sizeof(audiobuf)); + if (res > 0) { + bytes = res; + /* must come out */ + write(AUDIO_FILENO_OUT, audiobuf, bytes); + } + } + } +} + +static char *wait_result(void) +{ + fd_set fds; + int res; + static char astresp[256]; + char audiobuf[4096]; + for (;;) { + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + FD_SET(AUDIO_FILENO_IN, &fds); + /* Wait for *some* sort of I/O */ + res = select(AUDIO_FILENO_IN + 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_IN, &fds)) { + res = read(AUDIO_FILENO_IN, audiobuf, sizeof(audiobuf)); + /* drop it, like it's hot */ + } + } + +} + +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-echotest \"\""); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + app_echo(); + 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 < 2) { + fprintf(stderr, "No XAGI services available. Use XAGI, not AGI or EAGI\n"); + exit(1); + } + if (run_script()) + return -1; + exit(0); +} --- a/agi/Makefile +++ b/agi/Makefile @@ -13,7 +13,7 @@ .PHONY: clean all uninstall -AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi +AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi xagi-test ifeq ($(OSARCH),SunOS) LIBS+=-lsocket -lnsl @@ -38,7 +38,7 @@ uninstall: for x in $(AGIS); do rm -f $(DESTDIR)$(AGI_DIR)/$$x ; done clean: - rm -f *.so *.o look eagi-test eagi-sphinx-test + rm -f *.so *.o look eagi-test eagi-sphinx-test xagi-test rm -f .*.o.d .*.oo.d *.s *.i rm -f strcompat.c --- /dev/null +++ b/apps/app_segfault.c @@ -0,0 +1,55 @@ +/* + * Segfault application + * + * An application to provoke a segmentation fault from the dialplan. + * (I know what you are thinking now...., but since Asterisk is too stable... + * I needed something to test my failover switches.) + * + * Copyright (C) 2005 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * + * This program is free software, distributed under the terms of + * the GNU General Public License. THIS APPLICATION _WILL_ CRASH YOUR + * ASTERISK SERVER SO OF COURSE THERE IS NOT LIABILITY FOR NOTHING! + */ + +#include "asterisk.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *app = "Segfault"; + +static char *synopsis = "This application will crash Asterisk with a segmentation fault."; + +static char *descrip = +" Segfault(): Crash with a segfault. Never returns nufin.\n"; + +static int segfault_exec(struct ast_channel *chan, void *data) +{ + ((char *)0)[0] = 0; + return 0; +} + +static int unload_module(void) +{ + return ast_unregister_application(app); +} + +static int load_module(void) +{ + return ast_register_application(app, segfault_exec, synopsis, descrip); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Application for crashing Asterisk with a segmentation fault", + .load = load_module, + .unload = unload_module, +); --- a/apps/app_directed_pickup.c +++ b/apps/app_directed_pickup.c @@ -45,7 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #define PICKUPMARK "PICKUPMARK" -static const char *app = "Pickup"; +static const char *app = "DPickup"; static const char *synopsis = "Directed Call Pickup"; static const char *descrip = " Pickup(extension[@context][&extension2@context...]): This application can pickup any ringing channel\n" --- /dev/null +++ b/apps/app_pickup.c @@ -0,0 +1,300 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Pickup, channel independent call pickup + * + * Copyright (C) 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * Copyright (C) 2004, Florian Overkamp + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include "asterisk.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static char *app = "PickUp"; + +static char *synopsis = "Channel independent call pickup."; + +static char *descrip = +" PickDown([group]): Tries to pickup the first ringing channel with callgroup == group.\n" +" If called without the group argument, the pickupgroup of the channel will be used.\n"; + +static char *app2 = "Steal"; + +static char *synopsis2 = "Channel independent call stealing. Just like pickup but for answered channels."; + +static char *descrip2 = +" Steal([group]): Tries to steal the first bridged channel with callgroup == group.\n" +" If called without the group argument, the pickupgroup of the channel will be used.\n"; + +static char *app3 = "PickDown"; + +static char *synopsis3 = "Channel independent call pickdown."; + +static char *descrip3 = +" PickDown([group]): Tries to hangup the first ringing channel with callgroup == group.\n" +" If called without the group argument, the pickupgroup of the channel will be used.\n"; + +static char *app4 = "PickupChan"; + +static char *synopsis4 = "Channel independent call pickup."; + +static char *descrip4 = +" PickupChan(Technology/resource[&Technology2/resource2...]): Tries to pickup the first ringing channel in the parameter list.\n"; + +static char *app5 = "StealChan"; + +static char *synopsis5 = "Channel independent call stealing. Just like pickup but for answered channels."; + +static char *descrip5 = +" StealChan(Technology/resource[&Technology2/resource2...]): Tries to steal the first ringing channel in the parameter list.\n"; + + +static int my_pickup_call(struct ast_channel *chan, unsigned int pickupgroup, int chanstate, int bridge) { + struct ast_channel *cur; + int res = -1; + cur = ast_channel_walk_locked(NULL); + while(cur) { + if ((cur != chan) && + (pickupgroup & cur->callgroup) && + (cur->_state == chanstate)) { + break; + } + ast_mutex_unlock(&cur->lock); + cur = ast_channel_walk_locked(cur); + } + if (cur) { + if(option_verbose > 2) { + if (chanstate == AST_STATE_RINGING) { + if (bridge == 1) { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s picked up ringing channel %s\n",chan->name,cur->name); + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s hung up ringing channel %s\n",chan->name,cur->name); + } + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s stole channel %s\n",chan->name,cur->name); + } + } + if (bridge == 1) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + if (ast_channel_masquerade(cur, chan)) { + ast_log(LOG_ERROR, "unable to masquerade\n"); + } + ast_mutex_unlock(&cur->lock); + ast_mutex_unlock(&chan->lock); + } else { + cur->_softhangup = AST_SOFTHANGUP_DEV; + ast_mutex_unlock(&cur->lock); + } + } else { + if(option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "No channel found %d.\n",pickupgroup); + } + } + return res; +} + +static int my_pickup_channel(struct ast_channel *chan, void *data, int chanstate, int bridge) { + struct ast_channel *cur; + char channels[256]; + char evalchan[256]; + char *endptr; + int res = -1; + cur = ast_channel_walk_locked(NULL); + strncpy(channels, (char *)data, sizeof(channels) - 1); + while(cur) { + if ((cur != chan) && + (cur->_state == chanstate)) { + /* This call is a candidate (correct ringstate and not ourselves), now check if the channel is in our list */ + strncpy(evalchan, (char *)cur->name, sizeof(evalchan) - 1); + /* strip the subchannel tag */ + endptr = strrchr(evalchan, '-'); + if(endptr) { + *endptr = '\0'; + } + /* check for each of the members if they match (probably a stristr will do ?) */ + /* if we match the code, break */ + if(strstr(channels, evalchan) != NULL) { + ast_verbose(VERBOSE_PREFIX_1 "Nice channel, I'll take it: %s\n",evalchan); + break; + } + } + ast_mutex_unlock(&cur->lock); + cur = ast_channel_walk_locked(cur); + } + if (cur) { + if(option_verbose > 2) { + if (chanstate == AST_STATE_RINGING) { + if (bridge == 1) { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s picked up ringing channel %s\n",chan->name,cur->name); + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s hung up ringing channel %s\n",chan->name,cur->name); + } + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s stole channel %s\n",chan->name,cur->name); + } + } + if (bridge == 1) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + if (ast_channel_masquerade(cur, chan)) { + ast_log(LOG_ERROR, "unable to masquerade\n"); + } + ast_mutex_unlock(&cur->lock); + ast_mutex_unlock(&chan->lock); + } else { + cur->_softhangup = AST_SOFTHANGUP_DEV; + ast_mutex_unlock(&cur->lock); + } + } else { + if(option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "No channel found %s.\n",channels); + } + } + return res; +} + + +static int pickup_exec(struct ast_channel *chan, void *data) +{ + int res=0; + unsigned int pickupgroup=0; + struct ast_module_user *u; + if (!data || !strlen(data)) { + pickupgroup = chan->pickupgroup; + } else { + pickupgroup = ast_get_group(data); + } + u = ast_module_user_add(chan); + if (!res) { + res = my_pickup_call(chan, pickupgroup, AST_STATE_RINGING, 1); + } + if (res > 0) + res = 0; + ast_module_user_remove(u); + return res; +} + +static int steal_exec(struct ast_channel *chan, void *data) +{ + int res=0; + unsigned int pickupgroup=0; + struct ast_module_user *u; + if (!data || !strlen(data)) { + pickupgroup = chan->pickupgroup; + } else { + pickupgroup = ast_get_group(data); + } + u = ast_module_user_add(chan); + if (!res) { + res = my_pickup_call(chan, pickupgroup, AST_STATE_UP, 1); + } + if (res > 0) + res = 0; + ast_module_user_remove(u); + return res; +} + +static int pickdown_exec(struct ast_channel *chan, void *data) +{ + int res=0; + unsigned int pickupgroup=0; + struct ast_module_user *u; + if (!data || !strlen(data)) { + pickupgroup = chan->pickupgroup; + } else { + pickupgroup = ast_get_group(data); + } + u = ast_module_user_add(chan); + if (!res) { + res = my_pickup_call(chan, pickupgroup, AST_STATE_RINGING, 0); + } + if (res > 0) + res = 0; + ast_module_user_remove(u); + return res; +} + +static int pickupchan_exec(struct ast_channel *chan, void *data) { + int res=0; + struct ast_module_user *u; + if (!data) { + ast_log(LOG_WARNING, "PickupChan requires an argument (technology1/number1&technology2/number2...)\n"); + return -1; + } + u = ast_module_user_add(chan); + if (!res) { + res = my_pickup_channel(chan, data, AST_STATE_RINGING, 1); + } + if (res > 0) + res = 0; + ast_module_user_remove(u); + return res; +} + +static int stealchan_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct ast_module_user *u; + if (!data) { + ast_log(LOG_WARNING, "StealChan requires an argument (technology1/number1&technology2/number2...)\n"); + return -1; + } + + u = ast_module_user_add(chan); + if (!res) { + res = my_pickup_channel(chan, data, AST_STATE_UP, 1); + } + if (res > 0) + res = 0; + ast_module_user_remove(u); + return res; +} + + +static int unload_module(void) +{ + ast_module_user_hangup_all(); + ast_unregister_application(app5); + ast_unregister_application(app4); + ast_unregister_application(app3); + ast_unregister_application(app2); + return ast_unregister_application(app); +} + +static int load_module(void) +{ + ast_register_application(app5, stealchan_exec, synopsis5, descrip5); + ast_register_application(app4, pickupchan_exec, synopsis4, descrip4); + ast_register_application(app3, pickdown_exec, synopsis3, descrip3); + ast_register_application(app2, steal_exec, synopsis2, descrip2); + return ast_register_application(app, pickup_exec, synopsis, descrip); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PickUp/PickDown/Steal/PickupChan/StealChan", + .load = load_module, + .unload = unload_module, +); --- /dev/null +++ b/apps/app_devstate.c @@ -0,0 +1,202 @@ +/* + * Devstate application + * + * Since we like the snom leds so much, a little app to + * light the lights on the snom on demand .... + * + * Copyright (C) 2005, Druid Software + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include "asterisk.h" + +#include +#include +#include +#include + +#include "asterisk/lock.h" +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/astdb.h" +#include "asterisk/cli.h" +#include "asterisk/manager.h" +#include "asterisk/devicestate.h" +#include "asterisk/module.h" + + +static char type[] = "DS"; +static char tdesc[] = "Application for sending device state messages"; + +static char app[] = "Devstate"; + +static char synopsis[] = "Generate a device state change event given the input parameters"; + +static char descrip[] = " Devstate(device|state): Generate a device state change event given the input parameters. Returns 0. State values match the asterisk device states. They are 0 = unknown, 1 = not inuse, 2 = inuse, 3 = busy, 4 = invalid, 5 = unavailable, 6 = ringing\n"; + +static char devstate_cli_usage[] = +"Usage: devstate device state\n" +" Generate a device state change event given the input parameters.\n Mainly used for lighting the LEDs on the snoms.\n"; + +static int devstate_cli(int fd, int argc, char *argv[]); +static struct ast_cli_entry cli_dev_state = + { { "devstate", NULL }, devstate_cli, "Set the device state on one of the \"pseudo devices\".", devstate_cli_usage }; + + +static int devstate_cli(int fd, int argc, char *argv[]) +{ + char devName[128]; + if (argc != 3) + return RESULT_SHOWUSAGE; + + if (ast_db_put("DEVSTATES", argv[1], argv[2])) + { + ast_log(LOG_DEBUG, "ast_db_put failed\n"); + } + snprintf(devName, sizeof(devName), "DS/%s", argv[1]); + ast_device_state_changed_literal(devName); + return RESULT_SUCCESS; +} + +static int devstate_exec(struct ast_channel *chan, void *data) +{ + char *device, *state, *info; + char devName[128]; + struct ast_module_user *u; + + + if (!(info = ast_strdupa(data))) { + ast_log(LOG_WARNING, "Unable to dupe data :(\n"); + return -1; + } + + u = ast_module_user_add(chan); + device = info; + state = strchr(info, '|'); + if (state) { + *state = '\0'; + state++; + } + else + { + ast_log(LOG_DEBUG, "No state argument supplied\n"); + return -1; + } + + if (ast_db_put("DEVSTATES", device, state)) + { + ast_log(LOG_DEBUG, "ast_db_put failed\n"); + } + + snprintf(devName, sizeof(devName), "DS/%s", device); + ast_device_state_changed_literal(devName); + + ast_module_user_remove(u); + return 0; +} + + +static int ds_devicestate(void *data) +{ + char *dest = data; + char stateStr[16]; + if (ast_db_get("DEVSTATES", dest, stateStr, sizeof(stateStr))) + { + ast_log(LOG_DEBUG, "ds_devicestate couldnt get state in astdb\n"); + return 0; + } + else + { + ast_log(LOG_DEBUG, "ds_devicestate dev=%s returning state %d\n", + dest, atoi(stateStr)); + return (atoi(stateStr)); + } +} + +static struct ast_channel_tech devstate_tech = { + .type = type, + .description = tdesc, + .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), + .devicestate = ds_devicestate, + .requester = NULL, + .send_digit_begin = NULL, + .send_digit_end = NULL, + .send_text = NULL, + .call = NULL, + .hangup = NULL, + .answer = NULL, + .read = NULL, + .write = NULL, + .bridge = NULL, + .exception = NULL, + .indicate = NULL, + .fixup = NULL, + .setoption = NULL, +}; + +static char mandescr_devstate[] = +"Description: Put a value into astdb\n" +"Variables: \n" +" Family: ...\n" +" Key: ...\n" +" Value: ...\n"; + +static int action_devstate(struct mansession *s, const struct message *m) +{ + const char *devstate = astman_get_header(m, "Devstate"); + const char *value = astman_get_header(m, "Value"); + const char *id = astman_get_header(m,"ActionID"); + char devName[128]; + char idText[256] = ""; + + if (!strlen(devstate)) { + astman_send_error(s, m, "No Devstate specified"); + return 0; + } + if (!strlen(value)) { + astman_send_error(s, m, "No Value specified"); + return 0; + } + if (!ast_strlen_zero(id)) + snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); + + if (!ast_db_put("DEVSTATES", devstate, (char *)value)) { + snprintf(devName, sizeof(devName), "DS/%s", devstate); + ast_device_state_changed_literal(devName); + astman_append(s, "Response: Success\r\n%s\r\n", idText); + } else { + ast_log(LOG_DEBUG, "ast_db_put failed\n"); + astman_append(s, "Response: Failed\r\n%s\r\n", idText); + } + return 0; +} + +static int load_module(void) +{ + if (ast_channel_register(&devstate_tech)) { + ast_log(LOG_DEBUG, "Unable to register channel class %s\n", type); + return -1; + } + ast_cli_register(&cli_dev_state); + ast_manager_register2( "Devstate", EVENT_FLAG_CALL, action_devstate, "Change a device state", mandescr_devstate ); + return ast_register_application(app, devstate_exec, synopsis, descrip); +} + +static int unload_module(void) +{ + int res = 0; + + ast_module_user_hangup_all(); + ast_manager_unregister( "Devstate"); + ast_cli_unregister(&cli_dev_state); + res = ast_unregister_application(app); + ast_channel_unregister(&devstate_tech); + return res; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Devstate Application"); + --- /dev/null +++ b/configs/watchdog.conf.sample @@ -0,0 +1,22 @@ +; +; Configuration file for res_watchdog +; +; type = isdnguard | watchdog +; device = /dev/... +; interval = interval to trigger the watchdog in ms + +;[ISDNguard-direct] +;type = isdnguard +;device = /dev/ttyS0 +;interval = 200 + +;[ISDNguard-with-daemon] +;type = isdnguard +;device = /var/run/guard.ctl +;interval = 200 + +;[kernel_watchdog] +;type = watchdog +;device = /dev/watchdog +;interval = 100 + --- /dev/null +++ b/res/res_watchdog.c @@ -0,0 +1,137 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Resource to make watchdogs happy + * + * Copyright (C) 2005, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include "asterisk.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct watchdog_pvt *watchdogs = NULL; + +typedef struct watchdog_pvt { + char device[80]; + int fd; + int type; + int interval; + pthread_t watchdog_thread; + struct watchdog_pvt *next; +} watchdog_pvt; + +static void *do_watchdog_thread(void *data) { + struct watchdog_pvt *woof = (struct watchdog_pvt *)data; + for (;;) { + if (woof->fd) { + write(woof->fd, "PING\n", 5); + } + usleep(woof->interval * 1000); + } + return NULL; +} + + +static int load_module(void) +{ + int res = 0; + const char *cat, *utype, *udevice, *uinterval; + struct ast_config *cfg; + struct watchdog_pvt *woof = NULL; + + cfg = ast_config_load("watchdog.conf"); + if (cfg) { + cat = ast_category_browse(cfg, NULL); + while(cat) { + cat = ast_category_browse(cfg, cat); + utype = ast_variable_retrieve(cfg, cat, "type"); +/* if (utype) { + ast_log(LOG_NOTICE, "type = %s\n", utype); + } */ + udevice = ast_variable_retrieve(cfg, cat, "device"); +/* if (udevice) { + ast_log(LOG_NOTICE, "device = %s\n", udevice); + } */ + uinterval = ast_variable_retrieve(cfg, cat, "interval"); +/* if (uinterval) { + ast_log(LOG_NOTICE, "interval = %s\n", uinterval); + } */ + if (uinterval && udevice && utype) { + woof = malloc(sizeof(struct watchdog_pvt)); + if (!woof) { + ast_log(LOG_ERROR, "unable to malloc!\n"); + return -1; + } + memset(woof, 0x0, sizeof(struct watchdog_pvt)); + strncpy(woof->device, udevice, sizeof(woof->device) - 1); + + woof->interval = atoi(uinterval);; + woof->next = watchdogs; + watchdogs = woof; + woof->fd = open(woof->device, O_WRONLY | O_SYNC); + if (woof->fd) { + if (!strncmp(utype, "isdnguard", sizeof(utype))) { + woof->type = 1; + write(woof->fd, "START\n", 6); + } + ast_pthread_create(&woof->watchdog_thread, NULL, do_watchdog_thread, woof); + } else { + ast_log(LOG_WARNING, "error opening watchdog device %s !\n", woof->device); + } + } + } + ast_config_destroy(cfg); + } + return res; +} + + +static int unload_module(void) +{ + struct watchdog_pvt *dogs, *woof; + dogs = watchdogs; + while (dogs) { + pthread_cancel(dogs->watchdog_thread); + pthread_join(dogs->watchdog_thread, NULL); + close(dogs->fd); + woof = dogs->next; + free(dogs); + dogs = woof; + } + return 0; +} + + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Watchdog Resource", + .load = load_module, + .unload = unload_module, +); --- a/main/db.c +++ b/main/db.c @@ -574,7 +574,7 @@ static int manager_dbget(struct mansessi astman_append(s, "Event: DBGetResponse\r\n" "Family: %s\r\n" "Key: %s\r\n" - "Val: %s\r\n" + "Value: %s\r\n" "%s" "\r\n", family, key, tmp, idText); @@ -582,11 +582,35 @@ static int manager_dbget(struct mansessi return 0; } +static int manager_dbdel(struct mansession *s, const struct message *m) +{ + const char *family = astman_get_header(m, "Family"); + const char *key = astman_get_header(m, "Key"); + + if (!strlen(family)) { + astman_send_error(s, m, "No family specified"); + return 0; + } + if (!strlen(key)) { + astman_send_error(s, m, "No key specified"); + return 0; + } + + if (ast_db_del(family, key)) { + astman_send_error(s, m, "Failed to delete entry"); + } else { + astman_send_ack(s, m, "Deleted entry successfully"); + } + + return 0; +} + int astdb_init(void) { dbinit(); ast_cli_register_multiple(cli_database, sizeof(cli_database) / sizeof(struct ast_cli_entry)); - ast_manager_register("DBGet", EVENT_FLAG_SYSTEM, manager_dbget, "Get DB Entry"); - ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry"); + ast_manager_register("DBget", EVENT_FLAG_SYSTEM, manager_dbget, "Get DB Entry"); + ast_manager_register("DBput", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry"); + ast_manager_register("DBdel", EVENT_FLAG_SYSTEM, manager_dbdel, "Delete DB Entry"); return 0; } --- /dev/null +++ b/configs/esel.conf.sample @@ -0,0 +1,12 @@ +; +; Configuration file for res_esel +; + +;[asterisk-2] +;host = 192.168.0.1 +;port = 5038 +;username = manager +;secret = donkey + +; export the extension snom in context phones to DS/100 +;export => snom@phones,100 --- /dev/null +++ b/res/res_esel.c @@ -0,0 +1,384 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Extension State Export Logic (E.S.E.L) (Sorry, i couldnt resist...) + * + * Resource to export extension states to other Asterisk servers + * + * Copyright (C) 2006, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include "asterisk.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/options.h" +#include "asterisk/module.h" +#include "asterisk/translate.h" +#include "asterisk/say.h" +#include "asterisk/features.h" +#include "asterisk/musiconhold.h" +#include "asterisk/config.h" +#include "asterisk/cli.h" +#include "asterisk/manager.h" +#include "asterisk/utils.h" +#include "asterisk/lock.h" +#include "asterisk/adsi.h" + + +AST_MUTEX_DEFINE_STATIC(listlock); + +typedef struct esel_extension_state { + char context[AST_MAX_EXTENSION]; + char exten[AST_MAX_EXTENSION]; + int state; + char devstate[AST_MAX_EXTENSION]; + struct esel_extension_state *next; + struct esel_extension_state *prev; +} esel_extension_state; + +typedef struct esel_export { + char context[AST_MAX_EXTENSION]; + char exten[AST_MAX_EXTENSION]; + char devstate[AST_MAX_EXTENSION]; + struct esel_export *next; +} esel_export; + +typedef struct esel_queue { + struct esel_extension_state *head; + struct esel_extension_state *tail; + int count; + ast_cond_t cond; + ast_mutex_t lock; +} esel_queue; + +typedef struct esel_pvt { + char name[80]; + char username[80]; + char secret[80]; + char host[80]; + int port; + struct sockaddr_in raddr; + int sockfd; + int connected; + pthread_t esel_thread; + + /* list of extensions to export */ + struct esel_export *extensions; + + /* queue */ + struct esel_queue queue; + + struct esel_pvt *next; +} esel_pvt; + +static struct esel_pvt *donkeys = NULL; + +static int esel_queue_extension_state(struct esel_queue *queue, char *context, char *exten, int state, void *data) { + struct esel_extension_state *exstate = NULL; + + exstate = malloc(sizeof(struct esel_extension_state)); + if (!exstate) { + ast_log(LOG_ERROR, "Unable to malloc!\n"); + return 1; + } + memset(exstate,0,sizeof(struct esel_extension_state)); + exstate->next = NULL; + exstate->prev = NULL; + + ast_mutex_lock(&queue->lock); + if (queue->count > 100) { + ast_mutex_unlock(&queue->lock); + free(exstate); + if (option_verbose > 5) + ast_log(LOG_WARNING, "E.S.E.L Queue too long.\n"); + return -1; + } + ast_copy_string(exstate->exten, exten, sizeof(exstate->exten)); + ast_copy_string(exstate->context, context, sizeof(exstate->context)); + exstate->state = state; + if (!queue->head) { + /* Empty queue */ + queue->head = exstate; + queue->tail = exstate; + } else { + /* Double link */ + queue->tail->next = exstate; + exstate->prev = queue->tail; + queue->tail = exstate; + } + queue->count++; + ast_cond_signal(&queue->cond); + ast_mutex_unlock(&queue->lock); + return 0; +} + +static int esel_is_exported(struct esel_export *extensions, struct esel_extension_state *exstate) { + struct esel_export *export = NULL; + export = extensions; + while (export) { + if ((!strcasecmp(export->exten, exstate->exten)) && (!strcasecmp(export->context, exstate->context))) { + /* copy mapping */ + ast_copy_string(exstate->devstate, export->devstate, sizeof(exstate->devstate)); + return 1; + } + export = export->next; + } + return 0; +} + +static int esel_state2devstate(int state) { + switch(state) { + case 1: + return 2; + case 8: + return 6; + default: + return state; + } +} + +static void esel_export_to_remote(struct esel_extension_state *exstate, struct esel_pvt *esel) { + char msg[1024]; + int sent = 0; + memset(msg, 0x0, sizeof(msg)); + snprintf(msg, sizeof(msg) - 1, "Action: Devstate\r\nDevstate: %s\r\nValue: %d\r\n\r\n", exstate->devstate, esel_state2devstate(exstate->state)); + sent = send(esel->sockfd, msg, strlen(msg), 0); + if (sent == -1) { + esel->connected = 0; + } +// ast_log(LOG_NOTICE, "%s", msg); +} + +static void *do_esel_thread(void *data) { + struct esel_pvt *esel = (struct esel_pvt *)data; + struct esel_queue *queue = &esel->queue; + struct esel_extension_state *exstate = NULL; + char msg[1024]; + char buf[1024]; + int numbytes = 0; + int sent = 0; + int res = 0; + for (;;) { + if (esel->connected) { + ast_mutex_lock(&queue->lock); + if (queue->count == 0) + ast_cond_wait(&queue->cond, &queue->lock); + exstate = queue->head; + if (exstate) { + if (exstate->next) { + queue->head = exstate->next; + } else { + queue->head = NULL; + queue->tail = NULL; + } + queue->count--; + } else { + ast_log(LOG_ERROR, "I SHOULD NEVER HAPPEN! EXPECT SOME MAJOR KABOOM! DUCK AND COVER!\n"); + } + ast_mutex_unlock(&queue->lock); + + if (exstate) { + if (esel_is_exported(esel->extensions, exstate)) { + esel_export_to_remote(exstate, esel); + } + free(exstate); + exstate = NULL; + } + } else { + if (esel->sockfd > 0) + close(esel->sockfd); + if ((esel->sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + ast_log(LOG_ERROR, "unable to request socket!\n"); + return NULL; + } + /* try to connect */ + res = connect(esel->sockfd, (struct sockaddr *)&esel->raddr, sizeof(struct sockaddr)); + if (res) { + ast_log(LOG_NOTICE, "error connecting to %s:%d\n", esel->host, esel->port); + } else { + while (strncasecmp(buf, "Asterisk Call Manager:", 21)) { + if ((numbytes=recv(esel->sockfd, buf, sizeof(buf), 0)) == -1) { + esel->connected = 0; + continue; + } + buf[numbytes] = '\0'; + // ast_log(LOG_NOTICE, "read: %s", buf); + } + /* log into remote manager */ + memset(msg, 0x0, sizeof(msg)); + snprintf(msg, sizeof(msg) - 1, "Action: Login\r\nUsername: %s\r\nSecret: %s\r\n\r\n", esel->username, esel->secret); + sent = send(esel->sockfd, msg, strlen(msg), 0); + + while (strncasecmp(buf, "Response:", 9)) { + if ((numbytes=recv(esel->sockfd, buf, sizeof(buf), 0)) == -1) { + continue; + } + buf[numbytes] = '\0'; + // ast_log(LOG_NOTICE, "read: %s", buf); + } + + if (!strncasecmp(buf, "Response: Success", 17)) { + esel->connected = 1; + } else { + ast_log(LOG_ERROR, "error login into remote asterisk %s\n", esel->name); + } + } + /* time heals everything... */ + sleep(10); + } + } + return NULL; +} + +static int esel_state_cb(char *context, char *exten, int state, void *data) { + struct esel_pvt *esel; + + esel = donkeys; + ast_mutex_lock(&listlock); + while (esel) { + esel_queue_extension_state(&esel->queue, context, exten, state, data); + esel = esel->next; + } + ast_mutex_unlock(&listlock); + return 0; +} + + +static int load_module(void) +{ + int res = 0; + const char *cat, *host, *port, *username, *secret, *name; + struct ast_config *cfg; + struct ast_variable *var; + struct esel_pvt *esel = NULL; + struct esel_export *export = NULL; + struct hostent *he; + struct ast_hostent h; + + cfg = ast_config_load("esel.conf"); + if (cfg) { + cat = ast_category_browse(cfg, NULL); + while(cat) { + name = cat; + host = ast_variable_retrieve(cfg, cat, "host"); + username = ast_variable_retrieve(cfg, cat, "username"); + secret = ast_variable_retrieve(cfg, cat, "secret"); + port = ast_variable_retrieve(cfg, cat, "port"); + + if (name && host && username && secret && port) { + esel = malloc(sizeof(struct esel_pvt)); + if (!esel) { + ast_log(LOG_ERROR, "unable to malloc!\n"); + return -1; + } + memset(esel, 0x0, sizeof(struct esel_pvt)); + ast_copy_string(esel->name, name, sizeof(esel->name)); + ast_copy_string(esel->host, host, sizeof(esel->host)); + ast_copy_string(esel->username, username, sizeof(esel->username)); + ast_copy_string(esel->secret, secret, sizeof(esel->secret)); + + esel->port = atoi(port); + if ((he=ast_gethostbyname(host, &h)) == NULL) { + ast_log(LOG_ERROR, "unknown host!\n"); + return -1; + } + + esel->raddr.sin_family = AF_INET; + esel->raddr.sin_port = htons(esel->port); + esel->raddr.sin_addr = *((struct in_addr *)he->h_addr); + bzero(&(esel->raddr.sin_zero), 8); + + esel->connected = 0; + + ast_mutex_init(&esel->queue.lock); + ast_cond_init(&esel->queue.cond, NULL); + + + /* read exports */ + var = ast_variable_browse(cfg, cat); + while (var) { + if (!strcasecmp(var->name, "export")) { + char *extenp = NULL, *contextp = NULL, *devstatep = NULL; + extenp = var->value; + devstatep = strchr(var->value, ',') + 1; + contextp = strchr(var->value, '@') + 1; + if (devstatep && contextp) { + export = malloc(sizeof(struct esel_export)); + if (!export) { + ast_log(LOG_ERROR, "unable to malloc!\n"); + return -1; + } + memset(export, 0x0, sizeof(struct esel_export)); + ast_copy_string(export->exten, extenp, contextp - extenp); + ast_copy_string(export->context, contextp, devstatep - contextp); + ast_copy_string(export->devstate, devstatep, sizeof(export->devstate)); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "exporting %s @ %s as %s to %s\n", export->exten, export->context , export->devstate , esel->name); + export->next = esel->extensions; + esel->extensions = export; + export = NULL; + } + } + var = var->next; + } + + + + esel->next = donkeys; + donkeys = esel; + + ast_pthread_create(&esel->esel_thread, NULL, do_esel_thread, esel); + + } + cat = ast_category_browse(cfg, cat); + } + ast_config_destroy(cfg); + } + ast_extension_state_add(NULL, NULL, esel_state_cb, NULL); + return res; +} + + +static int unload_module(void) +{ + struct esel_pvt *esel, *eseln; + ast_module_user_hangup_all(); + esel = donkeys; + ast_mutex_lock(&listlock); + while (esel) { + pthread_cancel(esel->esel_thread); + pthread_join(esel->esel_thread, NULL); + ast_mutex_destroy(&esel->queue.lock); + close(esel->sockfd); + eseln = esel->next; + free(esel); + esel = eseln; + } + ast_mutex_unlock(&listlock); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Extension State Export Logic (E.S.E.L.) Resource", + .load = load_module, + .unload = unload_module, +); --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -11,6 +11,9 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Hangup cause signalling implementation by + * Levent Guendogdu + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -3240,7 +3243,7 @@ static int iax2_hangup(struct ast_channe ast_mutex_lock(&iaxsl[callno]); if (callno && iaxs[callno]) { if (option_debug) - ast_log(LOG_DEBUG, "We're hanging up %s now...\n", c->name); + ast_log(LOG_DEBUG, "We're hanging up %s with cause %i now...\n", c->name, c->hangupcause); alreadygone = ast_test_flag(iaxs[callno], IAX_ALREADYGONE); /* Send the hangup unless we have had a transmission error or are already gone */ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, (unsigned char)c->hangupcause); @@ -3294,7 +3297,8 @@ static int iax2_setoption(struct ast_cha static struct ast_frame *iax2_read(struct ast_channel *c) { - ast_log(LOG_NOTICE, "I should never be called!\n"); + if (option_verbose > 3) + ast_log(LOG_NOTICE, "I should never be called!\n"); return &ast_null_frame; } --- a/apps/app_zapras.c +++ b/apps/app_zapras.c @@ -183,7 +183,7 @@ static void run_ras(struct ast_channel * } } /* Throw back into audio mode */ - x = 1; + x = 0; ioctl(chan->fds[0], ZT_AUDIOMODE, &x); /* Restore saved values */ --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -1403,8 +1403,9 @@ static int conf_run(struct ast_channel * char members[10] = ""; int dtmf, opt_waitmarked_timeout = 0; time_t timeout = 0; + int dyna_buff = CONF_SIZE; ZT_BUFFERINFO bi; - char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; + char __buf[ZT_MAX_BUF_SPACE / ZT_DEFAULT_NUM_BUFS + AST_FRIENDLY_OFFSET]; char *buf = __buf + AST_FRIENDLY_OFFSET; int setusercount = 0; @@ -1604,7 +1605,7 @@ static int conf_run(struct ast_channel * } /* Setup buffering information */ memset(&bi, 0, sizeof(bi)); - bi.bufsize = CONF_SIZE/2; + bi.bufsize = dyna_buff / 2; bi.txbufpolicy = ZT_POLICY_IMMEDIATE; bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; bi.numbufs = audio_buffers; @@ -1913,6 +1914,14 @@ static int conf_run(struct ast_channel * f = ast_read(c); if (!f) break; + if (f->datalen && f->datalen != dyna_buff) { + ast_log(LOG_NOTICE, "Audio bytes: %d Buffer size: %d\n", f->datalen, dyna_buff); + if (f->datalen < ZT_MAX_BUF_SPACE/audio_buffers) { /* skip too large frame to avoid overflow */ + dyna_buff = f->datalen; + close(fd); + goto zapretry; + } + } if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) { if (user->talk.actual) ast_frame_adjust_volume(f, user->talk.actual); Answer the channel before saying things (SayNumber, SayDigits, SayCharacters, SayPhonetic). --- a/main/pbx.c +++ b/main/pbx.c @@ -6060,6 +6060,9 @@ static int pbx_builtin_saynumber(struct return -1; } } + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } return ast_say_number(chan, atoi(tmp), "", chan->language, options); } @@ -6067,8 +6070,12 @@ static int pbx_builtin_saydigits(struct { int res = 0; - if (data) + if (data) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } res = ast_say_digit_str(chan, data, "", chan->language); + } return res; } @@ -6076,8 +6083,12 @@ static int pbx_builtin_saycharacters(str { int res = 0; - if (data) + if (data) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } res = ast_say_character_str(chan, data, "", chan->language); + } return res; } @@ -6085,8 +6096,12 @@ static int pbx_builtin_sayphonetic(struc { int res = 0; - if (data) + if (data) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } res = ast_say_phonetic_str(chan, data, "", chan->language); + } return res; } --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -11,6 +11,10 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -125,7 +129,8 @@ static char *descrip = " H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n" " i - Asterisk will ignore any forwarding requests it may receive on this\n" " dial attempt.\n" -" j - Jump to priority n+101 if all of the requested channels were busy.\n" +" j - Jump to priority n+101 if the called party was busy.\n" +" Jump to priority n+201 if all of the requested channels were busy.\n" " k - Allow the called party to enable parking of the call by sending\n" " the DTMF sequence defined for call parking in features.conf.\n" " K - Allow the calling party to enable parking of the call by sending\n" @@ -1300,14 +1305,16 @@ static int dial_exec_full(struct ast_cha } if (!outgoing) { - strcpy(status, "CHANUNAVAIL"); + ast_copy_string(status, "CHANUNAVAIL", sizeof(status)); if(fulldial == num_dialed) { res = -1; goto out; } + /* See if there is a special message */ + ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201); } else { /* Our status will at least be NOANSWER */ - strcpy(status, "NOANSWER"); + ast_copy_string(status, "NOANSWER", sizeof(status)); if (ast_test_flag(outgoing, OPT_MUSICBACK)) { moh = 1; if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) { --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -66,6 +66,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #include "asterisk/privacy.h" #include "asterisk/stringfields.h" #include "asterisk/global_datastores.h" +#include "asterisk/transcap.h" static char *app = "Dial"; @@ -1654,23 +1655,25 @@ static int dial_exec_full(struct ast_cha ast_set_flag(&(config.features_caller), AST_FEATURE_PLAY_WARNING); if (play_to_callee) ast_set_flag(&(config.features_callee), AST_FEATURE_PLAY_WARNING); - if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER)) + if ((chan->transfercapability != AST_TRANS_CAP_DIGITAL) && (chan->transfercapability != AST_TRANS_CAP_RESTRICTED_DIGITAL)) { + /* only non-digital calls are allowed to go through userspace */ + if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER)) ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER)) + if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER)) ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP)) + if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP)) ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLER_HANGUP)) + if (ast_test_flag(peerflags, OPT_CALLER_HANGUP)) ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR)) + if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR)) ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); - if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) + if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); - if (ast_test_flag(peerflags, OPT_CALLEE_PARK)) + if (ast_test_flag(peerflags, OPT_CALLEE_PARK)) ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL); - if (ast_test_flag(peerflags, OPT_CALLER_PARK)) + if (ast_test_flag(peerflags, OPT_CALLER_PARK)) ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL); - + } config.timelimit = timelimit; config.play_warning = play_warning; config.warning_freq = warning_freq; --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -193,6 +193,8 @@ static char *descrip = " family/key is not specified.\n" " r - Indicate ringing to the calling party. Pass no audio to the calling\n" " party until the called channel has answered.\n" +" R - indicate ringing to the calling party when the called party indicates\n" +" ringing, pass no audio until answered.\n" " S(x) - Hang up the call after 'x' seconds *after* the called party has\n" " answered the call.\n" " t - Allow the called party to transfer the calling party by sending the\n" @@ -249,6 +251,7 @@ enum { OPT_CALLEE_PARK = (1 << 25), OPT_CALLER_PARK = (1 << 26), OPT_IGNORE_FORWARDING = (1 << 27), + OPT_NOINBAND = (1 << 28), } dial_exec_option_flags; #define DIAL_STILLGOING (1 << 30) @@ -292,6 +295,7 @@ AST_APP_OPTIONS(dial_exec_options, { AST_APP_OPTION('p', OPT_SCREENING), AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY), AST_APP_OPTION('r', OPT_RINGBACK), + AST_APP_OPTION('R', OPT_NOINBAND), AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP), AST_APP_OPTION('t', OPT_CALLEE_TRANSFER), AST_APP_OPTION('T', OPT_CALLER_TRANSFER), @@ -407,7 +411,7 @@ static struct ast_channel *wait_for_answ int orig = *to; struct ast_channel *peer = NULL; /* single is set if only one destination is enabled */ - int single = outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK); + int single = (outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK | OPT_NOINBAND)); if (single) { /* Turn off hold music, etc */ @@ -628,7 +632,7 @@ static struct ast_channel *wait_for_answ /* Setup early media if appropriate */ if (single && CAN_EARLY_BRIDGE(peerflags)) ast_rtp_early_bridge(in, c); - if (!ast_test_flag(outgoing, OPT_RINGBACK)) + if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_NOINBAND)) ast_indicate(in, AST_CONTROL_PROGRESS); break; case AST_CONTROL_VIDUPDATE: @@ -641,7 +645,7 @@ static struct ast_channel *wait_for_answ ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", c->name, in->name); if (single && CAN_EARLY_BRIDGE(peerflags)) ast_rtp_early_bridge(in, c); - if (!ast_test_flag(outgoing, OPT_RINGBACK)) + if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_NOINBAND)) ast_indicate(in, AST_CONTROL_PROCEEDING); break; case AST_CONTROL_HOLD: @@ -659,7 +663,7 @@ static struct ast_channel *wait_for_answ /* Ignore going off hook and flash */ break; case -1: - if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) { + if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK | OPT_NOINBAND)) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "%s stopped sounds\n", c->name); ast_indicate(in, -1); @@ -1099,7 +1103,7 @@ static int dial_exec_full(struct ast_cha outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"); } - ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING); + ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_NOINBAND); /* loop through the list of dial destinations */ rest = args.peers; while ((cur = strsep(&rest, "&")) ) { @@ -1124,7 +1128,7 @@ static int dial_exec_full(struct ast_cha OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK | - OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID); + OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID | OPT_NOINBAND); ast_set2_flag(tmp, args.url, DIAL_NOFORWARDHTML); } ast_copy_string(numsubst, number, sizeof(numsubst)); @@ -1327,7 +1331,7 @@ static int dial_exec_full(struct ast_cha ast_moh_start(chan, NULL, NULL); } ast_indicate(chan, AST_CONTROL_PROGRESS); - } else if (ast_test_flag(outgoing, OPT_RINGBACK)) { + } else if (ast_test_flag(outgoing, OPT_RINGBACK | OPT_NOINBAND)) { ast_indicate(chan, AST_CONTROL_RINGING); sentringing++; } @@ -1444,7 +1448,7 @@ static int dial_exec_full(struct ast_cha if (ast_test_flag(&opts, OPT_MUSICBACK)) { ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { + } else if (ast_test_flag(&opts, OPT_RINGBACK | OPT_NOINBAND)) { ast_indicate(chan, -1); sentringing=0; } --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -196,7 +196,8 @@ static char *descrip = " R - indicate ringing to the calling party when the called party indicates\n" " ringing, pass no audio until answered.\n" " S(x) - Hang up the call after 'x' seconds *after* the called party has\n" -" answered the call.\n" +" answered the call.\n" +" c - callback initiation, ring once and hangup.\n" " t - Allow the called party to transfer the calling party by sending the\n" " DTMF sequence defined in features.conf.\n" " T - Allow the calling party to transfer the called party by sending the\n" @@ -252,6 +253,7 @@ enum { OPT_CALLER_PARK = (1 << 26), OPT_IGNORE_FORWARDING = (1 << 27), OPT_NOINBAND = (1 << 28), + OPT_CALLBACK_INIT = (1 << 29), } dial_exec_option_flags; #define DIAL_STILLGOING (1 << 30) @@ -296,6 +298,7 @@ AST_APP_OPTIONS(dial_exec_options, { AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY), AST_APP_OPTION('r', OPT_RINGBACK), AST_APP_OPTION('R', OPT_NOINBAND), + AST_APP_OPTION('c', OPT_CALLBACK_INIT), AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP), AST_APP_OPTION('t', OPT_CALLEE_TRANSFER), AST_APP_OPTION('T', OPT_CALLER_TRANSFER), @@ -616,14 +619,20 @@ static struct ast_channel *wait_for_answ HANDLE_CAUSE(AST_CAUSE_CONGESTION, in); break; case AST_CONTROL_RINGING: - if (option_verbose > 2) + if (ast_test_flag(peerflags, OPT_CALLBACK_INIT)) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "%s is ringing, hanging up.\n", o->chan->name); + return NULL; + } else { + if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", c->name); - /* Setup early media if appropriate */ - if (single && CAN_EARLY_BRIDGE(peerflags)) + /* Setup early media if appropriate */ + if (single && CAN_EARLY_BRIDGE(peerflags)) ast_rtp_early_bridge(in, c); - if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) { + if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) { ast_indicate(in, AST_CONTROL_RINGING); (*sentringing)++; + } } break; case AST_CONTROL_PROGRESS: @@ -1103,7 +1112,7 @@ static int dial_exec_full(struct ast_cha outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"); } - ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_NOINBAND); + ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_CALLBACK_INIT | OPT_NOINBAND); /* loop through the list of dial destinations */ rest = args.peers; while ((cur = strsep(&rest, "&")) ) { --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -104,6 +104,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #include "asterisk/abstract_jb.h" #include "asterisk/smdi.h" #include "asterisk/astobj.h" +#include "asterisk/devicestate.h" + #define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */ /*! Global jitterbuffer configuration - by default, jb is disabled */ @@ -703,6 +705,7 @@ static const struct ast_channel_tech zap .fixup = zt_fixup, .setoption = zt_setoption, .func_channel_read = zt_func_read, +/* .devicestate = zt_devicestate, */ }; #ifdef HAVE_PRI @@ -761,6 +764,112 @@ static int cidrings[NUM_CADENCE_MAX] = { #define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */) #define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */) +static int zt_devicestate(void *data) +{ + int groupmatch = 0; + int channelmatch = 0; + struct zt_pvt *p; + char *dest=NULL; + int x; + char *s; + char opt=0; + int res, y=0; + struct zt_pvt *exit, *start, *end; + ast_mutex_t *lock; + +// ast_log(LOG_NOTICE, "data = %s\n", (char *)data); + return AST_DEVICE_UNKNOWN; + + /* Assume we're locking the iflock */ + lock = &iflock; + start = iflist; + end = ifend; + + if (data) { + dest = ast_strdupa((char *)data); + } else { + ast_log(LOG_WARNING, "Channel requested with no data\n"); + return AST_DEVICE_INVALID; + } + if (toupper(dest[0]) == 'G' || toupper(dest[0])=='R') { + /* Retrieve the group number */ + char *stringp=NULL; + stringp=dest + 1; + s = strsep(&stringp, "/"); + if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) { + ast_log(LOG_WARNING, "Unable to determine group for data %s\n", (char *)data); + return AST_DEVICE_INVALID; + } + groupmatch = 1 << x; + } else { + char *stringp=NULL; + stringp=dest; + s = strsep(&stringp, "/"); + p = iflist; + if (!strcasecmp(s, "pseudo")) { + /* Special case for pseudo */ + x = CHAN_PSEUDO; + channelmatch = x; + /* bail out */ + return AST_DEVICE_INVALID; + } + + else if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) { + ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", (char *)data); + return AST_DEVICE_INVALID; + } else { + channelmatch = x; + ast_log(LOG_NOTICE, "channelmatch = %d\n", channelmatch); + } + } + /* Search for an unowned channel */ + if (ast_mutex_lock(lock)) { + ast_log(LOG_ERROR, "Unable to lock interface list???\n"); + return AST_DEVICE_INVALID; + } + p = iflist; + exit = iflist; + res = AST_DEVICE_INVALID; /* start pessimistic */ + while(p) { + if (p) { + ast_mutex_lock(&p->lock); + if ((groupmatch && ((p->group & groupmatch) != 0)) || (channelmatch && (p->channel == channelmatch))) { +#ifdef ZAPATA_PRI + if (p->pri) { + for(d=0;dpri->dchanavail[d] & DCHAN_UP) { + res = AST_DEVICE_UNKNOWN; + } + } + } +#endif + if ((!ast_strlen_zero(p->cid_num) && (strncasecmp(p->cid_num, dest, strlen(p->cid_num)))) || (!ast_strlen_zero(p->dnid) && (strncasecmp(p->dnid, dest, strlen(p->dnid))))) { + res = AST_DEVICE_UNKNOWN; + if (p->owner) { + if ((p->owner->_state == AST_STATE_RINGING) && (p->outgoing)) { + res = AST_DEVICE_RINGING; + } + if (((p->owner->_state == AST_STATE_RINGING) && (!p->outgoing)) || (p->owner->_state == AST_STATE_UP) || (p->owner->_state == AST_STATE_DIALING) || (p->owner->_state == AST_STATE_RESERVED) || (p->owner->_state == AST_STATE_RING)){ + res = AST_DEVICE_INUSE; + } + } + if ((res == AST_DEVICE_INUSE) || (res == AST_DEVICE_RINGING)) { + /* stop searching now, one non-idle channel is sufficient */ + ast_mutex_unlock(&p->lock); + break; + } + } + } + ast_mutex_unlock(&p->lock); + } + p = p->next; + } + ast_mutex_unlock(lock); + + return res; + +} + static int zt_get_index(struct ast_channel *ast, struct zt_pvt *p, int nullok) { int res; Change the API of ast_sendtext and chan->sendtext to add dest and ispdu parameters, used by ast_send_message which is also introduced by this patch. --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -227,7 +227,7 @@ struct ast_channel_tech { int (* const write)(struct ast_channel *chan, struct ast_frame *frame); /*! \brief Display or transmit text */ - int (* const send_text)(struct ast_channel *chan, const char *text); + int (* const send_text)(struct ast_channel *chan, const char *dest, const char *text, int ispdu); /*! \brief Display or send an image */ int (* const send_image)(struct ast_channel *chan, struct ast_frame *frame); @@ -671,6 +671,16 @@ struct ast_channel *ast_request_and_dial struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname, struct outgoing_helper *oh); +/*! \brief "Requests" a channel for sending a message + * \param type type of channel to request + * \param data data to pass to the channel requester + * \param status status + * Request a channel of a given type, with data as optional information used + * by the low level module + * \return Returns 0 on success, -1 on failure. + */ +int ast_send_message(const char *type, void *data, char *to, char *from, char *message, int ispdu); + /*!\brief Register a channel technology (a new channel driver) * Called by a channel module to register the kind of channels it supports. * \param tech Structure defining channel technology or "type" @@ -887,10 +897,12 @@ int ast_set_write_format(struct ast_chan /*! \brief Sends text to a channel * Write text to a display on a channel * \param chan channel to act upon + * \param dest destination number/user * \param text string of text to send on the channel + * \param ispdu message is in PDU format * \return Returns 0 on success, -1 on failure */ -int ast_sendtext(struct ast_channel *chan, const char *text); +int ast_sendtext(struct ast_channel *chan, const char *dest, const char *text, int ispdu); /*! \brief Receives a text character from a channel * \param chan channel to act upon --- a/main/channel.c +++ b/main/channel.c @@ -2694,7 +2694,7 @@ char *ast_recvtext(struct ast_channel *c return buf; } -int ast_sendtext(struct ast_channel *chan, const char *text) +int ast_sendtext(struct ast_channel *chan, const char *dest, const char *text, int ispdu) { int res = 0; /* Stop if we're a zombie or need a soft hangup */ @@ -2702,7 +2702,7 @@ int ast_sendtext(struct ast_channel *cha return -1; CHECK_BLOCKING(chan); if (chan->tech->send_text) - res = chan->tech->send_text(chan, text); + res = chan->tech->send_text(chan, dest, text, ispdu); ast_clear_flag(chan, AST_FLAG_BLOCKING); return res; } @@ -2881,7 +2881,7 @@ int ast_write(struct ast_channel *chan, break; case AST_FRAME_TEXT: res = (chan->tech->send_text == NULL) ? 0 : - chan->tech->send_text(chan, (char *) fr->data); + chan->tech->send_text(chan, NULL, (char *) fr->data, 0); break; case AST_FRAME_HTML: res = (chan->tech->send_html == NULL) ? 0 : @@ -4908,6 +4908,25 @@ void ast_channel_stop_silence_generator( } +int ast_send_message(const char *type, void *data, char *to, char *from, char *message, int ispdu) { + struct ast_channel *chan = NULL; + int status; + int res = -1; + + chan = ast_request(type, AST_FORMAT_SLINEAR, data, &status); + if (chan) { + if (from) { + ast_set_callerid(chan, from, from, from); + } + res = ast_sendtext(chan, to, message, ispdu); + /* XXX what about message CDRs ??? XXX */ + ast_hangup(chan); + return res; + } + + return res; +} + /*! \ brief Convert channel reloadreason (ENUM) to text string for manager event */ const char *channelreloadreason2txt(enum channelreloadreason reason) { --- a/apps/app_sendtext.c +++ b/apps/app_sendtext.c @@ -103,7 +103,7 @@ static int sendtext_exec(struct ast_chan } status = "FAILURE"; ast_channel_unlock(chan); - res = ast_sendtext(chan, args.text); + res = ast_sendtext(chan, NULL, args.text, 0); if (!res) status = "SUCCESS"; pbx_builtin_setvar_helper(chan, "SENDTEXTSTATUS", status); --- a/res/res_agi.c +++ b/res/res_agi.c @@ -486,7 +486,7 @@ static int handle_sendtext(struct ast_ch would probably be to strip off the trailing newline before 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]); + res = ast_sendtext(chan, NULL, argv[2], 0); fdprintf(agi->fd, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; } --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -246,7 +246,7 @@ static int agent_answer(struct ast_chann static struct ast_frame *agent_read(struct ast_channel *ast); static int agent_write(struct ast_channel *ast, struct ast_frame *f); static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen); -static int agent_sendtext(struct ast_channel *ast, const char *text); +static int agent_sendtext(struct ast_channel *ast, const char *dest, const char *text, int ispdu); static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge); @@ -555,13 +555,13 @@ static int agent_sendhtml(struct ast_cha return res; } -static int agent_sendtext(struct ast_channel *ast, const char *text) +static int agent_sendtext(struct ast_channel *ast, const char *dest, const char *text, int ispdu) { struct agent_pvt *p = ast->tech_pvt; int res = -1; ast_mutex_lock(&p->lock); if (p->chan) - res = ast_sendtext(p->chan, text); + res = ast_sendtext(p->chan, dest, text, ispdu); ast_mutex_unlock(&p->lock); return res; } --- a/channels/chan_alsa.c +++ b/channels/chan_alsa.c @@ -186,7 +186,7 @@ static int nosound = 0; /* ZZ */ static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause); static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration); -static int alsa_text(struct ast_channel *c, const char *text); +static int alsa_text(struct ast_channel *c, const char *dest, const char *text, int ispdu); static int alsa_hangup(struct ast_channel *c); static int alsa_answer(struct ast_channel *c); static struct ast_frame *alsa_read(struct ast_channel *chan); @@ -496,7 +496,7 @@ static int alsa_digit(struct ast_channel return 0; } -static int alsa_text(struct ast_channel *c, const char *text) +static int alsa_text(struct ast_channel *c, const char *dest, const char *text, int ispdu) { ast_mutex_lock(&alsalock); ast_verbose(" << Console Received text %s >> \n", text); --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -77,7 +77,7 @@ static int local_write(struct ast_channe static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen); -static int local_sendtext(struct ast_channel *ast, const char *text); +static int local_sendtext(struct ast_channel *ast, const char *dest, const char *text, int ispdu); static int local_devicestate(void *data); /* PBX interface structure for channel registration */ @@ -390,7 +390,7 @@ static int local_digit_end(struct ast_ch return res; } -static int local_sendtext(struct ast_channel *ast, const char *text) +static int local_sendtext(struct ast_channel *ast, const char *dest, const char *text, int ispdu) { struct local_pvt *p = ast->tech_pvt; int res = -1; --- a/channels/chan_oss.c +++ b/channels/chan_oss.c @@ -405,7 +405,7 @@ static struct ast_channel *oss_request(c , int *cause); static int oss_digit_begin(struct ast_channel *c, char digit); static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration); -static int oss_text(struct ast_channel *c, const char *text); +static int oss_text(struct ast_channel *c, const char *dest, const char *text, int ispdu); static int oss_hangup(struct ast_channel *c); static int oss_answer(struct ast_channel *c); static struct ast_frame *oss_read(struct ast_channel *chan); @@ -773,7 +773,7 @@ static int oss_digit_end(struct ast_chan return 0; } -static int oss_text(struct ast_channel *c, const char *text) +static int oss_text(struct ast_channel *c, const char *dest, const char *text, int ispdu) { /* print received messages */ ast_verbose(" << Console Received text %s >> \n", text); --- a/channels/chan_phone.c +++ b/channels/chan_phone.c @@ -166,7 +166,7 @@ static int phone_answer(struct ast_chann static struct ast_frame *phone_read(struct ast_channel *ast); static int phone_write(struct ast_channel *ast, struct ast_frame *frame); static struct ast_frame *phone_exception(struct ast_channel *ast); -static int phone_send_text(struct ast_channel *ast, const char *text); +static int phone_send_text(struct ast_channel *ast, const char *dest, const char *text, int ispdu); static int phone_fixup(struct ast_channel *old, struct ast_channel *new); static int phone_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen); @@ -640,7 +640,7 @@ static int phone_write_buf(struct phone_ return len; } -static int phone_send_text(struct ast_channel *ast, const char *text) +static int phone_send_text(struct ast_channel *ast, const char *dest, const char *text, int ispdu) { int length = strlen(text); return phone_write_buf(ast->tech_pvt, text, length, length, 0) == --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1215,7 +1215,7 @@ static struct ast_config *notify_types; /*--- PBX interface functions */ static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause); static int sip_devicestate(void *data); -static int sip_sendtext(struct ast_channel *ast, const char *text); +static int sip_sendtext(struct ast_channel *ast, const char *dest, const char *text, int ispdu); static int sip_call(struct ast_channel *ast, char *dest, int timeout); static int sip_hangup(struct ast_channel *ast); static int sip_answer(struct ast_channel *ast); @@ -2347,7 +2347,7 @@ static char *get_in_brackets(char *tmp) /*! \brief Send SIP MESSAGE text within a call Called from PBX core sendtext() application */ -static int sip_sendtext(struct ast_channel *ast, const char *text) +static int sip_sendtext(struct ast_channel *ast, const char *dest, const char *text, int ispdu) { struct sip_pvt *p = ast->tech_pvt; int debug = sip_debug_test_pvt(p); --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -831,7 +831,7 @@ static int iax2_provision(struct sockadd static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final); static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen); static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img); -static int iax2_sendtext(struct ast_channel *c, const char *text); +static int iax2_sendtext(struct ast_channel *c, const char *dest, const char *text, int ispdu); static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen); static int iax2_transfer(struct ast_channel *c, const char *dest); static int iax2_write(struct ast_channel *c, struct ast_frame *f); @@ -2642,7 +2642,7 @@ static int iax2_digit_end(struct ast_cha return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1); } -static int iax2_sendtext(struct ast_channel *c, const char *text) +static int iax2_sendtext(struct ast_channel *c, const char *dest, const char *text, int ispdu) { return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_TEXT, --- a/main/manager.c +++ b/main/manager.c @@ -11,6 +11,9 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2003-2004, Junghanns.NET Gmbh + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -1426,6 +1429,49 @@ static int action_hangup(struct mansessi return 0; } +static char mandescr_message[] = +"Description: Send a message\n" +"Variables: \n" +" Channel: The destination channel(e.g. SIP/phone1)\n" +" From: \n" +" Message: The message to send\n"; + +static int action_message(struct mansession *s, const struct message *m) +{ + const char *name = astman_get_header(m, "Channel"); + const char *from = astman_get_header(m, "From"); + const char *message = astman_get_header(m, "Message"); + const char *pdu = astman_get_header(m, "PDU"); + char tmp[256]; + char *tech, *data; + int res; + if (ast_strlen_zero(name) || (ast_strlen_zero(message) && ast_strlen_zero(pdu))) { + astman_send_error(s, m, "No channel or message/PDU specified"); + return 0; + } + ast_copy_string(tmp, name, sizeof(tmp)); + tech = tmp; + data = strchr(tmp, '/'); + if (!data) { + astman_send_error(s, m, "Invalid channel\n"); + return 0; + } + *data = '\0'; + data++; + if (ast_strlen_zero(pdu)) { + res = ast_send_message(tech, (char *)data, (char *)name, (char *)from, (char *)message, 0); + } else { + res = ast_send_message(tech, (char *)data, (char *)name, (char *)from, (char *)pdu, 1); + } + + if (res) { + astman_send_error(s, m, "Error sending message"); + return 0; + } + astman_send_ack(s, m, "Message sent"); + return 0; +} + static char mandescr_setvar[] = "Description: Set a global or local channel variable.\n" "Variables: (Names marked with * are required)\n" @@ -2835,6 +2881,7 @@ int init_manager(void) ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events); ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff); ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup); + ast_manager_register2("Message", EVENT_FLAG_CALL, action_message, "Send Message", mandescr_message); ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" ); ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar ); ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar ); --- a/pbx/pbx_spool.c +++ b/pbx/pbx_spool.c @@ -87,6 +87,10 @@ struct outgoing { char app[256]; char data[256]; + /* If SMS */ + char message[256]; + char pdu[256]; + /* If extension/context/priority */ char exten[256]; char context[256]; @@ -181,6 +185,10 @@ static int apply_outgoing(struct outgoin ast_copy_string(o->app, c, sizeof(o->app)); } else if (!strcasecmp(buf, "data")) { ast_copy_string(o->data, c, sizeof(o->data)); + } else if (!strcasecmp(buf, "message")) { + strncpy(o->message, c, sizeof(o->message) - 1); + } else if (!strcasecmp(buf, "pdu")) { + strncpy(o->pdu, c, sizeof(o->pdu) - 1); } else if (!strcasecmp(buf, "maxretries")) { if (sscanf(c, "%d", &o->maxretries) != 1) { ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn); @@ -241,8 +249,8 @@ static int apply_outgoing(struct outgoin } } ast_copy_string(o->fn, fn, sizeof(o->fn)); - if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) { - ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn); + if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || ((ast_strlen_zero(o->app) && ast_strlen_zero(o->exten) && ast_strlen_zero(o->message) && ast_strlen_zero(o->pdu)))) { + ast_log(LOG_WARNING, "At least one of app or extension (or keyword message/pdu)must be specified, along with tech and dest in file %s\n", fn); return -1; } return 0; @@ -332,6 +340,14 @@ static void *attempt_thread(void *data) if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for application %s(%s) (Retry %d)\n", o->tech, o->dest, o->app, o->data, o->retries); res = ast_pbx_outgoing_app(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL); + } else if (!ast_strlen_zero(o->message)) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Attempting to send message on %s/%s (Retry %d)\n", o->tech, o->dest, o->retries); + res = ast_send_message(o->tech, o->dest, o->dest, (ast_strlen_zero(o->cid_name) ? o->cid_num : o->cid_name), o->message, 0); + } else if (!ast_strlen_zero(o->pdu)) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Attempting to send message in PDU format on %s/%s (Retry %d)\n", o->tech, o->dest, o->retries); + res = ast_send_message(o->tech, o->dest, o->dest, (ast_strlen_zero(o->cid_name) ? o->cid_num : o->cid_name), o->pdu, 1); } else { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries); @@ -348,9 +364,14 @@ static void *attempt_thread(void *data) safe_append(o, time(NULL), "EndRetry"); } } else { + if (!ast_strlen_zero(o->message)) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_2 "Message sent to %s/%s\n", o->tech, o->dest); + } else { ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest); ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest); - remove_from_queue(o, "Completed"); + } + remove_from_queue(o, "Completed"); } free_outgoing(o); return NULL; --- a/include/asterisk/monitor.h +++ b/include/asterisk/monitor.h @@ -38,6 +38,8 @@ struct ast_channel_monitor { char write_filename[FILENAME_MAX]; char filename_base[FILENAME_MAX]; int filename_changed; + char target_url[FILENAME_MAX]; + char target_script[FILENAME_MAX]; char *format; int joinfiles; enum AST_MONITORING_STATE state; @@ -46,7 +48,7 @@ struct ast_channel_monitor { /* Start monitoring a channel */ int ast_monitor_start(struct ast_channel *chan, const char *format_spec, - const char *fname_base, int need_lock ); + const char *fname_base, const char *target_url, const char *target_script, int need_lock ); /* Stop monitoring a channel */ int ast_monitor_stop(struct ast_channel *chan, int need_lock); --- a/res/res_monitor.c +++ b/res/res_monitor.c @@ -129,7 +129,7 @@ static int ast_monitor_set_state(struct /* Start monitoring a channel */ int ast_monitor_start( struct ast_channel *chan, const char *format_spec, - const char *fname_base, int need_lock) + const char *fname_base, const char *target_url, const char *target_script, int need_lock) { int res = 0; char tmp[256]; @@ -153,6 +153,11 @@ int ast_monitor_start( struct ast_channe return -1; } + if (target_url) + ast_copy_string(monitor->target_url, target_url, sizeof(monitor->target_url)); + if (target_script) + ast_copy_string(monitor->target_script, target_script, sizeof(monitor->target_script)); + /* Determine file names */ if (!ast_strlen_zero(fname_base)) { int directory = strchr(fname_base, '/') ? 1 : 0; @@ -295,6 +300,8 @@ int ast_monitor_stop(struct ast_channel if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) { char tmp[1024]; char tmp2[1024]; + char tmp3[1024]; + int result; const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format; char *name = chan->monitor->filename_base; int directory = strchr(name, '/') ? 1 : 0; @@ -322,8 +329,13 @@ int ast_monitor_stop(struct ast_channel snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */ ast_copy_string(tmp, tmp2, sizeof(tmp)); } - ast_log(LOG_DEBUG,"monitor executing %s\n",tmp); - if (ast_safe_system(tmp) == -1) + if (!ast_strlen_zero(chan->monitor->target_script) && !ast_strlen_zero(chan->monitor->target_url)) { + snprintf(tmp3,sizeof(tmp3), "( %s& nice -19 %s \"%s/%s.%s\" \"%s\" ) &",tmp, chan->monitor->target_script , dir, name, format, chan->monitor->target_url); + ast_copy_string(tmp, tmp3, sizeof(tmp)); + } + ast_log(LOG_NOTICE,"monitor executing %s\n",tmp); + result = ast_safe_system(tmp); + if (result == -1) ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp); } @@ -452,7 +464,7 @@ static int start_monitor_exec(struct ast return 0; } - res = ast_monitor_start(chan, format, fname_base, 1); + res = ast_monitor_start(chan, format, fname_base, NULL, NULL, 1); if (res < 0) res = ast_monitor_change_fname(chan, fname_base, 1); ast_monitor_setjoinfiles(chan, joinfiles); @@ -491,6 +503,8 @@ static int start_monitor_action(struct m const char *fname = astman_get_header(m, "File"); const char *format = astman_get_header(m, "Format"); const char *mix = astman_get_header(m, "Mix"); + const char *target_url = astman_get_header(m, "TargetURL"); + const char *target_script = astman_get_header(m, "TargetScript"); char *d; if (ast_strlen_zero(name)) { @@ -515,7 +529,7 @@ static int start_monitor_action(struct m *d = '-'; } - if (ast_monitor_start(c, format, fname, 1)) { + if (ast_monitor_start(c, format, fname, target_url, target_script, 1)) { if (ast_monitor_change_fname(c, fname, 1)) { astman_send_error(s, m, "Could not start monitoring channel"); ast_channel_unlock(c); --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -2804,13 +2804,13 @@ static int try_calling(struct queue_ent else which = peer; if (monitorfilename) - ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 ); + ast_monitor_start(which, qe->parent->monfmt, monitorfilename, NULL, NULL, 1 ); else if (qe->chan->cdr) - ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); + ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, NULL, NULL, 1 ); else { /* Last ditch effort -- no CDR, make up something */ snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); - ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 ); + ast_monitor_start(which, qe->parent->monfmt, tmpid, NULL, NULL, 1 ); } if (qe->parent->monjoin) ast_monitor_setjoinfiles(which, 1); --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -416,7 +416,7 @@ static int __agent_start_monitoring(stru if ((pointer = strchr(filename, '.'))) *pointer = '-'; snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename); - ast_monitor_start(ast, recordformat, tmp, needlock); + ast_monitor_start(ast, recordformat, tmp, NULL, NULL, needlock); ast_monitor_setjoinfiles(ast, 1); snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext); #if 0 --- a/include/asterisk/devicestate.h +++ b/include/asterisk/devicestate.h @@ -47,7 +47,7 @@ extern "C" { #define AST_DEVICE_ONHOLD 8 /*! \brief Devicestate watcher call back */ -typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data); +typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data, char *cid_num, char *cid_name); /*! \brief Devicestate provider call back */ typedef int (*ast_devstate_prov_cb_type)(const char *data); @@ -92,7 +92,7 @@ int ast_device_state_changed(const char * callbacks for the changed extensions * Returns 0 on success, -1 on failure */ -int ast_device_state_changed_literal(const char *device); +int ast_device_state_changed_literal(const char *device, const char *cid_num, const char *cid_name); /*! \brief Registers a device state change callback * \param callback Callback --- a/main/devicestate.c +++ b/main/devicestate.c @@ -78,6 +78,8 @@ static AST_LIST_HEAD_STATIC(devstate_cbs struct state_change { AST_LIST_ENTRY(state_change) list; + char cid_num[AST_MAX_EXTENSION]; + char cid_name[AST_MAX_EXTENSION]; char device[1]; }; @@ -277,7 +279,7 @@ void ast_devstate_del(ast_devstate_cb_ty /*! \brief Notify callback watchers of change, and notify PBX core for hint updates Normally executed within a separate thread */ -static void do_state_change(const char *device) +static void do_state_change(const char *device, char *cid_num, char *cid_name) { int state; struct devstate_cb *devcb; @@ -288,13 +290,13 @@ static void do_state_change(const char * AST_LIST_LOCK(&devstate_cbs); AST_LIST_TRAVERSE(&devstate_cbs, devcb, list) - devcb->callback(device, state, devcb->data); + devcb->callback(device, state, devcb->data, cid_num, cid_name); AST_LIST_UNLOCK(&devstate_cbs); - ast_hint_state_changed(device); + ast_hint_state_changed(device, cid_num, cid_name); } -static int __ast_device_state_changed_literal(char *buf) +static int __ast_device_state_changed_literal(char *buf, char *cid_num, char *cid_name) { char *device; struct state_change *change; @@ -312,10 +314,16 @@ static int __ast_device_state_changed_li if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) { /* we could not allocate a change struct, or */ /* there is no background thread, so process the change now */ - do_state_change(device); + do_state_change(device, cid_num, cid_name); } else { /* queue the change */ strcpy(change->device, device); + if (cid_num && (!ast_strlen_zero(cid_num))) { + strncpy(change->cid_num, cid_num, sizeof(change->cid_num) - 1); + } + if (cid_name && (!ast_strlen_zero(cid_name))) { + strncpy(change->cid_name, cid_name, sizeof(change->cid_name) - 1); + } AST_LIST_LOCK(&state_changes); AST_LIST_INSERT_TAIL(&state_changes, change, list); if (AST_LIST_FIRST(&state_changes) == change) @@ -327,11 +335,17 @@ static int __ast_device_state_changed_li return 1; } -int ast_device_state_changed_literal(const char *dev) +int ast_device_state_changed_literal(const char *dev, const char *cid_num, const char *cid_name) { char *buf; + char *buf2 = NULL; + char *buf3 = NULL; buf = ast_strdupa(dev); - return __ast_device_state_changed_literal(buf); + if (cid_num) + buf2 = ast_strdupa(cid_num); + if (cid_name) + buf3 = ast_strdupa(cid_name); + return __ast_device_state_changed_literal(buf, buf2, buf3); } /*! \brief Accept change notification, add it to change queue */ @@ -343,7 +357,7 @@ int ast_device_state_changed(const char va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - return __ast_device_state_changed_literal(buf); + return __ast_device_state_changed_literal(buf, NULL, NULL); } /*! \brief Go through the dev state change queue and update changes in the dev state thread */ @@ -358,7 +372,7 @@ static void *do_devstate_changes(void *d if (cur) { /* we got an entry, so unlock the list while we process it */ AST_LIST_UNLOCK(&state_changes); - do_state_change(cur->device); + do_state_change(cur->device, cur->cid_num, cur->cid_name); free(cur); AST_LIST_LOCK(&state_changes); } else { --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -590,8 +590,13 @@ int ast_channel_datastore_remove(struct /*! \brief Find a datastore on a channel */ struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid); +extern ast_mutex_t uniquelock; + +/*! \brief Change the state of a channel and the callerid of the calling channel*/ +int ast_setstate_and_cid(struct ast_channel *chan, enum ast_channel_state state, char *cid_num, char *cid_name); + /*! \brief Change the state of a channel */ -int ast_setstate(struct ast_channel *chan, enum ast_channel_state); +int ast_setstate(struct ast_channel *chan, enum ast_channel_state state); /*! \brief Create a channel structure \return Returns NULL on failure to allocate. --- a/main/channel.c +++ b/main/channel.c @@ -1277,7 +1277,7 @@ void ast_channel_free(struct ast_channel free(chan); AST_LIST_UNLOCK(&channels); - ast_device_state_changed_literal(name); + ast_device_state_changed_literal(name, NULL, NULL); } struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, char *uid) @@ -3941,7 +3941,7 @@ void ast_set_callerid(struct ast_channel ast_channel_unlock(chan); } -int ast_setstate(struct ast_channel *chan, enum ast_channel_state state) +int ast_setstate_and_cid(struct ast_channel *chan, enum ast_channel_state state, char *cid_num, char *cid_name) { int oldstate = chan->_state; @@ -3949,7 +3949,7 @@ int ast_setstate(struct ast_channel *cha return 0; chan->_state = state; - ast_device_state_changed_literal(chan->name); + ast_device_state_changed_literal(chan->name, cid_num, cid_name); /* setstate used to conditionally report Newchannel; this is no more */ manager_event(EVENT_FLAG_CALL, "Newstate", @@ -3966,6 +3966,11 @@ int ast_setstate(struct ast_channel *cha return 0; } +int ast_setstate(struct ast_channel *chan, enum ast_channel_state state) +{ + return ast_setstate_and_cid(chan, state, NULL, NULL); +} + /*! \brief Find bridged channel */ struct ast_channel *ast_bridged_channel(struct ast_channel *chan) { --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -63,7 +63,7 @@ struct ast_ignorepat; struct ast_sw; /*! \brief Typedef for devicestate and hint callbacks */ -typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data); +typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data, char *cid_num, char *cid_name); /*! \brief Data structure associated with a custom dialplan function */ struct ast_custom_function { @@ -875,7 +875,7 @@ int ast_func_read(struct ast_channel *ch */ int ast_func_write(struct ast_channel *chan, char *function, const char *value); -void ast_hint_state_changed(const char *device); +void ast_hint_state_changed(const char *device, char *cid_num, char *cid_name); #if defined(__cplusplus) || defined(c_plusplus) } --- a/main/pbx.c +++ b/main/pbx.c @@ -2011,7 +2011,7 @@ int ast_extension_state(struct ast_chann return ast_extension_state2(e); /* Check all devices in the hint */ } -void ast_hint_state_changed(const char *device) +void ast_hint_state_changed(const char *device, char *cid_num, char *cid_name) { struct ast_hint *hint; @@ -2042,11 +2042,11 @@ void ast_hint_state_changed(const char * /* For general callbacks */ for (cblist = statecbs; cblist; cblist = cblist->next) - cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data, cid_num, cid_name); /* For extension callbacks */ for (cblist = hint->callbacks; cblist; cblist = cblist->next) - cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data, cid_num, cid_name); hint->laststate = state; /* record we saw the change */ } @@ -2241,7 +2241,7 @@ static int ast_remove_hint(struct ast_ex /* Notify with -1 and remove all callbacks */ cbprev = cblist; cblist = cblist->next; - cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data); + cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data, NULL, NULL); free(cbprev); } hint->callbacks = NULL; @@ -4008,7 +4008,7 @@ void ast_merge_contexts_and_delete(struc while (thiscb) { prevcb = thiscb; thiscb = thiscb->next; - prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data); + prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data, NULL, NULL); free(prevcb); } } else { --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1338,7 +1338,7 @@ static void ast_quiet_chan(struct ast_ch static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); /*--- Device monitoring and Device/extension state handling */ -static int cb_extensionstate(char *context, char* exten, int state, void *data); +static int cb_extensionstate(char *context, char* exten, int state, void *data, char *cid_num, char *cid_name); static int sip_devicestate(void *data); static int sip_poke_noanswer(const void *data); static int sip_poke_peer(struct sip_peer *peer); @@ -8459,7 +8459,7 @@ static void sip_peer_hold(struct sip_pvt /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem \note If you add an "hint" priority to the extension in the dial plan, you will get notifications on device state changes */ -static int cb_extensionstate(char *context, char* exten, int state, void *data) +static int cb_extensionstate(char *context, char* exten, int state, void *data, char *cid_num, char *cid_name) { struct sip_pvt *p = data; --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -690,7 +690,7 @@ static void *device_state_thread(void *d return NULL; } -static int statechange_queue(const char *dev, int state, void *ign) +static int statechange_queue(const char *dev, int state, void *ign, char *cid_num, char *cid_name) { struct statechange *sc; --- a/include/asterisk/manager.h +++ b/include/asterisk/manager.h @@ -55,6 +55,7 @@ #define EVENT_FLAG_AGENT (1 << 5) /* Ability to read/set agent info */ #define EVENT_FLAG_USER (1 << 6) /* Ability to read/set user info */ #define EVENT_FLAG_CONFIG (1 << 7) /* Ability to modify configurations */ +#define EVENT_FLAG_EXTENSIONSTATUS (1 << 8) /* ExtensionStatus events */ /* Export manager structures */ #define AST_MAX_MANHEADERS 128 --- a/main/manager.c +++ b/main/manager.c @@ -129,6 +129,7 @@ static struct permalias { { EVENT_FLAG_AGENT, "agent" }, { EVENT_FLAG_USER, "user" }, { EVENT_FLAG_CONFIG, "config" }, + { EVENT_FLAG_EXTENSIONSTATUS, "extensionstatus" }, { -1, "all" }, { 0, "none" }, }; @@ -2538,10 +2539,12 @@ int ast_manager_unregister(char *action) return 0; } -static int manager_state_cb(char *context, char *exten, int state, void *data) +static int manager_state_cb(char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) { + char hint[256] = ""; + ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten); /* Notify managers of change */ - manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state); + manager_event(EVENT_FLAG_EXTENSIONSTATUS, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\nCallerID: \"%s\" <%s>\r\nHint: %s\r\n", exten, context, state, cid_num, cid_name, hint); return 0; } --- a/apps/app_devstate.c +++ b/apps/app_devstate.c @@ -50,7 +50,7 @@ static struct ast_cli_entry cli_dev_sta static int devstate_cli(int fd, int argc, char *argv[]) { char devName[128]; - if (argc != 3) + if ((argc != 3) && (argc != 4) && (argc != 5)) return RESULT_SHOWUSAGE; if (ast_db_put("DEVSTATES", argv[1], argv[2])) @@ -58,7 +58,15 @@ static int devstate_cli(int fd, int argc ast_log(LOG_DEBUG, "ast_db_put failed\n"); } snprintf(devName, sizeof(devName), "DS/%s", argv[1]); - ast_device_state_changed_literal(devName); + if (argc == 4) { + ast_log(LOG_NOTICE, "devname %s cid %s\n", devName, argv[3]); + ast_device_state_changed_literal(devName, argv[3], NULL); + } else if (argc == 5) { + ast_log(LOG_NOTICE, "devname %s cid %s cidname %s\n", devName, argv[3], argv[4]); + ast_device_state_changed_literal(devName, argv[3], argv[4]); + } else { + ast_device_state_changed_literal(devName, NULL, NULL); + } return RESULT_SUCCESS; } @@ -93,7 +101,7 @@ static int devstate_exec(struct ast_chan } snprintf(devName, sizeof(devName), "DS/%s", device); - ast_device_state_changed_literal(devName); + ast_device_state_changed_literal(devName, NULL, NULL); ast_module_user_remove(u); return 0; @@ -150,6 +158,8 @@ static int action_devstate(struct manses const char *devstate = astman_get_header(m, "Devstate"); const char *value = astman_get_header(m, "Value"); const char *id = astman_get_header(m,"ActionID"); + const char *cid_num = astman_get_header(m, "CallerID"); + const char *cid_name = astman_get_header(m, "CallerIDName"); char devName[128]; char idText[256] = ""; @@ -166,7 +176,7 @@ static int action_devstate(struct manses if (!ast_db_put("DEVSTATES", devstate, (char *)value)) { snprintf(devName, sizeof(devName), "DS/%s", devstate); - ast_device_state_changed_literal(devName); + ast_device_state_changed_literal(devName, cid_num, cid_name); astman_append(s, "Response: Success\r\n%s\r\n", idText); } else { ast_log(LOG_DEBUG, "ast_db_put failed\n"); --- a/res/res_esel.c +++ b/res/res_esel.c @@ -51,6 +51,8 @@ typedef struct esel_extension_state { char context[AST_MAX_EXTENSION]; char exten[AST_MAX_EXTENSION]; int state; + char cid_num[AST_MAX_EXTENSION]; + char cid_name[AST_MAX_EXTENSION]; char devstate[AST_MAX_EXTENSION]; struct esel_extension_state *next; struct esel_extension_state *prev; @@ -93,7 +95,7 @@ typedef struct esel_pvt { static struct esel_pvt *donkeys = NULL; -static int esel_queue_extension_state(struct esel_queue *queue, char *context, char *exten, int state, void *data) { +static int esel_queue_extension_state(struct esel_queue *queue, char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) { struct esel_extension_state *exstate = NULL; exstate = malloc(sizeof(struct esel_extension_state)); @@ -115,6 +117,8 @@ static int esel_queue_extension_state(st } ast_copy_string(exstate->exten, exten, sizeof(exstate->exten)); ast_copy_string(exstate->context, context, sizeof(exstate->context)); + ast_copy_string(exstate->cid_num, cid_num, sizeof(exstate->cid_num)); + ast_copy_string(exstate->cid_name, cid_name, sizeof(exstate->cid_name)); exstate->state = state; if (!queue->head) { /* Empty queue */ @@ -161,7 +165,7 @@ static void esel_export_to_remote(struct char msg[1024]; int sent = 0; memset(msg, 0x0, sizeof(msg)); - snprintf(msg, sizeof(msg) - 1, "Action: Devstate\r\nDevstate: %s\r\nValue: %d\r\n\r\n", exstate->devstate, esel_state2devstate(exstate->state)); + snprintf(msg, sizeof(msg) - 1, "Action: Devstate\r\nDevstate: %s\r\nValue: %d\r\nCallerID: %s\r\nCallerIDName: %s\r\n\r\n", exstate->devstate, esel_state2devstate(exstate->state), exstate->cid_num, exstate->cid_name); sent = send(esel->sockfd, msg, strlen(msg), 0); if (sent == -1) { esel->connected = 0; @@ -250,13 +254,13 @@ static void *do_esel_thread(void *data) return NULL; } -static int esel_state_cb(char *context, char *exten, int state, void *data) { +static int esel_state_cb(char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) { struct esel_pvt *esel; esel = donkeys; ast_mutex_lock(&listlock); while (esel) { - esel_queue_extension_state(&esel->queue, context, exten, state, data); + esel_queue_extension_state(&esel->queue, context, exten, state, data, cid_num, cid_name); esel = esel->next; } ast_mutex_unlock(&listlock); Use Asterisk's process ID when building the unique ID. --- a/main/channel.c +++ b/main/channel.c @@ -808,10 +808,10 @@ struct ast_channel *ast_channel_alloc(in tmp->fout = global_fout; if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) { - ast_string_field_build(tmp, uniqueid, "%li.%d", (long) time(NULL), + ast_string_field_build(tmp, uniqueid, "%d-%li.%d", ast_mainpid, (long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1)); } else { - ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME, + ast_string_field_build(tmp, uniqueid, "%s-%d-%li.%d", ast_config_AST_SYSTEM_NAME, ast_mainpid, (long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1)); } Add or convert channel operations so they can use the unique ID. --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -659,6 +659,18 @@ void ast_channel_free(struct ast_channe */ struct ast_channel *ast_request(const char *type, int format, void *data, int *status); +/*! \brief Requests a channel + * \param type type of channel to request + * \param format requested channel format (codec) + * \param data data to pass to the channel requester + * \param status status + * \param uniqueid uniqueid + * Request a channel of a given type, with data as optional information used + * by the low level module. Sets the channels uniqueid to 'uniqueid'. + * \return Returns an ast_channel on success, NULL on failure. + */ +struct ast_channel *ast_request_with_uniqueid(const char *type, int format, void *data, int *status, char *uniqueid); + /*! * \brief Request a channel of a given type, with data as optional information used * by the low level module and attempt to place a call on it @@ -672,9 +684,9 @@ struct ast_channel *ast_request(const ch * \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state * to know if the call was answered or not. */ -struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname); +struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, int callingpres, const char *cidnum, const char *cidname, char *uniqueid); -struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname, struct outgoing_helper *oh); +struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, int callingpres, const char *cidnum, const char *cidname, struct outgoing_helper *oh, char *uniqueid); /*! \brief "Requests" a channel for sending a message * \param type type of channel to request @@ -959,6 +971,8 @@ struct ast_channel *ast_get_channel_by_e /*! \brief Get next channel by exten (and optionally context) and lock it */ struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten, const char *context); +/*! Get channel by uniqueid (locks channel) */ +struct ast_channel *ast_get_channel_by_uniqueid_locked(const char *uniqueid); /*! ! \brief Waits for a digit * \param c channel to wait for a digit on --- a/main/channel.c +++ b/main/channel.c @@ -1035,7 +1035,7 @@ void ast_channel_undefer_dtmf(struct ast */ static struct ast_channel *channel_find_locked(const struct ast_channel *prev, const char *name, const int namelen, - const char *context, const char *exten) + const char *context, const char *exten, const char *uniqueid) { const char *msg = prev ? "deadlock" : "initial deadlock"; int retries; @@ -1063,7 +1063,10 @@ static struct ast_channel *channel_find_ * XXX Need a better explanation for this ... */ } - if (name) { /* want match by name */ + if (uniqueid) { + if (!strcasecmp(c->uniqueid, uniqueid)) + break; + } else if (name) { /* want match by name */ if ((!namelen && strcasecmp(c->name, name)) || (namelen && strncasecmp(c->name, name, namelen))) continue; /* name match failed */ @@ -1118,39 +1121,44 @@ static struct ast_channel *channel_find_ /*! \brief Browse channels in use */ struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev) { - return channel_find_locked(prev, NULL, 0, NULL, NULL); + return channel_find_locked(prev, NULL, 0, NULL, NULL, NULL); } /*! \brief Get channel by name and lock it */ struct ast_channel *ast_get_channel_by_name_locked(const char *name) { - return channel_find_locked(NULL, name, 0, NULL, NULL); + return channel_find_locked(NULL, name, 0, NULL, NULL, NULL); } /*! \brief Get channel by name prefix and lock it */ struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen) { - return channel_find_locked(NULL, name, namelen, NULL, NULL); + return channel_find_locked(NULL, name, namelen, NULL, NULL, NULL); } /*! \brief Get next channel by name prefix and lock it */ struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name, const int namelen) { - return channel_find_locked(chan, name, namelen, NULL, NULL); + return channel_find_locked(chan, name, namelen, NULL, NULL, NULL); } /*! \brief Get channel by exten (and optionally context) and lock it */ struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context) { - return channel_find_locked(NULL, NULL, 0, context, exten); + return channel_find_locked(NULL, NULL, 0, context, exten, NULL); } /*! \brief Get next channel by exten (and optionally context) and lock it */ struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten, const char *context) { - return channel_find_locked(chan, NULL, 0, context, exten); + return channel_find_locked(chan, NULL, 0, context, exten, NULL); +} + +struct ast_channel *ast_get_channel_by_uniqueid_locked(const char *uniqueid) +{ + return channel_find_locked(NULL, NULL, 0, NULL, NULL, uniqueid); } /*! \brief Wait, look for hangups and condition arg */ @@ -1220,8 +1228,10 @@ void ast_channel_free(struct ast_channel free(chan->tech_pvt); } - if (chan->sched) - sched_context_destroy(chan->sched); + if (chan->sched) { + sched_context_destroy(chan->sched); + chan->sched = NULL; + } ast_copy_string(name, chan->name, sizeof(name)); @@ -3106,7 +3116,7 @@ char *ast_channel_reason2str(int reason) } } -struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh) +struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, int callingpres, const char *cid_num, const char *cid_name, struct outgoing_helper *oh, char* uniqueid) { int dummy_outstate; int cause = 0; @@ -3118,7 +3128,7 @@ struct ast_channel *__ast_request_and_di else outstate = &dummy_outstate; /* make outstate always a valid pointer */ - chan = ast_request(type, format, data, &cause); + chan = ast_request_with_uniqueid(type, format, data, &cause, uniqueid); if (!chan) { ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); /* compute error and return */ @@ -3141,7 +3151,7 @@ struct ast_channel *__ast_request_and_di ast_cdr_setaccount(chan, oh->account); } ast_set_callerid(chan, cid_num, cid_name, cid_num); - + chan->cid.cid_pres = callingpres; if (!chan->cdr) { /* up till now, this insertion hasn't been done. Therefore, @@ -3230,12 +3240,12 @@ struct ast_channel *__ast_request_and_di return chan; } -struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname) +struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, int callingpres, const char *cidnum, const char *cidname, char *uniqueid) { - return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL); + return __ast_request_and_dial(type, format, data, timeout, outstate, 0, cidnum, cidname, NULL, uniqueid); } -struct ast_channel *ast_request(const char *type, int format, void *data, int *cause) +struct ast_channel *ast_request_with_uniqueid(const char *type, int format, void *data, int *cause, char *uniqueid) { struct chanlist *chan; struct ast_channel *c; @@ -3285,6 +3295,11 @@ struct ast_channel *ast_request(const ch return NULL; } +struct ast_channel *ast_request(const char *type, int format, void *data, int *cause) +{ + return ast_request_with_uniqueid(type, format, data, cause, NULL); +} + int ast_call(struct ast_channel *chan, char *addr, int timeout) { /* Place an outgoing call, but don't wait any longer than timeout ms before returning. @@ -3672,7 +3687,7 @@ int ast_do_masquerade(struct ast_channel ast_string_field_set(clone, name, masqn); /* Notify any managers of the change, first the masq then the other */ - manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clone->uniqueid); + manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\nNewUniqueid: %s\r\n", newn, masqn, clone->uniqueid, original->uniqueid); manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid); /* Swap the technologies */ --- a/apps/app_parkandannounce.c +++ b/apps/app_parkandannounce.c @@ -182,7 +182,7 @@ static int parkandannounce_exec(struct a memset(&oh, 0, sizeof(oh)); oh.parent_channel = chan; oh.vars = ast_variable_new("_PARKEDAT", buf); - dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh); + dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, 0, chan->cid.cid_num, chan->cid.cid_name, &oh, NULL); if(dchan) { if(dchan->_state == AST_STATE_UP) { --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -717,9 +717,17 @@ int ast_async_goto_by_name(const char *c int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel); /*! Synchronously or asynchronously make an outbound call and send it to a + particular extension (extended version with callinpres and uniqueid) */ +int ast_pbx_outgoing_exten2(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid); + +/*! Synchronously or asynchronously make an outbound call and send it to a particular application with given extension */ int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel); +/*! Synchronously or asynchronously make an outbound call and send it to a + particular application with given extension (extended version with callinpres and uniqueid) */ +int ast_pbx_outgoing_app2(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid); + /*! * \brief Evaluate a condition * --- a/main/pbx.c +++ b/main/pbx.c @@ -4985,7 +4985,7 @@ static int ast_pbx_outgoing_cdr_failed(v return 0; /* success */ } -int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel) +int ast_pbx_outgoing_exten2(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, char *uniqueid) { struct ast_channel *chan; struct async_stat *as; @@ -4995,7 +4995,7 @@ int ast_pbx_outgoing_exten(const char *t if (sync) { LOAD_OH(oh); - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); if (channel) { *channel = chan; if (chan) @@ -5080,7 +5080,7 @@ int ast_pbx_outgoing_exten(const char *t res = -1; goto outgoing_exten_cleanup; } - chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name); + chan = ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, uniqueid); if (channel) { *channel = chan; if (chan) @@ -5120,6 +5120,10 @@ outgoing_exten_cleanup: return res; } +int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel) +{ + return ast_pbx_outgoing_exten2(type, format, data, timeout, context, exten, priority, reason, sync, 0, cid_num, cid_name, vars, account, channel, NULL); +} struct app_tmp { char app[256]; char data[256]; @@ -5144,7 +5148,7 @@ static void *ast_pbx_run_app(void *data) return NULL; } -int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel) +int ast_pbx_outgoing_app2(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid) { struct ast_channel *chan; struct app_tmp *tmp; @@ -5163,10 +5167,10 @@ int ast_pbx_outgoing_app(const char *typ goto outgoing_app_cleanup; } if (sync) { - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); if (chan) { if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */ - ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name); + ast_log(LOG_WARNING, "%s already has a call detail record??\n", chan->name); } else { chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */ if(!chan->cdr) { @@ -5247,7 +5251,7 @@ int ast_pbx_outgoing_app(const char *typ res = -1; goto outgoing_app_cleanup; } - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); if (!chan) { free(as); res = -1; @@ -5287,6 +5291,10 @@ outgoing_app_cleanup: return res; } +int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel) +{ + return ast_pbx_outgoing_app2(type, format, data, timeout, app, appdata, reason, sync, 0, cid_num, cid_name, vars, account, locked_channel, NULL); +} void __ast_context_destroy(struct ast_context *con, const char *registrar) { struct ast_context *tmp, *tmpl=NULL; --- a/res/res_monitor.c +++ b/res/res_monitor.c @@ -337,6 +337,11 @@ int ast_monitor_stop(struct ast_channel result = ast_safe_system(tmp); if (result == -1) ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp); + manager_event(EVENT_FLAG_CALL, "MonitorStopped", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Result: %d\r\n" + ,chan->name, chan->uniqueid, result); } free(chan->monitor->format); @@ -503,18 +508,28 @@ static int start_monitor_action(struct m const char *fname = astman_get_header(m, "File"); const char *format = astman_get_header(m, "Format"); const char *mix = astman_get_header(m, "Mix"); + const char *uniqueid = astman_get_header(m, "Uniqueid"); const char *target_url = astman_get_header(m, "TargetURL"); const char *target_script = astman_get_header(m, "TargetScript"); char *d; - if (ast_strlen_zero(name)) { - astman_send_error(s, m, "No channel specified"); + if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) { + astman_send_error(s, m, "No channel/uniqueid specified"); + return 0; + } + + if (!ast_strlen_zero(uniqueid)) { + c = ast_get_channel_by_uniqueid_locked(uniqueid); + if (!c) { + astman_send_error(s, m, "No such uniqueid"); return 0; - } - c = ast_get_channel_by_name_locked(name); - if (!c) { + } + } else { + c = ast_get_channel_by_name_locked(name); + if (!c) { astman_send_error(s, m, "No such channel"); return 0; + } } if (ast_strlen_zero(fname)) { @@ -555,16 +570,30 @@ static int stop_monitor_action(struct ma { struct ast_channel *c = NULL; const char *name = astman_get_header(m, "Channel"); + const char *uniqueid = astman_get_header(m, "Uniqueid"); int res; if (ast_strlen_zero(name)) { astman_send_error(s, m, "No channel specified"); return 0; } - c = ast_get_channel_by_name_locked(name); - if (!c) { - astman_send_error(s, m, "No such channel"); + if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) { + astman_send_error(s, m, "No channel/uniqueid specified"); + return 0; + } + if (!ast_strlen_zero(uniqueid)) { + c = ast_get_channel_by_uniqueid_locked(uniqueid); + if (!c) { + astman_send_error(s, m, "No such uniqueid"); return 0; + } + } else { + c = ast_get_channel_by_name_locked(name); + if (!c) { + astman_send_error(s, m, "No such channel"); + return 0; + } } + res = ast_monitor_stop(c, 1); ast_channel_unlock(c); if (res) { --- a/apps/app_chanspy.c +++ b/apps/app_chanspy.c @@ -55,6 +55,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi static const char *tdesc = "Listen to a channel, and optionally whisper into it"; static const char *app_chan = "ChanSpy"; +static const char *app_chan2 = "ChanSpyChan"; static const char *desc_chan = " ChanSpy([chanprefix][|options]): This application is used to listen to the\n" "audio from an Asterisk channel. This includes the audio coming in and\n" @@ -85,6 +86,27 @@ static const char *desc_chan = " channel.\n" ; +static const char *desc_uniqueid = +" ChanSpyChan(uniqueid[|options]): This application is used to listen to the\n" +"audio from an Asterisk channel. This includes the audio coming in and\n" +"out of the channel being spied on. The 'uniqueid' parameter has to be specified,\n" +" While spying, the following actions may be performed:\n" +" - Dialing # cycles the volume level.\n" +" Options:\n" +" q - Don't play a beep when beginning to spy on a channel, or speak the\n" +" selected channel name.\n" +" r[(basename)] - Record the session to the monitor spool directory. An\n" +" optional base for the filename may be specified. The\n" +" default is 'chanspy'.\n" +" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n" +" negative value refers to a quieter setting.\n" +" w - Enable 'whisper' mode, so the spying channel can talk to\n" +" the spied-on channel.\n" +" W - Enable 'private whisper' mode, so the spying channel can\n" +" talk to the spied-on channel but cannot listen to that\n" +" channel.\n" +; + static const char *app_ext = "ExtenSpy"; static const char *desc_ext = " ExtenSpy(exten[@context][|options]): This application is used to listen to the\n" @@ -404,7 +426,7 @@ static int channel_spy(struct ast_channe } static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec, - const char *exten, const char *context) + const char *exten, const char *context, const char *uniqueid) { struct ast_channel *this; @@ -413,6 +435,8 @@ static struct ast_channel *next_channel( this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); else if (exten) this = ast_walk_channel_by_exten_locked(last, exten, context); + else if (uniqueid) + this = ast_get_channel_by_uniqueid_locked(uniqueid); else this = ast_channel_walk_locked(last); @@ -427,7 +451,7 @@ static struct ast_channel *next_channel( static int common_exec(struct ast_channel *chan, const struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *spec, - const char *exten, const char *context) + const char *exten, const char *context, const char *uniqueid) { struct ast_channel *peer, *prev, *next; char nameprefix[AST_NAME_STRLEN]; @@ -466,9 +490,9 @@ static int common_exec(struct ast_channe waitms = 100; peer = prev = next = NULL; - for (peer = next_channel(peer, spec, exten, context); + for (peer = next_channel(peer, spec, exten, context, NULL); peer; - prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) { + prev = peer, peer = next ? next : next_channel(peer, spec, exten, context, NULL), next = NULL) { const char *group; int igrp = !mygroup; char *groups[25]; @@ -625,7 +649,7 @@ static int chanspy_exec(struct ast_chann } } - res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL); + res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL, NULL); if (fd) close(fd); @@ -710,7 +734,92 @@ static int extenspy_exec(struct ast_chan } } - res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context); + res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context, NULL); + + if (fd) + close(fd); + + if (oldwf && ast_set_write_format(chan, oldwf) < 0) + ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + + ast_module_user_remove(u); + + return res; +} + +static int chanspychan_exec(struct ast_channel *chan, void *data) +{ + struct ast_module_user *u; + char *options = NULL; + char *uniqueid = NULL; + char *argv[2]; + char *mygroup = NULL; + char *recbase = NULL; + int fd = 0; + struct ast_flags flags; + int oldwf = 0; + int argc = 0; + int volfactor = 0; + int res; + + data = ast_strdupa(data); + + u = ast_module_user_add(chan); + + if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) { + uniqueid = argv[0]; + if (argc > 1) + options = argv[1]; + + if (ast_strlen_zero(uniqueid)) { + ast_log(LOG_ERROR, "no uniqueid specified.\n"); + ast_module_user_remove(u); + return -1; + } + } + + if (options) { + char *opts[OPT_ARG_ARRAY_SIZE]; + + ast_app_parse_options(spy_opts, &flags, opts, options); + if (ast_test_flag(&flags, OPTION_GROUP)) + mygroup = opts[OPT_ARG_GROUP]; + + if (ast_test_flag(&flags, OPTION_RECORD) && + !(recbase = opts[OPT_ARG_RECORD])) + recbase = "chanspy"; + + if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { + int vol; + + if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) + ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); + else + volfactor = vol; + } + + if (ast_test_flag(&flags, OPTION_PRIVATE)) + ast_set_flag(&flags, OPTION_WHISPER); + } + + oldwf = chan->writeformat; + if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { + ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + ast_module_user_remove(u); + return -1; + } + + if (recbase) { + char filename[512]; + + snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); + if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) { + ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); + fd = 0; + } + } + + res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, NULL, uniqueid); if (fd) close(fd); @@ -723,14 +832,15 @@ static int extenspy_exec(struct ast_chan return res; } + static int unload_module(void) { int res = 0; res |= ast_unregister_application(app_chan); + res |= ast_unregister_application(app_chan2); res |= ast_unregister_application(app_ext); - ast_module_user_hangup_all(); return res; } @@ -741,6 +851,7 @@ static int load_module(void) res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan); res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext); + res |= ast_register_application(app_chan2, chanspychan_exec, tdesc, desc_uniqueid); return res; } --- a/main/manager.c +++ b/main/manager.c @@ -87,6 +87,8 @@ struct fast_originate_helper { char idtext[AST_MAX_EXTENSION]; char account[AST_MAX_ACCOUNT_CODE]; int priority; + int callingpres; + char uniqueid[64]; struct ast_variable *vars; }; @@ -1415,11 +1417,20 @@ static int action_hangup(struct mansessi { struct ast_channel *c = NULL; const char *name = astman_get_header(m, "Channel"); - if (ast_strlen_zero(name)) { - astman_send_error(s, m, "No channel specified"); + const char *uniqueid = astman_get_header(m, "Uniqueid"); + + if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) { + astman_send_error(s, m, "No channel or uniqueid specified"); return 0; } - c = ast_get_channel_by_name_locked(name); + + if (!ast_strlen_zero(uniqueid)) { + c = ast_get_channel_by_uniqueid_locked(uniqueid); + } else { + if (!ast_strlen_zero(name)) + c = ast_get_channel_by_name_locked(name); + } + if (!c) { astman_send_error(s, m, "No such channel"); return 0; @@ -1670,12 +1681,18 @@ static int action_redirect(struct manses const char *exten = astman_get_header(m, "Exten"); const char *context = astman_get_header(m, "Context"); const char *priority = astman_get_header(m, "Priority"); + const char *uniqueid = astman_get_header(m, "Uniqueid"); + const char *uniqueid2 = astman_get_header(m, "ExtraUniqueid"); + const char *exten2 = astman_get_header(m, "ExtraExten"); + const char *context2 = astman_get_header(m, "ExtraContext"); + const char *priority2 = astman_get_header(m, "ExtraPriority"); struct ast_channel *chan, *chan2 = NULL; int pi = 0; + int pi2 = 0; int res; - if (ast_strlen_zero(name)) { - astman_send_error(s, m, "Channel not specified"); + if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) { + astman_send_error(s, m, "Channel or Uniqueid not specified"); return 0; } if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) { @@ -1685,7 +1702,11 @@ static int action_redirect(struct manses } } /* XXX watch out, possible deadlock!!! */ - chan = ast_get_channel_by_name_locked(name); + if (!ast_strlen_zero(uniqueid)) { + chan = ast_get_channel_by_uniqueid_locked(uniqueid); + } else { + chan = ast_get_channel_by_name_locked(name); + } if (!chan) { char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "Channel does not exist: %s", name); @@ -1707,9 +1728,9 @@ static int action_redirect(struct manses } res = ast_async_goto(chan, context, exten, pi); if (!res) { - if (!ast_strlen_zero(name2)) { + if ((!ast_strlen_zero(name2)) || (!ast_strlen_zero(uniqueid2))){ if (chan2) - res = ast_async_goto(chan2, context, exten, pi); + res = ast_async_goto(chan2, context2, exten2, pi2); else res = -1; if (!res) @@ -1788,15 +1809,15 @@ static void *fast_originate(void *data) char requested_channel[AST_CHANNEL_NAME]; if (!ast_strlen_zero(in->app)) { - res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, + res = ast_pbx_outgoing_app2(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, in->callingpres, S_OR(in->cid_num, NULL), S_OR(in->cid_name, NULL), - in->vars, in->account, &chan); + in->vars, in->account, &chan, in->uniqueid); } else { - res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, + res = ast_pbx_outgoing_exten2(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, in->callingpres, S_OR(in->cid_num, NULL), S_OR(in->cid_name, NULL), - in->vars, in->account, &chan); + in->vars, in->account, &chan, in->uniqueid); } if (!chan) @@ -1856,6 +1877,7 @@ static int action_originate(struct manse const char *appdata = astman_get_header(m, "Data"); const char *async = astman_get_header(m, "Async"); const char *id = astman_get_header(m, "ActionID"); + const char *callingpres = astman_get_header(m, "CallingPres"); struct ast_variable *vars = astman_get_variables(m); char *tech, *data; char *l = NULL, *n = NULL; @@ -1865,6 +1887,9 @@ static int action_originate(struct manse int reason = 0; char tmp[256]; char tmp2[256]; + char *uniqueid; + int cpresi = 0; + char idText[256] = ""; pthread_t th; pthread_attr_t attr; @@ -1882,6 +1907,10 @@ static int action_originate(struct manse astman_send_error(s, m, "Invalid timeout\n"); return 0; } + if (!ast_strlen_zero(callingpres) && (sscanf(callingpres, "%d", &cpresi) != 1)) { + astman_send_error(s, m, "Invalid CallingPres\n"); + return 0; + } ast_copy_string(tmp, name, sizeof(tmp)); tech = tmp; data = strchr(tmp, '/'); @@ -1901,6 +1930,7 @@ static int action_originate(struct manse if (ast_strlen_zero(l)) l = NULL; } + uniqueid = ast_alloc_uniqueid(); if (ast_true(async)) { struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast)); if (!fast) { @@ -1920,8 +1950,10 @@ static int action_originate(struct manse ast_copy_string(fast->context, context, sizeof(fast->context)); ast_copy_string(fast->exten, exten, sizeof(fast->exten)); ast_copy_string(fast->account, account, sizeof(fast->account)); + ast_copy_string(fast->uniqueid, uniqueid, sizeof(fast->uniqueid)); fast->timeout = to; fast->priority = pi; + fast->callingpres = cpresi; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ast_pthread_create(&th, &attr, fast_originate, fast)) { @@ -1932,19 +1964,28 @@ static int action_originate(struct manse pthread_attr_destroy(&attr); } } else if (!ast_strlen_zero(app)) { - res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL); + res = ast_pbx_outgoing_app2(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, cpresi, l, n, vars, account, NULL, uniqueid); } else { if (exten && context && pi) - res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL); + res = ast_pbx_outgoing_exten2(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, cpresi, l, n, vars, account, NULL, uniqueid); else { astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'"); return 0; } } - if (!res) - astman_send_ack(s, m, "Originate successfully queued"); - else + if (!res) { + if (id && !ast_strlen_zero(id)) { + snprintf(idText,256,"ActionID: %s\r\n",id); + } + ast_cli(s->fd, "Response: Success\r\n" + "%s" + "Message: Originate successfully queued\r\n" + "Uniqueid: %s\r\n" + "\r\n", + idText, uniqueid); + } else { astman_send_error(s, m, "Originate failed"); + } return 0; } --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -89,6 +89,9 @@ #include "asterisk/abstract_jb.h" +/* Max length of the uniqueid */ +#define AST_MAX_UNIQUEID 64 + #include #ifdef POLLCOMPAT #include "asterisk/poll-compat.h" @@ -1004,6 +1007,8 @@ int ast_waitfordigit_full(struct ast_cha 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); +char *ast_alloc_uniqueid(void); + /*! \brief Report DTMF on channel 0 */ #define AST_BRIDGE_DTMF_CHANNEL_0 (1 << 0) /*! \brief Report DTMF on channel 1 */ --- a/main/channel.c +++ b/main/channel.c @@ -724,6 +724,15 @@ static const struct ast_channel_tech nul .description = "Null channel (should not see this)", }; +/*! \brief Create a uniqueid */ +char *ast_alloc_uniqueid(void) { + char *uniqueid; + uniqueid = malloc(64); + if (!uniqueid) return NULL; + snprintf(uniqueid, 63, "%s-%d-%li.%d", ast_config_AST_SYSTEM_NAME, ast_mainpid, (long)time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1)); + return uniqueid; +} + /*! \brief Create a new channel structure */ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...) { --- a/include/asterisk/features.h +++ b/include/asterisk/features.h @@ -47,6 +47,8 @@ struct ast_call_feature { }; +extern int ast_autoanswer_login(struct ast_channel *chan, void *data); +extern int ast_masq_autoanswer_login(struct ast_channel *rchan, void *data); /*! \brief Park a call and read back parked location * \param chan the channel to actually be parked --- a/res/res_features.c +++ b/res/res_features.c @@ -11,6 +11,10 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -128,6 +132,20 @@ static char *descrip2 = "Park():" "it already exists. In that case, execution will continue at next\n" "priority.\n" ; +static char *autoanswerlogin = "AutoanswerLogin"; + +static char *synopsis3 = "Log in for autoanswer"; + +static char *descrip3 = "AutoanswerLogin([context]|exten):" +"Used to login to the autoanswer application for an extension.\n"; + +static char *autoanswer = "Autoanswer"; + +static char *synopsis4 = "Autoanswer a call"; + +static char *descrip4 = "Autoanswer([context]|exten):" +"Used to autoanswer a call for an extension.\n"; + static struct ast_app *monitor_app = NULL; static int monitor_ok = 1; @@ -146,6 +164,23 @@ struct parkeduser { struct parkeduser *next; }; +/* auto answer user */ +struct aauser { + struct ast_channel *chan; + struct timeval start; + /* waiting on this extension/context */ + char exten[AST_MAX_EXTENSION]; + char context[AST_MAX_EXTENSION]; + int priority; + int notquiteyet; + struct aauser *next; +}; + + +static struct aauser *aalot; +AST_MUTEX_DEFINE_STATIC(autoanswer_lock); +static pthread_t autoanswer_thread; + static struct parkeduser *parkinglot; AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */ @@ -401,11 +436,13 @@ static int park_call_full(struct ast_cha "From: %s\r\n" "Timeout: %ld\r\n" "CallerID: %s\r\n" - "CallerIDName: %s\r\n", + "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n", pu->parkingexten, pu->chan->name, peer ? peer->name : "", (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL), S_OR(pu->chan->cid.cid_num, ""), - S_OR(pu->chan->cid.cid_name, "") + S_OR(pu->chan->cid.cid_name, ""), + pu->chan->uniqueid ); if (peer && adsipark && ast_adsi_available(peer)) { @@ -1628,11 +1665,13 @@ static void post_manager_event(const cha "Exten: %s\r\n" "Channel: %s\r\n" "CallerID: %s\r\n" - "CallerIDName: %s\r\n\r\n", + "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n\r\n", parkingexten, chan->name, S_OR(chan->cid.cid_num, ""), - S_OR(chan->cid.cid_name, "") + S_OR(chan->cid.cid_name, ""), + chan->uniqueid ); } @@ -1887,10 +1926,12 @@ static int park_exec(struct ast_channel "Channel: %s\r\n" "From: %s\r\n" "CallerID: %s\r\n" - "CallerIDName: %s\r\n", + "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n", pu->parkingexten, pu->chan->name, chan->name, S_OR(pu->chan->cid.cid_num, ""), - S_OR(pu->chan->cid.cid_name, "") + S_OR(pu->chan->cid.cid_name, ""), + pu->chan->uniqueid ); free(pu); @@ -2044,15 +2085,10 @@ static struct ast_cli_entry cli_show_fea handle_showfeatures, NULL, NULL }; -static struct ast_cli_entry cli_features[] = { - { { "feature", "show", NULL }, - handle_showfeatures, "Lists configured features", - showfeatures_help, NULL, &cli_show_features_deprecated }, +static char showautoanswer_help[] = +"Usage: show autoanswer\n" +" Lists currently logged in autoanswer users.\n"; - { { "show", "parkedcalls", NULL }, - handle_parkedcalls, "Lists parked calls", - showparked_help }, -}; /*! \brief Dump lot status */ static int manager_parking_status( struct mansession *s, const struct message *m) @@ -2076,12 +2112,13 @@ static int manager_parking_status( struc "Timeout: %ld\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" + "Unqiueid: %s\r\n\r\n" "%s" "\r\n", cur->parkingnum, cur->chan->name, cur->peername, (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL), S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is */ - S_OR(cur->chan->cid.cid_name, ""), + S_OR(cur->chan->cid.cid_name, ""), cur->chan->uniqueid, idText); } @@ -2156,6 +2193,427 @@ static int manager_park(struct mansessio return 0; } +static int handle_autoanswer(int fd, int argc, char *argv[]) +{ + struct aauser *cur; + + ast_cli(fd, "%25s %10s %15s \n", "Channel" + , "Extension", "Context"); + + ast_mutex_lock(&autoanswer_lock); + + cur=aalot; + while(cur) { + ast_cli(fd, "%25s %10s %15s\n",cur->chan->name, cur->exten, cur->context); + + cur = cur->next; + } + + ast_mutex_unlock(&autoanswer_lock); + + return RESULT_SUCCESS; +} + +static struct ast_cli_entry cli_features[] = { + { { "feature", "list", NULL }, + handle_showfeatures, "Lists configured features", + showfeatures_help, NULL, &cli_show_features_deprecated }, + + { { "show", "parkedcalls", NULL }, + handle_parkedcalls, "Lists parked calls", + showparked_help }, + + { { "show", "autoanswer", NULL }, + handle_autoanswer, "Lists autoanswer users", + showautoanswer_help }, +}; +int ast_masq_autoanswer_login(struct ast_channel *rchan, void *data) +{ + struct ast_channel *chan; + struct ast_frame *f; + /* Make a new, fake channel that we'll use to masquerade in the real one */ + chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Autoanswer/%s", rchan->name); + if (chan) { + /* Let us keep track of the channel name */ + ast_string_field_build(chan, name, "Autoanswer/%s",rchan->name); + /* Make formats okay */ + chan->readformat = rchan->readformat; + chan->writeformat = rchan->writeformat; + ast_channel_masquerade(chan, rchan); + /* Setup the extensions and such */ + strncpy(chan->context, rchan->context, sizeof(chan->context) - 1); + strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1); + chan->priority = rchan->priority; + /* might be dirty but we want trackable channels */ + ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid); + /* Make the masq execute */ + f = ast_read(chan); + if (f) + ast_frfree(f); + ast_autoanswer_login(chan, data); + } else { + ast_log(LOG_WARNING, "Unable to create aa channel\n"); + return -1; + } + return 0; +} + +static int autoanswer_login_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct ast_module_user *u; + + u = ast_module_user_add(chan); + if (!data) { + ast_log(LOG_WARNING, "AutoanswerLogin requires an argument (extension number)\n"); + return -1; + } + res = ast_masq_autoanswer_login(chan, data); + ast_module_user_remove(u); + return res; +} + +int ast_autoanswer_login(struct ast_channel *chan, void *data) +{ + /* We put the user in the parking list, then wake up the parking thread to be sure it looks + after these channels too */ + struct ast_context *con; + char exten[AST_MAX_EXTENSION]; + struct aauser *pu,*pl = NULL; + char *s, *stringp, *aacontext, *aaexten = NULL; + + s = ast_strdupa((void *) data); + stringp=s; + aacontext = strsep(&stringp, "|"); + aaexten = strsep(&stringp, "|"); + if (!aaexten) { + aaexten = aacontext; + aacontext = NULL; + } + if (!aaexten) { + ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n"); + return -1; + } else { + if (!aacontext) { + aacontext = "default"; + } + } + + ast_mutex_lock(&autoanswer_lock); + pu = aalot; + while(pu) { + if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){ + if (pl) + pl->next = pu->next; + else + aalot = pu->next; + break; + } + pl = pu; + pu = pu->next; + } + ast_mutex_unlock(&autoanswer_lock); + if (pu) { + ast_log(LOG_NOTICE, "Logout old Channel %s for %s@%s.\n",pu->chan->name, pu->exten, pu->context); + manager_event(EVENT_FLAG_CALL, "AutoanswerLogout", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); + ast_hangup(pu->chan); + free(pu); + } + pu = malloc(sizeof(struct aauser)); + if (pu) { + memset(pu, 0, sizeof(pu)); + ast_mutex_lock(&autoanswer_lock); + chan->appl = "Autoanswer"; + chan->data = NULL; + + pu->chan = chan; + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + /* Start music on hold */ + ast_moh_start(pu->chan, NULL, NULL); + gettimeofday(&pu->start, NULL); + strncpy(pu->exten, aaexten, sizeof(pu->exten)-1); + strncpy(pu->context, aacontext, sizeof(pu->exten)-1); + pu->next = aalot; + aalot = pu; + con = ast_context_find(aacontext); + if (!con) { + con = ast_context_create(NULL,aacontext, registrar); + if (!con) { + ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", aacontext); + } + } + if (con) { + snprintf(exten, sizeof(exten), "%s", aaexten); + ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)data), free, registrar); + } + + ast_mutex_unlock(&autoanswer_lock); + /* Wake up the (presumably select()ing) thread */ + pthread_kill(autoanswer_thread, SIGURG); + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Autoanswer login from %s for %s@%s.\n", pu->chan->name, pu->exten, pu->context); + manager_event(EVENT_FLAG_CALL, "AutoanswerLogin", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); + + return 0; + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + return 0; +} + +static void autoanswer_reregister_extensions(void) +{ + struct aauser *cur; + struct ast_context *con; + char exten[AST_MAX_EXTENSION]; + char args[AST_MAX_EXTENSION]; + + ast_mutex_lock(&autoanswer_lock); + + cur=aalot; + while(cur) { + con = ast_context_find(cur->context); + if (!con) { + con = ast_context_create(NULL,cur->context, registrar); + if (!con) { + ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", cur->context); + } + } + if (con) { + snprintf(exten, sizeof(exten), "%s", cur->exten); + snprintf(args, sizeof(args), "%s|%s", cur->context, cur->exten); + ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)args), free, registrar); + } + cur = cur->next; + } + + ast_mutex_unlock(&autoanswer_lock); +} +static void *do_autoanswer_thread(void *ignore) +{ + int ms, tms, max; + struct ast_context *con; + char exten[AST_MAX_EXTENSION]; + struct aauser *pu, *pl, *pt = NULL; + struct timeval tv; + struct ast_frame *f; + int x; + fd_set rfds, efds; + fd_set nrfds, nefds; + FD_ZERO(&rfds); + FD_ZERO(&efds); + for (;;) { + ms = -1; + max = -1; + ast_mutex_lock(&autoanswer_lock); + pl = NULL; + pu = aalot; + gettimeofday(&tv, NULL); + FD_ZERO(&nrfds); + FD_ZERO(&nefds); + while(pu) { + tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000; + for (x=0;xchan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { + if (FD_ISSET(pu->chan->fds[x], &efds)) + ast_set_flag(pu->chan, AST_FLAG_EXCEPTION); + else + ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION); + pu->chan->fdno = x; + /* See if they need servicing */ + f = ast_read(pu->chan); + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { + /* There's a problem, hang them up*/ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s logged out of autoanswer app\n", pu->chan->name); + manager_event(EVENT_FLAG_CALL, "AutoanswerLogout", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); + ast_hangup(pu->chan); + con = ast_context_find(pu->context); + if (con) { + snprintf(exten, sizeof(exten), "%s", pu->exten); + if (ast_context_remove_extension2(con, exten, 1, registrar)) + ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); + } else { + ast_log(LOG_WARNING, "Whoa, no %s context?\n", pu->exten); + } + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + aalot = pu->next; + pt = pu; + pu = pu->next; + free(pt); + break; + } else { + /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + ast_frfree(f); + goto std; /* XXX Ick: jumping into an else statement??? XXX */ + } + } + } + if (x >= AST_MAX_FDS) { +std: for (x=0;xchan->fds[x] > -1) { + FD_SET(pu->chan->fds[x], &nrfds); + FD_SET(pu->chan->fds[x], &nefds); + if (pu->chan->fds[x] > max) + max = pu->chan->fds[x]; + } + } + /* Keep track of our longest wait */ + if ((tms < ms) || (ms < 0)) + ms = tms; + pl = pu; + pu = pu->next; + } + } + ast_mutex_unlock(&autoanswer_lock); + rfds = nrfds; + efds = nefds; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + /* Wait for something to happen */ + ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); + pthread_testcancel(); + } + return NULL; /* Never reached */ +} + +static int autoanswer_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct ast_channel *peer=NULL; + struct aauser *pu, *pl=NULL; + struct ast_bridge_config config; + char *s, *stringp, *aacontext, *aaexten = NULL; + char datastring[80]; + struct ast_module_user *u; + + + if (!data) { + ast_log(LOG_WARNING, "Autoanswer requires an argument (extension number)\n"); + return -1; + } + s = ast_strdupa((void *) data); + stringp=s; + aacontext = strsep(&stringp, "|"); + aaexten = strsep(&stringp, "|"); + if (!aaexten) { + aaexten = aacontext; + aacontext = NULL; + } + if (!aaexten) { + ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n"); + return -1; + } else { + if (!aacontext) { + aacontext = "default"; + } + } + + u = ast_module_user_add(chan); + ast_mutex_lock(&autoanswer_lock); + pu = aalot; + while(pu) { + if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){ + if (pl) + pl->next = pu->next; + else + aalot = pu->next; + break; + } + pl = pu; + pu = pu->next; + } + ast_mutex_unlock(&autoanswer_lock); + if (pu) { + peer = pu->chan; + free(pu); + pu = NULL; + } + /* JK02: it helps to answer the channel if not already up */ + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + if (peer) { + ast_moh_stop(peer); + /* Play a courtesy beep in the callED channel to prefix the bridge connecting */ + if (!ast_strlen_zero(courtesytone)) { + if (!ast_streamfile(peer, courtesytone, peer->language)) { + if (ast_waitstream(peer, "") < 0) { + ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); + ast_hangup(peer); + return -1; + } + } + } + + res = ast_channel_make_compatible(chan, peer); + if (res < 0) { + ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); + ast_hangup(peer); + return -1; + } + /* This runs sorta backwards, since we give the incoming channel control, as if it + were the person called. */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s autoanswered %s\n", peer->name, chan->name); + manager_event(EVENT_FLAG_CALL, "Autoanswer", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Channel2: %s\r\n" + "Uniqueid2: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,chan->name, chan->uniqueid, peer->name, peer->uniqueid, aacontext, aaexten); + + + memset(&config,0,sizeof(struct ast_bridge_config)); + ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); + ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); + config.timelimit = 0; + config.play_warning = 0; + config.warning_freq = 0; + config.warning_sound=NULL; + res = ast_bridge_call(chan,peer,&config); + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "returning from bridge %s\n", peer->name); + /* relogin */ + snprintf(datastring, sizeof(datastring) - 1, "%s|%s", aacontext, aaexten); + ast_autoanswer_login(peer, datastring); + return res; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Nobody logged in for autoanswer %s@%s\n", aaexten, aacontext); + res = -1; + } + ast_module_user_remove(u); + return res; +} + int ast_pickup_call(struct ast_channel *chan) { @@ -2419,6 +2877,7 @@ static int load_config(void) static int reload(void) { + autoanswer_reregister_extensions(); return load_config(); } @@ -2442,6 +2901,12 @@ static int load_module(void) "Park a channel", mandescr_park); } + ast_pthread_create(&autoanswer_thread, NULL, do_autoanswer_thread, NULL); + if (!res) + res |= ast_register_application(autoanswerlogin, autoanswer_login_exec, synopsis3, descrip3); + if (!res) + res |= ast_register_application(autoanswer, autoanswer_exec, synopsis4, descrip4); + res |= ast_devstate_prov_add("Park", metermaidstate); return res; @@ -2456,6 +2921,8 @@ static int unload_module(void) ast_manager_unregister("Park"); ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); ast_unregister_application(parkcall); + ast_unregister_application(autoanswer); + ast_unregister_application(autoanswerlogin); ast_devstate_prov_del("Park"); return ast_unregister_application(parkedcall); } --- a/include/asterisk/features.h +++ b/include/asterisk/features.h @@ -72,6 +72,12 @@ int ast_park_call(struct ast_channel *ch */ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout); +extern int ast_hold_call(struct ast_channel *chan, struct ast_channel *host); +extern int ast_masq_hold_call(struct ast_channel *rchan, struct ast_channel *host); +extern int ast_retrieve_call(struct ast_channel *chan, char *uniqueid); +extern int ast_retrieve_call_to_death(char *uniqueid); +extern struct ast_channel *ast_get_holded_call(char *uniqueid); + /*! \brief Determine system parking extension * Returns the call parking extension for drivers that provide special call parking help */ --- a/res/res_features.c +++ b/res/res_features.c @@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #include "asterisk/adsi.h" #include "asterisk/devicestate.h" #include "asterisk/monitor.h" +#include "asterisk/indications.h" #define DEFAULT_PARK_TIME 45000 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 @@ -80,6 +81,7 @@ enum { }; static char *parkedcall = "ParkedCall"; +static char *holdedcall = "HoldedCall"; static int parkaddhints = 0; /*!< Add parking hints automatically */ static int parkingtime = DEFAULT_PARK_TIME; /*!< No more than 45 seconds parked before you do something with them */ @@ -164,6 +166,22 @@ struct parkeduser { struct parkeduser *next; }; +struct holdeduser { + struct ast_channel *chan; + struct timeval start; + int parkingnum; + int cref; + int tei; + /* Where to go if our parking time expires */ + char context[AST_MAX_EXTENSION]; + char exten[AST_MAX_EXTENSION]; + int priority; + int parkingtime; + char uniqueid[AST_MAX_UNIQUEID]; + char uniqueidpeer[AST_MAX_UNIQUEID]; + struct holdeduser *next; +}; + /* auto answer user */ struct aauser { struct ast_channel *chan; @@ -183,10 +201,16 @@ static pthread_t autoanswer_thread; static struct parkeduser *parkinglot; +static struct holdeduser *holdlist; + AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */ +AST_MUTEX_DEFINE_STATIC(holding_lock); + static pthread_t parking_thread; +static pthread_t holding_thread; + char *ast_parking_ext(void) { return parking_ext; @@ -2011,6 +2035,282 @@ static int park_exec(struct ast_channel return res; } +int ast_hold_call(struct ast_channel *chan, struct ast_channel *peer) +{ + /* We put the user in the parking list, then wake up the parking thread to be sure it looks + after these channels too */ + struct holdeduser *pu; + pu = malloc(sizeof(struct holdeduser)); + if (pu) { + memset(pu, 0, sizeof(pu)); + ast_mutex_lock(&holding_lock); + chan->appl = "Holded Call"; + chan->data = NULL; + + pu->chan = chan; + strncpy(pu->uniqueid, chan->uniqueid, sizeof(pu->uniqueid)); + strncpy(pu->uniqueidpeer, peer->uniqueid, sizeof(pu->uniqueidpeer)); + /* Start music on hold */ + ast_moh_start(pu->chan, NULL, NULL); + gettimeofday(&pu->start, NULL); + pu->next = holdlist; + holdlist = pu; + ast_mutex_unlock(&holding_lock); + /* Wake up the (presumably select()ing) thread */ + pthread_kill(holding_thread, SIGURG); + + manager_event(EVENT_FLAG_CALL, "HoldedCall", + "Channel1: %s\r\n" + "Channel2: %s\r\n" + "Uniqueid1: %s\r\n" + "Uniqueid2: %s\r\n" + ,pu->chan->name, peer->name, pu->chan->uniqueid, peer->uniqueid); + + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + return 0; +} + +int ast_masq_hold_call(struct ast_channel *rchan, struct ast_channel *peer) +{ + struct ast_channel *chan; + struct ast_frame *f; + /* Make a new, fake channel that we'll use to masquerade in the real one */ + chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Onhold/%s",rchan->name); + if (chan) { + /* Let us keep track of the channel name */ + ast_string_field_build(chan, name, "Onhold/%s",rchan->name); + /* Make formats okay */ + chan->readformat = rchan->readformat; + chan->writeformat = rchan->writeformat; + ast_channel_masquerade(chan, rchan); + /* Setup the extensions and such */ + strncpy(chan->context, rchan->context, sizeof(chan->context) - 1); + strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1); + chan->priority = rchan->priority; + /* this might be dirty, but we need to preserve the uniqueid */ + ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid); + /* Make the masq execute */ + f = ast_read(chan); + if (f) + ast_frfree(f); + ast_hold_call(chan, peer); + return -1; + } else { + ast_log(LOG_WARNING, "Unable to create holded channel\n"); + return -1; + } + return 0; +} + +int ast_retrieve_call(struct ast_channel *chan, char *uniqueid) +{ + int res=-1, dres=-1; + struct ast_channel *peer=NULL; + struct ast_bridge_config config; + + peer = ast_get_holded_call(uniqueid); + + /* JK02: it helps to answer the channel if not already up */ + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + if (peer) { + ast_mutex_unlock(&peer->lock); + ast_moh_stop(peer); + res = ast_channel_make_compatible(chan, peer); + if (res < 0) { + ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); + ast_hangup(peer); + return -1; + } + /* This runs sorta backwards, since we give the incoming channel control, as if it + were the person called. */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to holded call %s\n", chan->name, peer->name); + + memset(&config,0,sizeof(struct ast_bridge_config)); + ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); + ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); + config.timelimit = 0; + config.play_warning = 0; + config.warning_freq = 0; + config.warning_sound=NULL; + res = ast_bridge_call(chan,peer,&config); + + /* Simulate the PBX hanging up */ + if (res != AST_PBX_NO_HANGUP_PEER) + ast_hangup(peer); + return res; + } else { + /* XXX Play a message XXX */ + dres = ast_streamfile(chan, "pbx-invalidpark", chan->language); + if (!dres) + dres = ast_waitstream(chan, ""); + else { + ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name); + dres = 0; + } + } + return res; +} + +int ast_retrieve_call_to_death(char *uniqueid) +{ + int res=-1; + struct ast_channel *peer=NULL; + + peer = ast_get_holded_call(uniqueid); + + if (peer) { + res=0; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name); + ast_mutex_unlock(&peer->lock); + ast_hangup(peer); + } else { + ast_log(LOG_WARNING, "Could not find channel with uniqueid %s to retrieve.\n", uniqueid); + } + return res; +} + +struct ast_channel *ast_get_holded_call(char *uniqueid) +{ + int res=-1; + struct ast_channel *peer=NULL; + struct holdeduser *pu, *pl=NULL; + + ast_mutex_lock(&holding_lock); + pu = holdlist; + while(pu) { + if (!strncmp(uniqueid,pu->uniqueid,sizeof(pu->uniqueid))) { + if (pl) + pl->next = pu->next; + else + holdlist = pu->next; + break; + } + pl = pu; + pu = pu->next; + } + ast_mutex_unlock(&holding_lock); + if (pu) { + peer = ast_get_channel_by_uniqueid_locked(pu->uniqueid); + free(pu); + if (peer) { + res=0; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name); + ast_moh_stop(peer); + return peer; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Could not find channel with uniqueid %s.\n", uniqueid); + return NULL; + } + } else { + ast_log(LOG_WARNING, "Could not find held channel with uniqueid %s to retrieve.\n", uniqueid); + } + return NULL; +} + +/* this is our autmagically service thread that keeps channels onhold happy */ +static void *do_holding_thread(void *ignore) +{ + int ms, tms, max; + struct holdeduser *pu, *pl, *pt = NULL; + struct timeval tv; + struct ast_frame *f; + int x; + fd_set rfds, efds; + fd_set nrfds, nefds; + FD_ZERO(&rfds); + FD_ZERO(&efds); + for (;;) { + ms = -1; + max = -1; + ast_mutex_lock(&holding_lock); + pl = NULL; + pu = holdlist; + gettimeofday(&tv, NULL); + FD_ZERO(&nrfds); + FD_ZERO(&nefds); + while(pu) { + tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000; + for (x=0;xchan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { + if (FD_ISSET(pu->chan->fds[x], &efds)) + ast_set_flag(pu->chan, AST_FLAG_EXCEPTION); + else + ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION); + pu->chan->fdno = x; + /* See if they need servicing */ + f = ast_read(pu->chan); + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { + /* There's a problem, hang them up*/ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being onhold\n", pu->chan->name); + ast_hangup(pu->chan); + /* find the corresponding channel and hang them up too! */ + /* but only if it is not bridged yet! */ + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + holdlist = pu->next; + pt = pu; + pu = pu->next; + free(pt); + break; + } else { + /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + ast_frfree(f); + goto std; /* XXX Ick: jumping into an else statement??? XXX */ + } + } + } + if (x >= AST_MAX_FDS) { +std: for (x=0;xchan->fds[x] > -1) { + FD_SET(pu->chan->fds[x], &nrfds); + FD_SET(pu->chan->fds[x], &nefds); + if (pu->chan->fds[x] > max) + max = pu->chan->fds[x]; + } + } + /* Keep track of our longest wait */ + if ((tms < ms) || (ms < 0)) + ms = tms; + pl = pu; + pu = pu->next; + } + } + ast_mutex_unlock(&holding_lock); + rfds = nrfds; + efds = nefds; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + /* Wait for something to happen */ + ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); + pthread_testcancel(); + } + return NULL; /* Never reached */ +} + +static int retrieve_call_exec(struct ast_channel *chan, void *data) { + int res=0; + struct ast_module_user *u; + char *uniqueid = (char *)data; + u = ast_module_user_add(chan); + res = ast_retrieve_call(chan, uniqueid); + ast_module_user_remove(u); + return res; +} + static int handle_showfeatures(int fd, int argc, char *argv[]) { int i; @@ -2892,6 +3192,7 @@ static int load_module(void) return res; ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL); + ast_pthread_create(&holding_thread, NULL, do_holding_thread, NULL); res = ast_register_application(parkedcall, park_exec, synopsis, descrip); if (!res) res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2); @@ -2901,6 +3202,7 @@ static int load_module(void) "Park a channel", mandescr_park); } + res |= ast_register_application(holdedcall, retrieve_call_exec, synopsis, descrip); ast_pthread_create(&autoanswer_thread, NULL, do_autoanswer_thread, NULL); if (!res) res |= ast_register_application(autoanswerlogin, autoanswer_login_exec, synopsis3, descrip3); @@ -2923,6 +3225,7 @@ static int unload_module(void) ast_unregister_application(parkcall); ast_unregister_application(autoanswer); ast_unregister_application(autoanswerlogin); + ast_unregister_application(holdedcall); ast_devstate_prov_del("Park"); return ast_unregister_application(parkedcall); } --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -423,6 +423,7 @@ struct ast_channel { unsigned int flags; /*!< channel flags of AST_FLAG_ type */ unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */ AST_LIST_HEAD_NOLOCK(, ast_frame) readq; + char lowlayercompat[16]; /*!< ISDN Low Layer Compatibility */ int alertpipe[2]; int nativeformats; /*!< Kinds of data this channel can natively handle */ --- a/main/pbx.c +++ b/main/pbx.c @@ -5132,7 +5132,7 @@ struct app_tmp { }; /*! \brief run the application and free the descriptor once done */ -static void *ast_pbx_run_app(void *data) +void *ast_pbx_run_app(void *data) { struct app_tmp *tmp = data; struct ast_app *app; --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -145,6 +145,8 @@ void ast_unregister_switch(struct ast_sw */ struct ast_app *pbx_findapp(const char *app); +void *ast_pbx_run_app(void *data); + /*! * \brief Execute an application * --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -11,6 +11,10 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2003-2006 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -192,7 +196,7 @@ static const char config[] = "zapata.con #define SIG_GR303FXOKS (0x0100000 | ZT_SIG_FXOKS) #define SIG_GR303FXSKS (0x0100000 | ZT_SIG_FXSKS) -#define NUM_SPANS 32 +#define NUM_SPANS 128 /*!<"32 spans", muahahaha, us alaws like to have some more... */ #define NUM_DCHANS 4 /*!< No more than 4 d-channels */ #define MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */ @@ -218,8 +222,6 @@ static struct ast_channel inuse; #ifdef PRI_GETSET_TIMERS static int pritimers[PRI_MAX_TIMERS]; #endif -static int pridebugfd = -1; -static char pridebugfilename[1024] = ""; #endif /*! \brief Wait up to 16 seconds for first digit (FXO logic) */ @@ -237,10 +239,6 @@ AST_MUTEX_DEFINE_STATIC(iflock); static int ifcount = 0; -#ifdef HAVE_PRI -AST_MUTEX_DEFINE_STATIC(pridebugfdlock); -#endif - /*! \brief Protect the monitoring thread, so only one process can kill or start it, and not when it's doing something critical. */ AST_MUTEX_DEFINE_STATIC(monlock); @@ -255,6 +253,7 @@ static enum ast_bridge_result zt_bridge( static int zt_sendtext(struct ast_channel *c, const char *text); + /*! \brief Avoid the silly zt_getevent which ignores a bunch of events */ static inline int zt_get_event(int fd) { @@ -299,6 +298,27 @@ static int ringt_base = DEFAULT_RINGT; #define PRI_SPAN(p) (((p) >> 8) & 0xff) #define PRI_EXPLICIT(p) (((p) >> 16) & 0x01) +struct zt_suspended_call { + ast_mutex_t lock; /* Mutex */ + char msn[AST_MAX_EXTENSION]; /* the MSN to which this parked call belongs */ + char callid[10]; /* the callID provided by the user */ + int parked_at; /* extension in the call parking context */ + struct zt_suspended_call *next; +}; + +struct zt_holded_call { + ast_mutex_t lock; /* Mutex */ + char msn[AST_MAX_EXTENSION]; /* the MSN to which this parked call belongs */ + char uniqueid[AST_MAX_EXTENSION]; /* unique id of the onhold channel */ + int tei; + int cref; + int alreadyhungup; + struct ast_channel *channel; + struct ast_channel *bridge; + q931_call *call; /* this also covers tei mumbojumbo */ + struct zt_holded_call *next; +}; + struct zt_pri { pthread_t master; /*!< Thread of master */ ast_mutex_t lock; /*!< Mutex */ @@ -312,6 +332,8 @@ struct zt_pri { int nsf; /*!< Network-Specific Facilities */ int dialplan; /*!< Dialing plan */ int localdialplan; /*!< Local dialing plan */ + char nocid[AST_MAX_EXTENSION]; /*!< CallerID string to use if none provided */ + char withheldcid[AST_MAX_EXTENSION]; /*!< CallerID string to use if CallerID is withheld */ char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */ char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */ char localprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */ @@ -323,6 +345,7 @@ struct zt_pri { int prilogicalspan; /*!< Logical span number within trunk group */ int numchans; /*!< Num of channels we represent */ int overlapdial; /*!< In overlap dialing mode */ + int usercid; int facilityenable; /*!< Enable facility IEs */ struct pri *dchans[NUM_DCHANS]; /*!< Actual d-channels */ int dchanavail[NUM_DCHANS]; /*!< Whether each channel is available */ @@ -338,6 +361,9 @@ struct zt_pri { struct zt_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */ struct zt_pvt *crvs; /*!< Member CRV structs */ struct zt_pvt *crvend; /*!< Pointer to end of CRV structs */ + struct zt_suspended_call *suspended_calls; /* Calls parked with SUSPEND messages */ + struct zt_holded_call *holded_calls; /* Calls on hold */ + int debugfd; }; @@ -455,6 +481,8 @@ static struct zt_pvt { unsigned int echocanbridged:1; unsigned int echocanon:1; unsigned int faxhandled:1; /*!< Has a fax tone already been handled? */ + /*!< KPJ: i will abuse this flag to implement a zapata option for dialing out + on a zap channel with EC to be off no matter what happens. */ unsigned int firstradio:1; unsigned int hanguponpolarityswitch:1; unsigned int hardwaredtmf:1; @@ -468,7 +496,8 @@ static struct zt_pvt { unsigned int overlapdial:1; unsigned int permcallwaiting:1; unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */ - unsigned int priindication_oob:1; + unsigned int priindication_oob:2; + unsigned int pritransfer:2; unsigned int priexclusive:1; unsigned int pulse:1; unsigned int pulsedial:1; /*!< whether a pulse dial phone is detected */ @@ -505,6 +534,7 @@ static struct zt_pvt { #endif char cid_num[AST_MAX_EXTENSION]; int cid_ton; /*!< Type Of Number (TON) */ + int cid_pres; /*!< Calling Presentation */ char cid_name[AST_MAX_EXTENSION]; char lastcid_num[AST_MAX_EXTENSION]; char lastcid_name[AST_MAX_EXTENSION]; @@ -570,6 +600,8 @@ static struct zt_pvt { struct zt_pvt *bearer; struct zt_pvt *realcall; q931_call *call; + int tei; /* channel in use by this tei */ + q931_call *holdedcall; int prioffset; int logicalspan; #endif @@ -615,11 +647,14 @@ static struct zt_chan_conf zt_chan_conf_ .minunused = 2, .idleext = "", .idledial = "", + .nocid = "No CID available", + .withheldcid = "CID withheld", .internationalprefix = "", .nationalprefix = "", .localprefix = "", .privateprefix = "", .unknownprefix = "", + .usercid = 0, .resetinterval = 3600 }, @@ -631,6 +666,8 @@ static struct zt_chan_conf zt_chan_conf_ .mohinterpret = "default", .mohsuggest = "", .transfertobusy = 1, + .priindication_oob = 0, + .pritransfer = 0, .cid_signalling = CID_SIG_BELL, .cid_start = CID_START_RING, @@ -685,6 +722,8 @@ static int zt_indicate(struct ast_channe static int zt_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int zt_setoption(struct ast_channel *chan, int option, void *data, int datalen); static int zt_func_read(struct ast_channel *chan, char *function, char *data, char *buf, size_t len); +static void enable_dtmf_detect(struct zt_pvt *p); +static void disable_dtmf_detect(struct zt_pvt *p); static const struct ast_channel_tech zap_tech = { .type = "Zap", @@ -717,6 +756,13 @@ static const struct ast_channel_tech zap struct zt_pvt *round_robin[32]; #ifdef HAVE_PRI +struct app_tmp { + char app[256]; + char data[256]; + struct ast_channel *chan; + pthread_t t; +}; + static inline int pri_grab(struct zt_pvt *pvt, struct zt_pri *pri) { int res; @@ -1525,12 +1571,16 @@ static void zt_enable_ec(struct zt_pvt * int res; if (!p) return; + if (p->faxhandled) { + ast_log(LOG_DEBUG, "Not enabling echo cancellation on a fax/modem call\n"); + return; + } if (p->echocanon) { ast_log(LOG_DEBUG, "Echo cancellation already on\n"); return; } if (p->digital) { - ast_log(LOG_DEBUG, "Echo cancellation isn't required on digital connection\n"); + ast_log(LOG_DEBUG, "Echo cancellation does not make any sense on digital connections!\n"); return; } if (p->echocancel) { @@ -1557,7 +1607,7 @@ static void zt_train_ec(struct zt_pvt *p { int x; int res; - if (p && p->echocancel && p->echotraining) { + if (p && p->echocancel && p->echotraining && (!p->digital) && (!p->faxhandled)) { x = p->echotraining; res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOTRAIN, &x); if (res) @@ -1918,7 +1968,12 @@ static int zt_call(struct ast_channel *a ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", p->channel); p->outgoing = 1; - set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law); + if (IS_DIGITAL(ast->transfercapability)) { + set_actual_gain(p->subs[SUB_REAL].zfd, 0, 0, 0, p->law); + } else { + set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law); + } + mysig = p->sig; if (p->outsigmod > -1) @@ -2149,6 +2204,7 @@ static int zt_call(struct ast_channel *a case SIG_PRI: /* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */ p->dialdest[0] = '\0'; + disable_dtmf_detect(p); break; default: ast_log(LOG_DEBUG, "not yet implemented\n"); @@ -2169,6 +2225,12 @@ static int zt_call(struct ast_channel *a const char *rr_str; int redirect_reason; + if ((p->pri->nodetype == BRI_NETWORK_PTMP) || (p->pri->nodetype == BRI_NETWORK)) { + // pass NO audio when ringing an isdn phone + p->dialing = 1; + // maybe we could allow passing audio when calling a p2p PBX, but well... ;-) + } + c = strchr(dest, '/'); if (c) c++; @@ -2191,6 +2253,7 @@ static int zt_call(struct ast_channel *a ast_mutex_unlock(&p->lock); return -1; } + strncpy(p->dnid, (c + p->stripmsd), sizeof(p->dnid)-1); if (mysig != SIG_FXSKS) { p->dop.op = ZT_DIAL_OP_REPLACE; s = strchr(c + p->stripmsd, 'w'); @@ -2214,6 +2277,8 @@ static int zt_call(struct ast_channel *a pri_rel(p->pri); ast_mutex_unlock(&p->lock); return -1; + } else { + // ast_log(LOG_NOTICE, "call %d\n", p->call); } if (!(sr = pri_sr_new())) { ast_log(LOG_WARNING, "Failed to allocate setup request channel %d\n", p->channel); @@ -2243,7 +2308,7 @@ static int zt_call(struct ast_channel *a pri_sr_set_channel(sr, p->bearer ? PVT_TO_CHANNEL(p->bearer) : PVT_TO_CHANNEL(p), exclusive, 1); pri_sr_set_bearer(sr, p->digital ? PRI_TRANS_CAP_DIGITAL : ast->transfercapability, (p->digital ? -1 : - ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW))); + ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW)), ast->lowlayercompat); if (p->pri->facilityenable) pri_facility_enable(p->pri->pri); @@ -2507,8 +2572,10 @@ static int pri_find_dchan(struct zt_pri } if (newslot < 0) { newslot = 0; - ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n", + if (pri->nodetype != BRI_CPE_PTMP) { + ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n", pri->dchannels[newslot]); + } } if (old && (oldslot != newslot)) ast_log(LOG_NOTICE, "Switching from from d-channel %d to channel %d!\n", @@ -2518,6 +2585,16 @@ static int pri_find_dchan(struct zt_pri } #endif +static int zt_setlaw(int zfd, int law) +{ + int res; + res = ioctl(zfd, ZT_SETLAW, &law); + if (res) + return res; + return 0; +} + + static int zt_hangup(struct ast_channel *ast) { int res; @@ -2565,8 +2642,7 @@ static int zt_hangup(struct ast_channel if (option_debug) ast_log(LOG_DEBUG, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n", p->channel, index, p->subs[SUB_REAL].zfd, p->subs[SUB_CALLWAIT].zfd, p->subs[SUB_THREEWAY].zfd); - p->ignoredtmf = 0; - + if (index > -1) { /* Real channel, do some fixup */ p->subs[index].owner = NULL; @@ -2668,6 +2744,7 @@ static int zt_hangup(struct ast_channel } if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) { + int outgoing = p->outgoing; p->owner = NULL; p->ringt = 0; p->distinctivering = 0; @@ -2710,7 +2787,7 @@ static int zt_hangup(struct ast_channel pri_call_set_useruser(p->call, useruser); #endif - pri_hangup(p->pri->pri, p->call, -1); + pri_hangup(p->pri->pri, p->call, -1, -1); p->call = NULL; if (p->bearer) p->bearer->call = NULL; @@ -2730,7 +2807,28 @@ static int zt_hangup(struct ast_channel if (atoi(cause)) icause = atoi(cause); } - pri_hangup(p->pri->pri, p->call, icause); + + pri_hangup(p->pri->pri, p->call, icause, -1); + + /* if we send a rel9999ease complete we wont ge no hangup event, so clear the call here */ + if (icause == 34 || icause == 44 || icause == 82 || icause == 1 || icause == 81 || icause == 17) { + if ((ast->_state == AST_STATE_RING) || (ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING) || (ast->_state == AST_STATE_RESERVED)) { + p->call = NULL; + } else { + ast_log(LOG_ERROR, "What is wrong with you? You cannot use cause %d number when in state %d!\n", icause, ast->_state); + icause = 16; /* Note, in pri_hangup() libpri will already override the cause */ + } + } + + if (p->pri->nodetype == BRI_NETWORK_PTMP) { + if ((icause == 16 || icause == -1) && (ast->_state != AST_STATE_UP)) { + if (outgoing) { + p->call = NULL; + } + } + } + + } if (res < 0) ast_log(LOG_WARNING, "pri_disconnect failed\n"); @@ -2914,10 +3012,14 @@ static int zt_answer(struct ast_channel p->proceeding = 1; res = pri_answer(p->pri->pri, p->call, 0, !p->digital); pri_rel(p->pri); + /* stop ignoring inband dtmf */ + enable_dtmf_detect(p); } else { ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); res = -1; } + /* the audio path is complete now, train the echo canceler */ + zt_train_ec(p); break; #endif case 0: @@ -3554,6 +3656,15 @@ static int zt_fixup(struct ast_channel * { struct zt_pvt *p = newchan->tech_pvt; int x; + if (newchan && newchan->tech_pvt) { + p = newchan->tech_pvt; + } + if (!p) { + if (newchan) { + ast_log(LOG_ERROR, "channel %s has no tech_pvt structure\n", newchan->name); + } + return 0; + } ast_mutex_lock(&p->lock); ast_log(LOG_DEBUG, "New owner for channel %d is %s\n", p->channel, newchan->name); if (p->owner == oldchan) { @@ -3763,8 +3874,10 @@ static void zt_handle_dtmfup(struct ast_ pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); if (ast_async_goto(ast, target_context, "fax", 1)) ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); - } else + } else { + if (option_verbose > 2) ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + } } else if (option_debug) ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n"); } else if (option_debug) @@ -3923,7 +4036,7 @@ static struct ast_frame *zt_handle_event if (p->call) { if (p->pri && p->pri->pri) { if (!pri_grab(p, p->pri)) { - pri_hangup(p->pri->pri, p->call, -1); + pri_hangup(p->pri->pri, p->call, -1, -1); pri_destroycall(p->pri->pri, p->call); p->call = NULL; pri_rel(p->pri); @@ -4974,7 +5087,7 @@ static struct ast_frame *zt_read(struct p->subs[index].f.data = NULL; p->subs[index].f.datalen= 0; } - if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) { + if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) { /* Perform busy detection. etc on the zap line */ f = ast_dsp_process(ast, p->dsp, &p->subs[index].f); if (f) { @@ -4986,8 +5099,9 @@ static struct ast_frame *zt_read(struct } } else if (f->frametype == AST_FRAME_DTMF) { #ifdef HAVE_PRI - if (!p->proceeding && p->sig==SIG_PRI && p->pri && p->pri->overlapdial) { - /* Don't accept in-band DTMF when in overlap dial mode */ + if (p->sig==SIG_PRI && p->pri && p->pri->overlapdial && p->ignoredtmf) { + /* Don't accept in-band DTMF when in overlap dial mode + or when in non-overlap overlapdialing mode ... */ f->frametype = AST_FRAME_NULL; f->subclass = 0; } @@ -5062,7 +5176,9 @@ static int zt_write(struct ast_channel * #endif /* Write a frame of (presumably voice) data */ if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype != AST_FRAME_IMAGE) + if (frame->frametype == AST_FRAME_TEXT) { + ast_log(LOG_NOTICE, "text\n"); + } else if (frame->frametype != AST_FRAME_IMAGE) ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype); return 0; } @@ -5130,7 +5246,7 @@ static int zt_indicate(struct ast_channe switch (condition) { case AST_CONTROL_BUSY: #ifdef HAVE_PRI - if (p->priindication_oob && p->sig == SIG_PRI) { + if ((p->priindication_oob == 1) && p->sig == SIG_PRI) { chan->hangupcause = AST_CAUSE_USER_BUSY; chan->_softhangup |= AST_SOFTHANGUP_DEV; res = 0; @@ -5212,7 +5328,7 @@ static int zt_indicate(struct ast_channe case AST_CONTROL_CONGESTION: chan->hangupcause = AST_CAUSE_CONGESTION; #ifdef HAVE_PRI - if (p->priindication_oob && p->sig == SIG_PRI) { + if ((p->priindication_oob == 1) && p->sig == SIG_PRI) { chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION; chan->_softhangup |= AST_SOFTHANGUP_DEV; res = 0; @@ -5406,8 +5522,12 @@ static struct ast_channel *zt_new(struct if (state == AST_STATE_RING) tmp->rings = 1; tmp->tech_pvt = i; +#ifdef HAVE_PRI + if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS) || (i->sig == SIG_PRI)) { +#else if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS)) { - /* Only FXO signalled stuff can be picked up */ +#endif + /* Only FXO signalled stuff can be picked up */ /* i dont think so, mr. ulaw! we alaws like to pick up BRIs/PRIs */ tmp->callgroup = i->callgroup; tmp->pickupgroup = i->pickupgroup; } @@ -5537,6 +5657,7 @@ static void *ss_thread(void *data) int len = 0; int res; int index; + int network; /* in the bizarre case where the channel has become a zombie before we even get started here, abort safely @@ -5565,10 +5686,17 @@ static void *ss_thread(void *data) len = strlen(exten); res = 0; while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) { - if (len && !ast_ignore_pattern(chan->context, exten)) + if (len && !ast_ignore_pattern(chan->context, exten)) { tone_zone_play_tone(p->subs[index].zfd, -1); - else - tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE); + } else { + network = p->pri->nodetype == PRI_NETWORK || p->pri->nodetype == BRI_NETWORK || p->pri->nodetype == BRI_NETWORK_PTMP; + if (network) { + tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE); + } else { + /* cpe be quiet */ + tone_zone_play_tone(p->subs[index].zfd, -1); + } + } if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num)) timeout = matchdigittimeout; else @@ -6784,18 +6912,44 @@ static int handle_init_event(struct zt_p break; case ZT_EVENT_NOALARM: i->inalarm = 0; +#ifdef HAVE_PRI + if (i->pri) { + if ((i->pri->nodetype == BRI_CPE_PTMP) || (i->pri->nodetype == BRI_CPE)) { + /* dont annoy BRI TE mode users with layer2layer alarms */ + } else { + ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel); + manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", + "Channel: %d\r\n", i->channel); + } + } +#else ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel); manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", "Channel: %d\r\n", i->channel); +#endif break; case ZT_EVENT_ALARM: i->inalarm = 1; res = get_alarms(i); +#ifdef HAVE_PRI + if (i->pri) { + if ((i->pri->nodetype == BRI_CPE_PTMP) || (i->pri->nodetype == BRI_CPE)) { + /* dont annoy BRI TE mode users with layer2layer alarms */ + } else { + ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", i->channel, alarm2str(res)); + manager_event(EVENT_FLAG_SYSTEM, "Alarm", + "Alarm: %s\r\n" + "Channel: %d\r\n", + alarm2str(res), i->channel); + } + } +#else ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", i->channel, alarm2str(res)); manager_event(EVENT_FLAG_SYSTEM, "Alarm", "Alarm: %s\r\n" "Channel: %d\r\n", alarm2str(res), i->channel); +#endif /* fall thru intentionally */ case ZT_EVENT_ONHOOK: if (i->radio) @@ -6839,8 +6993,10 @@ static int handle_init_event(struct zt_p zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK); break; case SIG_PRI: - zt_disable_ec(i); - res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1); + if (event != ZT_EVENT_ALARM) { + zt_disable_ec(i); + res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1); + } break; default: ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", sig2str(i->sig), i->channel); @@ -7139,6 +7295,8 @@ static int pri_resolve_span(int *span, i } else { if (si->totalchans == 31) { /* if it's an E1 */ pris[*span].dchannels[0] = 16 + offset; + } else if (si->totalchans == 3) { /* if it's an S0 ZAPBRI */ + pris[*span].dchannels[0] = 3 + offset; } else { pris[*span].dchannels[0] = 24 + offset; } @@ -7391,6 +7549,11 @@ static struct zt_pvt *mkintf(int channel destroy_zt_pvt(&tmp); return NULL; } + if ((pris[span].localdialplan) && (pris[span].localdialplan != conf.pri.localdialplan)) { + ast_log(LOG_ERROR, "Span %d is already a %s local dialing plan\n", span + 1, dialplan2str(pris[span].localdialplan)); + destroy_zt_pvt(&tmp); + return NULL; + } if (pris[span].minunused && (pris[span].minunused != conf.pri.minunused)) { ast_log(LOG_ERROR, "Span %d already has minunused of %d.\n", span + 1, conf.pri.minunused); destroy_zt_pvt(&tmp); @@ -7408,6 +7571,11 @@ static struct zt_pvt *mkintf(int channel return NULL; } pris[span].nodetype = conf.pri.nodetype; + + if (conf.pri.nodetype == BRI_NETWORK_PTMP) { + pris[span].dchanavail[0] = DCHAN_AVAILABLE; + pri_find_dchan(&pris[span]); + } pris[span].switchtype = myswitchtype; pris[span].nsf = conf.pri.nsf; pris[span].dialplan = conf.pri.dialplan; @@ -7416,9 +7584,14 @@ static struct zt_pvt *mkintf(int channel pris[span].minunused = conf.pri.minunused; pris[span].minidle = conf.pri.minidle; pris[span].overlapdial = conf.pri.overlapdial; + pris[span].usercid = conf.pri.usercid; + pris[span].suspended_calls = NULL; + pris[span].holded_calls = NULL; pris[span].facilityenable = conf.pri.facilityenable; ast_copy_string(pris[span].idledial, conf.pri.idledial, sizeof(pris[span].idledial)); ast_copy_string(pris[span].idleext, conf.pri.idleext, sizeof(pris[span].idleext)); + ast_copy_string(pris[span].nocid, conf.pri.nocid, sizeof(pris[span].nocid)); + ast_copy_string(pris[span].withheldcid, conf.pri.withheldcid, sizeof(pris[span].withheldcid)); ast_copy_string(pris[span].internationalprefix, conf.pri.internationalprefix, sizeof(pris[span].internationalprefix)); ast_copy_string(pris[span].nationalprefix, conf.pri.nationalprefix, sizeof(pris[span].nationalprefix)); ast_copy_string(pris[span].localprefix, conf.pri.localprefix, sizeof(pris[span].localprefix)); @@ -7554,6 +7727,7 @@ static struct zt_pvt *mkintf(int channel tmp->restrictcid = conf.chan.restrictcid; tmp->use_callingpres = conf.chan.use_callingpres; tmp->priindication_oob = conf.chan.priindication_oob; + tmp->pritransfer = conf.chan.pritransfer; tmp->priexclusive = conf.chan.priexclusive; if (tmp->usedistinctiveringdetection) { if (!tmp->use_callerid) { @@ -7833,7 +8007,7 @@ static int pri_find_empty_chan(struct zt break; if (!backwards && (x >= pri->numchans)) break; - if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner) { + if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner && !pri->pvts[x]->call) { ast_log(LOG_DEBUG, "Found empty available channel %d/%d\n", pri->pvts[x]->logicalspan, pri->pvts[x]->prioffset); return x; @@ -8029,6 +8203,11 @@ static struct ast_channel *zt_request(co p->digital = 1; if (tmp) tmp->transfercapability = AST_TRANS_CAP_DIGITAL; + } else if (opt == 'm') { + /* If this is a modem/fax call, pretend to have the fax handled and dont do EC */ + p->faxhandled = 1; + if (tmp) + tmp->transfercapability = AST_TRANS_CAP_3_1K_AUDIO; } else { ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data); } @@ -8062,13 +8241,14 @@ next: *cause = AST_CAUSE_BUSY; } else if (groupmatched) { *cause = AST_CAUSE_CONGESTION; + } else { + *cause = AST_CAUSE_CONGESTION; } } return tmp; } - #ifdef HAVE_PRI static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv) { @@ -8082,6 +8262,58 @@ static struct zt_pvt *pri_find_crv(struc return NULL; } +static int pri_find_tei(struct zt_pri *pri, q931_call *c, int tei) +{ + int x=0; + for (x=0;xnumchans;x++) { + if (!pri->pvts[x]) continue; + if ((pri->pvts[x]->tei == tei) && (pri->pvts[x]-> call != c)) { + return x; + } + } + return -1; +} + +static struct zt_holded_call *pri_get_callonhold(struct zt_pri *pri, int cref, int tei) { + struct zt_holded_call *zhc = pri->holded_calls; + struct zt_holded_call *zhctemp = NULL; + + while (zhc) { + if ((zhc->tei == tei) && ((zhc->cref == cref) || (cref == -1))) { + return zhc; + } + zhctemp = zhc; + if (zhc) zhc = zhc->next; + } + return NULL; +} + +static int pri_destroy_callonhold(struct zt_pri *pri, struct zt_holded_call *onhold) { + struct zt_holded_call *zhc = pri->holded_calls; + struct zt_holded_call *zhctemp = NULL; + + while (zhc) { + if (zhc == onhold) { + if (zhctemp) { + zhctemp->next = zhc->next; + zhc = zhctemp; + } else { + pri->holded_calls = zhc->next; + zhc = pri->holded_calls; + zhctemp = NULL; + } + } + zhctemp = zhc; + if (zhc) zhc = zhc->next; + } + if (onhold) { + free(onhold); + onhold = NULL; + return 1; + } + return 0; +} + static int pri_find_principle(struct zt_pri *pri, int channel) { @@ -8113,7 +8345,9 @@ static int pri_find_principle(struct zt_ static int pri_fixup_principle(struct zt_pri *pri, int principle, q931_call *c) { int x; + int res = 0; struct zt_pvt *crv; + char tmpname[256]; if (!c) { if (principle < 0) return -1; @@ -8141,6 +8375,7 @@ static int pri_fixup_principle(struct zt } /* Fix it all up now */ pri->pvts[principle]->owner = pri->pvts[x]->owner; + pri->pvts[principle]->outgoing = pri->pvts[x]->outgoing; if (pri->pvts[principle]->owner) { ast_string_field_build(pri->pvts[principle]->owner, name, "Zap/%d:%d-%d", pri->trunkgroup, @@ -8148,13 +8383,48 @@ static int pri_fixup_principle(struct zt pri->pvts[principle]->owner->tech_pvt = pri->pvts[principle]; pri->pvts[principle]->owner->fds[0] = pri->pvts[principle]->subs[SUB_REAL].zfd; pri->pvts[principle]->subs[SUB_REAL].owner = pri->pvts[x]->subs[SUB_REAL].owner; - } else + } else { ast_log(LOG_WARNING, "Whoa, there's no owner, and we're having to fix up channel %d to channel %d\n", pri->pvts[x]->channel, pri->pvts[principle]->channel); + } pri->pvts[principle]->call = pri->pvts[x]->call; + pri->pvts[principle]->dsp = pri->pvts[x]->dsp; + pri->pvts[principle]->alreadyhungup = pri->pvts[x]->alreadyhungup; + pri->pvts[principle]->digital = pri->pvts[x]->digital; + pri->pvts[principle]->faxhandled = pri->pvts[x]->faxhandled; + + if ((pri->nodetype == BRI_CPE_PTMP) || (pri->nodetype == BRI_CPE)) { + /* this might also apply for other pri types! */ + pri->pvts[principle]->law = pri->pvts[x]->law; + if (ioctl(pri->pvts[principle]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &pri->pvts[principle]->law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", pri->pvts[principle]->channel, pri->pvts[principle]->law); + res = zt_setlaw(pri->pvts[principle]->subs[SUB_REAL].zfd, pri->pvts[principle]->law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pri->pvts[principle]->channel); + if (!pri->pvts[principle]->digital) { + res = set_actual_gain(pri->pvts[principle]->subs[SUB_REAL].zfd, 0, pri->pvts[principle]->rxgain, pri->pvts[principle]->txgain, pri->pvts[principle]->law); + } else { + res = set_actual_gain(pri->pvts[principle]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[principle]->law); + } + if (res < 0) + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pri->pvts[principle]->channel); + zt_confmute(pri->pvts[x], 0); + update_conf(pri->pvts[x]); + reset_conf(pri->pvts[x]); + restore_gains(pri->pvts[x]); + zt_disable_ec(pri->pvts[x]); + zt_setlinear(pri->pvts[x]->subs[SUB_REAL].zfd, 0); + } + + if (pri->pvts[principle]->owner) { + snprintf(tmpname, sizeof(tmpname), "Zap/%d-1", pri->pvts[principle]->channel); + ast_change_name(pri->pvts[principle]->owner, tmpname); + } + /* Free up the old channel, now not in use */ pri->pvts[x]->subs[SUB_REAL].owner = NULL; pri->pvts[x]->owner = NULL; pri->pvts[x]->call = NULL; + pri->pvts[x]->dsp = NULL; } return principle; } @@ -8183,7 +8453,9 @@ static int pri_fixup_principle(struct zt } crv = crv->next; } - ast_log(LOG_WARNING, "Call specified, but not found?\n"); + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + ast_log(LOG_WARNING, "Call specified, but not found?\n"); + } return -1; } @@ -8242,86 +8514,21 @@ static void *do_idle_thread(void *vchan) #ifndef PRI_RESTART #error "Upgrade your libpri" #endif -static void zt_pri_message(struct pri *pri, char *s) +static void zt_pri_message(char *s, int span) { - int x, y; - int dchan = -1, span = -1; - int dchancount = 0; - - if (pri) { - for (x = 0; x < NUM_SPANS; x++) { - for (y = 0; y < NUM_DCHANS; y++) { - if (pris[x].dchans[y]) - dchancount++; - - if (pris[x].dchans[y] == pri) - dchan = y; - } - if (dchan >= 0) { - span = x; - break; - } - dchancount = 0; - } - if ((dchan >= 0) && (span >= 0)) { - if (dchancount > 1) - ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s); - else - ast_verbose("%s", s); - } else - ast_log(LOG_ERROR, "PRI debug error: could not find pri associated it with debug message output\n"); - } else - ast_verbose("%s", s); - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) - write(pridebugfd, s, strlen(s)); - - ast_mutex_unlock(&pridebugfdlock); + ast_verbose("%d %s", span, s); } -static void zt_pri_error(struct pri *pri, char *s) +static void zt_pri_error(char *s, int span) { - int x, y; - int dchan = -1, span = -1; - int dchancount = 0; - - if (pri) { - for (x = 0; x < NUM_SPANS; x++) { - for (y = 0; y < NUM_DCHANS; y++) { - if (pris[x].dchans[y]) - dchancount++; - - if (pris[x].dchans[y] == pri) - dchan = y; - } - if (dchan >= 0) { - span = x; - break; - } - dchancount = 0; - } - if ((dchan >= 0) && (span >= 0)) { - if (dchancount > 1) - ast_log(LOG_ERROR, "[Span %d D-Channel %d] PRI: %s", span, dchan, s); - else - ast_log(LOG_ERROR, "%s", s); - } else - ast_log(LOG_ERROR, "PRI debug error: could not find pri associated it with debug message output\n"); - } else - ast_log(LOG_ERROR, "%s", s); - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) - write(pridebugfd, s, strlen(s)); - - ast_mutex_unlock(&pridebugfdlock); + ast_log(LOG_WARNING, "%d %s", span, s); } static int pri_check_restart(struct zt_pri *pri) { + if ((pri->nodetype != PRI_NETWORK) && (pri->nodetype != PRI_CPE)) { + return 0; + } do { pri->resetpos++; } while ((pri->resetpos < pri->numchans) && @@ -8405,13 +8612,30 @@ static void apply_plan_to_number(char *b } } -static int zt_setlaw(int zfd, int law) -{ - int res; - res = ioctl(zfd, ZT_SETLAW, &law); - if (res) - return res; - return 0; +static void pri_make_callerid(struct zt_pri *pri, char *callerid, int callerid_len, char *callingnum, int callingnum_len, int callingplan, int callingpres, int stripmsd) { + if (callingnum && (callingnum_len > stripmsd)) { + callingnum += stripmsd; + } + switch (callingplan) { + case PRI_INTERNATIONAL_ISDN: + snprintf(callerid, callerid_len, "%s%s", pri->internationalprefix, callingnum); + break; + case PRI_NATIONAL_ISDN: + snprintf(callerid, callerid_len, "%s%s", pri->nationalprefix, callingnum); + break; + case PRI_LOCAL_ISDN: + snprintf(callerid, callerid_len, "%s%s", pri->localprefix, callingnum); + break; + case PRI_PRIVATE: + snprintf(callerid, callerid_len, "%s%s", pri->privateprefix, callingnum); + break; + case PRI_UNKNOWN: + snprintf(callerid, callerid_len, "%s%s", pri->unknownprefix, callingnum); + break; + default: + snprintf(callerid, callerid_len, "%s", callingnum); + break; + } } static void *pri_dchannel(void *vpri) @@ -8591,15 +8815,44 @@ static void *pri_dchannel(void *vpri) /* Check for an event */ x = 0; res = ioctl(pri->fds[which], ZT_GETEVENT, &x); - if (x) + if ((pri->nodetype != BRI_CPE) && (pri->nodetype != BRI_CPE_PTMP)) { + /* dont annoy BRI TE mode users with layer2layer alarms */ + if (x) ast_log(LOG_NOTICE, "PRI got event: %s (%d) on %s D-channel of span %d\n", event2str(x), x, pri_order(which), pri->span); + } /* Keep track of alarm state */ if (x == ZT_EVENT_ALARM) { pri->dchanavail[which] &= ~(DCHAN_NOTINALARM | DCHAN_UP); pri_find_dchan(pri); + if ((pri->nodetype == BRI_CPE) || (pri->nodetype == BRI_CPE_PTMP)) { + if (pri->pri) { + for (i=0; inumchans; i++) { + struct zt_pvt *p = pri->pvts[i]; + if (p) { + if (p->call) { + if (p->pri && p->pri->pri) { + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + p->tei = -1; + } else + ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); + } + if (p->owner) + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + p->inalarm = 1; + } + } + pri_shutdown(pri->pri); + } + } } else if (x == ZT_EVENT_NOALARM) { - pri->dchanavail[which] |= DCHAN_NOTINALARM; - pri_restart(pri->dchans[which]); + if ((pri->nodetype == BRI_CPE) || (pri->nodetype == BRI_CPE_PTMP)) { + pri->dchanavail[which] |= DCHAN_NOTINALARM; + // pri->dchanavail[which] |= DCHAN_UP; + } else { + pri->dchanavail[which] |= DCHAN_NOTINALARM; + pri_restart(pri->dchans[which]); + } } if (option_debug) @@ -8611,8 +8864,7 @@ static void *pri_dchannel(void *vpri) break; } } else if (errno != EINTR) - ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno)); - + ast_log(LOG_WARNING, "pri_event returned error %d (%s) on span %d\n", errno, strerror(errno), pri->span); if (e) { if (pri->debug) pri_dump_event(pri->dchans[which], e); @@ -8625,32 +8877,86 @@ static void *pri_dchannel(void *vpri) switch (e->e) { case PRI_EVENT_DCHAN_UP: - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span); - pri->dchanavail[which] |= DCHAN_UP; - if (!pri->pri) pri_find_dchan(pri); + if (pri->nodetype == BRI_NETWORK_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up for TEI %d\n", pri_order(which), pri->span, e->gen.tei); + pri->dchanavail[which] |= DCHAN_UP; + if (!pri->pri) pri_find_dchan(pri); + + /* Note presense of D-channel */ + time(&pri->lastreset); + + pri->resetting = 0; + /* Take the channels from inalarm condition */ + for (i=0; inumchans; i++) + if (pri->pvts[i]) { + pri->pvts[i]->inalarm = 0; + } + } else { + if (pri->nodetype == BRI_CPE_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span); + } else { + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span); + } + pri->dchanavail[which] |= DCHAN_UP; + if (!pri->pri) pri_find_dchan(pri); - /* Note presense of D-channel */ - time(&pri->lastreset); + /* Note presense of D-channel */ + time(&pri->lastreset); - /* Restart in 5 seconds */ - if (pri->resetinterval > -1) { + /* Restart in 5 seconds */ + if (pri->resetinterval > -1) { pri->lastreset -= pri->resetinterval; pri->lastreset += 5; - } - pri->resetting = 0; - /* Take the channels from inalarm condition */ - for (i = 0; i < pri->numchans; i++) + } + pri->resetting = 0; + /* Take the channels from inalarm condition */ + for (i = 0; i < pri->numchans; i++) if (pri->pvts[i]) { pri->pvts[i]->inalarm = 0; } + } break; case PRI_EVENT_DCHAN_DOWN: - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span); - pri->dchanavail[which] &= ~DCHAN_UP; - pri_find_dchan(pri); - if (!pri_is_up(pri)) { + if (pri->nodetype == BRI_NETWORK_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down for TEI %d\n", pri_order(which), pri->span, e->gen.tei); + // PTMP BRIs have N dchans, handled by libpri + if (e->gen.tei == 0) break; + /* Hangup active channels */ + for (i=0; inumchans; i++) { + struct zt_pvt *p = pri->pvts[i]; + if (p) { + // ast_log(LOG_NOTICE, "chan %d tei %d\n",i,p->tei); + if (p->tei == e->gen.tei) { + if (p->call) { + if (p->pri && p->pri->pri) { + pri_hangup(p->pri->pri, p->call, -1, -1); + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + } else + ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); + } + if (p->owner) + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + p->inalarm = 1; + p->tei = -1; + } + } + } + } else { + if (pri->nodetype == BRI_CPE_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span); + } else { + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span); + } + pri->dchanavail[which] &= ~DCHAN_UP; + pri_find_dchan(pri); + if (!pri_is_up(pri)) { pri->resetting = 0; /* Hangup active channels and put them in alarm mode */ for (i = 0; i < pri->numchans; i++) { @@ -8660,12 +8966,13 @@ static void *pri_dchannel(void *vpri) /* T309 is not enabled : hangup calls when alarm occurs */ if (p->call) { if (p->pri && p->pri->pri) { - pri_hangup(p->pri->pri, p->call, -1); + pri_hangup(p->pri->pri, p->call, -1, -1); pri_destroycall(p->pri->pri, p->call); p->call = NULL; } else ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); } + p->tei = -1; if (p->realcall) { pri_hangup_all(p->realcall, pri); } else if (p->owner) @@ -8674,6 +8981,7 @@ static void *pri_dchannel(void *vpri) p->inalarm = 1; } } + } } break; case PRI_EVENT_RESTART: @@ -8708,8 +9016,8 @@ static void *pri_dchannel(void *vpri) pri_destroycall(pri->pri, pri->pvts[x]->call); pri->pvts[x]->call = NULL; } - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); + if (pri->pvts[x]->realcall) + pri_hangup_all(pri->pvts[x]->realcall, pri); else if (pri->pvts[x]->owner) pri->pvts[x]->owner->_softhangup |= AST_SOFTHANGUP_DEV; ast_mutex_unlock(&pri->pvts[x]->lock); @@ -8743,7 +9051,6 @@ static void *pri_dchannel(void *vpri) } } break; - case PRI_EVENT_INFO_RECEIVED: chanpos = pri_find_principle(pri, e->ring.channel); if (chanpos < 0) { @@ -8752,9 +9059,11 @@ static void *pri_dchannel(void *vpri) } else { chanpos = pri_fixup_principle(pri, chanpos, e->ring.call); if (chanpos > -1) { +// ast_log(LOG_NOTICE, "INFO received on channel %d/%d span %d\n", +// PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); ast_mutex_lock(&pri->pvts[chanpos]->lock); /* queue DTMF frame if the PBX for this call was already started (we're forwarding INFORMATION further on */ - if (pri->overlapdial && pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) { + if (pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) { /* how to do that */ int digitlen = strlen(e->ring.callednum); char digit; @@ -8766,6 +9075,14 @@ static void *pri_dchannel(void *vpri) zap_queue_frame(pri->pvts[chanpos], &f, pri); } } + if (!pri->overlapdial) { + strncat(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten)); + if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten + 1)) { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1); + } else { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } + } } ast_mutex_unlock(&pri->pvts[chanpos]->lock); } @@ -8773,36 +9090,59 @@ static void *pri_dchannel(void *vpri) break; case PRI_EVENT_RING: crv = NULL; - if (e->ring.channel == -1) + if (e->ring.channel == -1) { + /* if no channel specified find one empty */ chanpos = pri_find_empty_chan(pri, 1); - else + } else { chanpos = pri_find_principle(pri, e->ring.channel); + } /* if no channel specified find one empty */ if (chanpos < 0) { - ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); + /* no channel specified and no free channel. this is a callwating SETUP */ + if (e->ring.channel <= 0) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Ignoring callwaiting SETUP on channel %d/%d span %d %d\n", PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span, e->ring.channel); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_USER_BUSY, -1); + break; + } } else { + /* ok, we got a b channel for this call, lock it */ ast_mutex_lock(&pri->pvts[chanpos]->lock); if (pri->pvts[chanpos]->owner) { - if (pri->pvts[chanpos]->call == e->ring.call) { - ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n", + /* safety check, for messed up retransmissions? */ + if (pri->pvts[chanpos]->call == e->ring.call) { + ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n", PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - break; + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + chanpos = -1; + break; + } else { + ast_log(LOG_WARNING, "Ring requested on channel %d/%d already in use on span %d. Hanging up owner.\n", + PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); + if (pri->pvts[chanpos]->realcall) { + pri_hangup_all(pri->pvts[chanpos]->realcall, pri); } else { - /* This is where we handle initial glare */ - ast_log(LOG_DEBUG, "Ring requested on channel %d/%d already in use or previously requested on span %d. Attempting to renegotiating channel.\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - chanpos = -1; + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; + /* XXX destroy the call here, so we can accept the retransmission as a new call */ + pri_destroycall(pri->pri, e->ring.call); } - } - if (chanpos > -1) ast_mutex_unlock(&pri->pvts[chanpos]->lock); + chanpos = -1; + break; + } + } + if (chanpos > -1) { + /* everything is ok with the b channel */ + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } } - if ((chanpos < 0) && (e->ring.flexible)) - chanpos = pri_find_empty_chan(pri, 1); + /* actually, we already got a valid channel by now */ if (chanpos > -1) { ast_mutex_lock(&pri->pvts[chanpos]->lock); + /* dont detect dtmfs before the signalling is done */ + disable_dtmf_detect(pri->pvts[chanpos]); + /* this channel is owned by this TEI */ + pri->pvts[chanpos]->tei = e->ring.tei; if (pri->switchtype == PRI_SWITCH_GR303_TMC) { /* Should be safe to lock CRV AFAIK while bearer is still locked */ crv = pri_find_crv(pri, pri_get_crv(pri->pri, e->ring.call, NULL)); @@ -8816,13 +9156,14 @@ static void *pri_dchannel(void *vpri) ast_log(LOG_WARNING, "Call received for busy CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span); } else ast_log(LOG_NOTICE, "Call received for unconfigured CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE, -1); if (crv) ast_mutex_unlock(&crv->lock); ast_mutex_unlock(&pri->pvts[chanpos]->lock); break; } } + /* assign call to b channel */ pri->pvts[chanpos]->call = e->ring.call; apply_plan_to_number(plancallingnum, sizeof(plancallingnum), pri, e->ring.callingnum, e->ring.callingplan); if (pri->pvts[chanpos]->use_callerid) { @@ -8847,34 +9188,82 @@ static void *pri_dchannel(void *vpri) } apply_plan_to_number(pri->pvts[chanpos]->rdnis, sizeof(pri->pvts[chanpos]->rdnis), pri, e->ring.redirectingnum, e->ring.callingplanrdnis); + /* get callingpres */ + pri->pvts[chanpos]->cid_pres = e->ring.callingpres; + switch (e->ring.callingpres) { + case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: + case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: + case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: + case PRES_PROHIB_NETWORK_NUMBER: + ast_copy_string(pri->pvts[chanpos]->cid_name, pri->withheldcid, sizeof(pri->pvts[chanpos]->cid_name)); + break; + case PRES_NUMBER_NOT_AVAILABLE: + ast_copy_string(pri->pvts[chanpos]->cid_name, pri->nocid, sizeof(pri->pvts[chanpos]->cid_name)); + break; + } /* If immediate=yes go to s|1 */ if (pri->pvts[chanpos]->immediate) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of immediate=yes\n"); pri->pvts[chanpos]->exten[0] = 's'; pri->pvts[chanpos]->exten[1] = '\0'; - } - /* Get called number */ - else if (!ast_strlen_zero(e->ring.callednum)) { - ast_copy_string(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten)); - ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid)); - } else if (pri->overlapdial) - pri->pvts[chanpos]->exten[0] = '\0'; - else { - /* Some PRI circuits are set up to send _no_ digits. Handle them as 's'. */ - pri->pvts[chanpos]->exten[0] = 's'; - pri->pvts[chanpos]->exten[1] = '\0'; - } - /* Set DNID on all incoming calls -- even immediate */ - if (!ast_strlen_zero(e->ring.callednum)) - ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid)); - /* No number yet, but received "sending complete"? */ - if (e->ring.complete && (ast_strlen_zero(e->ring.callednum))) { + } else if (ast_strlen_zero(e->ring.callednum)) { + /* called party number is empty */ + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + if (!pri->overlapdial) { + // be able to set digittimeout for BRI phones + pri->pvts[chanpos]->exten[0] = 's'; + pri->pvts[chanpos]->exten[1] = '\0'; + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } else { + pri->pvts[chanpos]->exten[0] = '\0'; + } + } else { + if (pri->nodetype == BRI_CPE) { + /* fix for .at p2p bri lines */ + pri->pvts[chanpos]->exten[0] = 's'; + pri->pvts[chanpos]->exten[1] = '\0'; + } else if (pri->overlapdial) { + pri->pvts[chanpos]->exten[0] = '\0'; + } else { + /* Some PRI circuits are set up to send _no_ digits. Handle them as 's'. */ + pri->pvts[chanpos]->exten[0] = 's'; + pri->pvts[chanpos]->exten[1] = '\0'; + } + } + /* No number yet, but received "sending complete"? */ + if (e->ring.complete) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of Complete received\n"); pri->pvts[chanpos]->exten[0] = 's'; pri->pvts[chanpos]->exten[1] = '\0'; - } + } + } else { + /* Get called number */ + pri_make_callerid(pri, pri->pvts[chanpos]->dnid, sizeof(pri->pvts[chanpos]->dnid), e->ring.callednum, sizeof(e->ring.callednum), e->ring.calledplan, 0, pri->pvts[chanpos]->stripmsd); + pri_make_callerid(pri, pri->pvts[chanpos]->exten, sizeof(pri->pvts[chanpos]->exten), e->ring.callednum, sizeof(e->ring.callednum), e->ring.calledplan, 0, pri->pvts[chanpos]->stripmsd); + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + /* if we get the next digit we should stop the dialtone */ + if (!pri->overlapdial) { + // with overlapdial=no the exten is always prefixed by "s" + if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten + 1)) { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1); + } else { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } + } else { + if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten)) { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1); + } else { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } + } + } + } + /* Part 3: create channel, setup audio... */ + /* Set DNID on all incoming calls -- even immediate */ + if (!ast_strlen_zero(e->ring.callednum)) + strncpy(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid) - 1); /* Make sure extension exists (or in overlap dial mode, can exist) */ if ((pri->overlapdial && ast_canmatch_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) || ast_exists_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) { @@ -8893,19 +9282,36 @@ static void *pri_dchannel(void *vpri) res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law); if (res < 0) ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pri->pvts[chanpos]->channel); - res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + if (IS_DIGITAL(e->ring.ctype)) { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[chanpos]->law); + } else { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + } if (res < 0) ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pri->pvts[chanpos]->channel); - if (e->ring.complete || !pri->overlapdial) { + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + if (e->ring.complete || !pri->overlapdial) { /* Just announce proceeding */ pri->pvts[chanpos]->proceeding = 1; pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0); - } else { + } else { if (pri->switchtype != PRI_SWITCH_GR303_TMC) pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); else pri_answer(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); + } + } else { + /* BRI_NETWORK | BRI_NETWORK_PTMP */ + if (pri->overlapdial || (!strcasecmp(pri->pvts[chanpos]->exten, "s"))) { + /* send a SETUP_ACKNOWLEDGE */ + pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); + } else { + /* send an ALERTING ??? wtf */ + // pri_acknowledge(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); + pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0); + } } + /* overlapdial = yes and the extension can be valid */ /* Get the use_callingpres state */ pri->pvts[chanpos]->callingpres = e->ring.callingpres; @@ -8917,10 +9323,17 @@ static void *pri_dchannel(void *vpri) /* Set bearer and such */ pri_assign_bearer(crv, pri, pri->pvts[chanpos]); c = zt_new(crv, AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype); + if (c && (e->ring.lowlayercompat[0] > 0)) { + memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat)); + } pri->pvts[chanpos]->owner = &inuse; ast_log(LOG_DEBUG, "Started up crv %d:%d on bearer channel %d\n", pri->trunkgroup, crv->channel, crv->bearer->channel); } else { c = zt_new(pri->pvts[chanpos], AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype); + if (c && (e->ring.lowlayercompat[0] > 0)) { + memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat)); + } + zt_enable_ec(pri->pvts[chanpos]); /* XXX rethink */ } ast_mutex_unlock(&pri->pvts[chanpos]->lock); @@ -8928,6 +9341,16 @@ static void *pri_dchannel(void *vpri) if (!ast_strlen_zero(e->ring.callingsubaddr)) { pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr); } + if (!ast_strlen_zero(e->ring.callingnum)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingnum, sizeof(e->ring.callingnum), e->ring.callingplan, e->ring.callingpres, 0); + pbx_builtin_setvar_helper(c, "PRI_NETWORK_CID", tmpstr); + } + if (!ast_strlen_zero(e->ring.callingani)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingani, sizeof(e->ring.callingani), e->ring.callingplanuser, e->ring.callingpresuser, 0); + pbx_builtin_setvar_helper(c, "PRI_USER_CID", tmpstr); + } if (e->ring.ani2 >= 0) { snprintf(ani2str, 5, "%.2d", e->ring.ani2); pbx_builtin_setvar_helper(c, "ANI2", ani2str); @@ -8951,8 +9374,8 @@ static void *pri_dchannel(void *vpri) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (c && !ast_pthread_create(&threadid, &attr, ss_thread, c)) { if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n", - plancallingnum, S_OR(pri->pvts[chanpos]->exten, ""), + ast_verbose(VERBOSE_PREFIX_3 "Accepting overlap %s call from '%s' to '%s' on channel %d/%d, span %d\n", + pri->pvts[chanpos]->digital ? "data" : "voice", plancallingnum, S_OR(pri->pvts[chanpos]->exten, ""), pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); } else { ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n", @@ -8960,15 +9383,19 @@ static void *pri_dchannel(void *vpri) if (c) ast_hangup(c); else { - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION, -1); pri->pvts[chanpos]->call = NULL; } } pthread_attr_destroy(&attr); } else { + /* overlapdial = no */ ast_mutex_unlock(&pri->lock); /* Release PRI lock while we create the channel */ c = zt_new(pri->pvts[chanpos], AST_STATE_RING, 1, SUB_REAL, law, e->ring.ctype); + if (c && (e->ring.lowlayercompat[0] > 0)) { + memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat)); + } if (c) { char calledtonstr[10]; @@ -8995,26 +9422,43 @@ static void *pri_dchannel(void *vpri) ast_mutex_lock(&pri->lock); if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting call from '%s' to '%s' on channel %d/%d, span %d\n", - plancallingnum, pri->pvts[chanpos]->exten, + ast_verbose(VERBOSE_PREFIX_3 "Accepting %s call from '%s' to '%s' on channel %d/%d, span %d\n", + pri->pvts[chanpos]->digital ? "data" : "voice", e->ring.callingnum, pri->pvts[chanpos]->exten, pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); zt_enable_ec(pri->pvts[chanpos]); + if(!ast_strlen_zero(e->ring.callingsubaddr)) { + pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr); + } + if (!ast_strlen_zero(e->ring.callingnum)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingnum, sizeof(e->ring.callingnum), e->ring.callingplan, e->ring.callingpres, 0); + pbx_builtin_setvar_helper(c, "PRI_NETWORK_CID", tmpstr); + } + if (!ast_strlen_zero(e->ring.callingani)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr,sizeof(tmpstr), e->ring.callingani, sizeof(e->ring.callingani), e->ring.callingplanuser, e->ring.callingpresuser, 0); + pbx_builtin_setvar_helper(c, "PRI_USER_CID", e->ring.callednum); + } + if (!ast_strlen_zero(e->ring.useruserinfo)) { + pbx_builtin_setvar_helper(c, "UUI", e->ring.useruserinfo); + } } else { ast_mutex_lock(&pri->lock); ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n", pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION, -1); pri->pvts[chanpos]->call = NULL; } } } else { + /* invalid extension */ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Extension '%s' in context '%s' from '%s' does not exist. Rejecting call on channel %d/%d, span %d\n", pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context, pri->pvts[chanpos]->cid_num, pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED, -1); pri->pvts[chanpos]->call = NULL; pri->pvts[chanpos]->exten[0] = '\0'; } @@ -9023,9 +9467,9 @@ static void *pri_dchannel(void *vpri) ast_mutex_unlock(&pri->pvts[chanpos]->lock); } else { if (e->ring.flexible) - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION, -1); else - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, -1); } break; case PRI_EVENT_RINGING: @@ -9041,7 +9485,7 @@ static void *pri_dchannel(void *vpri) } else { ast_mutex_lock(&pri->pvts[chanpos]->lock); if (ast_strlen_zero(pri->pvts[chanpos]->dop.dialstr)) { - zt_enable_ec(pri->pvts[chanpos]); + // XXX zt_enable_ec(pri->pvts[chanpos]); pri->pvts[chanpos]->subs[SUB_REAL].needringing = 1; pri->pvts[chanpos]->alerting = 1; } else @@ -9073,9 +9517,16 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_PROGRESS: - /* Get chan value if e->e is not PRI_EVNT_RINGING */ + /* Get chan value if e->e is not PRI_EVENT_RINGING */ chanpos = pri_find_principle(pri, e->proceeding.channel); if (chanpos > -1) { + if ((pri->pvts[chanpos]->priindication_oob == 2) && (e->proceeding.cause == PRI_CAUSE_USER_BUSY)) { + /* received PROGRESS with cause BUSY, no inband callprogress wanted => hang up! */ + if (pri->pvts[chanpos]->owner) { + pri->pvts[chanpos]->owner->hangupcause = AST_CAUSE_USER_BUSY; + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } + } else { #ifdef PRI_PROGRESS_MASK if ((!pri->pvts[chanpos]->progress) || (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)) { #else @@ -9122,6 +9573,12 @@ static void *pri_dchannel(void *vpri) case PRI_EVENT_PROCEEDING: chanpos = pri_find_principle(pri, e->proceeding.channel); if (chanpos > -1) { + chanpos = pri_fixup_principle(pri, chanpos, e->proceeding.call); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Received PROCEEDING on channel %d/%d not in use on span %d\n", + PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), pri->span); + chanpos = -1; + } else { if (!pri->pvts[chanpos]->proceeding) { struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROCEEDING, }; @@ -9169,6 +9626,295 @@ static void *pri_dchannel(void *vpri) } } break; + case PRI_EVENT_SUSPEND_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + pri_suspend_reject(pri->pri, e->suspend_req.call, ""); + break; + } + chanpos = pri_find_principle(pri, e->suspend_req.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Suspend requested on unconfigured channel %d span %d\n", chanpos, pri->span); + chanpos = -1; + } + + if (chanpos > -1) { + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (pri->pvts[chanpos]->owner) { + if (ast_bridged_channel(pri->pvts[chanpos]->owner)) { + struct zt_suspended_call *zpc; + char tmpstr[256]; + zpc = malloc(sizeof(struct zt_suspended_call)); + if (!zpc) { + ast_log(LOG_ERROR, "unable to malloc zt_suspended_call\n"); + break; + } + strncpy(zpc->msn, pri->pvts[chanpos]->cid_num, sizeof(zpc->msn)); + strncpy(zpc->callid, e->suspend_req.callid, sizeof(zpc->callid)); + ast_masq_park_call(ast_bridged_channel(pri->pvts[chanpos]->owner), NULL, 0, &zpc->parked_at); + zpc->next = pri->suspended_calls; + pri->suspended_calls = zpc; + snprintf(tmpstr, sizeof(tmpstr), "Parked at %d", zpc->parked_at); + pri_suspend_acknowledge(pri->pri, e->suspend_req.call,tmpstr); + pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } else { + pri_suspend_reject(pri->pri, e->suspend_req.call, "cant park a non-bridge"); + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + break; + } + } else { + pri_suspend_reject(pri->pri, e->suspend_req.call, ""); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + break; + case PRI_EVENT_RESUME_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + break; + } + chanpos = pri_find_empty_chan(pri, 1); + if (chanpos < 0) { + pri_resume_reject(pri->pri, e->resume_req.call,"All channels busy"); + ast_log(LOG_WARNING, "Resume requested on odd channel number %d span %d\n", chanpos, pri->span); + chanpos = -1; + } else if (!pri->pvts[chanpos]) { + pri_resume_reject(pri->pri, e->resume_req.call,"General protection fault in module 0x0BRI"); + chanpos = -1; + } + + if (chanpos > -1) { + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (!pri->pvts[chanpos]->owner) { + struct zt_suspended_call *zpc, *zpcl; + int unparked=0; + char extenstr[255], temp[255]; + zpc = NULL; + zpcl = pri->suspended_calls; + while (zpcl) { + // ast_log(LOG_NOTICE, "zpc->parked_at %d zpcl->callid %s\n",zpcl->parked_at, zpcl->callid); + if (((strlen(zpcl->callid) == 0) && (strlen(e->resume_req.callid)==0)) || (!strcmp(zpcl->callid,e->resume_req.callid))) { + int law; + // found a parked call + snprintf(extenstr, sizeof(extenstr), "%d", zpcl->parked_at); + strncpy(pri->pvts[chanpos]->exten, extenstr, sizeof(pri->pvts[chanpos]->exten)); + // strncpy(pri->pvts[chanpos]->context, ast_parking_con(), sizeof(pri->pvts[chanpos]->context)); + pri->pvts[chanpos]->call = e->resume_req.call; + law = 1; + if (ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos]), law); + // uhh ohh...what shall we do without the bearer cap??? + law = ZT_LAW_ALAW; + res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set law on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + if (!pri->pvts[chanpos]->digital) { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + } else { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[chanpos]->law); + } + if (res < 0) + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + /* Start PBX */ + c = zt_new(pri->pvts[chanpos], AST_STATE_UP, 1, SUB_REAL, law, PRI_TRANS_CAP_SPEECH); + if (c) { + pri->pvts[chanpos]->owner = c; + pri->pvts[chanpos]->call = e->resume_req.call; + zt_enable_ec(pri->pvts[chanpos]); + zt_train_ec(pri->pvts[chanpos]); + } else { + ast_log(LOG_ERROR, "unable to start pbx\n"); + } + + if (zpc) { + zpc->next = zpcl->next; + free(zpcl); + zpcl = zpc->next; + } else { + // remove head + pri->suspended_calls = zpcl->next; + free(zpcl); + zpcl = pri->suspended_calls; + zpc = NULL; + } + unparked = 1; + snprintf(temp, sizeof(temp), "Unparked %s", extenstr); + pri_resume_acknowledge(pri->pri, e->resume_req.call, chanpos + 1, temp); + break; + } + zpc = zpcl; + if (zpcl) zpcl = zpcl->next; + } + if (!unparked) + pri_resume_reject(pri->pri, e->resume_req.call,"No suspended call to unpark!"); + } else { + pri_resume_reject(pri->pri, e->resume_req.call,"No suspended call to unpark!"); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + break; + case PRI_EVENT_HOLD_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + pri_hold_reject(pri->pri, e->hold_req.call); + break; + } + chanpos = pri_find_principle(pri, e->hold_req.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Hold requested on unconfigured channel %d span %d\n", chanpos, pri->span); + chanpos = -1; + } + if (chanpos > -1) { + // ast_log(LOG_NOTICE, "Hold request for channel number %d span %d\n", chanpos, pri->span); + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (pri->pvts[chanpos]->owner) { + struct zt_pvt *p = pri->pvts[chanpos]; + struct zt_holded_call *zhc; + int holdacked=0; + +// ast_log(LOG_NOTICE,"HOLD request from channel %s tei %d\n",p->owner->name, e->hold_req.tei); + if (ast_bridged_channel(p->owner)) { + zhc = malloc(sizeof(struct zt_holded_call)); + if (!zhc) { + ast_log(LOG_ERROR, "unable to malloc zt_holded_call\n"); + break; + } + memset(zhc, 0, sizeof(zhc)); + strncpy(zhc->msn, pri->pvts[chanpos]->cid_num, sizeof(zhc->msn)); + strncpy(zhc->uniqueid, ast_bridged_channel(p->owner)->uniqueid, sizeof(zhc->uniqueid)); + zhc->tei = e->hold_req.tei; + zhc->cref = e->hold_req.cref; + zhc->call = e->hold_req.call; + zhc->channel = p->owner; + zhc->alreadyhungup = 0; + zhc->bridge = ast_bridged_channel(p->owner); + zhc->next = pri->holded_calls; + pri->holded_calls = zhc; + + /* put channel on hold */ + ast_masq_hold_call(ast_bridged_channel(p->owner), p->owner); + + pri_hold_acknowledge(pri->pri, e->hold_req.call); + holdacked = 1; + p->call = NULL; // free the bchannel withouth destroying the call + p->tei = -1; + } else { + // cant hold a non-bridge,...yet + + // make a fake channel + + // masquerade + + // put on hold + pri_hold_reject(pri->pri, e->hold_req.call); + } + } else { + pri_hold_reject(pri->pri, e->hold_req.call); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } else { + pri_hold_reject(pri->pri, e->hold_req.call); + } + break; + case PRI_EVENT_RETRIEVE_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + break; + } + chanpos = pri_find_empty_chan(pri, 1); + if (chanpos < 0) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + ast_log(LOG_WARNING, "Retrieve requested on odd channel number %d span %d\n", chanpos, pri->span); + chanpos = -1; + break; + } else if (!pri->pvts[chanpos]) { + ast_log(LOG_WARNING, "Retrieve requested on unconfigured channel number %d span %d\n", chanpos, pri->span); + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + chanpos = -1; + break; + } + if (chanpos > -1) { + struct zt_holded_call *onhold = NULL; + int retrieved = 0; + int res = -1; + struct app_tmp *tmp; + pthread_attr_t attr; + int law; + + onhold = pri_get_callonhold(pri, e->retrieve_req.cref, e->retrieve_req.tei); + + if (!onhold) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + break; + } + ast_mutex_lock(&pri->pvts[chanpos]->lock); + // found a parked call + law = 1; + if (ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos]), law); + // uhh ohh...what shall we do without the bearer cap??? + law = ZT_LAW_ALAW; + res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set law on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + /* Start PBX */ + c = zt_new(pri->pvts[chanpos], AST_STATE_UP, 0, SUB_REAL, law, PRI_TRANS_CAP_SPEECH); + if (c) { + pri->pvts[chanpos]->owner = c; + pri->pvts[chanpos]->outgoing = 1; /* for not sending proceedings... */ + pri->pvts[chanpos]->call = e->retrieve_req.call; + pri->pvts[chanpos]->tei = e->retrieve_req.tei; + zt_enable_ec(pri->pvts[chanpos]); + zt_train_ec(pri->pvts[chanpos]); + } else { + ast_log(LOG_ERROR, "unable to start pbx\n"); + } + + retrieved = 1; + // ast_log(LOG_NOTICE, "sending RETRIEVE ACK on channel %d, span %d for tei %d cref %d\n",chanpos,pri->span, e->retrieve_req.tei, e->retrieve_req.cref); + pri_retrieve_acknowledge(pri->pri, e->retrieve_req.call, chanpos + 1); + + // the magic begins here: .... + tmp = malloc(sizeof(struct app_tmp)); + if (tmp) { + memset(tmp, 0, sizeof(struct app_tmp)); + strncpy(tmp->app, "holdedcall", sizeof(tmp->app) - 1); + strncpy(tmp->data, onhold->uniqueid, sizeof(tmp->data) - 1); + tmp->chan = c; + } + pri_destroy_callonhold(pri, onhold); + onhold = NULL; + + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) { + ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", c->name, strerror(errno)); + free(tmp); + ast_hangup(c); + retrieved = 0; + } + + if (!retrieved) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + } + } + break; + case PRI_EVENT_DISPLAY_RECEIVED: + ast_log(LOG_NOTICE, "DISPLAY IE: [ %s ] received\n",e->display.text); + chanpos = pri_find_principle(pri, e->display.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "odd channel number %d span %d\n", chanpos, pri->span); + chanpos = -1; + } + if (chanpos > -1) { + if (pri->pvts[chanpos]->owner) { + // ast_sendtext(pri->pvt[chanpos]->owner, e->display.text); + } + } + break; case PRI_EVENT_ANSWER: chanpos = pri_find_principle(pri, e->answer.channel); if (chanpos < 0) { @@ -9181,6 +9927,7 @@ static void *pri_dchannel(void *vpri) PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span); } else { ast_mutex_lock(&pri->pvts[chanpos]->lock); + pri->pvts[chanpos]->tei = e->answer.tei; /* Now we can do call progress detection */ /* We changed this so it turns on the DSP no matter what... progress or no progress. @@ -9210,11 +9957,16 @@ static void *pri_dchannel(void *vpri) ast_log(LOG_DEBUG, "Sent deferred digit string: %s\n", pri->pvts[chanpos]->dop.dialstr); pri->pvts[chanpos]->dop.dialstr[0] = '\0'; } else if (pri->pvts[chanpos]->confirmanswer) { - ast_log(LOG_DEBUG, "Waiting on answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel); + ast_log(LOG_DEBUG, "Waiting for answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel); + enable_dtmf_detect(pri->pvts[chanpos]); } else { + pri->pvts[chanpos]->dialing = 0; pri->pvts[chanpos]->subs[SUB_REAL].needanswer =1; /* Enable echo cancellation if it's not on already */ zt_enable_ec(pri->pvts[chanpos]); + zt_train_ec(pri->pvts[chanpos]); + /* stop ignoring inband dtmf */ + enable_dtmf_detect(pri->pvts[chanpos]); } #ifdef SUPPORT_USERUSER @@ -9271,20 +10023,29 @@ static void *pri_dchannel(void *vpri) ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup, cause %d\n", pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, e->hangup.cause); } else { - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); + pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1); pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; } if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - if (option_verbose > 2) + if ((pri->nodetype != BRI_CPE_PTMP) && (pri->nodetype != BRI_NETWORK_PTMP)) { + if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d on span %d since channel reported in use\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; - } - if (e->hangup.aoc_units > -1) + PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); + pri->pvts[chanpos]->resetting = 1; + } + } + if (e->hangup.aoc_units > -1) { + if (pri->pvts[chanpos]->owner) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr); + } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + } #ifdef SUPPORT_USERUSER if (pri->pvts[chanpos]->owner && !ast_strlen_zero(e->hangup.useruserinfo)) { @@ -9297,8 +10058,20 @@ static void *pri_dchannel(void *vpri) ast_mutex_unlock(&pri->pvts[chanpos]->lock); } else { - ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + struct zt_holded_call *onhold = NULL; + /* check calls on hold */ + onhold = pri_get_callonhold(pri, e->hangup.cref, e->hangup.tei); + + if (onhold) { + // ast_log(LOG_NOTICE, "hangup, found cref %d, tei %d\n",e->hangup.cref, e->hangup.tei); + pri_hangup(pri->pri, onhold->call, e->hangup.cause, -1); + pri_destroy_callonhold(pri, onhold); + onhold = NULL; + } else { + ast_log(LOG_NOTICE, "Hangup, did not find cref %d, tei %d\n",e->hangup.cref, e->hangup.tei); + ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n", + PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } } } break; @@ -9308,15 +10081,23 @@ static void *pri_dchannel(void *vpri) case PRI_EVENT_HANGUP_REQ: chanpos = pri_find_principle(pri, e->hangup.channel); if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } else { + if (pri->nodetype == BRI_NETWORK_PTMP) { + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause, -1); + } else { + ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n", + PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } + } else if ((pri->pvts[chanpos]->priindication_oob != 2) || (!e->hangup.inband_progress) || (!pri->pvts[chanpos]->outgoing)) { + /* dont hang up if we want to hear inband call progress */ chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); if (chanpos > -1) { ast_mutex_lock(&pri->pvts[chanpos]->lock); if (pri->pvts[chanpos]->realcall) pri_hangup_all(pri->pvts[chanpos]->realcall, pri); else if (pri->pvts[chanpos]->owner) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", e->hangup.cause); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "PRI_CAUSE", tmpstr); pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; if (pri->pvts[chanpos]->owner->_state == AST_STATE_UP) pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; @@ -9343,16 +10124,86 @@ static void *pri_dchannel(void *vpri) if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + if (e->hangup.aoc_units > -1) { + if (pri->pvts[chanpos]->owner) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr); + } + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + } + if (pri->nodetype == BRI_NETWORK_PTMP) { + // check for bri transfers, not everybody uses ECT... + if (pri->pvts[chanpos]->owner) { + // find on hold call + struct zt_holded_call *onhold = NULL; + struct ast_channel *transferee = NULL; + int transfer_ok = 0; + + onhold = pri_get_callonhold(pri, -1, e->hangup.tei); + + if (onhold) { + if (pri->pvts[chanpos]->pritransfer == 2) { + if (((pri->pvts[chanpos]->owner->_state != AST_STATE_RING) && (pri->pvts[chanpos]->owner->_state != AST_STATE_RESERVED)) || ((!ast_strlen_zero(pri->pvts[chanpos]->exten)) && (strncasecmp(pri->pvts[chanpos]->exten, "s", sizeof(pri->pvts[chanpos]->exten))))) { + transferee = ast_get_holded_call(onhold->uniqueid); + + if (transferee) { + if (pri->pvts[chanpos]->owner->_state == AST_STATE_RINGING) { + ast_indicate(transferee, AST_CONTROL_RINGING); + } + + pri->pvts[chanpos]->owner->_softhangup &= ~AST_SOFTHANGUP_DEV; + + ast_mutex_unlock(&transferee->lock); + if (ast_channel_masquerade(pri->pvts[chanpos]->owner, transferee)) { + ast_log(LOG_WARNING, "unable to masquerade\n"); + } else { + /* beware of zombies!!! */ + ast_set_flag(transferee, AST_FLAG_ZOMBIE); + pri->pvts[chanpos]->owner = NULL; + pri->pvts[chanpos]->tei = -1; + transfer_ok = 1; + } + } + } + } else if (pri->pvts[chanpos]->pritransfer == 0) { + ast_log(LOG_NOTICE, "killing channel %s \n", onhold->uniqueid); + ast_retrieve_call_to_death(onhold->uniqueid); + transfer_ok = 1; + } else if (pri->pvts[chanpos]->pritransfer == 1) { + /* we use ECT transfers, so just ignore this */ + transfer_ok = 0; + } + + if (transfer_ok) { + onhold->alreadyhungup = 1; + pri_hangup(pri->pri, onhold->call, e->hangup.cause, -1); + onhold = NULL; + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + break; + } else { + pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1); + pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; + } + } + } } else { - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); + pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1); pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; } if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - if (option_verbose > 2) + if ((pri->nodetype != BRI_CPE_PTMP) && (pri->nodetype != BRI_NETWORK_PTMP)) { + if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d span %d since channel reported in use\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; + pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); + pri->pvts[chanpos]->resetting = 1; + } } #ifdef SUPPORT_USERUSER @@ -9366,9 +10217,39 @@ static void *pri_dchannel(void *vpri) ast_mutex_unlock(&pri->pvts[chanpos]->lock); } else { - ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + if (pri->nodetype != BRI_NETWORK_PTMP) { + ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } else { + // check holded_calls!!! + struct zt_holded_call *onhold = NULL; + + onhold = pri_get_callonhold(pri, e->hangup.cref, e->hangup.tei); + + if (onhold) { + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause, -1); + ast_retrieve_call_to_death(onhold->uniqueid); + pri_destroy_callonhold(pri, onhold); + onhold = NULL; + } else { + ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } + } } } + if ((chanpos > -1) && (pri->pvts[chanpos]->owner) && (pri->pvts[chanpos]->priindication_oob == 2) && (e->hangup.inband_progress) && (pri->pvts[chanpos]->outgoing)) { + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (e->hangup.aoc_units > -1) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + } + pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; + ast_channel_setwhentohangup(pri->pvts[chanpos]->owner, 5); + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } break; case PRI_EVENT_HANGUP_ACK: chanpos = pri_find_principle(pri, e->hangup.channel); @@ -9380,6 +10261,7 @@ static void *pri_dchannel(void *vpri) if (chanpos > -1) { ast_mutex_lock(&pri->pvts[chanpos]->lock); pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; pri->pvts[chanpos]->resetting = 0; if (pri->pvts[chanpos]->owner) { if (option_verbose > 2) @@ -9396,7 +10278,9 @@ static void *pri_dchannel(void *vpri) #endif ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } } + } } break; case PRI_EVENT_CONFIG_ERR: @@ -9486,10 +10370,22 @@ static void *pri_dchannel(void *vpri) ast_mutex_lock(&pri->pvts[chanpos]->lock); switch (e->notify.info) { case PRI_NOTIFY_REMOTE_HOLD: + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + ast_log(LOG_DEBUG, "Received REMOTE_HOLD notification on NETWORK channel. Starting MoH\n"); + ast_moh_start(ast_bridged_channel(pri->pvts[chanpos]->owner), NULL, pri->pvts[chanpos]->mohinterpret); + } else { + ast_log(LOG_DEBUG, "Received REMOTE_HOLD notification on CPE channel. Not Starting MoH\n"); + } f.subclass = AST_CONTROL_HOLD; zap_queue_frame(pri->pvts[chanpos], &f, pri); break; case PRI_NOTIFY_REMOTE_RETRIEVAL: + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + ast_log(LOG_DEBUG, "Received REMOTE_RETRIEVAL notification on NETWORK channel. Stopping MoH\n"); + ast_moh_stop(ast_bridged_channel(pri->pvts[chanpos]->owner)); + } else { + ast_log(LOG_DEBUG, "Received REMOTE_RETRIEVAL notification on CPE channel.\n"); + } f.subclass = AST_CONTROL_UNHOLD; zap_queue_frame(pri->pvts[chanpos], &f, pri); break; @@ -9497,6 +10393,77 @@ static void *pri_dchannel(void *vpri) ast_mutex_unlock(&pri->pvts[chanpos]->lock); } break; + case PRI_EVENT_FACILITY: + if (e->facility.operation == 0x06) { + struct ast_channel *chan = NULL; + struct zt_holded_call *onhold = NULL; + if (option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "ECT requested by TEI %d for cref %d\n", e->facility.tei, e->facility.cref); + } + /* search for cref/tei in held calls */ + onhold = pri_get_callonhold(pri, e->facility.cref, e->facility.tei); + if (onhold) { + chan = ast_get_holded_call(onhold->uniqueid); + onhold->alreadyhungup = 1; + onhold = NULL; + if (!chan) { + /* hang up */ + pri_hangup(pri->pri, e->facility.call, 16, -1); + break; + } + } else { + /* unknown cref/tei */ + ast_log(LOG_WARNING, "did not find call on hold for cref %d tei %d\n", e->facility.tei, e->facility.cref); + /* hang up */ + pri_hangup(pri->pri, e->facility.call, 16, -1); + break; + } + + /* find an active call for the same tei */ + chanpos = pri_find_tei(pri, e->facility.call, e->facility.tei); + if (chanpos < 0) { + /* did not find active call, hangup call on hold */ + if (chan) { + ast_hangup(chan); + chan = NULL; + } + } else { + ast_mutex_lock(&pri->pvts[chanpos]->lock); + /* transfer */ + if (pri->pvts[chanpos]->owner) { + if (option_verbose > 3) { + ast_verbose(VERBOSE_PREFIX_3 "ECT: found %s on channel %d for tei %d\n", pri->pvts[chanpos]->owner->name ,chanpos, e->facility.tei); + } + /* pass callprogress if the channel is not up yet */ + if (pri->pvts[chanpos]->owner->_state == AST_STATE_RINGING) { + ast_indicate(chan, AST_CONTROL_RINGING); + } + /* unlock the channel we removed from hold */ + ast_mutex_unlock(&chan->lock); + if (ast_channel_masquerade(pri->pvts[chanpos]->owner, chan)) { + ast_log(LOG_WARNING, "unable to masquerade\n"); + } else { + /* beware of zombies !!! */ + ast_set_flag(chan, AST_FLAG_ZOMBIE); + // chan->zombie = 1; + } + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + /* disconnect */ + pri_hangup(pri->pri, e->facility.call, 16, -1); + } else if (e->facility.operation == 0x0D) { + ast_log(LOG_NOTICE, "call deflection to %s requested.\n", e->facility.forwardnum); + ast_mutex_lock(&pri->pvts[chanpos]->lock); + /* transfer */ + if (pri->pvts[chanpos]->owner) { + snprintf(pri->pvts[chanpos]->owner->call_forward, sizeof(pri->pvts[chanpos]->owner->call_forward), "Local/%s@%s", e->facility.forwardnum, pri->pvts[chanpos]->owner->context); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } else { + ast_log(LOG_WARNING, "Unknown facility operation %#x requested.\n", e->facility.operation); + } + break; default: ast_log(LOG_DEBUG, "Event: %d\n", e->e); } @@ -9558,7 +10525,7 @@ static int start_pri(struct zt_pri *pri) pri->fds[i] = -1; return -1; } - pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype); + pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype, pri->span); /* Force overlap dial if we're doing GR-303! */ if (pri->switchtype == PRI_SWITCH_GR303_TMC) pri->overlapdial = 1; @@ -9626,39 +10593,77 @@ static char *complete_span_5(const char static int handle_pri_set_debug_file(int fd, int argc, char **argv) { - int myfd; + int myfd, x, d; + int span; + + if (argc < 6) + return RESULT_SHOWUSAGE; if (!strncasecmp(argv[1], "set", 3)) { - if (argc < 5) + if (argc < 7) return RESULT_SHOWUSAGE; - if (ast_strlen_zero(argv[4])) + if (!argv[4] || ast_strlen_zero(argv[4])) return RESULT_SHOWUSAGE; + if (!argv[5]) + return RESULT_SHOWUSAGE; + + if (!argv[6] || ast_strlen_zero(argv[6])) + return RESULT_SHOWUSAGE; + + span = atoi(argv[6]); + if ((span < 1) && (span > NUM_SPANS)) { + return RESULT_SUCCESS; + } + + myfd = open(argv[4], O_CREAT|O_WRONLY, 0600); if (myfd < 0) { ast_cli(fd, "Unable to open '%s' for writing\n", argv[4]); return RESULT_SUCCESS; } - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) - close(pridebugfd); - - pridebugfd = myfd; - ast_copy_string(pridebugfilename,argv[4],sizeof(pridebugfilename)); - - ast_mutex_unlock(&pridebugfdlock); - - ast_cli(fd, "PRI debug output will be sent to '%s'\n", argv[4]); + for (x=0; x < NUM_SPANS; x++) { + ast_mutex_lock(&pris[x].lock); + + if (pris[x].span == span) { + if (pris[x].debugfd >= 0) + close(pris[x].debugfd); + pris[x].debugfd = myfd; + for (d=0; d < NUM_DCHANS; d++) { + if (pris[x].dchans[d]) + pri_set_debug_fd(pris[x].dchans[d], myfd); + } + } + ast_mutex_unlock(&pris[x].lock); + } + + ast_cli(fd, "PRI debug output for span %d will be sent to '%s'\n", span, argv[4]); } else { + if (!argv[5] || ast_strlen_zero(argv[5])) + return RESULT_SHOWUSAGE; /* Assume it is unset */ - ast_mutex_lock(&pridebugfdlock); - close(pridebugfd); - pridebugfd = -1; - ast_cli(fd, "PRI debug output to file disabled\n"); - ast_mutex_unlock(&pridebugfdlock); + span = atoi(argv[5]); + if ((span < 1) && (span > NUM_SPANS)) { + return RESULT_SUCCESS; + } + + for (x=0; x < NUM_SPANS; x++) { + ast_mutex_lock(&pris[x].lock); + + if (pris[x].span == span) { + if (pris[x].debugfd >= 0) + close(pris[x].debugfd); + pris[x].debugfd = -1; + for (d=0; d < NUM_DCHANS; d++) { + if (pris[x].dchans[d]) + pri_set_debug_fd(pris[x].dchans[d], -1); + } + } + ast_mutex_unlock(&pris[x].lock); + } + + ast_cli(fd, "PRI debug output to file for span %d disabled\n", span); } return RESULT_SUCCESS; @@ -9692,6 +10697,7 @@ static int handle_pri_debug(int fd, int + static int handle_pri_no_debug(int fd, int argc, char *argv[]) { int span; @@ -9841,10 +10847,6 @@ static int handle_pri_show_debug(int fd, } } - ast_mutex_lock(&pridebugfdlock); - if (pridebugfd >= 0) - ast_cli(fd, "Logging PRI debug to file %s\n", pridebugfilename); - ast_mutex_unlock(&pridebugfdlock); if (!count) ast_cli(fd, "No debug set or no PRI running\n"); @@ -9871,6 +10873,18 @@ static const char pri_show_spans_help[] "Usage: pri show spans\n" " Displays PRI Information\n"; +static char bri_debug_help[] = + "Usage: bri debug span \n" + " Enables debugging on a given BRI span\n"; + +static char bri_no_debug_help[] = + "Usage: bri no debug span \n" + " Disables debugging on a given BRI span\n"; + +static char bri_really_debug_help[] = + "Usage: bri intensive debug span \n" + " Enables debugging down to the Q.921 level\n"; + static struct ast_cli_entry zap_pri_cli[] = { { { "pri", "debug", "span", NULL }, handle_pri_debug, "Enables PRI debugging on a span", @@ -9895,6 +10909,15 @@ static struct ast_cli_entry zap_pri_cli[ { { "pri", "show", "debug", NULL }, handle_pri_show_debug, "Displays current PRI debug settings" }, + { { "bri", "debug", "span", NULL }, handle_pri_debug, + "Enables BRI debugging on a span", bri_debug_help, complete_span_4 }, + + { { "bri", "no", "debug", "span", NULL }, handle_pri_no_debug, + "Disables BRI debugging on a span", bri_no_debug_help, complete_span_5 }, + + { { "bri", "intense", "debug", "span", NULL }, handle_pri_really_debug, + "Enables REALLY INTENSE BRI debugging", bri_really_debug_help, complete_span_5 }, + { { "pri", "set", "debug", "file", NULL }, handle_pri_set_debug_file, "Sends PRI debug output to the specified file" }, @@ -9902,8 +10925,76 @@ static struct ast_cli_entry zap_pri_cli[ handle_pri_set_debug_file, "Ends PRI debug output to file" }, }; +static char *zapCD_tdesc = "Call Deflection"; +static char *zapCD_app = "zapCD"; +static char *zapCD_synopsis = "Call Deflection"; + +static int app_zapCD(struct ast_channel *chan, void *data) +{ + struct zt_pvt *p = chan->tech_pvt; + + if((!p->pri) || (!p->pri->pri)) { + return -1; + } + + if(!data) { + ast_log(LOG_WARNING, "zapCD wants a number to deflect to\n"); + return -1; + } + return pri_deflect(p->pri->pri, p->call, data); +} + +static char *zapInband_tdesc = "Inband Call Progress (pre-answer)"; +static char *zapInband_app = "zapInband"; +static char *zapInband_synopsis = "Inband Call Progress"; + +static int app_zapInband(struct ast_channel *chan, void *data) +{ + struct zt_pvt *p = chan->tech_pvt; + + return pri_acknowledge(p->pri->pri, p->call, PVT_TO_CHANNEL(p), 1); +} + #endif /* HAVE_PRI */ +static int app_zapEC(struct ast_channel *chan, void *data) +{ + int res=-1; + struct zt_pvt *p = NULL; + + if (!data) { + ast_log(LOG_WARNING, "zapEC requires one argument (on | off)\n"); + } + if (chan && !strcasecmp("ZAP",chan->tech->type)) { + p = chan->tech_pvt; + if (!p) return res; + if (!strcasecmp("on",(char *)data)) { + zt_enable_ec(p); + res = 0; + if (option_verbose > 3) { + ast_verbose(VERBOSE_PREFIX_3 "Enabled echo cancelation on channel %s.\n", chan->name); + } + } else if (!strcasecmp("off",(char *)data)) { + zt_disable_ec(p); + res = 0; + if (option_verbose > 3) { + ast_verbose(VERBOSE_PREFIX_3 "Disabled echo cancelation on channel %s.\n", chan->name); + } + } else { + ast_log(LOG_WARNING, "Unknown argument %s to zapEC\n", (char *)data); + } + } else { + ast_log(LOG_WARNING, "zapNoEC only works on ZAP channels, check your extensions.conf!\n"); + res = 0; + } + + return res; +} + +static char *zapEC_tdesc = "Enable/disable Echo cancelation"; +static char *zapEC_app = "zapEC"; +static char *zapEC_synopsis = "Enable/Disable Echo Cancelation on a Zap channel"; + static int zap_destroy_channel(int fd, int argc, char **argv) { int channel; @@ -10484,8 +11575,11 @@ static int __unload_module(void) } ast_cli_unregister_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry)); ast_unregister_application(zap_send_keypad_facility_app); + ast_unregister_application(zapCD_app); + ast_unregister_application(zapInband_app); #endif ast_cli_unregister_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry)); + ast_unregister_application(zapEC_app); ast_manager_unregister( "ZapDialOffhook" ); ast_manager_unregister( "ZapHangup" ); ast_manager_unregister( "ZapTransfer" ); @@ -10987,6 +12081,22 @@ static int process_zap(struct zt_chan_co confp->chan.sig = SIG_GR303FXSKS; confp->chan.radio = 0; confp->pri.nodetype = PRI_CPE; + } else if (!strcasecmp(v->value, "bri_net_ptmp")) { + confp->chan.radio = 0; + confp->chan.sig = SIG_PRI; + confp->pri.nodetype = BRI_NETWORK_PTMP; + } else if (!strcasecmp(v->value, "bri_cpe_ptmp")) { + confp->chan.sig = SIG_PRI; + confp->chan.radio = 0; + confp->pri.nodetype = BRI_CPE_PTMP; + } else if (!strcasecmp(v->value, "bri_net")) { + confp->chan.radio = 0; + confp->chan.sig = SIG_PRI; + confp->pri.nodetype = BRI_NETWORK; + } else if (!strcasecmp(v->value, "bri_cpe")) { + confp->chan.sig = SIG_PRI; + confp->chan.radio = 0; + confp->pri.nodetype = BRI_CPE; #endif } else { ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value); @@ -11099,9 +12209,21 @@ static int process_zap(struct zt_chan_co confp->chan.priindication_oob = 1; else if (!strcasecmp(v->value, "inband")) confp->chan.priindication_oob = 0; + else if (!strcasecmp(v->value, "passthrough")) + confp->chan.priindication_oob = 2; else - ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband' or 'outofband' at line %d\n", + ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband', 'outofband' or 'passthrough' at line %d\n", v->value, v->lineno); + } else if (!strcasecmp(v->name, "pritransfer")) { + if (!strcasecmp(v->value, "no")) + confp->chan.pritransfer = 0; + else if (!strcasecmp(v->value, "ect")) + confp->chan.pritransfer = 1; + else if (!strcasecmp(v->value, "hangup")) + confp->chan.pritransfer = 2; + else + ast_log(LOG_WARNING, "'%s' is not a valid pri transfer value, should be 'no' , 'ect' or 'hangup' at line %d\n", + v->value, v->lineno); } else if (!strcasecmp(v->name, "priexclusive")) { confp->chan.priexclusive = ast_true(v->value); } else if (!strcasecmp(v->name, "internationalprefix")) { @@ -11114,6 +12236,10 @@ static int process_zap(struct zt_chan_co ast_copy_string(confp->pri.privateprefix, v->value, sizeof(confp->pri.privateprefix)); } else if (!strcasecmp(v->name, "unknownprefix")) { ast_copy_string(confp->pri.unknownprefix, v->value, sizeof(confp->pri.unknownprefix)); + } else if (!strcasecmp(v->name, "nocid")) { + ast_copy_string(confp->pri.nocid, v->value, sizeof(confp->pri.nocid)); + } else if (!strcasecmp(v->name, "withheldcid")) { + ast_copy_string(confp->pri.withheldcid, v->value, sizeof(confp->pri.withheldcid)); } else if (!strcasecmp(v->name, "resetinterval")) { if (!strcasecmp(v->value, "never")) confp->pri.resetinterval = -1; @@ -11130,6 +12256,8 @@ static int process_zap(struct zt_chan_co ast_copy_string(confp->pri.idleext, v->value, sizeof(confp->pri.idleext)); } else if (!strcasecmp(v->name, "idledial")) { ast_copy_string(confp->pri.idledial, v->value, sizeof(confp->pri.idledial)); + } else if (!strcasecmp(v->name, "pritrustusercid")) { + confp->pri.usercid = ast_true(v->value); } else if (!strcasecmp(v->name, "overlapdial")) { confp->pri.overlapdial = ast_true(v->value); } else if (!strcasecmp(v->name, "pritimer")) { @@ -11431,6 +12559,7 @@ static int setup_zap(int reload) #ifdef HAVE_PRI if (!reload) { for (x = 0; x < NUM_SPANS; x++) { + pris[x].debugfd = -1; if (pris[x].pvts[0]) { if (start_pri(pris + x)) { ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1); @@ -11478,7 +12607,10 @@ static int load_module(void) ast_string_field_init(&inuse, 16); ast_string_field_set(&inuse, name, "GR-303InUse"); ast_cli_register_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry)); + ast_register_application(zapCD_app, app_zapCD, zapCD_synopsis, zapCD_tdesc); + ast_register_application(zapInband_app, app_zapInband, zapInband_synopsis, zapInband_tdesc); #endif + ast_register_application(zapEC_app, app_zapEC, zapEC_synopsis, zapEC_tdesc); ast_cli_register_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry)); memset(round_robin, 0, sizeof(round_robin)); @@ -11512,6 +12644,7 @@ static int zt_sendtext(struct ast_channe float scont = 0.0; int index; + index = zt_get_index(c, p, 0); if (index < 0) { ast_log(LOG_WARNING, "Huh? I don't exist?\n"); --- a/configs/zapata.conf.sample +++ b/configs/zapata.conf.sample @@ -123,9 +123,20 @@ switchtype=national ; ; outofband: Signal Busy/Congestion out of band with RELEASE/DISCONNECT ; inband: Signal Busy/Congestion using in-band tones +; passthrough: Listen to the telco ; ; priindication = outofband ; +; PRI/BRI transfers (HOLD -> SETUP -> ECT/Hangup) +; +; Configure how transfers are initiated. ECT should be preferred +; +; no: no transfers allowed (results in hangup) +; ect: use ECT (facility) +; hangup: transfer on hangup (if your phones dont support ECT) +; +; pritransfer = ect +; ; If you need to override the existing channels selection routine and force all ; PRI channels to be marked as exclusively selected, set this to yes. ; priexclusive = yes --- a/main/channel.c +++ b/main/channel.c @@ -4209,6 +4209,10 @@ enum ast_bridge_result ast_channel_bridg c1->name, c1->_bridge->name); return -1; } + + if (IS_DIGITAL(c0->transfercapability) || IS_DIGITAL(c1->transfercapability)) { + config->flags = 0; + } /* Stop if we're a zombie or need a soft hangup */ if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) || --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -77,6 +77,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #ifdef HAVE_PRI #include #endif +#ifdef HAVE_GSMAT +#include +#endif #include "asterisk/lock.h" #include "asterisk/channel.h" @@ -187,6 +190,7 @@ static const char config[] = "zapata.con #define SIG_FXOGS ZT_SIG_FXOGS #define SIG_FXOKS ZT_SIG_FXOKS #define SIG_PRI ZT_SIG_CLEAR +#define SIG_GSM (0x100000 | ZT_SIG_CLEAR) #define SIG_SF ZT_SIG_SF #define SIG_SFWINK (0x0100000 | ZT_SIG_SF) #define SIG_SF_FEATD (0x0200000 | ZT_SIG_SF) @@ -236,6 +240,8 @@ static int matchdigittimeout = 3000; /*! \brief Protect the interface list (of zt_pvt's) */ AST_MUTEX_DEFINE_STATIC(iflock); +static char gsm_modem_pin[20]; +static char gsm_modem_exten[AST_MAX_EXTENSION]; static int ifcount = 0; @@ -251,7 +257,7 @@ static int restart_monitor(void); static enum ast_bridge_result zt_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); -static int zt_sendtext(struct ast_channel *c, const char *text); +static int zt_sendtext(struct ast_channel *c, const char *dest, const char *text, int ispdu); /*! \brief Avoid the silly zt_getevent which ignores a bunch of events */ @@ -366,6 +372,19 @@ struct zt_pri { int debugfd; }; +#ifdef HAVE_GSMAT +struct zt_gsm { + pthread_t master; + ast_mutex_t lock; /* Mutex */ + int fd; + int span; + struct gsm_modul *modul; + char pin[256]; + int available; + char exten[AST_MAX_EXTENSION]; /* Where to idle extra calls */ + struct zt_pvt *pvt; +}; +#endif static struct zt_pri pris[NUM_SPANS]; @@ -394,6 +413,7 @@ struct zt_pri; #define POLARITY_REV 1 + static struct zt_distRings drings; struct distRingData { @@ -605,6 +625,9 @@ static struct zt_pvt { int prioffset; int logicalspan; #endif +#ifdef HAVE_GSMAT + struct zt_gsm gsm; +#endif int polarity; int dsp_features; char begindigit; @@ -711,7 +734,7 @@ static struct zt_chan_conf zt_chan_conf_ static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause); static int zt_digit_begin(struct ast_channel *ast, char digit); static int zt_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int zt_sendtext(struct ast_channel *c, const char *text); +static int zt_sendtext(struct ast_channel *c, const char *dest, const char *text, int ispdu); static int zt_call(struct ast_channel *ast, char *rdest, int timeout); static int zt_hangup(struct ast_channel *ast); static int zt_answer(struct ast_channel *ast); @@ -1370,6 +1393,8 @@ static char *zap_sig2str(int sig) return "GR-303 with FXOKS"; case SIG_GR303FXSKS: return "GR-303 with FXSKS"; + case SIG_GSM: + return "GSM"; case 0: return "Pseudo"; default: @@ -1791,7 +1816,7 @@ static inline int zt_confmute(struct zt_ { int x, y, res; x = muted; - if (p->sig == SIG_PRI) { + if ((p->sig == SIG_PRI) || (p->sig == SIG_GSM)) { y = 1; res = ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &y); if (res) @@ -2206,6 +2231,25 @@ static int zt_call(struct ast_channel *a p->dialdest[0] = '\0'; disable_dtmf_detect(p); break; + case SIG_GSM: +#ifdef HAVE_GSMAT + if (p->gsm.modul) { + c = strchr(dest, '/'); + if (c) + c++; + else + c = dest; + ast_mutex_lock(&p->gsm.lock); + if (gsm_dial(p->gsm.modul, p->use_callingpres ? ast->cid.cid_pres : 0, c)) { + ast_log(LOG_WARNING, "dialing failed on channel %d\n", p->channel); + ast_mutex_unlock(&p->gsm.lock); + ast_mutex_unlock(&p->lock); + return -1; + } + ast_mutex_unlock(&p->gsm.lock); + } +#endif + break; default: ast_log(LOG_DEBUG, "not yet implemented\n"); ast_mutex_unlock(&p->lock); @@ -2845,7 +2889,13 @@ static int zt_hangup(struct ast_channel } } #endif - if (p->sig && (p->sig != SIG_PRI)) +#ifdef HAVE_GSMAT + if (p->gsm.modul) { + if (!p->alreadyhungup) + gsm_hangup(p->gsm.modul); + } +#endif + if (p->sig && (p->sig != SIG_PRI) && (p->sig != SIG_GSM)) res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK); if (res < 0) { ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name); @@ -3022,6 +3072,13 @@ static int zt_answer(struct ast_channel zt_train_ec(p); break; #endif +#ifdef HAVE_GSMAT + case SIG_GSM: + if (p->gsm.modul) { + gsm_answer(p->gsm.modul); + } + break; +#endif case 0: ast_mutex_unlock(&p->lock); return 0; @@ -7384,6 +7441,10 @@ static int pri_create_spanmap(int span, #endif +#ifdef HAVE_GSMAT +static void *gsm_dchannel(void *vgsm); +#endif + static struct zt_pvt *mkintf(int channel, struct zt_chan_conf conf, struct zt_pri *pri, int reloading) { /* Make a zt_pvt structure for this interface (or CRV if "pri" is specified) */ @@ -7612,6 +7673,37 @@ static struct zt_pvt *mkintf(int channel tmp->prioffset = 0; } #endif +#ifdef HAVE_GSMAT + if (conf.chan.sig == SIG_GSM) { + struct zt_bufferinfo bi; + ast_mutex_init(&tmp->gsm.lock); + strncpy(tmp->gsm.pin, gsm_modem_pin, sizeof(tmp->gsm.pin) - 1); + strncpy(tmp->gsm.exten, gsm_modem_exten, sizeof(tmp->gsm.exten) - 1); + tmp->gsm.available = 0; + snprintf(fn, sizeof(fn), "%d", channel + 1); + /* Open non-blocking */ + tmp->gsm.fd = zt_open(fn); + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + bi.numbufs = 16; + bi.bufsize = 1024; + if (ioctl(tmp->gsm.fd, ZT_SET_BUFINFO, &bi)) { + ast_log(LOG_ERROR, "Unable to set buffer info on channel '%s': %s\n", fn, strerror(errno)); + return NULL; + } + tmp->gsm.pvt = tmp; + tmp->gsm.span = tmp->span; + tmp->gsm.modul = gsm_new(tmp->gsm.fd, 0, tmp->gsm.pin, tmp->span, tmp->channel); + if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, tmp->channel)) { + ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d: %s\n", tmp->channel, strerror(errno)); + destroy_zt_pvt(&tmp); + return NULL; + } + if (ast_pthread_create(&tmp->gsm.master, NULL, gsm_dchannel, &tmp->gsm)) { + zt_close(tmp->gsm.fd); + } + } +#endif } else { conf.chan.sig = tmp->sig; conf.chan.radio = tmp->radio; @@ -7895,6 +7987,12 @@ static inline int available(struct zt_pv return 1; } #endif +#ifdef HAVE_GSMAT + if (p->gsm.modul) { + return gsm_available(p->gsm.modul); + } + +#endif if (!(p->radio || (p->oprmode < 0))) { if (!p->sig || (p->sig == SIG_FXSLS)) @@ -8249,6 +8347,235 @@ next: return tmp; } +#ifdef HAVE_GSMAT +static int zt_reset_span(int span, int sleep) { + int ctl; + int res; + + ctl = open("/dev/zap/ctl", O_RDWR); + if (ctl < 0) { + ast_log(LOG_WARNING, "Unable to open /dev/zap/ctl: %s\n", strerror(errno)); + return -1; + } + ast_verbose(VERBOSE_PREFIX_2 "Shutting down span %d. Please wait...\n", span); + res = ioctl(ctl, ZT_SHUTDOWN, &span); + if (res) { + ast_log(LOG_WARNING, "error shutting down span %d\n", span); + return -1; + } + usleep(sleep * 1000); + ast_verbose(VERBOSE_PREFIX_2 "Starting up span %d. Please wait...\n", span); + res = ioctl(ctl, ZT_STARTUP, &span); + if (res) { + ast_log(LOG_WARNING, "error starting up span %d\n", span); + return -1; + } + ast_verbose(VERBOSE_PREFIX_2 "Reset of span %d completed.\n", span); + return 0; +} + + +static void handle_gsm_event(struct zt_gsm *gsm, gsm_event *e) +{ + struct ast_channel *c = NULL; + int law = ZT_LAW_ALAW; + int res = 0; + + switch(e->e) { + case GSM_EVENT_DCHAN_UP: + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "GSM Span %d registered to network!\n", gsm->span); + gsm->available = 1; + break; + case GSM_EVENT_DCHAN_DOWN: + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "GSM Span %d unregistered from network!\n", gsm->span); + gsm->available = 0; +/* ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->alreadyhungup = 1; + if (gsm->pvt->owner) { + gsm->pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } + ast_mutex_unlock(&gsm->pvt->lock); */ + break; + case GSM_EVENT_RING: + ast_mutex_lock(&gsm->pvt->lock); + if (!ast_strlen_zero(e->ring.callingnum)) { + strncpy(gsm->pvt->cid_num, e->ring.callingnum, sizeof(gsm->pvt->cid_num) - 1); + } else { + strncpy(gsm->pvt->cid_name, "CID withheld", sizeof(gsm->pvt->cid_name)); + } + if (!ast_strlen_zero(gsm->exten)) { + strncpy(gsm->pvt->exten, gsm->exten, sizeof(gsm->pvt->exten) - 1); + } else { + gsm->pvt->exten[0] = 's'; + gsm->pvt->exten[1] = '\0'; + } + c = zt_new(gsm->pvt, AST_STATE_RING, 1, SUB_REAL, ZT_LAW_ALAW, AST_TRANS_CAP_SPEECH); + if (c) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Ring on channel %d (from %s to %s)\n", e->ring.channel, e->ring.callingnum, gsm->exten); + gsm->pvt->owner = c; + if (ioctl(gsm->pvt->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", gsm->pvt->channel, law); + res = zt_setlaw(gsm->pvt->subs[SUB_REAL].zfd, law); + res = set_actual_gain(gsm->pvt->subs[SUB_REAL].zfd, 0, gsm->pvt->rxgain, gsm->pvt->txgain, law); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", gsm->pvt->channel); +// } else { +// ast_log(LOG_NOTICE, "tx gain %f rx gain %f law %d pvt->law %d\n", gsm->pvt->txgain, gsm->pvt->rxgain, law, gsm->pvt->law); + } + } + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_HANGUP: + ast_verbose(VERBOSE_PREFIX_3 "Got hang up on channel %d\n", e->hangup.channel); + ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->alreadyhungup = 1; + if (gsm->pvt->owner) { + gsm->pvt->owner->hangupcause = e->hangup.cause; + gsm->pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_ERROR: + ast_log(LOG_WARNING, "Got error on channel\n"); + ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->alreadyhungup = 1; + if (gsm->pvt->owner) { + gsm->pvt->owner->hangupcause = e->error.cause; + gsm->pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } + ast_mutex_unlock(&gsm->pvt->lock); + if (e->error.hard) { +// gsm_poweroff(gsm->modul); + zt_reset_span(gsm->span, 8000); +// gsm_restart(gsm->modul, 10000); + } else { +// gsm_poweroff(gsm->modul); + zt_reset_span(gsm->span, 8000); +// gsm_restart(gsm->modul, 10000); + } + break; + case GSM_EVENT_ALERTING: + ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->subs[SUB_REAL].needringing =1; + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_ANSWER: + ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->dialing = 0; + gsm->pvt->subs[SUB_REAL].needanswer =1; + gsm->pvt->ignoredtmf = 0; + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_PIN_REQUIRED: + gsm_send_pin(gsm->modul, gsm->pin); + break; + case GSM_EVENT_SM_RECEIVED: + ast_verbose(VERBOSE_PREFIX_3 "SMS from %s received on span %d. (Text: %s) (PDU: %s)\n", e->sm_received.sender, gsm->span, e->sm_received.text, e->sm_received.pdu); + manager_event(EVENT_FLAG_CALL, "Message received", + "Span: %d\r\n" + "Sender: %s\r\n" + "SMSC: %s\r\n" + "Length: %d\r\n" + "Text: %s\r\n" + "PDU: %s\r\n", + gsm->span, + e->sm_received.sender, + e->sm_received.smsc, + e->sm_received.len, + e->sm_received.text, + e->sm_received.pdu); + break; + default: + ast_log(LOG_WARNING,"!! Unknown GSM event %d !!\n", e->e); + } +} + +static void *gsm_dchannel(void *vgsm) +{ + struct zt_gsm *gsm = vgsm; + gsm_event *e; + struct timeval tv = {0,0}, *next; + fd_set rfds, efds; + int res,x; + + if (!gsm) return NULL; + + if (!gsm->modul) { + fprintf(stderr, "No gsm_mod\n"); + return NULL; + } + gsm_set_debug(gsm->modul, GSM_DEBUG_NONE); + for (;;) { + + /* Run the D-Channel */ + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(gsm->fd, &rfds); + FD_SET(gsm->fd, &efds); + + if ((next = gsm_schedule_next(gsm->modul))) { + gettimeofday(&tv, NULL); + tv.tv_sec = next->tv_sec - tv.tv_sec; + tv.tv_usec = next->tv_usec - tv.tv_usec; + if (tv.tv_usec < 0) { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + if (tv.tv_sec < 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + res = select(gsm->fd + 1, &rfds, NULL, &efds, next ? &tv : NULL); + e = NULL; + + ast_mutex_lock(&gsm->lock); + if (!res) { + e = gsm_schedule_run(gsm->modul); + } else if (res > 0) { + e = gsm_check_event(gsm->modul, 1); + } else if (errno == ELAST) { + res = ioctl(gsm->fd, ZT_GETEVENT, &x); + printf("Got Zaptel event: %d\n", x); + } else if (errno != EINTR) + fprintf(stderr, "Error (%d) on select: %s\n", ELAST, strerror(errno)); + + if (!e) { + e = gsm_check_event(gsm->modul, 0); + } + + if (e) { + handle_gsm_event(gsm, e); + } + ast_mutex_unlock(&gsm->lock); + + res = ioctl(gsm->fd, ZT_GETEVENT, &x); + + if (!res && x) { + switch (x) { + case ZT_EVENT_NOALARM: + ast_log(LOG_NOTICE, "Alarm cleared on span %d\n", gsm->span); + usleep(1000); + gsm_restart(gsm->modul, 10000); + break; + case ZT_EVENT_ALARM: + ast_log(LOG_NOTICE, "Alarm detected on span %d\n", gsm->span); + break; + default: + fprintf(stderr, "Got event on GSM interface: %d\n", x); + } + } + + + } + return NULL; +} + +#endif + #ifdef HAVE_PRI static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv) { @@ -8524,6 +8851,18 @@ static void zt_pri_error(char *s, int sp ast_log(LOG_WARNING, "%d %s", span, s); } +#ifdef HAVE_GSMAT +static void zt_gsm_message(char *s, int channel) +{ + ast_verbose("GSM %d: %s", channel, s); +} + +static void zt_gsm_error(char *s, int channel) +{ + ast_log(LOG_WARNING, "GSM %d: %s", channel, s); +} +#endif + static int pri_check_restart(struct zt_pri *pri) { if ((pri->nodetype != PRI_NETWORK) && (pri->nodetype != PRI_CPE)) { @@ -10957,6 +11296,243 @@ static int app_zapInband(struct ast_chan #endif /* HAVE_PRI */ +#ifdef HAVE_GSMAT +static int handle_zap_reset_span(int fd, int argc, char *argv[]) +{ + int span; + int sleep = 5000; + if (argc < 4) + return RESULT_SHOWUSAGE; + span = atoi(argv[3]); + if ((span < 1) || (span > NUM_SPANS)) { + ast_cli(fd, "Invalid span '%s'. Should be a number from %d to %d\n", argv[3], 1, NUM_SPANS); + return RESULT_SUCCESS; + } + if (zt_reset_span(span, sleep)) { + return RESULT_FAILURE; + } + return RESULT_SUCCESS; +} + +static int handle_gsm_debug_helper(int fd, int channel, int debug) +{ +/* gsm debug channel */ + struct zt_pvt *pvt = NULL; + if (channel < 1) { + ast_cli(fd, "Invalid channel %d. Should be a number.\n", channel); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + ast_mutex_lock(&pvt->lock); + gsm_set_debug(pvt->gsm.modul, debug); + ast_mutex_unlock(&pvt->lock); + ast_cli(fd, "%s debugging on channel %d\n", debug ? "Enabled":"Disabled", channel); + return RESULT_SUCCESS; + } + pvt = pvt->next; + } + + ast_cli(fd, "No GSM running on channel %d\n", channel); + return RESULT_SUCCESS; +} + + + +static int handle_gsm_debug(int fd, int argc, char *argv[]) +{ +/* gsm debug channel */ + int channel; + if (argc < 4) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + return handle_gsm_debug_helper(fd, channel, GSM_DEBUG_AT); +} + +static int handle_gsm_no_debug(int fd, int argc, char *argv[]) +{ +/* gsm no debug channel */ + int channel; + if (argc < 5) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[4]); + return handle_gsm_debug_helper(fd, channel, GSM_DEBUG_NONE); +} + +static char zap_reset_help[] = + "Usage: zap reset span \n" + " Reset/Restart a zaptel span\n"; + +static char gsm_debug_help[] = + "Usage: gsm debug channel \n" + " Enables debugging on a given GSM channel\n"; + +static char gsm_no_debug_help[] = + "Usage: gsm no debug channel \n" + " Disables debugging on a given GSM channel\n"; + +static struct ast_cli_entry zap_gsm_cli[] = { + { { "zap", "reset", "span", NULL }, handle_zap_reset_span, + "Restart a zaptel span", zap_reset_help, complete_span_4 }, + { { "gsm", "debug", "channel", NULL }, handle_gsm_debug, + "Enables GSM debugging on a channel", gsm_debug_help }, + { { "gsm", "no", "debug", "channel", NULL }, handle_gsm_no_debug, + "Disables GSM debugging on a channel", gsm_no_debug_help}, +}; + + + +static char gsm_send_pdu_help[] = + "Usage: gsm send pdu \n" + " Sends a PDU on a GSM channel\n"; + + + +static int handle_gsm_send_pdu(int fd, int argc, char *argv[]) +{ +/* gsm send sms */ + int channel; + struct zt_pvt *pvt = NULL; + if (argc < 5) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + if (channel < 1) { + ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + if (pvt->owner) { + ast_cli(fd, "Channel in use.\n"); + return RESULT_FAILURE; + } else { + ast_mutex_lock(&pvt->lock); + gsm_sms_send_pdu(pvt->gsm.modul, argv[4]); + ast_mutex_unlock(&pvt->lock); + return RESULT_SUCCESS; + } + } + pvt = pvt->next; + } + + return RESULT_SUCCESS; +} + +static struct ast_cli_entry gsm_send_pdu = { + { "gsm", "send", "pdu", NULL }, handle_gsm_send_pdu, "Sends a SM on a GSM channel", gsm_send_pdu_help, complete_span_4 }; + + +static char gsm_send_sms_help[] = + "Usage: gsm send sms \n" + " Sends a SM on a GSM channel\n"; + + +static int handle_gsm_send_sms(int fd, int argc, char *argv[]) +{ +/* gsm send sms */ + int channel; + struct zt_pvt *pvt = NULL; + if (argc < 6) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + if (channel < 1) { + ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + if (pvt->owner) { + ast_cli(fd, "Channel in use.\n"); + return RESULT_FAILURE; + } else { + ast_mutex_lock(&pvt->lock); + gsm_sms_send_text(pvt->gsm.modul, argv[4], argv[5]); + ast_mutex_unlock(&pvt->lock); + return RESULT_SUCCESS; + } + } + pvt = pvt->next; + } + + return RESULT_SUCCESS; +} + +static int zt_gsm_sendtext(struct ast_channel *chan, const char * dest, const char *text, int ispdu) { + struct zt_pvt *pvt = NULL; + char *c = NULL; + pvt = chan->tech_pvt; + + if (!pvt) return -1; + + /* parse dialstring */ + c = strrchr(dest, '/'); + if (c) + c++; + else + c = (char *)dest; + + ast_mutex_lock(&pvt->lock); + if (ispdu) { + gsm_sms_send_pdu(pvt->gsm.modul, (char *)text); + } else { + gsm_sms_send_text(pvt->gsm.modul, c, (char *)text); + } + ast_mutex_unlock(&pvt->lock); + gsm_wait(pvt->gsm.modul); + return 0; +} + +static struct ast_cli_entry gsm_send_sms = { + { "gsm", "send", "sms", NULL }, handle_gsm_send_sms, "Sends a SM on a GSM channel", gsm_send_sms_help, complete_span_4 }; + +static char gsm_show_status_help[] = + "Usage: gsm show status >\n" + " Displays status information about the GSM channel.\n"; + + +static int handle_gsm_show_status(int fd, int argc, char *argv[]) +{ + int channel; + struct zt_pvt *pvt = NULL; + if (argc < 4) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + if (channel < 1) { + ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + if (pvt->owner) { + ast_cli(fd, "Channel in use.\n"); + return RESULT_FAILURE; + } else { + ast_mutex_lock(&pvt->lock); + gsm_request_status(pvt->gsm.modul); + ast_mutex_unlock(&pvt->lock); + return RESULT_SUCCESS; + } + } + pvt = pvt->next; + } + + return RESULT_SUCCESS; +} + +static struct ast_cli_entry gsm_show_status = { + { "gsm", "show", "status", NULL }, handle_gsm_show_status, "Displays status information about the GSM channel.", gsm_show_status_help, complete_span_4 }; + +#endif /* HAVE_GSMAT */ + static int app_zapEC(struct ast_channel *chan, void *data) { int res=-1; @@ -11578,6 +12154,12 @@ static int __unload_module(void) ast_unregister_application(zapCD_app); ast_unregister_application(zapInband_app); #endif +#ifdef HAVE_GSMAT + ast_cli_unregister_multiple(zap_gsm_cli, sizeof(zap_gsm_cli) / sizeof(zap_gsm_cli[0])); + ast_cli_unregister(&gsm_send_sms); + ast_cli_unregister(&gsm_send_pdu); + ast_cli_unregister(&gsm_show_status); +#endif ast_cli_unregister_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry)); ast_unregister_application(zapEC_app); ast_manager_unregister( "ZapDialOffhook" ); @@ -12098,6 +12680,11 @@ static int process_zap(struct zt_chan_co confp->chan.radio = 0; confp->pri.nodetype = BRI_CPE; #endif +#ifdef HAVE_GSMAT + } else if (!strcasecmp(v->value, "gsm")) { + confp->chan.sig = SIG_GSM; + confp->chan.radio = 0; +#endif } else { ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value); } @@ -12240,6 +12827,10 @@ static int process_zap(struct zt_chan_co ast_copy_string(confp->pri.nocid, v->value, sizeof(confp->pri.nocid)); } else if (!strcasecmp(v->name, "withheldcid")) { ast_copy_string(confp->pri.withheldcid, v->value, sizeof(confp->pri.withheldcid)); + } else if (!strcasecmp(v->name, "pin")) { + ast_copy_string(gsm_modem_pin, v->value, sizeof(gsm_modem_pin) - 1); + } else if (!strcasecmp(v->name, "exten")) { + ast_copy_string(gsm_modem_exten, v->value, sizeof(gsm_modem_exten) - 1); } else if (!strcasecmp(v->name, "resetinterval")) { if (!strcasecmp(v->value, "never")) confp->pri.resetinterval = -1; @@ -12594,6 +13185,10 @@ static int load_module(void) ast_register_application(zap_send_keypad_facility_app, zap_send_keypad_facility_exec, zap_send_keypad_facility_synopsis, zap_send_keypad_facility_descrip); #endif +#ifdef HAVE_GSMAT + gsm_set_error(zt_gsm_error); + gsm_set_message(zt_gsm_message); +#endif res = setup_zap(0); /* Make sure we can register our Zap channel type */ if (res) @@ -12612,6 +13207,12 @@ static int load_module(void) #endif ast_register_application(zapEC_app, app_zapEC, zapEC_synopsis, zapEC_tdesc); ast_cli_register_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry)); +#ifdef HAVE_GSMAT + ast_cli_register(&gsm_send_sms); + ast_cli_register(&gsm_send_pdu); + ast_cli_register(&gsm_show_status); + ast_cli_register_multiple(zap_gsm_cli, sizeof(zap_gsm_cli) / sizeof(zap_gsm_cli[0])); +#endif memset(round_robin, 0, sizeof(round_robin)); ast_manager_register( "ZapTransfer", 0, action_transfer, "Transfer Zap Channel" ); @@ -12625,7 +13226,48 @@ static int load_module(void) return res; } -static int zt_sendtext(struct ast_channel *c, const char *text) +#ifdef HAVE_PRI +static int zt_tdd_sendtext(struct ast_channel *c, const char *text); + +static int zt_pri_sendtext(struct ast_channel *c, const char *text) { + struct zt_pvt *p = c->tech_pvt; + if (!p) return -1; + if (!p->pri) return -1; + if (strlen(text)) { + if (p->pri) { + if (!pri_grab(p, p->pri)) { + // ast_log(LOG_NOTICE, "Sending Display IE '%s'\n", text); + pri_information_display(p->pri->pri,p->call,(char *)text); + pri_rel(p->pri); + } else ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); + } + } + return 0; +} +#endif + +static int zt_sendtext(struct ast_channel *c, const char *dest, const char *text, int ispdu) { + struct zt_pvt *p = c->tech_pvt; + if (!p) return -1; + if (p->sig == SIG_PRI) { +#ifdef HAVE_PRI + if (ispdu) { + ast_log(LOG_WARNING, "Dont know how to send PDU on ZAP ISDN channel\n"); + return -1; + } + return zt_pri_sendtext(c, text); +#endif + } else if (p->sig == SIG_GSM) { +#ifdef HAVE_GSMAT + return zt_gsm_sendtext(c, dest, text, ispdu); +#endif + } else { + return zt_tdd_sendtext(c, text); + } + return -1; +} + +static int zt_tdd_sendtext(struct ast_channel *c, const char *text) { #define END_SILENCE_LEN 400 #define HEADER_MS 50 --- a/configure.ac +++ b/configure.ac @@ -178,6 +178,7 @@ AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capa AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) AST_EXT_LIB_SETUP([GNUTLS], [GNU TLS support (used for iksemel only)], [gnutls]) AST_EXT_LIB_SETUP([GSM], [GSM], [gsm], [, or 'internal']) +AST_EXT_LIB_SETUP([GSMAT], [GSM AT command signalling], [gsmat]) AST_EXT_LIB_SETUP([IKSEMEL], [Iksemel Jabber Library], [iksemel]) AST_EXT_LIB_SETUP([IMAP_TK], [UW IMAP Toolkit], [imap]) AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux Library], [isdnnet]) @@ -466,6 +466,8 @@ fi fi +AST_EXT_LIB_CHECK([GSMAT], [gsmat], [gsm_new_call], [libgsmat.h]) + AST_EXT_LIB_CHECK([IKSEMEL], [iksemel], [iks_start_sasl], [iksemel.h]) if test "${PBX_IKSEMEL}" = 1; then diff -ur asterisk-1.4.17.org/channels/chan_zap.c asterisk-1.4.17/channels/chan_zap.c --- asterisk-1.4.17.org/channels/chan_zap.c 2008-02-09 21:02:04.714653000 +0100 +++ asterisk-1.4.17/channels/chan_zap.c 2008-02-09 21:05:07.690124195 +0100 @@ -47,6 +47,7 @@ zaptel tonezone res_features + gsmat pri ***/ Tylko w asterisk-1.4.17/channels: chan_zap.c~ diff -ur asterisk-1.4.17.org/channels/.chan_zap.moduleinfo asterisk-1.4.17/channels/.chan_zap.moduleinfo --- asterisk-1.4.17.org/channels/.chan_zap.moduleinfo 2008-01-02 21:30:40.000000000 +0100 +++ asterisk-1.4.17/channels/.chan_zap.moduleinfo 2008-02-09 21:04:54.802837479 +0100 @@ -4,5 +4,6 @@ zaptel tonezone res_features + gsmat pri Tylko w asterisk-1.4.17/channels: .chan_zap.moduleinfo~ diff -ur asterisk-1.4.17.org/channels/.moduleinfo asterisk-1.4.17/channels/.moduleinfo --- asterisk-1.4.17.org/channels/.moduleinfo 2008-01-02 21:30:40.000000000 +0100 +++ asterisk-1.4.17/channels/.moduleinfo 2008-02-09 21:04:30.284991178 +0100 @@ -50,6 +50,7 @@ zaptel tonezone res_features + gsmat pri Tylko w asterisk-1.4.17/channels: .moduleinfo~ diff -ur asterisk-1.4.17.org/menuselect-tree asterisk-1.4.17/menuselect-tree --- asterisk-1.4.17.org/menuselect-tree 2008-01-02 21:30:44.000000000 +0100 +++ asterisk-1.4.17/menuselect-tree 2008-02-09 21:03:56.613371109 +0100 @@ -247,6 +247,7 @@ zaptel tonezone res_features + gsmat pri --- asterisk-1.4.17/makeopts.in.org 2008-02-09 21:32:11.948061985 +0100 +++ asterisk-1.4.17/makeopts.in 2008-02-09 21:32:27.655480851 +0100 @@ -83,6 +83,9 @@ GSM_INCLUDE=@GSM_INCLUDE@ GSM_LIB=@GSM_LIB@ +GSMAT_INCLUDE=@GSMAT_INCLUDE@ +GSMAT_LIB=@GSMAT_LIB@ + GTK_INCLUDE=@GTK_INCLUDE@ GTK_LIB=@GTK_LIB@ --- a/build_tools/menuselect-deps.in~ 2007-09-14 17:50:49.000000000 +0200 +++ b/build_tools/menuselect-deps.in 2008-02-09 21:30:40.933703503 +0100 @@ -2,6 +2,7 @@ CURL=@PBX_CURL@ FREETDS=@PBX_FREETDS@ GSM=@PBX_GSM@ +GSMAT=@PBX_GSMAT@ GTK=@PBX_GTK@ GTK2=@PBX_GTK2@ H323=@PBX_H323@