From: cvs2git Date: Sat, 22 Nov 2008 19:37:24 +0000 (+0000) Subject: This commit was manufactured by cvs2git to create branch 'ASTERISK_1_4'. X-Git-Tag: auto/ti/asterisk-1_4_22_1-1~2 X-Git-Url: http://git.pld-linux.org/?p=packages%2Fasterisk.git;a=commitdiff_plain;h=b3826e10c0302a3b47e752db9f68b86827a090e9 This commit was manufactured by cvs2git to create branch 'ASTERISK_1_4'. Cherrypick from master 2008-10-03 10:46:02 UTC Arkadiusz Miśkiewicz '- up to 1.4.22': asterisk-bristuff-build.patch -> 1.4 asterisk-bristuff-libpri.patch -> 1.2 asterisk-bristuff.patch -> 1.11 asterisk-chan_bluetooth.patch -> 1.1 asterisk-fix-ptlib.patch -> 1.1 asterisk-m4.patch -> 1.1 asterisk-zhone.patch -> 1.3 asterisk.logrotate -> 1.1 asterisk.spec -> 1.126 Cherrypick from master 2008-11-22 19:37:24 UTC Arkadiusz Miśkiewicz '- fix from upstream': asterisk-r150557.patch -> 1.1 --- diff --git a/asterisk-bristuff-build.patch b/asterisk-bristuff-build.patch new file mode 100644 index 0000000..028fffa --- /dev/null +++ b/asterisk-bristuff-build.patch @@ -0,0 +1,139 @@ +diff -ur -x '*.c' -x '*.orig' -x configure asterisk-1.4.21.1.new/build_tools/menuselect-deps.in asterisk-1.4.21.1.old/build_tools/menuselect-deps.in +--- asterisk-1.4.21.1.new/build_tools/menuselect-deps.in 2008-05-06 00:10:05.000000000 +0200 ++++ asterisk-1.4.21.1.old/build_tools/menuselect-deps.in 2008-07-18 08:18:42.813626657 +0200 +@@ -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@ +diff -ur asterisk-1.4.21.1.org/channels/chan_zap.c asterisk-1.4.21.1/channels/chan_zap.c +--- asterisk-1.4.21.1/channels/chan_zap.c~ 2008-07-18 21:56:54.854339111 +0200 ++++ asterisk-1.4.21.1/channels/chan_zap.c 2008-07-18 22:02:00.487649563 +0200 +@@ -47,6 +47,7 @@ + zaptel + tonezone + res_features ++ gsmat + pri + ***/ + +diff -ur -x '*.c' -x '*.orig' -x configure asterisk-1.4.21.1.new/channels/.chan_zap.moduleinfo asterisk-1.4.21.1.old/channels/.chan_zap.moduleinfo +--- asterisk-1.4.21.1.new/channels/.chan_zap.moduleinfo 2008-06-30 18:24:08.000000000 +0200 ++++ asterisk-1.4.21.1.old/channels/.chan_zap.moduleinfo 2008-07-18 08:18:42.808831904 +0200 +@@ -4,5 +4,6 @@ + zaptel + tonezone + res_features ++ gsmat + pri + +diff -ur -x '*.c' -x '*.orig' -x configure asterisk-1.4.21.1.new/channels/.moduleinfo asterisk-1.4.21.1.old/channels/.moduleinfo +--- asterisk-1.4.21.1.new/channels/.moduleinfo 2008-06-30 18:24:08.000000000 +0200 ++++ asterisk-1.4.21.1.old/channels/.moduleinfo 2008-07-18 08:18:42.808831904 +0200 +@@ -51,6 +51,7 @@ + zaptel + tonezone + res_features ++ gsmat + pri + + +diff -ur -x '*.c' -x '*.orig' -x configure asterisk-1.4.21.1.new/configure.ac asterisk-1.4.21.1.old/configure.ac +--- asterisk-1.4.21.1.new/configure.ac 2008-07-18 08:18:57.389246988 +0200 ++++ asterisk-1.4.21.1.old/configure.ac 2008-07-18 08:18:42.806528869 +0200 +@@ -504,6 +504,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 -x '*.c' -x '*.orig' -x configure asterisk-1.4.21.1.new/makeopts.in asterisk-1.4.21.1.old/makeopts.in +--- asterisk-1.4.21.1.new/makeopts.in 2008-03-11 15:07:59.000000000 +0100 ++++ asterisk-1.4.21.1.old/makeopts.in 2008-07-18 08:18:42.813626657 +0200 +@@ -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@ + +diff -ur -x '*.c' -x '*.orig' -x configure asterisk-1.4.21.1.new/menuselect-tree asterisk-1.4.21.1.old/menuselect-tree +--- asterisk-1.4.21.1.new/menuselect-tree 2008-06-30 18:24:08.000000000 +0200 ++++ asterisk-1.4.21.1.old/menuselect-tree 2008-07-18 08:18:42.813626657 +0200 +@@ -251,6 +251,7 @@ + zaptel + tonezone + res_features ++ gsmat + pri + + +--- asterisk-1.4.21.1/configure.ac~ 2008-07-20 18:07:54.188016698 +0200 ++++ asterisk-1.4.21.1/configure.ac 2008-07-20 18:09:56.831337972 +0200 +@@ -178,7 +178,7 @@ + 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], [GSMAT], [GSM AT command signalling], [gsmat]) ++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]) +--- asterisk/channels/chan_zap.c~ 2008-07-20 18:15:39.464637514 +0200 ++++ asterisk/channels/chan_zap.c 2008-07-20 21:38:13.280088092 +0200 +@@ -7573,7 +7573,7 @@ + } + #endif + #ifdef HAVE_GSMAT +- if (conf->chan.sig == SIG_GSM) { ++ if (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); +--- asterisk/channels/chan_zap.c~ 2008-07-20 21:39:17.274665128 +0200 ++++ asterisk/channels/chan_zap.c 2008-07-20 22:18:57.866822681 +0200 +@@ -743,9 +743,7 @@ + .send_digit_begin = zt_digit_begin, + .send_digit_end = zt_digit_end, + .send_text = zt_sendtext, +-#if 0 /* we (Debian) disable that addition because of ABI breakage */ + .send_message = zt_sendmessage, +-#endif + .call = zt_call, + .hangup = zt_hangup, + .answer = zt_answer, +--- asterisk/include/asterisk/channel.h~ 2008-07-20 22:20:30.489834448 +0200 ++++ asterisk/include/asterisk/channel.h 2008-07-20 22:24:21.859824471 +0200 +@@ -250,10 +250,8 @@ + /*! \brief Display or transmit text */ + int (* const send_text)(struct ast_channel *chan, const char *text); + +-#if 0 /* we (Debian) disable that addition because of ABI breakage */ + /*! \brief send a message */ + int (* const send_message)(struct ast_channel *chan, const char *dest, const char *text, int ispdu); +-#endif + + /*! \brief Display or send an image */ + int (* const send_image)(struct ast_channel *chan, struct ast_frame *frame); +--- asterisk/main/channel.c~ 2008-07-20 22:20:30.489834448 +0200 ++++ asterisk/main/channel.c 2008-07-20 22:25:38.689861614 +0200 +@@ -2496,10 +2496,8 @@ + if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) + return -1; + CHECK_BLOCKING(chan); +-#if 0 /* we (Debian) disable that addition because of ABI breakage */ + if (chan->tech->send_message) + res = chan->tech->send_message(chan, dest, text, ispdu); +-#endif + ast_clear_flag(chan, AST_FLAG_BLOCKING); + return res; + } diff --git a/asterisk-bristuff-libpri.patch b/asterisk-bristuff-libpri.patch new file mode 100644 index 0000000..414afd6 --- /dev/null +++ b/asterisk-bristuff-libpri.patch @@ -0,0 +1,22 @@ +--- a/configure.ac ++++ b/configure.ac +@@ -823,7 +823,7 @@ fi + + AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h]) + +-AST_EXT_LIB_CHECK([PRI], [pri], [pri_keypad_facility], [libpri.h]) ++AST_EXT_LIB_CHECK([PRI], [pri-bristuff], [pri_keypad_facility], [bristuff/libpri.h]) + + if test "${USE_PWLIB}" != "no"; then + if test -n "${PWLIB_DIR}"; then +--- a/channels/chan_zap.c ++++ b/channels/chan_zap.c +@@ -71,7 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi + #include + + #ifdef HAVE_PRI +-#include ++#include + #endif + + #include "asterisk/lock.h" diff --git a/asterisk-bristuff.patch b/asterisk-bristuff.patch new file mode 100644 index 0000000..85f9a30 --- /dev/null +++ b/asterisk-bristuff.patch @@ -0,0 +1,7998 @@ +--- 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 +@@ -2427,6 +2427,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) { +@@ -2551,6 +2552,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 +@@ -30,6 +30,7 @@ extern "C" { + typedef struct agi_state { + int fd; /* FD for general output */ + int audio; /* 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 */ +@@ -359,12 +400,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; +@@ -394,7 +442,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 : ""); +@@ -1835,8 +1883,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) */ +@@ -1850,10 +1903,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 > -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 */ +@@ -1871,6 +1936,16 @@ static enum agi_result run_agi(struct as + 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; + +@@ -1922,6 +1997,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"); +@@ -2030,6 +2106,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; +@@ -2056,12 +2133,13 @@ static int agi_exec_full(struct ast_chan + } + #endif + ast_replace_sigchld(); +- 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_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 */ +@@ -2071,6 +2149,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); +@@ -2119,6 +2199,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)) +@@ -2174,6 +2283,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); +@@ -2184,6 +2294,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. +@@ -3362,7 +3365,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); +@@ -3416,7 +3419,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/main/channel.c ++++ b/main/channel.c +@@ -3898,6 +3898,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/apps/app_meetme.c ++++ b/apps/app_meetme.c +@@ -1405,8 +1405,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; + +@@ -1615,7 +1616,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; +@@ -1926,6 +1927,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 +@@ -6076,6 +6076,9 @@ static int pbx_builtin_saynumber(struct + return -1; + } + } ++ if (chan->_state != AST_STATE_UP) { ++ ast_answer(chan); ++ } + + if (ast_say_number(chan, atoi(tmp), "", chan->language, options)) { + ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp); +@@ -6088,8 +6091,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; + } + +@@ -6097,8 +6104,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; + } + +@@ -6106,8 +6117,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. +@@ -130,7 +134,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" +@@ -1292,14 +1297,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 +@@ -71,6 +71,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"; + +@@ -1652,23 +1653,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 +@@ -198,6 +198,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" +@@ -254,6 +256,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) +@@ -297,6 +300,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), +@@ -412,7 +416,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 */ +@@ -633,7 +637,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: +@@ -651,7 +655,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: +@@ -669,7 +673,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); +@@ -1091,7 +1095,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, "&")) ) { +@@ -1116,7 +1120,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)); +@@ -1319,7 +1323,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++; + } +@@ -1442,7 +1446,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 +@@ -201,7 +201,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" +@@ -257,6 +258,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) +@@ -301,6 +303,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), +@@ -621,14 +624,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: +@@ -1095,7 +1104,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, "&")) ) { +# Allow as many spans as Zaptel can take (defualt: 128). +# There is a static array of zt_pris allocated at this size. +# For that reason upstream only sort-of merged it: made it a +# compile-time option in 1.6 . + +--- a/channels/chan_zap.c ++++ b/channels/chan_zap.c +@@ -190,7 +190,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 ZT_MAX_SPANS + #define NUM_DCHANS 4 /*!< No more than 4 d-channels */ + #define MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */ + +--- a/include/asterisk/channel.h ++++ b/include/asterisk/channel.h +@@ -435,6 +435,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/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. +@@ -216,8 +220,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) */ +@@ -235,10 +237,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); +@@ -253,6 +251,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) + { +@@ -297,6 +296,14 @@ 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_pri { + pthread_t master; /*!< Thread of master */ + ast_mutex_t lock; /*!< Mutex */ +@@ -310,6 +317,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) */ +@@ -321,6 +330,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 */ +@@ -336,6 +346,8 @@ 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 */ ++ int debugfd; + }; + + +@@ -453,6 +465,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; +@@ -467,7 +481,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 */ +@@ -504,6 +519,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]; +@@ -569,6 +585,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 +@@ -614,11 +632,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 + }, +@@ -630,6 +651,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, +@@ -684,6 +707,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", +@@ -715,6 +740,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; +@@ -1417,12 +1449,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) { +@@ -1449,7 +1485,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) +@@ -1810,7 +1846,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) +@@ -2041,6 +2082,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"); +@@ -2061,6 +2103,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++; +@@ -2083,6 +2131,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'); +@@ -2106,6 +2155,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); +@@ -2135,7 +2186,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); + +@@ -2399,8 +2450,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", +@@ -2410,6 +2463,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; +@@ -2457,8 +2520,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; +@@ -2560,6 +2622,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; +@@ -2602,7 +2665,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; +@@ -2622,7 +2685,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"); +@@ -2806,10 +2890,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: +@@ -3446,6 +3534,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) { +@@ -3655,8 +3752,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) +@@ -3815,7 +3914,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); +@@ -4886,7 +4985,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) { +@@ -4898,8 +4997,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; + } +@@ -4974,7 +5074,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; + } +@@ -5042,7 +5144,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; +@@ -5124,7 +5226,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; +@@ -5321,8 +5423,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; + } +@@ -5452,6 +5558,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 +@@ -5480,10 +5587,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 +@@ -6697,9 +6811,20 @@ static int handle_init_event(struct zt_p + case ZT_EVENT_NOALARM: + i->inalarm = 0; + if (!i->unknown_alarm) { ++#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 { ++#endif + ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel); + manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", + "Channel: %d\r\n", i->channel); ++#ifdef HAVE_PRI ++ } ++ } ++#endif ++ + } else { + i->unknown_alarm = 0; + } +@@ -6708,7 +6833,13 @@ static int handle_init_event(struct zt_p + i->inalarm = 1; + res = get_alarms(i); + do { +- const char *alarm_str = alarm2str(res); ++#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 { ++#endif ++ const char *alarm_str = alarm2str(res); + + /* hack alert! Zaptel 1.4 now exposes FXO battery as an alarm, but asterisk 1.4 + * doesn't know what to do with it. Don't confuse users with log messages. */ +@@ -6724,6 +6855,10 @@ static int handle_init_event(struct zt_p + "Alarm: %s\r\n" + "Channel: %d\r\n", + alarm_str, i->channel); ++#ifdef HAVE_PRI ++ } ++ } ++#endif + } while (0); + /* fall thru intentionally */ + case ZT_EVENT_ONHOOK: +@@ -6768,8 +6903,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); +@@ -7062,6 +7199,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; + } +@@ -7314,6 +7453,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); +@@ -7331,6 +7475,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; +@@ -7339,9 +7488,13 @@ 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].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)); +@@ -7483,6 +7636,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) { +@@ -7765,7 +7919,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; +@@ -7961,6 +8115,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); + } +@@ -7994,13 +8153,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) + { +@@ -8045,6 +8205,7 @@ 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; + if (!c) { + if (principle < 0) +@@ -8075,6 +8236,7 @@ static int pri_fixup_principle(struct zt + } + /* Fix it all up now */ + new->owner = old->owner; ++ new->outgoing = old->outgoing; + old->owner = NULL; + if (new->owner) { + ast_string_field_build(new->owner, name, +@@ -8094,6 +8256,34 @@ static int pri_fixup_principle(struct zt + new->dsp_features = old->dsp_features; + old->dsp = NULL; + old->dsp_features = 0; ++ ++ /* Copy faxhandled/digial, alreadyhungup */ ++ new->faxhandled = old->faxhandled; ++ new->digital = old->digital; ++ new->alreadyhungup = old->alreadyhungup; ++ ++ /* Copy law, gains, etc */ ++ new->law = old->law; ++ if (ioctl(new->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &new->law) == -1) ++ ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", new->channel, new->law); ++ res = zt_setlaw(new->subs[SUB_REAL].zfd, new->law); ++ if (res < 0) ++ ast_log(LOG_WARNING, "Unable to set law on channel %d\n", new->channel); ++ if (!new->digital) { ++ res = set_actual_gain(new->subs[SUB_REAL].zfd, 0, new->rxgain, new->txgain, new->law); ++ } else { ++ res = set_actual_gain(new->subs[SUB_REAL].zfd, 0, 0, 0, new->law); ++ } ++ if (res < 0) ++ ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", new->channel); ++ ++ /* Shutdown old channel */ ++ zt_confmute(old, 0); ++ update_conf(old); ++ reset_conf(old); ++ restore_gains(old); ++ zt_disable_ec(old); ++ zt_setlinear(old->subs[SUB_REAL].zfd, 0); + } + return principle; + } +@@ -8122,7 +8312,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; + } + +@@ -8181,86 +8373,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) && +@@ -8344,13 +8471,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) +@@ -8530,15 +8674,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) +@@ -8550,8 +8723,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); +@@ -8576,6 +8748,17 @@ static void *pri_dchannel(void *vpri) + + switch (e->e) { + case PRI_EVENT_DCHAN_UP: ++ 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); ++ } 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); ++ } ++ + if (!pri->pri) pri_find_dchan(pri); + + /* Note presense of D-channel */ +@@ -8594,6 +8777,12 @@ static void *pri_dchannel(void *vpri) + } + break; + case PRI_EVENT_DCHAN_DOWN: ++ 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; ++ } + pri_find_dchan(pri); + if (!pri_is_up(pri)) { + pri->resetting = 0; +@@ -8601,16 +8790,18 @@ static void *pri_dchannel(void *vpri) + for (i = 0; i < pri->numchans; i++) { + struct zt_pvt *p = pri->pvts[i]; + if (p) { ++ if ((p->tei == e->gen.tei) || (pri->nodetype != BRI_NETWORK_PTMP)) { + if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { + /* 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) +@@ -8619,6 +8810,7 @@ static void *pri_dchannel(void *vpri) + p->inalarm = 1; + } + } ++ } + } + break; + case PRI_EVENT_RESTART: +@@ -8653,8 +8845,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); +@@ -8688,7 +8880,6 @@ static void *pri_dchannel(void *vpri) + } + } + break; +- + case PRI_EVENT_INFO_RECEIVED: + chanpos = pri_find_principle(pri, e->ring.channel); + if (chanpos < 0) { +@@ -8697,9 +8888,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; +@@ -8711,6 +8904,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); + } +@@ -8718,36 +8919,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)); +@@ -8761,13 +8985,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) { +@@ -8792,34 +9017,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)) { +@@ -8838,19 +9111,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; + +@@ -8862,10 +9152,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); +@@ -8873,6 +9170,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); +@@ -8896,8 +9203,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", +@@ -8905,15 +9212,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]; + +@@ -8940,26 +9251,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'; + } +@@ -8968,9 +9296,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: +@@ -8986,7 +9314,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 +@@ -9018,9 +9346,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 +@@ -9062,11 +9397,18 @@ static void *pri_dchannel(void *vpri) + pri->pvts[chanpos]->progress = 1; + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } ++ } + } + break; + 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, }; + +@@ -9091,6 +9433,7 @@ static void *pri_dchannel(void *vpri) + pri->pvts[chanpos]->proceeding = 1; + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } ++ } + } + break; + case PRI_EVENT_FACNAME: +@@ -9114,6 +9457,163 @@ 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; ++ } ++ /* holded calls are not implemented yet */ ++ 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; ++ } ++ /* Holded calls are currently not supported */ ++ pri_retrieve_reject(pri->pri, e->retrieve_req.call); ++ chanpos = -1; ++ 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) { +@@ -9126,6 +9626,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. +@@ -9155,11 +9656,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 +@@ -9216,20 +9722,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)) { +@@ -9242,8 +9757,9 @@ 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); ++ 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; +@@ -9253,15 +9769,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; +@@ -9288,16 +9812,34 @@ 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) { ++ 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 +@@ -9311,9 +9853,27 @@ 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 { ++ 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); +@@ -9325,6 +9885,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) +@@ -9431,10 +9992,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; +@@ -9442,6 +10015,23 @@ static void *pri_dchannel(void *vpri) + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + break; ++ case PRI_EVENT_FACILITY: ++ if (e->facility.operation == 0x0D) { ++ struct ast_channel *owner = pri->pvts[chanpos]->owner; ++ ++ ast_log(LOG_NOTICE, "call deflection to %s requested.\n", e->facility.forwardnum); ++ ast_mutex_lock(&pri->pvts[chanpos]->lock); ++ /* transfer */ ++ if (owner) { ++ ast_string_field_build(owner, call_forward, ++ "Local/%s@%s", e->facility.forwardnum, ++ 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); + } +@@ -9503,7 +10093,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; +@@ -9571,39 +10161,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; + } ++ for (x=0; x < NUM_SPANS; x++) { ++ ast_mutex_lock(&pris[x].lock); + +- ast_mutex_lock(&pridebugfdlock); +- +- if (pridebugfd >= 0) +- close(pridebugfd); +- +- pridebugfd = myfd; +- ast_copy_string(pridebugfilename,argv[4],sizeof(pridebugfilename)); +- +- ast_mutex_unlock(&pridebugfdlock); ++ 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 will be sent to '%s'\n", argv[4]); ++ 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; +@@ -9644,6 +10272,7 @@ static int handle_pri_debug(int fd, int + + + ++ + static int handle_pri_no_debug(int fd, int argc, char *argv[]) + { + int span; +@@ -9793,10 +10422,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"); +@@ -9823,6 +10448,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", +@@ -9847,6 +10484,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" }, + +@@ -9859,8 +10505,76 @@ static struct ast_cli_entry zap_pri_cli[ + #endif + }; + ++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; +@@ -10441,8 +11155,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" ); +@@ -10944,6 +11661,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); +@@ -11056,9 +11789,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")) { +@@ -11071,6 +11816,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; +@@ -11087,6 +11836,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")) { +@@ -11389,6 +12140,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); +@@ -11436,7 +12188,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)); +@@ -11470,6 +12225,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 +Change the ABI of ast_channel_tech to add a new function, send_message that is +to be used by channels wanting to send text messages with a destination number +(mainly GSM). + +--- a/include/asterisk/channel.h ++++ b/include/asterisk/channel.h +@@ -247,6 +247,11 @@ struct ast_channel_tech { + /*! \brief Display or transmit text */ + int (* const send_text)(struct ast_channel *chan, const char *text); + ++#if 0 /* we (Debian) disable that addition because of ABI breakage */ ++ /*! \brief send a message */ ++ int (* const send_message)(struct ast_channel *chan, const char *dest, const char *text, int ispdu); ++#endif ++ + /*! \brief Display or send an image */ + int (* const send_image)(struct ast_channel *chan, struct ast_frame *frame); + +@@ -689,6 +694,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" +@@ -910,6 +925,16 @@ int ast_set_write_format(struct ast_chan + */ + int ast_sendtext(struct ast_channel *chan, const char *text); + ++/*! \brief Sends message 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_sendmessage(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 + * \param timeout timeout in milliseconds (0 for infinite wait) +--- a/main/channel.c ++++ b/main/channel.c +@@ -2472,6 +2472,21 @@ int ast_sendtext(struct ast_channel *cha + return res; + } + ++int ast_sendmessage(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 */ ++ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) ++ return -1; ++ CHECK_BLOCKING(chan); ++#if 0 /* we (Debian) disable that addition because of ABI breakage */ ++ if (chan->tech->send_message) ++ res = chan->tech->send_message(chan, dest, text, ispdu); ++#endif ++ ast_clear_flag(chan, AST_FLAG_BLOCKING); ++ return res; ++} ++ + int ast_senddigit_begin(struct ast_channel *chan, char digit) + { + /* Device does not support DTMF tones, lets fake +@@ -4515,6 +4530,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_sendmessage(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/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. +@@ -1427,6 +1430,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" +@@ -2853,6 +2899,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 +@@ -130,7 +130,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]; +@@ -154,6 +154,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; +@@ -297,6 +302,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; +@@ -325,8 +332,13 @@ int ast_monitor_stop(struct ast_channel + snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s%s%s-\"* ) &",tmp, dir, absolute, 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); + } + +@@ -467,7 +479,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); +@@ -506,6 +518,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)) { +@@ -530,7 +544,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 +@@ -2891,13 +2891,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 +@@ -419,7 +419,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, int norecurse) ++static int __ast_device_state_changed_literal(char *buf, int norecurse, char *cid_num, char *cid_name) + { + char *device; + struct state_change *change; +@@ -308,10 +310,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) +@@ -329,17 +337,23 @@ static int __ast_device_state_changed_li + */ + if (!norecurse && (tmp = strrchr(device, '-'))) { + *tmp = '\0'; +- __ast_device_state_changed_literal(device, 1); ++ __ast_device_state_changed_literal(device, 1, cid_num, cid_name); + } + + 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, 0); ++ if (cid_num) ++ buf2 = ast_strdupa(cid_num); ++ if (cid_name) ++ buf3 = ast_strdupa(cid_name); ++ return __ast_device_state_changed_literal(buf, 0, buf2, buf3); + } + + /*! \brief Accept change notification, add it to change queue */ +@@ -351,7 +365,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, 0); ++ return __ast_device_state_changed_literal(buf, 0, NULL, NULL); + } + + /*! \brief Go through the dev state change queue and update changes in the dev state thread */ +@@ -366,7 +380,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 +@@ -613,8 +613,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_callerid(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 +@@ -1261,7 +1261,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) +@@ -3673,7 +3673,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_callerid(struct ast_channel *chan, enum ast_channel_state state, char *cid_num, char *cid_name) + { + int oldstate = chan->_state; + +@@ -3681,7 +3681,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", +@@ -3698,6 +3698,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_callerid(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 +@@ -2022,7 +2022,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; + +@@ -2053,11 +2053,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 */ + } +@@ -2252,7 +2252,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; +@@ -4021,7 +4021,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 +@@ -1342,7 +1342,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); +@@ -8593,7 +8593,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; + +@@ -12783,7 +12783,7 @@ static void handle_response(struct sip_p + if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) { + /* Ready to send the next state we have on queue */ + ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE); +- cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p); ++ cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p, NULL, NULL); + } + } + } else if (sipmethod == SIP_REGISTER) +@@ -13036,7 +13036,7 @@ static void handle_response(struct sip_p + if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) { + /* Ready to send the next state we have on queue */ + ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE); +- cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p); ++ cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p, NULL, NULL); + } + } + } else if (sipmethod == SIP_BYE) +--- a/apps/app_queue.c ++++ b/apps/app_queue.c +@@ -721,7 +721,7 @@ static void *device_state_thread(void *d + return NULL; + } + /*! \brief Producer of the statechange queue */ +-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" }, + }; +@@ -2551,10 +2552,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); +Add or convert channel operations so they can use the unique ID. + +--- a/include/asterisk/channel.h ++++ b/include/asterisk/channel.h +@@ -682,6 +682,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 +@@ -697,8 +709,12 @@ struct ast_channel *ast_request(const ch + */ + 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_uniqueid(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_uniqueid(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 + * \param data data to pass to the channel requester +@@ -990,6 +1006,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 +@@ -1017,7 +1017,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; +@@ -1045,7 +1045,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 */ +@@ -1100,39 +1103,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 */ +@@ -2862,6 +2870,12 @@ 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) + { ++ return __ast_request_and_dial_uniqueid(type, format, data, ++ timeout, outstate, 0, cid_num, cid_name, oh, NULL); ++} ++ ++struct ast_channel *__ast_request_and_dial_uniqueid(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; + struct ast_channel *chan; +@@ -2873,7 +2887,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 */ +@@ -2993,8 +3007,12 @@ struct ast_channel *ast_request_and_dial + { + return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL); + } ++struct ast_channel *ast_request_and_dial_uniqueid(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_uniqueid(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; +@@ -3033,6 +3051,7 @@ struct ast_channel *ast_request(const ch + if (!(c = chan->tech->requester(type, capabilities | videoformat, data, cause))) + return NULL; + ++ if (uniqueid) strncpy(c->uniqueid, uniqueid, sizeof(c->uniqueid)); + /* no need to generate a Newchannel event here; it is done in the channel_alloc call */ + return c; + } +@@ -3044,6 +3063,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. +--- 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_exten_uniqueid(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_app_uniqueid(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 +@@ -4992,7 +4992,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_exten_uniqueid(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; +@@ -5002,7 +5002,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_uniqueid(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); + if (channel) { + *channel = chan; + if (chan) +@@ -5094,7 +5094,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_uniqueid(type, format, data, timeout, reason, callingpres, cid_num, cid_name, uniqueid); + if (channel) { + *channel = chan; + if (chan) +@@ -5134,6 +5134,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_exten_uniqueid(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]; +@@ -5158,7 +5162,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_app_uniqueid(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; +@@ -5177,7 +5181,7 @@ 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_uniqueid(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 */ + chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */ +@@ -5259,7 +5263,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_uniqueid(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); + if (!chan) { + free(as); + res = -1; +@@ -5299,6 +5303,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_app_uniqueid(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 +@@ -340,6 +340,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); +@@ -518,18 +523,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)) { +@@ -570,16 +585,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 +@@ -57,6 +57,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_chan_uniqueid = "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" +@@ -87,6 +88,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" +@@ -456,7 +478,7 @@ static struct chanspy_ds *setup_chanspy_ + + static struct chanspy_ds *next_channel(struct ast_channel *chan, + const struct ast_channel *last, const char *spec, +- const char *exten, const char *context, struct chanspy_ds *chanspy_ds) ++ const char *exten, const char *context, struct chanspy_ds *chanspy_ds, const char *uniqueid) + { + struct ast_channel *this; + +@@ -465,6 +487,8 @@ redo: + 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); + +@@ -485,7 +509,7 @@ redo: + + 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) + { + char nameprefix[AST_NAME_STRLEN]; + char peer_name[AST_NAME_STRLEN + 5]; +@@ -530,11 +554,11 @@ static int common_exec(struct ast_channe + waitms = 100; + num_spyed_upon = 0; + +- for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); ++ for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds, NULL); + peer_chanspy_ds; + chanspy_ds_free(peer_chanspy_ds), prev = peer, + peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : +- next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { ++ next_channel(chan, prev, spec, exten, context, &chanspy_ds, NULL), next_chanspy_ds = NULL) { + const char *group; + int igrp = !mygroup; + char *groups[25]; +@@ -733,7 +757,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); +@@ -818,7 +842,7 @@ 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); +@@ -831,14 +855,100 @@ static int extenspy_exec(struct ast_chan + 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); ++ ++ 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 unload_module(void) + { + int res = 0; + + res |= ast_unregister_application(app_chan); ++ res |= ast_unregister_application(app_chan_uniqueid); + res |= ast_unregister_application(app_ext); + +- ast_module_user_hangup_all(); + + return res; + } +@@ -849,6 +959,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_chan_uniqueid, 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; + }; + +@@ -1416,11 +1418,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; +@@ -1671,12 +1682,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,8 +1702,18 @@ static int action_redirect(struct manses + return 0; + } + } ++ if (!ast_strlen_zero(priority2) && (sscanf(priority2, "%d", &pi2) != 1)) { ++ if ((pi = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL)) < 1) { ++ astman_send_error(s, m, "Invalid extra priority\n"); ++ return 0; ++ } ++ } + /* 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); +@@ -1698,8 +1725,11 @@ static int action_redirect(struct manses + ast_channel_unlock(chan); + return 0; + } +- if (!ast_strlen_zero(name2)) ++ if (!ast_strlen_zero(uniqueid2)) { ++ chan2 = ast_get_channel_by_uniqueid_locked(uniqueid2); ++ } else if (!ast_strlen_zero(name2)) { + chan2 = ast_get_channel_by_name_locked(name2); ++ } + if (chan2 && ast_check_hangup(chan2)) { + astman_send_error(s, m, "Redirect failed, extra channel not up.\n"); + ast_channel_unlock(chan); +@@ -1708,9 +1738,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) +@@ -1789,15 +1819,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_app_uniqueid(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_exten_uniqueid(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) +@@ -1857,6 +1887,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; +@@ -1866,6 +1897,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; +@@ -1883,6 +1917,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, '/'); +@@ -1902,6 +1940,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) { +@@ -1921,8 +1960,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)) { +@@ -1933,19 +1974,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_app_uniqueid(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_exten_uniqueid(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" +@@ -1039,6 +1042,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 +@@ -706,6 +706,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. +@@ -132,6 +136,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; + +@@ -150,6 +168,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 */ +@@ -405,11 +440,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)) { +@@ -1656,11 +1693,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 + ); + } + +@@ -1928,10 +1967,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); +@@ -2085,15 +2126,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) +@@ -2117,12 +2153,13 @@ static int manager_parking_status( struc + "Timeout: %ld\r\n" + "CallerID: %s\r\n" + "CallerIDName: %s\r\n" ++ "Uniqueid: %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); + } + +@@ -2197,6 +2234,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) + { +@@ -2460,6 +2918,7 @@ static int load_config(void) + + static int reload(void) + { ++ autoanswer_reregister_extensions(); + return load_config(); + } + +@@ -2483,6 +2942,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; +@@ -2497,6 +2962,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 +@@ -66,6 +66,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 +@@ -84,6 +85,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 */ +@@ -168,6 +170,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; +@@ -187,10 +205,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; +@@ -2052,6 +2076,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; +@@ -2933,6 +3233,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); +@@ -2942,6 +3243,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); +@@ -2964,6 +3266,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/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" +@@ -185,6 +188,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) +@@ -234,6 +238,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,6 +257,7 @@ static enum ast_bridge_result zt_bridge( + + static int zt_sendtext(struct ast_channel *c, const char *text); + ++static int zt_sendmessage(struct ast_channel *c, const char *dest, const char *text, int ispdu); + + /*! \brief Avoid the silly zt_getevent which ignores a bunch of events */ + static inline int zt_get_event(int fd) +@@ -364,6 +371,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]; + +@@ -392,6 +412,7 @@ struct zt_pri; + #define POLARITY_REV 1 + + ++ + static struct zt_distRings drings; + + struct distRingData { +@@ -604,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; +@@ -710,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_sendmessage(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); +@@ -732,6 +756,9 @@ static const struct ast_channel_tech zap + .send_digit_begin = zt_digit_begin, + .send_digit_end = zt_digit_end, + .send_text = zt_sendtext, ++#if 0 /* we (Debian) disable that addition because of ABI breakage */ ++ .send_message = zt_sendmessage, ++#endif + .call = zt_call, + .hangup = zt_hangup, + .answer = zt_answer, +@@ -1262,6 +1289,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: +@@ -1683,7 +1712,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) +@@ -2098,6 +2127,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); +@@ -2737,7 +2785,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); +@@ -2914,6 +2968,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; +@@ -7302,6 +7363,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, const 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) */ +@@ -7530,6 +7595,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; +@@ -7819,6 +7915,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)) +@@ -8176,6 +8278,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) + { +@@ -8450,6 +8781,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)) { +@@ -10868,6 +11211,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; +@@ -11489,6 +12069,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" ); +@@ -12009,6 +12595,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); + } +@@ -12151,6 +12742,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; +@@ -12506,6 +13101,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) +@@ -12524,6 +13123,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" ); +@@ -12537,7 +13142,66 @@ 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 *text) { ++ struct zt_pvt *p = c->tech_pvt; ++ if (!p) return -1; ++ if (p->sig == SIG_PRI) { ++#ifdef HAVE_PRI ++ return zt_pri_sendtext(c, text); ++#endif ++ } else if (p->sig == SIG_GSM) { ++ } else { ++ return zt_tdd_sendtext(c, text); ++ } ++ return -1; ++} ++ ++static int zt_sendmessage(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 { ++ if (ispdu) { ++ ast_log(LOG_WARNING, "Dont know how to send PDU on ZAP channel\n"); ++ return -1; ++ } ++ 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], [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]) +--- a/configure ++++ b/configure +@@ -26600,6 +26600,188 @@ echo "$as_me: *** without explicitly spe + fi + fi + ++{ echo "$as_me:$LINENO: checking for ${GSMAT_DIR}/include/libgsmat.h" >&5 ++echo $ECHO_N "checking for ${GSMAT_DIR}/include/libgsmat.h... $ECHO_C" >&6; } ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ eval "$as_ac_Header=\$ac_header_preproc" ++fi ++ac_res=`eval echo '${'$as_ac_Header'}'` ++ { echo "$as_me:$LINENO: result: $ac_res" >&5 ++echo "${ECHO_T}$ac_res" >&6; } ++ ++if test `eval echo '${'$as_ac_Header'}'` = yes; then ++ GSMAT_HEADER_FOUND=1 ++else ++ GSMAT_HEADER_FOUND=0 ++fi ++ ++ ++ ++ if test "${GSMAT_HEADER_FOUND}" = "yes"; then ++ GSMAT_LIB="-lgsmat " ++ GSMAT_HEADER_FOUND="1" ++ if test "x${GSMAT_DIR}" != "x"; then ++ GSMAT_LIB="${pbxlibdir} ${GSMAT_LIB}" ++ GSMAT_INCLUDE="-I${GSMAT_DIR}/include" ++ fi ++ CPPFLAGS="${saved_cppflags}" ++ else ++ if test "xlibgsmat.h" != "x" ; then ++ if test "${ac_cv_header_libpri_h+set}" = set; then ++ { echo "$as_me:$LINENO: checking for libgsmat.h" >&5 ++echo $ECHO_N "checking for libgsmat.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_libgsmat_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_libgsmat_h" >&5 ++echo "${ECHO_T}$ac_cv_header_libgsmat_h" >&6; } ++else ++ # Is the header compilable? ++{ echo "$as_me:$LINENO: checking libgsmat.h usability" >&5 ++echo $ECHO_N "checking libgsmat.h usability... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++$ac_includes_default ++#include ++_ACEOF ++rm -f conftest.$ac_objext ++if { (ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_compile") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then ++ ac_header_compiler=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_compiler=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 ++echo "${ECHO_T}$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ echo "$as_me:$LINENO: checking libgsmat.h presence" >&5 ++echo $ECHO_N "checking libgsmat.h presence... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++#include ++_ACEOF ++if { (ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } >/dev/null && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then ++ ac_header_preproc=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_preproc=no ++fi ++ ++rm -f conftest.err conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 ++echo "${ECHO_T}$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in ++ yes:no: ) ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: accepted by the compiler, rejected by the preprocessor!" >&5 ++echo "$as_me: WARNING: libgsmat.h: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: proceeding with the compiler's result" >&5 ++echo "$as_me: WARNING: libgsmat.h: proceeding with the compiler's result" >&2;} ++ ac_header_preproc=yes ++ ;; ++ no:yes:* ) ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: present but cannot be compiled" >&5 ++echo "$as_me: WARNING: libgsmat.h: present but cannot be compiled" >&2;} ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: check for missing prerequisite headers?" >&5 ++echo "$as_me: WARNING: libgsmat.h: check for missing prerequisite headers?" >&2;} ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: see the Autoconf documentation" >&5 ++echo "$as_me: WARNING: libgsmat.h: see the Autoconf documentation" >&2;} ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: section \"Present But Cannot Be Compiled\"" >&5 ++echo "$as_me: WARNING: libgsmat.h: section \"Present But Cannot Be Compiled\"" >&2;} ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: proceeding with the preprocessor's result" >&5 ++echo "$as_me: WARNING: libgsmat.h: proceeding with the preprocessor's result" >&2;} ++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: in the future, the compiler will take precedence" >&5 ++echo "$as_me: WARNING: libgsmat.h: in the future, the compiler will take precedence" >&2;} ++ ++ ;; ++esac ++{ echo "$as_me:$LINENO: checking for libgsmat.h" >&5 ++echo $ECHO_N "checking for libgsmat.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_libgsmat_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_cv_header_libgsmat_h=$ac_header_preproc ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_libgsmat_h" >&5 ++echo "${ECHO_T}$ac_cv_header_libgsmat_h" >&6; } ++ ++fi ++ ++ ++ fi ++ fi ++ if test "x${GSMAT_HEADER_FOUND}" = "x0" ; then ++ if test -n "${GSMAT_MANDATORY}" ; ++ then ++ { echo "$as_me:$LINENO: ***" >&5 ++echo "$as_me: ***" >&6;} ++ { echo "$as_me:$LINENO: *** It appears that you do not have the GSMAT development package installed." >&5 ++echo "$as_me: *** It appears that you do not have the GSMAT development package installed." >&6;} ++ { echo "$as_me:$LINENO: *** Please install it to include ${GSMAT_DESCRIP} support, or re-run configure" >&5 ++echo "$as_me: *** Please install it to include ${GSMAT_DESCRIP} support, or re-run configure" >&6;} ++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${GSMAT_OPTION}" >&5 ++echo "$as_me: *** without explicitly specifying --with-${GSMAT_OPTION}" >&6;} ++ exit 1 ++ fi ++ GSMAT_LIB="" ++ GSMAT_INCLUDE="" ++ PBX_GSMAT=0 ++ else ++ PBX_GSMAT=1 ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_GSMAT 1 ++_ACEOF ++ ++fi + + if test "${USE_PWLIB}" != "no"; then + if test -n "${PWLIB_DIR}"; then diff --git a/asterisk-chan_bluetooth.patch b/asterisk-chan_bluetooth.patch new file mode 100644 index 0000000..9c8a44a --- /dev/null +++ b/asterisk-chan_bluetooth.patch @@ -0,0 +1,3225 @@ +diff -urN asterisk-1.4.0-beta3-o-o/build_tools/menuselect-deps.in asterisk-1.4.0-beta3/build_tools/menuselect-deps.in +--- asterisk-1.4.0-beta3-o-o/build_tools/menuselect-deps.in 2006-09-19 11:07:22.000000000 -0600 ++++ asterisk-1.4.0-beta3/build_tools/menuselect-deps.in 2006-11-06 12:45:04.000000000 -0700 +@@ -1,4 +1,5 @@ + ASOUND=@PBX_ALSA@ ++BLUETOOTH=@PBX_BLUETOOTH@ + CURL=@PBX_CURL@ + FREETDS=@PBX_FREETDS@ + GSM=@PBX_GSM@ +diff -urN asterisk-1.4.0-beta3-o-o/channels/chan_bluetooth.c asterisk-1.4.0-beta3/channels/chan_bluetooth.c +--- asterisk-1.4.0-beta3-o-o/channels/chan_bluetooth.c 1969-12-31 17:00:00.000000000 -0700 ++++ asterisk-1.4.0-beta3/channels/chan_bluetooth.c 2006-11-06 12:44:39.000000000 -0700 +@@ -0,0 +1,3145 @@ ++/* ++ * Asterisk -- A telephony toolkit for Linux. ++ * ++ * Asterisk Bluetooth Channel ++ * ++ * Author: Theo Zourzouvillys ++ * ++ * Adaptive Linux Solutions ++ * ++ * Copyright (C) 2004 Adaptive Linux Solutions ++ * ++ * This program is free software, distributed under the terms of ++ * the GNU General Public License ++ * ++ * ******************* NOTE NOTE NOTE NOTE NOTE ********************* ++ * ++ * This code is not at all tested, and only been developed with a ++ * HBH-200 headset and a Nokia 6310i right now. ++ * ++ * Expect it to crash, dial random numbers, and steal all your money. ++ * ++ * PLEASE try any headsets and phones, and let me know the results, ++ * working or not, along with all debug output! ++ * ++ * ------------------------------------------------------------------ ++ * ++ * Asterisk Bluetooth Support ++ * ++ * Well, here we go - Attempt to provide Handsfree profile support in ++ * both AG and HF modes, AG (AudioGateway) mode support for using ++ * headsets, and HF (Handsfree) mode for utilising mobile/cell phones ++ * ++ * It would be nice to also provide Headset support at some time in ++ * the future, however, a working Handsfree profile is nice for now, ++ * and as far as I can see, almost all new HS devices also support HF ++ * ++ * ------------------------------------------------------------------ ++ * INSTRUCTIONS ++ * ++ * You need to have bluez's bluetooth stack, along with user space ++ * tools (>=v2.10), and running hcid and sdsp. ++ * ++ * See bluetooth.conf for configuration details. ++ * ++ * - Ensure bluetooth subsystem is up and running. 'hciconfig' ++ * should show interface as UP. ++ * ++ * - If you're trying to use a headset/HS, start up asterisk, and try ++ * to pair it as you normally would. ++ * ++ * - If you're trying to use a Phone/AG, just make sure bluetooth is ++ * enabled on your phone, and start up asterisk. ++ * ++ * - 'bluetooth show peers' will show all bluetooth devices states. ++ * ++ * - Send a call out by using Dial(BLT/DevName/0123456). Call a HS ++ * with Dial(BLT/DevName) ++ * ++ * ------------------------------------------------------------------ ++ * BUGS ++ * ++ * - What should happen when an AG is paired with asterisk and ++ * someone uses the AG dalling a number manually? My test phone ++ * seems to try to open an SCO link. Perhaps an extension to ++ * route the call to, or maybe drop the RFCOM link all together? ++ * ++ * ------------------------------------------------------------------ ++ * COMPATIBILITY ++ * ++ * PLEASE email with the results of ANY ++ * device not listed in here (working or not), or if the device is ++ * listed and it doesn't work! Please also email full debug output ++ * for any device not working correctly or generating errors in log. ++ * ++ * HandsFree Profile: ++ * ++ * HS (HeadSet): ++ * - Ericsson HBH-200 ++ * ++ * AG (AudioGateway): ++ * - Nokia 6310i ++ * ++ * ------------------------------------------------------------------ ++ * ++ * Questions, bugs, or (preferably) patches to: ++ * ++ * ++ * ++ * ------------------------------------------------------------------ ++ */ ++ ++/*! \file ++ * ++ * \brief Channel driver for Bluetooth phones and headsets ++ * ++ * \author Theo Zourzouvillys ++ * ++ * \par See also ++ * \arg \ref Config_bluetooth ++ * ++ * \ingroup channel_drivers ++ */ ++ ++ ++/*** MODULEINFO ++ bluetooth ++ ***/ ++ ++ ++/* ---------------------------------- */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* --- Data types and definitions --- */ ++ ++#ifndef HANDSFREE_AUDIO_GW_SVCLASS_ID ++# define HANDSFREE_AUDIO_GW_SVCLASS_ID 0x111f ++#endif ++ ++#define BLUETOOTH_FORMAT AST_FORMAT_SLINEAR ++#define BLT_CHAN_NAME "BLT" ++#define BLT_CONFIG_FILE "bluetooth.conf" ++#define BLT_RDBUFF_MAX 1024 ++#define BLT_DEFAULT_HCI_DEV 0 ++#define BLT_SVN_REVISION "$Rev: 38 $" ++ ++/* ---------------------------------- */ ++ ++typedef enum { ++ BLT_ROLE_NONE = 0, // Unknown Device ++ BLT_ROLE_HS = 1, // Device is a Headset ++ BLT_ROLE_AG = 2 // Device is an Audio Gateway ++} blt_role_t; ++ ++/* State when we're in HS mode */ ++ ++typedef enum { ++ BLT_STATE_WANT_R = 0, ++ BLT_STATE_WANT_N = 1, ++ BLT_STATE_WANT_CMD = 2, ++ BLT_STATE_WANT_N2 = 3, ++} blt_state_t; ++ ++typedef enum { ++ BLT_STATUS_DOWN, ++ BLT_STATUS_CONNECTING, ++ BLT_STATUS_NEGOTIATING, ++ BLT_STATUS_READY, ++ BLT_STATUS_RINGING, ++ BLT_STATUS_IN_CALL, ++} blt_status_t; ++ ++/* ---------------------------------- */ ++ ++/* Default config settings */ ++ ++#define BLT_DEFAULT_CHANNEL_AG 5 ++#define BLT_DEFAULT_CHANNEL_HS 6 ++#define BLT_DEFAULT_ROLE BLT_ROLE_HS ++#define BLT_OBUF_LEN (48 * 25) ++ ++#define BUFLEN 4800 ++ ++/* ---------------------------------- */ ++ ++typedef struct blt_dev blt_dev_t; ++ ++// XXX:T: Tidy this lot up. ++struct blt_dev { ++ ++ blt_status_t status; /* Device Status */ ++ ++ struct ast_channel * owner; /* Channel we belong to, possibly NULL */ ++ blt_dev_t * dev; /* The bluetooth device channel is for */ ++ struct ast_frame fr; /* Recieved frame */ ++ ++ /* SCO Handler */ ++ int sco_pipe[2]; /* SCO alert pipe */ ++ int sco; /* SCO fd */ ++ int sco_handle; /* SCO Handle */ ++ int sco_mtu; /* SCO MTU */ ++ int sco_running; /* 1 when sCO thread should be running */ ++ pthread_t sco_thread; /* SCO thread */ ++ ast_mutex_t sco_lock; /* SCO lock */ ++ int sco_pos_in; /* Reader in position */ ++ int sco_pos_out; /* Reader out position */ ++ int sco_sending; /* Sending SCO packets */ ++ char buf[1024]; /* Incoming data buffer */ ++ char sco_buf_out[BUFLEN+1]; /* 24 chunks of 48 */ ++ char sco_buf_in[BUFLEN+1]; /* 24 chunks of 48 */ ++ ++ char dnid[1024]; /* Outgoi gncall dialed number */ ++ unsigned char * obuf[BLT_OBUF_LEN]; /* Outgoing data buffer */ ++ int obuf_len; /* Output Buffer Position */ ++ int obuf_wpos; /* Buffer Reader */ ++ ++ // device ++ int autoconnect; /* 1 for autoconnect */ ++ int outgoing_id; /* Outgoing connection scheduler id */ ++ char * name; /* Devices friendly name */ ++ blt_role_t role; /* Device role (HS or AG) */ ++ bdaddr_t bdaddr; /* remote address */ ++ int channel; /* remote channel */ ++ int rd; /* RFCOMM fd */ ++ int tmp_rd; /* RFCOMM fd */ ++ int call_cnt; /* Number of attempted calls */ ++ ast_mutex_t lock; /* RFCOMM socket lock */ ++ char rd_buff[BLT_RDBUFF_MAX]; /* RFCOMM input buffer */ ++ int rd_buff_pos; /* RFCOMM input buffer position */ ++ int ready; /* 1 When ready */ ++ ++ /* AG mode */ ++ char last_ok_cmd[BLT_RDBUFF_MAX]; /* Runtime[AG]: Last AT command that was OK */ ++ int cind; /* Runtime[AG]: Recieved +CIND */ ++ int call_pos, service_pos, callsetup_pos; /* Runtime[AG]: Positions in CIND/CMER */ ++ int call, service, callsetup; /* Runtime[AG]: Values */ ++ ++ /* HS mode */ ++ blt_state_t state; /* Runtime: Device state (AG mode only) */ ++ int ring_timer; /* Runtime:Ring Timer */ ++ char last_err_cmd[BLT_RDBUFF_MAX]; /* Runtime: Last AT command that was OK */ ++ void (*cb)(blt_dev_t * dev, char * str); /* Runtime: Callback when in HS mode */ ++ ++ int brsf; /* Runtime: Bluetooth Retrieve Supported Features */ ++ int bvra; /* Runtime: Bluetooth Voice Recognised Activation */ ++ int gain_speaker; /* Runtime: Gain Of Speaker */ ++ int clip; /* Runtime: Supports CLID */ ++ int colp; /* Runtime: Connected Line ID */ ++ int elip; /* Runtime: (Ericsson) Supports CLID */ ++ int eolp; /* Runtime: (Ericsson) Connected Line ID */ ++ int ringing; /* Runtime: Device is ringing */ ++ ++ blt_dev_t * next; /* Next in linked list */ ++ ++}; ++ ++typedef struct blt_atcb { ++ ++ /* The command */ ++ char * str; ++ ++ /* DTE callbacks: */ ++ int (*set)(blt_dev_t * dev, const char * arg, int len); ++ int (*read)(blt_dev_t * dev); ++ int (*execute)(blt_dev_t * dev, const char * data); ++ int (*test)(blt_dev_t * dev); ++ ++ /* DCE callbacks: */ ++ int (*unsolicited)(blt_dev_t * dev, const char * value); ++ ++} blt_atcb_t; ++ ++/* ---------------------------------- */ ++ ++static void rd_close(blt_dev_t * dev, int reconnect, int err); ++static int send_atcmd(blt_dev_t * device, const char * fmt, ...); ++static int sco_connect(blt_dev_t * dev); ++ ++/* ---------------------------------- */ ++ ++/* RFCOMM channel we listen on*/ ++static int rfcomm_channel_ag = BLT_DEFAULT_CHANNEL_AG; ++static int rfcomm_channel_hs = BLT_DEFAULT_CHANNEL_HS; ++ ++/* Address of local bluetooth interface */ ++static int hcidev_id; ++static bdaddr_t local_bdaddr; ++ ++/* All the current sockets */ ++AST_MUTEX_DEFINE_STATIC(iface_lock); ++static blt_dev_t * iface_head; ++static int ifcount = 0; ++ ++static int sdp_record_hs = -1; ++static int sdp_record_ag = -1; ++ ++/* RFCOMM listen socket */ ++static int rfcomm_sock_ag = -1; ++static int rfcomm_sock_hs = -1; ++static int sco_socket = -1; ++ ++static int monitor_pid = -1; ++ ++/* The socket monitoring thread */ ++static pthread_t monitor_thread = AST_PTHREADT_NULL; ++AST_MUTEX_DEFINE_STATIC(monitor_lock); ++ ++/* Cound how many times this module is currently in use */ ++static int usecnt = 0; ++AST_MUTEX_DEFINE_STATIC(usecnt_lock); ++ ++static struct sched_context * sched = NULL; ++ ++/* ---------------------------------- */ ++ ++static const char * ++role2str(blt_role_t role) ++{ ++ switch (role) { ++ case BLT_ROLE_HS: ++ return "HS"; ++ case BLT_ROLE_AG: ++ return "AG"; ++ case BLT_ROLE_NONE: ++ return "??"; ++ } ++} ++ ++static const char * ++status2str(blt_status_t status) ++{ ++ switch (status) { ++ case BLT_STATUS_DOWN: ++ return "Down"; ++ case BLT_STATUS_CONNECTING: ++ return "Connecting"; ++ case BLT_STATUS_NEGOTIATING: ++ return "Negotiating"; ++ case BLT_STATUS_READY: ++ return "Ready"; ++ case BLT_STATUS_RINGING: ++ return "Ringing"; ++ case BLT_STATUS_IN_CALL: ++ return "InCall"; ++ }; ++ return "Unknown"; ++} ++ ++int sock_err(int fd) ++{ ++ int ret; ++ int len = sizeof(ret); ++ getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len); ++ return ret; ++} ++ ++/* ---------------------------------- */ ++ ++static const char * ++parse_cind(const char * str, char * name, int name_len) ++{ ++ int c = 0; ++ ++ memset(name, 0, name_len); ++ ++ while (*str) { ++ if (*str == '(') { ++ if (++c == 1 && *(str+1) == '"') { ++ const char * start = str + 2; ++ int len = 0; ++ str += 2; ++ while (*str && *str != '"') { ++ len++; ++ str++; ++ } ++ if (len == 0) ++ return NULL; ++ strncpy(name, start, (len > name_len) ? name_len : len); ++ } ++ } else if (*str == ')') ++ c--; ++ else if (c == 0 && *str == ',') ++ return str + 1; ++ str++; ++ } ++ return NULL; ++} ++ ++static void ++set_cind(blt_dev_t * dev, int indicator, int val) ++{ ++ ++ ast_log(LOG_DEBUG, "CIND %d set to %d\n", indicator, val); ++ ++ if (indicator == dev->callsetup_pos) { ++ ++ // call progress ++ ++ dev->callsetup = val; ++ ++ switch (val) { ++ case 3: ++ // Outgoign ringing ++ if (dev->owner && dev->role == BLT_ROLE_AG) ++ ast_queue_control(dev->owner, AST_CONTROL_RINGING); ++ break; ++ case 2: ++ break; ++ case 1: ++ break; ++ case 0: ++ if (dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0) ++ ast_queue_control(dev->owner, AST_CONTROL_CONGESTION); ++ break; ++ } ++ ++ } else if (indicator == dev->service_pos) { ++ ++ // Signal ++ ++ if (val == 0) ++ ast_log(LOG_NOTICE, "Audio Gateway %s lost signal\n", dev->name); ++ else if (dev->service == 0 && val > 0) ++ ast_log(LOG_NOTICE, "Audio Gateway %s got signal\n", dev->name); ++ ++ dev->service = val; ++ ++ } else if (indicator == dev->call_pos) { ++ ++ // Call ++ ++ dev->call = val; ++ ++ if (dev->owner) { ++ if (val == 1) { ++ ast_queue_control(dev->owner, AST_CONTROL_ANSWER); ++ } else if (val == 0) ++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP); ++ } ++ ++ } ++ ++ ++} ++ ++/* ---------------------------------- */ ++ ++int ++set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len) ++{ ++ int start_pos = *(pos); ++ int done = 0; ++ int copy; ++ ++ while (data_len) { ++ // Set can_do to the most we can do in this copy. ++ ++ copy = MIN(circular_len - start_pos, data_len); ++ memcpy(ring + start_pos, data + done, copy); ++ ++ done += copy; ++ start_pos += copy; ++ data_len -= copy; ++ ++ if (start_pos == circular_len) ++ start_pos = 0; ++ } ++ *(pos) = start_pos; ++ return 0; ++} ++ ++int ++get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy) ++{ ++ int copy; ++ ++ // |1|2|3|4|5|6|7|8|9| ++ // |-----| ++ ++ while (to_copy) { ++ ++ // Set can_do to the most we can do in this copy. ++ copy = MIN(ring_size - *head, to_copy); ++ ++ // ast_log(LOG_DEBUG, "Getting: %d bytes, From pos %d\n", copy, *head); ++ memcpy(dst, ring + *head, copy); ++ ++ dst += copy; ++ *head += copy; ++ to_copy -= copy; ++ ++ if (*head == ring_size ) ++ *head = 0; ++ ++ } ++ ++ return 0; ++} ++ ++/* Handle SCO audio sync. ++ * ++ * If we are the MASTER, then we control the timing, ++ * in 48 byte chunks. If we're the SLAVE, we send ++ * as and when we recieve a packet. ++ * ++ * Because of packet/timing nessecity, we ++ * start up a thread when we're passing audio, so ++ * that things are timed exactly right. ++ * ++ * sco_thread() is the function that handles it. ++ * ++ */ ++ ++static void * ++sco_thread(void * data) ++{ ++ blt_dev_t * dev = (blt_dev_t*)data; ++ int res; ++ struct pollfd pfd[2]; ++ int in_pos = 0; ++ int out_pos = 0; ++ char c = 1; ++ int sending; ++ char buf[1024]; ++ int len; ++ ++ // Avoid deadlock in odd circumstances ++ ++ ast_log(LOG_DEBUG, "SCO thread started on fd %d, pid %d\n", dev->sco, getpid()); ++ ++ // dev->status = BLT_STATUS_IN_CALL; ++ // ast_queue_control(dev->owner, AST_CONTROL_ANSWER); ++ // Set buffer to silence, just incase. ++ ++ ast_mutex_lock(&(dev->sco_lock)); ++ ++ memset(dev->sco_buf_in, 0x7f, BUFLEN); ++ memset(dev->sco_buf_out, 0x7f, BUFLEN); ++ ++ dev->sco_pos_in = 0; ++ dev->sco_pos_out = 0; ++ ++ ast_mutex_unlock(&(dev->sco_lock)); ++ ++ while (1) { ++ ++ ast_mutex_lock(&(dev->sco_lock)); ++ ++ if (dev->sco_running != 1) { ++ ast_log(LOG_DEBUG, "SCO stopped.\n"); ++ break; ++ } ++ ++ pfd[0].fd = dev->sco; ++ pfd[0].events = POLLIN; ++ ++ pfd[1].fd = dev->sco_pipe[1]; ++ pfd[1].events = POLLIN; ++ ++ ast_mutex_unlock(&(dev->sco_lock)); ++ ++ res = poll(pfd, 2, 50); ++ ++ if (res == -1 && errno != EINTR) { ++ ast_log(LOG_DEBUG, "SCO poll() error\n"); ++ break; ++ } ++ ++ if (res == 0) ++ continue; ++ ++ ast_mutex_lock(&(dev->sco_lock)); ++ ++ if (pfd[0].revents & POLLIN) { ++ ++ len = read(dev->sco, buf, 48); ++ ++ if (len) { ++ ast_mutex_lock(&(dev->lock)); ++ set_buffer(dev->sco_buf_in, buf, BUFLEN, &in_pos, len); ++ get_buffer(buf, dev->sco_buf_out, BUFLEN, &out_pos, len); ++ write(dev->sco, buf, len); ++ if (dev->owner && dev->owner->_state == AST_STATE_UP) ++ write(dev->sco_pipe[1], &c, 1); ++ ast_mutex_unlock(&(dev->lock)); ++ } ++ ++ ast_mutex_unlock(&(dev->sco_lock)); ++ ++ } else if (pfd[0].revents) { ++ ++ int e = sock_err(pfd[0].fd); ++ ast_log(LOG_ERROR, "SCO connection error: %s (errno %d)\n", strerror(e), e); ++ break; ++ ++ } else if (pfd[1].revents & POLLIN) { ++ ++ int len; ++ ++ len = read(pfd[1].fd, &c, 1); ++ sending = (sending) ? 0 : 1; ++ ++ ast_mutex_unlock(&(dev->sco_lock)); ++ ++ } else if (pfd[1].revents) { ++ ++ int e = sock_err(pfd[1].fd); ++ ast_log(LOG_ERROR, "SCO pipe connection event %d on pipe[1]=%d: %s (errno %d)\n", pfd[1].revents, pfd[1].fd, strerror(e), e); ++ break; ++ ++ } else { ++ ast_log(LOG_NOTICE, "Unhandled poll output\n"); ++ ast_mutex_unlock(&(dev->sco_lock)); ++ } ++ ++ } ++ ++ ast_mutex_lock(&(dev->lock)); ++ close(dev->sco); ++ dev->sco = -1; ++ dev->sco_running = -1; ++ ast_mutex_unlock(&(dev->sco_lock)); ++ if (dev->owner) ++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP); ++ ast_mutex_unlock(&(dev->lock)); ++ ast_log(LOG_DEBUG, "SCO thread stopped\n"); ++ return NULL; ++} ++ ++/* Start SCO thread. Must be called with dev->lock */ ++ ++static int ++sco_start(blt_dev_t * dev, int fd) ++{ ++ ++ if (dev->sco_pipe[1] <= 0) { ++ ast_log(LOG_ERROR, "SCO pipe[1] == %d\n", dev->sco_pipe[1]); ++ return -1; ++ } ++ ++ ast_mutex_lock(&(dev->sco_lock)); ++ ++ if (dev->sco_running != -1) { ++ ast_log(LOG_ERROR, "Tried to start SCO thread while already running\n"); ++ ast_mutex_unlock(&(dev->sco_lock)); ++ return -1; ++ } ++ ++ if (dev->sco == -1) { ++ if (fd > 0) { ++ dev->sco = fd; ++ } else if (sco_connect(dev) != 0) { ++ ast_log(LOG_ERROR, "SCO fd invalid\n"); ++ ast_mutex_unlock(&(dev->sco_lock)); ++ return -1; ++ } ++ } ++ ++ dev->sco_running = 1; ++ ++ if (ast_pthread_create(&(dev->sco_thread), NULL, sco_thread, dev) < 0) { ++ ast_log(LOG_ERROR, "Unable to start SCO thread.\n"); ++ dev->sco_running = -1; ++ ast_mutex_unlock(&(dev->sco_lock)); ++ return -1; ++ } ++ ++ ast_mutex_unlock(&(dev->sco_lock)); ++ ++ return 0; ++} ++ ++/* Stop SCO thread. Must be called with dev->lock */ ++ ++static int ++sco_stop(blt_dev_t * dev) ++{ ++ ast_mutex_lock(&(dev->sco_lock)); ++ if (dev->sco_running == 1) ++ dev->sco_running = 0; ++ else ++ dev->sco_running = -1; ++ dev->sco_sending = 0; ++ ast_mutex_unlock(&(dev->sco_lock)); ++ return 0; ++} ++ ++/* ---------------------------------- */ ++ ++/* Answer the call. Call with lock held on device */ ++ ++static int ++answer(blt_dev_t * dev) ++{ ++ ++ if ( (!dev->owner) || (dev->ready != 1) || (dev->status != BLT_STATUS_READY && dev->status != BLT_STATUS_RINGING)) { ++ ast_log(LOG_ERROR, "Attempt to answer() in invalid state (owner=%p, ready=%d, status=%s)\n", ++ dev->owner, dev->ready, status2str(dev->status)); ++ return -1; ++ } ++ ++ // dev->sd = sco_connect(&local_bdaddr, &(dev->bdaddr), NULL, NULL, 0); ++ // dev->status = BLT_STATUS_IN_CALL; ++ // dev->owner->fds[0] = dev->sd; ++ // if we are answering (hitting button): ++ ast_queue_control(dev->owner, AST_CONTROL_ANSWER); ++ // if asterisk signals us to answer: ++ // ast_setstate(ast, AST_STATE_UP); ++ ++ /* Start SCO link */ ++ sco_start(dev, -1); ++ return 0; ++} ++ ++/* ---------------------------------- */ ++ ++static int ++blt_write(struct ast_channel * ast, struct ast_frame * frame) ++{ ++ blt_dev_t * dev = ast->pvt->pvt; ++ ++ /* Write a frame of (presumably voice) data */ ++ ++ if (frame->frametype != AST_FRAME_VOICE) { ++ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype); ++ return 0; ++ } ++ ++ if (!(frame->subclass & BLUETOOTH_FORMAT)) { ++ ast_log(LOG_WARNING, "Cannot handle frames in format %d\n", frame->subclass); ++ return 0; ++ } ++ ++ if (ast->_state != AST_STATE_UP) { ++ return 0; ++ } ++ ++ ast_mutex_lock(&(dev->sco_lock)); ++ set_buffer(dev->sco_buf_out, frame->data, BUFLEN, &(dev->sco_pos_out), MIN(frame->datalen, BUFLEN)); ++ ast_mutex_unlock(&(dev->sco_lock)); ++ ++ return 0; ++ ++} ++ ++static struct ast_frame * ++blt_read(struct ast_channel * ast) ++{ ++ blt_dev_t * dev = ast->pvt->pvt; ++ char c = 1; ++ int len; ++ ++ /* Some nice norms */ ++ ++ dev->fr.datalen = 0; ++ dev->fr.samples = 0; ++ dev->fr.data = NULL; ++ dev->fr.src = BLT_CHAN_NAME; ++ dev->fr.offset = 0; ++ dev->fr.mallocd = 0; ++ dev->fr.delivery.tv_sec = 0; ++ dev->fr.delivery.tv_usec = 0; ++ ++ ast_mutex_lock(&(dev->sco_lock)); ++ dev->sco_sending = 1; ++ read(dev->sco_pipe[0], &c, 1); ++ len = get_buffer(dev->buf, dev->sco_buf_in, BUFLEN, &(dev->sco_pos_in), 48); ++ ast_mutex_unlock(&(dev->sco_lock)); ++ ++ dev->fr.data = dev->buf; ++ dev->fr.samples = len / 2; ++ dev->fr.datalen = len; ++ dev->fr.frametype = AST_FRAME_VOICE; ++ dev->fr.subclass = BLUETOOTH_FORMAT; ++ dev->fr.offset = 0; ++ ++ return &dev->fr; ++} ++ ++/* Escape Any '"' in str. Return malloc()ed string */ ++static char * ++escape_str(char * str) ++{ ++ char * ptr = str; ++ char * pret; ++ char * ret; ++ int len = 0; ++ ++ while (*ptr) { ++ if (*ptr == '"') ++ len++; ++ len++; ++ ptr++; ++ } ++ ++ ret = malloc(len + 1); ++ pret = memset(ret, 0, len + 1); ++ ++ ptr = str; ++ ++ while (*ptr) { ++ if (*ptr == '"') ++ *pret++ = '\\'; ++ *pret++ = *ptr++; ++ } ++ ++ return ret; ++} ++ ++static int ++ring_hs(blt_dev_t * dev) ++{ ++#if (ASTERISK_VERSION_NUM < 010100) ++ char tmp[AST_MAX_EXTENSION]; ++ char *name, *num; ++#endif ++ ++ ast_mutex_lock(&(dev->lock)); ++ ++ if (dev->owner == NULL) { ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++ } ++ ++ dev->ringing = 1; ++ dev->status = BLT_STATUS_RINGING; ++ ++ send_atcmd(dev, "RING"); ++ ++ dev->owner->rings++; ++ ++ // XXX:T: '"' needs to be escaped in ELIP. ++ ++#if (ASTERISK_VERSION_NUM < 010100) ++ ++ if (dev->owner->callerid) { ++ ++ memset(tmp, 0, sizeof(tmp)); ++ strncpy(tmp, dev->owner->callerid, sizeof(tmp)-1); ++ ++ if (!ast_callerid_parse(tmp, &name, &num)) { ++ ++ if (dev->clip && num) ++ send_atcmd(dev, "+CLIP: \"%s\",129", num); ++ ++ if (dev->elip && name) { ++ char * esc = escape_str(name); ++ send_atcmd(dev, "*ELIP: \"%s\"", esc); ++ free(esc); ++ } ++ } ++ } ++ ++ ++#else ++ ++ if (dev->clip && dev->owner->cid.cid_num) ++ send_atcmd(dev, "+CLIP: \"%s\",129", dev->owner->cid.cid_num); ++ ++ if (dev->elip && dev->owner->cid.cid_name) { ++ char * esc = escape_str(dev->owner->cid.cid_name); ++ send_atcmd(dev, "*ELIP: \"%s\"", esc); ++ free(esc); ++ } ++ ++#endif ++ ++ ast_mutex_unlock(&(dev->lock)); ++ ++ return 1; ++} ++ ++/* ++ * If the HS is already connected, then just send RING, otherwise, things get a ++ * little more sticky. We first have to find the channel for HS using SDP, ++ * then intiate the connection. Once we've done that, we can start the call. ++ */ ++ ++static int ++blt_call(struct ast_channel * ast, char * dest, int timeout) ++{ ++ blt_dev_t * dev = ast->pvt->pvt; ++ ++ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ++ ast_log(LOG_WARNING, "blt_call called on %s, neither down nor reserved\n", ast->name); ++ return -1; ++ } ++ ++ ast_log(LOG_DEBUG, "Calling %s on %s [t: %d]\n", dest, ast->name, timeout); ++ ++ if (ast_mutex_lock(&iface_lock)) { ++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n"); ++ return -1; ++ } ++ ++// ast_mutex_lock(&(dev->lock)); ++ ++ if (dev->ready == 0) { ++ ast_log(LOG_WARNING, "Tried to call a device not ready/connected.\n"); ++ ast_setstate(ast, AST_CONTROL_CONGESTION); ++// ast_mutex_unlock(&(dev->lock)); ++ ast_mutex_unlock(&iface_lock); ++ return 0; ++ } ++ ++ if (dev->role == BLT_ROLE_HS) { ++ ++ send_atcmd(dev, "+CIEV: 3,1"); ++ ++ dev->ring_timer = ast_sched_add(sched, 5000, AST_SCHED_CB(ring_hs), dev); ++ ++ ring_hs(dev); ++ ++ ast_setstate(ast, AST_STATE_RINGING); ++ ast_queue_control(ast, AST_CONTROL_RINGING); ++ ++ } else if (dev->role == BLT_ROLE_AG) { ++ ++ send_atcmd(dev, "ATD%s;", dev->dnid); ++ ++ } else { ++ ++ ast_setstate(ast, AST_CONTROL_CONGESTION); ++ ast_log(LOG_ERROR, "Unknown device role\n"); ++ ++ } ++ ++// ast_mutex_unlock(&(dev->lock)); ++ ast_mutex_unlock(&iface_lock); ++ ++ return 0; ++} ++ ++static int ++blt_hangup(struct ast_channel * ast) ++{ ++ blt_dev_t * dev = ast->pvt->pvt; ++ ++ ast_log(LOG_DEBUG, "blt_hangup(%s)\n", ast->name); ++ ++ if (!ast->pvt->pvt) { ++ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); ++ return 0; ++ } ++ ++ if (ast_mutex_lock(&iface_lock)) { ++ ast_log(LOG_ERROR, "Failed to get iface_lock\n"); ++ return 0; ++ } ++ ++ ast_mutex_lock(&(dev->lock)); ++ ++ sco_stop(dev); ++ dev->sco_sending = 0; ++ ++ if (dev->role == BLT_ROLE_HS) { ++ ++ if (dev->ringing == 0) { ++ // Actual call in progress ++ send_atcmd(dev, "+CIEV: 2,0"); ++ } else { ++ ++ // Just ringing still ++ ++ if (dev->role == BLT_ROLE_HS) ++ send_atcmd(dev, "+CIEV: 3,0"); ++ ++ if (dev->ring_timer >= 0) ++ ast_sched_del(sched, dev->ring_timer); ++ ++ dev->ring_timer = -1; ++ dev->ringing = 0; ++ ++ } ++ ++ } else if (dev->role == BLT_ROLE_AG) { ++ ++ // Cancel call. ++ send_atcmd(dev, "AT+CHUP"); ++ ++ } ++ ++ if (dev->status == BLT_STATUS_IN_CALL || dev->status == BLT_STATUS_RINGING) ++ dev->status = BLT_STATUS_READY; ++ ++ ast->pvt->pvt = NULL; ++ dev->owner = NULL; ++ ast_mutex_unlock(&(dev->lock)); ++ ast_setstate(ast, AST_STATE_DOWN); ++ ast_mutex_unlock(&(iface_lock)); ++ ++ return 0; ++} ++ ++static int ++blt_indicate(struct ast_channel * c, int condition) ++{ ++ ast_log(LOG_DEBUG, "blt_indicate (%d)\n", condition); ++ ++ switch(condition) { ++ case AST_CONTROL_RINGING: ++ return -1; ++ default: ++ ast_log(LOG_WARNING, "Don't know how to condition %d\n", condition); ++ break; ++ } ++ return -1; ++} ++ ++static int ++blt_answer(struct ast_channel * ast) ++{ ++ blt_dev_t * dev = ast->pvt->pvt; ++ ++ ast_mutex_lock(&dev->lock); ++ ++ // if (dev->ring_timer >= 0) ++ // ast_sched_del(sched, dev->ring_timer); ++ // dev->ring_timer = -1; ++ ++ ast_log(LOG_DEBUG, "Answering interface\n"); ++ ++ if (ast->_state != AST_STATE_UP) { ++ send_atcmd(dev, "+CIEV: 2,1"); ++ send_atcmd(dev, "+CIEV: 3,0"); ++ sco_start(dev, -1); ++ ast_setstate(ast, AST_STATE_UP); ++ } ++ ++ ast_mutex_unlock(&dev->lock); ++ ++ return 0; ++} ++ ++static struct ast_channel * ++blt_new(blt_dev_t * dev, int state, const char * context, const char * number) ++{ ++ struct ast_channel * ast; ++ char c = 0; ++ ++ if ((ast = ast_channel_alloc(1)) == NULL) { ++ ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); ++ return NULL; ++ } ++ ++ snprintf(ast->name, sizeof(ast->name), "BLT/%s", dev->name); ++ ++ // ast->fds[0] = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); ++ ++ ast->nativeformats = BLUETOOTH_FORMAT; ++ ast->pvt->rawreadformat = BLUETOOTH_FORMAT; ++ ast->pvt->rawwriteformat = BLUETOOTH_FORMAT; ++ ast->writeformat = BLUETOOTH_FORMAT; ++ ast->readformat = BLUETOOTH_FORMAT; ++ ++ ast_setstate(ast, state); ++ ++ ast->type = BLT_CHAN_NAME; ++ ++ ast->pvt->pvt = dev; ++ ++ ast->pvt->call = blt_call; ++ ast->pvt->indicate = blt_indicate; ++ ast->pvt->hangup = blt_hangup; ++ ast->pvt->read = blt_read; ++ ast->pvt->write = blt_write; ++ ast->pvt->answer = blt_answer; ++ ++ strncpy(ast->context, context, sizeof(ast->context)-1); ++ strncpy(ast->exten, number, sizeof(ast->exten) - 1); ++ ++ ast->language[0] = '\0'; ++ ++ ast->fds[0] = dev->sco_pipe[0]; ++ write(dev->sco_pipe[1], &c, 1); ++ ++ dev->owner = ast; ++ ++ ast_mutex_lock(&usecnt_lock); ++ usecnt++; ++ ast_mutex_unlock(&usecnt_lock); ++ ++ ast_update_use_count(); ++ ++ if (state != AST_STATE_DOWN) { ++ if (ast_pbx_start(ast)) { ++ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast->name); ++ ast_hangup(ast); ++ } ++ } ++ ++ return ast; ++} ++ ++static struct ast_channel * ++#if (ASTERISK_VERSION_NUM < 010100) ++blt_request(char * type, int format, void * local_data) ++#else ++blt_request(const char * type, int format, void * local_data) ++#endif ++{ ++ char * data = (char*)local_data; ++ int oldformat; ++ blt_dev_t * dev = NULL; ++ struct ast_channel * ast = NULL; ++ char * number = data, * dname; ++ ++ dname = strsep(&number, "/"); ++ ++ oldformat = format; ++ ++ format &= BLUETOOTH_FORMAT; ++ ++ if (!format) { ++ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat); ++ return NULL; ++ } ++ ++ ast_log(LOG_DEBUG, "Dialing '%s' via '%s'\n", number, dname); ++ ++ if (ast_mutex_lock(&iface_lock)) { ++ ast_log(LOG_ERROR, "Unable to lock iface_list\n"); ++ return NULL; ++ } ++ ++ dev = iface_head; ++ ++ while (dev) { ++ if (strcmp(dev->name, dname) == 0) { ++ ast_mutex_lock(&(dev->lock)); ++ if (!dev->ready) { ++ ast_log(LOG_ERROR, "Device %s is not connected\n", dev->name); ++ ast_mutex_unlock(&(dev->lock)); ++ ast_mutex_unlock(&iface_lock); ++ return NULL; ++ } ++ break; ++ } ++ dev = dev->next; ++ } ++ ++ ast_mutex_unlock(&iface_lock); ++ ++ if (!dev) { ++ ast_log(LOG_WARNING, "Failed to find device named '%s'\n", dname); ++ return NULL; ++ } ++ ++ if (number && dev->role != BLT_ROLE_AG) { ++ ast_log(LOG_WARNING, "Tried to send a call out on non AG\n"); ++ ast_mutex_unlock(&(dev->lock)); ++ return NULL; ++ } ++ ++ if (dev->role == BLT_ROLE_AG) ++ strncpy(dev->dnid, number, sizeof(dev->dnid) - 1); ++ ++ ast = blt_new(dev, AST_STATE_DOWN, "bluetooth", "s"); ++ ++ ast_mutex_unlock(&(dev->lock)); ++ ++ return ast; ++} ++ ++/* ---------------------------------- */ ++ ++ ++/* ---- AT COMMAND SOCKET STUFF ---- */ ++ ++static int ++send_atcmd(blt_dev_t * dev, const char * fmt, ...) ++{ ++ char buf[1024]; ++ va_list ap; ++ int len; ++ ++ va_start(ap, fmt); ++ len = vsnprintf(buf, 1023, fmt, ap); ++ va_end(ap); ++ ++ if (option_verbose) ++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < %s\n", role2str(dev->role), 10, dev->name, buf); ++ ++ write(dev->rd, "\r\n", 2); ++ len = write(dev->rd, buf, len); ++ write(dev->rd, "\r\n", 2); ++ return (len) ? 0 : -1; ++} ++ ++ ++static int ++send_atcmd_ok(blt_dev_t * dev, const char * cmd) ++{ ++ int len; ++ strncpy(dev->last_ok_cmd, cmd, BLT_RDBUFF_MAX - 1); ++ if (option_verbose) ++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < OK\n", role2str(dev->role), 10, dev->name); ++ len = write(dev->rd, "\r\nOK\r\n", 6); ++ return (len) ? 0 : -1; ++} ++ ++static int ++send_atcmd_error(blt_dev_t * dev) ++{ ++ int len; ++ ++ if (option_verbose) ++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < ERROR\n", role2str(dev->role), 10, dev->name); ++ ++// write(dev->rd, "\r\n", 2); ++// len = write(dev->rd, dev->last_ok_cmd, 5); ++ write(dev->rd, "\r\n", 2); ++ len = write(dev->rd, "ERROR", 5); ++ write(dev->rd, "\r\n", 2); ++ ++ return (len) ? 0 : -1; ++} ++ ++ ++/* ---------------------------------- */ ++ ++/* -- Handle negotiation when we're an AG -- */ ++ ++/* Bluetooth Support */ ++ ++static int ++atcmd_brsf_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ ast_log(LOG_DEBUG, "Device Supports: %s\n", arg); ++ dev->brsf = atoi(arg); ++ send_atcmd(dev, "+BRSF: %d", 23); ++ return 0; ++} ++ ++/* Bluetooth Voice Recognition */ ++ ++static int ++atcmd_bvra_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ ast_log(LOG_WARNING, "+BVRA Not Yet Supported\n"); ++ return -1; ++#if 0 ++ // XXX:T: Fix voice recognition somehow! ++ int action = atoi(arg); ++ ast_log(LOG_DEBUG, "Voice Recognition: %s\n", (a) ? "ACTIVATED" : "DEACTIVATED"); ++ if ((action == 0) & (dev->bvra == 1)) { ++ /* Disable it */ ++ dev->bvra = 0; ++ // XXX:T: Shutdown any active bvra channel ++ ast_log(LOG_DEBUG, "Voice Recognition: DISABLED\n"); ++ } else if ((action == 1) && (dev->bvra == 0)) { ++ /* Enable it */ ++ dev->bvra = 1; ++ // XXX:T: Schedule connection to voice recognition extension/application ++ ast_log(LOG_DEBUG, "Voice Recognition: ENABLED\n"); ++ } else { ++ ast_log(LOG_ERROR, "+BVRA out of sync (we think %d, but HS wants %d)\n", dev->bvra, action); ++ return -1; ++ } ++ return 0; ++#endif ++} ++ ++/* Clock */ ++ ++static int ++atcmd_cclk_read(blt_dev_t * dev) ++{ ++ struct tm t, *tp; ++ const time_t ti = time(0); ++ tp = localtime_r(&ti, &t); ++ send_atcmd(dev, "+CCLK: \"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"", ++ (tp->tm_year % 100), (tp->tm_mon + 1), (tp->tm_mday), ++ tp->tm_hour, tp->tm_min, tp->tm_sec, ((tp->tm_gmtoff / 60) / 15)); ++ return 0; ++} ++ ++/* CHUP - Hangup Call */ ++ ++static int ++atcmd_chup_execute(blt_dev_t * dev, const char * data) ++{ ++ if (!dev->owner) { ++ ast_log(LOG_ERROR, "Request to hangup call when none in progress\n"); ++ return -1; ++ } ++ ast_log(LOG_DEBUG, "Hangup Call\n"); ++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP); ++ return 0; ++} ++ ++/* CIND - Call Indicator */ ++ ++static int ++atcmd_cind_read(blt_dev_t * dev) ++{ ++ send_atcmd(dev, "+CIND: 1,0,0"); ++ return 0; ++} ++ ++static int ++atcmd_cind_test(blt_dev_t * dev) ++{ ++ send_atcmd(dev, "+CIND: (\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0-4))"); ++ return 0; ++} ++ ++/* Set Language */ ++ ++static int ++atcmd_clan_read(blt_dev_t * dev) ++{ ++ send_atcmd(dev, "+CLAN: \"en\""); ++ return 0; ++} ++ ++/* Caller Id Presentation */ ++ ++static int ++atcmd_clip_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ dev->clip = atoi(arg); ++ return 0; ++} ++ ++/* Conneced Line Identification Presentation */ ++ ++static int ++atcmd_colp_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ dev->colp = atoi(arg); ++ return 0; ++} ++ ++/* CMER - Mobile Equipment Event Reporting */ ++ ++static int ++atcmd_cmer_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ dev->ready = 1; ++ dev->status = BLT_STATUS_READY; ++ return 0; ++} ++ ++/* PhoneBook Types: ++ * ++ * - FD - SIM Fixed Dialing Phone Book ++ * - ME - ME Phone book ++ * - SM - SIM Phone Book ++ * - DC - ME dialled-calls list ++ * - RC - ME recieved-calls lisr ++ * - MC - ME missed-calls list ++ * - MV - ME Voice Activated Dialing List ++ * - HP - Hierachial Phone Book ++ * - BC - Own Business Card (PIN2 required) ++ * ++ */ ++ ++/* Read Phone Book Entry */ ++ ++static int ++atcmd_cpbr_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ // XXX:T: Fix the phone book! ++ // * Maybe add res_phonebook or something? */ ++ send_atcmd(dev, "+CPBR: %d,\"%s\",128,\"%s\"", atoi(arg), arg, arg); ++ return 0; ++} ++ ++/* Select Phone Book */ ++ ++static int ++atcmd_cpbs_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ // XXX:T: I guess we'll just accept any? ++ return 0; ++} ++ ++static int ++atcmd_cscs_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ // XXX:T: Language ++ return 0; ++} ++ ++static int ++atcmd_eips_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ ast_log(LOG_DEBUG, "Identify Presentation Set: %s=%s\n", ++ (*(arg) == 49) ? "ELIP" : "EOLP", ++ (*(arg+2) == 49) ? "ON" : "OFF"); ++ ++ if (*(arg) == 49) ++ dev->eolp = (*(arg+2) == 49) ? 1 : 0; ++ else ++ dev->elip = (*(arg+2) == 49) ? 1 : 0; ++ ++ return 0; ++} ++ ++/* VGS - Speaker Volume Gain */ ++ ++static int ++atcmd_vgs_set(blt_dev_t * dev, const char * arg, int len) ++{ ++ dev->gain_speaker = atoi(arg); ++ return 0; ++} ++ ++/* Dial */ ++static int ++atcmd_dial_execute(blt_dev_t * dev, const char * data) ++{ ++ char * number = NULL; ++ ++ /* Make sure there is a ';' at the end of the line */ ++ if (*(data + (strlen(data) - 1)) != ';') { ++ ast_log(LOG_WARNING, "Can't dial non-voice right now: %s\n", data); ++ return -1; ++ } ++ ++ number = strndup(data, strlen(data) - 1); ++ ast_log(LOG_NOTICE, "Dial: [%s]\n", number); ++ ++ send_atcmd(dev, "+CIEV: 2,1"); ++ send_atcmd(dev, "+CIEV: 3,0"); ++ ++ sco_start(dev, -1); ++ ++ if (blt_new(dev, AST_STATE_UP, "bluetooth", number) == NULL) { ++ sco_stop(dev); ++ } ++ ++ free(number); ++ ++ return 0; ++} ++ ++/* Answer */ ++ ++static int ++atcmd_answer_execute(blt_dev_t * dev, const char * data) ++{ ++ ++ if (!dev->ringing || !dev->owner) { ++ ast_log(LOG_WARNING, "Can't answer non existant call\n"); ++ return -1; ++ } ++ ++ dev->ringing = 0; ++ ++ if (dev->ring_timer >= 0) ++ ast_sched_del(sched, dev->ring_timer); ++ ++ dev->ring_timer = -1; ++ ++ send_atcmd(dev, "+CIEV: 2,1"); ++ send_atcmd(dev, "+CIEV: 3,0"); ++ ++ return answer(dev); ++} ++ ++static int ++ag_unsol_ciev(blt_dev_t * dev, const char * data) ++{ ++ const char * orig = data; ++ int indicator; ++ int status; ++ ++ while (*(data) && *(data) == ' ') ++ data++; ++ ++ if (*(data) == 0) { ++ ast_log(LOG_WARNING, "Invalid value[1] for '+CIEV:%s'\n", orig); ++ return -1; ++ } ++ ++ indicator = *(data++) - 48; ++ ++ if (*(data++) != ',') { ++ ast_log(LOG_WARNING, "Invalid value[2] for '+CIEV:%s'\n", orig); ++ return -1; ++ } ++ ++ if (*(data) == 0) { ++ ast_log(LOG_WARNING, "Invalid value[3] for '+CIEV:%s'\n", orig); ++ return -1; ++ } ++ ++ status = *(data) - 48; ++ ++ set_cind(dev, indicator, status); ++ ++ return 0; ++} ++ ++static int ++ag_unsol_cind(blt_dev_t * dev, const char * data) ++{ ++ ++ while (*(data) && *(data) == ' ') ++ data++; ++ ++ ++ if (dev->cind == 0) ++ { ++ int pos = 1; ++ char name[1024]; ++ ++ while ((data = parse_cind(data, name, 1023)) != NULL) { ++ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name); ++ if (strcmp(name, "call") == 0) ++ dev->call_pos = pos; ++ else if (strcmp(name, "service") == 0) ++ dev->service_pos = pos; ++ else if (strcmp(name, "call_setup") == 0 || strcmp(name, "callsetup") == 0) ++ dev->callsetup_pos = pos; ++ pos++; ++ } ++ ++ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name); ++ ++ } else { ++ ++ int pos = 1, len = 0; ++ char val[128]; ++ const char * start = data; ++ ++ while (*data) { ++ if (*data == ',') { ++ memset(val, 0, 128); ++ strncpy(val, start, len); ++ set_cind(dev, pos, atoi(val)); ++ pos++; ++ len = 0; ++ data++; ++ start = data; ++ continue; ++ } ++ len++; ++ data++; ++ } ++ ++ memset(val, 0, 128); ++ strncpy(val, start, len); ++ ast_log(LOG_DEBUG, "CIND IND %d set to %d [%s]\n", pos, atoi(val), val); ++ ++ ++ } ++ ++ return 0; ++} ++ ++static blt_atcb_t ++atcmd_list[] = ++{ ++ { "A", NULL, NULL, atcmd_answer_execute, NULL, NULL }, ++ { "D", NULL, NULL, atcmd_dial_execute, NULL, NULL }, ++ { "+BRSF", atcmd_brsf_set, NULL, NULL, NULL, NULL }, ++ { "+BVRA", atcmd_bvra_set, NULL, NULL, NULL, NULL }, ++ { "+CCLK", NULL, atcmd_cclk_read, NULL, NULL, NULL }, ++ { "+CHUP", NULL, NULL, atcmd_chup_execute, NULL, NULL }, ++ { "+CIEV", NULL, NULL, NULL, NULL, ag_unsol_ciev }, ++ { "+CIND", NULL, atcmd_cind_read, NULL, atcmd_cind_test, ag_unsol_cind }, ++ { "+CLAN", NULL, atcmd_clan_read, NULL, NULL, NULL }, ++ { "+CLIP", atcmd_clip_set, NULL, NULL, NULL, NULL }, ++ { "+COLP", atcmd_colp_set, NULL, NULL, NULL, NULL }, ++ { "+CMER", atcmd_cmer_set, NULL, NULL, NULL, NULL }, ++ { "+CPBR", atcmd_cpbr_set, NULL, NULL, NULL, NULL }, ++ { "+CPBS", atcmd_cpbs_set, NULL, NULL, NULL, NULL }, ++ { "+CSCS", atcmd_cscs_set, NULL, NULL, NULL, NULL }, ++ { "*EIPS", atcmd_eips_set, NULL, NULL, NULL, NULL }, ++ { "+VGS", atcmd_vgs_set, NULL, NULL, NULL, NULL }, ++}; ++ ++#define ATCMD_LIST_LEN (sizeof(atcmd_list) / sizeof(blt_atcb_t)) ++ ++/* ---------------------------------- */ ++ ++/* -- Handle negotiation when we're a HS -- */ ++ ++void ++ag_unknown_response(blt_dev_t * dev, char * cmd) ++{ ++ ast_log(LOG_DEBUG, "Got UNKN response: %s\n", cmd); ++ ++ // DELAYED ++ // NO CARRIER ++ ++} ++ ++void ++ag_cgmi_response(blt_dev_t * dev, char * cmd) ++{ ++ // CGMM - Phone Model ++ // CGMR - Phone Revision ++ // CGSN - IMEI ++ // AT* ++ // VTS - send tone ++ // CREG ++ // CBC - BATTERY ++ // CSQ - SIGANL ++ // CSMS - SMS STUFFS ++ // CMGL ++ // CMGR ++ // CMGS ++ // CSCA - sms CENTER NUMBER ++ // CNMI - SMS INDICATION ++ // ast_log(LOG_DEBUG, "Manufacturer: %s\n", cmd); ++ dev->cb = ag_unknown_response; ++} ++ ++void ++ag_cgmi_valid_response(blt_dev_t * dev, char * cmd) ++{ ++ // send_atcmd(dev, "AT+WS46?"); ++ // send_atcmd(dev, "AT+CRC=1"); ++ // send_atcmd(dev, "AT+CNUM"); ++ ++ if (strcmp(cmd, "OK") == 0) { ++ send_atcmd(dev, "AT+CGMI"); ++ dev->cb = ag_cgmi_response; ++ } else { ++ dev->cb = ag_unknown_response; ++ } ++} ++ ++void ++ag_clip_response(blt_dev_t * dev, char * cmd) ++{ ++ send_atcmd(dev, "AT+CGMI=?"); ++ dev->cb = ag_cgmi_valid_response; ++} ++ ++void ++ag_cmer_response(blt_dev_t * dev, char * cmd) ++{ ++ dev->cb = ag_clip_response; ++ dev->ready = 1; ++ dev->status = BLT_STATUS_READY; ++ send_atcmd(dev, "AT+CLIP=1"); ++} ++ ++void ++ag_cind_status_response(blt_dev_t * dev, char * cmd) ++{ ++ // XXX:T: Handle response. ++ dev->cb = ag_cmer_response; ++ send_atcmd(dev, "AT+CMER=3,0,0,1"); ++ // Initiase SCO link! ++} ++ ++void ++ag_cind_response(blt_dev_t * dev, char * cmd) ++{ ++ dev->cb = ag_cind_status_response; ++ dev->cind = 1; ++ send_atcmd(dev, "AT+CIND?"); ++} ++ ++void ++ag_brsf_response(blt_dev_t * dev, char * cmd) ++{ ++ dev->cb = ag_cind_response; ++ ast_log(LOG_DEBUG, "Bluetooth features: %s\n", cmd); ++ dev->cind = 0; ++ send_atcmd(dev, "AT+CIND=?"); ++} ++ ++/* ---------------------------------- */ ++ ++static int ++sdp_register(sdp_session_t * session) ++{ ++ // XXX:T: Fix this horrible function so it makes some sense and is extensible! ++ sdp_list_t *svclass_id, *pfseq, *apseq, *root; ++ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; ++ sdp_profile_desc_t profile; ++ sdp_list_t *aproto, *proto[2]; ++ sdp_record_t record; ++ uint8_t u8 = rfcomm_channel_ag; ++ uint8_t u8_hs = rfcomm_channel_hs; ++ sdp_data_t *channel; ++ int ret = 0; ++ ++ memset((void *)&record, 0, sizeof(sdp_record_t)); ++ record.handle = 0xffffffff; ++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); ++ root = sdp_list_append(0, &root_uuid); ++ sdp_set_browse_groups(&record, root); ++ ++ // Register as an AG ++ ++ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID); ++ svclass_id = sdp_list_append(0, &svclass_uuid); ++ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); ++ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); ++ sdp_set_service_classes(&record, svclass_id); ++ sdp_uuid16_create(&profile.uuid, 0x111f); ++ profile.version = 0x0100; ++ pfseq = sdp_list_append(0, &profile); ++ ++ sdp_set_profile_descs(&record, pfseq); ++ ++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); ++ proto[0] = sdp_list_append(0, &l2cap_uuid); ++ apseq = sdp_list_append(0, proto[0]); ++ ++ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); ++ proto[1] = sdp_list_append(0, &rfcomm_uuid); ++ channel = sdp_data_alloc(SDP_UINT8, &u8); ++ proto[1] = sdp_list_append(proto[1], channel); ++ apseq = sdp_list_append(apseq, proto[1]); ++ ++ aproto = sdp_list_append(0, apseq); ++ sdp_set_access_protos(&record, aproto); ++ ++ sdp_set_info_attr(&record, "Voice Gateway", 0, 0); ++ ++ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) { ++ ast_log(LOG_ERROR, "Service Record registration failed\n"); ++ ret = -1; ++ goto end; ++ } ++ ++ sdp_record_ag = record.handle; ++ ++ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n"); ++ ++ sdp_data_free(channel); ++ sdp_list_free(proto[0], 0); ++ sdp_list_free(proto[1], 0); ++ sdp_list_free(apseq, 0); ++ sdp_list_free(aproto, 0); ++ ++ // ------------- ++ ++ memset((void *)&record, 0, sizeof(sdp_record_t)); ++ record.handle = 0xffffffff; ++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); ++ root = sdp_list_append(0, &root_uuid); ++ sdp_set_browse_groups(&record, root); ++ ++ // Register as an HS ++ ++ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID); ++ svclass_id = sdp_list_append(0, &svclass_uuid); ++ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); ++ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); ++ sdp_set_service_classes(&record, svclass_id); ++ sdp_uuid16_create(&profile.uuid, 0x111e); ++ profile.version = 0x0100; ++ pfseq = sdp_list_append(0, &profile); ++ sdp_set_profile_descs(&record, pfseq); ++ ++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); ++ proto[0] = sdp_list_append(0, &l2cap_uuid); ++ apseq = sdp_list_append(0, proto[0]); ++ ++ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); ++ proto[1] = sdp_list_append(0, &rfcomm_uuid); ++ channel = sdp_data_alloc(SDP_UINT8, &u8_hs); ++ proto[1] = sdp_list_append(proto[1], channel); ++ apseq = sdp_list_append(apseq, proto[1]); ++ ++ aproto = sdp_list_append(0, apseq); ++ sdp_set_access_protos(&record, aproto); ++ sdp_set_info_attr(&record, "Voice Gateway", 0, 0); ++ ++ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) { ++ ast_log(LOG_ERROR, "Service Record registration failed\n"); ++ ret = -1; ++ goto end; ++ } ++ ++ sdp_record_hs = record.handle; ++ ++ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n"); ++ ++end: ++ sdp_data_free(channel); ++ sdp_list_free(proto[0], 0); ++ sdp_list_free(proto[1], 0); ++ sdp_list_free(apseq, 0); ++ sdp_list_free(aproto, 0); ++ ++ return ret; ++} ++ ++static int ++rfcomm_listen(bdaddr_t * bdaddr, int channel) ++{ ++ ++ int sock = -1; ++ struct sockaddr_rc loc_addr; ++ int on = 1; ++ ++ if ((sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { ++ ast_log(LOG_ERROR, "Can't create socket: %s (errno: %d)\n", strerror(errno), errno); ++ return -1; ++ } ++ ++ loc_addr.rc_family = AF_BLUETOOTH; ++ ++ /* Local Interface Address */ ++ bacpy(&loc_addr.rc_bdaddr, bdaddr); ++ ++ /* Channel */ ++ loc_addr.rc_channel = channel; ++ ++ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) { ++ ast_log(LOG_ERROR, "Can't bind socket: %s (errno: %d)\n", strerror(errno), errno); ++ close(sock); ++ return -1; ++ } ++ ++ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { ++ ast_log(LOG_ERROR, "Set socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno)); ++ close(sock); ++ return -1; ++ } ++ ++ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0) ++ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n"); ++ ++ if (listen(sock, 10) < 0) { ++ ast_log(LOG_ERROR,"Can not listen on the socket. %s(%d)\n", strerror(errno), errno); ++ close(sock); ++ return -1; ++ } ++ ++ ast_log(LOG_NOTICE, "Listening for RFCOMM channel %d connections on FD %d\n", channel, sock); ++ ++ return sock; ++} ++ ++ ++static int ++sco_listen(bdaddr_t * bdaddr) ++{ ++ int sock = -1; ++ int on = 1; ++ struct sockaddr_sco loc_addr; ++ ++ memset(&loc_addr, 0, sizeof(loc_addr)); ++ ++ if ((sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { ++ ast_log(LOG_ERROR, "Can't create SCO socket: %s (errno: %d)\n", strerror(errno), errno); ++ return -1; ++ } ++ ++ loc_addr.sco_family = AF_BLUETOOTH; ++ bacpy(&loc_addr.sco_bdaddr, BDADDR_ANY); ++ ++ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) { ++ ast_log(LOG_ERROR, "Can't bind SCO socket: %s (errno: %d)\n", strerror(errno), errno); ++ close(sock); ++ return -1; ++ } ++ ++ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { ++ ast_log(LOG_ERROR, "Set SCO socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno)); ++ close(sock); ++ return -1; ++ } ++ ++ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0) ++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n"); ++ ++ if (listen(sock, 10) < 0) { ++ ast_log(LOG_ERROR,"Can not listen on SCO socket: %s(%d)\n", strerror(errno), errno); ++ close(sock); ++ return -1; ++ } ++ ++ ast_log(LOG_NOTICE, "Listening for SCO connections on FD %d\n", sock); ++ ++ return sock; ++} ++ ++static int ++rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, int channel, int nbio) ++{ ++ struct sockaddr_rc addr; ++ int s; ++ ++ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.rc_family = AF_BLUETOOTH; ++ bacpy(&addr.rc_bdaddr, src); ++ addr.rc_channel = 0; ++ ++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ close(s); ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.rc_family = AF_BLUETOOTH; ++ bacpy(&addr.rc_bdaddr, dst); ++ addr.rc_channel = channel; ++ ++ if (nbio) { ++ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0) ++ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n"); ++ } ++ ++ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 && (nbio != 1 || (errno != EAGAIN))) { ++ close(s); ++ return -1; ++ } ++ ++ return s; ++} ++ ++/* Must be called with dev->lock held */ ++ ++static int ++sco_connect(blt_dev_t * dev) ++{ ++ struct sockaddr_sco addr; ++ // struct sco_conninfo conn; ++ // struct sco_options opts; ++ // int size; ++ // bdaddr_t * src = &local_bdaddr; ++ ++ int s; ++ bdaddr_t * dst = &(dev->bdaddr); ++ ++ if (dev->sco != -1) { ++ ast_log(LOG_ERROR, "SCO fd already open.\n"); ++ return -1; ++ } ++ ++ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { ++ ast_log(LOG_ERROR, "Can't create SCO socket(): %s\n", strerror(errno)); ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ ++ addr.sco_family = AF_BLUETOOTH; ++ bacpy(&addr.sco_bdaddr, BDADDR_ANY); ++ ++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ ast_log(LOG_ERROR, "Can't bind() SCO socket: %s\n", strerror(errno)); ++ close(s); ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.sco_family = AF_BLUETOOTH; ++ bacpy(&addr.sco_bdaddr, dst); ++ ++ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0) ++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n"); ++ ++ if ((connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) && (errno != EAGAIN)) { ++ ast_log(LOG_ERROR, "Can't connect() SCO socket: %s (errno %d)\n", strerror(errno), errno); ++ close(s); ++ return -1; ++ } ++ ++ //size = sizeof(conn); ++ ++ ++/* XXX:T: HERE, fix getting SCO conninfo. ++ ++ if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) { ++ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno)); ++ close(s); ++ return -1; ++ } ++ ++ size = sizeof(opts); ++ ++ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) { ++ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno)); ++ close(s); ++ return -1; ++ } ++ ++ dev->sco_handle = conn.hci_handle; ++ dev->sco_mtu = opts.mtu; ++ ++*/ ++ ++ ast_log(LOG_DEBUG, "SCO: %d\n", s); ++ ++ dev->sco = s; ++ ++ return 0; ++} ++ ++ ++/* ---------------------------------- */ ++ ++/* Non blocking (async) outgoing bluetooth connection */ ++ ++static int ++try_connect(blt_dev_t * dev) ++{ ++ int fd; ++ ast_mutex_lock(&(dev->lock)); ++ ++ if (dev->status != BLT_STATUS_CONNECTING && dev->status != BLT_STATUS_DOWN) { ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++ } ++ ++ if (dev->rd != -1) { ++ ++ int ret; ++ struct pollfd pfd; ++ ++ if (dev->status != BLT_STATUS_CONNECTING) { ++ ast_mutex_unlock(&(dev->lock)); ++ dev->outgoing_id = -1; ++ return 0; ++ } ++ ++ // ret = connect(dev->rd, (struct sockaddr *)&(dev->addr), sizeof(struct sockaddr_rc)); // ++ ++ pfd.fd = dev->rd; ++ pfd.events = POLLIN | POLLOUT; ++ ++ ret = poll(&pfd, 1, 0); ++ ++ if (ret == -1) { ++ close(dev->rd); ++ dev->rd = -1; ++ dev->status = BLT_STATUS_DOWN; ++ dev->outgoing_id = ast_sched_add(sched, 10000, AST_SCHED_CB(try_connect), dev); ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++ } ++ ++ if (ret > 0) { ++ ++ int len = sizeof(ret); ++ getsockopt(dev->rd, SOL_SOCKET, SO_ERROR, &ret, &len); ++ ++ if (ret == 0) { ++ ++ ast_log(LOG_NOTICE, "Initialised bluetooth link to device %s\n", dev->name); ++ ++#if 0 ++ { ++ struct hci_conn_info_req * cr; ++ int dd; ++ char name[248]; ++ ++ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); ++ dd = hci_open_dev(hcidev_id); ++ cr->type = ACL_LINK; ++ bacpy(&cr->bdaddr, &(dev->bdaddr)); ++ ++ if (ioctl(dd, HCIGETCONNINFO, (unsigned long)cr) < 0) { ++ ast_log(LOG_ERROR, "Failed to get connection info: %s\n", strerror(errno)); ++ } else { ++ ast_log(LOG_DEBUG, "HCI Handle: %d\n", cr->conn_info->handle); ++ } ++ ++ if (hci_read_remote_name(dd, &(dev->bdaddr), sizeof(name), name, 25000) == 0) ++ ast_log(LOG_DEBUG, "Remote Name: %s\n", name); ++ free(cr); ++ } ++#endif ++ ++ dev->status = BLT_STATUS_NEGOTIATING; ++ ++ /* If this device is a AG, we initiate the negotiation. */ ++ ++ if (dev->role == BLT_ROLE_AG) { ++ dev->cb = ag_brsf_response; ++ send_atcmd(dev, "AT+BRSF=23"); ++ } ++ ++ dev->outgoing_id = -1; ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++ ++ } else { ++ ++ if (ret != EHOSTDOWN) ++ ast_log(LOG_NOTICE, "Connect to device %s failed: %s (errno %d)\n", dev->name, strerror(ret), ret); ++ ++ close(dev->rd); ++ dev->rd = -1; ++ dev->status = BLT_STATUS_DOWN; ++ dev->outgoing_id = ast_sched_add(sched, (ret == EHOSTDOWN) ? 10000 : 2500, AST_SCHED_CB(try_connect), dev); ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++ ++ } ++ ++ } ++ ++ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev); ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++ } ++ ++ fd = rfcomm_connect(&local_bdaddr, &(dev->bdaddr), dev->channel, 1); ++ ++ if (fd == -1) { ++ ast_log(LOG_WARNING, "NBIO connect() to %s returned %d: %s\n", dev->name, errno, strerror(errno)); ++ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev); ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++ } ++ ++ dev->rd = fd; ++ dev->status = BLT_STATUS_CONNECTING; ++ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev); ++ ast_mutex_unlock(&(dev->lock)); ++ return 0; ++} ++ ++ ++/* Called whenever a new command is recieved while we're the AG */ ++ ++ ++static int ++process_rfcomm_cmd(blt_dev_t * dev, char * cmd) ++{ ++ int i; ++ char * fullcmd = cmd; ++ ++ if (option_verbose) ++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, cmd); ++ ++ /* Read the 'AT' from the start of the string */ ++ if (strncmp(cmd, "AT", 2)) { ++ ast_log(LOG_WARNING, "Unknown command without 'AT': %s\n", cmd); ++ send_atcmd_error(dev); ++ return 0; ++ } ++ ++ cmd += 2; ++ ++ // Don't forget 'AT' on it's own is OK. ++ ++ if (strlen(cmd) == 0) { ++ send_atcmd_ok(dev, fullcmd); ++ return 0; ++ } ++ ++ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) { ++ if (strncmp(atcmd_list[i].str, cmd, strlen(atcmd_list[i].str)) == 0) { ++ char * pos = (cmd + strlen(atcmd_list[i].str)); ++ if ((strncmp(pos, "=?", 2) == 0) && (strlen(pos) == 2)) { ++ /* TEST command */ ++ if (atcmd_list[i].test) { ++ if (atcmd_list[i].test(dev) == 0) ++ send_atcmd_ok(dev, fullcmd); ++ else ++ send_atcmd_error(dev); ++ } else { ++ send_atcmd_ok(dev, fullcmd); ++ } ++ } else if ((strncmp(pos, "?", 1) == 0) && (strlen(pos) == 1)) { ++ /* READ command */ ++ if (atcmd_list[i].read) { ++ if (atcmd_list[i].read(dev) == 0) ++ send_atcmd_ok(dev, fullcmd); ++ else ++ send_atcmd_error(dev); ++ } else { ++ ast_log(LOG_WARNING, "AT Command: '%s' missing READ function\n", fullcmd); ++ send_atcmd_error(dev); ++ } ++ } else if (strncmp(pos, "=", 1) == 0) { ++ /* SET command */ ++ if (atcmd_list[i].set) { ++ if (atcmd_list[i].set(dev, (pos + 1), (*(pos + 1)) ? strlen(pos + 1) : 0) == 0) ++ send_atcmd_ok(dev, fullcmd); ++ else ++ send_atcmd_error(dev); ++ } else { ++ ast_log(LOG_WARNING, "AT Command: '%s' missing SET function\n", fullcmd); ++ send_atcmd_error(dev); ++ } ++ } else { ++ /* EXECUTE command */ ++ if (atcmd_list[i].execute) { ++ if (atcmd_list[i].execute(dev, cmd + strlen(atcmd_list[i].str)) == 0) ++ send_atcmd_ok(dev, fullcmd); ++ else ++ send_atcmd_error(dev); ++ } else { ++ ast_log(LOG_WARNING, "AT Command: '%s' missing EXECUTE function\n", fullcmd); ++ send_atcmd_error(dev); ++ } ++ } ++ return 0; ++ } ++ } ++ ++ ast_log(LOG_WARNING, "Unknown AT Command: '%s' (%s)\n", fullcmd, cmd); ++ send_atcmd_error(dev); ++ ++ return 0; ++} ++ ++/* Called when a socket is incoming */ ++ ++static void ++handle_incoming(int fd, blt_role_t role) ++{ ++ blt_dev_t * dev; ++ struct sockaddr_rc addr; ++ int len = sizeof(addr); ++ ++ // Got a new incoming socket. ++ ast_log(LOG_DEBUG, "Incoming RFCOMM socket\n"); ++ ++ ast_mutex_lock(&iface_lock); ++ ++ fd = accept(fd, (struct sockaddr*)&addr, &len); ++ ++ dev = iface_head; ++ while (dev) { ++ if (bacmp(&(dev->bdaddr), &addr.rc_bdaddr) == 0) { ++ ast_log(LOG_DEBUG, "Connect from %s\n", dev->name); ++ ast_mutex_lock(&(dev->lock)); ++ /* Kill any outstanding connect attempt. */ ++ if (dev->outgoing_id > -1) { ++ ast_sched_del(sched, dev->outgoing_id); ++ dev->outgoing_id = -1; ++ } ++ ++ rd_close(dev, 0, 0); ++ ++ dev->status = BLT_STATUS_NEGOTIATING; ++ dev->rd = fd; ++ ++ if (dev->role == BLT_ROLE_AG) { ++ dev->cb = ag_brsf_response; ++ send_atcmd(dev, "AT+BRSF=23"); ++ } ++ ++ ast_mutex_unlock(&(dev->lock)); ++ break; ++ } ++ dev = dev->next; ++ } ++ ++ if (dev == NULL) { ++ ast_log(LOG_WARNING, "Connect from unknown device\n"); ++ close(fd); ++ } ++ ast_mutex_unlock(&iface_lock); ++ ++ return; ++} ++ ++static void ++handle_incoming_sco(int master) ++{ ++ ++ blt_dev_t * dev; ++ struct sockaddr_sco addr; ++ struct sco_conninfo conn; ++ struct sco_options opts; ++ int len = sizeof(addr); ++ int fd; ++ ++ ast_log(LOG_DEBUG, "Incoming SCO socket\n"); ++ ++ fd = accept(master, (struct sockaddr*)&addr, &len); ++ ++ if (fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK) != 0) { ++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n"); ++ close(fd); ++ return; ++ } ++ ++ len = sizeof(conn); ++ ++ if (getsockopt(fd, SOL_SCO, SCO_CONNINFO, &conn, &len) < 0) { ++ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno)); ++ close(fd); ++ return; ++ } ++ ++ len = sizeof(opts); ++ ++ if (getsockopt(fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) { ++ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno)); ++ close(fd); ++ return; ++ } ++ ++ ast_mutex_lock(&iface_lock); ++ dev = iface_head; ++ while (dev) { ++ if (bacmp(&(dev->bdaddr), &addr.sco_bdaddr) == 0) { ++ ast_log(LOG_DEBUG, "SCO Connect from %s\n", dev->name); ++ ast_mutex_lock(&(dev->lock)); ++ if (dev->sco_running != -1) { ++ ast_log(LOG_ERROR, "Incoming SCO socket, but SCO thread already running.\n"); ++ } else { ++ sco_start(dev, fd); ++ } ++ ast_mutex_unlock(&(dev->lock)); ++ break; ++ } ++ dev = dev->next; ++ } ++ ++ ast_mutex_unlock(&iface_lock); ++ ++ if (dev == NULL) { ++ ast_log(LOG_WARNING, "SCO Connect from unknown device\n"); ++ close(fd); ++ } else { ++ // XXX:T: We need to handle the fact we might have an outgoing connection attempt in progress. ++ ast_log(LOG_DEBUG, "SCO: %d, HCIHandle=%d, MUT=%d\n", fd, conn.hci_handle, opts.mtu); ++ } ++ ++ ++ ++ return; ++} ++ ++/* Called when there is data waiting on a socket */ ++ ++static int ++handle_rd_data(blt_dev_t * dev) ++{ ++ char c; ++ int ret; ++ ++ while ((ret = read(dev->rd, &c, 1)) == 1) { ++ ++ // log_buf[i++] = c; ++ ++ if (dev->role == BLT_ROLE_HS) { ++ ++ if (c == '\r') { ++ ret = process_rfcomm_cmd(dev, dev->rd_buff); ++ dev->rd_buff_pos = 0; ++ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX); ++ return ret; ++ } ++ ++ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX) ++ return 0; ++ ++ dev->rd_buff[dev->rd_buff_pos++] = c; ++ ++ } else if (dev->role == BLT_ROLE_AG) { ++ ++ switch (dev->state) { ++ ++ case BLT_STATE_WANT_R: ++ if (c == '\r') { ++ dev->state = BLT_STATE_WANT_N; ++ } else if (c == '+') { ++ dev->state = BLT_STATE_WANT_CMD; ++ dev->rd_buff[dev->rd_buff_pos++] = '+'; ++ } else { ++ ast_log(LOG_ERROR, "Device %s: Expected '\\r', got %d. state=BLT_STATE_WANT_R\n", dev->name, c); ++ return -1; ++ } ++ break; ++ ++ case BLT_STATE_WANT_N: ++ if (c == '\n') ++ dev->state = BLT_STATE_WANT_CMD; ++ else { ++ ast_log(LOG_ERROR, "Device %s: Expected '\\n', got %d. state=BLT_STATE_WANT_N\n", dev->name, c); ++ return -1; ++ } ++ break; ++ ++ case BLT_STATE_WANT_CMD: ++ if (c == '\r') ++ dev->state = BLT_STATE_WANT_N2; ++ else { ++ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX) { ++ ast_log(LOG_ERROR, "Device %s: Buffer exceeded\n", dev->name); ++ return -1; ++ } ++ dev->rd_buff[dev->rd_buff_pos++] = c; ++ } ++ break; ++ ++ case BLT_STATE_WANT_N2: ++ if (c == '\n') { ++ ++ dev->state = BLT_STATE_WANT_R; ++ ++ if (dev->rd_buff[0] == '+') { ++ int i; ++ // find unsolicited ++ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) { ++ if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) { ++ if (atcmd_list[i].unsolicited) ++ atcmd_list[i].unsolicited(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1); ++ else ++ ast_log(LOG_WARNING, "Device %s: Unhandled Unsolicited: %s\n", dev->name, dev->rd_buff); ++ break; ++ } ++ } ++ ++ if (option_verbose) ++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff); ++ ++ if (i == ATCMD_LIST_LEN) ++ ast_log(LOG_DEBUG, "Device %s: Got unsolicited message: %s\n", dev->name, dev->rd_buff); ++ ++ } else { ++ ++ if ( ++ strcmp(dev->rd_buff, "OK") != 0 && ++ strcmp(dev->rd_buff, "CONNECT") != 0 && ++ strcmp(dev->rd_buff, "RING") != 0 && ++ strcmp(dev->rd_buff, "NO CARRIER") != 0 && ++ strcmp(dev->rd_buff, "ERROR") != 0 && ++ strcmp(dev->rd_buff, "NO DIALTONE") != 0 && ++ strcmp(dev->rd_buff, "BUSY") != 0 && ++ strcmp(dev->rd_buff, "NO ANSWER") != 0 && ++ strcmp(dev->rd_buff, "DELAYED") != 0 ++ ){ ++ // It must be a multiline error ++ strncpy(dev->last_err_cmd, dev->rd_buff, 1023); ++ if (option_verbose) ++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff); ++ } else if (dev->cb) { ++ if (option_verbose) ++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff); ++ dev->cb(dev, dev->rd_buff); ++ } else { ++ ast_log(LOG_ERROR, "Device %s: Data on socket in HS mode, but no callback\n", dev->name); ++ } ++ ++ } ++ ++ dev->rd_buff_pos = 0; ++ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX); ++ ++ } else { ++ ++ ast_log(LOG_ERROR, "Device %s: Expected '\\n' got %d. state = BLT_STATE_WANT_N2:\n", dev->name, c); ++ return -1; ++ ++ } ++ ++ break; ++ ++ default: ++ ast_log(LOG_ERROR, "Device %s: Unknown device state %d\n", dev->name, dev->state); ++ return -1; ++ ++ } ++ ++ } ++ ++ } ++ ++ return 0; ++} ++ ++/* Close the devices RFCOMM socket, and SCO if it exists. Must hold dev->lock */ ++ ++static void ++rd_close(blt_dev_t * dev, int reconnect, int e) ++{ ++ dev->ready = 0; ++ ++ if (dev->rd) ++ close(dev->rd); ++ ++ dev->rd = -1; ++ ++ dev->status = BLT_STATUS_DOWN; ++ ++ sco_stop(dev); ++ ++ if (dev->owner) { ++ ast_setstate(dev->owner, AST_STATE_DOWN); ++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP); ++ } ++ ++ /* Schedule a reconnect */ ++ if (reconnect && dev->autoconnect) { ++ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev); ++ ++ if (monitor_thread == pthread_self()) { ++ // Because we're not the monitor thread, we needd to inturrupt poll(). ++ pthread_kill(monitor_thread, SIGURG); ++ } ++ ++ if (e) ++ ast_log(LOG_NOTICE, "Device %s disconnected, scheduled reconnect in 5 seconds: %s (errno %d)\n", dev->name, strerror(e), e); ++ } else if (e) { ++ ast_log(LOG_NOTICE, "Device %s disconnected: %s (errno %d)\n", dev->name, strerror(e), e); ++ } ++ ++ return; ++} ++ ++/* ++ * Remember that we can only add to the scheduler from ++ * the do_monitor thread, as it calculates time to next one from ++ * this loop. ++ */ ++ ++static void * ++do_monitor(void * data) ++{ ++#define SRV_SOCK_CNT 3 ++ ++ int res = 0; ++ blt_dev_t * dev; ++ struct pollfd * pfds = malloc(sizeof(struct pollfd) * (ifcount + SRV_SOCK_CNT)); ++ ++ /* -- We start off by trying to connect all of our devices (non blocking) -- */ ++ ++ monitor_pid = getpid(); ++ ++ if (ast_mutex_lock(&iface_lock)) { ++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n"); ++ return NULL; ++ } ++ ++ dev = iface_head; ++ while (dev) { ++ ++ if (socketpair(PF_UNIX, SOCK_STREAM, 0, dev->sco_pipe) != 0) { ++ ast_log(LOG_ERROR, "Failed to create socket pair: %s (errno %d)\n", strerror(errno), errno); ++ ast_mutex_unlock(&iface_lock); ++ return NULL; ++ } ++ ++ if (dev->autoconnect && dev->status == BLT_STATUS_DOWN) ++ dev->outgoing_id = ast_sched_add(sched, 1500, AST_SCHED_CB(try_connect), dev); ++ dev = dev->next; ++ } ++ ast_mutex_unlock(&iface_lock); ++ ++ /* -- Now, Scan all sockets, and service scheduler -- */ ++ ++ pfds[0].fd = rfcomm_sock_ag; ++ pfds[0].events = POLLIN; ++ ++ pfds[1].fd = rfcomm_sock_hs; ++ pfds[1].events = POLLIN; ++ ++ pfds[2].fd = sco_socket; ++ pfds[2].events = POLLIN; ++ ++ while (1) { ++ int cnt = SRV_SOCK_CNT; ++ int i; ++ ++ /* -- Build pfds -- */ ++ ++ if (ast_mutex_lock(&iface_lock)) { ++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n"); ++ return NULL; ++ } ++ dev = iface_head; ++ while (dev) { ++ ast_mutex_lock(&(dev->lock)); ++ if (dev->rd > 0 && ((dev->status != BLT_STATUS_DOWN) && (dev->status != BLT_STATUS_CONNECTING))) { ++ pfds[cnt].fd = dev->rd; ++ pfds[cnt].events = POLLIN; ++ cnt++; ++ } ++ ast_mutex_unlock(&(dev->lock)); ++ dev = dev->next; ++ } ++ ast_mutex_unlock(&iface_lock); ++ ++ /* -- End Build pfds -- */ ++ ++ res = ast_sched_wait(sched); ++ res = poll(pfds, cnt, MAX(100, MIN(100, res))); ++ ++ if (res == 0) ++ ast_sched_runq(sched); ++ ++ if (pfds[0].revents) { ++ handle_incoming(rfcomm_sock_ag, BLT_ROLE_AG); ++ res--; ++ } ++ ++ if (pfds[1].revents) { ++ handle_incoming(rfcomm_sock_hs, BLT_ROLE_HS); ++ res--; ++ } ++ ++ if (pfds[2].revents) { ++ handle_incoming_sco(sco_socket); ++ res--; ++ } ++ ++ if (res == 0) ++ continue; ++ ++ for (i = SRV_SOCK_CNT ; i < cnt ; i++) { ++ ++ /* Optimise a little bit */ ++ if (res == 0) ++ break; ++ else if (pfds[i].revents == 0) ++ continue; ++ ++ /* -- Find the socket that has activity -- */ ++ ++ if (ast_mutex_lock(&iface_lock)) { ++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n"); ++ return NULL; ++ } ++ ++ dev = iface_head; ++ ++ while (dev) { ++ if (pfds[i].fd == dev->rd) { ++ ast_mutex_lock(&(dev->lock)); ++ if (pfds[i].revents & POLLIN) { ++ if (handle_rd_data(dev) == -1) { ++ rd_close(dev, 0, 0); ++ } ++ } else { ++ rd_close(dev, 1, sock_err(dev->rd)); ++ } ++ ast_mutex_unlock(&(dev->lock)); ++ res--; ++ break; ++ } ++ dev = dev->next; ++ } ++ ++ if (dev == NULL) { ++ ast_log(LOG_ERROR, "Unhandled fd from poll()\n"); ++ close(pfds[i].fd); ++ } ++ ++ ast_mutex_unlock(&iface_lock); ++ ++ /* -- End find socket with activity -- */ ++ ++ } ++ ++ } ++ ++ return NULL; ++} ++ ++static int ++restart_monitor(void) ++{ ++ ++ if (monitor_thread == AST_PTHREADT_STOP) ++ return 0; ++ ++ if (ast_mutex_lock(&monitor_lock)) { ++ ast_log(LOG_WARNING, "Unable to lock monitor\n"); ++ return -1; ++ } ++ ++ if (monitor_thread == pthread_self()) { ++ ast_mutex_unlock(&monitor_lock); ++ ast_log(LOG_WARNING, "Cannot kill myself\n"); ++ return -1; ++ } ++ ++ if (monitor_thread != AST_PTHREADT_NULL) { ++ ++ /* Just signal it to be sure it wakes up */ ++ pthread_cancel(monitor_thread); ++ pthread_kill(monitor_thread, SIGURG); ++ ast_log(LOG_DEBUG, "Waiting for monitor thread to join...\n"); ++ pthread_join(monitor_thread, NULL); ++ ast_log(LOG_DEBUG, "joined\n"); ++ ++ } else { ++ ++ /* Start a new monitor */ ++ if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) { ++ ast_mutex_unlock(&monitor_lock); ++ ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); ++ return -1; ++ } ++ ++ } ++ ++ ast_mutex_unlock(&monitor_lock); ++ return 0; ++} ++ ++static int ++blt_parse_config(void) ++{ ++ struct ast_config * cfg; ++ struct ast_variable * v; ++ char * cat; ++ ++ cfg = ast_load(BLT_CONFIG_FILE); ++ ++ if (!cfg) { ++ ast_log(LOG_NOTICE, "Unable to load Bluetooth config: %s. Bluetooth disabled\n", BLT_CONFIG_FILE); ++ return -1; ++ } ++ ++ v = ast_variable_browse(cfg, "general"); ++ ++ while (v) { ++ if (!strcasecmp(v->name, "rfchannel_ag")) { ++ rfcomm_channel_ag = atoi(v->value); ++ } else if (!strcasecmp(v->name, "rfchannel_hs")) { ++ rfcomm_channel_hs = atoi(v->value); ++ } else if (!strcasecmp(v->name, "interface")) { ++ hcidev_id = atoi(v->value); ++ } else { ++ ast_log(LOG_WARNING, "Unknown config key '%s' in section [general]\n", v->name); ++ } ++ v = v->next; ++ } ++ cat = ast_category_browse(cfg, NULL); ++ ++ while(cat) { ++ ++ char * str; ++ ++ if (strcasecmp(cat, "general")) { ++ blt_dev_t * device = malloc(sizeof(blt_dev_t)); ++ memset(device, 0, sizeof(blt_dev_t)); ++ device->sco_running = -1; ++ device->sco = -1; ++ device->rd = -1; ++ device->outgoing_id = -1; ++ device->status = BLT_STATUS_DOWN; ++ str2ba(cat, &(device->bdaddr)); ++ device->name = ast_variable_retrieve(cfg, cat, "name"); ++ ++ str = ast_variable_retrieve(cfg, cat, "type"); ++ ++ if (str == NULL) { ++ ast_log(LOG_ERROR, "Device [%s] has no role. Specify type=\n", cat); ++ return -1; ++ } else if (strcasecmp(str, "HS") == 0) ++ device->role = BLT_ROLE_HS; ++ else if (strcasecmp(str, "AG") == 0) { ++ device->role = BLT_ROLE_AG; ++ } else { ++ ast_log(LOG_ERROR, "Device [%s] has invalid role '%s'\n", cat, str); ++ return -1; ++ } ++ ++ /* XXX:T: Find channel to use using SDP. ++ * However, this needs to be non blocking, and I can't see ++ * anything in sdp_lib.h that will allow non blocking calls. ++ */ ++ ++ device->channel = 1; ++ ++ if ((str = ast_variable_retrieve(cfg, cat, "channel")) != NULL) ++ device->channel = atoi(str); ++ ++ if ((str = ast_variable_retrieve(cfg, cat, "autoconnect")) != NULL) ++ device->autoconnect = (strcasecmp(str, "yes") == 0 || strcmp(str, "1") == 0) ? 1 : 0; ++ ++ device->next = iface_head; ++ iface_head = device; ++ ifcount++; ++ } ++ ++ cat = ast_category_browse(cfg, cat); ++ } ++ return 0; ++} ++ ++ ++static int ++blt_show_peers(int fd, int argc, char *argv[]) ++{ ++ blt_dev_t * dev; ++ ++ if (ast_mutex_lock(&iface_lock)) { ++ ast_log(LOG_ERROR, "Failed to get Iface lock\n"); ++ ast_cli(fd, "Failed to get iface lock\n"); ++ return RESULT_FAILURE; ++ } ++ ++ dev = iface_head; ++ ++ ast_cli(fd, "BDAddr Name Role Status A/C SCOCon/Fd/Th Sig\n"); ++ ast_cli(fd, "----------------- ---------- ---- ----------- --- ------------ ---\n"); ++ ++ while (dev) { ++ char b1[18]; ++ ba2str(&(dev->bdaddr), b1); ++ ast_cli(fd, "%s %-10s %-4s %-11s %-3s %2d/%02d/%-6ld %s\n", ++ b1, dev->name, (dev->role == BLT_ROLE_HS) ? "HS" : "AG", status2str(dev->status), ++ (dev->autoconnect) ? "Yes" : "No", ++ dev->sco_running, ++ dev->sco, ++ dev->sco_thread, ++ (dev->role == BLT_ROLE_AG) ? (dev->service) ? "Yes" : "No" : "N/A" ++ ); ++ dev = dev->next; ++ } ++ ++ ast_mutex_unlock(&iface_lock); ++ return RESULT_SUCCESS; ++} ++ ++static int ++blt_show_information(int fd, int argc, char *argv[]) ++{ ++ char b1[18]; ++ ba2str(&local_bdaddr, b1); ++ ast_cli(fd, "-------------------------------------------\n"); ++ ast_cli(fd, " Version : %s\n", BLT_SVN_REVISION); ++ ast_cli(fd, " Monitor PID : %d\n", monitor_pid); ++ ast_cli(fd, " RFCOMM AG : Channel %d, FD %d\n", rfcomm_channel_ag, rfcomm_sock_ag); ++ ast_cli(fd, " RFCOMM HS : Channel %d, FD %d\n", rfcomm_channel_hs, rfcomm_sock_hs); ++ ast_cli(fd, " Device : hci%d, MAC Address %s\n", hcidev_id, b1); ++ ast_cli(fd, "-------------------------------------------\n"); ++ return RESULT_SUCCESS; ++} ++ ++static int ++blt_ag_sendcmd(int fd, int argc, char *argv[]) ++{ ++ blt_dev_t * dev; ++ ++ if (argc != 4) ++ return RESULT_SHOWUSAGE; ++ ++ ast_mutex_lock(&iface_lock); ++ dev = iface_head; ++ while (dev) { ++ if (!strcasecmp(argv[2], dev->name)) ++ break; ++ dev = dev->next; ++ } ++ ast_mutex_unlock(&iface_lock); ++ ++ if (!dev) { ++ ast_cli(fd, "Device '%s' does not exist\n", argv[2]); ++ return RESULT_FAILURE; ++ } ++ ++ if (dev->role != BLT_ROLE_AG) { ++ ast_cli(fd, "Device '%s' is not an AudioGateway\n", argv[2]); ++ return RESULT_FAILURE; ++ } ++ ++ if (dev->status == BLT_STATUS_DOWN || dev->status == BLT_STATUS_NEGOTIATING) { ++ ast_cli(fd, "Device '%s' is not connected\n", argv[2]); ++ return RESULT_FAILURE; ++ } ++ ++ if (*(argv[3] + strlen(argv[3]) - 1) == '.') ++ *(argv[3] + strlen(argv[3]) - 1) = '?'; ++ ++ ast_cli(fd, "Sending AT command to %s: %s\n", dev->name, argv[3]); ++ ++ ast_mutex_lock(&(dev->lock)); ++ send_atcmd(dev, argv[3]); ++ ast_mutex_unlock(&(dev->lock)); ++ ++ return RESULT_SUCCESS; ++} ++ ++static char * ++complete_device(char * line, char * word, int pos, int state, int rpos, blt_role_t role) ++{ ++ blt_dev_t * dev; ++ int which = 0; ++ char *ret; ++ ++ if (pos != rpos) ++ return NULL; ++ ++ ast_mutex_lock(&iface_lock); ++ ++ dev = iface_head; ++ ++ while (dev) { ++ ++ if ((dev->role == role) && (!strncasecmp(word, dev->name, strlen(word)))) { ++ if (++which > state) ++ break; ++ } ++ ++ dev = dev->next; ++ } ++ ++ if (dev) ++ ret = strdup(dev->name); ++ else ++ ret = NULL; ++ ++ ast_mutex_unlock(&iface_lock); ++ ++ return ret; ++} ++ ++static char * ++complete_device_2_ag(char * line, char * word, int pos, int state) ++{ ++ return complete_device(line, word, pos, state, 2, BLT_ROLE_AG); ++} ++ ++static char show_peers_usage[] = ++"Usage: bluetooth show peers\n" ++" List all bluetooth peers and their status\n"; ++ ++static struct ast_cli_entry ++cli_show_peers = ++ { { "bluetooth", "show", "peers", NULL }, blt_show_peers, "List Bluetooth Peers", show_peers_usage }; ++ ++ ++static char ag_sendcmd[] = ++"Usage: bluetooth ag sendcmd \n" ++" Sends a AT cmd over the RFCOMM link, and print result (AG only)\n"; ++ ++static struct ast_cli_entry ++cli_ag_sendcmd = ++ { { "bluetooth", "sendcmd", NULL }, blt_ag_sendcmd, "Send AG an AT command", ag_sendcmd, complete_device_2_ag }; ++ ++static char show_information[] = ++"Usage: bluetooth show information\n" ++" Lists information about the bluetooth subsystem\n"; ++ ++static struct ast_cli_entry ++cli_show_information = ++ { { "bluetooth", "show", "information", NULL }, blt_show_information, "List Bluetooth Info", show_information }; ++ ++void ++remove_sdp_records(void) ++{ ++ ++ sdp_session_t * sdp; ++ sdp_list_t * attr; ++ sdp_record_t * rec; ++ int res = -1; ++ uint32_t range = 0x0000ffff; ++ ++ if (sdp_record_ag == -1 || sdp_record_hs == -1) ++ return; ++ ++ ast_log(LOG_DEBUG, "Removing SDP records\n"); ++ ++ sdp = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY); ++ ++ if (!sdp) ++ return; ++ ++ attr = sdp_list_append(0, &range); ++ rec = sdp_service_attr_req(sdp, sdp_record_ag, SDP_ATTR_REQ_RANGE, attr); ++ sdp_list_free(attr, 0); ++ ++ if (rec) ++ if (sdp_record_unregister(sdp, rec) == 0) ++ res = 0; ++ ++ attr = sdp_list_append(0, &range); ++ rec = sdp_service_attr_req(sdp, sdp_record_hs, SDP_ATTR_REQ_RANGE, attr); ++ sdp_list_free(attr, 0); ++ ++ if (rec) ++ if (sdp_record_unregister(sdp, rec) == 0) ++ res = 0; ++ ++ sdp_close(sdp); ++ ++ if (res == 0) ++ ast_log(LOG_NOTICE, "Removed SDP records\n"); ++ else ++ ast_log(LOG_ERROR, "Failed to remove SDP records\n"); ++ ++} ++ ++static int ++__unload_module(void) ++{ ++ ++ ast_channel_unregister(BLT_CHAN_NAME); ++ ++ if (monitor_thread != AST_PTHREADT_NULL) { ++ ++ if (ast_mutex_lock(&monitor_lock)) { ++ ++ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { ++ pthread_cancel(monitor_thread); ++ pthread_kill(monitor_thread, SIGURG); ++ fprintf(stderr, "Waiting for monitor thread to join...\n"); ++ pthread_join(monitor_thread, NULL); ++ fprintf(stderr, "joined\n"); ++ } ++ monitor_thread = AST_PTHREADT_STOP; ++ ast_mutex_unlock(&monitor_lock); ++ ++ } else { ++ ++ ast_log(LOG_WARNING, "Unable to lock the monitor\n"); ++ return -1; ++ ++ } ++ ++ } ++ ++ ast_unregister_atexit(remove_sdp_records); ++ remove_sdp_records(); ++ return 0; ++} ++ ++int ++load_module() ++{ ++ sdp_session_t * sess; ++ int dd; ++ uint16_t vs; ++ ++ hcidev_id = BLT_DEFAULT_HCI_DEV; ++ ++ if (blt_parse_config() != 0) { ++ ast_log(LOG_ERROR, "Bluetooth configuration error. Bluetooth Disabled\n"); ++ return unload_module(); ++ } ++ ++ dd = hci_open_dev(hcidev_id); ++ if (dd == -1) { ++ ast_log(LOG_ERROR, "Unable to open interface hci%d: %s.\n", hcidev_id, strerror(errno)); ++ return -1; ++ } ++ ++ hci_read_voice_setting(dd, &vs, 1000); ++ vs = htobs(vs); ++ close(dd); ++ ++ if (vs != 0x0060) { ++ ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060, not 0x%04x\n", vs); ++ unload_module(); ++ return 0; ++ } ++ ++ if ((sched = sched_context_create()) == NULL) { ++ ast_log(LOG_WARNING, "Unable to create schedule context\n"); ++ return -1; ++ } ++ ++ memset(&local_bdaddr, 0, sizeof(local_bdaddr)); ++ ++ hci_devba(hcidev_id, &local_bdaddr); ++ ++ /* --- Add SDP record --- */ ++ ++ sess = sdp_connect(&local_bdaddr, BDADDR_LOCAL, SDP_RETRY_IF_BUSY); ++ ++ if ((rfcomm_sock_ag = rfcomm_listen(&local_bdaddr, rfcomm_channel_ag)) < 0) { ++ return -1; ++ } ++ ++ if ((rfcomm_sock_hs = rfcomm_listen(&local_bdaddr, rfcomm_channel_hs)) < 0) ++ return -1; ++ ++ if ((sco_socket = sco_listen(&local_bdaddr)) < 0) ++ return -1; ++ ++ if (!sess) { ++ ast_log(LOG_ERROR, "Failed to connect to SDP server: %s\n", strerror(errno)); ++ return -1; ++ } ++ ++ if (sdp_register(sess) != 0) { ++ ast_log(LOG_ERROR, "Failed to register HeadsetAudioGateway in SDP\n"); ++ return -1; ++ } ++ ++ sdp_close(sess); ++ ++ if (restart_monitor() != 0) ++ return -1; ++ ++ if (ast_channel_register(BLT_CHAN_NAME, "Bluetooth Driver", BLUETOOTH_FORMAT, blt_request)) { ++ ast_log(LOG_ERROR, "Unable to register channel class BTL\n"); ++ __unload_module(); ++ return -1; ++ } ++ ++ ast_cli_register(&cli_show_information); ++ ast_cli_register(&cli_show_peers); ++ ast_cli_register(&cli_ag_sendcmd); ++ ++ ast_register_atexit(remove_sdp_records); ++ ++ ast_log(LOG_NOTICE, "Loaded Bluetooth support, %s\n", BLT_SVN_REVISION + 1); ++ ++ return 0; ++} ++ ++int ++unload_module(void) ++{ ++ ast_cli_unregister(&cli_ag_sendcmd); ++ ast_cli_unregister(&cli_show_peers); ++ ast_cli_unregister(&cli_show_information); ++ return __unload_module(); ++} ++ ++int ++usecount() ++{ ++ int res; ++ ast_mutex_lock(&usecnt_lock); ++ res = usecnt; ++ ast_mutex_unlock(&usecnt_lock); ++ return res; ++} ++ ++char *description() ++{ ++ return "Bluetooth Channel Driver"; ++} ++ ++char * ++key() ++{ ++ return ASTERISK_GPL_KEY; ++} ++ ++ +diff -urN asterisk-1.4.0-beta3-o-o/configs/bluetooth.conf.sample asterisk-1.4.0-beta3/configs/bluetooth.conf.sample +--- asterisk-1.4.0-beta3-o-o/configs/bluetooth.conf.sample 1969-12-31 17:00:00.000000000 -0700 ++++ asterisk-1.4.0-beta3/configs/bluetooth.conf.sample 2006-11-06 12:44:39.000000000 -0700 +@@ -0,0 +1,33 @@ ++[general] ++; Channel we listen on as a HS (Headset) ++rfchannel_hs = 2 ++; Channel we listen on as an AG (AudioGateway) ++rfchannel_ag = 3 ++; hci interface to use (number - e.g '0') ++interface = 0 ++ ++;; A HBH-500 Handsfree Kit ++[00:0A:D9:A1:AA:D2] ++; Any name to use, this is what we use to send calls to (BLT/). ++name = HBH-500 ++; IS this a HS or AG? ++type = HS ++; ++; ++; RFCOMM channel to connect to. For a HandsSet: ++; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111E ++; or,for an AudioGateway (Phone): ++; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111F ++; ++; Find the 'channel' value under RFCOMM. ++; ++channel = 2 ++; Automatically conenct? ++autoconnect = yes ++ ++;; A Nokia 6310i ++[00:60:57:1C:00:99] ++name = Neil ++type = AG ++channel = 13 ++autoconnect = yes +diff -urN asterisk-1.4.0-beta3-o-o/configure.ac asterisk-1.4.0-beta3/configure.ac +--- asterisk-1.4.0-beta3-o-o/configure.ac 2006-10-13 09:41:14.000000000 -0600 ++++ asterisk-1.4.0-beta3/configure.ac 2006-11-06 12:44:39.000000000 -0700 +@@ -152,6 +152,7 @@ + # by the --with option name, to make things easier for the users :-) + + AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound]) ++AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth], [bluetooth]) + AST_EXT_LIB_SETUP([CURL], [cURL], [curl]) + AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) + AST_EXT_LIB_SETUP([GNUTLS], [GNU TLS support (used for iksemel only)], [gnutls]) +@@ -314,6 +315,8 @@ + + AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl]) + ++AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [bt_malloc], [bluetooth/bluetooth.h]) ++ + AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h]) + + GSM_INTERNAL="yes" +diff -urN asterisk-1.4.0-beta3-o-o/makeopts.in asterisk-1.4.0-beta3/makeopts.in +--- asterisk-1.4.0-beta3-o-o/makeopts.in 2006-09-19 08:04:15.000000000 -0600 ++++ asterisk-1.4.0-beta3/makeopts.in 2006-11-06 12:44:39.000000000 -0700 +@@ -157,3 +157,6 @@ + + SUPPSERV_INCLUDE=@SUPPSERV_INCLUDE@ + SUPPSERV_LIB=@SUPPSERV_LIB@ ++ ++BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@ ++BLUETOOTH_LIB=@BLUETOOTH_LIB@ diff --git a/asterisk-fix-ptlib.patch b/asterisk-fix-ptlib.patch new file mode 100644 index 0000000..4d4e3ba --- /dev/null +++ b/asterisk-fix-ptlib.patch @@ -0,0 +1,12 @@ +diff -urN asterisk-1.4.0-beta3-o-o/acinclude.m4 asterisk-1.4.0-beta3/acinclude.m4 +--- asterisk-1.4.0-beta3-o-o/acinclude.m4 2006-09-19 11:07:22.000000000 -0600 ++++ asterisk-1.4.0-beta3/acinclude.m4 2006-11-06 12:44:39.000000000 -0700 +@@ -146,7 +146,7 @@ + else + AC_CHECK_FILE(/usr/include/ptlib.h, HAS_PWLIB=1, ) + if test "${HAS_PWLIB:-unset}" != "unset" ; then +- AC_PATH_PROG(PTLIB_CONFIG, ptlib-config, , /usr/share/pwlib/make) ++ AC_PATH_PROG(PTLIB_CONFIG, ptlib-config, , /usr/bin) + PWLIB_INCDIR="/usr/include" + PWLIB_LIBDIR="/usr/lib" + fi diff --git a/asterisk-m4.patch b/asterisk-m4.patch new file mode 100644 index 0000000..e87efe3 --- /dev/null +++ b/asterisk-m4.patch @@ -0,0 +1,34 @@ +--- acinclude.m4~ 2008-07-16 20:41:56.892069130 +0200 ++++ acinclude.m4 2008-07-16 20:42:33.158731990 +0200 +@@ -725,19 +725,7 @@ + # Check for a fully functional sed program that truncates + # as few characters as possible. Prefer GNU sed if found. + AC_DEFUN([AST_PROG_SED], +-[AC_CACHE_CHECK([for a sed that does not truncate output], ac_cv_path_SED, +- [dnl ac_script should not contain more than 99 commands (for HP-UX sed), +- dnl but more than about 7000 bytes, to catch a limit in Solaris 8 /usr/ucb/sed. +- ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ +- for ac_i in 1 2 3 4 5 6 7; do +- ac_script="$ac_script$as_nl$ac_script" +- done +- echo "$ac_script" | sed 99q >conftest.sed +- $as_unset ac_script || ac_script= +- _AC_PATH_PROG_FEATURE_CHECK(SED, [sed gsed], +- [_AC_FEATURE_CHECK_LENGTH([ac_path_SED], [ac_cv_path_SED], +- ["$ac_path_SED" -f conftest.sed])])]) +- SED="$ac_cv_path_SED" ++ [ SED="sed" + AC_SUBST([SED])dnl + rm -f conftest.sed + ])# AST_PROG_SED +--- acinclude.m4~ 2008-07-16 21:00:37.112374194 +0200 ++++ acinclude.m4 2008-07-16 21:02:12.078198664 +0200 +@@ -423,7 +423,7 @@ + AC_DEFUN( + [AST_CHECK_PWLIB_VERSION], [ + if test "${HAS_$2:-unset}" != "unset"; then +- $2_VERSION=`grep "$2_VERSION" ${$2_INCDIR}/$3 | cut -f2 -d ' ' | sed -e 's/"//g'` ++ $2_VERSION=`grep "$2_VERSION" ${$2_INCDIR}/$3 | cut -f3 -d ' ' | sed -e 's/"//g'` + $2_MAJOR_VERSION=`echo ${$2_VERSION} | cut -f1 -d.` + $2_MINOR_VERSION=`echo ${$2_VERSION} | cut -f2 -d.` + $2_BUILD_NUMBER=`echo ${$2_VERSION} | cut -f3 -d.` diff --git a/asterisk-r150557.patch b/asterisk-r150557.patch new file mode 100644 index 0000000..cb3b7c6 --- /dev/null +++ b/asterisk-r150557.patch @@ -0,0 +1,20 @@ +--- branches/1.4/channels/chan_dahdi.c 2008/10/08 22:22:09 147681 ++++ branches/1.4/channels/chan_dahdi.c 2008/10/17 15:31:35 150557 +@@ -3668,7 +3668,7 @@ + { + int res; + struct dahdi_spaninfo zi; +-#if defined(HAVE_DAHDI) || defined(HAVE_ZAPTEL_CHANALARMS) ++#if !defined(HAVE_ZAPTEL) || defined(HAVE_ZAPTEL_CHANALARMS) + /* + * The conditional compilation is needed only in asterisk-1.4 for + * backward compatibility with old zaptel drivers that don't have +@@ -3687,7 +3687,7 @@ + } + if (zi.alarms != DAHDI_ALARM_NONE) + return zi.alarms; +-#if defined(HAVE_DAHDI) || defined(HAVE_ZAPTEL_CHANALARMS) ++#if !defined(HAVE_ZAPTEL) || defined(HAVE_ZAPTEL_CHANALARMS) + /* No alarms on the span. Check for channel alarms. */ + if ((res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, ¶ms)) >= 0) + return params.chan_alarms; diff --git a/asterisk-zhone.patch b/asterisk-zhone.patch new file mode 100644 index 0000000..683b19f --- /dev/null +++ b/asterisk-zhone.patch @@ -0,0 +1,65 @@ +diff -urN asterisk-1.4.11.org/channels/chan_zap.c asterisk-1.4.11/channels/chan_zap.c +--- asterisk-1.4.11.org/channels/chan_zap.c 2007-08-17 23:01:43.000000000 +0200 ++++ asterisk-1.4.11/channels/chan_zap.c 2007-10-06 06:09:46.353781463 +0200 +@@ -137,7 +137,7 @@ + * is entirely unwilling to provide any assistance with their channel banks + * even though their web site says they support their products for life. + */ +-/* #define ZHONE_HACK */ ++#define ZHONE_HACK 1 + + /*! \note + * Define if you want to check the hook state for an FXO (FXS signalled) interface +@@ -3700,21 +3700,17 @@ + ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index); + + if (res & (ZT_EVENT_PULSEDIGIT | ZT_EVENT_DTMFUP)) { +- p->pulsedial = (res & ZT_EVENT_PULSEDIGIT) ? 1 : 0; ++ int pulse = (res & ZT_EVENT_PULSEDIGIT) ? 1 : 0; + +- ast_log(LOG_DEBUG, "Detected %sdigit '%c'\n", p->pulsedial ? "pulse ": "", res & 0xff); +-#ifdef HAVE_PRI +- if (!p->proceeding && p->sig == SIG_PRI && p->pri && p->pri->overlapdial) { +- /* absorb event */ +- } else { +-#endif +- p->subs[index].f.frametype = AST_FRAME_DTMF_END; +- p->subs[index].f.subclass = res & 0xff; +-#ifdef HAVE_PRI ++ ast_log(LOG_DEBUG, "Detected %sdigit '%c'\n", pulse ? "pulse ": "", res & 0xff); ++ ++ if (res & 0xff != 1) { ++ /* try to reset zhone */ ++ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK); ++ usleep(10); ++ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK); + } +-#endif +- zt_handle_dtmfup(ast, index, &f); +- return f; ++ /* do nothing */ + } + + if (res & ZT_EVENT_DTMFDOWN) { +--- asterisk/channels/chan_zap.c.org 2007-09-26 09:08:42.423698383 +0200 ++++ asterisk/channels/chan_zap.c 2007-09-26 09:09:43.422303554 +0200 +@@ -1607,11 +1607,18 @@ + + static inline int zt_set_hook(int fd, int hs) + { +- int x, res; ++ int x, res, count = 0; + + x = hs; + res = ioctl(fd, ZT_HOOK, &x); + ++ while (res < 0 && count < 20) { ++ usleep(100000); /* 1/10 sec. */ ++ x = hs; ++ res = ioctl(fd, ZT_HOOK, &x); ++ count++; ++ } ++ + if (res < 0) { + if (errno == EINPROGRESS) + return 0; diff --git a/asterisk.logrotate b/asterisk.logrotate new file mode 100644 index 0000000..a5ecc5f --- /dev/null +++ b/asterisk.logrotate @@ -0,0 +1,12 @@ +/var/log/asterisk/* /var/log/asterisk/*/* { + create 640 root logs + daily + missingok + compress + delaycompress + rotate 30 + sharedscripts + postrotate + /sbin/service asterisk logger-reload + endscript +} diff --git a/asterisk.spec b/asterisk.spec new file mode 100644 index 0000000..399e9a6 --- /dev/null +++ b/asterisk.spec @@ -0,0 +1,313 @@ +# TODO: +# - cgi-bin package - separate, because of suid-root +# - separate plugins into packages +# - use shared versions of lpc10, gsm,... +# - CFLAGS passing +# - fix bluetooth patch +# - package commandline tools (aelparse etc.) +# +# Conditional build: +%bcond_with rxfax # without rx (also tx :-D) fax +%bcond_with bluetooth # without bluetooth support (NFT) +%bcond_with zhone # zhone hack +%bcond_with zhone_hack # huge hack workarounding broken zhone channel banks which start randomly + # issuing pulse-dialled calls to weird numbers +%bcond_with bristuff # BRIstuff (Junghanns.NET BRI adapters) support +%bcond_with verbose # verbose build +# +%define _spandsp_version 0.0.2pre26 +# +Summary: Asterisk PBX +Summary(pl.UTF-8): Centralka (PBX) Asterisk +Name: asterisk +Version: 1.4.22 +Release: 1%{?with_bristuff:.bristuff} +License: GPL v2 +Group: Applications/System +Source0: http://ftp.digium.com/pub/asterisk/releases/%{name}-%{version}.tar.gz +# Source0-md5: 7626febc4a01e16e012dfccb9e4ab9d2 +Source1: %{name}.init +Source2: %{name}.sysconfig +Source3: http://ftp.digium.com/pub/telephony/sounds/releases/asterisk-core-sounds-en-gsm-1.4.6.tar.gz +# Source3-md5: 02e582b6c7de58e5eaba220229c0a71a +Source4: http://ftp.digium.com/pub/telephony/sounds/asterisk-moh-freeplay-wav.tar.gz +# Source4-md5: e523fc2b4ac524f45da7815e97780540 +Source5: %{name}.logrotate +Patch0: %{name}-m4.patch +Patch1: %{name}-configure.patch +Patch2: %{name}-no_k6_on_sparc.patch +Patch3: %{name}-lib.patch +Source10: http://soft-switch.org/downloads/spandsp/spandsp-%{_spandsp_version}/asterisk-1.2.x/app_txfax.c +# Source10-md5: 8c8fcb263b76897022b4c28052a7b439 +Source11: http://soft-switch.org/downloads/spandsp/spandsp-%{_spandsp_version}/asterisk-1.2.x/app_rxfax.c +# Source11-md5: ab6983b51c412883545b36993d704999 +# http://soft-switch.org/downloads/spandsp/spandsp-%{_spandsp_version}/asterisk-1.2.x/apps_Makefile.patch +Patch10: %{name}-txfax-Makefile.patch +Patch11: %{name}-fix-ptlib.patch +Patch12: %{name}-chan_bluetooth.patch +Patch13: %{name}-zhone.patch +# http://svn.debian.org/wsvn/pkg-voip/asterisk/trunk/debian/patches/bristuff +Patch14: %{name}-bristuff.patch +Patch15: %{name}-bristuff-build.patch +Patch16: %{name}-bristuff-libpri.patch +URL: http://www.asterisk.org/ +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: bison +%{?with_bluetooth:BuildRequires: bluez-devel} +BuildRequires: freetds >= 0.63 +BuildRequires: gawk +BuildRequires: gcc >= 5:3.4 +BuildRequires: iksemel-devel +BuildRequires: imap-static +BuildRequires: mysql-devel +BuildRequires: ncurses-devel +BuildRequires: openssl-devel >= 0.9.7d +BuildRequires: rpmbuild(macros) >= 1.268 +BuildRequires: sed >= 4.0 +%{?with_rxfax:BuildRequires: spandsp-devel-%{_spandsp_version}} +BuildRequires: speex-devel +BuildRequires: unixODBC-devel +BuildRequires: zaptel-devel >= 1.2.10 +BuildRequires: zlib-devel +BuildRequires: openh323-devel +BuildRequires: pwlib-devel +%if %{with bristuff} +BuildRequires: libgsmat-devel +BuildRequires: libpri-bristuff-devel >= 1.2.4 +%else +BuildRequires: libpri-devel >= 1.2.4 +%endif +Requires(post,preun): /sbin/chkconfig +Requires: rc-scripts +%requires_eq openh323 +%requires_eq pwlib +BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n) + +%description +Asterisk is an Open Source PBX and telephony development platform that +can both replace a conventional PBX and act as a platform for +developing custom telephony applications for delivering dynamic +content over a telephone similarly to how one can deliver dynamic +content through a web browser using CGI and a web server. + +Asterisk talks to a variety of telephony hardware including BRI, PRI, +POTS, and IP telephony clients using the Inter-Asterisk eXchange +protocol (e.g. gnophone or miniphone). For more information and a +current list of supported hardware, see http://www.asterisk.org/. + +%description -l pl.UTF-8 +Asterisk to wolnodostępna centralka (PBX) i platforma programistyczna +dla telefonii, mogąca zastąpić konwencjonalne PBX-y oraz służyć jako +platforma do rozwijania własnych aplikacji telefonicznych do +przekazywania dynamicznej treści przez telefon, podobnie jak można +przekazywać dynamiczną treść przez przeglądarkę WWW przy użyciu CGI i +serwera WWW. + +Asterisk współpracuje z wielorakim sprzętem telefonicznym, w tym BRI, +PRI, POTS oraz klienty telefonii IP używające protokołu Inter-Asterisk +eXchange (np. gnophone lub miniphone). Więcej informacji i listę +obsługiwanego sprzętu można znaleźć pod http://www.asterisk.org/. + +%package devel +Summary: Header files for Asterisk platform +Summary(pl.UTF-8): Pliki nagłówkowe platformy Asterisk +Group: Development + +%description devel +Header files for Asterisk development platform. + +%description devel -l pl.UTF-8 +Pliki nagłówkowe platformy programistycznej Asterisk. + +%package examples +Summary: Example files for the Asterisk PBX +Summary(pl.UTF-8): Pliki przykładowe dla centralki Asterisk +Group: Applications/System +Requires: %{name} = %{version}-%{release} + +%description examples +Example files for the Asterisk PBX. + +%description examples -l pl.UTF-8 +Pliki przykładowe dla centralki Asterisk. + +%prep +%setup -q -n %{name}-%{version} + +%{?with_zhone:sed -i -e 's|.*#define.*ZHONE_HACK.*|#define ZHONE_HACK 1|g' channels/chan_zap.c} + +%patch0 -p0 +#%patch1 -p1 +#%patch2 -p1 +#%patch3 -p1 +#%patch5 -p1 +#%patch6 -p1 +#%patch7 -p1 +#%patch9 -p1 + +%if %{with rxfax} +cd apps +%patch10 -p0 +cp %{SOURCE10} . +cp %{SOURCE11} . +%endif + +%patch11 -p1 + +%{?with_bluetooth:%patch12 -p1} +%{?with_zhonehack:%patch13 -p1} +%if %{with bristuff} +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%endif + +sed -i -e "s#/usr/lib/#/usr/%{_lib}/#g#" Makefile + +mkdir -p imap/c-client +ln -s %{_libdir}/libc-client.a imap/c-client/c-client.a +ln -s %{_includedir}/imap/* imap/c-client/ +echo '-lssl -lpam' > imap/c-client/LDFLAGS + +%build +rm -f pbx/.depend + +%{__aclocal} +%{__autoheader} +%{__autoconf} + +CPPFLAGS="-I/usr/include/openh323"; export CPPFLAGS +%configure \ + %{?with_bristuff:--with-gsmat=%{_prefix}} \ + --with-imap="`pwd`"/imap + +# safe checks +%{?with_bristuff:grep '^#define HAVE_GSMAT 1' include/asterisk/autoconfig.h || exit 1} + +cp -f .cleancount .lastclean + +%{__make} -j1 -C menuselect \ + %{?with_verbose:NOISY_BUILD=yes} \ + CC="%{__cc}" \ + OPTIMIZE="%{rpmcflags}" + +%{__make} -j1 \ + %{?with_verbose:NOISY_BUILD=yes} \ + CC="%{__cc}" \ + OPTIMIZE="%{rpmcflags}" \ + CHANNEL_LIBS+=chan_bluetooth.so || : + +# rerun needed; asterisk want's that +%{__make} -j1 \ + %{?with_verbose:NOISY_BUILD=yes} \ + CC="%{__cc}" \ + OPTIMIZE="%{rpmcflags}" \ + CHANNEL_LIBS+=chan_bluetooth.so + +# it requires doxygen - I don't know if we should do this... +#%{__make} progdocs + +# safe checks +%{?with_bristuff:objdump -p channels/chan_zap.so | grep -qE 'NEEDED +libgsmat\.so' || exit 1} + +%install +rm -rf $RPM_BUILD_ROOT +install -d $RPM_BUILD_ROOT{/var/{log/asterisk/cdr-csv,spool/asterisk/monitor},/etc/{rc.d/init.d,sysconfig,logrotate.d}} + +install %{SOURCE3} sounds +install %{SOURCE4} sounds +install %{SOURCE5} $RPM_BUILD_ROOT/etc/logrotate.d/%{name} + +%{__make} -j1 install \ + DESTDIR=$RPM_BUILD_ROOT +%{__make} -j1 samples \ + DESTDIR=$RPM_BUILD_ROOT + +install %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/%{name} +install %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/%{name} + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +/sbin/chkconfig --add asterisk +# this was insane; breaking all current calls +#%%service asterisk restart "Asterisk daemon" + +%preun +if [ "$1" = "0" ]; then + %service asterisk stop + /sbin/chkconfig --del asterisk +fi + +%files +%defattr(644,root,root,755) +%doc BUGS ChangeLog CHANGES CREDITS README* UPGRADE.txt configs doc/*.txt +%attr(755,root,root) %{_sbindir}/* +%dir %{_sysconfdir}/asterisk +%attr(754,root,root) /etc/rc.d/init.d/%{name} +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/logrotate.d/%{name} +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/sysconfig/%{name} +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/asterisk/*.conf +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/asterisk/*.adsi +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/asterisk/extensions.ael +%dir %{_libdir}/asterisk +%dir %{_libdir}/asterisk/modules +%attr(755,root,root) %{_libdir}/asterisk/modules/*.so +%dir /var/lib/asterisk +%dir /var/lib/asterisk/agi-bin +%dir /var/lib/asterisk/images +%dir /var/lib/asterisk/keys +%dir /var/lib/asterisk/moh +/var/lib/asterisk/moh/fpm-calm-river.wav +/var/lib/asterisk/moh/fpm-sunshine.wav +/var/lib/asterisk/moh/fpm-world-mix.wav +%dir /var/lib/asterisk/sounds +%dir /var/lib/asterisk/sounds/digits +%dir /var/lib/asterisk/sounds/dictate +%dir /var/lib/asterisk/sounds/followme +%dir /var/lib/asterisk/sounds/letters +%dir /var/lib/asterisk/sounds/phonetic +%dir /var/lib/asterisk/sounds/silence +/var/lib/asterisk/images/*.jpg +/var/lib/asterisk/keys/*.pub +/var/lib/asterisk/sounds/*.gsm +/var/lib/asterisk/sounds/digits/*.gsm +/var/lib/asterisk/sounds/dictate/*.gsm +/var/lib/asterisk/sounds/followme/*.gsm +/var/lib/asterisk/sounds/letters/*.gsm +/var/lib/asterisk/sounds/phonetic/*.gsm +/var/lib/asterisk/sounds/silence/*.gsm +%dir /var/spool/asterisk +%dir /var/spool/asterisk/monitor +#%%dir /var/spool/asterisk/vm +%dir /var/spool/asterisk/voicemail +%dir /var/spool/asterisk/voicemail/default +%dir /var/spool/asterisk/voicemail/default/1234 +%dir /var/log/asterisk +%dir /var/log/asterisk/cdr-csv +%{_mandir}/man8/asterisk.8* +%{_mandir}/man8/astgenkey.8* +%{_mandir}/man8/autosupport.8* +%{_mandir}/man8/safe_asterisk.8* +%dir /var/lib/asterisk/firmware +%dir /var/lib/asterisk/firmware/iax +/var/lib/asterisk/firmware/iax/iaxy.bin + +%files examples +%defattr(644,root,root,755) +%attr(755,root,root) /var/lib/asterisk/agi-bin/agi-test.agi +%attr(755,root,root) /var/lib/asterisk/agi-bin/eagi-sphinx-test +%attr(755,root,root) /var/lib/asterisk/agi-bin/eagi-test +%attr(755,root,root) /var/lib/asterisk/agi-bin/jukebox.agi +/var/spool/asterisk/voicemail/default/1234/busy.gsm +/var/spool/asterisk/voicemail/default/1234/unavail.gsm + +#%dir /var/lib/asterisk/agi-bin/* + +%files devel +%defattr(644,root,root,755) +%dir %{_includedir}/asterisk +%{_includedir}/asterisk/*.h +%{_includedir}/asterisk.h