diff options
Diffstat (limited to 'extra/asterisk/asterisk-03-1.6.2.0-beta1-to-r186562.patch')
-rw-r--r-- | extra/asterisk/asterisk-03-1.6.2.0-beta1-to-r186562.patch | 41697 |
1 files changed, 41697 insertions, 0 deletions
diff --git a/extra/asterisk/asterisk-03-1.6.2.0-beta1-to-r186562.patch b/extra/asterisk/asterisk-03-1.6.2.0-beta1-to-r186562.patch new file mode 100644 index 0000000000..21ff8236e9 --- /dev/null +++ b/extra/asterisk/asterisk-03-1.6.2.0-beta1-to-r186562.patch @@ -0,0 +1,41697 @@ +Index: .version +=================================================================== +--- a/.version (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/.version (.../trunk) (revision 186562) +@@ -1 +1 @@ +-1.6.2.0-beta1 ++1.6.2.0-beta1-r186562 +Index: build_tools/strip_nonapi +=================================================================== +--- a/build_tools/strip_nonapi (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/build_tools/strip_nonapi (.../trunk) (revision 186562) +@@ -1,38 +0,0 @@ +-#!/bin/sh -e +- +-# This script is designed to remove all non-API global symbols from an object +-# file. The only global symbols that should be retained are those that belong +-# to the official namespace. Unfortunately doing this is platform-specific, as +-# the object file manipulation tools are not consistent across platforms. +-# +-# On platforms where this script does not know what to do, the object file +-# will retain non-API global symbols, and this may have unpleasant side effects. +-# +-# Prefixes that belong to the official namespace are: +-# ast_ +-# _ast_ +-# __ast_ +-# astman_ +-# pbx_ +-# resample_ +- +-FILTER="${GREP} -v -e ^ast_ -e ^_ast_ -e ^__ast_ -e ^astman_ -e ^pbx_ -e ^resample_" +- +-case "${PROC}" in +- powerpc64) +- TEXTSYM=" D " +- ;; +- *) +- TEXTSYM=" T " +- ;; +-esac +- +-case "${OSARCH}" in +- linux-gnu|FreeBSD) +- nm ${1} | ${GREP} -e "$TEXTSYM" | cut -d" " -f3 | ${FILTER} > striplist +- sed -e "s/^/-N /" striplist | xargs -n 40 ${STRIP} ${1} +- rm -f striplist +- ;; +- *) +- ;; +-esac +Index: bridges/bridge_softmix.c +=================================================================== +--- a/bridges/bridge_softmix.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/bridges/bridge_softmix.c (.../trunk) (revision 186562) +@@ -85,17 +85,25 @@ + /*! \brief Function called when a bridge is created */ + static int softmix_bridge_create(struct ast_bridge *bridge) + { +- int timingfd; ++ struct ast_timer *timer; + +- if ((timingfd = ast_timer_open()) < 0) { ++ if (!(timer = ast_timer_open())) { + return -1; + } + +- ast_timer_close(timingfd); ++ bridge->bridge_pvt = timer; + + return 0; + } + ++/*! \brief Function called when a bridge is destroyed */ ++static int softmix_bridge_destroy(struct ast_bridge *bridge) ++{ ++ ast_timer_close((struct ast_timer *) bridge->bridge_pvt); ++ ++ return 0; ++} ++ + /*! \brief Function called when a channel is joined into the bridge */ + static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) + { +@@ -199,14 +207,11 @@ + /*! \brief Function which acts as the mixing thread */ + static int softmix_bridge_thread(struct ast_bridge *bridge) + { +- int timingfd; ++ struct ast_timer *timer = (struct ast_timer *) bridge->bridge_pvt; ++ int timingfd = ast_timer_fd(timer); + +- if ((timingfd = ast_timer_open()) < 0) { +- return -1; +- } ++ ast_timer_set_rate(timer, (1000 / SOFTMIX_INTERVAL)); + +- ast_timer_set_rate(timingfd, (1000 / SOFTMIX_INTERVAL)); +- + while (!bridge->stop && !bridge->refresh && bridge->array_num) { + struct ast_bridge_channel *bridge_channel = NULL; + short buf[SOFTMIX_DATALEN] = {0, }; +@@ -262,14 +267,11 @@ + /* Wait for the timing source to tell us to wake up and get things done */ + ast_waitfor_n_fd(&timingfd, 1, &timeout, NULL); + +- ast_timer_ack(timingfd, 1); ++ ast_timer_ack(timer, 1); + + ao2_lock(bridge); + } + +- ast_timer_set_rate(timingfd, 0); +- ast_timer_close(timingfd); +- + return 0; + } + +@@ -283,6 +285,7 @@ + .formats = AST_FORMAT_SLINEAR, + #endif + .create = softmix_bridge_create, ++ .destroy = softmix_bridge_destroy, + .join = softmix_bridge_join, + .leave = softmix_bridge_leave, + .write = softmix_bridge_write, +Index: configure +=================================================================== +--- a/configure (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/configure (.../trunk) (revision 186562) +@@ -1,5 +1,5 @@ + #! /bin/sh +-# From configure.ac Revision: 182355 . ++# From configure.ac Revision: 182847 . + # Guess values for system-dependent variables and create Makefiles. + # Generated by GNU Autoconf 2.61 for asterisk 1.6. + # +@@ -992,7 +992,6 @@ + AST_FORTIFY_SOURCE + AST_NO_STRICT_OVERFLOW + AST_SHADOW_WARNINGS +-PBX_RTLD_NOLOAD + PBX_IP_MTU_DISCOVER + PBX_DAHDI_HALF_FULL + GSM_INTERNAL +@@ -1631,7 +1630,7 @@ + --with-crypto=PATH use OpenSSL Cryptography support files in PATH + --with-dahdi=PATH use DAHDI files in PATH + --with-avcodec=PATH use Ffmpeg and avcodec library files in PATH +- --with-gsm=PATH use External GSM library files in PATH , use ++ --with-gsm=PATH use External GSM library files in PATH, use + 'internal' GSM otherwise + --with-gtk=PATH use gtk libraries files in PATH + --with-gtk2=PATH use gtk2 libraries files in PATH +@@ -19141,83 +19140,6 @@ + conftest$ac_exeext conftest.$ac_ext + + +- if test "x${PBX_RTLD_NOLOAD}" != "x1"; then +- { echo "$as_me:$LINENO: checking for RTLD_NOLOAD in dlfcn.h" >&5 +-echo $ECHO_N "checking for RTLD_NOLOAD in dlfcn.h... $ECHO_C" >&6; } +- saved_cppflags="${CPPFLAGS}" +- if test "x${RTLD_NOLOAD_DIR}" != "x"; then +- RTLD_NOLOAD_INCLUDE="-I${RTLD_NOLOAD_DIR}/include" +- fi +- CPPFLAGS="${CPPFLAGS} ${RTLD_NOLOAD_INCLUDE}" +- +- cat >conftest.$ac_ext <<_ACEOF +- /* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF +-/* end confdefs.h. */ +-#include <dlfcn.h> +-int +-main () +-{ +-#if defined(RTLD_NOLOAD) +- int foo = 0; +- #else +- int foo = bar; +- #endif +- 0 +- +- ; +- return 0; +-} +-_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 +- { echo "$as_me:$LINENO: result: yes" >&5 +-echo "${ECHO_T}yes" >&6; } +- PBX_RTLD_NOLOAD=1 +- +-cat >>confdefs.h <<\_ACEOF +-#define HAVE_RTLD_NOLOAD 1 +-_ACEOF +- +- +-cat >>confdefs.h <<\_ACEOF +-#define HAVE_RTLD_NOLOAD_VERSION +-_ACEOF +- +- +-else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } +- +-fi +- +-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +- CPPFLAGS="${saved_cppflags}" +- fi +- +- +- +- + if test "x${PBX_IP_MTU_DISCOVER}" != "x1"; then + { echo "$as_me:$LINENO: checking for IP_MTU_DISCOVER in netinet/in.h" >&5 + echo $ECHO_N "checking for IP_MTU_DISCOVER in netinet/in.h... $ECHO_C" >&6; } +@@ -54451,7 +54373,6 @@ + AST_FORTIFY_SOURCE!$AST_FORTIFY_SOURCE$ac_delim + AST_NO_STRICT_OVERFLOW!$AST_NO_STRICT_OVERFLOW$ac_delim + AST_SHADOW_WARNINGS!$AST_SHADOW_WARNINGS$ac_delim +-PBX_RTLD_NOLOAD!$PBX_RTLD_NOLOAD$ac_delim + PBX_IP_MTU_DISCOVER!$PBX_IP_MTU_DISCOVER$ac_delim + PBX_DAHDI_HALF_FULL!$PBX_DAHDI_HALF_FULL$ac_delim + GSM_INTERNAL!$GSM_INTERNAL$ac_delim +@@ -54466,6 +54387,7 @@ + PWLIB_LIBDIR!$PWLIB_LIBDIR$ac_delim + PWLIB_PLATFORM!$PWLIB_PLATFORM$ac_delim + OPENH323DIR!$OPENH323DIR$ac_delim ++OPENH323_INCDIR!$OPENH323_INCDIR$ac_delim + _ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then +@@ -54507,7 +54429,6 @@ + ac_delim='%!_!# ' + for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +-OPENH323_INCDIR!$OPENH323_INCDIR$ac_delim + OPENH323_LIBDIR!$OPENH323_LIBDIR$ac_delim + OPENH323_SUFFIX!$OPENH323_SUFFIX$ac_delim + OPENH323_BUILD!$OPENH323_BUILD$ac_delim +@@ -54528,7 +54449,7 @@ + LTLIBOBJS!$LTLIBOBJS$ac_delim + _ACEOF + +- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 19; then ++ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 18; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +Index: default.exports +=================================================================== +--- a/default.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/default.exports (.../trunk) (revision 186562) +@@ -0,0 +1,4 @@ ++{ ++ local: ++ *; ++}; + +Property changes on: default.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: pbx/pbx_dundi.c +=================================================================== +--- a/pbx/pbx_dundi.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/pbx/pbx_dundi.c (.../trunk) (revision 186562) +@@ -4614,7 +4614,7 @@ + ast_log(LOG_WARNING, "Unable to get host name!\n"); + AST_LIST_LOCK(&peers); + +- memcpy(&global_eid, &g_eid, sizeof(global_eid)); ++ memcpy(&global_eid, &ast_eid_default, sizeof(global_eid)); + + global_storehistory = 0; + ast_copy_string(secretpath, "dundi", sizeof(secretpath)); +Index: channels/h323/Makefile.in +=================================================================== +--- a/channels/h323/Makefile.in (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/h323/Makefile.in (.../trunk) (revision 186562) +@@ -24,7 +24,9 @@ + OPENH323DIR=@OPENH323DIR@ + endif + ++ifneq ($(wildcard $(OPENH323DIR)/openh323u.mak),) + include $(OPENH323DIR)/openh323u.mak ++endif + + notrace:: + $(MAKE) NOTRACE=1 opt +Index: channels/misdn_config.c +=================================================================== +--- a/channels/misdn_config.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn_config.c (.../trunk) (revision 186562) +@@ -1,6 +1,6 @@ + /* + * Asterisk -- An open source telephony toolkit. +- * ++ * + * Copyright (C) 2005, Christian Richter + * + * Christian Richter <crich@beronet.com> +@@ -132,69 +132,79 @@ + { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE, + "Sets the musiconhold class." }, + { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE, +- "Sets the caller ID." }, ++ "Set the outgoing caller id to the value." }, + { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE, +- "Sets the method to use for channel selection:\n" +- "\t standard - always choose the first free channel with the lowest number\n" +- "\t round_robin - use the round robin algorithm to select a channel. use this\n" +- "\t if you want to balance your load." }, ++ "Set the method to use for channel selection:\n" ++ "\t standard - Use the first free channel starting from the lowest number.\n" ++ "\t standard_dec - Use the first free channel starting from the highest number.\n" ++ "\t round_robin - Use the round robin algorithm to select a channel. Use this\n" ++ "\t if you want to balance your load." }, + { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE, +- "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" +- "\n" ++ "Dialplan means Type Of Number in ISDN Terms\n" + "\tThere are different types of the dialplan:\n" + "\n" +- "\tdialplan -> outgoing Number\n" +- "\tlocaldialplan -> callerid\n" +- "\tcpndialplan -> connected party number\n" ++ "\tdialplan -> for outgoing call's dialed number\n" ++ "\tlocaldialplan -> for outgoing call's callerid\n" ++ "\t (if -1 is set use the value from the asterisk channel)\n" ++ "\tcpndialplan -> for incoming call's connected party number sent to caller\n" ++ "\t (if -1 is set use the value from the asterisk channel)\n" + "\n" + "\tdialplan options:\n" + "\n" + "\t0 - unknown\n" + "\t1 - International\n" + "\t2 - National\n" +- "\t4 - Subscriber\n" +- "\n" +- "\tThis setting is used for outgoing calls." }, ++ "\t4 - Subscriber" }, + { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE, +- "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" +- "\n" ++ "Dialplan means Type Of Number in ISDN Terms\n" + "\tThere are different types of the dialplan:\n" + "\n" +- "\tdialplan -> outgoing Number\n" +- "\tlocaldialplan -> callerid\n" +- "\tcpndialplan -> connected party number\n" ++ "\tdialplan -> for outgoing call's dialed number\n" ++ "\tlocaldialplan -> for outgoing call's callerid\n" ++ "\t (if -1 is set use the value from the asterisk channel)\n" ++ "\tcpndialplan -> for incoming call's connected party number sent to caller\n" ++ "\t (if -1 is set use the value from the asterisk channel)\n" + "\n" + "\tdialplan options:\n" + "\n" + "\t0 - unknown\n" + "\t1 - International\n" + "\t2 - National\n" +- "\t4 - Subscriber\n" +- "\n" +- "\tThis setting is used for outgoing calls." }, ++ "\t4 - Subscriber" }, + { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE, +- "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" +- "\n" ++ "Dialplan means Type Of Number in ISDN Terms\n" + "\tThere are different types of the dialplan:\n" + "\n" +- "\tdialplan -> outgoing Number\n" +- "\tlocaldialplan -> callerid\n" +- "\tcpndialplan -> connected party number\n" ++ "\tdialplan -> for outgoing call's dialed number\n" ++ "\tlocaldialplan -> for outgoing call's callerid\n" ++ "\t (if -1 is set use the value from the asterisk channel)\n" ++ "\tcpndialplan -> for incoming call's connected party number sent to caller\n" ++ "\t (if -1 is set use the value from the asterisk channel)\n" + "\n" + "\tdialplan options:\n" + "\n" + "\t0 - unknown\n" + "\t1 - International\n" + "\t2 - National\n" +- "\t4 - Subscriber\n" +- "\n" +- "\tThis setting is used for outgoing calls." }, +- { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE, +- "Prefix for national, this is put before the\n" +- "\toad if an according dialplan is set by the other end." }, +- { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE, +- "Prefix for international, this is put before the\n" +- "\toad if an according dialplan is set by the other end." }, ++ "\t4 - Subscriber" }, ++ { "unknownprefix", MISDN_CFG_TON_PREFIX_UNKNOWN, MISDN_CTYPE_STR, "", NONE, ++ "Prefix for unknown numbers, this is put before an incoming number\n" ++ "\tif its type-of-number is unknown." }, ++ { "internationalprefix", MISDN_CFG_TON_PREFIX_INTERNATIONAL, MISDN_CTYPE_STR, "00", NONE, ++ "Prefix for international numbers, this is put before an incoming number\n" ++ "\tif its type-of-number is international." }, ++ { "nationalprefix", MISDN_CFG_TON_PREFIX_NATIONAL, MISDN_CTYPE_STR, "0", NONE, ++ "Prefix for national numbers, this is put before an incoming number\n" ++ "\tif its type-of-number is national." }, ++ { "netspecificprefix", MISDN_CFG_TON_PREFIX_NETWORK_SPECIFIC, MISDN_CTYPE_STR, "", NONE, ++ "Prefix for network-specific numbers, this is put before an incoming number\n" ++ "\tif its type-of-number is network-specific." }, ++ { "subscriberprefix", MISDN_CFG_TON_PREFIX_SUBSCRIBER, MISDN_CTYPE_STR, "", NONE, ++ "Prefix for subscriber numbers, this is put before an incoming number\n" ++ "\tif its type-of-number is subscriber." }, ++ { "abbreviatedprefix", MISDN_CFG_TON_PREFIX_ABBREVIATED, MISDN_CTYPE_STR, "", NONE, ++ "Prefix for abbreviated numbers, this is put before an incoming number\n" ++ "\tif its type-of-number is abbreviated." }, + { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE, + "These (presentation and screen) are the exact isdn screening and presentation\n" + "\tindicators.\n" +@@ -211,6 +221,22 @@ + "\n" + "\tscreen=0, presentation=0 -> callerid presented\n" + "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" }, ++ { "display_connected", MISDN_CFG_DISPLAY_CONNECTED, MISDN_CTYPE_INT, "0", NONE, ++ "Put a display ie in the CONNECT message containing the following\n" ++ "\tinformation if it is available (nt port only):\n" ++ "\n" ++ "\t0 - Do not put the connected line information in the display ie.\n" ++ "\t1 - Put the available connected line name in the display ie.\n" ++ "\t2 - Put the available connected line number in the display ie.\n" ++ "\t3 - Put the available connected line name and number in the display ie." }, ++ { "display_setup", MISDN_CFG_DISPLAY_SETUP, MISDN_CTYPE_INT, "0", NONE, ++ "Put a display ie in the SETUP message containing the following\n" ++ "\tinformation if it is available (nt port only):\n" ++ "\n" ++ "\t0 - Do not put the caller information in the display ie.\n" ++ "\t1 - Put the available caller name in the display ie.\n" ++ "\t2 - Put the available caller number in the display ie.\n" ++ "\t3 - Put the available caller name and number in the display ie." }, + { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE, + "Enable this to get into the s dialplan-extension.\n" + "\tThere you can use DigitTimeout if you can't or don't want to use\n" +@@ -219,7 +245,7 @@ + { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE, + "Enable this to prevent chan_misdn to generate the dialtone\n" + "\tThis makes only sense together with the always_immediate=yes option\n" +- "\tto generate your own dialtone with Playtones or so."}, ++ "\tto generate your own dialtone with Playtones or so." }, + { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE, + "Enable this if you want callers which called exactly the base\n" + "\tnumber (so no extension is set) to jump into the s extension.\n" +@@ -256,17 +282,17 @@ + #endif + #ifdef WITH_BEROEC + { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64, +- "echotail in ms (1-200)\n"}, ++ "echotail in ms (1-200)" }, + { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE, +- "Use antihowl\n"}, ++ "Use antihowl" }, + { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE, +- "Nonlinear Processing (much faster adaption)"}, ++ "Nonlinear Processing (much faster adaption)" }, + { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE, +- "ZeroCoeffeciens\n"}, ++ "ZeroCoeffeciens" }, + { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE, +- "Disable Tone\n"}, ++ "Disable Tone" }, + { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE, +- "Adaption mode (0=no,1=full,2=fast)\n"}, ++ "Adaption mode (0=no,1=full,2=fast)" }, + #endif + { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE, + "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n" +@@ -310,13 +336,13 @@ + { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE, + "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." }, + { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4, +- "Watches the layer 1. If the layer 1 is down, it tries to\n" +- "\tget it up. The timeout is given in seconds. with 0 as value it\n" +- "\tdoes not watch the l1 at all\n" ++ "Monitors L1 of the port. If L1 is down it tries\n" ++ "\tto bring it up. The polling timeout is given in seconds.\n" ++ "\tSetting the value to 0 disables monitoring L1 of the port.\n" + "\n" +- "\tThis option is only read at loading time of chan_misdn, which\n" +- "\tmeans you need to unload and load chan_misdn to change the value,\n" +- "\tan Asterisk restart should do the trick." }, ++ "\tThis option is only read at chan_misdn loading time.\n" ++ "\tYou need to unload and load chan_misdn to change the\n" ++ "\tvalue. An asterisk restart will also do the trick." }, + { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4, + "Enables overlap dial for the given amount of seconds.\n" + "\tPossible values are positive integers or:\n" +@@ -364,8 +390,8 @@ + { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE, + "Keys for cryption, you reference them in the dialplan\n" + "\tLater also in dynamic encr." }, +- { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, +- "avoid dropping calls if the L2 goes down. some Nortel pbx\n" ++ { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, ++ "avoid dropping calls if the L2 goes down. some Nortel pbx\n" + "do put down the L2/L1 for some milliseconds even if there\n" + "are running calls. with this option you can avoid dropping them" }, + { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE, +@@ -386,7 +412,7 @@ + /* maps enum config elements to array positions */ + static int *map; + +-static ast_mutex_t config_mutex; ++static ast_mutex_t config_mutex; + + #define CLI_ERROR(name, value, section) ({ \ + ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \ +@@ -475,7 +501,7 @@ + int i, j; + int gn = map[MISDN_CFG_GROUPNAME]; + union misdn_cfg_pt* free_list[max_ports + 2]; +- ++ + memset(free_list, 0, sizeof(free_list)); + free_list[0] = port_cfg[0]; + for (i = 1; i <= max_ports; ++i) { +@@ -507,7 +533,7 @@ + { + int i; + +- for (i = 0; i < NUM_GEN_ELEMENTS; i++) ++ for (i = 0; i < NUM_GEN_ELEMENTS; i++) + if (general_cfg[i].any) + ast_free(general_cfg[i].any); + } +@@ -579,11 +605,11 @@ + pos = get_cfg_position(name, PORT_CFG); + if (pos >= 0) + return port_spec[pos].elem; +- ++ + pos = get_cfg_position(name, GEN_CFG); + if (pos >= 0) + return gen_spec[pos].elem; +- ++ + return MISDN_CFG_FIRST; + } + +@@ -597,7 +623,7 @@ + memset(buf, 0, 1); + return; + } +- ++ + /* here comes a hack to replace the (not existing) "name" element with the "ports" element */ + if (elem == MISDN_CFG_GROUPNAME) { + if (!snprintf(buf, bufsize, "ports")) +@@ -630,7 +656,7 @@ + spec = (struct misdn_cfg_spec *)port_spec; + else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST)) + spec = (struct misdn_cfg_spec *)gen_spec; +- ++ + if (!spec || !spec[place].desc) + memset(buf, 0, 1); + else { +@@ -659,7 +685,7 @@ + iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml; + else + iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml; +- for (; iter; iter = iter->next) ++ for (; iter; iter = iter->next) + if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) { + re = 1; + break; +@@ -688,7 +714,7 @@ + for (i = 1; i <= max_ports; i++) { + if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) { + if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group)) +- method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? ++ method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? + port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str); + } + } +@@ -708,7 +734,7 @@ + return re; + } + +-/*! ++/*! + * \brief Generate a comma separated list of all active ports + */ + void misdn_cfg_get_ports_string (char *ports) +@@ -776,10 +802,10 @@ + break; + case MISDN_CTYPE_ASTGROUP: + if (port_cfg[port][place].grp) +- snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, ++ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, + ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp)); + else if (port_cfg[0][place].grp) +- snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, ++ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, + ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp)); + else + snprintf(buf, bufsize, " -> %s:", port_spec[place].name); +@@ -844,7 +870,7 @@ + { + int p = -1; + int gn = map[MISDN_CFG_GROUPNAME]; +- ++ + misdn_cfg_lock(); + for (port++; port <= max_ports; port++) { + if (port_cfg[port][gn].str) { +@@ -936,7 +962,7 @@ + for (; v; v = v->next) { + if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) + continue; +- if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || ++ if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || + (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0)) + CLI_ERROR(v->name, v->value, "general"); + } +@@ -958,7 +984,7 @@ + cfg_for_ports[0] = 1; + } + +- if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || ++ if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || + (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) { + CLI_ERROR(v->name, v->value, cat); + return; +@@ -969,7 +995,7 @@ + char *token, *tmp = ast_strdupa(v->value); + char ptpbuf[BUFFERSIZE] = ""; + int start, end; +- for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) { ++ for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) { + if (!*token) + continue; + if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) { +@@ -992,7 +1018,7 @@ + } + } + } else { +- if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || ++ if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || + (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) + CLI_ERROR(v->name, v->value, cat); + } +Index: channels/chan_jingle.c +=================================================================== +--- a/channels/chan_jingle.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_jingle.c (.../trunk) (revision 186562) +@@ -53,7 +53,7 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" + #include "asterisk/file.h" +@@ -112,9 +112,9 @@ + char exten[80]; /*!< Called extension */ + struct ast_channel *owner; /*!< Master Channel */ + char audio_content_name[100]; /*!< name attribute of content tag */ +- struct ast_rtp *rtp; /*!< RTP audio session */ ++ struct ast_rtp_instance *rtp; /*!< RTP audio session */ + char video_content_name[100]; /*!< name attribute of content tag */ +- struct ast_rtp *vrtp; /*!< RTP video session */ ++ struct ast_rtp_instance *vrtp; /*!< RTP video session */ + int jointcapability; /*!< Supported capability at both ends (codecs ) */ + int peercapability; + struct jingle_pvt *next; /* Next entity */ +@@ -183,11 +183,6 @@ + static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, const char *sid); + static char *jingle_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); + static char *jingle_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); +-/*----- RTP interface functions */ +-static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, +- struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active); +-static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); +-static int jingle_get_codec(struct ast_channel *chan); + + /*! \brief PBX interface structure for channel registration */ + static const struct ast_channel_tech jingle_tech = { +@@ -197,7 +192,7 @@ + .requester = jingle_request, + .send_digit_begin = jingle_digit_begin, + .send_digit_end = jingle_digit_end, +- .bridge = ast_rtp_bridge, ++ .bridge = ast_rtp_instance_bridge, + .call = jingle_call, + .hangup = jingle_hangup, + .answer = jingle_answer, +@@ -216,15 +211,6 @@ + static struct io_context *io; /*!< The IO context */ + static struct in_addr __ourip; + +- +-/*! \brief RTP driver interface */ +-static struct ast_rtp_protocol jingle_rtp = { +- type: "Jingle", +- get_rtp_info: jingle_get_rtp_peer, +- set_rtp_peer: jingle_set_rtp_peer, +- get_codec: jingle_get_codec, +-}; +- + static struct ast_cli_entry jingle_cli[] = { + AST_CLI_DEFINE(jingle_do_reload, "Reload Jingle configuration"), + AST_CLI_DEFINE(jingle_show_channels, "Show Jingle channels"), +@@ -304,7 +290,6 @@ + iks_insert_attrib(payload_g723, "name", "G723"); + iks_insert_node(dcodecs, payload_g723); + } +- ast_rtp_lookup_code(p->rtp, 1, codec); + } + + static int jingle_accept_call(struct jingle *client, struct jingle_pvt *p) +@@ -398,18 +383,19 @@ + return res; + } + +-static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { + struct jingle_pvt *p = chan->tech_pvt; +- enum ast_rtp_get_result res = AST_RTP_GET_FAILED; ++ enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; + + if (!p) + return res; + + ast_mutex_lock(&p->lock); + if (p->rtp) { +- *rtp = p->rtp; +- res = AST_RTP_TRY_PARTIAL; ++ ao2_ref(p->rtp, +1); ++ *instance = p->rtp; ++ res = AST_RTP_GLUE_RESULT_LOCAL; + } + ast_mutex_unlock(&p->lock); + +@@ -422,7 +408,7 @@ + return p->peercapability; + } + +-static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active) ++static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *tpeer, int codecs, int nat_active) + { + struct jingle_pvt *p; + +@@ -442,6 +428,13 @@ + return 0; + } + ++static struct ast_rtp_glue jingle_rtp_glue = { ++ .type = "Jingle", ++ .get_rtp_info = jingle_get_rtp_peer, ++ .get_codec = jingle_get_codec, ++ .update_peer = jingle_set_rtp_peer, ++}; ++ + static int jingle_response(struct jingle *client, ikspak *pak, const char *reasonstr, const char *reasonstr2) + { + iks *response = NULL, *error = NULL, *reason = NULL; +@@ -621,7 +614,7 @@ + goto safeout; + } + +- ast_rtp_get_us(p->rtp, &sin); ++ ast_rtp_instance_get_local_address(p->rtp, &sin); + ast_find_ourip(&us, bindaddr); + + /* Setup our first jingle candidate */ +@@ -779,7 +772,7 @@ + ast_copy_string(tmp->them, idroster, sizeof(tmp->them)); + tmp->initiator = 1; + } +- tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); ++ tmp->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); + tmp->parent = client; + if (!tmp->rtp) { + ast_log(LOG_WARNING, "Out of RTP sessions?\n"); +@@ -825,18 +818,18 @@ + + /* Set Frame packetization */ + if (i->rtp) +- ast_rtp_codec_setpref(i->rtp, &i->prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs); + + tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK); + fmt = ast_best_codec(tmp->nativeformats); + + if (i->rtp) { +- ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); +- ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); ++ ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); ++ ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); + } + if (i->vrtp) { +- ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); +- ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); ++ ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); ++ ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); + } + if (state == AST_STATE_RING) + tmp->rings = 1; +@@ -942,9 +935,9 @@ + if (p->owner) + ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); + if (p->rtp) +- ast_rtp_destroy(p->rtp); ++ ast_rtp_instance_destroy(p->rtp); + if (p->vrtp) +- ast_rtp_destroy(p->vrtp); ++ ast_rtp_instance_destroy(p->vrtp); + jingle_free_candidates(p->theircandidates); + ast_free(p); + } +@@ -1009,8 +1002,8 @@ + ast_copy_string(p->audio_content_name, iks_find_attrib(content, "name"), sizeof(p->audio_content_name)); + + while (codec) { +- ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); +- ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); ++ ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + codec = iks_next(codec); + } + } +@@ -1025,8 +1018,8 @@ + ast_copy_string(p->video_content_name, iks_find_attrib(content, "name"), sizeof(p->video_content_name)); + + while (codec) { +- ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); +- ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); ++ ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + codec = iks_next(codec); + } + } +@@ -1079,7 +1072,7 @@ + sin.sin_port = htons(tmp->port); + snprintf(username, sizeof(username), "%s:%s", tmp->ufrag, p->ourcandidates->ufrag); + +- ast_rtp_stun_request(p->rtp, &sin, username); ++ ast_rtp_instance_stun_request(p->rtp, &sin, username); + tmp = tmp->next; + } + return 1; +@@ -1169,7 +1162,7 @@ + + if (!p->rtp) + return &ast_null_frame; +- f = ast_rtp_read(p->rtp); ++ f = ast_rtp_instance_read(p->rtp, 0); + jingle_update_stun(p->parent, p); + if (p->owner) { + /* We already hold the channel lock */ +@@ -1220,7 +1213,7 @@ + if (p) { + ast_mutex_lock(&p->lock); + if (p->rtp) { +- res = ast_rtp_write(p->rtp, frame); ++ res = ast_rtp_instance_write(p->rtp, frame); + } + ast_mutex_unlock(&p->lock); + } +@@ -1229,7 +1222,7 @@ + if (p) { + ast_mutex_lock(&p->lock); + if (p->vrtp) { +- res = ast_rtp_write(p->vrtp, frame); ++ res = ast_rtp_instance_write(p->vrtp, frame); + } + ast_mutex_unlock(&p->lock); + } +@@ -1879,7 +1872,7 @@ + return 0; + } + +- ast_rtp_proto_register(&jingle_rtp); ++ ast_rtp_glue_register(&jingle_rtp_glue); + ast_cli_register_multiple(jingle_cli, ARRAY_LEN(jingle_cli)); + /* Make sure we can register our channel type */ + if (ast_channel_register(&jingle_tech)) { +@@ -1902,7 +1895,7 @@ + ast_cli_unregister_multiple(jingle_cli, ARRAY_LEN(jingle_cli)); + /* First, take us out of the channel loop */ + ast_channel_unregister(&jingle_tech); +- ast_rtp_proto_unregister(&jingle_rtp); ++ ast_rtp_glue_unregister(&jingle_rtp_glue); + + if (!ast_mutex_lock(&jinglelock)) { + /* Hangup all interfaces if they have an owner */ +Index: channels/chan_dahdi.c +=================================================================== +--- a/channels/chan_dahdi.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_dahdi.c (.../trunk) (revision 186562) +@@ -153,9 +153,6 @@ + <description> + <para>This application will Accept the R2 call either with charge or no charge.</para> + </description> +- <description> +- <para>This application will Accept the R2 call either with charge or no charge.</para> +- </description> + </application> + ***/ + +@@ -2535,9 +2532,6 @@ + /* Don't delete if we don't think it's conferenced at all (implied) */ + ) return 0; + memset(&zi, 0, sizeof(zi)); +- zi.chan = 0; +- zi.confno = 0; +- zi.confmode = 0; + if (ioctl(c->dfd, DAHDI_SETCONF, &zi)) { + ast_log(LOG_WARNING, "Failed to drop %d from conference %d/%d: %s\n", c->dfd, c->curconf.confmode, c->curconf.confno, strerror(errno)); + return -1; +@@ -2593,11 +2587,12 @@ + + static int reset_conf(struct dahdi_pvt *p) + { +- struct dahdi_confinfo zi; +- memset(&zi, 0, sizeof(zi)); + p->confno = -1; + memset(&p->subs[SUB_REAL].curconf, 0, sizeof(p->subs[SUB_REAL].curconf)); + if (p->subs[SUB_REAL].dfd > -1) { ++ struct dahdi_confinfo zi; ++ ++ memset(&zi, 0, sizeof(zi)); + if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCONF, &zi)) + ast_log(LOG_WARNING, "Failed to reset conferencing on channel %d: %s\n", p->channel, strerror(errno)); + } +@@ -2911,8 +2906,7 @@ + p->saveconf.confmode = 0; + return -1; + } +- c.chan = 0; +- c.confno = 0; ++ memset(&c, 0, sizeof(c)); + c.confmode = DAHDI_CONF_NORMAL; + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCONF, &c); + if (res) { +@@ -2962,10 +2956,7 @@ + return; + } + +- ast_event_queue_and_cache(event, +- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_END); ++ ast_event_queue_and_cache(event); + + if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) { + snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot); +@@ -3019,7 +3010,6 @@ + event = ast_event_get_cached(AST_EVENT_MWI, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context, +- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, + AST_EVENT_IE_END); + + if (event) { +@@ -3166,7 +3156,7 @@ + } + p->callwaitcas = 0; + if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) { +- p->cidlen = ast_callerid_generate(p->cidspill, ast->cid.cid_name, ast->cid.cid_num, AST_LAW(p)); ++ p->cidlen = ast_callerid_generate(p->cidspill, ast->connected.id.name, ast->connected.id.number, AST_LAW(p)); + p->cidpos = 0; + send_callerid(p); + } +@@ -3207,12 +3197,12 @@ + } else { + /* Call waiting call */ + p->callwaitrings = 0; +- if (ast->cid.cid_num) +- ast_copy_string(p->callwait_num, ast->cid.cid_num, sizeof(p->callwait_num)); ++ if (ast->connected.id.number) ++ ast_copy_string(p->callwait_num, ast->connected.id.number, sizeof(p->callwait_num)); + else + p->callwait_num[0] = '\0'; +- if (ast->cid.cid_name) +- ast_copy_string(p->callwait_name, ast->cid.cid_name, sizeof(p->callwait_name)); ++ if (ast->connected.id.name) ++ ast_copy_string(p->callwait_name, ast->connected.id.name, sizeof(p->callwait_name)); + else + p->callwait_name[0] = '\0'; + /* Call waiting tone instead */ +@@ -3224,8 +3214,8 @@ + if (tone_zone_play_tone(p->subs[SUB_CALLWAIT].dfd, DAHDI_TONE_RINGTONE)) + ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name); + } +- n = ast->cid.cid_name; +- l = ast->cid.cid_num; ++ n = ast->connected.id.name; ++ l = ast->connected.id.number; + if (l) + ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num)); + else +@@ -3291,14 +3281,14 @@ + + switch (mysig) { + case SIG_FEATD: +- l = ast->cid.cid_num; ++ l = ast->connected.id.number; + if (l) + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c); + else + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c); + break; + case SIG_FEATDMF: +- l = ast->cid.cid_num; ++ l = ast->connected.id.number; + if (l) + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c); + else +@@ -3394,6 +3384,7 @@ + case SIG_MFCR2: + /* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */ + p->dialdest[0] = '\0'; ++ p->dialing = 1; + break; + default: + ast_debug(1, "not yet implemented\n"); +@@ -3432,7 +3423,7 @@ + } + + if (!p->hidecallerid) { +- l = ast->cid.cid_num; ++ l = ast->connected.id.number; + } else { + l = NULL; + } +@@ -3481,10 +3472,10 @@ + } + } + isup_set_calling(p->ss7call, l ? (l + calling_nai_strip) : NULL, ss7_calling_nai, +- p->use_callingpres ? cid_pres2ss7pres(ast->cid.cid_pres) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED), +- p->use_callingpres ? cid_pres2ss7screen(ast->cid.cid_pres) : SS7_SCREENING_USER_PROVIDED ); ++ p->use_callingpres ? cid_pres2ss7pres(ast->connected.id.number_presentation) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED), ++ p->use_callingpres ? cid_pres2ss7screen(ast->connected.id.number_presentation) : SS7_SCREENING_USER_PROVIDED ); + +- isup_set_oli(p->ss7call, ast->cid.cid_ani2); ++ isup_set_oli(p->ss7call, ast->connected.ani2); + isup_init_call(p->ss7->ss7, p->ss7call, p->cic, p->dpc); + + ast_channel_lock(ast); +@@ -3596,9 +3587,9 @@ + l = NULL; + n = NULL; + if (!p->hidecallerid) { +- l = ast->cid.cid_num; ++ l = ast->connected.id.number; + if (!p->hidecalleridname) { +- n = ast->cid.cid_name; ++ n = ast->connected.id.name; + } + } + +@@ -3812,7 +3803,7 @@ + } + } + pri_sr_set_caller(sr, l ? (l + ldp_strip) : NULL, n, prilocaldialplan, +- p->use_callingpres ? ast->cid.cid_pres : (l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE)); ++ p->use_callingpres ? ast->connected.id.number_presentation : (l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE)); + if ((rr_str = pbx_builtin_getvar_helper(ast, "PRIREDIRECTREASON"))) { + if (!strcasecmp(rr_str, "UNKNOWN")) + redirect_reason = 0; +@@ -4468,6 +4459,7 @@ + p->onhooktime = time(NULL); + #if defined(HAVE_PRI) || defined(HAVE_SS7) + p->proceeding = 0; ++ p->dialing = 0; + p->progress = 0; + p->alerting = 0; + p->setup_ack = 0; +@@ -4604,6 +4596,7 @@ + case SIG_FXOGS: + case SIG_FXOLS: + case SIG_FXOKS: ++ memset(&par, 0, sizeof(par)); + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par); + if (!res) { + #if 0 +@@ -4769,6 +4762,7 @@ + /* Send a pri acknowledge */ + if (!pri_grab(p, p->pri)) { + p->proceeding = 1; ++ p->dialing = 0; + res = pri_answer(p->pri->pri, p->call, 0, !p->digital); + pri_rel(p->pri); + } else { +@@ -5580,6 +5574,7 @@ + } + + /* No alarms on the span. Check for channel alarms. */ ++ memset(¶ms, 0, sizeof(params)); + if ((res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, ¶ms)) >= 0) + return params.chan_alarms; + +@@ -6206,6 +6201,7 @@ + { + struct dahdi_params par; + ++ memset(&par, 0, sizeof(par)); + if (ioctl(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par) != -1) + { + if (!par.rxisoffhook) +@@ -6698,6 +6694,7 @@ + { + struct dahdi_params ps; + ++ memset(&ps, 0, sizeof(ps)); + ps.channo = p->channel; + if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps) < 0) { + ast_mutex_unlock(&p->lock); +@@ -7209,6 +7206,7 @@ + ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); + } + p->proceeding = 1; ++ p->dialing = 0; + } + #endif + #ifdef HAVE_SS7 +@@ -7390,6 +7388,7 @@ + if (!tmp) + return NULL; + tmp->tech = &dahdi_tech; ++ memset(&ps, 0, sizeof(ps)); + ps.channo = i->channel; + res = ioctl(i->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps); + if (res) { +@@ -7407,7 +7406,7 @@ + deflaw = AST_FORMAT_ULAW; + } + ast_channel_set_fd(tmp, 0, i->subs[idx].dfd); +- tmp->nativeformats = AST_FORMAT_SLINEAR | deflaw; ++ tmp->nativeformats = deflaw; + /* Start out assuming ulaw since it's smaller :) */ + tmp->rawreadformat = deflaw; + tmp->readformat = deflaw; +@@ -9017,8 +9016,8 @@ + #ifdef HAVE_DAHDI_LINEREVERSE_VMWI + if (pvt->mwisend_fsk) { + #endif +- pvt->cidlen = vmwi_generate(pvt->cidspill, has_voicemail(pvt), CID_MWI_TYPE_MDMF_FULL, +- AST_LAW(pvt), pvt->cid_name, pvt->cid_num, 0); ++ pvt->cidlen = ast_callerid_vmwi_generate(pvt->cidspill, has_voicemail(pvt), CID_MWI_TYPE_MDMF_FULL, ++ AST_LAW(pvt), pvt->cid_name, pvt->cid_num, 0); + pvt->cidpos = 0; + #ifdef HAVE_DAHDI_LINEREVERSE_VMWI + } +@@ -10219,9 +10218,10 @@ + #endif + } else { + chan_sig = tmp->sig; +- memset(&p, 0, sizeof(p)); +- if (tmp->subs[SUB_REAL].dfd > -1) ++ if (tmp->subs[SUB_REAL].dfd > -1) { ++ memset(&p, 0, sizeof(p)); + res = ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &p); ++ } + } + /* Adjust starttime on loopstart and kewlstart trunks to reasonable values */ + switch (chan_sig) { +@@ -10602,9 +10602,10 @@ + if (!p->sig || (p->sig == SIG_FXSLS)) + return 1; + /* Check hook state */ +- if (p->subs[SUB_REAL].dfd > -1) ++ if (p->subs[SUB_REAL].dfd > -1) { ++ memset(&par, 0, sizeof(par)); + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par); +- else { ++ } else { + /* Assume not off hook on CVRS */ + res = 0; + par.rxisoffhook = 0; +@@ -11940,6 +11941,7 @@ + + if (!explicit) { + spanfd = pri_active_dchan_fd(pri); ++ memset(¶m, 0, sizeof(param)); + if (ioctl(spanfd, DAHDI_GET_PARAMS, ¶m)) + return -1; + span = pris[param.spanno - 1].prilogicalspan; +@@ -12991,8 +12993,12 @@ + ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features); + pri->pvts[chanpos]->dsp_features = 0; + } ++ /* Bring voice path up */ ++ f.subclass = AST_CONTROL_PROGRESS; ++ dahdi_queue_frame(pri->pvts[chanpos], &f, pri); + } + pri->pvts[chanpos]->progress = 1; ++ pri->pvts[chanpos]->dialing = 0; + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + } +@@ -13024,6 +13030,7 @@ + dahdi_queue_frame(pri->pvts[chanpos], &f, pri); + } + pri->pvts[chanpos]->proceeding = 1; ++ pri->pvts[chanpos]->dialing = 0; + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + } +@@ -13400,6 +13407,7 @@ + ast_log(LOG_ERROR, "Unable to open D-channel %d (%s)\n", x, strerror(errno)); + return -1; + } ++ memset(&p, 0, sizeof(p)); + res = ioctl(pri->fds[i], DAHDI_GET_PARAMS, &p); + if (res) { + dahdi_close_pri_fd(pri, i); +@@ -13421,6 +13429,7 @@ + pri->dchanavail[i] |= DCHAN_NOTINALARM; + else + pri->dchanavail[i] &= ~DCHAN_NOTINALARM; ++ memset(&bi, 0, sizeof(bi)); + bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; + bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; + bi.numbufs = 32; +@@ -14632,12 +14641,14 @@ + memset(&ci, 0, sizeof(ci)); + ps.channo = tmp->channel; + if (tmp->subs[SUB_REAL].dfd > -1) { ++ memset(&ci, 0, sizeof(ci)); + if (!ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GETCONF, &ci)) { + ast_cli(a->fd, "Actual Confinfo: Num/%d, Mode/0x%04x\n", ci.confno, ci.confmode); + } + if (!ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GETCONFMUTE, &x)) { + ast_cli(a->fd, "Actual Confmute: %s\n", x ? "Yes" : "No"); + } ++ memset(&ps, 0, sizeof(ps)); + if (ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps) < 0) { + ast_log(LOG_WARNING, "Failed to get parameters on channel %d: %s\n", tmp->channel, strerror(errno)); + } else { +@@ -15017,7 +15028,7 @@ + AST_CLI_DEFINE(dahdi_show_version, "Show the DAHDI version in use"), + AST_CLI_DEFINE(dahdi_set_hwgain, "Set hardware gain on a channel"), + AST_CLI_DEFINE(dahdi_set_swgain, "Set software gain on a channel"), +- AST_CLI_DEFINE(dahdi_set_dnd, "Set software gain on a channel"), ++ AST_CLI_DEFINE(dahdi_set_dnd, "Sets/resets DND (Do Not Disturb) mode on a channel"), + }; + + #define TRANSFER 0 +@@ -15297,6 +15308,7 @@ + ast_log(LOG_ERROR, "Unable to open SS7 sigchan %d (%s)\n", sigchan, strerror(errno)); + return -1; + } ++ memset(&p, 0, sizeof(p)); + res = ioctl(link->fds[curfd], DAHDI_GET_PARAMS, &p); + if (res) { + dahdi_close_ss7_fd(link, curfd); +@@ -15309,6 +15321,7 @@ + return -1; + } + ++ memset(&bi, 0, sizeof(bi)); + bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; + bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; + bi.numbufs = 32; +Index: channels/chan_phone.c +=================================================================== +--- a/channels/chan_phone.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_phone.c (.../trunk) (revision 186562) +@@ -303,13 +303,13 @@ + snprintf(cid.min, sizeof(cid.min), "%02d", tm.tm_min); + } + /* the standard format of ast->callerid is: "name" <number>, but not always complete */ +- if (ast_strlen_zero(ast->cid.cid_name)) ++ if (ast_strlen_zero(ast->connected.id.name)) + strcpy(cid.name, DEFAULT_CALLER_ID); + else +- ast_copy_string(cid.name, ast->cid.cid_name, sizeof(cid.name)); ++ ast_copy_string(cid.name, ast->connected.id.name, sizeof(cid.name)); + +- if (ast->cid.cid_num) +- ast_copy_string(cid.number, ast->cid.cid_num, sizeof(cid.number)); ++ if (ast->connected.id.number) ++ ast_copy_string(cid.number, ast->connected.id.number, sizeof(cid.number)); + + p = ast->tech_pvt; + +Index: channels/chan_h323.c +=================================================================== +--- a/channels/chan_h323.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_h323.c (.../trunk) (revision 186562) +@@ -76,7 +76,7 @@ + #include "asterisk/utils.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" + #include "asterisk/cli.h" +@@ -161,7 +161,7 @@ + char accountcode[256]; /*!< Account code */ + char rdnis[80]; /*!< Referring DNIS, if available */ + int amaflags; /*!< AMA Flags */ +- struct ast_rtp *rtp; /*!< RTP Session */ ++ struct ast_rtp_instance *rtp; /*!< RTP Session */ + struct ast_dsp *vad; /*!< Used for in-band DTMF detection */ + int nativeformats; /*!< Codec formats supported by a channel */ + int needhangup; /*!< Send hangup when Asterisk is ready */ +@@ -254,7 +254,7 @@ + .write = oh323_write, + .indicate = oh323_indicate, + .fixup = oh323_fixup, +- .bridge = ast_rtp_bridge, ++ .bridge = ast_rtp_instance_bridge, + }; + + static const char* redirectingreason2str(int redirectingreason) +@@ -381,8 +381,8 @@ + if (pvt->update_rtp_info > 0) { + if (pvt->rtp) { + ast_jb_configure(c, &global_jbconf); +- ast_channel_set_fd(c, 0, ast_rtp_fd(pvt->rtp)); +- ast_channel_set_fd(c, 1, ast_rtcp_fd(pvt->rtp)); ++ ast_channel_set_fd(c, 0, ast_rtp_instance_fd(pvt->rtp, 0)); ++ ast_channel_set_fd(c, 1, ast_rtp_instance_fd(pvt->rtp, 1)); + ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ + } + pvt->update_rtp_info = -1; +@@ -444,7 +444,7 @@ + AST_SCHED_DEL(sched, pvt->DTMFsched); + + if (pvt->rtp) { +- ast_rtp_destroy(pvt->rtp); ++ ast_rtp_instance_destroy(pvt->rtp); + } + + /* Free dsp used for in-band DTMF detection */ +@@ -510,7 +510,7 @@ + if (h323debug) { + ast_log(LOG_DTMF, "Begin sending out-of-band digit %c on %s\n", digit, c->name); + } +- ast_rtp_senddigit_begin(pvt->rtp, digit); ++ ast_rtp_instance_dtmf_begin(pvt->rtp, digit); + ast_mutex_unlock(&pvt->lock); + } else if (pvt->txDtmfDigit != digit) { + /* in-band DTMF */ +@@ -549,7 +549,7 @@ + if (h323debug) { + ast_log(LOG_DTMF, "End sending out-of-band digit %c on %s, duration %d\n", digit, c->name, duration); + } +- ast_rtp_senddigit_end(pvt->rtp, digit); ++ ast_rtp_instance_dtmf_end(pvt->rtp, digit); + ast_mutex_unlock(&pvt->lock); + } else { + /* in-band DTMF */ +@@ -606,18 +606,18 @@ + /* make sure null terminated */ + called_addr[sizeof(called_addr) - 1] = '\0'; + +- if (c->cid.cid_num) +- ast_copy_string(pvt->options.cid_num, c->cid.cid_num, sizeof(pvt->options.cid_num)); ++ if (c->connected.id.number) ++ ast_copy_string(pvt->options.cid_num, c->connected.id.number, sizeof(pvt->options.cid_num)); + +- if (c->cid.cid_name) +- ast_copy_string(pvt->options.cid_name, c->cid.cid_name, sizeof(pvt->options.cid_name)); ++ if (c->connected.id.name) ++ ast_copy_string(pvt->options.cid_name, c->connected.id.name, sizeof(pvt->options.cid_name)); + + if (c->cid.cid_rdnis) { + ast_copy_string(pvt->options.cid_rdnis, c->cid.cid_rdnis, sizeof(pvt->options.cid_rdnis)); + } + +- pvt->options.presentation = c->cid.cid_pres; +- pvt->options.type_of_number = c->cid.cid_ton; ++ pvt->options.presentation = c->connected.id.number_presentation; ++ pvt->options.type_of_number = c->connected.id.number_type; + + if ((addr = pbx_builtin_getvar_helper(c, "PRIREDIRECTREASON"))) { + if (!strcasecmp(addr, "UNKNOWN")) +@@ -747,11 +747,11 @@ + + /* Only apply it for the first packet, we just need the correct ip/port */ + if (pvt->options.nat) { +- ast_rtp_setnat(pvt->rtp, pvt->options.nat); ++ ast_rtp_instance_set_prop(pvt->rtp, AST_RTP_PROPERTY_NAT, pvt->options.nat); + pvt->options.nat = 0; + } + +- f = ast_rtp_read(pvt->rtp); ++ f = ast_rtp_instance_read(pvt->rtp, 0); + /* Don't send RFC2833 if we're not supposed to */ + if (f && (f->frametype == AST_FRAME_DTMF) && !(pvt->options.dtmfmode & (H323_DTMF_RFC2833 | H323_DTMF_CISCO))) { + return &ast_null_frame; +@@ -808,7 +808,7 @@ + break; + case 1: + if (pvt->rtp) +- fr = ast_rtcp_read(pvt->rtp); ++ fr = ast_rtp_instance_read(pvt->rtp, 1); + else + fr = &ast_null_frame; + break; +@@ -842,7 +842,7 @@ + if (pvt) { + ast_mutex_lock(&pvt->lock); + if (pvt->rtp && !pvt->recvonly) +- res = ast_rtp_write(pvt->rtp, frame); ++ res = ast_rtp_instance_write(pvt->rtp, frame); + __oh323_update_info(c, pvt); + ast_mutex_unlock(&pvt->lock); + } +@@ -910,7 +910,7 @@ + res = 0; + break; + case AST_CONTROL_SRCUPDATE: +- ast_rtp_new_source(pvt->rtp); ++ ast_rtp_instance_new_source(pvt->rtp); + res = 0; + break; + case AST_CONTROL_PROCEEDING: +@@ -946,17 +946,17 @@ + + static int __oh323_rtp_create(struct oh323_pvt *pvt) + { +- struct in_addr our_addr; ++ struct sockaddr_in our_addr; + + if (pvt->rtp) + return 0; + +- if (ast_find_ourip(&our_addr, bindaddr)) { ++ if (ast_find_ourip(&our_addr.sin_addr, bindaddr)) { + ast_mutex_unlock(&pvt->lock); + ast_log(LOG_ERROR, "Unable to locate local IP address for RTP stream\n"); + return -1; + } +- pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, our_addr); ++ pvt->rtp = ast_rtp_instance_new(NULL, sched, &our_addr, NULL); + if (!pvt->rtp) { + ast_mutex_unlock(&pvt->lock); + ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno)); +@@ -965,24 +965,24 @@ + if (h323debug) + ast_debug(1, "Created RTP channel\n"); + +- ast_rtp_setqos(pvt->rtp, tos, cos, "H323 RTP"); ++ ast_rtp_instance_set_qos(pvt->rtp, tos, cos, "H323 RTP"); + + if (h323debug) + ast_debug(1, "Setting NAT on RTP to %d\n", pvt->options.nat); +- ast_rtp_setnat(pvt->rtp, pvt->options.nat); ++ ast_rtp_instance_set_prop(pvt->rtp, AST_RTP_PROPERTY_NAT, pvt->options.nat); + + if (pvt->dtmf_pt[0] > 0) +- ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[0], "audio", "telephone-event", 0); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pvt->dtmf_pt[0], "audio", "telephone-event", 0); + if (pvt->dtmf_pt[1] > 0) +- ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[1], "audio", "cisco-telephone-event", 0); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pvt->dtmf_pt[1], "audio", "cisco-telephone-event", 0); + + if (pvt->peercapability) +- ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, &pvt->peer_prefs); + + if (pvt->owner && !ast_channel_trylock(pvt->owner)) { + ast_jb_configure(pvt->owner, &global_jbconf); +- ast_channel_set_fd(pvt->owner, 0, ast_rtp_fd(pvt->rtp)); +- ast_channel_set_fd(pvt->owner, 1, ast_rtcp_fd(pvt->rtp)); ++ ast_channel_set_fd(pvt->owner, 0, ast_rtp_instance_fd(pvt->rtp, 0)); ++ ast_channel_set_fd(pvt->owner, 1, ast_rtp_instance_fd(pvt->rtp, 1)); + ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ + ast_channel_unlock(pvt->owner); + } else +@@ -1028,13 +1028,13 @@ + if (!pvt->rtp) + __oh323_rtp_create(pvt); + #if 0 +- ast_channel_set_fd(ch, 0, ast_rtp_fd(pvt->rtp)); +- ast_channel_set_fd(ch, 1, ast_rtcp_fd(pvt->rtp)); ++ ast_channel_set_fd(ch, 0, ast_rtp_instance_fd(pvt->rtp, 0)); ++ ast_channel_set_fd(ch, 1, ast_rtp_instance_fd(pvt->rtp, 1)); + #endif + #ifdef VIDEO_SUPPORT + if (pvt->vrtp) { +- ast_channel_set_fd(ch, 2, ast_rtp_fd(pvt->vrtp)); +- ast_channel_set_fd(ch, 3, ast_rtcp_fd(pvt->vrtp)); ++ ast_channel_set_fd(ch, 2, ast_rtp_instance_fd(pvt->vrtp, 0)); ++ ast_channel_set_fd(ch, 3, ast_rtp_instance_fd(pvt->vrtp, 1)); + } + #endif + #ifdef T38_SUPPORT +@@ -1112,7 +1112,7 @@ + } + if (!pvt->cd.call_token) { + ast_log(LOG_ERROR, "Not enough memory to alocate call token\n"); +- ast_rtp_destroy(pvt->rtp); ++ ast_rtp_instance_destroy(pvt->rtp); + ast_free(pvt); + return NULL; + } +@@ -1912,7 +1912,7 @@ + return NULL; + } + /* figure out our local RTP port and tell the H.323 stack about it */ +- ast_rtp_get_us(pvt->rtp, &us); ++ ast_rtp_instance_get_local_address(pvt->rtp, &us); + ast_mutex_unlock(&pvt->lock); + + ast_copy_string(info->addr, ast_inet_ntoa(us.sin_addr), sizeof(info->addr)); +@@ -1931,7 +1931,6 @@ + { + struct oh323_pvt *pvt; + struct sockaddr_in them; +- struct rtpPayloadType rtptype; + int nativeformats_changed; + enum { NEED_NONE, NEED_HOLD, NEED_UNHOLD } rtp_change = NEED_NONE; + +@@ -1953,7 +1952,7 @@ + __oh323_rtp_create(pvt); + + if ((pt == 2) && (pvt->jointcapability & AST_FORMAT_G726_AAL2)) { +- ast_rtp_set_rtpmap_type(pvt->rtp, pt, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pt, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD); + } + + them.sin_family = AF_INET; +@@ -1962,13 +1961,13 @@ + them.sin_port = htons(remotePort); + + if (them.sin_addr.s_addr) { +- ast_rtp_set_peer(pvt->rtp, &them); ++ ast_rtp_instance_set_remote_address(pvt->rtp, &them); + if (pvt->recvonly) { + pvt->recvonly = 0; + rtp_change = NEED_UNHOLD; + } + } else { +- ast_rtp_stop(pvt->rtp); ++ ast_rtp_instance_stop(pvt->rtp); + if (!pvt->recvonly) { + pvt->recvonly = 1; + rtp_change = NEED_HOLD; +@@ -1978,7 +1977,7 @@ + /* Change native format to reflect information taken from OLC/OLCAck */ + nativeformats_changed = 0; + if (pt != 128 && pvt->rtp) { /* Payload type is invalid, so try to use previously decided */ +- rtptype = ast_rtp_lookup_pt(pvt->rtp, pt); ++ struct ast_rtp_payload_type rtptype = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(pvt->rtp), pt); + if (h323debug) + ast_debug(1, "Native format is set to %d from %d by RTP payload type %d\n", rtptype.code, pvt->nativeformats, pt); + if (pvt->nativeformats != rtptype.code) { +@@ -2359,7 +2358,7 @@ + } + if (pvt->rtp) { + /* Immediately stop RTP */ +- ast_rtp_destroy(pvt->rtp); ++ ast_rtp_instance_destroy(pvt->rtp); + pvt->rtp = NULL; + } + /* Free dsp used for in-band DTMF detection */ +@@ -2421,7 +2420,7 @@ + return; + } + if (pvt->rtp) { +- ast_rtp_set_rtpmap_type(pvt->rtp, payload, "audio", (is_cisco ? "cisco-telephone-event" : "telephone-event"), 0); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, payload, "audio", (is_cisco ? "cisco-telephone-event" : "telephone-event"), 0); + } + pvt->dtmf_pt[is_cisco ? 1 : 0] = payload; + ast_mutex_unlock(&pvt->lock); +@@ -2452,7 +2451,7 @@ + } + } + if (pvt->rtp) +- ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, &pvt->peer_prefs); + } + ast_mutex_unlock(&pvt->lock); + } +@@ -3113,19 +3112,19 @@ + static struct ast_cli_entry cli_h323_reload = + AST_CLI_DEFINE(handle_cli_h323_reload, "Reload H.323 configuration"); + +-static enum ast_rtp_get_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { + struct oh323_pvt *pvt; +- enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; ++ enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL; + + if (!(pvt = (struct oh323_pvt *)chan->tech_pvt)) +- return AST_RTP_GET_FAILED; ++ return AST_RTP_GLUE_RESULT_FORBID; + + ast_mutex_lock(&pvt->lock); +- *rtp = pvt->rtp; ++ *instance = pvt->rtp ? ao2_ref(pvt->rtp, +1), pvt->rtp : NULL; + #if 0 + if (pvt->options.bridge) { +- res = AST_RTP_TRY_NATIVE; ++ res = AST_RTP_GLUE_RESULT_REMOTE; + } + #endif + ast_mutex_unlock(&pvt->lock); +@@ -3133,11 +3132,6 @@ + return res; + } + +-static enum ast_rtp_get_result oh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +-{ +- return AST_RTP_GET_FAILED; +-} +- + static char *convertcap(int cap) + { + switch (cap) { +@@ -3165,7 +3159,7 @@ + } + } + +-static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) ++static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) + { + /* XXX Deal with Video */ + struct oh323_pvt *pvt; +@@ -3183,19 +3177,18 @@ + ast_log(LOG_ERROR, "No Private Structure, this is bad\n"); + return -1; + } +- ast_rtp_get_peer(rtp, &them); +- ast_rtp_get_us(rtp, &us); ++ ast_rtp_instance_get_remote_address(rtp, &them); ++ ast_rtp_instance_get_local_address(rtp, &us); + #if 0 /* Native bridge still isn't ready */ + h323_native_bridge(pvt->cd.call_token, ast_inet_ntoa(them.sin_addr), mode); + #endif + return 0; + } + +-static struct ast_rtp_protocol oh323_rtp = { ++static struct ast_rtp_glue oh323_rtp_glue = { + .type = "H323", + .get_rtp_info = oh323_get_rtp_peer, +- .get_vrtp_info = oh323_get_vrtp_peer, +- .set_rtp_peer = oh323_set_rtp_peer, ++ .update_peer = oh323_set_rtp_peer, + }; + + static enum ast_module_load_result load_module(void) +@@ -3250,7 +3243,7 @@ + } + ast_cli_register_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); + +- ast_rtp_proto_register(&oh323_rtp); ++ ast_rtp_glue_register(&oh323_rtp_glue); + + /* Register our callback functions */ + h323_callback_register(setup_incoming_call, +@@ -3271,7 +3264,7 @@ + /* start the h.323 listener */ + if (h323_start_listener(h323_signalling_port, bindaddr)) { + ast_log(LOG_ERROR, "Unable to create H323 listener.\n"); +- ast_rtp_proto_unregister(&oh323_rtp); ++ ast_rtp_glue_unregister(&oh323_rtp_glue); + ast_cli_unregister_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); + ast_cli_unregister(&cli_h323_reload); + h323_end_process(); +@@ -3310,7 +3303,7 @@ + ast_cli_unregister(&cli_h323_reload); + + ast_channel_unregister(&oh323_tech); +- ast_rtp_proto_unregister(&oh323_rtp); ++ ast_rtp_glue_unregister(&oh323_rtp_glue); + + if (!ast_mutex_lock(&iflock)) { + /* hangup all interfaces if they have an owner */ +Index: channels/chan_sip.c +=================================================================== +--- a/channels/chan_sip.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_sip.c (.../trunk) (revision 186562) +@@ -229,7 +229,7 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" + #include "asterisk/udptl.h" + #include "asterisk/acl.h" + #include "asterisk/manager.h" +@@ -271,6 +271,7 @@ + #include "asterisk/ast_version.h" + #include "asterisk/event.h" + #include "asterisk/tcptls.h" ++#include "asterisk/stun.h" + + /*** DOCUMENTATION + <application name="SIPDtmfMode" language="en_US"> +@@ -691,6 +692,7 @@ + AUTH_PEER_NOT_DYNAMIC = -6, + AUTH_ACL_FAILED = -7, + AUTH_BAD_TRANSPORT = -8, ++ AUTH_RTP_FAILED = 9, + }; + + /*! \brief States for outbound registrations (with register= lines in sip.conf */ +@@ -940,7 +942,55 @@ + { SIP_OPT_TARGET_DIALOG,NOT_SUPPORTED, "tdialog" }, + }; + ++/*! \brief Diversion header reasons ++ * ++ * The core defines a bunch of constants used to define ++ * redirecting reasons. This provides a translation table ++ * between those and the strings which may be present in ++ * a SIP Diversion header ++ */ ++static const struct sip_reasons { ++ enum AST_REDIRECTING_REASON code; ++ char * const text; ++} sip_reason_table[] = { ++ { AST_REDIRECTING_REASON_UNKNOWN, "unknown" }, ++ { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" }, ++ { AST_REDIRECTING_REASON_NO_ANSWER, "no-answer" }, ++ { AST_REDIRECTING_REASON_UNAVAILABLE, "unavailable" }, ++ { AST_REDIRECTING_REASON_UNCONDITIONAL, "unconditional" }, ++ { AST_REDIRECTING_REASON_TIME_OF_DAY, "time-of-day" }, ++ { AST_REDIRECTING_REASON_DO_NOT_DISTURB, "do-not-disturb" }, ++ { AST_REDIRECTING_REASON_DEFLECTION, "deflection" }, ++ { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" }, ++ { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" }, ++ { AST_REDIRECTING_REASON_AWAY, "away" }, ++ { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"} ++}; + ++static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text) ++{ ++ enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN; ++ int i; ++ ++ for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) { ++ if (!strcasecmp(text, sip_reason_table[i].text)) { ++ ast = sip_reason_table[i].code; ++ break; ++ } ++ } ++ ++ return ast; ++} ++ ++static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code) ++{ ++ if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) { ++ return sip_reason_table[code].text; ++ } ++ ++ return "unknown"; ++} ++ + /*! \brief SIP Methods we support + \todo This string should be set dynamically. We only support REFER and SUBSCRIBE if we have + allowsubscribe and allowrefer on in sip.conf. +@@ -1011,6 +1061,7 @@ + #define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */ + #define DEFAULT_SDPSESSION "Asterisk PBX" /*!< Default SDP session name, (s=) header unless re-defined in sip.conf */ + #define DEFAULT_SDPOWNER "root" /*!< Default SDP username field in (o=) header unless re-defined in sip.conf */ ++#define DEFAULT_ENGINE "asterisk" /*!< Default RTP engine to use for sessions */ + #endif + /*@}*/ + +@@ -1029,6 +1080,7 @@ + static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting + * a bridged channel on hold */ + static char default_parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */ ++static char default_engine[256]; /*!< Default RTP engine */ + static int default_maxcallbitrate; /*!< Maximum bitrate for call */ + static struct ast_codec_pref default_prefs; /*!< Default codec prefs */ + static unsigned int default_transports; /*!< Default Transports (enum sip_transport) that are acceptable */ +@@ -1350,7 +1402,10 @@ + #define SIP_PROG_INBAND_NO (1 << 25) + #define SIP_PROG_INBAND_YES (2 << 25) + +-#define SIP_SENDRPID (1 << 29) /*!< DP: Remote Party-ID Support */ ++#define SIP_SENDRPID (3 << 29) /*!< DP: Remote Party-ID Support */ ++#define SIP_SENDRPID_NO (0 << 29) ++#define SIP_SENDRPID_PAI (1 << 29) /*!< Use "P-Asserted-Identity" for rpid */ ++#define SIP_SENDRPID_RPID (2 << 29) /*!< Use "Remote-Party-ID" for rpid */ + #define SIP_G726_NONSTANDARD (1 << 31) /*!< DP: Use non-standard packing for G726-32 data */ + + /*! \brief Flags to copy from peer/user to dialog */ +@@ -1369,6 +1424,10 @@ + /* Space for addition of other realtime flags in the future */ + #define SIP_PAGE2_STATECHANGEQUEUE (1 << 9) /*!< D: Unsent state pending change exists */ + ++#define SIP_PAGE2_CONNECTLINEUPDATE_PEND (1 << 10) ++#define SIP_PAGE2_RPID_IMMEDIATE (1 << 11) ++ ++#define SIP_PAGE2_PREFERRED_CODEC (1 << 13) /*!< GDP: Only respond with single most preferred joint codec */ + #define SIP_PAGE2_VIDEOSUPPORT (1 << 14) /*!< DP: Video supported if offered? */ + #define SIP_PAGE2_TEXTSUPPORT (1 << 15) /*!< GDP: Global text enable */ + #define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 16) /*!< GP: Allow subscriptions from this peer? */ +@@ -1398,7 +1457,8 @@ + (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \ + SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | \ + SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_FAX_DETECT | \ +- SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS) ++ SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS | SIP_PAGE2_PREFERRED_CODEC | \ ++ SIP_PAGE2_RPID_IMMEDIATE) + + /*@}*/ + +@@ -1455,7 +1515,6 @@ + /*! \brief T38 States for a call */ + enum t38state { + T38_DISABLED = 0, /*!< Not enabled */ +- T38_LOCAL_DIRECT, /*!< Offered from local */ + T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */ + T38_PEER_DIRECT, /*!< Offered from peer */ + T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */ +@@ -1469,6 +1528,7 @@ + int peercapability; /*!< Peers T38 capability */ + int jointcapability; /*!< Supported T38 capability at both ends */ + enum t38state state; /*!< T.38 state */ ++ unsigned int direct:1; /*!< Whether the T38 came from the initial invite or not */ + }; + + /*! \brief Parameters to know status of transfer */ +@@ -1606,10 +1666,9 @@ + AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */ + /* we only store the part in <brackets> in this field. */ + AST_STRING_FIELD(our_contact); /*!< Our contact header */ +- AST_STRING_FIELD(rpid); /*!< Our RPID header */ +- AST_STRING_FIELD(rpid_from); /*!< Our RPID From header */ + AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */ + AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ ++ AST_STRING_FIELD(engine); /*!< RTP engine to use */ + ); + char via[128]; /*!< Via: header */ + struct sip_socket socket; /*!< The socket used for this dialog */ +@@ -1669,17 +1728,19 @@ + struct ast_channel *owner; /*!< Who owns us (if we have an owner) */ + struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */ + int route_persistant; /*!< Is this the "real" route? */ +- struct ast_variable *notify_headers; /*!< Custom notify type */ ++ struct ast_variable *notify_headers; /*!< Custom notify type */ + struct sip_auth *peerauth; /*!< Realm authentication */ + int noncecount; /*!< Nonce-count */ + char lastmsg[256]; /*!< Last Message sent/received */ + int amaflags; /*!< AMA Flags */ + int pendinginvite; /*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */ ++ int glareinvite; /*!< A invite received while a pending invite is already present is stored here. Its seqno is the ++ value. Since this glare invite's seqno is not the same as the pending invite's, it must be ++ held in order to properly process acknowledgements for our 491 response. */ + struct sip_request initreq; /*!< Latest request that opened a new transaction + within this dialog. +- NOT the request that opened the dialog +- */ +- ++ NOT the request that opened the dialog */ ++ + int initid; /*!< Auto-congest ID if appropriate (scheduler) */ + int waitid; /*!< Wait ID for scheduler after 491 or other delays */ + int autokillid; /*!< Auto-kill ID (scheduler) */ +@@ -1690,15 +1751,15 @@ + int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */ + int laststate; /*!< SUBSCRIBE: Last known extension state */ + int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */ +- ++ + struct ast_dsp *vad; /*!< Inband DTMF Detection dsp */ +- ++ + struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one + Used in peerpoke, mwi subscriptions */ + struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ +- struct ast_rtp *rtp; /*!< RTP Session */ +- struct ast_rtp *vrtp; /*!< Video RTP session */ +- struct ast_rtp *trtp; /*!< Text RTP session */ ++ struct ast_rtp_instance *rtp; /*!< RTP Session */ ++ struct ast_rtp_instance *vrtp; /*!< Video RTP session */ ++ struct ast_rtp_instance *trtp; /*!< Text RTP session */ + struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ + struct sip_history_head *history; /*!< History of this SIP dialog */ + size_t history_entries; /*!< Number of entires in the history */ +@@ -1841,6 +1902,7 @@ + AST_STRING_FIELD(mohsuggest); /*!< Music on Hold class */ + AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ + AST_STRING_FIELD(useragent); /*!< User agent in SIP request (saved from registration) */ ++ AST_STRING_FIELD(engine); /*!< RTP Engine to use */ + ); + struct sip_socket socket; /*!< Socket used for this peer */ + unsigned int transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ +@@ -2228,11 +2290,11 @@ + static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req); + static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req); + static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req); +-static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp); ++static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid); + static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported); + static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale); + static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); +-static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable); ++static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable); + static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch); + static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch); + static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init); +@@ -2249,7 +2311,7 @@ + static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno); + static void copy_request(struct sip_request *dst, const struct sip_request *src); + static void receive_message(struct sip_pvt *p, struct sip_request *req); +-static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req); ++static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward); + static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only); + + /*--- Dialog management */ +@@ -2293,7 +2355,7 @@ + static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, + struct ast_str **m_buf, struct ast_str **a_buf, + int debug); +-static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp); ++static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38); + static void do_setnat(struct sip_pvt *p, int natflags); + static void stop_media_flows(struct sip_pvt *p); + +@@ -2497,11 +2559,14 @@ + static int set_address_from_contact(struct sip_pvt *pvt); + static void check_via(struct sip_pvt *p, struct sip_request *req); + static char *get_calleridname(const char *input, char *output, size_t outputsize); +-static int get_rpid_num(const char *input, char *output, int maxlen); +-static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq); ++static int get_rpid(struct sip_pvt *p, struct sip_request *oreq); ++static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason); + static int get_destination(struct sip_pvt *p, struct sip_request *oreq); + static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline); + static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout); ++static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen); ++static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen); ++static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req, struct ast_party_redirecting *redirecting, int set_call_forward); + + /*-- TCP connection handling ---*/ + static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session); +@@ -2528,6 +2593,7 @@ + static int add_line(struct sip_request *req, const char *line); + static int add_text(struct sip_request *req, const char *text); + static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode); ++static int add_rpid(struct sip_request *req, struct sip_pvt *p); + static int add_vidupdate(struct sip_request *req); + static void add_route(struct sip_request *req, struct sip_route *route); + static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field); +@@ -2536,7 +2602,6 @@ + static void set_destination(struct sip_pvt *p, char *uri); + static void append_date(struct sip_request *req); + static void build_contact(struct sip_pvt *p); +-static void build_rpid(struct sip_pvt *p); + + /*------Request handling functions */ + static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock); +@@ -2561,14 +2626,6 @@ + static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); + static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); + +-/*----- RTP interface functions */ +-static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); +-static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); +-static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); +-static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); +-static int sip_get_codec(struct ast_channel *chan); +-static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect); +- + /*------ T38 Support --------- */ + static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); + static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan); +@@ -2589,6 +2646,9 @@ + static enum st_mode st_get_mode(struct sip_pvt *); + static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p); + ++/*------- RTP Glue functions -------- */ ++static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active); ++ + /*!--- SIP MWI Subscription support */ + static int sip_subscribe_mwi(const char *value, int lineno); + static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi); +@@ -2617,8 +2677,8 @@ + .fixup = sip_fixup, /* called with chan locked */ + .send_digit_begin = sip_senddigit_begin, /* called with chan unlocked */ + .send_digit_end = sip_senddigit_end, +- .bridge = ast_rtp_bridge, /* XXX chan unlocked ? */ +- .early_bridge = ast_rtp_early_bridge, ++ .bridge = ast_rtp_instance_bridge, /* XXX chan unlocked ? */ ++ .early_bridge = ast_rtp_instance_early_bridge, + .send_text = sip_sendtext, /* called with chan locked */ + .func_channel_read = acf_channel_read, + .queryoption = sip_queryoption, +@@ -2691,17 +2751,6 @@ + return errorvalue; + } + +- +-/*! \brief Interface structure with callbacks used to connect to RTP module */ +-static struct ast_rtp_protocol sip_rtp = { +- .type = "SIP", +- .get_rtp_info = sip_get_rtp_peer, +- .get_vrtp_info = sip_get_vrtp_peer, +- .get_trtp_info = sip_get_trtp_peer, +- .set_rtp_peer = sip_set_rtp_peer, +- .get_codec = sip_get_codec, +-}; +- + /*! + * duplicate a list of channel variables, \return the copy. + */ +@@ -3896,7 +3945,6 @@ + /* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */ + if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT)) { + switch (p->t38.state) { +- case T38_LOCAL_DIRECT: + case T38_LOCAL_REINVITE: + case T38_PEER_DIRECT: + case T38_PEER_REINVITE: +@@ -4591,11 +4639,11 @@ + + if (p->rtp) { + ast_debug(1, "Setting NAT on RTP to %s\n", mode); +- ast_rtp_setnat(p->rtp, natflags); ++ ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, natflags); + } + if (p->vrtp) { + ast_debug(1, "Setting NAT on VRTP to %s\n", mode); +- ast_rtp_setnat(p->vrtp, natflags); ++ ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_NAT, natflags); + } + if (p->udptl) { + ast_debug(1, "Setting NAT on UDPTL to %s\n", mode); +@@ -4603,7 +4651,7 @@ + } + if (p->trtp) { + ast_debug(1, "Setting NAT on TRTP to %s\n", mode); +- ast_rtp_setnat(p->trtp, natflags); ++ ast_rtp_instance_set_prop(p->trtp, AST_RTP_PROPERTY_NAT, natflags); + } + } + +@@ -4618,6 +4666,10 @@ + if (old == state) + return; + ++ if (state == T38_PEER_DIRECT) { ++ p->t38.direct = 1; ++ } ++ + p->t38.state = state; + ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>"); + +@@ -4691,6 +4743,51 @@ + *to_sock = *from_sock; + } + ++/*! \brief Initialize RTP portion of a dialog ++ * \returns -1 on failure, 0 on success ++ */ ++static int dialog_initialize_rtp(struct sip_pvt *dialog) ++{ ++ if (!sip_methods[dialog->method].need_rtp) { ++ return 0; ++ } ++ ++ if (!(dialog->rtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { ++ return -1; ++ } ++ ++ if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (dialog->capability & AST_FORMAT_VIDEO_MASK)) { ++ if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { ++ return -1; ++ } ++ ast_rtp_instance_set_timeout(dialog->vrtp, global_rtptimeout); ++ ast_rtp_instance_set_hold_timeout(dialog->vrtp, global_rtpholdtimeout); ++ ++ ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1); ++ } ++ ++ if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT)) { ++ if (!(dialog->trtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { ++ return -1; ++ } ++ ast_rtp_instance_set_timeout(dialog->trtp, global_rtptimeout); ++ ast_rtp_instance_set_hold_timeout(dialog->trtp, global_rtpholdtimeout); ++ ++ ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1); ++ } ++ ++ ast_rtp_instance_set_timeout(dialog->rtp, global_rtptimeout); ++ ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout); ++ ++ ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1); ++ ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); ++ ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ++ ++ ast_rtp_instance_set_qos(dialog->rtp, global_tos_audio, 0, "SIP RTP"); ++ ++ return 0; ++} ++ + /*! \brief Create address structure from peer reference. + * This function copies data from peer to the dialog, so we don't have to look up the peer + * again from memory or database during the life time of the dialog. +@@ -4718,17 +4815,6 @@ + ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); + ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); + dialog->capability = peer->capability; +- if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) && +- (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || +- !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && +- dialog->vrtp) { +- ast_rtp_destroy(dialog->vrtp); +- dialog->vrtp = NULL; +- } +- if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT) && dialog->trtp) { +- ast_rtp_destroy(dialog->trtp); +- dialog->trtp = NULL; +- } + dialog->prefs = peer->prefs; + if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) { + if (!dialog->udptl) { +@@ -4744,29 +4830,28 @@ + } + do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE); + ++ ast_string_field_set(dialog, engine, peer->engine); ++ ++ if (dialog_initialize_rtp(dialog)) { ++ return -1; ++ } ++ + if (dialog->rtp) { /* Audio */ +- ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); +- ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); +- ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout); +- ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout); +- ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive); ++ ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); ++ ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ++ ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout); ++ ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout); + /* Set Frame packetization */ +- ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs); + dialog->autoframing = peer->autoframing; + } + if (dialog->vrtp) { /* Video */ +- ast_rtp_setdtmf(dialog->vrtp, 0); +- ast_rtp_setdtmfcompensate(dialog->vrtp, 0); +- ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout); +- ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout); +- ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive); ++ ast_rtp_instance_set_timeout(dialog->vrtp, peer->rtptimeout); ++ ast_rtp_instance_set_hold_timeout(dialog->vrtp, peer->rtpholdtimeout); + } + if (dialog->trtp) { /* Realtime text */ +- ast_rtp_setdtmf(dialog->trtp, 0); +- ast_rtp_setdtmfcompensate(dialog->trtp, 0); +- ast_rtp_set_rtptimeout(dialog->trtp, peer->rtptimeout); +- ast_rtp_set_rtpholdtimeout(dialog->trtp, peer->rtpholdtimeout); +- ast_rtp_set_rtpkeepalive(dialog->trtp, peer->rtpkeepalive); ++ ast_rtp_instance_set_timeout(dialog->trtp, peer->rtptimeout); ++ ast_rtp_instance_set_hold_timeout(dialog->trtp, peer->rtpholdtimeout); + } + + ast_string_field_set(dialog, peername, peer->name); +@@ -4779,7 +4864,10 @@ + ast_string_field_set(dialog, tohost, peer->tohost); + ast_string_field_set(dialog, fullcontact, peer->fullcontact); + ast_string_field_set(dialog, context, peer->context); ++ ast_string_field_set(dialog, cid_num, peer->cid_num); ++ ast_string_field_set(dialog, cid_name, peer->cid_name); + ast_string_field_set(dialog, parkinglot, peer->parkinglot); ++ ast_string_field_set(dialog, engine, peer->engine); + ref_proxy(dialog, obproxy_get(dialog, peer)); + dialog->callgroup = peer->callgroup; + dialog->pickupgroup = peer->pickupgroup; +@@ -4875,6 +4963,12 @@ + return res; + } + ++ if (dialog_initialize_rtp(dialog)) { ++ return -1; ++ } ++ ++ do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE); ++ + ast_string_field_set(dialog, tohost, peername); + + /* Get the outbound proxy information */ +@@ -4995,9 +5089,6 @@ + } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) { + /* We're replacing a call. */ + p->options->replaces = ast_var_value(current); +- } else if (!strcasecmp(ast_var_name(current), "T38CALL")) { +- p->t38.state = T38_LOCAL_DIRECT; +- ast_debug(1, "T38State change to %d on channel %s\n", p->t38.state, ast->name); + } + } + +@@ -5039,11 +5130,13 @@ + p->t38.jointcapability = p->t38.capability; + ast_debug(2, "Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability); + ++ sip_pvt_lock(p); + xmitres = transmit_invite(p, SIP_INVITE, 1, 2); ++ sip_pvt_unlock(p); + if (xmitres == XMIT_ERROR) + return -1; + p->invitestate = INV_CALLING; +- ++ + /* Initialize auto-congest time */ + AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p, + dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"), +@@ -5150,15 +5243,13 @@ + p->notify_headers = NULL; + } + if (p->rtp) { +- ast_rtp_destroy(p->rtp); ++ ast_rtp_instance_destroy(p->rtp); + } + if (p->vrtp) { +- ast_rtp_destroy(p->vrtp); ++ ast_rtp_instance_destroy(p->vrtp); + } + if (p->trtp) { +- while (ast_rtp_get_bridged(p->trtp)) +- usleep(1); +- ast_rtp_destroy(p->trtp); ++ ast_rtp_instance_destroy(p->trtp); + } + if (p->udptl) + ast_udptl_destroy(p->udptl); +@@ -5677,42 +5768,50 @@ + + if (!p->pendinginvite) { + struct ast_channel *bridge = ast_bridged_channel(oldowner); +- char *audioqos = ""; +- char *videoqos = ""; +- char *textqos = ""; ++ char quality_buf[AST_MAX_USER_FIELD], *quality; + +- if (p->rtp) +- ast_rtp_set_vars(oldowner, p->rtp); ++ if (p->rtp) { ++ ast_rtp_instance_set_stats_vars(oldowner, p->rtp); ++ } + + if (bridge) { + struct sip_pvt *q = bridge->tech_pvt; + +- if (IS_SIP_TECH(bridge->tech) && q) +- ast_rtp_set_vars(bridge, q->rtp); ++ if (IS_SIP_TECH(bridge->tech) && q) { ++ ast_rtp_instance_set_stats_vars(bridge, q->rtp); ++ } + } + +- if (p->vrtp) +- videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); +- if (p->trtp) +- textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); ++ if (p->do_history || oldowner) { ++ if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { ++ if (p->do_history) { ++ append_history(p, "RTCPaudio", "Quality:%s", quality); ++ } ++ if (oldowner) { ++ pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", quality); ++ } ++ } ++ if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { ++ if (p->do_history) { ++ append_history(p, "RTCPvideo", "Quality:%s", quality); ++ } ++ if (oldowner) { ++ pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", quality); ++ } ++ } ++ if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { ++ if (p->do_history) { ++ append_history(p, "RTCPtext", "Quality:%s", quality); ++ } ++ if (oldowner) { ++ pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", quality); ++ } ++ } ++ } ++ + /* Send a hangup */ + transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); + +- /* Get RTCP quality before end of call */ +- if (p->do_history) { +- if (p->rtp) +- append_history(p, "RTCPaudio", "Quality:%s", audioqos); +- if (p->vrtp) +- append_history(p, "RTCPvideo", "Quality:%s", videoqos); +- if (p->trtp) +- append_history(p, "RTCPtext", "Quality:%s", textqos); +- } +- if (p->rtp && oldowner) +- pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos); +- if (p->vrtp && oldowner) +- pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos); +- if (p->trtp && oldowner) +- pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", textqos); + } else { + /* Note we will need a BYE when this all settles out + but we can't send one while we have "INVITE" outstanding. */ +@@ -5769,13 +5868,10 @@ + ast_debug(1, "SIP answering channel: %s\n", ast->name); + if (p->t38.state == T38_PEER_DIRECT) { + change_t38_state(p, T38_ENABLED); +- res = transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); +- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); +- } else { +- ast_rtp_new_source(p->rtp); +- res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE); +- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); + } ++ ast_rtp_instance_new_source(p->rtp); ++ res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE, TRUE); ++ ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); + } + sip_pvt_unlock(p); + return res; +@@ -5808,13 +5904,17 @@ + if ((ast->_state != AST_STATE_UP) && + !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && + !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { +- ast_rtp_new_source(p->rtp); ++ ast_rtp_instance_new_source(p->rtp); + p->invitestate = INV_EARLY_MEDIA; +- transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); +- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); ++ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); ++ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); ++ } else if (p->t38.state == T38_ENABLED && !p->t38.direct) { ++ change_t38_state(p, T38_DISABLED); ++ transmit_reinvite_with_sdp(p, FALSE, FALSE); ++ } else { ++ p->lastrtptx = time(NULL); ++ res = ast_rtp_instance_write(p->rtp, frame); + } +- p->lastrtptx = time(NULL); +- res = ast_rtp_write(p->rtp, frame); + } + sip_pvt_unlock(p); + } +@@ -5828,11 +5928,11 @@ + !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && + !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + p->invitestate = INV_EARLY_MEDIA; +- transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); +- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); ++ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); ++ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + } + p->lastrtptx = time(NULL); +- res = ast_rtp_write(p->vrtp, frame); ++ res = ast_rtp_instance_write(p->vrtp, frame); + } + sip_pvt_unlock(p); + } +@@ -5841,7 +5941,7 @@ + if (p) { + sip_pvt_lock(p); + if (p->red) { +- red_buffer_t140(p->trtp, frame); ++ ast_rtp_red_buffer(p->trtp, frame); + } else { + if (p->trtp) { + /* Activate text early media */ +@@ -5849,11 +5949,11 @@ + !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && + !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + p->invitestate = INV_EARLY_MEDIA; +- transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); +- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); ++ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); ++ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + } + p->lastrtptx = time(NULL); +- res = ast_rtp_write(p->trtp, frame); ++ res = ast_rtp_instance_write(p->trtp, frame); + } + } + sip_pvt_unlock(p); +@@ -5869,8 +5969,16 @@ + we simply forget the frames if we get modem frames before the bridge is up. + Fax will re-transmit. + */ +- if (p->udptl && ast->_state == AST_STATE_UP) +- res = ast_udptl_write(p->udptl, frame); ++ if (ast->_state == AST_STATE_UP) { ++ if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->t38.state == T38_DISABLED) { ++ if (!p->pendinginvite) { ++ change_t38_state(p, T38_LOCAL_REINVITE); ++ transmit_reinvite_with_sdp(p, TRUE, FALSE); ++ } ++ } else if (p->udptl && p->t38.state == T38_ENABLED) { ++ res = ast_udptl_write(p->udptl, frame); ++ } ++ } + sip_pvt_unlock(p); + } + break; +@@ -5933,11 +6041,15 @@ + sip_pvt_lock(p); + switch (ast_test_flag(&p->flags[0], SIP_DTMF)) { + case SIP_DTMF_INBAND: +- res = -1; /* Tell Asterisk to generate inband indications */ ++ if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_INBAND) { ++ ast_rtp_instance_dtmf_begin(p->rtp, digit); ++ } else { ++ res = -1; /* Tell Asterisk to generate inband indications */ ++ } + break; + case SIP_DTMF_RFC2833: + if (p->rtp) +- ast_rtp_senddigit_begin(p->rtp, digit); ++ ast_rtp_instance_dtmf_begin(p->rtp, digit); + break; + default: + break; +@@ -5962,10 +6074,14 @@ + break; + case SIP_DTMF_RFC2833: + if (p->rtp) +- ast_rtp_senddigit_end(p->rtp, digit); ++ ast_rtp_instance_dtmf_end(p->rtp, digit); + break; + case SIP_DTMF_INBAND: +- res = -1; /* Tell Asterisk to stop inband indications */ ++ if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_INBAND) { ++ ast_rtp_instance_dtmf_end(p->rtp, digit); ++ } else { ++ res = -1; /* Tell Asterisk to stop inband indications */ ++ } + break; + } + sip_pvt_unlock(p); +@@ -6053,18 +6169,18 @@ + !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && + !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + p->invitestate = INV_EARLY_MEDIA; +- transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); +- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); ++ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); ++ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + break; + } + res = -1; + break; + case AST_CONTROL_HOLD: +- ast_rtp_new_source(p->rtp); ++ ast_rtp_instance_new_source(p->rtp); + ast_moh_start(ast, data, p->mohinterpret); + break; + case AST_CONTROL_UNHOLD: +- ast_rtp_new_source(p->rtp); ++ ast_rtp_instance_new_source(p->rtp); + ast_moh_stop(ast); + break; + case AST_CONTROL_VIDUPDATE: /* Request a video frame update */ +@@ -6085,7 +6201,7 @@ + AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr")); + change_t38_state(p, T38_ENABLED); + transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); +- } else if (p->t38.state != T38_ENABLED) { ++ } else if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) { + change_t38_state(p, T38_LOCAL_REINVITE); + if (!p->pendinginvite) { + transmit_reinvite_with_sdp(p, TRUE, FALSE); +@@ -6110,8 +6226,14 @@ + } + break; + case AST_CONTROL_SRCUPDATE: +- ast_rtp_new_source(p->rtp); ++ ast_rtp_instance_new_source(p->rtp); + break; ++ case AST_CONTROL_CONNECTED_LINE: ++ update_connectedline(p, data, datalen); ++ break; ++ case AST_CONTROL_REDIRECTING: ++ update_redirecting(p, data, datalen); ++ break; + case -1: + res = -1; + break; +@@ -6224,23 +6346,29 @@ + ast_debug(3, "This channel will not be able to handle video.\n"); + + if ((ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { +- i->vad = ast_dsp_new(); +- ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); +- if (global_relaxdtmf) +- ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); ++ if (!i->rtp || ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_INBAND)) { ++ i->vad = ast_dsp_new(); ++ ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); ++ if (global_relaxdtmf) ++ ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); ++ } ++ } else if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) { ++ if (i->rtp) { ++ ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_RFC2833); ++ } + } + + /* Set file descriptors for audio, video, realtime text and UDPTL as needed */ + if (i->rtp) { +- ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); +- ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); ++ ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); ++ ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); + } + if (needvideo && i->vrtp) { +- ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); +- ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); ++ ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); ++ ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); + } + if (needtext && i->trtp) +- ast_channel_set_fd(tmp, 4, ast_rtp_fd(i->trtp)); ++ ast_channel_set_fd(tmp, 4, ast_rtp_instance_fd(i->trtp, 0)); + if (i->udptl) + ast_channel_set_fd(tmp, 5, ast_udptl_fd(i->udptl)); + +@@ -6292,10 +6420,6 @@ + if (i->rtp) + ast_jb_configure(tmp, &global_jbconf); + +- /* If the INVITE contains T.38 SDP information set the proper channel variable so a created outgoing call will also have T.38 */ +- if (i->udptl && i->t38.state == T38_PEER_DIRECT) +- pbx_builtin_setvar_helper(tmp, "_T38CALL", "1"); +- + /* Set channel variables for this call from configuration */ + for (v = i->chanvars ; v ; v = v->next) { + char valuebuf[1024]; +@@ -6468,19 +6592,19 @@ + + switch(ast->fdno) { + case 0: +- f = ast_rtp_read(p->rtp); /* RTP Audio */ ++ f = ast_rtp_instance_read(p->rtp, 0); /* RTP Audio */ + break; + case 1: +- f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */ ++ f = ast_rtp_instance_read(p->rtp, 1); /* RTCP Control Channel */ + break; + case 2: +- f = ast_rtp_read(p->vrtp); /* RTP Video */ ++ f = ast_rtp_instance_read(p->vrtp, 0); /* RTP Video */ + break; + case 3: +- f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */ ++ f = ast_rtp_instance_read(p->vrtp, 1); /* RTCP Control Channel for video */ + break; + case 4: +- f = ast_rtp_read(p->trtp); /* RTP Text */ ++ f = ast_rtp_instance_read(p->trtp, 0); /* RTP Text */ + if (sipdebug_text) { + int i; + unsigned char* arr = f->data.ptr; +@@ -6687,50 +6811,11 @@ + p->ocseq = INITIAL_CSEQ; + + if (sip_methods[intended_method].need_rtp) { +- p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); +- /* If the global videosupport flag is on, we always create a RTP interface for video */ +- if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT)) +- p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); +- if (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT)) +- p->trtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); +- if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) +- p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); +- if (!p->rtp|| (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp) +- || (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && !p->trtp)) { +- ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n", +- ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video " : "", +- ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "and text " : "", strerror(errno)); +- if (p->chanvars) { +- ast_variables_destroy(p->chanvars); +- p->chanvars = NULL; +- } +- ao2_t_ref(p, -1, "failed to create RTP audio session, drop p"); +- return NULL; ++ if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) { ++ ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio); + } +- ast_rtp_setqos(p->rtp, global_tos_audio, global_cos_audio, "SIP RTP"); +- ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); +- ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); +- ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout); +- ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout); +- ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive); +- if (p->vrtp) { +- ast_rtp_setqos(p->vrtp, global_tos_video, global_cos_video, "SIP VRTP"); +- ast_rtp_setdtmf(p->vrtp, 0); +- ast_rtp_setdtmfcompensate(p->vrtp, 0); +- ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout); +- ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout); +- ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive); +- } +- if (p->trtp) { +- ast_rtp_setqos(p->trtp, global_tos_text, global_cos_text, "SIP TRTP"); +- ast_rtp_setdtmf(p->trtp, 0); +- ast_rtp_setdtmfcompensate(p->trtp, 0); +- } +- if (p->udptl) +- ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio); + p->maxcallbitrate = default_maxcallbitrate; + p->autoframing = global_autoframing; +- ast_rtp_codec_setpref(p->rtp, &p->prefs); + } + + if (useglobal_nat && sin) { +@@ -6762,6 +6847,7 @@ + } + ast_string_field_set(p, context, sip_cfg.default_context); + ast_string_field_set(p, parkinglot, default_parkinglot); ++ ast_string_field_set(p, engine, default_engine); + + AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue); + +@@ -7396,7 +7482,7 @@ + int iterator; + int sendonly = -1; + int numberofports; +- struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp; /* Buffers for codec handling */ ++ struct ast_rtp_codecs newaudiortp, newvideortp, newtextrtp; + int newjointcapability; /* Negotiated capability */ + int newpeercapability; + int newnoncodeccapability; +@@ -7421,34 +7507,11 @@ + return -1; + } + +- /* Initialize the temporary RTP structures we use to evaluate the offer from the peer */ +-#ifdef LOW_MEMORY +- newaudiortp = ast_threadstorage_get(&ts_audio_rtp, ast_rtp_alloc_size()); +-#else +- newaudiortp = alloca(ast_rtp_alloc_size()); +-#endif +- memset(newaudiortp, 0, ast_rtp_alloc_size()); +- ast_rtp_new_init(newaudiortp); +- ast_rtp_pt_clear(newaudiortp); ++ /* Make sure that the codec structures are all cleared out */ ++ ast_rtp_codecs_payloads_clear(&newaudiortp, NULL); ++ ast_rtp_codecs_payloads_clear(&newvideortp, NULL); ++ ast_rtp_codecs_payloads_clear(&newtextrtp, NULL); + +-#ifdef LOW_MEMORY +- newvideortp = ast_threadstorage_get(&ts_video_rtp, ast_rtp_alloc_size()); +-#else +- newvideortp = alloca(ast_rtp_alloc_size()); +-#endif +- memset(newvideortp, 0, ast_rtp_alloc_size()); +- ast_rtp_new_init(newvideortp); +- ast_rtp_pt_clear(newvideortp); +- +-#ifdef LOW_MEMORY +- newtextrtp = ast_threadstorage_get(&ts_text_rtp, ast_rtp_alloc_size()); +-#else +- newtextrtp = alloca(ast_rtp_alloc_size()); +-#endif +- memset(newtextrtp, 0, ast_rtp_alloc_size()); +- ast_rtp_new_init(newtextrtp); +- ast_rtp_pt_clear(newtextrtp); +- + /* Update our last rtprx when we receive an SDP, too */ + p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ + +@@ -7529,12 +7592,14 @@ + p->novideo = TRUE; + p->notext = TRUE; + +- if (p->vrtp) +- ast_rtp_pt_clear(newvideortp); /* Must be cleared in case no m=video line exists */ +- +- if (p->trtp) +- ast_rtp_pt_clear(newtextrtp); /* Must be cleared in case no m=text line exists */ ++ if (p->vrtp) { ++ ast_rtp_codecs_payloads_clear(&newvideortp, NULL); ++ } + ++ if (p->trtp) { ++ ast_rtp_codecs_payloads_clear(&newtextrtp, NULL); ++ } ++ + /* Find media streams in this SDP offer */ + while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { + int x; +@@ -7558,7 +7623,8 @@ + } + if (debug) + ast_verbose("Found RTP audio format %d\n", codec); +- ast_rtp_set_m_type(newaudiortp, codec); ++ ++ ast_rtp_codecs_payloads_set_m_type(&newaudiortp, NULL, codec); + } + } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || + (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len >= 0)) { +@@ -7574,7 +7640,7 @@ + } + if (debug) + ast_verbose("Found RTP video format %d\n", codec); +- ast_rtp_set_m_type(newvideortp, codec); ++ ast_rtp_codecs_payloads_set_m_type(&newvideortp, NULL, codec); + } + } else if ((sscanf(m, "text %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || + (sscanf(m, "text %d RTP/AVP %n", &x, &len) == 1 && len > 0)) { +@@ -7590,7 +7656,7 @@ + } + if (debug) + ast_verbose("Found RTP text format %d\n", codec); +- ast_rtp_set_m_type(newtextrtp, codec); ++ ast_rtp_codecs_payloads_set_m_type(&newtextrtp, NULL, codec); + } + } else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1 && len > 0) || + (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1 && len > 0) )) { +@@ -7655,10 +7721,10 @@ + if (udptlportno > 0) { + sin.sin_port = htons(udptlportno); + if (ast_test_flag(&p->flags[0], SIP_NAT) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) { +- struct sockaddr_in peer; +- ast_rtp_get_peer(p->rtp, &peer); +- if (peer.sin_addr.s_addr) { +- memcpy(&sin.sin_addr, &peer.sin_addr, sizeof(sin.sin_addr)); ++ struct sockaddr_in remote_address; ++ ast_rtp_instance_get_remote_address(p->rtp, &remote_address); ++ if (remote_address.sin_addr.s_addr) { ++ memcpy(&sin, &remote_address, sizeof(sin)); + if (debug) { + ast_log(LOG_DEBUG, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_inet_ntoa(sin.sin_addr)); + } +@@ -7678,7 +7744,7 @@ + if (p->rtp) { + if (portno > 0) { + sin.sin_port = htons(portno); +- ast_rtp_set_peer(p->rtp, &sin); ++ ast_rtp_instance_set_remote_address(p->rtp, &sin); + if (debug) + ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } else { +@@ -7686,7 +7752,7 @@ + if (debug) + ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid); + } else { +- ast_rtp_stop(p->rtp); ++ ast_rtp_instance_stop(p->rtp); + if (debug) + ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid); + } +@@ -7769,18 +7835,17 @@ + } + } + if (framing && p->autoframing) { +- struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); ++ struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref; + int codec_n; +- int format = 0; +- for (codec_n = 0; codec_n < MAX_RTP_PT; codec_n++) { +- format = ast_rtp_codec_getformat(codec_n); +- if (!format) /* non-codec or not found */ ++ for (codec_n = 0; codec_n < AST_RTP_MAX_PT; codec_n++) { ++ struct ast_rtp_payload_type format = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(p->rtp), codec_n); ++ if (!format.asterisk_format || !format.code) /* non-codec or not found */ + continue; + if (option_debug) +- ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing); +- ast_codec_pref_setsize(pref, format, framing); ++ ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format.code, framing); ++ ast_codec_pref_setsize(pref, format.code, framing); + } +- ast_rtp_codec_setpref(p->rtp, pref); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, pref); + } + continue; + } +@@ -7792,7 +7857,7 @@ + + sscanf(red_cp, "%u", &red_data_pt[red_num_gen]); + red_cp = strtok(red_cp, "/"); +- while (red_cp && red_num_gen++ < RED_MAX_GENERATION) { ++ while (red_cp && red_num_gen++ < AST_RED_MAX_GENERATION) { + sscanf(red_cp, "%u", &red_data_pt[red_num_gen]); + red_cp = strtok(NULL, "/"); + } +@@ -7801,15 +7866,15 @@ + } + + if (sscanf(a, "fmtp: %u %63s", &codec, fmtp_string) == 2) { +- struct rtpPayloadType payload; ++ struct ast_rtp_payload_type payload; + unsigned int handled = 0; + +- payload = ast_rtp_lookup_pt(newaudiortp, codec); ++ payload = ast_rtp_codecs_payload_lookup(&newaudiortp, codec); + if (!payload.code) { + /* it wasn't found, try the video rtp */ +- payload = ast_rtp_lookup_pt(newvideortp, codec); ++ payload = ast_rtp_codecs_payload_lookup(&newvideortp, codec); + } +- if (payload.code && payload.isAstFormat) { ++ if (payload.code && payload.asterisk_format) { + unsigned int bit_rate; + + switch (payload.code) { +@@ -7817,7 +7882,7 @@ + if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { + if (bit_rate != 32000) { + ast_log(LOG_WARNING, "Got Siren7 offer at %d bps, but only 32000 bps supported; ignoring.\n", bit_rate); +- ast_rtp_unset_m_type(newaudiortp, codec); ++ ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); + } else { + handled = 1; + } +@@ -7827,7 +7892,7 @@ + if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { + if (bit_rate != 48000) { + ast_log(LOG_WARNING, "Got Siren14 offer at %d bps, but only 48000 bps supported; ignoring.\n", bit_rate); +- ast_rtp_unset_m_type(newaudiortp, codec); ++ ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); + } else { + handled = 1; + } +@@ -7849,24 +7914,24 @@ + /* Note: should really look at the '#chans' params too */ + /* Note: This should all be done in the context of the m= above */ + if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { /* Video */ +- if (ast_rtp_set_rtpmap_type_rate(newvideortp, codec, "video", mimeSubtype, 0, sample_rate) != -1) { ++ if (ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newvideortp, NULL, codec, "video", mimeSubtype, 0, sample_rate) != -1) { + if (debug) + ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec); + found_rtpmap_codecs[last_rtpmap_codec] = codec; + last_rtpmap_codec++; + } else { +- ast_rtp_unset_m_type(newvideortp, codec); ++ ast_rtp_codecs_payloads_unset(&newvideortp, NULL, codec); + if (debug) + ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); + } + } else if (!strncasecmp(mimeSubtype, "T140", 4)) { /* Text */ + if (p->trtp) { + /* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */ +- ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); ++ ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate); + } + } else if (!strncasecmp(mimeSubtype, "RED", 3)) { /* Text with Redudancy */ + if (p->trtp) { +- ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); ++ ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate); + red_pt = codec; + sprintf(red_fmtp, "fmtp:%d ", red_pt); + +@@ -7874,15 +7939,14 @@ + ast_verbose("RED submimetype has payload type: %d\n", red_pt); + } + } else { /* Must be audio?? */ +- if (ast_rtp_set_rtpmap_type_rate(newaudiortp, codec, "audio", mimeSubtype, +- ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, +- sample_rate) != -1) { ++ if (ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newaudiortp, NULL, codec, "audio", mimeSubtype, ++ ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, sample_rate) != -1) { + if (debug) + ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec); + found_rtpmap_codecs[last_rtpmap_codec] = codec; + last_rtpmap_codec++; + } else { +- ast_rtp_unset_m_type(newaudiortp, codec); ++ ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); + if (debug) + ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); + } +@@ -8007,7 +8071,7 @@ + + /* Remote party offers T38, we need to update state */ + if (t38action == SDP_T38_ACCEPT) { +- if (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE) ++ if (p->t38.state == T38_LOCAL_REINVITE) + change_t38_state(p, T38_ENABLED); + } else if (t38action == SDP_T38_INITIATE) { + if (p->owner && p->lastinvite) { +@@ -8021,15 +8085,14 @@ + } + + /* Now gather all of the codecs that we are asked for: */ +- ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); +- ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); +- ast_rtp_get_current_formats(newtextrtp, &tpeercapability, &tpeernoncodeccapability); ++ ast_rtp_codecs_payload_formats(&newaudiortp, &peercapability, &peernoncodeccapability); ++ ast_rtp_codecs_payload_formats(&newvideortp, &vpeercapability, &vpeernoncodeccapability); ++ ast_rtp_codecs_payload_formats(&newtextrtp, &tpeercapability, &tpeernoncodeccapability); + + newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability); + newpeercapability = (peercapability | vpeercapability | tpeercapability); + newnoncodeccapability = p->noncodeccapability & peernoncodeccapability; +- +- ++ + if (debug) { + /* shame on whoever coded this.... */ + char s1[SIPBUFSIZE], s2[SIPBUFSIZE], s3[SIPBUFSIZE], s4[SIPBUFSIZE], s5[SIPBUFSIZE]; +@@ -8040,11 +8103,17 @@ + ast_getformatname_multiple(s3, SIPBUFSIZE, vpeercapability), + ast_getformatname_multiple(s4, SIPBUFSIZE, tpeercapability), + ast_getformatname_multiple(s5, SIPBUFSIZE, newjointcapability)); ++ } + ++ if (debug) { ++ struct ast_str *s1 = ast_str_alloca(SIPBUFSIZE); ++ struct ast_str *s2 = ast_str_alloca(SIPBUFSIZE); ++ struct ast_str *s3 = ast_str_alloca(SIPBUFSIZE); ++ + ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n", +- ast_rtp_lookup_mime_multiple(s1, SIPBUFSIZE, p->noncodeccapability, 0, 0), +- ast_rtp_lookup_mime_multiple(s2, SIPBUFSIZE, peernoncodeccapability, 0, 0), +- ast_rtp_lookup_mime_multiple(s3, SIPBUFSIZE, newnoncodeccapability, 0, 0)); ++ ast_rtp_lookup_mime_multiple2(s1, p->noncodeccapability, 0, 0), ++ ast_rtp_lookup_mime_multiple2(s2, peernoncodeccapability, 0, 0), ++ ast_rtp_lookup_mime_multiple2(s3, newnoncodeccapability, 0, 0)); + } + if (!newjointcapability) { + /* If T.38 was not negotiated either, totally bail out... */ +@@ -8064,18 +8133,24 @@ + p->peercapability = newpeercapability; /* The other sides capability in latest offer */ + p->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */ + ++ if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) { /* respond with single most preferred joint codec, limiting the other side's choice */ ++ p->jointcapability = ast_codec_choose(&p->prefs, p->jointcapability, 1); ++ } ++ + if (p->jointcapability & AST_FORMAT_T140RED) { +- p->red = 1; +- rtp_red_init(p->trtp, 300, red_data_pt, 2); ++ p->red = 1; ++ ast_rtp_red_init(p->trtp, 300, red_data_pt, 2); + } else { +- p->red = 0; ++ p->red = 0; + } + +- ast_rtp_pt_copy(p->rtp, newaudiortp); +- if (p->vrtp) +- ast_rtp_pt_copy(p->vrtp, newvideortp); +- if (p->trtp) +- ast_rtp_pt_copy(p->trtp, newtextrtp); ++ ast_rtp_codecs_payloads_copy(&newaudiortp, ast_rtp_instance_get_codecs(p->rtp), p->rtp); ++ if (p->vrtp) { ++ ast_rtp_codecs_payloads_copy(&newvideortp, ast_rtp_instance_get_codecs(p->vrtp), p->vrtp); ++ } ++ if (p->trtp) { ++ ast_rtp_codecs_payloads_copy(&newtextrtp, ast_rtp_instance_get_codecs(p->trtp), p->trtp); ++ } + + if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) { + ast_clear_flag(&p->flags[0], SIP_DTMF); +@@ -8083,8 +8158,8 @@ + /* XXX Would it be reasonable to drop the DSP at this point? XXX */ + ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833); + /* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */ +- ast_rtp_setdtmf(p->rtp, 1); +- ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ++ ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, 1); ++ ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + } else { + ast_set_flag(&p->flags[0], SIP_DTMF_INBAND); + } +@@ -8092,21 +8167,21 @@ + + /* Setup audio port number */ + if (p->rtp && sin.sin_port) { +- ast_rtp_set_peer(p->rtp, &sin); ++ ast_rtp_instance_set_remote_address(p->rtp, &sin); + if (debug) + ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } + + /* Setup video port number */ + if (p->vrtp && vsin.sin_port) { +- ast_rtp_set_peer(p->vrtp, &vsin); ++ ast_rtp_instance_set_remote_address(p->vrtp, &vsin); + if (debug) + ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port)); + } + + /* Setup text port number */ + if (p->trtp && tsin.sin_port) { +- ast_rtp_set_peer(p->trtp, &tsin); ++ ast_rtp_instance_set_remote_address(p->trtp, &tsin); + if (debug) + ast_verbose("Peer text RTP is at port %s:%d\n", ast_inet_ntoa(tsin.sin_addr), ntohs(tsin.sin_port)); + } +@@ -8153,7 +8228,7 @@ + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + if (sendonly) +- ast_rtp_stop(p->rtp); ++ ast_rtp_instance_stop(p->rtp); + /* RTCP needs to go ahead, even if we're on hold!!! */ + /* Activate a re-invite */ + ast_queue_frame(p->owner, &ast_null_frame); +@@ -8700,9 +8775,6 @@ + if (!ast_strlen_zero(global_useragent)) + add_header(req, "User-Agent", global_useragent); + +- if (!ast_strlen_zero(p->rpid)) +- add_header(req, "Remote-Party-ID", p->rpid); +- + if (!ast_strlen_zero(p->url)) { + add_header(req, "Access-URL", p->url); + ast_string_field_set(p, url, NULL); +@@ -8740,6 +8812,14 @@ + return -1; + } + respprep(&resp, p, msg, req); ++ ++ if (ast_test_flag(&p->flags[0], SIP_SENDRPID) ++ && ast_test_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND) ++ && (!strncmp(msg, "180", 3) || !strncmp(msg, "183", 3))) { ++ ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND); ++ add_rpid(&resp, p); ++ } ++ + add_header_contentLength(&resp, 0); + /* If we are cancelling an incoming invite for some reason, add information + about the reason why we are doing this in clear text */ +@@ -8959,6 +9039,89 @@ + return 0; + } + ++/*! ++ * \pre if p->owner exists, it must be locked ++ * \brief Add Remote-Party-ID header to SIP message ++ */ ++static int add_rpid(struct sip_request *req, struct sip_pvt *p) ++{ ++ struct ast_str *tmp = ast_str_alloca(256); ++ char *lid_num = NULL; ++ char *lid_name = NULL; ++ int lid_pres; ++ const char *fromdomain; ++ const char *privacy = NULL; ++ const char *screen = NULL; ++ const char *anonymous_string = "\"Anonymous\" <anonymous@anonymous.invalid>"; ++ ++ if (!ast_test_flag(&p->flags[0], SIP_SENDRPID)) { ++ return 0; ++ } ++ ++ if (p->owner && p->owner->connected.id.number) ++ lid_num = p->owner->connected.id.number; ++ if (p->owner && p->owner->connected.id.name) ++ lid_name = p->owner->connected.id.name; ++ lid_pres = (p->owner) ? p->owner->connected.id.number_presentation : AST_PRES_NUMBER_NOT_AVAILABLE; ++ ++ if (ast_strlen_zero(lid_num)) ++ return 0; ++ if (ast_strlen_zero(lid_name)) ++ lid_name = lid_num; ++ fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)); ++ ++ if (ast_test_flag(&p->flags[0], SIP_SENDRPID_PAI)) { ++ if ((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) { ++ ast_str_set(&tmp, -1, "%s", anonymous_string); ++ } else { ++ ast_str_set(&tmp, -1, "\"%s\" <sip:%s@%s>", lid_name, lid_num, fromdomain); ++ } ++ add_header(req, "P-Asserted-Identity", ast_str_buffer(tmp)); ++ } else { ++ ast_str_set(&tmp, -1, "\"%s\" <sip:%s@%s>;party=%s", lid_name, lid_num, fromdomain, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "calling" : "called"); ++ ++ switch (lid_pres) { ++ case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: ++ case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: ++ privacy = "off"; ++ screen = "no"; ++ break; ++ case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: ++ case AST_PRES_ALLOWED_NETWORK_NUMBER: ++ privacy = "off"; ++ screen = "yes"; ++ break; ++ case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED: ++ case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: ++ privacy = "full"; ++ screen = "no"; ++ break; ++ case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: ++ case AST_PRES_PROHIB_NETWORK_NUMBER: ++ privacy = "full"; ++ screen = "yes"; ++ break; ++ case AST_PRES_NUMBER_NOT_AVAILABLE: ++ break; ++ default: ++ if ((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) { ++ privacy = "full"; ++ } ++ else ++ privacy = "off"; ++ screen = "no"; ++ break; ++ } ++ ++ if (!ast_strlen_zero(privacy) && !ast_strlen_zero(screen)) { ++ ast_str_append(&tmp, -1, ";privacy=%s;screen=%s", privacy, screen); ++ } ++ ++ add_header(req, "Remote-Party-ID", ast_str_buffer(tmp)); ++ } ++ return 0; ++} ++ + /*! \brief add XML encoded media control with update + \note XML: The only way to turn 0 bits of information into a few hundred. (markster) */ + static int add_vidupdate(struct sip_request *req) +@@ -8990,19 +9153,19 @@ + + if (debug) + ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); +- if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1) ++ if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 1, codec)) == -1) + return; + + if (p->rtp) { +- struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); ++ struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref; + fmt = ast_codec_pref_getsize(pref, codec); + } else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */ + return; + ast_str_append(m_buf, 0, " %d", rtp_code); + ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, +- ast_rtp_lookup_mime_subtype(1, codec, ++ ast_rtp_lookup_mime_subtype2(1, codec, + ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), +- ast_rtp_lookup_sample_rate(1, codec)); ++ ast_rtp_lookup_sample_rate2(1, codec)); + + switch (codec) { + case AST_FORMAT_G729A: +@@ -9049,13 +9212,13 @@ + if (debug) + ast_verbose("Adding video codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); + +- if ((rtp_code = ast_rtp_lookup_code(p->vrtp, 1, codec)) == -1) ++ if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->vrtp), 1, codec)) == -1) + return; + + ast_str_append(m_buf, 0, " %d", rtp_code); + ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, +- ast_rtp_lookup_mime_subtype(1, codec, 0), +- ast_rtp_lookup_sample_rate(1, codec)); ++ ast_rtp_lookup_mime_subtype2(1, codec, 0), ++ ast_rtp_lookup_sample_rate2(1, codec)); + /* Add fmtp code here */ + } + +@@ -9072,20 +9235,21 @@ + if (debug) + ast_verbose("Adding text codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); + +- if ((rtp_code = ast_rtp_lookup_code(p->trtp, 1, codec)) == -1) ++ if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, codec)) == -1) + return; + + ast_str_append(m_buf, 0, " %d", rtp_code); + ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, +- ast_rtp_lookup_mime_subtype(1, codec, 0), +- ast_rtp_lookup_sample_rate(1, codec)); ++ ast_rtp_lookup_mime_subtype2(1, codec, 0), ++ ast_rtp_lookup_sample_rate2(1, codec)); + /* Add fmtp code here */ + + if (codec == AST_FORMAT_T140RED) { +- ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, +- ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), +- ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), +- ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140)); ++ int t140code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, AST_FORMAT_T140); ++ ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, ++ t140code, ++ t140code, ++ t140code); + + } + } +@@ -9120,92 +9284,6 @@ + } + } + +-/*! \brief Add T.38 Session Description Protocol message */ +-static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p) +-{ +- int len = 0; +- int x = 0; +- struct sockaddr_in udptlsin; +- struct ast_str *m_modem = ast_str_alloca(1024); +- struct ast_str *a_modem = ast_str_alloca(1024); +- struct sockaddr_in udptldest = { 0, }; +- int debug; +- +- debug = sip_debug_test_pvt(p); +- len = 0; +- if (!p->udptl) { +- ast_log(LOG_WARNING, "No way to add SDP without an UDPTL structure\n"); +- return -1; +- } +- +- if (!p->sessionid) { +- p->sessionid = (int)ast_random(); +- p->sessionversion = p->sessionid; +- } else +- p->sessionversion++; +- +- /* Our T.38 end is */ +- ast_udptl_get_us(p->udptl, &udptlsin); +- +- /* Determine T.38 UDPTL destination */ +- if (p->udptlredirip.sin_addr.s_addr) { +- udptldest.sin_port = p->udptlredirip.sin_port; +- udptldest.sin_addr = p->udptlredirip.sin_addr; +- } else { +- udptldest.sin_addr = p->ourip.sin_addr; +- udptldest.sin_port = udptlsin.sin_port; +- } +- +- if (debug) +- ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(udptlsin.sin_port)); +- +- /* We break with the "recommendation" and send our IP, in order that our +- peer doesn't have to ast_gethostbyname() us */ +- +- if (debug) { +- ast_debug(1, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n", +- p->t38.capability, +- p->t38.peercapability, +- p->t38.jointcapability); +- } +- ast_str_append(&m_modem, 0, "v=0\r\n"); +- ast_str_append(&m_modem, 0, "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner , p->sessionid, p->sessionversion, ast_inet_ntoa(udptldest.sin_addr)); +- ast_str_append(&m_modem, 0, "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession); +- ast_str_append(&m_modem, 0, "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr)); +- ast_str_append(&m_modem, 0, "t=0 0\r\n"); +- ast_str_append(&m_modem, 0, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port)); +- +- if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0) +- ast_str_append(&a_modem, 0, "a=T38FaxVersion:0\r\n"); +- if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1) +- ast_str_append(&a_modem, 0, "a=T38FaxVersion:1\r\n"); +- if ((x = t38_get_rate(p->t38.jointcapability))) +- ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n", x); +- if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL) +- ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval\r\n"); +- if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR) +- ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR\r\n"); +- if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG) +- ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG\r\n"); +- ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF"); +- x = ast_udptl_get_local_max_datagram(p->udptl); +- ast_str_append(&a_modem, 0, "a=T38FaxMaxBuffer:%d\r\n", x); +- ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n", x); +- if (p->t38.jointcapability != T38FAX_UDP_EC_NONE) +- ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC"); +- len = m_modem->used + a_modem->used; +- add_header(resp, "Content-Type", "application/sdp"); +- add_header_contentLength(resp, len); +- add_line(resp, m_modem->str); +- add_line(resp, a_modem->str); +- +- /* Update lastrtprx when we send our SDP */ +- p->lastrtprx = p->lastrtptx = time(NULL); +- +- return 0; +-} +- +- + /*! \brief Add RFC 2833 DTMF offer to SDP */ + static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, + struct ast_str **m_buf, struct ast_str **a_buf, +@@ -9214,14 +9292,14 @@ + int rtp_code; + + if (debug) +- ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0)); +- if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1) ++ ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype2(0, format, 0)); ++ if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 0, format)) == -1) + return; + + ast_str_append(m_buf, 0, " %d", rtp_code); + ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, +- ast_rtp_lookup_mime_subtype(0, format, 0), +- ast_rtp_lookup_sample_rate(0, format)); ++ ast_rtp_lookup_mime_subtype2(0, format, 0), ++ ast_rtp_lookup_sample_rate2(0, format)); + if (format == AST_RTP_DTMF) /* Indicate we support DTMF and FLASH... */ + ast_str_append(a_buf, 0, "a=fmtp:%d 0-16\r\n", rtp_code); + } +@@ -9234,11 +9312,11 @@ + struct sockaddr_in *dest, struct sockaddr_in *vdest) + { + /* First, get our address */ +- ast_rtp_get_us(p->rtp, sin); ++ ast_rtp_instance_get_local_address(p->rtp, sin); + if (p->vrtp) +- ast_rtp_get_us(p->vrtp, vsin); ++ ast_rtp_instance_get_local_address(p->vrtp, vsin); + if (p->trtp) +- ast_rtp_get_us(p->trtp, tsin); ++ ast_rtp_instance_get_local_address(p->trtp, tsin); + + /* Now, try to figure out where we want them to send data */ + /* Is this a re-invite to move the media out, then use the original offer from caller */ +@@ -9268,7 +9346,7 @@ + is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions + without modifying the media session in any way. + */ +-static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp) ++static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38) + { + int len = 0; + int alreadysent = 0; +@@ -9277,8 +9355,10 @@ + struct sockaddr_in vsin; + struct sockaddr_in tsin; + struct sockaddr_in dest; ++ struct sockaddr_in udptlsin; + struct sockaddr_in vdest = { 0, }; + struct sockaddr_in tdest = { 0, }; ++ struct sockaddr_in udptldest = { 0, }; + + /* SDP fields */ + char *version = "v=0\r\n"; /* Protocol version */ +@@ -9287,16 +9367,18 @@ + char connection[256]; /* Connection data */ + char *session_time = "t=0 0\r\n"; /* Time the session is active */ + char bandwidth[256] = ""; /* Max bitrate */ +- char *hold; ++ char *hold = ""; + struct ast_str *m_audio = ast_str_alloca(256); /* Media declaration line for audio */ + struct ast_str *m_video = ast_str_alloca(256); /* Media declaration line for video */ + struct ast_str *m_text = ast_str_alloca(256); /* Media declaration line for text */ ++ struct ast_str *m_modem = ast_str_alloca(256); /* Media declaration line for modem */ + struct ast_str *a_audio = ast_str_alloca(1024); /* Attributes for audio */ + struct ast_str *a_video = ast_str_alloca(1024); /* Attributes for video */ + struct ast_str *a_text = ast_str_alloca(1024); /* Attributes for text */ ++ struct ast_str *a_modem = ast_str_alloca(1024); /* Attributes for modem */ + + int x; +- int capability; ++ int capability = 0; + int needaudio = FALSE; + int needvideo = FALSE; + int needtext = FALSE; +@@ -9327,184 +9409,235 @@ + p->sessionversion++; + } + +- capability = p->jointcapability; ++ get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest); + +- /* XXX note, Video and Text are negated - 'true' means 'no' */ +- ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), +- p->novideo ? "True" : "False", p->notext ? "True" : "False"); +- ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec)); ++ snprintf(owner, sizeof(owner), "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner, p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr)); ++ snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr)); ++ ++ if (add_audio) { ++ capability = p->jointcapability; ++ ++ /* XXX note, Video and Text are negated - 'true' means 'no' */ ++ ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), ++ p->novideo ? "True" : "False", p->notext ? "True" : "False"); ++ ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec)); + + #ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS +- if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) { +- ast_str_append(&m_audio, 0, " %d", 191); +- ast_str_append(&a_audio, 0, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000); +- } ++ if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) { ++ ast_str_append(&m_audio, 0, " %d", 191); ++ ast_str_append(&a_audio, 0, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000); ++ } + #endif + +- /* Check if we need audio */ +- if (capability & AST_FORMAT_AUDIO_MASK) +- needaudio = TRUE; ++ /* Check if we need audio */ ++ if (capability & AST_FORMAT_AUDIO_MASK) ++ needaudio = TRUE; + +- /* Check if we need video in this call */ +- if ((capability & AST_FORMAT_VIDEO_MASK) && !p->novideo) { +- if (p->vrtp) { +- needvideo = TRUE; +- ast_debug(2, "This call needs video offers!\n"); +- } else +- ast_debug(2, "This call needs video offers, but there's no video support enabled!\n"); +- } ++ /* Check if we need video in this call */ ++ if ((capability & AST_FORMAT_VIDEO_MASK) && !p->novideo) { ++ if (p->vrtp) { ++ needvideo = TRUE; ++ ast_debug(2, "This call needs video offers!\n"); ++ } else ++ ast_debug(2, "This call needs video offers, but there's no video support enabled!\n"); ++ } + +- /* Get our media addresses */ +- get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest); +- +- if (debug) +- ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port)); ++ if (debug) ++ ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port)); + +- /* Ok, we need video. Let's add what we need for video and set codecs. +- Video is handled differently than audio since we can not transcode. */ +- if (needvideo) { +- ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port)); ++ /* Ok, we need video. Let's add what we need for video and set codecs. ++ Video is handled differently than audio since we can not transcode. */ ++ if (needvideo) { ++ ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port)); + +- /* Build max bitrate string */ +- if (p->maxcallbitrate) +- snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate); +- if (debug) +- ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(vsin.sin_port)); +- } ++ /* Build max bitrate string */ ++ if (p->maxcallbitrate) ++ snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate); ++ if (debug) ++ ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(vsin.sin_port)); ++ } + +- /* Check if we need text in this call */ +- if((capability & AST_FORMAT_TEXT_MASK) && !p->notext) { +- if (sipdebug_text) +- ast_verbose("We think we can do text\n"); +- if (p->trtp) { ++ /* Check if we need text in this call */ ++ if((capability & AST_FORMAT_TEXT_MASK) && !p->notext) { + if (sipdebug_text) +- ast_verbose("And we have a text rtp object\n"); +- needtext = TRUE; +- ast_debug(2, "This call needs text offers! \n"); +- } else +- ast_debug(2, "This call needs text offers, but there's no text support enabled ! \n"); +- } ++ ast_verbose("We think we can do text\n"); ++ if (p->trtp) { ++ if (sipdebug_text) ++ ast_verbose("And we have a text rtp object\n"); ++ needtext = TRUE; ++ ast_debug(2, "This call needs text offers! \n"); ++ } else ++ ast_debug(2, "This call needs text offers, but there's no text support enabled ! \n"); ++ } + +- /* Ok, we need text. Let's add what we need for text and set codecs. +- Text is handled differently than audio since we can not transcode. */ +- if (needtext) { +- if (sipdebug_text) +- ast_verbose("Lets set up the text sdp\n"); +- /* Determine text destination */ +- if (p->tredirip.sin_addr.s_addr) { +- tdest.sin_addr = p->tredirip.sin_addr; +- tdest.sin_port = p->tredirip.sin_port; +- } else { +- tdest.sin_addr = p->ourip.sin_addr; +- tdest.sin_port = tsin.sin_port; ++ /* Ok, we need text. Let's add what we need for text and set codecs. ++ Text is handled differently than audio since we can not transcode. */ ++ if (needtext) { ++ if (sipdebug_text) ++ ast_verbose("Lets set up the text sdp\n"); ++ /* Determine text destination */ ++ if (p->tredirip.sin_addr.s_addr) { ++ tdest.sin_addr = p->tredirip.sin_addr; ++ tdest.sin_port = p->tredirip.sin_port; ++ } else { ++ tdest.sin_addr = p->ourip.sin_addr; ++ tdest.sin_port = tsin.sin_port; ++ } ++ ast_str_append(&m_text, 0, "m=text %d RTP/AVP", ntohs(tdest.sin_port)); ++ if (debug) /* XXX should I use tdest below ? */ ++ ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port)); ++ + } +- ast_str_append(&m_text, 0, "m=text %d RTP/AVP", ntohs(tdest.sin_port)); + +- if (debug) /* XXX should I use tdest below ? */ +- ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port)); ++ /* Start building generic SDP headers */ + +- } ++ /* We break with the "recommendation" and send our IP, in order that our ++ peer doesn't have to ast_gethostbyname() us */ + +- /* Start building generic SDP headers */ ++ ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port)); + +- /* We break with the "recommendation" and send our IP, in order that our +- peer doesn't have to ast_gethostbyname() us */ ++ if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) ++ hold = "a=recvonly\r\n"; ++ else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE) ++ hold = "a=inactive\r\n"; ++ else ++ hold = "a=sendrecv\r\n"; + +- snprintf(owner, sizeof(owner), "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner, p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr)); +- snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr)); +- ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port)); ++ /* Now, start adding audio codecs. These are added in this order: ++ - First what was requested by the calling channel ++ - Then preferences in order from sip.conf device config for this peer/user ++ - Then other codecs in capabilities, including video ++ */ + +- if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) +- hold = "a=recvonly\r\n"; +- else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE) +- hold = "a=inactive\r\n"; +- else +- hold = "a=sendrecv\r\n"; ++ /* Prefer the audio codec we were requested to use, first, no matter what ++ Note that p->prefcodec can include video codecs, so mask them out ++ */ ++ if (capability & p->prefcodec) { ++ int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK; + +- /* Now, start adding audio codecs. These are added in this order: +- - First what was requested by the calling channel +- - Then preferences in order from sip.conf device config for this peer/user +- - Then other codecs in capabilities, including video +- */ ++ add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size); ++ alreadysent |= codec; ++ } + +- /* Prefer the audio codec we were requested to use, first, no matter what +- Note that p->prefcodec can include video codecs, so mask them out +- */ +- if (capability & p->prefcodec) { +- int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK; ++ /* Start by sending our preferred audio/video codecs */ ++ for (x = 0; x < 32; x++) { ++ int codec; + +- add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size); +- alreadysent |= codec; +- } ++ if (!(codec = ast_codec_pref_index(&p->prefs, x))) ++ break; + +- /* Start by sending our preferred audio/video codecs */ +- for (x = 0; x < 32; x++) { +- int codec; ++ if (!(capability & codec)) ++ continue; + +- if (!(codec = ast_codec_pref_index(&p->prefs, x))) +- break; ++ if (alreadysent & codec) ++ continue; + +- if (!(capability & codec)) +- continue; ++ add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size); ++ alreadysent |= codec; ++ } + +- if (alreadysent & codec) +- continue; ++ /* Now send any other common audio and video codecs, and non-codec formats: */ ++ for (x = 1; x <= (needtext ? AST_FORMAT_TEXT_MASK : (needvideo ? AST_FORMAT_VIDEO_MASK : AST_FORMAT_AUDIO_MASK)); x <<= 1) { ++ if (!(capability & x)) /* Codec not requested */ ++ continue; + +- add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size); +- alreadysent |= codec; +- } ++ if (alreadysent & x) /* Already added to SDP */ ++ continue; + +- /* Now send any other common audio and video codecs, and non-codec formats: */ +- for (x = 1; x <= (needtext ? AST_FORMAT_TEXT_MASK : (needvideo ? AST_FORMAT_VIDEO_MASK : AST_FORMAT_AUDIO_MASK)); x <<= 1) { +- if (!(capability & x)) /* Codec not requested */ +- continue; ++ if (x & AST_FORMAT_AUDIO_MASK) ++ add_codec_to_sdp(p, x, &m_audio, &a_audio, debug, &min_audio_packet_size); ++ else if (x & AST_FORMAT_VIDEO_MASK) ++ add_vcodec_to_sdp(p, x, &m_video, &a_video, debug, &min_video_packet_size); ++ else if (x & AST_FORMAT_TEXT_MASK) ++ add_tcodec_to_sdp(p, x, &m_text, &a_text, debug, &min_text_packet_size); ++ } + +- if (alreadysent & x) /* Already added to SDP */ +- continue; ++ /* Now add DTMF RFC2833 telephony-event as a codec */ ++ for (x = 1; x <= AST_RTP_MAX; x <<= 1) { ++ if (!(p->jointnoncodeccapability & x)) ++ continue; + +- if (x & AST_FORMAT_AUDIO_MASK) +- add_codec_to_sdp(p, x, &m_audio, &a_audio, debug, &min_audio_packet_size); +- else if (x & AST_FORMAT_VIDEO_MASK) +- add_vcodec_to_sdp(p, x, &m_video, &a_video, debug, &min_video_packet_size); +- else if (x & AST_FORMAT_TEXT_MASK) +- add_tcodec_to_sdp(p, x, &m_text, &a_text, debug, &min_text_packet_size); +- } ++ add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug); ++ } + +- /* Now add DTMF RFC2833 telephony-event as a codec */ +- for (x = 1; x <= AST_RTP_MAX; x <<= 1) { +- if (!(p->jointnoncodeccapability & x)) +- continue; ++ ast_debug(3, "-- Done with adding codecs to SDP\n"); + +- add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug); ++ if (!p->owner || !ast_internal_timing_enabled(p->owner)) ++ ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n"); ++ ++ if (min_audio_packet_size) ++ ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size); ++ ++ /* XXX don't think you can have ptime for video */ ++ if (min_video_packet_size) ++ ast_str_append(&a_video, 0, "a=ptime:%d\r\n", min_video_packet_size); ++ ++ /* XXX don't think you can have ptime for text */ ++ if (min_text_packet_size) ++ ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size); ++ ++ if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 || ++ m_text->len - m_text->used < 2 || a_text->len - a_text->used < 2 || ++ a_audio->len - a_audio->used < 2 || a_video->len - a_video->used < 2) ++ ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n"); + } + +- ast_debug(3, "-- Done with adding codecs to SDP\n"); ++ if (add_t38) { ++ /* Our T.38 end is */ ++ ast_udptl_get_us(p->udptl, &udptlsin); + +- if (!p->owner || !ast_internal_timing_enabled(p->owner)) +- ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n"); ++ /* Determine T.38 UDPTL destination */ ++ if (p->udptlredirip.sin_addr.s_addr) { ++ udptldest.sin_port = p->udptlredirip.sin_port; ++ udptldest.sin_addr = p->udptlredirip.sin_addr; ++ } else { ++ udptldest.sin_addr = p->ourip.sin_addr; ++ udptldest.sin_port = udptlsin.sin_port; ++ } + +- if (min_audio_packet_size) +- ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size); ++ if (debug) ++ ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(udptlsin.sin_port)); + +- /* XXX don't think you can have ptime for video */ +- if (min_video_packet_size) +- ast_str_append(&a_video, 0, "a=ptime:%d\r\n", min_video_packet_size); ++ /* We break with the "recommendation" and send our IP, in order that our ++ peer doesn't have to ast_gethostbyname() us */ + +- /* XXX don't think you can have ptime for text */ +- if (min_text_packet_size) +- ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size); +- +- if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 || +- m_text->len - m_text->used < 2 || a_text->len - a_text->used < 2 || +- a_audio->len - a_audio->used < 2 || a_video->len - a_video->used < 2) +- ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n"); ++ if (debug) { ++ ast_debug(1, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n", ++ p->t38.capability, ++ p->t38.peercapability, ++ p->t38.jointcapability); ++ } + ++ ast_str_append(&m_modem, 0, "m=image %d udptl t38", ntohs(udptldest.sin_port)); ++ ++ if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0) ++ ast_str_append(&a_modem, 0, "a=T38FaxVersion:0\r\n"); ++ if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1) ++ ast_str_append(&a_modem, 0, "a=T38FaxVersion:1\r\n"); ++ if ((x = t38_get_rate(p->t38.jointcapability))) ++ ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n", x); ++ if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL) ++ ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval\r\n"); ++ if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR) ++ ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR\r\n"); ++ if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG) ++ ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG\r\n"); ++ ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF"); ++ x = ast_udptl_get_local_max_datagram(p->udptl); ++ ast_str_append(&a_modem, 0, "a=T38FaxMaxBuffer:%d\r\n", x); ++ ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n", x); ++ if (p->t38.jointcapability != T38FAX_UDP_EC_NONE) ++ ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC"); ++ } ++ + if (needaudio) + ast_str_append(&m_audio, 0, "\r\n"); + if (needvideo) + ast_str_append(&m_video, 0, "\r\n"); + if (needtext) + ast_str_append(&m_text, 0, "\r\n"); ++ if (add_t38) ++ ast_str_append(&m_modem, 0, "\r\n"); + + len = strlen(version) + strlen(subject) + strlen(owner) + + strlen(connection) + strlen(session_time); +@@ -9514,6 +9647,8 @@ + len += m_video->used + a_video->used + strlen(bandwidth) + strlen(hold); + if (needtext) /* only if text response is appropriate */ + len += m_text->used + a_text->used + strlen(hold); ++ if (add_t38) ++ len += m_modem->used + a_modem->used; + + add_header(resp, "Content-Type", "application/sdp"); + add_header_contentLength(resp, len); +@@ -9539,6 +9674,10 @@ + add_line(resp, a_text->str); + add_line(resp, hold); /* Repeat hold for the text stream */ + } ++ if (add_t38) { ++ add_line(resp, m_modem->str); ++ add_line(resp, a_modem->str); ++ } + + /* Update lastrtprx when we send our SDP */ + p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ +@@ -9561,7 +9700,7 @@ + respprep(&resp, p, msg, req); + if (p->udptl) { + ast_udptl_offered_from_local(p->udptl, 0); +- add_t38_sdp(&resp, p); ++ add_sdp(&resp, p, 0, 0, 1); + } else + ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid); + if (retrans && !p->pendinginvite) +@@ -9596,7 +9735,7 @@ + /*! \brief Used for 200 OK and 183 early media + \return Will return XMIT_ERROR for network errors. + */ +-static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp) ++static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid) + { + struct sip_request resp; + int seqno; +@@ -9605,13 +9744,20 @@ + return -1; + } + respprep(&resp, p, msg, req); ++ if (rpid == TRUE) { ++ add_rpid(&resp, p); ++ } + if (p->rtp) { + if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + ast_debug(1, "Setting framing from config on incoming call\n"); +- ast_rtp_codec_setpref(p->rtp, &p->prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs); + } +- try_suggested_sip_codec(p); +- add_sdp(&resp, p, oldsdp); ++ try_suggested_sip_codec(p); ++ if (p->t38.state == T38_PEER_DIRECT || p->t38.state == T38_ENABLED) { ++ add_sdp(&resp, p, oldsdp, TRUE, TRUE); ++ } else { ++ add_sdp(&resp, p, oldsdp, TRUE, FALSE); ++ } + } else + ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid); + if (reliable && !p->pendinginvite) +@@ -9693,9 +9839,9 @@ + if (p->do_history) + append_history(p, "ReInv", "Re-invite sent"); + if (t38version) +- add_t38_sdp(&req, p); ++ add_sdp(&req, p, oldsdp, FALSE, TRUE); + else +- add_sdp(&req, p, oldsdp); ++ add_sdp(&req, p, oldsdp, TRUE, FALSE); + + /* Use this as the basis */ + initialize_initreq(p, &req); +@@ -9750,85 +9896,6 @@ + } + } + +-/*! \brief Build the Remote Party-ID & From using callingpres options */ +-static void build_rpid(struct sip_pvt *p) +-{ +- int send_pres_tags = TRUE; +- const char *privacy=NULL; +- const char *screen=NULL; +- char buf[256]; +- const char *clid = default_callerid; +- const char *clin = NULL; +- const char *fromdomain; +- +- if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from)) +- return; +- +- if (p->owner && p->owner->cid.cid_num) +- clid = p->owner->cid.cid_num; +- if (p->owner && p->owner->cid.cid_name) +- clin = p->owner->cid.cid_name; +- if (ast_strlen_zero(clin)) +- clin = clid; +- +- switch (p->callingpres) { +- case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: +- privacy = "off"; +- screen = "no"; +- break; +- case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: +- privacy = "off"; +- screen = "yes"; +- break; +- case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: +- privacy = "off"; +- screen = "no"; +- break; +- case AST_PRES_ALLOWED_NETWORK_NUMBER: +- privacy = "off"; +- screen = "yes"; +- break; +- case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED: +- privacy = "full"; +- screen = "no"; +- break; +- case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: +- privacy = "full"; +- screen = "yes"; +- break; +- case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: +- privacy = "full"; +- screen = "no"; +- break; +- case AST_PRES_PROHIB_NETWORK_NUMBER: +- privacy = "full"; +- screen = "yes"; +- break; +- case AST_PRES_NUMBER_NOT_AVAILABLE: +- send_pres_tags = FALSE; +- break; +- default: +- ast_log(LOG_WARNING, "Unsupported callingpres (%d)\n", p->callingpres); +- if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) +- privacy = "full"; +- else +- privacy = "off"; +- screen = "no"; +- break; +- } +- +- fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)); +- +- snprintf(buf, sizeof(buf), "\"%s\" <sip:%s@%s>", clin, clid, fromdomain); +- if (send_pres_tags) +- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ";privacy=%s;screen=%s", privacy, screen); +- ast_string_field_set(p, rpid, buf); +- +- ast_string_field_build(p, rpid_from, "\"%s\" <sip:%s@%s>;tag=%s", clin, +- S_OR(p->fromuser, clid), +- fromdomain, p->tag); +-} +- + /*! \brief Initiate new SIP request to peer/user */ + static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod) + { +@@ -9864,16 +9931,10 @@ + + snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text); + +- if (p->owner) { +- l = p->owner->cid.cid_num; +- n = p->owner->cid.cid_name; ++ if (p->owner && (p->owner->connected.id.number_presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { ++ l = p->owner->connected.id.number; ++ n = p->owner->connected.id.name; + } +- /* if we are not sending RPID and user wants his callerid restricted */ +- if (!ast_test_flag(&p->flags[0], SIP_SENDRPID) && +- ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) { +- l = CALLERID_UNKNOWN; +- n = l; +- } + if (ast_strlen_zero(l)) + l = default_callerid; + if (ast_strlen_zero(n)) +@@ -9961,12 +10022,7 @@ + /* SLD: FIXME?: do Route: here too? I think not cos this is the first request. + * OTOH, then we won't have anything in p->route anyway */ + +- /* Build Remote Party-ID and From */ +- if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) { +- build_rpid(p); +- add_header(req, "From", p->rpid_from); +- } else +- add_header(req, "From", from); ++ add_header(req, "From", from); + add_header(req, "To", to); + ast_string_field_set(p, exten, l); + build_contact(p); +@@ -9975,10 +10031,46 @@ + add_header(req, "CSeq", tmp_n); + if (!ast_strlen_zero(global_useragent)) + add_header(req, "User-Agent", global_useragent); +- if (!ast_strlen_zero(p->rpid)) +- add_header(req, "Remote-Party-ID", p->rpid); + } + ++/*! \brief Add "Diversion" header to outgoing message ++ * ++ * We need to add a Diversion header if the owner channel of ++ * this dialog has redirecting information associated with it. ++ * ++ * \param req The request/response to which we will add the header ++ * \param pvt The sip_pvt which represents the call-leg ++ * \param apr Redirecting data used to make the diversion header ++ */ ++static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt) ++{ ++ const char *diverting_number; ++ const char *diverting_name; ++ const char *reason; ++ char header_text[256]; ++ ++ if (!pvt->owner) { ++ return; ++ } ++ ++ diverting_number = pvt->owner->cid.cid_rdnis; ++ diverting_name = pvt->owner->redirecting.from.name; ++ reason = sip_reason_code_to_str(pvt->owner->redirecting.reason); ++ ++ if (ast_strlen_zero(diverting_number)) { ++ return; ++ } ++ ++ /* We at least have a number to place in the Diversion header, which is enough */ ++ if (ast_strlen_zero(diverting_name)) { ++ snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s", diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason); ++ } else { ++ snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s", diverting_name, diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason); ++ } ++ ++ add_header(req, "Diversion", header_text); ++} ++ + /*! \brief Build REFER/INVITE/OPTIONS/SUBSCRIBE message and transmit it + \param init 0 = Prepare request within dialog, 1= prepare request, new branch, 2= prepare new request and new dialog. do_proxy_auth calls this with init!=2 + \param p sip_pvt structure +@@ -10097,13 +10189,18 @@ + + ast_channel_unlock(chan); + } ++ if ((sipmethod == SIP_INVITE || sipmethod == SIP_UPDATE) && ast_test_flag(&p->flags[0], SIP_SENDRPID)) ++ add_rpid(&req, p); ++ if (sipmethod == SIP_INVITE) { ++ add_diversion_header(&req, p); ++ } + if (sdp) { +- if (p->udptl && (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE)) { ++ if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) { + ast_udptl_offered_from_local(p->udptl, 1); + ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); +- add_t38_sdp(&req, p); ++ add_sdp(&req, p, FALSE, FALSE, TRUE); + } else if (p->rtp) +- add_sdp(&req, p, FALSE); ++ add_sdp(&req, p, FALSE, TRUE, FALSE); + } else { + if (!p->notify_headers) { + add_header_contentLength(&req, 0); +@@ -10579,6 +10676,80 @@ + " *Variable: <name>=<value> At least one variable pair must be specified.\n" + " ActionID: <id> Action ID for this transaction. Will be returned.\n"; + ++/*! \brief Send a provisional response indicating that a call was redirected ++ */ ++static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen) ++{ ++ struct sip_request resp; ++ ++ if (p->owner->_state == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) { ++ return; ++ } ++ ++ if (!ast_strlen_zero(p->owner->redirecting.to.number)) { ++ ast_string_field_set(p, exten, p->owner->redirecting.to.number); ++ build_contact(p); ++ } ++ respprep(&resp, p, "181 Call is being forwarded", &p->initreq); ++ add_diversion_header(&resp, p); ++ send_response(p, &resp, XMIT_UNRELIABLE, 0); ++} ++ ++/*! \brief Notify peer that the connected line has changed */ ++static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen) ++{ ++ ++ if (!ast_test_flag(&p->flags[0], SIP_SENDRPID)) ++ return; ++ if (ast_strlen_zero(p->owner->connected.id.number)) ++ return; ++ ++ append_history(p, "ConnectedLine", "%s party is now %s <%s>", ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "Calling" : "Called", p->owner->connected.id.name, p->owner->connected.id.number); ++ ++ if (p->owner->_state == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) { ++ struct sip_request req; ++ ++ if (p->invitestate == INV_CONFIRMED || p->invitestate == INV_TERMINATED) { ++ reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); ++ ++ add_header(&req, "Allow", ALLOWED_METHODS); ++ add_header(&req, "Supported", SUPPORTED_EXTENSIONS); ++ add_rpid(&req, p); ++ add_sdp(&req, p, FALSE, TRUE, FALSE); ++ ++ initialize_initreq(p, &req); ++ p->lastinvite = p->ocseq; ++ ast_set_flag(&p->flags[0], SIP_OUTGOING); ++ send_request(p, &req, XMIT_CRITICAL, p->ocseq); ++ } else { ++ reqprep(&req, p, SIP_UPDATE, 0, 1); ++ add_rpid(&req, p); ++ add_header_contentLength(&req, 0); ++ send_request(p, &req, XMIT_CRITICAL, p->ocseq); ++ } ++ } else { ++ if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPID_IMMEDIATE)) { ++ struct sip_request resp; ++ ++ if ((p->owner->_state == AST_STATE_RING) && !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT)) { ++ respprep(&resp, p, "180 Ringing", &p->initreq); ++ add_rpid(&resp, p); ++ send_response(p, &resp, XMIT_UNRELIABLE, 0); ++ ast_set_flag(&p->flags[0], SIP_RINGING); ++ } else if (p->owner->_state == AST_STATE_RINGING) { ++ respprep(&resp, p, "183 Session Progress", &p->initreq); ++ add_rpid(&resp, p); ++ send_response(p, &resp, XMIT_UNRELIABLE, 0); ++ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); ++ } else { ++ ast_log(LOG_DEBUG, "Unable able to send update to '%s' in state '%s'\n", p->owner->name, ast_state2str(p->owner->_state)); ++ } ++ } else { ++ ast_set_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND); ++ } ++ } ++} ++ + static const struct _map_x_s regstatestrings[] = { + { REG_STATE_FAILED, "Failed" }, + { REG_STATE_UNREGISTERED, "Unregistered"}, +@@ -10606,7 +10777,7 @@ + static int sip_reregister(const void *data) + { + /* if we are here, we know that we need to reregister. */ +- struct sip_registry *r= (struct sip_registry *) data; ++ struct sip_registry *r = (struct sip_registry *) data; + + /* if we couldn't get a reference to the registry object, punt */ + if (!r) +@@ -11916,10 +12087,96 @@ + /*! \brief Send a fake 401 Unauthorized response when the administrator + wants to hide the names of local devices from fishers + */ +-static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable) ++static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable) + { +- ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */ +- transmit_response_with_auth(p, "401 Unauthorized", req, p->randdata, reliable, "WWW-Authenticate", 0); ++ /* We have to emulate EXACTLY what we'd get with a good peer ++ * and a bad password, or else we leak information. */ ++ const char *response = "407 Proxy Authentication Required"; ++ const char *reqheader = "Proxy-Authorization"; ++ const char *respheader = "Proxy-Authenticate"; ++ const char *authtoken; ++ struct ast_str *buf; ++ char *c; ++ ++ /* table of recognised keywords, and their value in the digest */ ++ enum keys { K_NONCE, K_LAST }; ++ struct x { ++ const char *key; ++ const char *s; ++ } *i, keys[] = { ++ [K_NONCE] = { "nonce=", "" }, ++ [K_LAST] = { NULL, NULL} ++ }; ++ ++ if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) { ++ response = "401 Unauthorized"; ++ reqheader = "Authorization"; ++ respheader = "WWW-Authenticate"; ++ } ++ authtoken = get_header(req, reqheader); ++ if (req->ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) { ++ /* This is a retransmitted invite/register/etc, don't reconstruct authentication ++ * information */ ++ transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0); ++ /* Schedule auto destroy in 32 seconds (according to RFC 3261) */ ++ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); ++ return; ++ } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) { ++ /* We have no auth, so issue challenge and request authentication */ ++ ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */ ++ transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0); ++ /* Schedule auto destroy in 32 seconds */ ++ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); ++ return; ++ } ++ ++ if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) { ++ transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); ++ return; ++ } ++ ++ /* Make a copy of the response and parse it */ ++ if (ast_str_set(&buf, 0, "%s", authtoken) == AST_DYNSTR_BUILD_FAILED) { ++ transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); ++ return; ++ } ++ ++ c = buf->str; ++ ++ while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */ ++ for (i = keys; i->key != NULL; i++) { ++ const char *separator = ","; /* default */ ++ ++ if (strncasecmp(c, i->key, strlen(i->key)) != 0) { ++ continue; ++ } ++ /* Found. Skip keyword, take text in quotes or up to the separator. */ ++ c += strlen(i->key); ++ if (*c == '"') { /* in quotes. Skip first and look for last */ ++ c++; ++ separator = "\""; ++ } ++ i->s = c; ++ strsep(&c, separator); ++ break; ++ } ++ if (i->key == NULL) { /* not found, jump after space or comma */ ++ strsep(&c, " ,"); ++ } ++ } ++ ++ /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */ ++ if (strcasecmp(p->randdata, keys[K_NONCE].s)) { ++ if (!req->ignore) { ++ ast_string_field_build(p, randdata, "%08lx", ast_random()); ++ } ++ transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, FALSE); ++ ++ /* Schedule auto destroy in 32 seconds */ ++ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); ++ } else { ++ transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); ++ } + } + + /*! +@@ -12011,12 +12268,6 @@ + } + + if (peer) { +- /*! \todo OEJ Remove this - there's never RTP in a REGISTER dialog... */ +- /* Set Frame packetization */ +- if (p->rtp) { +- ast_rtp_codec_setpref(p->rtp, &peer->prefs); +- p->autoframing = peer->autoframing; +- } + if (!peer->host_dynamic) { + ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); + res = AUTH_PEER_NOT_DYNAMIC; +@@ -12095,6 +12346,14 @@ + } + } + } ++ if (!peer && sip_cfg.alwaysauthreject) { ++ /* If we found a peer, we transmit a 100 Trying. Therefore, if we're ++ * trying to avoid leaking information, we MUST also transmit the same ++ * response when we DON'T find a peer. */ ++ transmit_response(p, "100 Trying", req); ++ /* Insert a fake delay between the 100 and the subsequent failure. */ ++ sched_yield(); ++ } + if (!res) { + ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + } +@@ -12108,7 +12367,7 @@ + name, ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + break; + case AUTH_USERNAME_MISMATCH: +- /* Username and digest username does not match. ++ /* Username and digest username does not match. + Asterisk uses the From: username for authentication. We need the + devices to use the same authentication user name until we support + proper authentication by digest auth name */ +@@ -12121,7 +12380,12 @@ + case AUTH_PEER_NOT_DYNAMIC: + case AUTH_ACL_FAILED: + if (sip_cfg.alwaysauthreject) { +- transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE); ++ transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE); ++ if (global_authfailureevents) { ++ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Rejected\r\nCause: %s\r\nAddress: %s\r\nPort: %d\r\n", ++ name, res == AUTH_PEER_NOT_DYNAMIC ? "AUTH_PEER_NOT_DYNAMIC" : "URI_NOT_FOUND", ++ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); ++ } + } else { + /* URI not found */ + if (res == AUTH_PEER_NOT_DYNAMIC) { +@@ -12178,23 +12442,199 @@ + } + } + ++/*! \brief Parse the parts of the P-Asserted-Identity header ++ * on an incoming packet. Returns 1 if a valid header is found ++ * and it is different from the current caller id. ++ */ ++static int get_pai(struct sip_pvt *p, struct sip_request *req) ++{ ++ char pai[256]; ++ char privacy[64]; ++ char *cid_num = ""; ++ char *cid_name = ""; ++ int callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; ++ char *start = NULL, *end = NULL; ++ ++ ast_copy_string(pai, get_header(req, "P-Asserted-Identity"), sizeof(pai)); ++ ++ if (ast_strlen_zero(pai)) { ++ return 0; ++ } ++ ++ start = pai; ++ if (*start == '"') { ++ *start++ = '\0'; ++ end = strchr(start, '"'); ++ if (!end) ++ return 0; ++ *end++ = '\0'; ++ cid_name = start; ++ start = ast_skip_blanks(end); ++ } ++ ++ if (*start != '<') ++ return 0; ++ *start++ = '\0'; ++ end = strchr(start, '@'); ++ if (!end) ++ return 0; ++ *end++ = '\0'; ++ if (!strncasecmp(start, "anonymous@anonymous.invalid", 27)) { ++ callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; ++ /*XXX Assume no change in cid_num. Perhaps it should be ++ * blanked? ++ */ ++ cid_num = (char *)p->cid_num; ++ } else if (!strncasecmp(start, "sip:", 4)) { ++ cid_num = start + 4; ++ if (ast_is_shrinkable_phonenumber(cid_num)) ++ ast_shrink_phone_number(cid_num); ++ start = end; ++ ++ end = strchr(start, '>'); ++ if (!end) ++ return 0; ++ *end = '\0'; ++ } else { ++ return 0; ++ } ++ ++ ast_copy_string(privacy, get_header(req, "Privacy"), sizeof(privacy)); ++ if (!ast_strlen_zero(privacy) && strncmp(privacy, "id", 2)) { ++ callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; ++ } ++ ++ /* Only return true if the supplied caller id is different */ ++ if (!strcasecmp(p->cid_num, cid_num) && !strcasecmp(p->cid_name, cid_name) && p->callingpres == callingpres) ++ return 0; ++ ++ ast_string_field_set(p, cid_num, cid_num); ++ ast_string_field_set(p, cid_name, cid_name); ++ p->callingpres = callingpres; ++ ++ if (p->owner) { ++ ast_set_callerid(p->owner, cid_num, cid_name, NULL); ++ p->owner->cid.cid_pres = callingpres; ++ } ++ ++ return 1; ++} ++ ++/*! \brief Get name, number and presentation from remote party id header, ++ * returns true if a valid header was found and it was different from the ++ * current caller id. ++ */ ++static int get_rpid(struct sip_pvt *p, struct sip_request *oreq) ++{ ++ char tmp[256]; ++ struct sip_request *req; ++ char *cid_num = ""; ++ char *cid_name = ""; ++ int callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; ++ char *privacy = ""; ++ char *screen = ""; ++ char *start, *end; ++ ++ if (!ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) ++ return 0; ++ req = oreq; ++ if (!req) ++ req = &p->initreq; ++ ast_copy_string(tmp, get_header(req, "Remote-Party-ID"), sizeof(tmp)); ++ if (ast_strlen_zero(tmp)) { ++ return get_pai(p, req); ++ } ++ ++ start = tmp; ++ if (*start == '"') { ++ *start++ = '\0'; ++ end = strchr(start, '"'); ++ if (!end) ++ return 0; ++ *end++ = '\0'; ++ cid_name = start; ++ start = ast_skip_blanks(end); ++ } ++ ++ if (*start != '<') ++ return 0; ++ *start++ = '\0'; ++ end = strchr(start, '@'); ++ if (!end) ++ return 0; ++ *end++ = '\0'; ++ if (strncasecmp(start, "sip:", 4)) ++ return 0; ++ cid_num = start + 4; ++ if (ast_is_shrinkable_phonenumber(cid_num)) ++ ast_shrink_phone_number(cid_num); ++ start = end; ++ ++ end = strchr(start, '>'); ++ if (!end) ++ return 0; ++ *end++ = '\0'; ++ if (*end) { ++ start = end; ++ if (*start != ';') ++ return 0; ++ *start++ = '\0'; ++ while (!ast_strlen_zero(start)) { ++ end = strchr(start, ';'); ++ if (end) ++ *end++ = '\0'; ++ if (!strncasecmp(start, "privacy=", 8)) ++ privacy = start + 8; ++ else if (!strncasecmp(start, "screen=", 7)) ++ screen = start + 7; ++ start = end; ++ } ++ ++ if (!strcasecmp(privacy, "full")) { ++ if (!strcasecmp(screen, "yes")) ++ callingpres = AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN; ++ else if (!strcasecmp(screen, "no")) ++ callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; ++ } else { ++ if (!strcasecmp(screen, "yes")) ++ callingpres = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN; ++ else if (!strcasecmp(screen, "no")) ++ callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; ++ } ++ } ++ ++ /* Only return true if the supplied caller id is different */ ++ if (!strcasecmp(p->cid_num, cid_num) && !strcasecmp(p->cid_name, cid_name) && p->callingpres == callingpres) ++ return 0; ++ ++ ast_string_field_set(p, cid_num, cid_num); ++ ast_string_field_set(p, cid_name, cid_name); ++ p->callingpres = callingpres; ++ ++ if (p->owner) { ++ ast_set_callerid(p->owner, cid_num, cid_name, NULL); ++ p->owner->cid.cid_pres = callingpres; ++ } ++ ++ return 1; ++} ++ + /*! \brief Get referring dnis */ +-static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq) ++static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason) + { +- char tmp[256], *exten, *rexten, *rdomain; +- char *params, *reason = NULL; ++ char tmp[256], *exten, *rexten, *rdomain, *rname = NULL; ++ char *params, *reason_param = NULL; + struct sip_request *req; +- ++ + req = oreq ? oreq : &p->initreq; + + ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp)); + if (ast_strlen_zero(tmp)) +- return 0; ++ return -1; + +- /*! \todo This function does not take user-parameters into consideration. +- First look for @, then start looking for ; to find uri-parameters. +- */ +- params = strchr(tmp, ';'); ++ if ((params = strchr(tmp, '>'))) { ++ params = strchr(params, ';'); ++ } + + exten = get_in_brackets(tmp); + if (!strncasecmp(exten, "sip:", 4)) { +@@ -12213,16 +12653,16 @@ + while (*params == ';' || *params == ' ') + params++; + /* Check if we have a reason parameter */ +- if ((reason = strcasestr(params, "reason="))) { +- reason+=7; ++ if ((reason_param = strcasestr(params, "reason="))) { ++ reason_param+=7; + /* Remove enclosing double-quotes */ +- if (*reason == '"') +- ast_strip_quoted(reason, "\"", "\""); +- if (!ast_strlen_zero(reason)) { +- sip_set_redirstr(p, reason); ++ if (*reason_param == '"') ++ ast_strip_quoted(reason_param, "\"", "\""); ++ if (!ast_strlen_zero(reason_param)) { ++ sip_set_redirstr(p, reason_param); + if (p->owner) { + pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause); +- pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason); ++ pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param); + } + } + } +@@ -12230,14 +12670,33 @@ + + rdomain = exten; + rexten = strsep(&rdomain, "@"); /* trim anything after @ */ +- if (p->owner) ++ if (p->owner) + pbx_builtin_setvar_helper(p->owner, "__SIPRDNISDOMAIN", rdomain); + + if (sip_debug_test_pvt(p)) +- ast_verbose("RDNIS for this call is is %s (reason %s)\n", exten, reason ? reason : ""); ++ ast_verbose("RDNIS for this call is %s (reason %s)\n", exten, reason ? reason_param : ""); + +- ast_string_field_set(p, rdnis, rexten); ++ /*ast_string_field_set(p, rdnis, rexten);*/ + ++ if (*tmp == '\"') { ++ char *end_quote; ++ rname = tmp + 1; ++ end_quote = strchr(rname, '\"'); ++ *end_quote = '\0'; ++ } ++ ++ if (number) { ++ *number = ast_strdup(rexten); ++ } ++ ++ if (name && rname) { ++ *name = ast_strdup(rname); ++ } ++ ++ if (reason && !ast_strlen_zero(reason_param)) { ++ *reason = sip_reason_str_to_code(reason_param); ++ } ++ + return 0; + } + +@@ -12849,58 +13308,12 @@ + return output; + } + +-/*! \brief Get caller id number from Remote-Party-ID header field +- * Returns true if number should be restricted (privacy setting found) +- * output is set to NULL if no number found +- */ +-static int get_rpid_num(const char *input, char *output, int maxlen) +-{ +- char *start; +- char *end; + +- start = strchr(input, ':'); +- if (!start) { +- output[0] = '\0'; +- return 0; +- } +- start++; +- +- /* we found "number" */ +- ast_copy_string(output, start, maxlen); +- output[maxlen-1] = '\0'; +- +- end = strchr(output, '@'); +- if (end) +- *end = '\0'; +- else +- output[0] = '\0'; +- if (strstr(input, "privacy=full") || strstr(input, "privacy=uri")) +- return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; +- +- return 0; +-} +- +- +-/*! \brief helper function for check_{user|peer}_ok() */ +-static void replace_cid(struct sip_pvt *p, const char *rpid_num, const char *calleridname) +-{ +- /* replace callerid if rpid found, and not restricted */ +- if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) { +- char *tmp = ast_strdupa(rpid_num); /* XXX the copy can be done later */ +- if (!ast_strlen_zero(calleridname)) +- ast_string_field_set(p, cid_name, calleridname); +- if (ast_is_shrinkable_phonenumber(tmp)) +- ast_shrink_phone_number(tmp); +- ast_string_field_set(p, cid_num, tmp); +- } +-} +- + /*! \brief Validate device authentication */ + static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, + struct sip_request *req, int sipmethod, struct sockaddr_in *sin, + struct sip_peer **authpeer, +- enum xmittype reliable, +- char *rpid_num, char *calleridname, char *uri2) ++ enum xmittype reliable, char *calleridname, char *uri2) + { + enum check_auth_result res; + int debug=sip_debug_test_addr(sin); +@@ -12935,7 +13348,7 @@ + /* XXX what about p->prefs = peer->prefs; ? */ + /* Set Frame packetization */ + if (p->rtp) { +- ast_rtp_codec_setpref(p->rtp, &peer->prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs); + p->autoframing = peer->autoframing; + } + +@@ -12948,7 +13361,6 @@ + if (p->sipoptions) + peer->sipoptions = p->sipoptions; + +- replace_cid(p, rpid_num, calleridname); + do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT_ROUTE)); + + ast_string_field_set(p, peersecret, peer->secret); +@@ -12957,6 +13369,7 @@ + ast_string_field_set(p, mohinterpret, peer->mohinterpret); + ast_string_field_set(p, mohsuggest, peer->mohsuggest); + ast_string_field_set(p, parkinglot, peer->parkinglot); ++ ast_string_field_set(p, engine, peer->engine); + if (peer->callingpres) /* Peer calling pres setting will override RPID */ + p->callingpres = peer->callingpres; + if (peer->maxms && peer->lastms) +@@ -13000,14 +13413,18 @@ + /* XXX this takes the name from the caller... can we override ? */ + ast_string_field_set(p, authname, peer->username); + } +- if (!ast_strlen_zero(peer->cid_num)) { +- char *tmp = ast_strdupa(peer->cid_num); +- if (ast_is_shrinkable_phonenumber(tmp)) +- ast_shrink_phone_number(tmp); +- ast_string_field_set(p, cid_num, tmp); ++ if (!get_rpid(p, req)) { ++ if (!ast_strlen_zero(peer->cid_num)) { ++ char *tmp = ast_strdupa(peer->cid_num); ++ if (ast_is_shrinkable_phonenumber(tmp)) ++ ast_shrink_phone_number(tmp); ++ ast_string_field_set(p, cid_num, tmp); ++ } ++ if (!ast_strlen_zero(peer->cid_name)) ++ ast_string_field_set(p, cid_name, peer->cid_name); ++ if (peer->callingpres) ++ p->callingpres = peer->callingpres; + } +- if (!ast_strlen_zero(peer->cid_name)) +- ast_string_field_set(p, cid_name, peer->cid_name); + ast_string_field_set(p, fullcontact, peer->fullcontact); + if (!ast_strlen_zero(peer->context)) + ast_string_field_set(p, context, peer->context); +@@ -13024,17 +13441,6 @@ + if (p->peercapability) + p->jointcapability &= p->peercapability; + p->maxcallbitrate = peer->maxcallbitrate; +- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) && +- (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) || +- !(p->capability & AST_FORMAT_VIDEO_MASK)) && +- p->vrtp) { +- ast_rtp_destroy(p->vrtp); +- p->vrtp = NULL; +- } +- if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) || !(p->capability & AST_FORMAT_TEXT_MASK)) && p->trtp) { +- ast_rtp_destroy(p->trtp); +- p->trtp = NULL; +- } + if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) + p->noncodeccapability |= AST_RTP_DTMF; +@@ -13043,6 +13449,12 @@ + p->jointnoncodeccapability = p->noncodeccapability; + if (p->t38.peercapability) + p->t38.jointcapability &= p->t38.peercapability; ++ if (!dialog_initialize_rtp(p)) { ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs); ++ p->autoframing = peer->autoframing; ++ } else { ++ res = AUTH_RTP_FAILED; ++ } + } + unref_peer(peer, "check_peer_ok: unref_peer: tossing temp ptr to peer from find_peer"); + return res; +@@ -13062,8 +13474,6 @@ + char *dummy; /* dummy return value for parse_uri */ + char *domain; /* dummy return value for parse_uri */ + char *of, *of2; +- char rpid_num[50]; +- const char *rpid; + enum check_auth_result res; + char calleridname[50]; + char *uri2 = ast_strdupa(uri); +@@ -13079,11 +13489,6 @@ + if (calleridname[0]) + ast_string_field_set(p, cid_name, calleridname); + +- rpid = get_header(req, "Remote-Party-ID"); +- memset(rpid_num, 0, sizeof(rpid_num)); +- if (!ast_strlen_zero(rpid)) +- p->callingpres = get_rpid_num(rpid, rpid_num, sizeof(rpid_num)); +- + of = get_in_brackets(from); + if (ast_strlen_zero(p->exten)) { + char *t = uri2; +@@ -13157,14 +13562,18 @@ + } + + res = check_peer_ok(p, of, req, sipmethod, sin, +- authpeer, reliable, rpid_num, calleridname, uri2); ++ authpeer, reliable, calleridname, uri2); + if (res != AUTH_DONT_KNOW) + return res; + + /* Finally, apply the guest policy */ + if (sip_cfg.allowguest) { +- replace_cid(p, rpid_num, calleridname); +- res = AUTH_SUCCESSFUL; ++ get_rpid(p, req); ++ if (!dialog_initialize_rtp(p)) { ++ res = AUTH_SUCCESSFUL; ++ } else { ++ res = AUTH_RTP_FAILED; ++ } + } else if (sip_cfg.alwaysauthreject) + res = AUTH_FAKE_AUTH; /* reject with fake authorization request */ + else +@@ -13961,7 +14370,20 @@ + */ + return 0; + } +- ++ ++ /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */ ++ if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) { ++ ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); ++ sip_pvt_unlock(dialog); ++ return 0; ++ } ++ ++ if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) { ++ ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); ++ sip_pvt_unlock(dialog); ++ return 0; ++ } ++ + /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */ + check_rtp_timeout(dialog, *t); + +@@ -13970,13 +14392,13 @@ + - if that's the case, wait with destruction */ + if (dialog->needdestroy && !dialog->packets && !dialog->owner) { + /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */ +- if (dialog->rtp && ast_rtp_get_bridged(dialog->rtp)) { ++ if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) { + ast_debug(2, "Bridge still active. Delaying destruction of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); + sip_pvt_unlock(dialog); + return 0; + } + +- if (dialog->vrtp && ast_rtp_get_bridged(dialog->vrtp)) { ++ if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) { + ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); + sip_pvt_unlock(dialog); + return 0; +@@ -14466,6 +14888,7 @@ + ast_cli(fd, " Sess-Refresh : %s\n", strefresher2str(peer->stimer.st_ref)); + ast_cli(fd, " Sess-Expires : %d secs\n", peer->stimer.st_max_se); + ast_cli(fd, " Min-Sess : %d secs\n", peer->stimer.st_min_se); ++ ast_cli(fd, " RTP Engine : %s\n", peer->engine); + ast_cli(fd, "\n"); + peer = unref_peer(peer, "sip_show_peer: unref_peer: done with peer ptr"); + } else if (peer && type == 1) { /* manager listing */ +@@ -14513,6 +14936,7 @@ + astman_append(s, "SIP-Sess-Refresh: %s\r\n", strefresher2str(peer->stimer.st_ref)); + astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se); + astman_append(s, "SIP-Sess-Min: %d\r\n", peer->stimer.st_min_se); ++ astman_append(s, "SIP-RTP-Engine: %s\r\n", peer->engine); + + /* - is enumerated */ + astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF))); +@@ -14645,6 +15069,7 @@ + ast_cli(a->fd, " Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref)); + ast_cli(a->fd, " Sess-Expires : %d secs\n", user->stimer.st_max_se); + ast_cli(a->fd, " Sess-Min-SE : %d secs\n", user->stimer.st_min_se); ++ ast_cli(a->fd, " RTP Engine : %s\n", user->engine); + + ast_cli(a->fd, " Codec Order : ("); + print_codec_to_cli(a->fd, &user->prefs); +@@ -14799,11 +15224,10 @@ + #define FORMAT2 "%-15.15s %-11.11s %-8.8s %-10.10s %-10.10s (%-2.2s) %-6.6s %-10.10s %-10.10s ( %%) %-6.6s\n" + #define FORMAT "%-15.15s %-11.11s %-8.8s %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u\n" + struct sip_pvt *cur = __cur; +- unsigned int rxcount; +- unsigned int txcount; ++ struct ast_rtp_instance_stats stats; + char durbuf[10]; +- int duration; +- int durh, durm, durs; ++ int duration; ++ int durh, durm, durs; + struct ast_channel *c = cur->owner; + struct __show_chan_arg *arg = __arg; + int fd = arg->fd; +@@ -14817,10 +15241,9 @@ + ast_cli(fd, "%-15.15s %-11.11s (inv state: %s) -- %s\n", ast_inet_ntoa(cur->sa.sin_addr), cur->callid, invitestate2string[cur->invitestate].desc, "-- No RTP active"); + return 0; /* don't care, we scan all channels */ + } +- rxcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXCOUNT); +- txcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXCOUNT); + +- /* Find the duration of this channel */ ++ ast_rtp_instance_get_stats(cur->rtp, &stats, AST_RTP_INSTANCE_STAT_ALL); ++ + if (c && c->cdr && !ast_tvzero(c->cdr->start)) { + duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000); + durh = duration / 3600; +@@ -14830,21 +15253,21 @@ + } else { + durbuf[0] = '\0'; + } +- /* Print stats for every call with RTP */ ++ + ast_cli(fd, FORMAT, + ast_inet_ntoa(cur->sa.sin_addr), + cur->callid, + durbuf, +- rxcount > (unsigned int) 100000 ? (unsigned int) (rxcount)/(unsigned int) 1000 : rxcount, +- rxcount > (unsigned int) 100000 ? "K":" ", +- ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS), +- rxcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) / rxcount * 100) : 0, +- ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXJITTER), +- txcount > (unsigned int) 100000 ? (unsigned int) (txcount)/(unsigned int) 1000 : txcount, +- txcount > (unsigned int) 100000 ? "K":" ", +- ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS), +- txcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS)/ txcount * 100) : 0, +- ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXJITTER) ++ stats.rxcount > (unsigned int) 100000 ? (unsigned int) (stats.rxcount)/(unsigned int) 1000 : stats.rxcount, ++ stats.rxcount > (unsigned int) 100000 ? "K":" ", ++ stats.rxploss, ++ stats.rxcount > stats.rxploss ? (stats.rxploss / stats.rxcount * 100) : 0, ++ stats.rxjitter, ++ stats.txcount > (unsigned int) 100000 ? (unsigned int) (stats.txcount)/(unsigned int) 1000 : stats.txcount, ++ stats.txcount > (unsigned int) 100000 ? "K":" ", ++ stats.txploss, ++ stats.txcount > stats.txploss ? (stats.txploss / stats.txcount * 100) : 0, ++ stats.txjitter + ); + arg->numchans++; + +@@ -16387,29 +16810,150 @@ + .read = function_sipchaninfo_read, + }; + ++static int read_to_parts(struct sip_pvt *p, struct sip_request *req, char **name, char **number) ++{ ++ ++ char to_header[256]; ++ char *to_name = NULL; ++ char *to_number = NULL; ++ char *separator; ++ ++ ast_copy_string(to_header, get_header(req, "To"), sizeof(to_header)); ++ ++ /* Let's get that number first! */ ++ to_number = get_in_brackets(to_header); ++ ++ if (!strncasecmp(to_number, "sip:", 4)) { ++ to_number += 4; ++ } else if (!strncasecmp(to_number, "sips:", 5)) { ++ to_number += 5; ++ } else { ++ ast_log(LOG_WARNING, "Not a SIP URI? (%s)!\n", to_number); ++ return -1; ++ } ++ ++ /* Remove the host and such since we just want the number */ ++ if ((separator = strchr(to_number, '@'))) { ++ *separator = '\0'; ++ } ++ ++ /* We have the number. Let's get the name now. */ ++ ++ if (*to_header == '\"') { ++ to_name = to_header + 1; ++ if (!(separator = (char *)find_closing_quote(to_name, NULL))) { ++ ast_log(LOG_NOTICE, "No closing quote in name section of To: header (%s)\n", to_header); ++ return -1; ++ } ++ *separator = '\0'; ++ } ++ ++ if (number) { ++ *number = ast_strdup(to_number); ++ } ++ if (name && !ast_strlen_zero(to_name)) { ++ *name = ast_strdup(to_name); ++ } ++ ++ return 0; ++} ++ ++/*! \brief update redirecting information for a channel based on headers ++ * ++ */ ++static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req, struct ast_party_redirecting *redirecting, int set_call_forward) ++{ ++ char *redirecting_from_name = NULL; ++ char *redirecting_from_number = NULL; ++ char *redirecting_to_name = NULL; ++ char *redirecting_to_number = NULL; ++ int reason = AST_REDIRECTING_REASON_UNCONDITIONAL; ++ int is_response = req->method == SIP_RESPONSE; ++ int res = 0; ++ ++ res = get_rdnis(p, req, &redirecting_from_name, &redirecting_from_number, &reason); ++ if (res == -1) { ++ if (is_response) { ++ read_to_parts(p, req, &redirecting_from_name, &redirecting_from_number); ++ } else { ++ return; ++ } ++ } ++ ++ /* At this point, all redirecting "from" info should be filled in appropriately ++ * on to the "to" info ++ */ ++ ++ if (is_response) { ++ parse_moved_contact(p, req, &redirecting_to_name, &redirecting_to_number, set_call_forward); ++ } else { ++ read_to_parts(p, req, &redirecting_to_name, &redirecting_to_number); ++ } ++ ++ if (!ast_strlen_zero(redirecting_from_number)) { ++ if (redirecting->from.number) { ++ ast_free(redirecting->from.number); ++ } ++ ast_debug(3, "Got redirecting from number %s\n", redirecting_from_number); ++ redirecting->from.number = redirecting_from_number; ++ } ++ if (!ast_strlen_zero(redirecting_from_name)) { ++ if (redirecting->from.name) { ++ ast_free(redirecting->from.name); ++ } ++ ast_debug(3, "Got redirecting from name %s\n", redirecting_from_name); ++ redirecting->from.name = redirecting_from_name; ++ } ++ if (!ast_strlen_zero(redirecting_to_number)) { ++ if (redirecting->to.number) { ++ ast_free(redirecting->to.number); ++ } ++ ast_debug(3, "Got redirecting to number %s\n", redirecting_to_number); ++ redirecting->to.number = redirecting_to_number; ++ } ++ if (!ast_strlen_zero(redirecting_to_name)) { ++ if (redirecting->to.name) { ++ ast_free(redirecting->to.name); ++ } ++ ast_debug(3, "Got redirecting to name %s\n", redirecting_from_number); ++ redirecting->to.name = redirecting_to_name; ++ } ++ redirecting->reason = reason; ++} ++ + /*! \brief Parse 302 Moved temporalily response + \todo XXX Doesn't redirect over TLS on sips: uri's. + If we get a redirect to a SIPS: uri, this needs to be going back to the + dialplan (this is a request for a secure signalling path). + Note that transport=tls is deprecated, but we need to support it on incoming requests. + */ +-static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) ++static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward) + { +- char tmp[SIPBUFSIZE]; +- char *s, *e, *t, *trans; ++ char contact[SIPBUFSIZE]; ++ char *contact_name = NULL; ++ char *contact_number = NULL; ++ char *separator, *trans; + char *domain; + enum sip_transport transport = SIP_TRANSPORT_UDP; + +- ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp)); +- if ((t = strchr(tmp, ','))) +- *t = '\0'; ++ ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); ++ if ((separator = strchr(contact, ','))) ++ *separator = '\0'; + +- s = get_in_brackets(tmp); +- if ((trans = strcasestr(s, ";transport="))) do { ++ /* ooh, a name */ ++ if (*contact == '"') { ++ contact_name = contact + 1; ++ if ((separator = strchr(contact_name, '"'))) { ++ *separator++ = '\0'; ++ } ++ } ++ ++ contact_number = get_in_brackets(contact); ++ if ((trans = strcasestr(contact_number, ";transport="))) { + trans += 11; + +- if ((e = strchr(trans, ';'))) +- *e = '\0'; ++ if ((separator = strchr(trans, ';'))) ++ *separator = '\0'; + + if (!strncasecmp(trans, "tcp", 3)) + transport = SIP_TRANSPORT_TCP; +@@ -16417,12 +16961,12 @@ + transport = SIP_TRANSPORT_TLS; + else { + if (strncasecmp(trans, "udp", 3)) +- ast_debug(1, "received contact with an invalid transport, '%s'\n", s); ++ ast_debug(1, "received contact with an invalid transport, '%s'\n", contact_number); + /* This will assume UDP for all unknown transports */ + transport = SIP_TRANSPORT_UDP; + } +- } while(0); +- s = remove_uri_parameters(s); ++ } ++ contact_number = remove_uri_parameters(contact_number); + + if (p->socket.tcptls_session) { + ao2_ref(p->socket.tcptls_session, -1); +@@ -16432,51 +16976,70 @@ + p->socket.fd = -1; + p->socket.type = transport; + +- if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) { ++ if (set_call_forward && ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) { + char *host = NULL; +- if (!strncasecmp(s, "sip:", 4)) +- s += 4; +- else if (!strncasecmp(s, "sips:", 5)) +- s += 5; +- e = strchr(s, '/'); +- if (e) +- *e = '\0'; +- if ((host = strchr(s, '@'))) { ++ if (!strncasecmp(contact_number, "sip:", 4)) ++ contact_number += 4; ++ else if (!strncasecmp(contact_number, "sips:", 5)) ++ contact_number += 5; ++ separator = strchr(contact_number, '/'); ++ if (separator) ++ *separator = '\0'; ++ if ((host = strchr(contact_number, '@'))) { + *host++ = '\0'; +- ast_debug(2, "Found promiscuous redirection to 'SIP/%s::::%s@%s'\n", s, get_transport(transport), host); ++ ast_debug(2, "Found promiscuous redirection to 'SIP/%s::::%s@%s'\n", contact_number, get_transport(transport), host); + if (p->owner) +- ast_string_field_build(p->owner, call_forward, "SIP/%s::::%s@%s", s, get_transport(transport), host); ++ ast_string_field_build(p->owner, call_forward, "SIP/%s::::%s@%s", contact_number, get_transport(transport), host); + } else { +- ast_debug(2, "Found promiscuous redirection to 'SIP/::::%s@%s'\n", get_transport(transport), s); ++ ast_debug(2, "Found promiscuous redirection to 'SIP/::::%s@%s'\n", get_transport(transport), contact_number); + if (p->owner) +- ast_string_field_build(p->owner, call_forward, "SIP/::::%s@%s", get_transport(transport), s); ++ ast_string_field_build(p->owner, call_forward, "SIP/::::%s@%s", get_transport(transport), contact_number); + } + } else { +- e = strchr(tmp, '@'); +- if (e) { +- *e++ = '\0'; +- domain = e; ++ separator = strchr(contact, '@'); ++ if (separator) { ++ *separator++ = '\0'; ++ domain = separator; + } else { + /* No username part */ +- domain = tmp; ++ domain = contact; + } +- e = strchr(tmp, '/'); /* WHEN do we hae a forward slash in the URI? */ +- if (e) +- *e = '\0'; ++ separator = strchr(contact, '/'); /* WHEN do we hae a forward slash in the URI? */ ++ if (separator) ++ *separator = '\0'; + +- if (!strncasecmp(s, "sip:", 4)) +- s += 4; +- else if (!strncasecmp(s, "sips:", 5)) +- s += 5; +- e = strchr(s, ';'); /* And username ; parameters? */ +- if (e) +- *e = '\0'; +- ast_debug(2, "Received 302 Redirect to extension '%s' (domain %s)\n", s, domain); +- if (p->owner) { +- pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain); +- ast_string_field_set(p->owner, call_forward, s); ++ if (!strncasecmp(contact_number, "sip:", 4)) ++ contact_number += 4; ++ else if (!strncasecmp(contact_number, "sips:", 5)) ++ contact_number += 5; ++ separator = strchr(contact_number, ';'); /* And username ; parameters? */ ++ if (separator) ++ *separator = '\0'; ++ if (set_call_forward) { ++ ast_debug(2, "Received 302 Redirect to extension '%s' (domain %s)\n", contact_number, domain); ++ if (p->owner) { ++ pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain); ++ ast_string_field_set(p->owner, call_forward, contact_number); ++ } + } + } ++ ++ /* We've gotten the number for the contact, now get the name */ ++ ++ if (*contact == '\"') { ++ contact_name = contact + 1; ++ if (!(separator = (char *)find_closing_quote(contact_name, NULL))) { ++ ast_log(LOG_NOTICE, "No closing quote on name in Contact header? %s\n", contact); ++ } ++ *separator = '\0'; ++ } ++ ++ if (name && !ast_strlen_zero(contact_name)) { ++ *name = ast_strdup(contact_name); ++ } ++ if (number) { ++ *number = ast_strdup(contact_number); ++ } + } + + /*! \brief Check pending actions on SIP call */ +@@ -16516,12 +17079,15 @@ + to avoid race conditions between asterisk servers. + Called from the scheduler. + */ +-static int sip_reinvite_retry(const void *data) ++static int sip_reinvite_retry(const void *data) + { + struct sip_pvt *p = (struct sip_pvt *) data; + +- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); ++ sip_pvt_lock(p); /* called from schedule thread which requires a lock */ ++ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); + p->waitid = -1; ++ check_pendings(p); ++ sip_pvt_unlock(p); + dialog_unref(p, "unref the dialog ptr from sip_reinvite_retry, because it held a dialog ptr"); + return 0; + } +@@ -16536,7 +17102,8 @@ + int reinvite = (p->owner && p->owner->_state == AST_STATE_UP); + char *p_hdrval; + int rtn; +- ++ struct ast_party_connected_line connected; ++ + if (reinvite) + ast_debug(4, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid); + else +@@ -16554,7 +17121,7 @@ + /* RFC3261 says we must treat every 1xx response (but not 100) + that we don't recognize as if it was 183. + */ +- if (resp > 100 && resp < 200 && resp!=101 && resp != 180 && resp != 182 && resp != 183) ++ if (resp > 100 && resp < 200 && resp!=101 && resp != 180 && resp != 181 && resp != 182 && resp != 183) + resp = 183; + + /* Any response between 100 and 199 is PROCEEDING */ +@@ -16582,6 +17149,14 @@ + if (!req->ignore && p->invitestate != INV_CANCELLED && sip_cancel_destroy(p)) + ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); + if (!req->ignore && p->owner) { ++ if (get_rpid(p, req)) { ++ ast_party_connected_line_init(&connected); ++ connected.id.number = (char *) p->cid_num; ++ connected.id.name = (char *) p->cid_name; ++ connected.id.number_presentation = p->callingpres; ++ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_queue_connected_line_update(p->owner, &connected); ++ } + ast_queue_control(p->owner, AST_CONTROL_RINGING); + if (p->owner->_state != AST_STATE_UP) { + ast_setstate(p->owner, AST_STATE_RINGING); +@@ -16599,10 +17174,32 @@ + check_pendings(p); + break; + ++ case 181: /* Call Is Being Forwarded */ ++ if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) ++ ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); ++ if (!req->ignore && p->owner) { ++ struct ast_party_redirecting redirecting = {{0,},}; ++ change_redirecting_information(p, req, &redirecting, FALSE); ++ ast_channel_queue_redirecting_update(p->owner, &redirecting); ++ } ++ check_pendings(p); ++ break; ++ + case 183: /* Session progress */ + if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) + ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); + /* Ignore 183 Session progress without SDP */ ++ if (!req->ignore && p->owner) { ++ if (get_rpid(p, req)) { ++ /* Queue a connected line update */ ++ ast_party_connected_line_init(&connected); ++ connected.id.number = (char *) p->cid_num; ++ connected.id.name = (char *) p->cid_name; ++ connected.id.number_presentation = p->callingpres; ++ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_queue_connected_line_update(p->owner, &connected); ++ } ++ } + if (find_sdp(req)) { + if (p->invitestate != INV_CANCELLED) + p->invitestate = INV_EARLY_MEDIA; +@@ -16624,9 +17221,19 @@ + if (!reinvite) + /* This 200 OK's SDP is not acceptable, so we need to ack, then hangup */ + /* For re-invites, we try to recover */ +- ast_set_flag(&p->flags[0], SIP_PENDINGBYE); ++ ast_set_flag(&p->flags[0], SIP_PENDINGBYE); + } + ++ if (!req->ignore && p->owner && get_rpid(p, req)) { ++ /* Queue a connected line update */ ++ ast_party_connected_line_init(&connected); ++ connected.id.number = (char *) p->cid_num; ++ connected.id.name = (char *) p->cid_name; ++ connected.id.number_presentation = p->callingpres; ++ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_queue_connected_line_update(p->owner, &connected); ++ } ++ + /* Parse contact header for continued conversation */ + /* When we get 200 OK, we know which device (and IP) to contact for this call */ + /* This is important when we have a SIP proxy between us and the phone */ +@@ -16648,6 +17255,9 @@ + + if (!req->ignore && p->owner) { + if (!reinvite) { ++ struct ast_party_connected_line connected; ++ ast_party_connected_line_collect_caller(&connected, &p->owner->cid); ++ ast_channel_queue_connected_line_update(p->owner, &connected); + ast_queue_control(p->owner, AST_CONTROL_ANSWER); + if (sip_cfg.callevents) + manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", +@@ -16788,24 +17398,10 @@ + if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) { + change_t38_state(p, T38_DISABLED); + /* Try to reset RTP timers */ +- ast_rtp_set_rtptimers_onhold(p->rtp); ++ //ast_rtp_set_rtptimers_onhold(p->rtp); + + /* Trigger a reinvite back to audio */ + transmit_reinvite_with_sdp(p, FALSE, FALSE); +- } else if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) { +- /* We tried to send T.38 out in an initial INVITE and the remote side rejected it, +- right now we can't fall back to audio so totally abort. +- */ +- /* Try to reset RTP timers */ +- ast_rtp_set_rtptimers_onhold(p->rtp); +- ast_log(LOG_ERROR, "Got error on T.38 initial invite. Bailing out.\n"); +- +- change_t38_state(p, T38_DISABLED); +- /* The dialog is now terminated */ +- if (p->owner && !req->ignore) +- ast_queue_control(p->owner, AST_CONTROL_CONGESTION); +- pvt_set_needdestroy(p, "got error on T.38 initial invite"); +- sip_alreadygone(p); + } else { + /* We can't set up this call, so give up */ + if (p->owner && !req->ignore) +@@ -16826,8 +17422,15 @@ + /* This is a re-invite that failed. */ + /* Reset the flag after a while + */ +- int wait = 3 + ast_random() % 5; +- p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, dialog_ref(p, "passing dialog ptr into sched structure based on waitid for sip_reinvite_retry.")); ++ int wait; ++ /* RFC 3261, if owner of call, wait between 2.1 to 4 seconds, ++ * if not owner of call, wait 0 to 2 seconds */ ++ if (p->outgoing_call) { ++ wait = 2100 + ast_random() % 2000; ++ } else { ++ wait = ast_random() % 2000; ++ } ++ p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, dialog_ref(p, "passing dialog ptr into sched structure based on waitid for sip_reinvite_retry.")); + ast_log(LOG_WARNING, "just did sched_add waitid(%d) for sip_reinvite_retry for dialog %s in handle_response_invite\n", p->waitid, p->callid); + ast_debug(2, "Reinvite race. Waiting %d secs before retry\n", wait); + } +@@ -16953,6 +17556,8 @@ + */ + static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) + { ++ enum ast_control_transfer message = AST_TRANSFER_FAILED; ++ + /* If no refer structure exists, then do nothing */ + if (!p->refer) + return; +@@ -16972,12 +17577,18 @@ + if (ast_strlen_zero(p->authname)) { + ast_log(LOG_WARNING, "Asked to authenticate REFER to %s:%d but we have no matching peer or realm auth!\n", + ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port)); ++ if (p->owner) { ++ ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); ++ } + pvt_set_needdestroy(p, "unable to authenticate REFER"); + } + if (p->authtries > 1 || do_proxy_auth(p, req, resp, SIP_REFER, 0)) { + ast_log(LOG_NOTICE, "Failed to authenticate on REFER to '%s'\n", get_header(&p->initreq, "From")); + p->refer->status = REFER_NOAUTH; +- pvt_set_needdestroy(p, "failed to authenticat REFER"); ++ if (p->owner) { ++ ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); ++ } ++ pvt_set_needdestroy(p, "failed to authenticate REFER"); + } + break; + case 481: /* Call leg does not exist */ +@@ -16998,11 +17609,17 @@ + ast_log(LOG_NOTICE, "SIP transfer to %s failed, call miserably fails. \n", p->refer->refer_to); + pvt_set_needdestroy(p, "received 500/501 response"); + p->refer->status = REFER_FAILED; ++ if (p->owner) { ++ ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); ++ } + break; + case 603: /* Transfer declined */ + ast_log(LOG_NOTICE, "SIP transfer to %s declined, call miserably fails. \n", p->refer->refer_to); + p->refer->status = REFER_FAILED; + pvt_set_needdestroy(p, "received 603 response"); ++ if (p->owner) { ++ ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); ++ } + break; + } + } +@@ -17215,11 +17832,11 @@ + { + /* Immediately stop RTP, VRTP and UDPTL as applicable */ + if (p->rtp) +- ast_rtp_stop(p->rtp); ++ ast_rtp_instance_stop(p->rtp); + if (p->vrtp) +- ast_rtp_stop(p->vrtp); ++ ast_rtp_instance_stop(p->vrtp); + if (p->trtp) +- ast_rtp_stop(p->trtp); ++ ast_rtp_instance_stop(p->trtp); + if (p->udptl) + ast_udptl_stop(p->udptl); + } +@@ -17296,6 +17913,7 @@ + case 183: /* 183 Session Progress */ + case 180: /* 180 Ringing */ + case 182: /* 182 Queued */ ++ case 181: /* 181 Call Is Being Forwarded */ + if (sipmethod == SIP_INVITE) + handle_response_invite(p, resp, rest, req, seqno); + break; +@@ -17463,7 +18081,11 @@ + case 301: /* Moved permanently */ + case 302: /* Moved temporarily */ + case 305: /* Use Proxy */ +- parse_moved_contact(p, req); ++ { ++ struct ast_party_redirecting redirecting = {{0,},}; ++ change_redirecting_information(p, req, &redirecting, TRUE); ++ ast_channel_set_redirecting(p->owner, &redirecting); ++ } + /* Fall through */ + case 486: /* Busy here */ + case 600: /* Busy everywhere */ +@@ -18037,7 +18659,11 @@ + if (!success) { + ast_log(LOG_NOTICE, "Transfer failed. Sorry. Nothing further to do with this call\n"); + } +- ++ ++ if (p->owner) { ++ enum ast_control_transfer message = success ? AST_TRANSFER_SUCCESS : AST_TRANSFER_FAILED; ++ ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); ++ } + /* Confirm that we received this packet */ + transmit_response(p, "200 OK", req); + } else if (p->mwi && !strcmp(event, "message-summary")) { +@@ -18054,10 +18680,7 @@ + AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(new), + AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(old), + AST_EVENT_IE_END))) { +- ast_event_queue_and_cache(event, +- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_END); ++ ast_event_queue_and_cache(event); + } + } + +@@ -18158,7 +18781,7 @@ + /* We should answer something here. If we are here, the + call we are replacing exists, so an accepted + can't harm */ +- transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE); ++ transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE); + /* Do something more clever here */ + ast_channel_unlock(c); + sip_pvt_unlock(p->refer->refer_call); +@@ -18192,7 +18815,7 @@ + Targetcall is not touched by the masq */ + + /* Answer the incoming call and set channel to UP state */ +- transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE); ++ transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE); + + ast_setstate(c, AST_STATE_UP); + +@@ -18592,10 +19215,11 @@ + return 0; + } + +-/*! \brief Handle incoming INVITE request +-\note If the INVITE has a Replaces header, it is part of an ++/*! ++ * \brief Handle incoming INVITE request ++ * \note If the INVITE has a Replaces header, it is part of an + * attended transfer. If so, we do not go through the dial +- * plan but tries to find the active call and masquerade ++ * plan but try to find the active call and masquerade + * into it + */ + static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock) +@@ -18704,9 +19328,10 @@ + return transmit_invite(p, SIP_INVITE, 1, 3); + } + } +- ++ + if (!req->ignore && p->pendinginvite) { + /* We already have a pending invite. Sorry. You are on hold. */ ++ p->glareinvite = seqno; /* must hold on to this seqno to process ack and retransmit correctly */ + transmit_response_reliable(p, "491 Request Pending", req); + ast_debug(1, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid); + /* Don't destroy dialog here */ +@@ -18865,6 +19490,16 @@ + parse_ok_contact(p, req); + } else { /* Re-invite on existing call */ + ast_clear_flag(&p->flags[0], SIP_OUTGOING); /* This is now an inbound dialog */ ++ if (get_rpid(p, req)) { ++ struct ast_party_connected_line connected; ++ ++ ast_party_connected_line_init(&connected); ++ connected.id.number = (char *) p->cid_num; ++ connected.id.name = (char *) p->cid_name; ++ connected.id.number_presentation = p->callingpres; ++ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_queue_connected_line_update(p->owner, &connected); ++ } + /* Handle SDP here if we already have an owner */ + if (find_sdp(req)) { + if (process_sdp(p, req, SDP_T38_INITIATE)) { +@@ -18887,6 +19522,7 @@ + if (!p->lastinvite && !req->ignore && !p->owner) { + /* This is a new invite */ + /* Handle authentication if this is our first invite */ ++ struct ast_party_redirecting redirecting = {{0,},}; + res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin); + if (res == AUTH_CHALLENGE_SENT) { + p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */ +@@ -18895,7 +19531,7 @@ + if (res < 0) { /* Something failed in authentication */ + if (res == AUTH_FAKE_AUTH) { + ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From")); +- transmit_fake_auth_response(p, req, XMIT_RELIABLE); ++ transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE); + } else { + ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From")); + transmit_response_reliable(p, "403 Forbidden", req); +@@ -18944,13 +19580,13 @@ + return 0; + } + gotdest = get_destination(p, NULL); /* Get destination right away */ +- get_rdnis(p, NULL); /* Get redirect information */ ++ change_redirecting_information(p, req, &redirecting, FALSE); /*Will return immediately if no Diversion header is present */ + extract_uri(p, req); /* Get the Contact URI */ + build_contact(p); /* Build our contact header */ + + if (p->rtp) { +- ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); +- ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ++ ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); ++ ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + } + + if (!replace_id && gotdest) { /* No matching extension found */ +@@ -18988,9 +19624,11 @@ + if (c) { + /* Pre-lock the call */ + ast_channel_lock(c); ++ ast_channel_set_redirecting(c, &redirecting); + } + } + } else { ++ struct ast_party_redirecting redirecting = {{0,},}; + if (sipdebug) { + if (!req->ignore) + ast_debug(2, "Got a SIP re-invite for call %s\n", p->callid); +@@ -19000,6 +19638,10 @@ + if (!req->ignore) + reinvite = 1; + c = p->owner; ++ change_redirecting_information(p, req, &redirecting, FALSE); /*Will return immediately if no Diversion header is present */ ++ if (c) { ++ ast_channel_set_redirecting(c, &redirecting); ++ } + } + + /* Session-Timers */ +@@ -19210,7 +19852,6 @@ + c->hangupcause = AST_CAUSE_CALL_REJECTED; + } else { + sip_pvt_unlock(p); +- ast_setstate(c, AST_STATE_DOWN); + c->hangupcause = AST_CAUSE_NORMAL_CLEARING; + } + p->invitestate = INV_COMPLETED; +@@ -19240,7 +19881,7 @@ + } else if (p->t38.state == T38_DISABLED) { + /* If this is not a re-invite or something to ignore - it's critical */ + ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); +- transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE:TRUE); ++ transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE : TRUE, FALSE); + } + + p->invitestate = INV_TERMINATED; +@@ -19358,6 +19999,8 @@ + else + ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); + } else { ++ struct ast_party_connected_line connected_caller; ++ + /* Transfer succeeded! */ + const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND"); + +@@ -19372,6 +20015,45 @@ + ast_debug(1, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name); + ast_channel_unlock(targetcall_pvt->owner); + } ++ ++ if (target.chan2) { ++ if (current->chan2) { ++ /* Tell each of the other channels to whom they are now connected */ ++ ast_channel_lock(current->chan2); ++ ast_connected_line_copy_from_caller(&connected_caller, ¤t->chan2->cid); ++ ast_channel_unlock(current->chan2); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_update_connected_line(target.chan2, &connected_caller); ++ ast_channel_lock(target.chan2); ++ ast_connected_line_copy_from_caller(&connected_caller, &target.chan2->cid); ++ ast_channel_unlock(target.chan2); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_update_connected_line(current->chan2, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ } ++ } else { ++ /* Notify the first other party that they are connected to someone else assuming that target.chan1 ++ has progressed far enough through the dialplan to have it's called party information set. */ ++ if (current->chan2) { ++ ast_channel_lock(target.chan1); ++ ast_party_connected_line_copy(&connected_caller, &target.chan1->connected); ++ ast_channel_unlock(target.chan1); ++ connected_caller = target.chan1->connected; ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_update_connected_line(current->chan2, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ } ++ ++ /* We can't indicate to the called channel directly so we force the masquerade to complete ++ and queue and update to be read and passed-through */ ++ ast_channel_lock(target.chan1); ++ ast_do_masquerade(target.chan1); ++ ast_channel_unlock(target.chan1); ++ ++ ast_party_connected_line_collect_caller(&connected_caller, &target.chan1->cid); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_queue_connected_line_update(target.chan1, &connected_caller); ++ } + } + if (targetcall_pvt) + ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt"); +@@ -19769,7 +20451,7 @@ + static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen) + { + struct sip_pvt *p = chan->tech_pvt; +- char *all = "", *parse = ast_strdupa(preparse); ++ char *parse = ast_strdupa(preparse); + int res = 0; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(param); +@@ -19807,61 +20489,70 @@ + args.type = "audio"; + + if (!strcasecmp(args.type, "audio")) +- ast_rtp_get_peer(p->rtp, &sin); ++ ast_rtp_instance_get_remote_address(p->rtp, &sin); + else if (!strcasecmp(args.type, "video")) +- ast_rtp_get_peer(p->vrtp, &sin); ++ ast_rtp_instance_get_remote_address(p->vrtp, &sin); + else if (!strcasecmp(args.type, "text")) +- ast_rtp_get_peer(p->trtp, &sin); ++ ast_rtp_instance_get_remote_address(p->trtp, &sin); + else + return -1; + + snprintf(buf, buflen, "%s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } else if (!strcasecmp(args.param, "rtpqos")) { +- struct ast_rtp_quality qos; +- struct ast_rtp *rtp = p->rtp; +- +- memset(&qos, 0, sizeof(qos)); ++ struct ast_rtp_instance *rtp = NULL; + +- if (ast_strlen_zero(args.type)) ++ if (ast_strlen_zero(args.type)) { + args.type = "audio"; +- if (ast_strlen_zero(args.field)) +- args.field = "all"; +- +- if (!strcasecmp(args.type, "AUDIO")) { +- all = ast_rtp_get_quality(rtp = p->rtp, &qos, RTPQOS_SUMMARY); +- } else if (!strcasecmp(args.type, "VIDEO")) { +- all = ast_rtp_get_quality(rtp = p->vrtp, &qos, RTPQOS_SUMMARY); +- } else if (!strcasecmp(args.type, "TEXT")) { +- all = ast_rtp_get_quality(rtp = p->trtp, &qos, RTPQOS_SUMMARY); ++ } ++ ++ if (!strcasecmp(args.type, "audio")) { ++ rtp = p->rtp; ++ } else if (!strcasecmp(args.type, "video")) { ++ rtp = p->vrtp; ++ } else if (!strcasecmp(args.type, "text")) { ++ rtp = p->trtp; + } else { +- return -1; ++ return -1; + } +- +- if (!strcasecmp(args.field, "local_ssrc")) +- snprintf(buf, buflen, "%u", qos.local_ssrc); +- else if (!strcasecmp(args.field, "local_lostpackets")) +- snprintf(buf, buflen, "%u", qos.local_lostpackets); +- else if (!strcasecmp(args.field, "local_jitter")) +- snprintf(buf, buflen, "%.0f", qos.local_jitter * 1000.0); +- else if (!strcasecmp(args.field, "local_count")) +- snprintf(buf, buflen, "%u", qos.local_count); +- else if (!strcasecmp(args.field, "remote_ssrc")) +- snprintf(buf, buflen, "%u", qos.remote_ssrc); +- else if (!strcasecmp(args.field, "remote_lostpackets")) +- snprintf(buf, buflen, "%u", qos.remote_lostpackets); +- else if (!strcasecmp(args.field, "remote_jitter")) +- snprintf(buf, buflen, "%.0f", qos.remote_jitter * 1000.0); +- else if (!strcasecmp(args.field, "remote_count")) +- snprintf(buf, buflen, "%u", qos.remote_count); +- else if (!strcasecmp(args.field, "rtt")) +- snprintf(buf, buflen, "%.0f", qos.rtt * 1000.0); +- else if (!strcasecmp(args.field, "all")) +- ast_copy_string(buf, all, buflen); +- else if (!ast_rtp_get_qos(rtp, args.field, buf, buflen)) +- ; +- else { +- ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); +- return -1; ++ ++ if (ast_strlen_zero(args.field) || !strcasecmp(args.field, "all")) { ++ char quality_buf[AST_MAX_USER_FIELD], *quality; ++ ++ if (!(quality = ast_rtp_instance_get_quality(rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { ++ return -1; ++ } ++ ++ ast_copy_string(buf, quality_buf, buflen); ++ return res; ++ } else { ++ struct ast_rtp_instance_stats stats; ++ ++ if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) { ++ return -1; ++ } ++ ++ if (!strcasecmp(args.field, "local_ssrc")) { ++ snprintf(buf, buflen, "%u", stats.local_ssrc); ++ } else if (!strcasecmp(args.field, "local_lostpackets")) { ++ snprintf(buf, buflen, "%u", stats.rxploss); ++ } else if (!strcasecmp(args.field, "local_jitter")) { ++ snprintf(buf, buflen, "%u", stats.rxjitter); ++ } else if (!strcasecmp(args.field, "local_count")) { ++ snprintf(buf, buflen, "%u", stats.rxcount); ++ } else if (!strcasecmp(args.field, "remote_ssrc")) { ++ snprintf(buf, buflen, "%u", stats.remote_ssrc); ++ } else if (!strcasecmp(args.field, "remote_lostpackets")) { ++ snprintf(buf, buflen, "%u", stats.txploss); ++ } else if (!strcasecmp(args.field, "remote_jitter")) { ++ snprintf(buf, buflen, "%u", stats.txjitter); ++ } else if (!strcasecmp(args.field, "remote_count")) { ++ snprintf(buf, buflen, "%u", stats.txcount); ++ } else if (!strcasecmp(args.field, "rtt")) { ++ snprintf(buf, buflen, "%u", stats.rtt); ++ } else { ++ ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); ++ return -1; ++ } + } + } else { + res = -1; +@@ -19893,53 +20584,53 @@ + + /* Get RTCP quality before end of call */ + if (p->do_history || p->owner) { ++ char quality_buf[AST_MAX_USER_FIELD], *quality; + struct ast_channel *bridge = p->owner ? ast_bridged_channel(p->owner) : NULL; +- char *videoqos, *textqos; + +- if (p->rtp) { ++ if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { +- char *audioqos, +- *audioqos_jitter, +- *audioqos_loss, +- *audioqos_rtt; ++ append_history(p, "RTCPaudio", "Quality:%s", quality); + +- audioqos = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_SUMMARY); +- audioqos_jitter = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_JITTER); +- audioqos_loss = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_LOSS); +- audioqos_rtt = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_RTT); ++ if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) { ++ append_history(p, "RTCPaudioJitter", "Quality:%s", quality); ++ } ++ if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) { ++ append_history(p, "RTCPaudioLoss", "Quality:%s", quality); ++ } ++ if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) { ++ append_history(p, "RTCPaudioRTT", "Quality:%s", quality); ++ } ++ } + +- append_history(p, "RTCPaudio", "Quality:%s", audioqos); +- append_history(p, "RTCPaudioJitter", "Quality:%s", audioqos_jitter); +- append_history(p, "RTCPaudioLoss", "Quality:%s", audioqos_loss); +- append_history(p, "RTCPaudioRTT", "Quality:%s", audioqos_rtt); +- } +- + if (p->owner) { +- ast_rtp_set_vars(p->owner, p->rtp); ++ ast_rtp_instance_set_stats_vars(p->owner, p->rtp); + } ++ + } + + if (bridge) { + struct sip_pvt *q = bridge->tech_pvt; + +- if (IS_SIP_TECH(bridge->tech) && q->rtp) +- ast_rtp_set_vars(bridge, q->rtp); ++ if (IS_SIP_TECH(bridge->tech) && q->rtp) { ++ ast_rtp_instance_set_stats_vars(bridge, q->rtp); ++ } + } + +- if (p->vrtp) { +- videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); +- if (p->do_history) +- append_history(p, "RTCPvideo", "Quality:%s", videoqos); +- if (p->owner) +- pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos); ++ if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { ++ if (p->do_history) { ++ append_history(p, "RTCPvideo", "Quality:%s", quality); ++ } ++ if (p->owner) { ++ pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", quality); ++ } + } +- +- if (p->trtp) { +- textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); +- if (p->do_history) +- append_history(p, "RTCPtext", "Quality:%s", textqos); +- if (p->owner) +- pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", textqos); ++ if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { ++ if (p->do_history) { ++ append_history(p, "RTCPtext", "Quality:%s", quality); ++ } ++ if (p->owner) { ++ pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", quality); ++ } + } + } + +@@ -20092,7 +20783,7 @@ + if (res < 0) { + if (res == AUTH_FAKE_AUTH) { + ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From")); +- transmit_fake_auth_response(p, req, XMIT_UNRELIABLE); ++ transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE); + } else { + ast_log(LOG_NOTICE, "Failed to authenticate device %s for SUBSCRIBE\n", get_header(req, "From")); + transmit_response_reliable(p, "403 Forbidden", req); +@@ -20436,10 +21127,10 @@ + */ + int ret = 0; + +- if (p->ocseq < seqno && seqno != p->lastnoninvite) { ++ if (p->ocseq < seqno && p->lastinvite != seqno && p->lastnoninvite != seqno) { + ast_debug(1, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq); + ret = -1; +- } else if (p->ocseq != seqno && seqno != p->lastnoninvite) { ++ } else if (p->ocseq != seqno && p->lastinvite != seqno && p->lastnoninvite != seqno) { + /* ignore means "don't do anything with it" but still have to + * respond appropriately. + * But in this case this is a response already, so we really +@@ -20580,8 +21271,12 @@ + if (find_sdp(req)) { + if (process_sdp(p, req, SDP_T38_NONE)) + return -1; +- } ++ } + check_pendings(p); ++ } else if (p->glareinvite == seqno) { ++ /* handle ack for the 491 pending sent for glareinvite */ ++ p->glareinvite = 0; ++ __sip_ack(p, seqno, 1, 0); + } + /* Got an ACK that we did not match. Ignore silently */ + if (!p->lastinvite && ast_strlen_zero(p->randdata)) { +@@ -21033,8 +21728,6 @@ + event = ast_event_get_cached(AST_EVENT_MWI, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"), +- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, +- AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS, + AST_EVENT_IE_END); + if (!event) + continue; +@@ -21126,15 +21819,8 @@ + return; + + /* If we have no timers set, return now */ +- if ((ast_rtp_get_rtpkeepalive(dialog->rtp) == 0) && (ast_rtp_get_rtptimeout(dialog->rtp) == 0) && (ast_rtp_get_rtpholdtimeout(dialog->rtp) == 0)) ++ if (!ast_rtp_instance_get_timeout(dialog->rtp) && !ast_rtp_instance_get_hold_timeout(dialog->rtp)) { + return; +- +- /* Check AUDIO RTP keepalives */ +- if (dialog->lastrtptx && ast_rtp_get_rtpkeepalive(dialog->rtp) && +- (t > dialog->lastrtptx + ast_rtp_get_rtpkeepalive(dialog->rtp))) { +- /* Need to send an empty RTP packet */ +- dialog->lastrtptx = time(NULL); +- ast_rtp_sendcng(dialog->rtp, 0); + } + + /*! \todo Check video RTP keepalives +@@ -21144,16 +21830,10 @@ + */ + + /* Check AUDIO RTP timers */ +- if (dialog->lastrtprx && (ast_rtp_get_rtptimeout(dialog->rtp) || ast_rtp_get_rtpholdtimeout(dialog->rtp)) && +- (t > dialog->lastrtprx + ast_rtp_get_rtptimeout(dialog->rtp))) { +- +- /* Might be a timeout now -- see if we're on hold */ +- struct sockaddr_in sin; +- ast_rtp_get_peer(dialog->rtp, &sin); +- if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (ast_rtp_get_rtpholdtimeout(dialog->rtp) && +- (t > dialog->lastrtprx + ast_rtp_get_rtpholdtimeout(dialog->rtp)))) { ++ if (dialog->lastrtprx && (ast_rtp_instance_get_timeout(dialog->rtp) || ast_rtp_instance_get_hold_timeout(dialog->rtp)) && (t > dialog->lastrtprx + ast_rtp_instance_get_timeout(dialog->rtp))) { ++ if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (ast_rtp_instance_get_hold_timeout(dialog->rtp) && (t > dialog->lastrtprx + ast_rtp_instance_get_hold_timeout(dialog->rtp)))) { + /* Needs a hangup */ +- if (ast_rtp_get_rtptimeout(dialog->rtp)) { ++ if (ast_rtp_instance_get_timeout(dialog->rtp)) { + while (dialog->owner && ast_channel_trylock(dialog->owner)) { + sip_pvt_unlock(dialog); + usleep(1); +@@ -21168,11 +21848,11 @@ + has already been requested and we don't want to + repeatedly request hangups + */ +- ast_rtp_set_rtptimeout(dialog->rtp, 0); +- ast_rtp_set_rtpholdtimeout(dialog->rtp, 0); ++ ast_rtp_instance_set_timeout(dialog->rtp, 0); ++ ast_rtp_instance_set_hold_timeout(dialog->rtp, 0); + if (dialog->vrtp) { +- ast_rtp_set_rtptimeout(dialog->vrtp, 0); +- ast_rtp_set_rtpholdtimeout(dialog->vrtp, 0); ++ ast_rtp_instance_set_timeout(dialog->vrtp, 0); ++ ast_rtp_instance_set_hold_timeout(dialog->vrtp, 0); + } + } + } +@@ -22024,7 +22704,16 @@ + ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID); + } else if (!strcasecmp(v->name, "sendrpid")) { + ast_set_flag(&mask[0], SIP_SENDRPID); +- ast_set2_flag(&flags[0], ast_true(v->value), SIP_SENDRPID); ++ if (!strcasecmp(v->value, "pai")) { ++ ast_set_flag(&flags[0], SIP_SENDRPID_PAI); ++ } else if (!strcasecmp(v->value, "rpid")) { ++ ast_set_flag(&flags[0], SIP_SENDRPID_RPID); ++ } else if (ast_true(v->value)) { ++ ast_set_flag(&flags[0], SIP_SENDRPID_RPID); ++ } ++ } else if (!strcasecmp(v->name, "rpid_immediate")) { ++ ast_set_flag(&mask[1], SIP_PAGE2_RPID_IMMEDIATE); ++ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RPID_IMMEDIATE); + } else if (!strcasecmp(v->name, "g726nonstandard")) { + ast_set_flag(&mask[0], SIP_G726_NONSTANDARD); + ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD); +@@ -22332,6 +23021,7 @@ + ast_string_field_set(peer, language, default_language); + ast_string_field_set(peer, mohinterpret, default_mohinterpret); + ast_string_field_set(peer, mohsuggest, default_mohsuggest); ++ ast_string_field_set(peer, engine, default_engine); + peer->addr.sin_family = AF_INET; + peer->defaddr.sin_family = AF_INET; + peer->capability = global_capability; +@@ -22671,6 +23361,8 @@ + ast_string_field_set(peer, mohsuggest, v->value); + } else if (!strcasecmp(v->name, "parkinglot")) { + ast_string_field_set(peer, parkinglot, v->value); ++ } else if (!strcasecmp(v->name, "rtp_engine")) { ++ ast_string_field_set(peer, engine, v->value); + } else if (!strcasecmp(v->name, "mailbox")) { + add_peer_mailboxes(peer, v->value); + } else if (!strcasecmp(v->name, "hasvoicemail")) { +@@ -22697,6 +23389,8 @@ + int error = ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, FALSE); + if (error) + ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value); ++ } else if (!strcasecmp(v->name, "preferred_codec_only")) { ++ ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_PREFERRED_CODEC); + } else if (!strcasecmp(v->name, "registertrying")) { + ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_REGISTERTRYING); + } else if (!strcasecmp(v->name, "autoframing")) { +@@ -23118,6 +23812,7 @@ + ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */ + ast_set_flag(&global_flags[0], SIP_NAT_RFC3581); /*!< NAT support if requested by device with rport */ + ast_set_flag(&global_flags[0], SIP_CAN_REINVITE); /*!< Allow re-invites */ ++ ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine)); + + /* Debugging settings, always default to off */ + dumphistory = FALSE; +@@ -23432,6 +24127,8 @@ + int error = ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, FALSE); + if (error) + ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value); ++ } else if (!strcasecmp(v->name, "preferred_codec_only")) { ++ ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_PREFERRED_CODEC); + } else if (!strcasecmp(v->name, "autoframing")) { + global_autoframing = ast_true(v->value); + } else if (!strcasecmp(v->name, "allowexternaldomains")) { +@@ -23856,156 +24553,176 @@ + return 0; + } + +-/*! \brief Returns null if we can't reinvite audio (part of RTP interface) */ +-static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { +- struct sip_pvt *p = NULL; +- enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; ++ struct sip_pvt *p = NULL; ++ enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL; + +- if (!(p = chan->tech_pvt)) +- return AST_RTP_GET_FAILED; +- +- sip_pvt_lock(p); +- if (!(p->rtp)) { +- sip_pvt_unlock(p); +- return AST_RTP_GET_FAILED; ++ if (!(p = chan->tech_pvt)) { ++ return AST_RTP_GLUE_RESULT_FORBID; + } + +- *rtp = p->rtp; ++ sip_pvt_lock(p); ++ if (!(p->rtp)) { ++ sip_pvt_unlock(p); ++ return AST_RTP_GLUE_RESULT_FORBID; ++ } + +- if (ast_rtp_getnat(*rtp) && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) +- res = AST_RTP_TRY_PARTIAL; +- else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) +- res = AST_RTP_TRY_NATIVE; +- else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) +- res = AST_RTP_GET_FAILED; ++ ao2_ref(p->rtp, +1); ++ *instance = p->rtp; + +- sip_pvt_unlock(p); ++ if (!ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { ++ res = AST_RTP_GLUE_RESULT_LOCAL; ++ } else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { ++ res = AST_RTP_GLUE_RESULT_REMOTE; ++ } else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) { ++ res = AST_RTP_GLUE_RESULT_FORBID; ++ } + +- return res; ++ sip_pvt_unlock(p); ++ ++ return res; + } + +-/*! \brief Returns null if we can't reinvite video (part of RTP interface) */ +-static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { + struct sip_pvt *p = NULL; +- enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; +- +- if (!(p = chan->tech_pvt)) +- return AST_RTP_GET_FAILED; ++ enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; + ++ if (!(p = chan->tech_pvt)) { ++ return AST_RTP_GLUE_RESULT_FORBID; ++ } ++ + sip_pvt_lock(p); + if (!(p->vrtp)) { + sip_pvt_unlock(p); +- return AST_RTP_GET_FAILED; ++ return AST_RTP_GLUE_RESULT_FORBID; + } + +- *rtp = p->vrtp; ++ ao2_ref(p->vrtp, +1); ++ *instance = p->vrtp; + +- if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) +- res = AST_RTP_TRY_NATIVE; ++ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { ++ res = AST_RTP_GLUE_RESULT_REMOTE; ++ } + + sip_pvt_unlock(p); + + return res; + } + +-/*! \brief Returns null if we can't reinvite text (part of RTP interface) */ +-static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { +- struct sip_pvt *p = NULL; +- enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; +- +- if (!(p = chan->tech_pvt)) +- return AST_RTP_GET_FAILED; ++ struct sip_pvt *p = NULL; ++ enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; + +- sip_pvt_lock(p); +- if (!(p->trtp)) { +- sip_pvt_unlock(p); +- return AST_RTP_GET_FAILED; +- } ++ if (!(p = chan->tech_pvt)) { ++ return AST_RTP_GLUE_RESULT_FORBID; ++ } + +- *rtp = p->trtp; ++ sip_pvt_lock(p); ++ if (!(p->trtp)) { ++ sip_pvt_unlock(p); ++ return AST_RTP_GLUE_RESULT_FORBID; ++ } + +- if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) +- res = AST_RTP_TRY_NATIVE; ++ ao2_ref(p->trtp, +1); ++ *instance = p->trtp; + +- sip_pvt_unlock(p); ++ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { ++ res = AST_RTP_GLUE_RESULT_REMOTE; ++ } + +- return res; ++ sip_pvt_unlock(p); ++ ++ return res; + } + +-/*! \brief Set the RTP peer for this call */ +-static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) ++static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active) + { +- struct sip_pvt *p; +- int changed = 0; ++ struct sip_pvt *p; ++ int changed = 0; + +- p = chan->tech_pvt; +- if (!p) +- return -1; ++ p = chan->tech_pvt; ++ if (!p) ++ return -1; + + /* Disable early RTP bridge */ + if (chan->_state != AST_STATE_UP && !sip_cfg.directrtpsetup) /* We are in early state */ + return 0; + +- sip_pvt_lock(p); +- if (p->alreadygone) { +- /* If we're destroyed, don't bother */ +- sip_pvt_unlock(p); +- return 0; +- } ++ sip_pvt_lock(p); ++ if (p->alreadygone) { ++ /* If we're destroyed, don't bother */ ++ sip_pvt_unlock(p); ++ return 0; ++ } + +- /* if this peer cannot handle reinvites of the media stream to devices +- that are known to be behind a NAT, then stop the process now ++ /* if this peer cannot handle reinvites of the media stream to devices ++ that are known to be behind a NAT, then stop the process now + */ +- if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { +- sip_pvt_unlock(p); +- return 0; +- } ++ if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { ++ sip_pvt_unlock(p); ++ return 0; ++ } + +- if (rtp) { +- changed |= ast_rtp_get_peer(rtp, &p->redirip); +- } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { +- memset(&p->redirip, 0, sizeof(p->redirip)); +- changed = 1; +- } +- if (vrtp) { +- changed |= ast_rtp_get_peer(vrtp, &p->vredirip); +- } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { +- memset(&p->vredirip, 0, sizeof(p->vredirip)); +- changed = 1; +- } +- if (trtp) { +- changed |= ast_rtp_get_peer(trtp, &p->tredirip); +- } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) { +- memset(&p->tredirip, 0, sizeof(p->tredirip)); +- changed = 1; +- } +- if (codecs && (p->redircodecs != codecs)) { +- p->redircodecs = codecs; +- changed = 1; +- } +- if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { +- if (chan->_state != AST_STATE_UP) { /* We are in early state */ +- if (p->do_history) +- append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); +- ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); +- } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ +- ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); +- transmit_reinvite_with_sdp(p, FALSE, FALSE); +- } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { +- ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); +- /* We have a pending Invite. Send re-invite when we're done with the invite */ +- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); +- } +- } +- /* Reset lastrtprx timer */ +- p->lastrtprx = p->lastrtptx = time(NULL); +- sip_pvt_unlock(p); +- return 0; ++ if (instance) { ++ changed |= ast_rtp_instance_get_remote_address(instance, &p->redirip); ++ } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { ++ memset(&p->redirip, 0, sizeof(p->redirip)); ++ changed = 1; ++ } ++ if (vinstance) { ++ changed |= ast_rtp_instance_get_remote_address(vinstance, &p->vredirip); ++ } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { ++ memset(&p->vredirip, 0, sizeof(p->vredirip)); ++ changed = 1; ++ } ++ if (tinstance) { ++ changed |= ast_rtp_instance_get_remote_address(tinstance, &p->tredirip); ++ } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) { ++ memset(&p->tredirip, 0, sizeof(p->tredirip)); ++ changed = 1; ++ } ++ if (codecs && (p->redircodecs != codecs)) { ++ p->redircodecs = codecs; ++ changed = 1; ++ } ++ if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { ++ if (chan->_state != AST_STATE_UP) { /* We are in early state */ ++ if (p->do_history) ++ append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); ++ ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); ++ } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ ++ ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); ++ transmit_reinvite_with_sdp(p, FALSE, FALSE); ++ } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { ++ ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); ++ /* We have a pending Invite. Send re-invite when we're done with the invite */ ++ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); ++ } ++ } ++ /* Reset lastrtprx timer */ ++ p->lastrtprx = p->lastrtptx = time(NULL); ++ sip_pvt_unlock(p); ++ return 0; + } + ++static int sip_get_codec(struct ast_channel *chan) ++{ ++ struct sip_pvt *p = chan->tech_pvt; ++ return p->peercapability ? p->peercapability : p->capability; ++} ++ ++static struct ast_rtp_glue sip_rtp_glue = { ++ .type = "SIP", ++ .get_rtp_info = sip_get_rtp_peer, ++ .get_vrtp_info = sip_get_vrtp_peer, ++ .get_trtp_info = sip_get_trtp_peer, ++ .update_peer = sip_set_rtp_peer, ++ .get_codec = sip_get_codec, ++}; ++ + static char *app_dtmfmode = "SIPDtmfMode"; + static char *app_sipaddheader = "SIPAddHeader"; + static char *app_sipremoveheader = "SIPRemoveHeader"; +@@ -24051,7 +24768,7 @@ + } else + ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n", mode); + if (p->rtp) +- ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); ++ ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { + if (!p->vad) { + p->vad = ast_dsp_new(); +@@ -24195,17 +24912,15 @@ + + sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Make sure we stop send this reply. */ + sip_alreadygone(p); ++ ++ if (p->owner) { ++ enum ast_control_transfer message = AST_TRANSFER_SUCCESS; ++ ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); ++ } + /* hangup here */ + return 0; + } + +-/*! \brief Return SIP UA's codec (part of the RTP interface) */ +-static int sip_get_codec(struct ast_channel *chan) +-{ +- struct sip_pvt *p = chan->tech_pvt; +- return p->jointcapability ? p->jointcapability : p->capability; +-} +- + /*! \brief Send a poke to all known peers */ + static void sip_poke_all_peers(void) + { +@@ -24413,12 +25128,12 @@ + /* Register all CLI functions for SIP */ + ast_cli_register_multiple(cli_sip, ARRAY_LEN(cli_sip)); + +- /* Tell the RTP subdriver that we're here */ +- ast_rtp_proto_register(&sip_rtp); +- + /* Tell the UDPTL subdriver that we're here */ + ast_udptl_proto_register(&sip_udptl); + ++ /* Tell the RTP engine about our RTP glue */ ++ ast_rtp_glue_register(&sip_rtp_glue); ++ + /* Register dialplan applications */ + ast_register_application_xml(app_dtmfmode, sip_dtmfmode); + ast_register_application_xml(app_sipaddheader, sip_addheader); +@@ -24489,12 +25204,12 @@ + /* Unregister CLI commands */ + ast_cli_unregister_multiple(cli_sip, ARRAY_LEN(cli_sip)); + +- /* Disconnect from the RTP subsystem */ +- ast_rtp_proto_unregister(&sip_rtp); +- + /* Disconnect from UDPTL */ + ast_udptl_proto_unregister(&sip_udptl); + ++ /* Disconnect from RTP engine */ ++ ast_rtp_glue_unregister(&sip_rtp_glue); ++ + /* Unregister AMI actions */ + ast_manager_unregister("SIPpeers"); + ast_manager_unregister("SIPshowpeer"); +Index: channels/chan_agent.c +=================================================================== +--- a/channels/chan_agent.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_agent.c (.../trunk) (revision 186562) +@@ -52,7 +52,6 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" + #include "asterisk/file.h" +@@ -758,8 +757,7 @@ + time(&p->start); + /* Call on this agent */ + ast_verb(3, "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name); +- ast_set_callerid(p->chan, +- ast->cid.cid_num, ast->cid.cid_name, NULL); ++ ast_channel_set_connected_line(p->chan, &ast->connected); + ast_channel_inherit_variables(ast, p->chan); + res = ast_call(p->chan, p->loginchan, 0); + CLEANUP(ast,p); +Index: channels/Makefile +=================================================================== +--- a/channels/Makefile (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/Makefile (.../trunk) (revision 186562) +@@ -57,25 +57,14 @@ + LIBS+= -lres_monitor.so -lres_features.so + endif + +-clean:: +- $(MAKE) -C misdn clean +- + ifneq ($(wildcard h323/Makefile.ast),) +- include h323/Makefile.ast ++include h323/Makefile.ast ++endif + H323LDFLAGS+=-Wl,--version-script=h323/noexport.map ++ + clean:: ++ $(MAKE) -C misdn clean + if [ -f h323/Makefile ]; then $(MAKE) -C h323 clean; fi +-else +-h323/libchanh323.a h323/Makefile.ast: +- $(CMD_PREFIX) $(MAKE) -C h323 +- $(CMD_PREFIX) rm -f ../main/asterisk +- $(CMD_PREFIX) echo "***************************************************************" +- $(CMD_PREFIX) echo +- $(CMD_PREFIX) echo "********** Re-run 'make' to pick up H.323 parameters **********" +- $(CMD_PREFIX) echo +- $(CMD_PREFIX) echo "***************************************************************" +- $(CMD_PREFIX) exit 1 +-endif + + dist-clean:: + rm -f h323/Makefile +@@ -107,4 +96,10 @@ + chan_usbradio.so: LIBS+=-lusb -lasound + chan_usbradio.so: ASTCFLAGS+=-DNDEBUG + ++h323/Makefile.ast: ++ $(CMD_PREFIX) $(MAKE) -C h323 Makefile.ast + ++h323/libchanh323.a: ++ $(CMD_PREFIX) $(MAKE) -C h323 libchanh323.a ++ ++ +Index: channels/chan_iax2.c +=================================================================== +--- a/channels/chan_iax2.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_iax2.c (.../trunk) (revision 186562) +@@ -88,6 +88,7 @@ + #include "asterisk/event.h" + #include "asterisk/astobj2.h" + #include "asterisk/timing.h" ++#include "asterisk/taskprocessor.h" + + #include "iax2.h" + #include "iax2-parser.h" +@@ -257,7 +258,7 @@ + + static int srvlookup = 0; + +-static int timingfd = -1; /* Timing file descriptor */ ++static struct ast_timer *timer; /* Timer for trunking */ + + static struct ast_netsock_list *netsock; + static struct ast_netsock_list *outsock; /*!< used if sourceaddress specified and bindaddr == INADDR_ANY */ +@@ -270,6 +271,9 @@ + /* T1, maybe ISDN */ + #define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \ + ~AST_FORMAT_SLINEAR & \ ++ ~AST_FORMAT_SLINEAR16 & \ ++ ~AST_FORMAT_SIREN7 & \ ++ ~AST_FORMAT_SIREN14 & \ + ~AST_FORMAT_ULAW & \ + ~AST_FORMAT_ALAW & \ + ~AST_FORMAT_G722) +@@ -376,7 +380,9 @@ + them before sending voice or anything else*/ + IAX_ALLOWFWDOWNLOAD = (1 << 26), /*!< Allow the FWDOWNL command? */ + IAX_IMMEDIATE = (1 << 27), /*!< Allow immediate off-hook to extension s */ +- IAX_FORCE_ENCRYPT = (1 << 28), /*!< Forces call encryption, if encryption not possible hangup */ ++ IAX_SENDCONNECTEDLINE = (1 << 28), /*!< Allow sending of connected line updates */ ++ IAX_RECVCONNECTEDLINE = (1 << 29), /*!< Allow receiving of connected line updates */ ++ IAX_FORCE_ENCRYPT = (1 << 30), /*!< Forces call encryption, if encryption not possible hangup */ + }; + + static int global_rtautoclear = 120; +@@ -755,9 +761,13 @@ + * \note The contents of this list do not need to be explicitly destroyed + * on module unload. This is because all active calls are destroyed, and + * all frames in this queue will get destroyed as a part of that process. ++ * ++ * \note Contents protected by the iaxsl[] locks + */ +-static AST_LIST_HEAD_STATIC(frame_queue, iax_frame); ++static AST_LIST_HEAD_NOLOCK(, iax_frame) frame_queue[IAX_MAX_CALLS]; + ++static struct ast_taskprocessor *transmit_processor; ++ + /*! + * This module will get much higher performance when doing a lot of + * user and peer lookups if the number of buckets is increased from 1. +@@ -1585,21 +1595,19 @@ + struct iax_frame *cur = NULL; + + ast_mutex_lock(&iaxsl[pvt->callno]); ++ + iax2_destroy_helper(pvt); +- ast_mutex_unlock(&iaxsl[pvt->callno]); + + /* Already gone */ +- ast_set_flag(pvt, IAX_ALREADYGONE); ++ ast_set_flag(pvt, IAX_ALREADYGONE); + +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_TRAVERSE(&frame_queue, cur, list) { ++ AST_LIST_TRAVERSE(&frame_queue[pvt->callno], cur, list) { + /* Cancel any pending transmissions */ +- if (cur->callno == pvt->callno) { +- cur->retries = -1; +- } ++ cur->retries = -1; + } +- AST_LIST_UNLOCK(&frame_queue); + ++ ast_mutex_unlock(&iaxsl[pvt->callno]); ++ + if (pvt->reg) { + pvt->reg->callno = 0; + } +@@ -1970,7 +1978,7 @@ + iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x); + iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x); + iaxs[x]->amaflags = amaflags; +- ast_copy_flags(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_FORCE_ENCRYPT); ++ ast_copy_flags(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); + ast_string_field_set(iaxs[x], accountcode, accountcode); + ast_string_field_set(iaxs[x], mohinterpret, mohinterpret); + ast_string_field_set(iaxs[x], mohsuggest, mohsuggest); +@@ -2620,17 +2628,16 @@ + f->retries = -1; + freeme = 1; + } +- if (callno) +- ast_mutex_unlock(&iaxsl[callno]); +- /* Do not try again */ ++ + if (freeme) { + /* Don't attempt delivery, just remove it from the queue */ +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_REMOVE(&frame_queue, f, list); +- AST_LIST_UNLOCK(&frame_queue); ++ AST_LIST_REMOVE(&frame_queue[callno], f, list); ++ ast_mutex_unlock(&iaxsl[callno]); + f->retrans = -1; + /* Free the IAX frame */ + iax2_frame_free(f); ++ } else if (callno) { ++ ast_mutex_unlock(&iaxsl[callno]); + } + } + +@@ -2920,7 +2927,7 @@ + static char *handle_cli_iax2_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) + { + struct iax_frame *cur; +- int cnt = 0, dead = 0, final = 0; ++ int cnt = 0, dead = 0, final = 0, i = 0; + + switch (cmd) { + case CLI_INIT: +@@ -2936,15 +2943,17 @@ + if (a->argc != 3) + return CLI_SHOWUSAGE; + +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_TRAVERSE(&frame_queue, cur, list) { +- if (cur->retries < 0) +- dead++; +- if (cur->final) +- final++; +- cnt++; ++ for (i = 0; i < ARRAY_LEN(frame_queue); i++) { ++ ast_mutex_lock(&iaxsl[i]); ++ AST_LIST_TRAVERSE(&frame_queue[i], cur, list) { ++ if (cur->retries < 0) ++ dead++; ++ if (cur->final) ++ final++; ++ cnt++; ++ } ++ ast_mutex_unlock(&iaxsl[i]); + } +- AST_LIST_UNLOCK(&frame_queue); + + ast_cli(a->fd, " IAX Statistics\n"); + ast_cli(a->fd, "---------------------\n"); +@@ -3301,23 +3310,39 @@ + return 0; + } + +-static int iax2_transmit(struct iax_frame *fr) ++static int transmit_frame(void *data) + { +- /* Lock the queue and place this packet at the end */ +- /* By setting this to 0, the network thread will send it for us, and +- queue retransmission if necessary */ +- fr->sentyet = 0; +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_INSERT_TAIL(&frame_queue, fr, list); +- AST_LIST_UNLOCK(&frame_queue); +- /* Wake up the network and scheduler thread */ +- if (netthreadid != AST_PTHREADT_NULL) +- pthread_kill(netthreadid, SIGURG); +- ast_sched_thread_poke(sched); ++ struct iax_frame *fr = data; ++ ++ ast_mutex_lock(&iaxsl[fr->callno]); ++ ++ fr->sentyet = 1; ++ ++ if (iaxs[fr->callno]) { ++ send_packet(fr); ++ } ++ ++ if (fr->retries < 0) { ++ ast_mutex_unlock(&iaxsl[fr->callno]); ++ /* No retransmit requested */ ++ iax_frame_free(fr); ++ } else { ++ /* We need reliable delivery. Schedule a retransmission */ ++ AST_LIST_INSERT_TAIL(&frame_queue[fr->callno], fr, list); ++ ast_mutex_unlock(&iaxsl[fr->callno]); ++ fr->retries++; ++ fr->retrans = iax2_sched_add(sched, fr->retrytime, attempt_transmit, fr); ++ } ++ + return 0; + } + ++static int iax2_transmit(struct iax_frame *fr) ++{ ++ fr->sentyet = 0; + ++ return ast_taskprocessor_push(transmit_processor, transmit_frame, fr); ++} + + static int iax2_digit_begin(struct ast_channel *c, char digit) + { +@@ -3578,6 +3603,8 @@ + char outkey[80]; + char timezone[80]; + char prefs[32]; ++ char cid_num[80]; ++ char cid_name[80]; + char context[AST_MAX_CONTEXT]; + char peercontext[AST_MAX_CONTEXT]; + char mohinterpret[MAX_MUSICCLASS]; +@@ -3621,7 +3648,7 @@ + if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0))) + goto return_unref; + +- ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_FORCE_ENCRYPT); ++ ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); + cai->maxtime = peer->maxms; + cai->capability = peer->capability; + cai->encmethods = peer->encmethods; +@@ -3639,6 +3666,8 @@ + ast_copy_string(cai->username, peer->username, sizeof(cai->username)); + ast_copy_string(cai->timezone, peer->zonetag, sizeof(cai->timezone)); + ast_copy_string(cai->outkey, peer->outkey, sizeof(cai->outkey)); ++ ast_copy_string(cai->cid_num, peer->cid_num, sizeof(cai->cid_num)); ++ ast_copy_string(cai->cid_name, peer->cid_name, sizeof(cai->cid_name)); + ast_copy_string(cai->mohinterpret, peer->mohinterpret, sizeof(cai->mohinterpret)); + ast_copy_string(cai->mohsuggest, peer->mohsuggest, sizeof(cai->mohsuggest)); + if (ast_strlen_zero(peer->dbsecret)) { +@@ -3847,8 +3876,8 @@ + if (pds.port) + sin.sin_port = htons(atoi(pds.port)); + +- l = c->cid.cid_num; +- n = c->cid.cid_name; ++ l = c->connected.id.number; ++ n = c->connected.id.name; + + /* Now build request */ + memset(&ied, 0, sizeof(ied)); +@@ -3865,21 +3894,21 @@ + + if (l) { + iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, l); +- iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres); ++ iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->connected.id.number_presentation); + } else { + if (n) +- iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres); ++ iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->connected.id.number_presentation); + else + iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, AST_PRES_NUMBER_NOT_AVAILABLE); + } + +- iax_ie_append_byte(&ied, IAX_IE_CALLINGTON, c->cid.cid_ton); ++ iax_ie_append_byte(&ied, IAX_IE_CALLINGTON, c->connected.id.number_type); + iax_ie_append_short(&ied, IAX_IE_CALLINGTNS, c->cid.cid_tns); + + if (n) + iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, n); +- if (ast_test_flag(iaxs[callno], IAX_SENDANI) && c->cid.cid_ani) +- iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, c->cid.cid_ani); ++ if (ast_test_flag(iaxs[callno], IAX_SENDANI) && c->connected.ani) ++ iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, c->connected.ani); + + if (!ast_strlen_zero(c->language)) + iax_ie_append_str(&ied, IAX_IE_LANGUAGE, c->language); +@@ -4375,6 +4404,11 @@ + ast_moh_stop(c); + goto done; + } ++ break; ++ case AST_CONTROL_CONNECTED_LINE: ++ if (!ast_test_flag(pvt, IAX_SENDCONNECTEDLINE)) ++ goto done; ++ break; + } + + res = send_command(pvt, AST_FRAME_CONTROL, condition, 0, data, datalen, -1); +@@ -4390,6 +4424,7 @@ + unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); + struct iax_ie_data ied = { "", }; + char tmp[256], *context; ++ enum ast_control_transfer message = AST_TRANSFER_SUCCESS; + ast_copy_string(tmp, dest, sizeof(tmp)); + context = strchr(tmp, '@'); + if (context) { +@@ -4400,6 +4435,7 @@ + if (context) + iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context); + ast_debug(1, "Transferring '%s' to '%s'\n", c->name, dest); ++ ast_queue_control_data(c, AST_CONTROL_TRANSFER, &message, sizeof(message)); + return send_command_locked(callno, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1); + } + +@@ -6356,7 +6392,7 @@ + iaxs[callno]->amaflags = user->amaflags; + if (!ast_strlen_zero(user->language)) + ast_string_field_set(iaxs[callno], language, user->language); +- ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); ++ ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + /* Keep this check last */ + if (!ast_strlen_zero(user->dbsecret)) { + char *family, *key=NULL; +@@ -6682,15 +6718,20 @@ + ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */ + + return_unref: +- ast_string_field_set(iaxs[callno], peer, peer); +- /* Choose lowest expiry number */ +- if (expire && (expire < iaxs[callno]->expiry)) +- iaxs[callno]->expiry = expire; ++ if (iaxs[callno]) { ++ ast_string_field_set(iaxs[callno], peer, peer); + ++ /* Choose lowest expiry number */ ++ if (expire && (expire < iaxs[callno]->expiry)) { ++ iaxs[callno]->expiry = expire; ++ } ++ } ++ + res = 0; + +- if (p) ++ if (p) { + peer_unref(p); ++ } + + return res; + } +@@ -7007,16 +7048,13 @@ + pvt->lastsent = 0; + pvt->nextpred = 0; + pvt->pingtime = DEFAULT_RETRY_TIME; +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_TRAVERSE(&frame_queue, cur, list) { ++ AST_LIST_TRAVERSE(&frame_queue[callno], cur, list) { + /* We must cancel any packets that would have been transmitted + because now we're talking to someone new. It's okay, they + were transmitted to someone that didn't care anyway. */ +- if (callno == cur->callno) +- cur->retries = -1; ++ cur->retries = -1; + } +- AST_LIST_UNLOCK(&frame_queue); +- return 0; ++ return 0; + } + + /*! \brief Acknowledgment received for OUR registration */ +@@ -7386,8 +7424,6 @@ + event = ast_event_get_cached(AST_EVENT_MWI, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context, +- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, +- AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS, + AST_EVENT_IE_END); + if (event) { + new = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS); +@@ -7621,16 +7657,13 @@ + { + struct iax_frame *f; + +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_TRAVERSE(&frame_queue, f, list) { ++ AST_LIST_TRAVERSE(&frame_queue[callno], f, list) { + /* Send a copy immediately */ +- if ((f->callno == callno) && iaxs[f->callno] && +- ((unsigned char ) (f->oseqno - last) < 128) && +- (f->retries >= 0)) { ++ if (((unsigned char) (f->oseqno - last) < 128) && ++ (f->retries >= 0)) { + send_packet(f); + } + } +- AST_LIST_UNLOCK(&frame_queue); + } + + static void __iax2_poke_peer_s(const void *data) +@@ -7711,8 +7744,8 @@ + if (iaxtrunkdebug) + ast_verbose("Beginning trunk processing. Trunk queue ceiling is %d bytes per host\n", trunkmaxsize); + +- if (timingfd > -1) { +- ast_timer_ack(timingfd, 1); ++ if (timer) { ++ ast_timer_ack(timer, 1); + } + + /* For each peer that supports trunking... */ +@@ -8647,17 +8680,15 @@ + if (iaxdebug) + ast_debug(1, "Cancelling transmission of packet %d\n", x); + call_to_destroy = 0; +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_TRAVERSE(&frame_queue, cur, list) { ++ AST_LIST_TRAVERSE(&frame_queue[fr->callno], cur, list) { + /* If it's our call, and our timestamp, mark -1 retries */ +- if ((fr->callno == cur->callno) && (x == cur->oseqno)) { ++ if (x == cur->oseqno) { + cur->retries = -1; + /* Destroy call if this is the end */ + if (cur->final) + call_to_destroy = fr->callno; + } + } +- AST_LIST_UNLOCK(&frame_queue); + if (call_to_destroy) { + if (iaxdebug) + ast_debug(1, "Really destroying %d, having been acked on final message\n", call_to_destroy); +@@ -8899,13 +8930,12 @@ + case IAX_COMMAND_TXACC: + if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) { + /* Ack the packet with the given timestamp */ +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_TRAVERSE(&frame_queue, cur, list) { ++ AST_LIST_TRAVERSE(&frame_queue[fr->callno], cur, list) { + /* Cancel any outstanding txcnt's */ +- if ((fr->callno == cur->callno) && (cur->transfer)) ++ if (cur->transfer) { + cur->retries = -1; ++ } + } +- AST_LIST_UNLOCK(&frame_queue); + memset(&ied1, 0, sizeof(ied1)); + iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->callno); + send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1); +@@ -9804,13 +9834,12 @@ + break; + case IAX_COMMAND_TXMEDIA: + if (iaxs[fr->callno]->transferring == TRANSFER_READY) { +- AST_LIST_LOCK(&frame_queue); +- AST_LIST_TRAVERSE(&frame_queue, cur, list) { ++ AST_LIST_TRAVERSE(&frame_queue[fr->callno], cur, list) { + /* Cancel any outstanding frames and start anew */ +- if ((fr->callno == cur->callno) && (cur->transfer)) ++ if (cur->transfer) { + cur->retries = -1; ++ } + } +- AST_LIST_UNLOCK(&frame_queue); + /* Start sending our media to the transfer address, but otherwise leave the call as-is */ + iaxs[fr->callno]->transferring = TRANSFER_MEDIAPASS; + } +@@ -9936,6 +9965,31 @@ + ast_mutex_unlock(&iaxsl[fr->callno]); + return 1; + } ++ /* Don't allow connected line updates unless we are configured to */ ++ if (f.frametype == AST_FRAME_CONTROL && f.subclass == AST_CONTROL_CONNECTED_LINE) { ++ struct ast_party_connected_line connected; ++ ++ if (!ast_test_flag(iaxs[fr->callno], IAX_RECVCONNECTEDLINE)) { ++ ast_mutex_unlock(&iaxsl[fr->callno]); ++ return 1; ++ } ++ ++ /* Initialize defaults */ ++ ast_party_connected_line_init(&connected); ++ connected.id.number_presentation = iaxs[fr->callno]->calling_pres; ++ ++ if (!ast_connected_line_parse_data(f.data.ptr, f.datalen, &connected)) { ++ ast_string_field_set(iaxs[fr->callno], cid_num, connected.id.number); ++ ast_string_field_set(iaxs[fr->callno], cid_name, connected.id.name); ++ iaxs[fr->callno]->calling_pres = connected.id.number_presentation; ++ ++ if (iaxs[fr->callno]->owner) { ++ ast_set_callerid(iaxs[fr->callno]->owner, S_OR(connected.id.number, ""), S_OR(connected.id.name, ""), NULL); ++ iaxs[fr->callno]->owner->cid.cid_pres = connected.id.number_presentation; ++ } ++ } ++ ast_party_connected_line_free(&connected); ++ } + /* Common things */ + f.src = "IAX2"; + f.mallocd = 0; +@@ -10431,7 +10485,7 @@ + memset(&cai, 0, sizeof(cai)); + cai.capability = iax2_capability; + +- ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); ++ ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + + /* Populate our address from the given */ + if (create_addr(pds.peer, NULL, &sin, &cai)) { +@@ -10450,7 +10504,7 @@ + } + + /* If this is a trunk, update it now */ +- ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); ++ ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + if (ast_test_flag(&cai, IAX_TRUNK)) { + int new_callno; + if ((new_callno = make_trunk(callno, 1)) != -1) +@@ -10489,66 +10543,18 @@ + + static void *network_thread(void *ignore) + { +- /* Our job is simple: Send queued messages, retrying if necessary. Read frames +- from the network, and queue them for delivery to the channels */ +- int res, count, wakeup; +- struct iax_frame *f; ++ if (timer) { ++ ast_io_add(io, ast_timer_fd(timer), timing_read, AST_IO_IN | AST_IO_PRI, NULL); ++ } + +- if (timingfd > -1) +- ast_io_add(io, timingfd, timing_read, AST_IO_IN | AST_IO_PRI, NULL); +- +- for(;;) { ++ for (;;) { + pthread_testcancel(); ++ /* Wake up once a second just in case SIGURG was sent while ++ * we weren't in poll(), to make sure we don't hang when trying ++ * to unload. */ ++ ast_io_wait(io, 1000); ++ } + +- /* Go through the queue, sending messages which have not yet been +- sent, and scheduling retransmissions if appropriate */ +- AST_LIST_LOCK(&frame_queue); +- count = 0; +- wakeup = -1; +- AST_LIST_TRAVERSE_SAFE_BEGIN(&frame_queue, f, list) { +- if (f->sentyet) +- continue; +- +- /* Try to lock the pvt, if we can't... don't fret - defer it till later */ +- if (ast_mutex_trylock(&iaxsl[f->callno])) { +- wakeup = 1; +- continue; +- } +- +- f->sentyet = 1; +- +- if (iaxs[f->callno]) { +- send_packet(f); +- count++; +- } +- +- ast_mutex_unlock(&iaxsl[f->callno]); +- +- if (f->retries < 0) { +- /* This is not supposed to be retransmitted */ +- AST_LIST_REMOVE_CURRENT(list); +- /* Free the iax frame */ +- iax_frame_free(f); +- } else { +- /* We need reliable delivery. Schedule a retransmission */ +- f->retries++; +- f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f); +- } +- } +- AST_LIST_TRAVERSE_SAFE_END; +- AST_LIST_UNLOCK(&frame_queue); +- +- pthread_testcancel(); +- if (count >= 20) +- ast_debug(1, "chan_iax2: Sent %d queued outbound frames all at once\n", count); +- +- /* Now do the IO, and run scheduled tasks */ +- res = ast_io_wait(io, wakeup); +- if (res >= 0) { +- if (res >= 20) +- ast_debug(1, "chan_iax2: ast_io_wait ran %d I/Os all at once\n", res); +- } +- } + return NULL; + } + +@@ -10761,7 +10767,7 @@ + + if (peer) { + if (firstpass) { +- ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_FORCE_ENCRYPT); ++ ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); + peer->encmethods = iax2_encryption; + peer->adsi = adsi; + ast_string_field_set(peer,secret,""); +@@ -10803,7 +10809,7 @@ + ast_string_field_set(peer, dbsecret, v->value); + } else if (!strcasecmp(v->name, "trunk")) { + ast_set2_flag(peer, ast_true(v->value), IAX_TRUNK); +- if (ast_test_flag(peer, IAX_TRUNK) && (timingfd < 0)) { ++ if (ast_test_flag(peer, IAX_TRUNK) && !timer) { + ast_log(LOG_WARNING, "Unable to support trunking on peer '%s' without a timing interface\n", peer->name); + ast_clear_flag(peer, IAX_TRUNK); + } +@@ -10934,6 +10940,18 @@ + ast_string_field_set(peer, zonetag, v->value); + } else if (!strcasecmp(v->name, "adsi")) { + peer->adsi = ast_true(v->value); ++ } else if (!strcasecmp(v->name, "connectedline")) { ++ if (ast_true(v->value)) { ++ ast_set_flag(peer, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); ++ } else if (!strcasecmp(v->value, "send")) { ++ ast_clear_flag(peer, IAX_RECVCONNECTEDLINE); ++ ast_set_flag(peer, IAX_SENDCONNECTEDLINE); ++ } else if (!strcasecmp(v->value, "receive")) { ++ ast_clear_flag(peer, IAX_SENDCONNECTEDLINE); ++ ast_set_flag(peer, IAX_RECVCONNECTEDLINE); ++ } else { ++ ast_clear_flag(peer, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); ++ } + }/* else if (strcasecmp(v->name,"type")) */ + /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ + v = v->next; +@@ -11032,7 +11050,7 @@ + user->adsi = adsi; + ast_string_field_set(user, name, name); + ast_string_field_set(user, language, language); +- ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_FORCE_ENCRYPT); ++ ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); + ast_clear_flag(user, IAX_HASCALLERID); + ast_string_field_set(user, cid_name, ""); + ast_string_field_set(user, cid_num, ""); +@@ -11070,7 +11088,7 @@ + ast_parse_allow_disallow(&user->prefs, &user->capability,v->value, 0); + } else if (!strcasecmp(v->name, "trunk")) { + ast_set2_flag(user, ast_true(v->value), IAX_TRUNK); +- if (ast_test_flag(user, IAX_TRUNK) && (timingfd < 0)) { ++ if (ast_test_flag(user, IAX_TRUNK) && !timer) { + ast_log(LOG_WARNING, "Unable to support trunking on user '%s' without a timing interface\n", user->name); + ast_clear_flag(user, IAX_TRUNK); + } +@@ -11177,6 +11195,18 @@ + user->maxauthreq = 0; + } else if (!strcasecmp(v->name, "adsi")) { + user->adsi = ast_true(v->value); ++ } else if (!strcasecmp(v->name, "connectedline")) { ++ if (ast_true(v->value)) { ++ ast_set_flag(user, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); ++ } else if (!strcasecmp(v->value, "send")) { ++ ast_clear_flag(user, IAX_RECVCONNECTEDLINE); ++ ast_set_flag(user, IAX_SENDCONNECTEDLINE); ++ } else if (!strcasecmp(v->value, "receive")) { ++ ast_clear_flag(user, IAX_SENDCONNECTEDLINE); ++ ast_set_flag(user, IAX_RECVCONNECTEDLINE); ++ } else { ++ ast_clear_flag(user, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); ++ } + }/* else if (strcasecmp(v->name,"type")) */ + /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ + v = v->next; +@@ -11292,10 +11322,8 @@ + trunkmaxsize = MAX_TRUNKDATA; + amaflags = 0; + delayreject = 0; +- ast_clear_flag((&globalflags), IAX_NOTRANSFER); +- ast_clear_flag((&globalflags), IAX_TRANSFERMEDIA); +- ast_clear_flag((&globalflags), IAX_USEJITTERBUF); +- ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF); ++ ast_clear_flag((&globalflags), IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | ++ IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + delete_users(); + } + +@@ -11599,6 +11627,18 @@ + adsi = ast_true(v->value); + } else if (!strcasecmp(v->name, "srvlookup")) { + srvlookup = ast_true(v->value); ++ } else if (!strcasecmp(v->name, "connectedline")) { ++ if (ast_true(v->value)) { ++ ast_set_flag((&globalflags), IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); ++ } else if (!strcasecmp(v->value, "send")) { ++ ast_clear_flag((&globalflags), IAX_RECVCONNECTEDLINE); ++ ast_set_flag((&globalflags), IAX_SENDCONNECTEDLINE); ++ } else if (!strcasecmp(v->value, "receive")) { ++ ast_clear_flag((&globalflags), IAX_SENDCONNECTEDLINE); ++ ast_set_flag((&globalflags), IAX_RECVCONNECTEDLINE); ++ } else { ++ ast_clear_flag((&globalflags), IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); ++ } + } /*else if (strcasecmp(v->name,"type")) */ + /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ + v = v->next; +@@ -12420,19 +12460,14 @@ + struct ast_context *con; + int x; + +- /* Make sure threads do not hold shared resources when they are canceled */ +- +- /* Grab the sched lock resource to keep it away from threads about to die */ +- /* Cancel the network thread, close the net socket */ + if (netthreadid != AST_PTHREADT_NULL) { +- AST_LIST_LOCK(&frame_queue); + pthread_cancel(netthreadid); +- AST_LIST_UNLOCK(&frame_queue); ++ pthread_kill(netthreadid, SIGURG); + pthread_join(netthreadid, NULL); + } + + sched = ast_sched_thread_destroy(sched); +- ++ + /* Call for all threads to halt */ + AST_LIST_LOCK(&idle_list); + while ((thread = AST_LIST_REMOVE_HEAD(&idle_list, list))) +@@ -12480,9 +12515,10 @@ + ao2_ref(users, -1); + ao2_ref(iax_peercallno_pvts, -1); + ao2_ref(iax_transfercallno_pvts, -1); +- if (timingfd > -1) { +- ast_timer_close(timingfd); ++ if (timer) { ++ ast_timer_close(timer); + } ++ transmit_processor = ast_taskprocessor_unreference(transmit_processor); + + con = ast_context_find(regcontext); + if (con) +@@ -12551,19 +12587,23 @@ + struct iax2_registry *reg = NULL; + + peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb); +- if (!peers) ++ if (!peers) { + return AST_MODULE_LOAD_FAILURE; ++ } ++ + users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb); + if (!users) { + ao2_ref(peers, -1); + return AST_MODULE_LOAD_FAILURE; + } ++ + iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb); + if (!iax_peercallno_pvts) { + ao2_ref(peers, -1); + ao2_ref(users, -1); + return AST_MODULE_LOAD_FAILURE; + } ++ + iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb); + if (!iax_transfercallno_pvts) { + ao2_ref(peers, -1); +@@ -12571,6 +12611,16 @@ + ao2_ref(iax_peercallno_pvts, -1); + return AST_MODULE_LOAD_FAILURE; + } ++ ++ transmit_processor = ast_taskprocessor_get("iax2_transmit", TPS_REF_DEFAULT); ++ if (!transmit_processor) { ++ ao2_ref(peers, -1); ++ ao2_ref(users, -1); ++ ao2_ref(iax_peercallno_pvts, -1); ++ ao2_ref(iax_transfercallno_pvts, -1); ++ return AST_MODULE_LOAD_FAILURE; ++ } ++ + ast_custom_function_register(&iaxpeer_function); + ast_custom_function_register(&iaxvar_function); + +@@ -12621,9 +12671,8 @@ + ast_manager_register( "IAXnetstats", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_netstats, "Show IAX Netstats" ); + ast_manager_register( "IAXregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_registry, "Show IAX registrations"); + +- timingfd = ast_timer_open(); +- if (timingfd > -1) { +- ast_timer_set_rate(timingfd, trunkfreq); ++ if ((timer = ast_timer_open())) { ++ ast_timer_set_rate(timer, trunkfreq); + } + + if (set_config(config, 0) == -1) { +Index: channels/chan_misdn.c +=================================================================== +--- a/channels/chan_misdn.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_misdn.c (.../trunk) (revision 186562) +@@ -1,6 +1,6 @@ + /* + * Asterisk -- An open source telephony toolkit. +- * ++ * + * Copyright (C) 2004 - 2006, Christian Richter + * + * Christian Richter <crich@beronet.com> +@@ -47,6 +47,7 @@ + #include <signal.h> + #include <sys/file.h> + #include <semaphore.h> ++#include <ctype.h> + + #include "asterisk/channel.h" + #include "asterisk/config.h" +@@ -88,8 +89,6 @@ + ast_mutex_t mutexjb; + }; + +- +- + /*! \brief allocates the jb-structure and initialize the elements */ + struct misdn_jb *misdn_jb_init(int size, int upper_threshold); + +@@ -135,7 +134,6 @@ + MISDN_HUNGUP_FROM_AST, /*!< when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ + MISDN_HOLDED, /*!< when on hold */ + MISDN_HOLD_DISCONNECT, /*!< when on hold */ +- + }; + + #define ORG_AST 1 +@@ -143,13 +141,13 @@ + + struct hold_info { + /*! +- * \brief Logical port the channel call record is HOLDED on +- * because the B channel is no longer associated. ++ * \brief Logical port the channel call record is HOLDED on ++ * because the B channel is no longer associated. + */ + int port; + + /*! +- * \brief Original B channel number the HOLDED call was using. ++ * \brief Original B channel number the HOLDED call was using. + * \note Used only for debug display messages. + */ + int channel; +@@ -159,18 +157,18 @@ + * \brief Channel call record structure + */ + struct chan_list { +- /*! ++ /*! + * \brief The "allowed_bearers" string read in from /etc/asterisk/misdn.conf + */ + char allowed_bearers[BUFFERSIZE + 1]; +- +- /*! ++ ++ /*! + * \brief State of the channel + */ + enum misdn_chan_state state; + +- /*! +- * \brief TRUE if a hangup needs to be queued ++ /*! ++ * \brief TRUE if a hangup needs to be queued + * \note This is a debug flag only used to catch calls to hangup_chan() that are already hungup. + */ + int need_queue_hangup; +@@ -184,30 +182,30 @@ + * \brief TRUE if we could send an AST_CONTROL_BUSY if needed. + */ + int need_busy; +- ++ + /*! + * \brief Who originally created this channel. ORG_AST or ORG_MISDN + */ + int originator; + +- /*! ++ /*! + * \brief TRUE of we are not to respond immediately to a SETUP message. Check the dialplan first. + * \note The "noautorespond_on_setup" boolean read in from /etc/asterisk/misdn.conf + */ + int noautorespond_on_setup; +- ++ + int norxtone; /*!< Boolean assigned values but the value is not used. */ + + /*! + * \brief TRUE if we are not to generate tones (Playtones) + */ +- int notxtone; ++ int notxtone; + + /*! + * \brief TRUE if echo canceller is enabled. Value is toggled. + */ + int toggle_ec; +- ++ + /*! + * \brief TRUE if you want to send Tone Indications to an incoming + * ISDN channel on a TE Port. +@@ -222,8 +220,8 @@ + int ignore_dtmf; + + /*! +- * \brief Pipe file descriptor handles array. +- * Read from pipe[0], write to pipe[1] ++ * \brief Pipe file descriptor handles array. ++ * Read from pipe[0], write to pipe[1] + */ + int pipe[2]; + +@@ -282,31 +280,31 @@ + /*! + * \brief Allocated jitterbuffer controller + * \note misdn_jb_init() creates the jitterbuffer. +- * \note Must use misdn_jb_destroy() to clean up. ++ * \note Must use misdn_jb_destroy() to clean up. + */ + struct misdn_jb *jb; +- ++ + /*! + * \brief Allocated DSP controller + * \note ast_dsp_new() creates the DSP controller. +- * \note Must use ast_dsp_free() to clean up. ++ * \note Must use ast_dsp_free() to clean up. + */ + struct ast_dsp *dsp; + + /*! + * \brief Allocated audio frame sample translator + * \note ast_translator_build_path() creates the translator path. +- * \note Must use ast_translator_free_path() to clean up. ++ * \note Must use ast_translator_free_path() to clean up. + */ + struct ast_trans_pvt *trans; +- ++ + /*! + * \brief Associated Asterisk channel structure. + */ + struct ast_channel * ast; + + //int dummy; /* Not used */ +- ++ + /*! + * \brief Associated B channel structure. + */ +@@ -317,13 +315,13 @@ + */ + struct hold_info hold_info; + +- /*! +- * \brief From associated B channel: Layer 3 process ID +- * \note Used to find the HOLDED channel call record when retrieving a call. ++ /*! ++ * \brief From associated B channel: Layer 3 process ID ++ * \note Used to find the HOLDED channel call record when retrieving a call. + */ + unsigned int l3id; + +- /*! ++ /*! + * \brief From associated B channel: B Channel mISDN driver layer ID from mISDN_get_layerid() + * \note Used only for debug display messages. + */ +@@ -341,10 +339,6 @@ + */ + char mohinterpret[MAX_MUSICCLASS]; + +-#if 0 +- int zero_read_cnt; /* Not used */ +-#endif +- + /*! + * \brief Number of outgoing audio frames dropped since last debug gripe message. + */ +@@ -363,24 +357,24 @@ + int nttimeout; + + /*! +- * \brief Other channel call record PID +- * \note Value imported from Asterisk environment variable MISDN_PID ++ * \brief Other channel call record PID ++ * \note Value imported from Asterisk environment variable MISDN_PID + */ + int other_pid; + + /*! + * \brief Bridged other channel call record +- * \note Pointer set when other_pid imported from Asterisk environment ++ * \note Pointer set when other_pid imported from Asterisk environment + * variable MISDN_PID by either side. + */ + struct chan_list *other_ch; + + /*! + * \brief Tone zone sound used for dialtone generation. +- * \note Used as a boolean. Non-NULL to prod generation if enabled. ++ * \note Used as a boolean. Non-NULL to prod generation if enabled. + */ + struct ast_tone_zone_sound *ts; +- ++ + /*! + * \brief Enables overlap dialing for the set amount of seconds. (0 = Disabled) + * \note The "overlapdial" value read in from /etc/asterisk/misdn.conf +@@ -402,18 +396,10 @@ + */ + struct timeval overlap_tv; + +-#if 0 +- struct chan_list *peer; /* Not used */ +-#endif +- + /*! + * \brief Next channel call record in the list. + */ + struct chan_list *next; +-#if 0 +- struct chan_list *prev; /* Not used */ +- struct chan_list *first; /* Not used */ +-#endif + }; + + +@@ -424,13 +410,14 @@ + void import_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch); + static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame); + +-static struct robin_list { ++struct robin_list { + char *group; + int port; + int channel; + struct robin_list *next; + struct robin_list *prev; +-} *robin = NULL; ++}; ++static struct robin_list *robin = NULL; + + + static inline void free_robin_list_r(struct robin_list *r) +@@ -450,7 +437,7 @@ + robin = NULL; + } + +-static struct robin_list* get_robin_position(char *group) ++static struct robin_list* get_robin_position(char *group) + { + struct robin_list *new; + struct robin_list *iter = robin; +@@ -539,7 +526,7 @@ + + int chan_misdn_jb_empty(struct misdn_bchannel *bc, char *buf, int len); + +-void debug_numplan(int port, int numplan, char *type); ++void debug_numtype(int port, int numtype, char *type); + + int add_out_calls(int port); + int add_in_calls(int port); +@@ -558,31 +545,559 @@ + static struct chan_list * get_chan_by_ast(struct ast_channel *ast) + { + struct chan_list *tmp; +- ++ + for (tmp = cl_te; tmp; tmp = tmp->next) { + if (tmp->ast == ast) { + return tmp; + } + } +- ++ + return NULL; + } + + static struct chan_list * get_chan_by_ast_name(char *name) + { + struct chan_list *tmp; +- ++ + for (tmp = cl_te; tmp; tmp = tmp->next) { + if (tmp->ast && strcmp(tmp->ast->name, name) == 0) { + return tmp; + } + } +- ++ + return NULL; + } + ++/*! ++ * \internal ++ * \brief Convert the mISDN type of number code to a string ++ * ++ * \param number_type mISDN type of number code. ++ * ++ * \return The mISDN type of number code as a string ++ */ ++static const char *misdn_to_str_ton(enum mISDN_NUMBER_TYPE number_type) ++{ ++ const char *str; + ++ switch (number_type) { ++ default: ++ case NUMTYPE_UNKNOWN: ++ str = "Unknown"; ++ break; + ++ case NUMTYPE_INTERNATIONAL: ++ str = "International"; ++ break; ++ ++ case NUMTYPE_NATIONAL: ++ str = "National"; ++ break; ++ ++ case NUMTYPE_NETWORK_SPECIFIC: ++ str = "Network Specific"; ++ break; ++ ++ case NUMTYPE_SUBSCRIBER: ++ str = "Subscriber"; ++ break; ++ ++ case NUMTYPE_ABBREVIATED: ++ str = "Abbreviated"; ++ break; ++ } ++ ++ return str; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN type of number code to Asterisk type of number code ++ * ++ * \param number_type mISDN type of number code. ++ * ++ * \return Asterisk type of number code ++ */ ++static int misdn_to_ast_ton(enum mISDN_NUMBER_TYPE number_type) ++{ ++ int ast_number_type; ++ ++ switch (number_type) { ++ default: ++ case NUMTYPE_UNKNOWN: ++ ast_number_type = NUMTYPE_UNKNOWN << 4; ++ break; ++ ++ case NUMTYPE_INTERNATIONAL: ++ ast_number_type = NUMTYPE_INTERNATIONAL << 4; ++ break; ++ ++ case NUMTYPE_NATIONAL: ++ ast_number_type = NUMTYPE_NATIONAL << 4; ++ break; ++ ++ case NUMTYPE_NETWORK_SPECIFIC: ++ ast_number_type = NUMTYPE_NETWORK_SPECIFIC << 4; ++ break; ++ ++ case NUMTYPE_SUBSCRIBER: ++ ast_number_type = NUMTYPE_SUBSCRIBER << 4; ++ break; ++ ++ case NUMTYPE_ABBREVIATED: ++ ast_number_type = NUMTYPE_ABBREVIATED << 4; ++ break; ++ } ++ ++ return ast_number_type; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the Asterisk type of number code to mISDN type of number code ++ * ++ * \param ast_number_type Asterisk type of number code. ++ * ++ * \return mISDN type of number code ++ */ ++static enum mISDN_NUMBER_TYPE ast_to_misdn_ton(unsigned ast_number_type) ++{ ++ enum mISDN_NUMBER_TYPE number_type; ++ ++ switch ((ast_number_type >> 4) & 0x07) { ++ default: ++ case NUMTYPE_UNKNOWN: ++ number_type = NUMTYPE_UNKNOWN; ++ break; ++ ++ case NUMTYPE_INTERNATIONAL: ++ number_type = NUMTYPE_INTERNATIONAL; ++ break; ++ ++ case NUMTYPE_NATIONAL: ++ number_type = NUMTYPE_NATIONAL; ++ break; ++ ++ case NUMTYPE_NETWORK_SPECIFIC: ++ number_type = NUMTYPE_NETWORK_SPECIFIC; ++ break; ++ ++ case NUMTYPE_SUBSCRIBER: ++ number_type = NUMTYPE_SUBSCRIBER; ++ break; ++ ++ case NUMTYPE_ABBREVIATED: ++ number_type = NUMTYPE_ABBREVIATED; ++ break; ++ } ++ ++ return number_type; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN numbering plan code to a string ++ * ++ * \param number_plan mISDN numbering plan code. ++ * ++ * \return The mISDN numbering plan code as a string ++ */ ++static const char *misdn_to_str_plan(enum mISDN_NUMBER_PLAN number_plan) ++{ ++ const char *str; ++ ++ switch (number_plan) { ++ default: ++ case NUMPLAN_UNKNOWN: ++ str = "Unknown"; ++ break; ++ ++ case NUMPLAN_ISDN: ++ str = "ISDN"; ++ break; ++ ++ case NUMPLAN_DATA: ++ str = "Data"; ++ break; ++ ++ case NUMPLAN_TELEX: ++ str = "Telex"; ++ break; ++ ++ case NUMPLAN_NATIONAL: ++ str = "National"; ++ break; ++ ++ case NUMPLAN_PRIVATE: ++ str = "Private"; ++ break; ++ } ++ ++ return str; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN numbering plan code to Asterisk numbering plan code ++ * ++ * \param number_plan mISDN numbering plan code. ++ * ++ * \return Asterisk numbering plan code ++ */ ++static int misdn_to_ast_plan(enum mISDN_NUMBER_PLAN number_plan) ++{ ++ int ast_number_plan; ++ ++ switch (number_plan) { ++ default: ++ case NUMPLAN_UNKNOWN: ++ ast_number_plan = NUMPLAN_UNKNOWN; ++ break; ++ ++ case NUMPLAN_ISDN: ++ ast_number_plan = NUMPLAN_ISDN; ++ break; ++ ++ case NUMPLAN_DATA: ++ ast_number_plan = NUMPLAN_DATA; ++ break; ++ ++ case NUMPLAN_TELEX: ++ ast_number_plan = NUMPLAN_TELEX; ++ break; ++ ++ case NUMPLAN_NATIONAL: ++ ast_number_plan = NUMPLAN_NATIONAL; ++ break; ++ ++ case NUMPLAN_PRIVATE: ++ ast_number_plan = NUMPLAN_PRIVATE; ++ break; ++ } ++ ++ return ast_number_plan; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the Asterisk numbering plan code to mISDN numbering plan code ++ * ++ * \param ast_number_plan Asterisk numbering plan code. ++ * ++ * \return mISDN numbering plan code ++ */ ++static enum mISDN_NUMBER_PLAN ast_to_misdn_plan(unsigned ast_number_plan) ++{ ++ enum mISDN_NUMBER_PLAN number_plan; ++ ++ switch (ast_number_plan & 0x0F) { ++ default: ++ case NUMPLAN_UNKNOWN: ++ number_plan = NUMPLAN_UNKNOWN; ++ break; ++ ++ case NUMPLAN_ISDN: ++ number_plan = NUMPLAN_ISDN; ++ break; ++ ++ case NUMPLAN_DATA: ++ number_plan = NUMPLAN_DATA; ++ break; ++ ++ case NUMPLAN_TELEX: ++ number_plan = NUMPLAN_TELEX; ++ break; ++ ++ case NUMPLAN_NATIONAL: ++ number_plan = NUMPLAN_NATIONAL; ++ break; ++ ++ case NUMPLAN_PRIVATE: ++ number_plan = NUMPLAN_PRIVATE; ++ break; ++ } ++ ++ return number_plan; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN presentation code to a string ++ * ++ * \param presentation mISDN number presentation restriction code. ++ * ++ * \return The mISDN presentation code as a string ++ */ ++static const char *misdn_to_str_pres(int presentation) ++{ ++ const char *str; ++ ++ switch (presentation) { ++ case 0: ++ str = "Allowed"; ++ break; ++ ++ case 1: ++ str = "Restricted"; ++ break; ++ ++ case 2: ++ str = "Unavailable"; ++ break; ++ ++ default: ++ str = "Unknown"; ++ break; ++ } ++ ++ return str; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN presentation code to Asterisk presentation code ++ * ++ * \param presentation mISDN number presentation restriction code. ++ * ++ * \return Asterisk presentation code ++ */ ++static int misdn_to_ast_pres(int presentation) ++{ ++ switch (presentation) { ++ default: ++ case 0: ++ presentation = AST_PRES_ALLOWED; ++ break; ++ ++ case 1: ++ presentation = AST_PRES_RESTRICTED; ++ break; ++ ++ case 2: ++ presentation = AST_PRES_UNAVAILABLE; ++ break; ++ } ++ ++ return presentation; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the Asterisk presentation code to mISDN presentation code ++ * ++ * \param presentation Asterisk number presentation restriction code. ++ * ++ * \return mISDN presentation code ++ */ ++static int ast_to_misdn_pres(int presentation) ++{ ++ switch (presentation & AST_PRES_RESTRICTION) { ++ default: ++ case AST_PRES_ALLOWED: ++ presentation = 0; ++ break; ++ ++ case AST_PRES_RESTRICTED: ++ presentation = 1; ++ break; ++ ++ case AST_PRES_UNAVAILABLE: ++ presentation = 2; ++ break; ++ } ++ ++ return presentation; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN screening code to a string ++ * ++ * \param screening mISDN number screening code. ++ * ++ * \return The mISDN screening code as a string ++ */ ++static const char *misdn_to_str_screen(int screening) ++{ ++ const char *str; ++ ++ switch (screening) { ++ case 0: ++ str = "Unscreened"; ++ break; ++ ++ case 1: ++ str = "Passed Screen"; ++ break; ++ ++ case 2: ++ str = "Failed Screen"; ++ break; ++ ++ case 3: ++ str = "Network Number"; ++ break; ++ ++ default: ++ str = "Unknown"; ++ break; ++ } ++ ++ return str; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN screening code to Asterisk screening code ++ * ++ * \param screening mISDN number screening code. ++ * ++ * \return Asterisk screening code ++ */ ++static int misdn_to_ast_screen(int screening) ++{ ++ switch (screening) { ++ default: ++ case 0: ++ screening = AST_PRES_USER_NUMBER_UNSCREENED; ++ break; ++ ++ case 1: ++ screening = AST_PRES_USER_NUMBER_PASSED_SCREEN; ++ break; ++ ++ case 2: ++ screening = AST_PRES_USER_NUMBER_FAILED_SCREEN; ++ break; ++ ++ case 3: ++ screening = AST_PRES_NETWORK_NUMBER; ++ break; ++ } ++ ++ return screening; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the Asterisk screening code to mISDN screening code ++ * ++ * \param screening Asterisk number screening code. ++ * ++ * \return mISDN screening code ++ */ ++static int ast_to_misdn_screen(int screening) ++{ ++ switch (screening & AST_PRES_NUMBER_TYPE) { ++ default: ++ case AST_PRES_USER_NUMBER_UNSCREENED: ++ screening = 0; ++ break; ++ ++ case AST_PRES_USER_NUMBER_PASSED_SCREEN: ++ screening = 1; ++ break; ++ ++ case AST_PRES_USER_NUMBER_FAILED_SCREEN: ++ screening = 2; ++ break; ++ ++ case AST_PRES_NETWORK_NUMBER: ++ screening = 3; ++ break; ++ } ++ ++ return screening; ++} ++ ++/*! ++ * \internal ++ * \brief Convert Asterisk redirecting reason to mISDN redirecting reason code. ++ * ++ * \param ast Asterisk redirecting reason code. ++ * ++ * \return mISDN reason code ++ */ ++static enum mISDN_REDIRECTING_REASON ast_to_misdn_reason(const enum AST_REDIRECTING_REASON ast) ++{ ++ unsigned index; ++ ++ static const struct misdn_reasons { ++ enum AST_REDIRECTING_REASON ast; ++ enum mISDN_REDIRECTING_REASON q931; ++ } misdn_reason_table[] = { ++ /* *INDENT-OFF* */ ++ { AST_REDIRECTING_REASON_UNKNOWN, mISDN_REDIRECTING_REASON_UNKNOWN }, ++ { AST_REDIRECTING_REASON_USER_BUSY, mISDN_REDIRECTING_REASON_CALL_FWD_BUSY }, ++ { AST_REDIRECTING_REASON_NO_ANSWER, mISDN_REDIRECTING_REASON_NO_REPLY }, ++ { AST_REDIRECTING_REASON_UNAVAILABLE, mISDN_REDIRECTING_REASON_NO_REPLY }, ++ { AST_REDIRECTING_REASON_UNCONDITIONAL, mISDN_REDIRECTING_REASON_CALL_FWD }, ++ { AST_REDIRECTING_REASON_TIME_OF_DAY, mISDN_REDIRECTING_REASON_UNKNOWN }, ++ { AST_REDIRECTING_REASON_DO_NOT_DISTURB, mISDN_REDIRECTING_REASON_UNKNOWN }, ++ { AST_REDIRECTING_REASON_DEFLECTION, mISDN_REDIRECTING_REASON_DEFLECTION }, ++ { AST_REDIRECTING_REASON_FOLLOW_ME, mISDN_REDIRECTING_REASON_UNKNOWN }, ++ { AST_REDIRECTING_REASON_OUT_OF_ORDER, mISDN_REDIRECTING_REASON_OUT_OF_ORDER }, ++ { AST_REDIRECTING_REASON_AWAY, mISDN_REDIRECTING_REASON_UNKNOWN }, ++ { AST_REDIRECTING_REASON_CALL_FWD_DTE, mISDN_REDIRECTING_REASON_CALL_FWD_DTE } ++ /* *INDENT-ON* */ ++ }; ++ ++ for (index = 0; index < ARRAY_LEN(misdn_reason_table); ++index) { ++ if (misdn_reason_table[index].ast == ast) { ++ return misdn_reason_table[index].q931; ++ } ++ } ++ return mISDN_REDIRECTING_REASON_UNKNOWN; ++} ++ ++/*! ++ * \internal ++ * \brief Convert the mISDN redirecting reason to Asterisk redirecting reason code ++ * ++ * \param q931 mISDN redirecting reason code. ++ * ++ * \return Asterisk redirecting reason code ++ */ ++static enum AST_REDIRECTING_REASON misdn_to_ast_reason(const enum mISDN_REDIRECTING_REASON q931) ++{ ++ enum AST_REDIRECTING_REASON ast; ++ ++ switch (q931) { ++ default: ++ case mISDN_REDIRECTING_REASON_UNKNOWN: ++ ast = AST_REDIRECTING_REASON_UNKNOWN; ++ break; ++ ++ case mISDN_REDIRECTING_REASON_CALL_FWD_BUSY: ++ ast = AST_REDIRECTING_REASON_USER_BUSY; ++ break; ++ ++ case mISDN_REDIRECTING_REASON_NO_REPLY: ++ ast = AST_REDIRECTING_REASON_NO_ANSWER; ++ break; ++ ++ case mISDN_REDIRECTING_REASON_DEFLECTION: ++ ast = AST_REDIRECTING_REASON_DEFLECTION; ++ break; ++ ++ case mISDN_REDIRECTING_REASON_OUT_OF_ORDER: ++ ast = AST_REDIRECTING_REASON_OUT_OF_ORDER; ++ break; ++ ++ case mISDN_REDIRECTING_REASON_CALL_FWD_DTE: ++ ast = AST_REDIRECTING_REASON_CALL_FWD_DTE; ++ break; ++ ++ case mISDN_REDIRECTING_REASON_CALL_FWD: ++ ast = AST_REDIRECTING_REASON_UNCONDITIONAL; ++ break; ++ } ++ ++ return ast; ++} ++ ++ ++ + struct allowed_bearers { + char *name; /*!< Bearer capability name string used in /etc/misdn.conf allowed_bearers */ + char *display; /*!< Bearer capability displayable name */ +@@ -591,7 +1106,7 @@ + }; + + /* *INDENT-OFF* */ +-static const struct allowed_bearers allowed_bearers_array[]= { ++static const struct allowed_bearers allowed_bearers_array[] = { + /* Name, Displayable Name Bearer Capability, Deprecated */ + { "speech", "Speech", INFO_CAPABILITY_SPEECH, 0 }, + { "3_1khz", "3.1KHz Audio", INFO_CAPABILITY_AUDIO_3_1K, 0 }, +@@ -610,7 +1125,7 @@ + if (allowed_bearers_array[index].cap == cap) { + return allowed_bearers_array[index].display; + } +- } /* end for */ ++ } + + return "Unknown Bearer"; + } +@@ -669,10 +1184,10 @@ + } + } + +-static void print_bearer(struct misdn_bchannel *bc) ++static void print_bearer(struct misdn_bchannel *bc) + { + chan_misdn_log(2, bc->port, " --> Bearer: %s\n", bearer2str(bc->capability)); +- ++ + switch(bc->law) { + case INFO_CODEC_ALAW: + chan_misdn_log(2, bc->port, " --> Codec: Alaw\n"); +@@ -683,6 +1198,95 @@ + } + } + ++/*! ++ * \internal ++ * \brief Prefix a string to another string in place. ++ * ++ * \param str_prefix String to prefix to the main string. ++ * \param str_main String to get the prefix added to it. ++ * \param size Buffer size of the main string (Includes null terminator). ++ * ++ * \note The str_main buffer size must be greater than one. ++ * ++ * \return Nothing ++ */ ++static void misdn_prefix_string(const char *str_prefix, char *str_main, size_t size) ++{ ++ size_t len_over; ++ size_t len_total; ++ size_t len_main; ++ size_t len_prefix; ++ ++ len_prefix = strlen(str_prefix); ++ if (!len_prefix) { ++ /* There is no prefix to prepend. */ ++ return; ++ } ++ len_main = strlen(str_main); ++ len_total = len_prefix + len_main; ++ if (size <= len_total) { ++ /* We need to truncate since the buffer is too small. */ ++ len_over = len_total + 1 - size; ++ if (len_over <= len_main) { ++ len_main -= len_over; ++ } else { ++ len_over -= len_main; ++ len_main = 0; ++ len_prefix -= len_over; ++ } ++ } ++ if (len_main) { ++ memmove(str_main + len_prefix, str_main, len_main); ++ } ++ memcpy(str_main, str_prefix, len_prefix); ++ str_main[len_prefix + len_main] = '\0'; ++} ++ ++/*! ++ * \internal ++ * \brief Add a configured prefix to the given number. ++ * ++ * \param port Logical port number ++ * \param number_type Type-of-number passed in. ++ * \param number Given number string to add prefix ++ * \param size Buffer size number string occupies. ++ * ++ * \return Nothing ++ */ ++static void misdn_add_number_prefix(int port, enum mISDN_NUMBER_TYPE number_type, char *number, size_t size) ++{ ++ enum misdn_cfg_elements type_prefix; ++ char num_prefix[MISDN_MAX_NUMBER_LEN]; ++ ++ /* Get prefix string. */ ++ switch (number_type) { ++ case NUMTYPE_UNKNOWN: ++ type_prefix = MISDN_CFG_TON_PREFIX_UNKNOWN; ++ break; ++ case NUMTYPE_INTERNATIONAL: ++ type_prefix = MISDN_CFG_TON_PREFIX_INTERNATIONAL; ++ break; ++ case NUMTYPE_NATIONAL: ++ type_prefix = MISDN_CFG_TON_PREFIX_NATIONAL; ++ break; ++ case NUMTYPE_NETWORK_SPECIFIC: ++ type_prefix = MISDN_CFG_TON_PREFIX_NETWORK_SPECIFIC; ++ break; ++ case NUMTYPE_SUBSCRIBER: ++ type_prefix = MISDN_CFG_TON_PREFIX_SUBSCRIBER; ++ break; ++ case NUMTYPE_ABBREVIATED: ++ type_prefix = MISDN_CFG_TON_PREFIX_ABBREVIATED; ++ break; ++ default: ++ /* Type-of-number does not have a prefix that can be added. */ ++ return; ++ } ++ misdn_cfg_get(port, type_prefix, num_prefix, sizeof(num_prefix)); ++ ++ misdn_prefix_string(num_prefix, number, size); ++} ++ + static void export_aoc_vars(int originator, struct ast_channel *ast, struct misdn_bchannel *bc) + { + char buf[128]; +@@ -692,7 +1296,8 @@ + } + + if (originator == ORG_AST) { +- if (!(ast = ast_bridged_channel(ast))) { ++ ast = ast_bridged_channel(ast); ++ if (!ast) { + return; + } + } +@@ -739,14 +1344,15 @@ + default: + break; + } +- ++ + bc->AOCD_need_export = 0; + } + + /*************** Helpers END *************/ + + static void sighandler(int sig) +-{} ++{ ++} + + static void *misdn_tasks_thread_func(void *data) + { +@@ -758,7 +1364,7 @@ + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGUSR1); + sigaction(SIGUSR1, &sa, NULL); +- ++ + sem_post((sem_t *)data); + + while (1) { +@@ -785,7 +1391,7 @@ + } + + chan_misdn_log(4, 0, "Starting misdn_tasks thread\n"); +- ++ + misdn_tasks = sched_context_create(); + pthread_create(&misdn_tasks_thread, NULL, misdn_tasks_thread_func, &blocker); + +@@ -841,6 +1447,7 @@ + static int misdn_l1_task(const void *vdata) + { + const int *data = vdata; ++ + misdn_lib_isdn_l1watcher(*data); + chan_misdn_log(5, *data, "L1watcher timeout\n"); + return 1; +@@ -867,21 +1474,22 @@ + tv_end.tv_sec += ch->overlap_dial; + tv_now = ast_tvnow(); + +- if ((diff = ast_tvdiff_ms(tv_end, tv_now)) > 100) { ++ diff = ast_tvdiff_ms(tv_end, tv_now); ++ if (100 < diff) { + return diff; + } + + /* if we are 100ms near the timeout, we are satisfied.. */ + stop_indicate(ch); + +- if (ast_strlen_zero(ch->bc->dad)) { ++ if (ast_strlen_zero(ch->bc->dialed.number)) { + dad = "s"; +- ast_copy_string(ch->ast->exten, "s", sizeof(ch->ast->exten)); ++ strcpy(ch->ast->exten, dad); + } else { +- dad = ch->bc->dad; ++ dad = ch->bc->dialed.number; + } + +- if (ast_exists_extension(ch->ast, ch->context, dad, 1, ch->bc->oad)) { ++ if (ast_exists_extension(ch->ast, ch->context, dad, 1, ch->bc->caller.number)) { + ch->state = MISDN_DIALING; + if (pbx_start_chan(ch) < 0) { + chan_misdn_log(-1, ch->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n"); +@@ -918,8 +1526,8 @@ + "!941+1209/100,!0/100", /* * */ + "!941+1477/100,!0/100", /* # */ + }; +- struct ast_channel *chan = cl->ast; +- ++ struct ast_channel *chan = cl->ast; ++ + if (digit >= '0' && digit <='9') { + ast_playtones_start(chan, 0, dtmf_tones[digit - '0'], 0); + } else if (digit >= 'A' && digit <= 'D') { +@@ -958,8 +1566,10 @@ + level = 1; + } else if (!strcasecmp(a->argv[3], "off")) { + level = 0; ++ } else if (isdigit(a->argv[3][0])) { ++ level = atoi(a->argv[3]); + } else { +- level = atoi(a->argv[3]); ++ return CLI_SHOWUSAGE; + } + + switch (a->argc) { +@@ -975,7 +1585,7 @@ + only = 1; + } + } +- ++ + for (i = 0; i <= max_ports; i++) { + misdn_debug[i] = level; + misdn_debug_only[i] = only; +@@ -1267,18 +1877,18 @@ + for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) { + misdn_cfg_get_config_string(port, elem, buffer, sizeof(buffer)); + ast_cli(a->fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : ""); +- } ++ } + ast_cli(a->fd, "\n"); + } + } +- ++ + if (onlyport > 0) { + if (misdn_cfg_is_port_valid(onlyport)) { + ast_cli(a->fd, "[PORT %d]\n", onlyport); + for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) { + misdn_cfg_get_config_string(onlyport, elem, buffer, sizeof(buffer)); + ast_cli(a->fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : ""); +- } ++ } + ast_cli(a->fd, "\n"); + } else { + ast_cli(a->fd, "Port %d is not active!\n", onlyport); +@@ -1294,38 +1904,40 @@ + }; + + static struct state_struct state_array[] = { ++/* *INDENT-OFF* */ + { MISDN_NOTHING, "NOTHING" }, /* at beginning */ +- { MISDN_WAITING4DIGS, "WAITING4DIGS" }, /* when waiting for infos */ +- { MISDN_EXTCANTMATCH, "EXTCANTMATCH" }, /* when asterisk couldn't match our ext */ +- { MISDN_INCOMING_SETUP, "INCOMING SETUP" }, /* when pbx_start */ +- { MISDN_DIALING, "DIALING" }, /* when pbx_start */ +- { MISDN_PROGRESS, "PROGRESS" }, /* when pbx_start */ +- { MISDN_PROCEEDING, "PROCEEDING" }, /* when pbx_start */ +- { MISDN_CALLING, "CALLING" }, /* when misdn_call is called */ +- { MISDN_CALLING_ACKNOWLEDGE, "CALLING_ACKNOWLEDGE" }, /* when misdn_call is called */ +- { MISDN_ALERTING, "ALERTING" }, /* when Alerting */ +- { MISDN_BUSY, "BUSY" }, /* when BUSY */ +- { MISDN_CONNECTED, "CONNECTED" }, /* when connected */ +- { MISDN_PRECONNECTED, "PRECONNECTED" }, /* when connected */ +- { MISDN_DISCONNECTED, "DISCONNECTED" }, /* when connected */ +- { MISDN_RELEASED, "RELEASED" }, /* when connected */ +- { MISDN_BRIDGED, "BRIDGED" }, /* when bridged */ ++ { MISDN_WAITING4DIGS, "WAITING4DIGS" }, /* when waiting for infos */ ++ { MISDN_EXTCANTMATCH, "EXTCANTMATCH" }, /* when asterisk couldn't match our ext */ ++ { MISDN_INCOMING_SETUP, "INCOMING SETUP" }, /* when pbx_start */ ++ { MISDN_DIALING, "DIALING" }, /* when pbx_start */ ++ { MISDN_PROGRESS, "PROGRESS" }, /* when pbx_start */ ++ { MISDN_PROCEEDING, "PROCEEDING" }, /* when pbx_start */ ++ { MISDN_CALLING, "CALLING" }, /* when misdn_call is called */ ++ { MISDN_CALLING_ACKNOWLEDGE, "CALLING_ACKNOWLEDGE" }, /* when misdn_call is called */ ++ { MISDN_ALERTING, "ALERTING" }, /* when Alerting */ ++ { MISDN_BUSY, "BUSY" }, /* when BUSY */ ++ { MISDN_CONNECTED, "CONNECTED" }, /* when connected */ ++ { MISDN_PRECONNECTED, "PRECONNECTED" }, /* when connected */ ++ { MISDN_DISCONNECTED, "DISCONNECTED" }, /* when connected */ ++ { MISDN_RELEASED, "RELEASED" }, /* when connected */ ++ { MISDN_BRIDGED, "BRIDGED" }, /* when bridged */ + { MISDN_CLEANING, "CLEANING" }, /* when hangup from * but we were connected before */ +- { MISDN_HUNGUP_FROM_MISDN, "HUNGUP_FROM_MISDN" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ +- { MISDN_HOLDED, "HOLDED" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ +- { MISDN_HOLD_DISCONNECT, "HOLD_DISCONNECT" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ ++ { MISDN_HUNGUP_FROM_MISDN, "HUNGUP_FROM_MISDN" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ ++ { MISDN_HOLDED, "HOLDED" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ ++ { MISDN_HOLD_DISCONNECT, "HOLD_DISCONNECT" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ + { MISDN_HUNGUP_FROM_AST, "HUNGUP_FROM_AST" }, /* when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ ++/* *INDENT-ON* */ + }; + +-static const char *misdn_get_ch_state(struct chan_list *p) ++static const char *misdn_get_ch_state(struct chan_list *p) + { + int i; + static char state[8]; +- ++ + if (!p) { + return NULL; + } +- ++ + for (i = 0; i < ARRAY_LEN(state_array); i++) { + if (state_array[i].state == p->state) { + return state_array[i].txt; +@@ -1346,7 +1958,7 @@ + ast_log(LOG_WARNING, "chan_misdn is not initialized properly, still reloading ?\n"); + return ; + } +- ++ + free_robin_list(); + misdn_cfg_reload(); + misdn_cfg_update_ptp(); +@@ -1385,18 +1997,24 @@ + static void print_bc_info (int fd, struct chan_list *help, struct misdn_bchannel *bc) + { + struct ast_channel *ast = help->ast; ++ + ast_cli(fd, +- "* Pid:%d Prt:%d Ch:%d Mode:%s Org:%s dad:%s oad:%s rad:%s ctx:%s state:%s\n", +- +- bc->pid, bc->port, bc->channel, ++ "* Pid:%d Port:%d Ch:%d Mode:%s Orig:%s dialed:%s\n" ++ " --> caller:\"%s\" <%s>\n" ++ " --> redirecting:\"%s\" <%s>\n" ++ " --> context:%s state:%s\n", ++ bc->pid, ++ bc->port, ++ bc->channel, + bc->nt ? "NT" : "TE", + help->originator == ORG_AST ? "*" : "I", +- ast ? ast->exten : NULL, +- ast ? ast->cid.cid_num : NULL, +- bc->rad, +- ast ? ast->context : NULL, +- misdn_get_ch_state(help) +- ); ++ ast ? ast->exten : "", ++ (ast && ast->cid.cid_name) ? ast->cid.cid_name : "", ++ (ast && ast->cid.cid_num) ? ast->cid.cid_num : "", ++ bc->redirecting.from.name, ++ bc->redirecting.from.number, ++ ast ? ast->context : "", ++ misdn_get_ch_state(help)); + if (misdn_debug[bc->port] > 0) { + ast_cli(fd, + " --> astname: %s\n" +@@ -1419,21 +2037,18 @@ + help->l3id, + help->addr, + bc->addr, +- bc ? bc->l3_id : -1, ++ bc->l3_id, + bc->display, +- + bc->active, + bc_state2str(bc->bc_state), + bearer2str(bc->capability), + #ifdef MISDN_1_2 + bc->pipeline, + #else +- bc->ec_enable, ++ bc->ec_enable, + #endif +- + help->norxtone, help->notxtone, +- bc->holded +- ); ++ bc->holded); + } + } + +@@ -1457,11 +2072,11 @@ + } + + help = cl_te; +- ++ + ast_cli(a->fd, "Channel List: %p\n", cl_te); + + for (; help; help = help->next) { +- struct misdn_bchannel *bc = help->bc; ++ struct misdn_bchannel *bc = help->bc; + struct ast_channel *ast = help->ast; + if (!ast) { + if (!bc) { +@@ -1481,15 +2096,17 @@ + if (help->state == MISDN_HOLDED) { + ast_cli(a->fd, "ITS A HOLDED BC:\n"); + ast_cli(a->fd, " --> l3_id: %x\n" +- " --> dad:%s oad:%s\n" +- " --> hold_port: %d\n" +- " --> hold_channel: %d\n", +- help->l3id, +- ast->exten, +- ast->cid.cid_num, +- help->hold_info.port, +- help->hold_info.channel +- ); ++ " --> dialed:%s\n" ++ " --> caller:\"%s\" <%s>\n" ++ " --> hold_port: %d\n" ++ " --> hold_channel: %d\n", ++ help->l3id, ++ ast->exten, ++ ast->cid.cid_name ? ast->cid.cid_name : "", ++ ast->cid.cid_num ? ast->cid.cid_num : "", ++ help->hold_info.port, ++ help->hold_info.channel ++ ); + } else { + ast_cli(a->fd, "* Channel in unknown STATE !!! Exten:%s, Callerid:%s\n", ast->exten, ast->cid.cid_num); + } +@@ -1523,13 +2140,13 @@ + help = cl_te; + + for (; help; help = help->next) { +- struct misdn_bchannel *bc = help->bc; ++ struct misdn_bchannel *bc = help->bc; + struct ast_channel *ast = help->ast; +- ++ + if (bc && ast) { + if (!strcasecmp(ast->name, a->argv[3])) { + print_bc_info(a->fd, help, bc); +- break; ++ break; + } + } + } +@@ -1639,7 +2256,7 @@ + } + + port = atoi(a->argv[3]); +- ++ + ast_cli(a->fd, "BEGIN STACK_LIST:\n"); + get_show_stack_details(port, buf); + ast_cli(a->fd, " %s Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port] ? "(only)" : ""); +@@ -1649,13 +2266,13 @@ + + static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) + { +- char *channame; ++ char *channame; + char *nr; + struct chan_list *tmp; +- int port; ++ int port; + char *served_nr; + struct misdn_bchannel dummy, *bc=&dummy; +- ++ + switch (cmd) { + case CLI_INIT: + e->command = "misdn send facility"; +@@ -1673,7 +2290,7 @@ + if (a->argc < 5) { + return CLI_SHOWUSAGE; + } +- ++ + if (strstr(a->argv[3], "calldeflect")) { + if (a->argc < 6) { + ast_verbose("calldeflect requires 1 arg: ToNumber\n\n"); +@@ -1686,15 +2303,15 @@ + tmp = get_chan_by_ast_name(channame); + if (!tmp) { + ast_verbose("Sending CD with nr %s to %s failed: Channel does not exist.\n", nr, channame); +- return 0; ++ return 0; + } + + if (strlen(nr) >= 15) { + ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to 15 digits are allowed).\n", nr, channame); +- return 0; ++ return 0; + } + tmp->bc->fac_out.Function = Fac_CD; +- ast_copy_string((char *)tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr, sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber)); ++ ast_copy_string((char *) tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr, sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber)); + misdn_lib_send_event(tmp->bc, EVENT_FACILITY); + } else if (strstr(a->argv[3], "CFActivate")) { + if (a->argc < 7) { +@@ -1719,20 +2336,20 @@ + } else if (strstr(a->argv[3], "CFDeactivate")) { + + if (a->argc < 6) { +- ast_verbose("CFActivate requires 1 arg: FromNumber\n\n"); ++ ast_verbose("CFDeactivate requires 1 arg: FromNumber\n\n"); + return 0; + } + port = atoi(a->argv[4]); + served_nr = a->argv[5]; +- ++ + misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0); + ast_verbose("Sending CFDeactivate Port:(%d) FromNr. (%s)\n", port, served_nr); + + bc->fac_out.Function = Fac_CFDeactivate; +- bc->fac_out.u.CFDeactivate.BasicService = 0; //All Services +- bc->fac_out.u.CFDeactivate.Procedure = 0; //Unconditional +- +- ast_copy_string((char *)bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); ++ bc->fac_out.u.CFDeactivate.BasicService = 0; /* All Services */ ++ bc->fac_out.u.CFDeactivate.Procedure = 0; /* Unconditional */ ++ ast_copy_string((char *) bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); ++ + misdn_lib_send_event(bc, EVENT_FACILITY); + } + +@@ -1773,8 +2390,8 @@ + + static char *handle_cli_misdn_send_digit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) + { +- char *channame; +- char *msg; ++ char *channame; ++ char *msg; + struct chan_list *tmp; + int i, msglen; + +@@ -1803,7 +2420,7 @@ + tmp = get_chan_by_ast_name(channame); + if (!tmp) { + ast_cli(a->fd, "Sending %s to %s failed Channel does not exist\n", msg, channame); +- return CLI_SUCCESS; ++ return CLI_SUCCESS; + } + #if 1 + for (i = 0; i < msglen; i++) { +@@ -1841,9 +2458,9 @@ + } + + channame = a->argv[3]; +- ++ + ast_cli(a->fd, "Toggling EchoCancel on %s\n", channame); +- ++ + tmp = get_chan_by_ast_name(channame); + if (!tmp) { + ast_cli(a->fd, "Toggling EchoCancel %s failed Channel does not exist\n", channame); +@@ -1893,7 +2510,7 @@ + + ast_cli(a->fd, "Sending %s to %s\n", msg, channame); + tmp = get_chan_by_ast_name(channame); +- ++ + if (tmp && tmp->bc) { + ast_copy_string(tmp->bc->display, msg, sizeof(tmp->bc->display)); + misdn_lib_send_event(tmp->bc, EVENT_INFORMATION); +@@ -2010,23 +2627,25 @@ + }; + + /*! \brief Updates caller ID information from config */ +-static int update_config(struct chan_list *ch, int orig) ++static void update_config(struct chan_list *ch) + { + struct ast_channel *ast; + struct misdn_bchannel *bc; +- int port, hdlc = 0; +- int pres, screen; ++ int port; ++ int hdlc = 0; ++ int pres; ++ int screen; + + if (!ch) { + ast_log(LOG_WARNING, "Cannot configure without chanlist\n"); +- return -1; ++ return; + } + + ast = ch->ast; + bc = ch->bc; + if (! ast || ! bc) { + ast_log(LOG_WARNING, "Cannot configure without ast || bc\n"); +- return -1; ++ return; + } + + port = bc->port; +@@ -2034,7 +2653,6 @@ + chan_misdn_log(7, port, "update_config: Getting Config\n"); + + misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(int)); +- + if (hdlc) { + switch (bc->capability) { + case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: +@@ -2049,50 +2667,19 @@ + misdn_cfg_get(port, MISDN_CFG_PRES, &pres, sizeof(pres)); + misdn_cfg_get(port, MISDN_CFG_SCREEN, &screen, sizeof(screen)); + chan_misdn_log(2, port, " --> pres: %d screen: %d\n", pres, screen); +- ++ + if (pres < 0 || screen < 0) { +- chan_misdn_log(2, port, " --> pres: %x\n", ast->cid.cid_pres); +- +- switch (ast->cid.cid_pres & 0x60) { +- case AST_PRES_RESTRICTED: +- bc->pres = 1; +- chan_misdn_log(2, port, " --> PRES: Restricted (1)\n"); +- break; +- case AST_PRES_UNAVAILABLE: +- bc->pres = 2; +- chan_misdn_log(2, port, " --> PRES: Unavailable (2)\n"); +- break; +- default: +- bc->pres = 0; +- chan_misdn_log(2, port, " --> PRES: Allowed (0)\n"); +- break; +- } ++ chan_misdn_log(2, port, " --> pres: %x\n", ast->connected.id.number_presentation); + +- switch (ast->cid.cid_pres & 0x3) { +- default: +- case AST_PRES_USER_NUMBER_UNSCREENED: +- bc->screen = 0; +- chan_misdn_log(2, port, " --> SCREEN: Unscreened (0)\n"); +- break; +- case AST_PRES_USER_NUMBER_PASSED_SCREEN: +- bc->screen = 1; +- chan_misdn_log(2, port, " --> SCREEN: Passed Screen (1)\n"); +- break; +- case AST_PRES_USER_NUMBER_FAILED_SCREEN: +- bc->screen = 2; +- chan_misdn_log(2, port, " --> SCREEN: Failed Screen (2)\n"); +- break; +- case AST_PRES_NETWORK_NUMBER: +- bc->screen = 3; +- chan_misdn_log(2, port, " --> SCREEN: Network Nr. (3)\n"); +- break; +- } ++ bc->caller.presentation = ast_to_misdn_pres(ast->connected.id.number_presentation); ++ chan_misdn_log(2, port, " --> PRES: %s(%d)\n", misdn_to_str_pres(bc->caller.presentation), bc->caller.presentation); ++ ++ bc->caller.screening = ast_to_misdn_screen(ast->connected.id.number_presentation); ++ chan_misdn_log(2, port, " --> SCREEN: %s(%d)\n", misdn_to_str_screen(bc->caller.screening), bc->caller.screening); + } else { +- bc->screen = screen; +- bc->pres = pres; ++ bc->caller.screening = screen; ++ bc->caller.presentation = pres; + } +- +- return 0; + } + + +@@ -2100,9 +2687,9 @@ + { + struct misdn_bchannel *bc = ch->bc; + int len = ch->jb_len, threshold = ch->jb_upper_threshold; +- ++ + chan_misdn_log(5, bc->port, "config_jb: Called\n"); +- ++ + if (! len) { + chan_misdn_log(1, bc->port, "config_jb: Deactivating Jitterbuffer\n"); + bc->nojitter=1; +@@ -2131,20 +2718,26 @@ + } + + +-void debug_numplan(int port, int numplan, char *type) ++void debug_numtype(int port, int numtype, char *type) + { +- switch (numplan) { +- case NUMPLAN_INTERNATIONAL: ++ switch (numtype) { ++ case NUMTYPE_UNKNOWN: ++ chan_misdn_log(2, port, " --> %s: Unknown\n", type); ++ break; ++ case NUMTYPE_INTERNATIONAL: + chan_misdn_log(2, port, " --> %s: International\n", type); + break; +- case NUMPLAN_NATIONAL: ++ case NUMTYPE_NATIONAL: + chan_misdn_log(2, port, " --> %s: National\n", type); + break; +- case NUMPLAN_SUBSCRIBER: ++ case NUMTYPE_NETWORK_SPECIFIC: ++ chan_misdn_log(2, port, " --> %s: Network Specific\n", type); ++ break; ++ case NUMTYPE_SUBSCRIBER: + chan_misdn_log(2, port, " --> %s: Subscriber\n", type); + break; +- case NUMPLAN_UNKNOWN: +- chan_misdn_log(2, port, " --> %s: Unknown\n", type); ++ case NUMTYPE_ABBREVIATED: ++ chan_misdn_log(2, port, " --> %s: Abbreviated\n", type); + break; + /* Maybe we should cut off the prefix if present ? */ + default: +@@ -2194,7 +2787,7 @@ + #endif + + +-static int read_config(struct chan_list *ch, int orig) ++static int read_config(struct chan_list *ch) + { + struct ast_channel *ast; + struct misdn_bchannel *bc; +@@ -2218,7 +2811,7 @@ + ast_log(LOG_WARNING, "Cannot configure without ast || bc\n"); + return -1; + } +- ++ + port = bc->port; + chan_misdn_log(1, port, "read_config: Getting Config\n"); + +@@ -2233,9 +2826,8 @@ + misdn_cfg_get(port, MISDN_CFG_INCOMING_EARLY_AUDIO, &ch->incoming_early_audio, sizeof(ch->incoming_early_audio)); + + misdn_cfg_get(port, MISDN_CFG_SENDDTMF, &bc->send_dtmf, sizeof(bc->send_dtmf)); +- ++ + misdn_cfg_get(port, MISDN_CFG_ASTDTMF, &ch->ast_dsp, sizeof(int)); +- + if (ch->ast_dsp) { + ch->ignore_dtmf = 1; + } +@@ -2252,7 +2844,6 @@ + misdn_cfg_get(port, MISDN_CFG_FAXDETECT, faxdetect, sizeof(faxdetect)); + + misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(hdlc)); +- + if (hdlc) { + switch (bc->capability) { + case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: +@@ -2261,7 +2852,7 @@ + bc->hdlc = 1; + break; + } +- ++ + } + /*Initialize new Jitterbuffer*/ + misdn_cfg_get(port, MISDN_CFG_JITTERBUFFER, &ch->jb_len, sizeof(ch->jb_len)); +@@ -2281,14 +2872,16 @@ + + misdn_cfg_get(bc->port, MISDN_CFG_EARLY_BCONNECT, &bc->early_bconnect, sizeof(bc->early_bconnect)); + ++ misdn_cfg_get(port, MISDN_CFG_DISPLAY_CONNECTED, &bc->display_connected, sizeof(bc->display_connected)); ++ misdn_cfg_get(port, MISDN_CFG_DISPLAY_SETUP, &bc->display_setup, sizeof(bc->display_setup)); ++ + misdn_cfg_get(port, MISDN_CFG_PICKUPGROUP, &pg, sizeof(pg)); + misdn_cfg_get(port, MISDN_CFG_CALLGROUP, &cg, sizeof(cg)); +- + chan_misdn_log(5, port, " --> * CallGrp:%s PickupGrp:%s\n", ast_print_group(buf, sizeof(buf), cg), ast_print_group(buf2, sizeof(buf2), pg)); + ast->pickupgroup = pg; + ast->callgroup = cg; +- +- if (orig == ORG_AST) { ++ ++ if (ch->originator == ORG_AST) { + char callerid[BUFFERSIZE + 1]; + + /* ORIGINATOR Asterisk (outgoing call) */ +@@ -2301,87 +2894,53 @@ + + misdn_cfg_get(port, MISDN_CFG_CALLERID, callerid, sizeof(callerid)); + if (!ast_strlen_zero(callerid)) { +- chan_misdn_log(1, port, " --> * Setting Cid to %s\n", callerid); +- ast_copy_string(bc->oad, callerid, sizeof(bc->oad)); ++ char *cid_name = NULL; ++ char *cid_num = NULL; ++ ++ ast_callerid_parse(callerid, &cid_name, &cid_num); ++ if (cid_name) { ++ ast_copy_string(bc->caller.name, cid_name, sizeof(bc->caller.name)); ++ } else { ++ bc->caller.name[0] = '\0'; ++ } ++ if (cid_num) { ++ ast_copy_string(bc->caller.number, cid_num, sizeof(bc->caller.number)); ++ } else { ++ bc->caller.number[0] = '\0'; ++ } ++ chan_misdn_log(1, port, " --> * Setting caller to \"%s\" <%s>\n", bc->caller.name, bc->caller.number); + } + +- misdn_cfg_get(port, MISDN_CFG_DIALPLAN, &bc->dnumplan, sizeof(bc->dnumplan)); +- misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &bc->onumplan, sizeof(bc->onumplan)); +- misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(bc->cpnnumplan)); +- debug_numplan(port, bc->dnumplan, "TON"); +- debug_numplan(port, bc->onumplan, "LTON"); +- debug_numplan(port, bc->cpnnumplan, "CTON"); ++ misdn_cfg_get(port, MISDN_CFG_DIALPLAN, &bc->dialed.number_type, sizeof(bc->dialed.number_type)); ++ bc->dialed.number_plan = NUMPLAN_ISDN; ++ debug_numtype(port, bc->dialed.number_type, "TON"); + + ch->overlap_dial = 0; + } else { + /* ORIGINATOR MISDN (incoming call) */ +- char prefix[BUFFERSIZE + 1] = ""; + + if (strstr(faxdetect, "incoming") || strstr(faxdetect, "both")) { + ch->faxdetect = (strstr(faxdetect, "nojump")) ? 2 : 1; + } + +- misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(bc->cpnnumplan)); +- debug_numplan(port, bc->cpnnumplan, "CTON"); ++ /* Add configured prefix to caller.number */ ++ misdn_add_number_prefix(bc->port, bc->caller.number_type, bc->caller.number, sizeof(bc->caller.number)); + +- switch (bc->onumplan) { +- case NUMPLAN_INTERNATIONAL: +- misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, sizeof(prefix)); +- break; +- +- case NUMPLAN_NATIONAL: +- misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, sizeof(prefix)); +- break; +- default: +- break; ++ if (ast_strlen_zero(bc->dialed.number) && !ast_strlen_zero(bc->keypad)) { ++ ast_copy_string(bc->dialed.number, bc->keypad, sizeof(bc->dialed.number)); + } + +- ast_copy_string(buf, bc->oad, sizeof(buf)); +- snprintf(bc->oad, sizeof(bc->oad), "%s%s", prefix, buf); ++ /* Add configured prefix to dialed.number */ ++ misdn_add_number_prefix(bc->port, bc->dialed.number_type, bc->dialed.number, sizeof(bc->dialed.number)); + +- if (!ast_strlen_zero(bc->dad)) { +- ast_copy_string(bc->orig_dad, bc->dad, sizeof(bc->orig_dad)); +- } ++ ast_copy_string(ast->exten, bc->dialed.number, sizeof(ast->exten)); + +- if (ast_strlen_zero(bc->dad) && !ast_strlen_zero(bc->keypad)) { +- ast_copy_string(bc->dad, bc->keypad, sizeof(bc->dad)); +- } +- +- prefix[0] = 0; +- +- switch (bc->dnumplan) { +- case NUMPLAN_INTERNATIONAL: +- misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, sizeof(prefix)); +- break; +- case NUMPLAN_NATIONAL: +- misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, sizeof(prefix)); +- break; +- default: +- break; +- } +- +- ast_copy_string(buf, bc->dad, sizeof(buf)); +- snprintf(bc->dad, sizeof(bc->dad), "%s%s", prefix, buf); +- +- if (strcmp(bc->dad, ast->exten)) { +- ast_copy_string(ast->exten, bc->dad, sizeof(ast->exten)); +- } +- +- ast_set_callerid(ast, bc->oad, NULL, bc->oad); +- +- if ( !ast_strlen_zero(bc->rad) ) { +- if (ast->cid.cid_rdnis) { +- ast_free(ast->cid.cid_rdnis); +- } +- ast->cid.cid_rdnis = ast_strdup(bc->rad); +- } +- + misdn_cfg_get(bc->port, MISDN_CFG_OVERLAP_DIAL, &ch->overlap_dial, sizeof(ch->overlap_dial)); + ast_mutex_init(&ch->overlap_tv_lock); + } /* ORIG MISDN END */ + + ch->overlap_dial_task = -1; +- ++ + if (ch->faxdetect || ch->ast_dsp) { + misdn_cfg_get(port, MISDN_CFG_FAXDETECT_TIMEOUT, &ch->faxdetect_timeout, sizeof(ch->faxdetect_timeout)); + if (!ch->dsp) { +@@ -2401,7 +2960,80 @@ + return 0; + } + ++/*! ++ * \internal ++ * \brief Notify peer that the connected line has changed. ++ * ++ * \param ast Current Asterisk channel ++ * \param bc Associated B channel ++ * \param originator Who originally created this channel. ORG_AST or ORG_MISDN ++ * ++ * \return Nothing ++ */ ++static void misdn_update_connected_line(struct ast_channel *ast, struct misdn_bchannel *bc, int originator) ++{ ++ int number_type; + ++ if (originator == ORG_MISDN) { ++ /* ORIGINATOR MISDN (incoming call) */ ++ ++ ast_copy_string(bc->connected.name, S_OR(ast->connected.id.name, ""), sizeof(bc->connected.name)); ++ ast_copy_string(bc->connected.number, S_OR(ast->connected.id.number, ""), sizeof(bc->connected.number)); ++ bc->connected.presentation = ast_to_misdn_pres(ast->connected.id.number_presentation); ++ bc->connected.screening = ast_to_misdn_screen(ast->connected.id.number_presentation); ++ ++ misdn_cfg_get(bc->port, MISDN_CFG_CPNDIALPLAN, &number_type, sizeof(number_type)); ++ if (number_type < 0) { ++ bc->connected.number_type = ast_to_misdn_ton(ast->connected.id.number_type); ++ bc->connected.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); ++ } else { ++ /* Force us to send in CONNECT message */ ++ bc->connected.number_type = number_type; ++ bc->connected.number_plan = NUMPLAN_ISDN; ++ } ++ debug_numtype(bc->port, bc->connected.number_type, "CTON"); ++ } else { ++ /* ORIGINATOR Asterisk (outgoing call) */ ++ ++ ast_copy_string(bc->caller.name, S_OR(ast->connected.id.name, ""), sizeof(bc->caller.name)); ++ ast_copy_string(bc->caller.number, S_OR(ast->connected.id.number, ""), sizeof(bc->caller.number)); ++ bc->caller.presentation = ast_to_misdn_pres(ast->connected.id.number_presentation); ++ bc->caller.screening = ast_to_misdn_screen(ast->connected.id.number_presentation); ++ ++ misdn_cfg_get(bc->port, MISDN_CFG_LOCALDIALPLAN, &number_type, sizeof(number_type)); ++ if (number_type < 0) { ++ bc->caller.number_type = ast_to_misdn_ton(ast->connected.id.number_type); ++ bc->caller.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); ++ } else { ++ /* Force us to send in SETUP message */ ++ bc->caller.number_type = number_type; ++ bc->caller.number_plan = NUMPLAN_ISDN; ++ } ++ debug_numtype(bc->port, bc->caller.number_type, "LTON"); ++ } ++} ++ ++/*! ++ * \internal ++ * \brief Copy the redirecting info out of the Asterisk channel ++ * ++ * \param bc Associated B channel ++ * \param ast Current Asterisk channel ++ * ++ * \return Nothing ++ */ ++static void misdn_copy_redirecting_from_ast(struct misdn_bchannel *bc, struct ast_channel *ast) ++{ ++ ast_copy_string(bc->redirecting.from.name, S_OR(ast->redirecting.from.name, ""), sizeof(bc->redirecting.from.name)); ++ ast_copy_string(bc->redirecting.from.number, S_OR(ast->cid.cid_rdnis, ""), sizeof(bc->redirecting.from.number)); ++ bc->redirecting.from.presentation = ast_to_misdn_pres(ast->redirecting.from.number_presentation); ++ bc->redirecting.from.screening = ast_to_misdn_screen(ast->redirecting.from.number_presentation); ++ bc->redirecting.from.number_type = ast_to_misdn_ton(ast->redirecting.from.number_type); ++ bc->redirecting.from.number_plan = ast_to_misdn_plan(ast->redirecting.from.number_type); ++ bc->redirecting.reason = ast_to_misdn_reason(ast->redirecting.reason); ++} ++ ++ + /*****************************/ + /*** AST Indications Start ***/ + /*****************************/ +@@ -2412,22 +3044,17 @@ + int r; + int exceed; + int bridging; +- struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(ast); ++ int number_type; ++ struct chan_list *ch; + struct misdn_bchannel *newbc; +- char *dest_cp = ast_strdupa(dest); ++ char *dest_cp; ++ + AST_DECLARE_APP_ARGS(args, +- AST_APP_ARG(type); +- AST_APP_ARG(ext); +- AST_APP_ARG(opts); ++ AST_APP_ARG(intf); /* The interface token is discarded. */ ++ AST_APP_ARG(ext); /* extension token */ ++ AST_APP_ARG(opts); /* options token */ + ); + +- AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); +- +- if (ast_strlen_zero(args.ext)) { +- chan_misdn_log(0, 0, "misdn_call: No Extension given!\n"); +- return -1; +- } +- + if (!ast) { + ast_log(LOG_WARNING, " --> ! misdn_call called on ast_channel *ast where ast == NULL\n"); + return -1; +@@ -2440,58 +3067,85 @@ + return -1; + } + ++ ch = MISDN_ASTERISK_TECH_PVT(ast); + if (!ch) { +- ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name); ++ ast_log(LOG_WARNING, " --> ! misdn_call called on %s, chan_list *ch==NULL\n", ast->name); + ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; + ast_setstate(ast, AST_STATE_DOWN); + return -1; + } +- ++ + newbc = ch->bc; +- + if (!newbc) { +- ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name); ++ ast_log(LOG_WARNING, " --> ! misdn_call called on %s, newbc==NULL\n", ast->name); + ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; + ast_setstate(ast, AST_STATE_DOWN); + return -1; + } +- ++ ++ /* ++ * dest is ---v ++ * Dial(mISDN/g:group_name[/extension[/options]]) ++ * Dial(mISDN/port[:preselected_channel][/extension[/options]]) ++ * ++ * The dial extension could be empty if you are using MISDN_KEYPAD ++ * to control ISDN provider features. ++ */ ++ dest_cp = ast_strdupa(dest); ++ AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); ++ if (!args.ext) { ++ args.ext = ""; ++ } ++ + port = newbc->port; + +- if ((exceed = add_out_calls(port))) { ++ exceed = add_out_calls(port); ++ if (exceed != 0) { + char tmp[16]; + snprintf(tmp, sizeof(tmp), "%d", exceed); + pbx_builtin_setvar_helper(ast, "MAX_OVERFLOW", tmp); ++ ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; ++ ast_setstate(ast, AST_STATE_DOWN); + return -1; + } +- ++ + chan_misdn_log(1, port, "* CALL: %s\n", dest); +- +- chan_misdn_log(2, port, " --> * dad:%s tech:%s ctx:%s\n", ast->exten, ast->name, ast->context); +- +- chan_misdn_log(3, port, " --> * adding2newbc ext %s\n", ast->exten); +- if (ast->exten) { +- ast_copy_string(ast->exten, args.ext, sizeof(ast->exten)); +- ast_copy_string(newbc->dad, args.ext, sizeof(newbc->dad)); +- } + +- ast_copy_string(newbc->rad, S_OR(ast->cid.cid_rdnis, ""), sizeof(newbc->rad)); ++ chan_misdn_log(2, port, " --> * dialed:%s tech:%s context:%s\n", args.ext, ast->name, ast->context); + +- chan_misdn_log(3, port, " --> * adding2newbc callerid %s\n", ast->cid.cid_num); +- if (ast_strlen_zero(newbc->oad) && !ast_strlen_zero(ast->cid.cid_num)) { +- ast_copy_string(newbc->oad, ast->cid.cid_num, sizeof(newbc->oad)); ++ ast_copy_string(ast->exten, args.ext, sizeof(ast->exten)); ++ ast_copy_string(newbc->dialed.number, args.ext, sizeof(newbc->dialed.number)); ++ ++ if (ast_strlen_zero(newbc->caller.name) && !ast_strlen_zero(ast->connected.id.name)) { ++ ast_copy_string(newbc->caller.name, ast->connected.id.name, sizeof(newbc->caller.name)); ++ chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); + } ++ if (ast_strlen_zero(newbc->caller.number) && !ast_strlen_zero(ast->connected.id.number)) { ++ ast_copy_string(newbc->caller.number, ast->connected.id.number, sizeof(newbc->caller.number)); ++ chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); ++ } + ++ misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &number_type, sizeof(number_type)); ++ if (number_type < 0) { ++ newbc->caller.number_type = ast_to_misdn_ton(ast->connected.id.number_type); ++ newbc->caller.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); ++ } else { ++ /* Force us to send in SETUP message */ ++ newbc->caller.number_type = number_type; ++ newbc->caller.number_plan = NUMPLAN_ISDN; ++ } ++ debug_numtype(port, newbc->caller.number_type, "LTON"); ++ + newbc->capability = ast->transfercapability; + pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(newbc->capability)); + if ( ast->transfercapability == INFO_CAPABILITY_DIGITAL_UNRESTRICTED) { + chan_misdn_log(2, port, " --> * Call with flag Digital\n"); + } + +- /* update screening and presentation */ +- update_config(ch, ORG_AST); +- +- /* fill in some ies from channel vary */ ++ /* update caller screening and presentation */ ++ update_config(ch); ++ ++ /* fill in some ies from channel dialplan variables */ + import_ch(ast, newbc, ch); + + /* Finally The Options Override Everything */ +@@ -2500,7 +3154,12 @@ + } else { + chan_misdn_log(2, port, "NO OPTS GIVEN\n"); + } ++ if (newbc->set_presentation) { ++ newbc->caller.presentation = newbc->presentation; ++ } + ++ misdn_copy_redirecting_from_ast(newbc, ast); ++ + /*check for bridging*/ + misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); + if (bridging && ch->other_ch) { +@@ -2520,7 +3179,7 @@ + /** we should have l3id after sending setup **/ + ch->l3id = newbc->l3_id; + +- if (r == -ENOCHAN ) { ++ if (r == -ENOCHAN) { + chan_misdn_log(0, port, " --> * Theres no Channel at the moment .. !\n"); + chan_misdn_log(1, port, " --> * SEND: State Down pid:%d\n", newbc ? newbc->pid : -1); + ast->hangupcause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; +@@ -2538,8 +3197,8 @@ + } + + ch->state = MISDN_CALLING; +- +- return 0; ++ ++ return 0; + } + + +@@ -2551,9 +3210,9 @@ + if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast))) { + return -1; + } +- ++ + chan_misdn_log(1, p ? (p->bc ? p->bc->port : 0) : 0, "* ANSWER:\n"); +- ++ + if (!p) { + ast_log(LOG_WARNING, " --> Channel not connected ??\n"); + ast_queue_hangup_with_cause(ast, AST_CAUSE_NETWORK_OUT_OF_ORDER); +@@ -2586,9 +3245,19 @@ + p->state = MISDN_CONNECTED; + stop_indicate(p); + +- if ( ast_strlen_zero(p->bc->cad) ) { +- chan_misdn_log(2, p->bc->port, " --> empty cad using dad\n"); +- ast_copy_string(p->bc->cad, p->bc->dad, sizeof(p->bc->cad)); ++ if (ast_strlen_zero(p->bc->connected.number)) { ++ chan_misdn_log(2,p->bc->port," --> empty connected number using dialed number\n"); ++ ast_copy_string(p->bc->connected.number, p->bc->dialed.number, sizeof(p->bc->connected.number)); ++ ++ /* ++ * Use the misdn_set_opt() application to set the presentation ++ * before we answer or you can use the CONECTEDLINE() function ++ * to set everything before using the Answer() application. ++ */ ++ p->bc->connected.presentation = p->bc->presentation; ++ p->bc->connected.screening = 0; /* unscreened */ ++ p->bc->connected.number_type = p->bc->dialed.number_type; ++ p->bc->connected.number_plan = p->bc->dialed.number_plan; + } + + misdn_lib_send_event(p->bc, EVENT_CONNECT); +@@ -2615,12 +3284,12 @@ + + bc = p->bc; + chan_misdn_log(1, bc ? bc->port : 0, "* IND : Digit %c\n", digit); +- ++ + if (!bc) { + ast_log(LOG_WARNING, " --> !! Got Digit Event without having bchannel Object\n"); + return -1; + } +- ++ + switch (p->state ) { + case MISDN_CALLING: + if (strlen(bc->infos_pending) < sizeof(bc->infos_pending) - 1) { +@@ -2629,15 +3298,15 @@ + break; + case MISDN_CALLING_ACKNOWLEDGE: + ast_copy_string(bc->info_dad, buf, sizeof(bc->info_dad)); +- if (strlen(bc->dad) < sizeof(bc->dad) - 1) { +- strncat(bc->dad, buf, sizeof(bc->dad) - strlen(bc->dad) - 1); ++ if (strlen(bc->dialed.number) < sizeof(bc->dialed.number) - 1) { ++ strncat(bc->dialed.number, buf, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); + } +- ast_copy_string(p->ast->exten, bc->dad, sizeof(p->ast->exten)); ++ ast_copy_string(p->ast->exten, bc->dialed.number, sizeof(p->ast->exten)); + misdn_lib_send_event(bc, EVENT_INFORMATION); + break; + default: + /* Do not send Digits in CONNECTED State, when +- * the other side is too mISDN. */ ++ * the other side is also mISDN. */ + if (p->other_ch) { + return 0; + } +@@ -2677,18 +3346,18 @@ + ast_log(LOG_WARNING, "Returned -1 in misdn_indication\n"); + return -1; + } +- ++ + if (!p->bc ) { + chan_misdn_log(1, 0, "* IND : Indication from %s\n", ast->exten); + ast_log(LOG_WARNING, "Private Pointer but no bc ?\n"); + return -1; + } +- ++ + chan_misdn_log(5, p->bc->port, "* IND : Indication [%d] from %s\n", cond, ast->exten); +- ++ + switch (cond) { + case AST_CONTROL_BUSY: +- chan_misdn_log(1, p->bc->port, "* IND :\tbusy pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, "* IND :\tbusy pid:%d\n", p->bc->pid); + ast_setstate(ast, AST_STATE_BUSY); + + p->bc->out_cause = AST_CAUSE_USER_BUSY; +@@ -2700,20 +3369,20 @@ + } + return -1; + case AST_CONTROL_RING: +- chan_misdn_log(1, p->bc->port, "* IND :\tring pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, "* IND :\tring pid:%d\n", p->bc->pid); + return -1; + case AST_CONTROL_RINGING: +- chan_misdn_log(1, p->bc->port, "* IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, "* IND :\tringing pid:%d\n", p->bc->pid); + switch (p->state) { + case MISDN_ALERTING: +- chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but I was Ringing before, so ignoring it\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but I was Ringing before, so ignoring it\n", p->bc->pid); + break; + case MISDN_CONNECTED: +- chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but Connected, so just send TONE_ALERTING without state changes \n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but Connected, so just send TONE_ALERTING without state changes \n", p->bc->pid); + return -1; + default: + p->state = MISDN_ALERTING; +- chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d\n", p->bc->pid); + misdn_lib_send_event( p->bc, EVENT_ALERTING); + + if (p->other_ch && p->other_ch->bc) { +@@ -2728,7 +3397,7 @@ + } + } + +- chan_misdn_log(3, p->bc->port, " --> * SEND: State Ring pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(3, p->bc->port, " --> * SEND: State Ring pid:%d\n", p->bc->pid); + ast_setstate(ast, AST_STATE_RING); + + if (!p->bc->nt && (p->originator == ORG_MISDN) && !p->incoming_early_audio) { +@@ -2739,28 +3408,28 @@ + } + break; + case AST_CONTROL_ANSWER: +- chan_misdn_log(1, p->bc->port, " --> * IND :\tanswer pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> * IND :\tanswer pid:%d\n", p->bc->pid); + start_bc_tones(p); + break; + case AST_CONTROL_TAKEOFFHOOK: +- chan_misdn_log(1, p->bc->port, " --> *\ttakeoffhook pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> *\ttakeoffhook pid:%d\n", p->bc->pid); + return -1; + case AST_CONTROL_OFFHOOK: +- chan_misdn_log(1, p->bc->port, " --> *\toffhook pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> *\toffhook pid:%d\n", p->bc->pid); + return -1; + case AST_CONTROL_FLASH: +- chan_misdn_log(1, p->bc->port, " --> *\tflash pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> *\tflash pid:%d\n", p->bc->pid); + break; + case AST_CONTROL_PROGRESS: +- chan_misdn_log(1, p->bc->port, " --> * IND :\tprogress pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> * IND :\tprogress pid:%d\n", p->bc->pid); + misdn_lib_send_event( p->bc, EVENT_PROGRESS); + break; + case AST_CONTROL_PROCEEDING: +- chan_misdn_log(1, p->bc->port, " --> * IND :\tproceeding pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> * IND :\tproceeding pid:%d\n", p->bc->pid); + misdn_lib_send_event( p->bc, EVENT_PROCEEDING); + break; + case AST_CONTROL_CONGESTION: +- chan_misdn_log(1, p->bc->port, " --> * IND :\tcongestion pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> * IND :\tcongestion pid:%d\n", p->bc->pid); + + p->bc->out_cause = AST_CAUSE_SWITCH_CONGESTION; + start_bc_tones(p); +@@ -2771,7 +3440,7 @@ + } + break; + case -1 : +- chan_misdn_log(1, p->bc->port, " --> * IND :\t-1! (stop indication) pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> * IND :\t-1! (stop indication) pid:%d\n", p->bc->pid); + + stop_indicate(p); + +@@ -2780,17 +3449,26 @@ + } + break; + case AST_CONTROL_HOLD: +- ast_moh_start(ast, data, p->mohinterpret); +- chan_misdn_log(1, p->bc->port, " --> *\tHOLD pid:%d\n", p->bc ? p->bc->pid : -1); ++ ast_moh_start(ast, data, p->mohinterpret); ++ chan_misdn_log(1, p->bc->port, " --> *\tHOLD pid:%d\n", p->bc->pid); + break; + case AST_CONTROL_UNHOLD: + ast_moh_stop(ast); +- chan_misdn_log(1, p->bc->port, " --> *\tUNHOLD pid:%d\n", p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> *\tUNHOLD pid:%d\n", p->bc->pid); + break; ++ case AST_CONTROL_CONNECTED_LINE: ++ chan_misdn_log(1, p->bc->port, "* IND :\tconnected line update pid:%d\n", p->bc->pid); ++ misdn_update_connected_line(ast, p->bc, p->originator); ++ break; ++ case AST_CONTROL_REDIRECTING: ++ chan_misdn_log(1, p->bc->port, "* IND :\tredirecting info update pid:%d\n", p->bc->pid); ++ misdn_copy_redirecting_from_ast(p->bc, ast); ++ break; + default: +- chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc ? p->bc->pid : -1); ++ chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc->pid); ++ break; + } +- ++ + return 0; + } + +@@ -2815,8 +3493,10 @@ + + if (bc) { + const char *tmp; ++ + ast_channel_lock(ast); +- if ((tmp = pbx_builtin_getvar_helper(ast, "MISDN_USERUSER"))) { ++ tmp = pbx_builtin_getvar_helper(ast, "MISDN_USERUSER"); ++ if (tmp) { + ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", tmp); + strcpy(bc->uu, tmp); + bc->uulen = strlen(bc->uu); +@@ -2827,16 +3507,16 @@ + MISDN_ASTERISK_TECH_PVT(ast) = NULL; + p->ast = NULL; + +- if (ast->_state == AST_STATE_RESERVED || +- p->state == MISDN_NOTHING || +- p->state == MISDN_HOLDED || ++ if (ast->_state == AST_STATE_RESERVED || ++ p->state == MISDN_NOTHING || ++ p->state == MISDN_HOLDED || + p->state == MISDN_HOLD_DISCONNECT ) { + + CLEAN_CH: + /* between request and call */ + ast_debug(1, "State Reserved (or nothing) => chanIsAvail\n"); + MISDN_ASTERISK_TECH_PVT(ast) = NULL; +- ++ + ast_mutex_lock(&release_lock); + cl_dequeue_chan(&cl_te, p); + close(p->pipe[0]); +@@ -2876,17 +3556,23 @@ + } + ast_channel_unlock(ast); + +- chan_misdn_log(1, bc->port, "* IND : HANGUP\tpid:%d ctx:%s dad:%s oad:%s State:%s\n", p->bc ? p->bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(p)); ++ chan_misdn_log(1, bc->port, ++ "* IND : HANGUP\tpid:%d context:%s dialed:%s caller:\"%s\" <%s> State:%s\n", ++ p->bc ? p->bc->pid : -1, ++ ast->context, ++ ast->exten, ++ ast->cid.cid_name ? ast->cid.cid_name : "", ++ ast->cid.cid_num ? ast->cid.cid_num : "", ++ misdn_get_ch_state(p)); + chan_misdn_log(3, bc->port, " --> l3id:%x\n", p->l3id); + chan_misdn_log(3, bc->port, " --> cause:%d\n", bc->cause); + chan_misdn_log(2, bc->port, " --> out_cause:%d\n", bc->out_cause); +- chan_misdn_log(2, bc->port, " --> state:%s\n", misdn_get_ch_state(p)); + + switch (p->state) { + case MISDN_INCOMING_SETUP: + case MISDN_CALLING: + p->state = MISDN_CLEANING; +- /* This is the only place in misdn_hangup, where we ++ /* This is the only place in misdn_hangup, where we + * can call release_chan, else it might create lot's of trouble + * */ + ast_log(LOG_NOTICE, "release channel, in CALLING/INCOMING_SETUP state.. no other events happened\n"); +@@ -2970,7 +3656,7 @@ + if (bc->need_release) { + misdn_lib_send_event(bc, EVENT_RELEASE); + } +- p->state = MISDN_CLEANING; ++ p->state = MISDN_CLEANING; + } else { + if (bc->need_disconnect) { + misdn_lib_send_event(bc, EVENT_DISCONNECT); +@@ -2989,7 +3675,7 @@ + static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame) + { + struct ast_frame *f,*f2; +- ++ + if (tmp->trans) { + f2 = ast_translate(tmp->trans, frame, 0); + f = ast_dsp_process(tmp->ast, tmp->dsp, f2); +@@ -3000,9 +3686,9 @@ + + if (!f || (f->frametype != AST_FRAME_DTMF)) + return frame; +- ++ + ast_debug(1, "Detected inband DTMF digit: %c\n", f->subclass); +- ++ + if (tmp->faxdetect && (f->subclass == 'f')) { + /* Fax tone -- Handle and return NULL */ + if (!tmp->faxhandled) { +@@ -3035,7 +3721,7 @@ + ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, context); + } + } else { +- ast_log(LOG_NOTICE, "Fax detected, but no fax extension ctx:%s exten:%s\n", context, ast->exten); ++ ast_log(LOG_NOTICE, "Fax detected but no fax extension, context:%s exten:%s\n", context, ast->exten); + } + } else { + ast_debug(1, "Already in a fax extension, not redirecting\n"); +@@ -3049,7 +3735,7 @@ + ast_debug(1, "Fax already handled\n"); + } + } +- ++ + if (tmp->ast_dsp && (f->subclass != 'f')) { + chan_misdn_log(2, tmp->bc->port, " --> * SEND: DTMF (AST_DSP) :%c\n", f->subclass); + } +@@ -3082,7 +3768,8 @@ + FD_ZERO(&rrfs); + FD_SET(tmp->pipe[0], &rrfs); + +- if (!(t = select(FD_SETSIZE, &rrfs, NULL, NULL, &tv))) { ++ t = select(FD_SETSIZE, &rrfs, NULL, NULL, &tv); ++ if (!t) { + chan_misdn_log(3, tmp->bc->port, "read Select Timed out\n"); + len = 160; + } +@@ -3151,7 +3838,7 @@ + { + struct chan_list *ch; + int i = 0; +- ++ + if (!ast || !(ch = MISDN_ASTERISK_TECH_PVT(ast))) { + return -1; + } +@@ -3160,12 +3847,12 @@ + chan_misdn_log(7, 0, "misdn_write: Returning because holded\n"); + return 0; + } +- ++ + if (!ch->bc ) { + ast_log(LOG_WARNING, "private but no bc\n"); + return -1; + } +- ++ + if (ch->notxtone) { + chan_misdn_log(7, ch->bc->port, "misdn_write: Returning because notxtone\n"); + return 0; +@@ -3176,16 +3863,16 @@ + chan_misdn_log(4, ch->bc->port, "misdn_write: * prods us\n"); + return 0; + } +- ++ + if (!(frame->subclass & prefformat)) { + chan_misdn_log(-1, ch->bc->port, "Got Unsupported Frame with Format:%d\n", frame->subclass); + return 0; + } +- + ++ + if (!frame->samples ) { + chan_misdn_log(4, ch->bc->port, "misdn_write: zero write\n"); +- ++ + if (!strcmp(frame->src,"ast_prod")) { + chan_misdn_log(1, ch->bc->port, "misdn_write: state (%s) prodded.\n", misdn_get_ch_state(ch)); + +@@ -3203,7 +3890,7 @@ + chan_misdn_log(8, ch->bc->port, "misdn_write: no addr for bc dropping:%d\n", frame->samples); + return 0; + } +- ++ + #ifdef MISDN_DEBUG + { + int i, max = 5 > frame->samples ? frame->samples : 5; +@@ -3224,7 +3911,7 @@ + if (!ch->dropped_frame_cnt) { + chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) dropping: %d frames addr:%x exten:%s cid:%s ch->state:%s bc_state:%d l3id:%x\n", frame->samples, ch->bc->addr, ast->exten, ast->cid.cid_num, misdn_get_ch_state( ch), ch->bc->bc_state, ch->bc->l3_id); + } +- ++ + if (++ch->dropped_frame_cnt > 100) { + ch->dropped_frame_cnt = 0; + chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) dropping: %d frames addr:%x dropped > 100 frames!\n", frame->samples, ch->bc->addr); +@@ -3241,7 +3928,7 @@ + cb_log(0, ch->bc->port, "Misdn Jitterbuffer Overflow.\n"); + } + } +- ++ + } else { + /*transmit without jitterbuffer*/ + i = misdn_lib_tx2misdn_frm(ch->bc, frame->data.ptr, frame->samples); +@@ -3250,15 +3937,11 @@ + return 0; + } + +- +- +- +-static enum ast_bridge_result misdn_bridge (struct ast_channel *c0, +- struct ast_channel *c1, int flags, +- struct ast_frame **fo, +- struct ast_channel **rc, +- int timeoutms) +- ++static enum ast_bridge_result misdn_bridge(struct ast_channel *c0, ++ struct ast_channel *c1, int flags, ++ struct ast_frame **fo, ++ struct ast_channel **rc, ++ int timeoutms) + { + struct chan_list *ch1, *ch2; + struct ast_channel *carr[2], *who; +@@ -3266,13 +3949,13 @@ + struct ast_frame *f; + int p1_b, p2_b; + int bridging; +- ++ + ch1 = get_chan_by_ast(c0); + ch2 = get_chan_by_ast(c1); + + carr[0] = c0; + carr[1] = c1; +- ++ + if (!(ch1 && ch2)) { + return -1; + } +@@ -3294,8 +3977,12 @@ + + ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name); + +- chan_misdn_log(1, ch1->bc->port, "* Making Native Bridge between %s and %s\n", ch1->bc->oad, ch2->bc->oad); +- ++ chan_misdn_log(1, ch1->bc->port, "* Making Native Bridge between \"%s\" <%s> and \"%s\" <%s>\n", ++ ch1->bc->caller.name, ++ ch1->bc->caller.number, ++ ch2->bc->caller.name, ++ ch2->bc->caller.number); ++ + if (! (flags & AST_BRIDGE_DTMF_CHANNEL_0) ) { + ch1->ignore_dtmf = 1; + } +@@ -3317,7 +4004,7 @@ + if (!f || f->frametype == AST_FRAME_CONTROL) { + /* got hangup .. */ + +- if (!f) ++ if (!f) + chan_misdn_log(4, ch1->bc->port, "Read Null Frame\n"); + else + chan_misdn_log(4, ch1->bc->port, "Read Frame Control class:%d\n", f->subclass); +@@ -3326,7 +4013,7 @@ + *rc = who; + break; + } +- ++ + if ( f->frametype == AST_FRAME_DTMF ) { + chan_misdn_log(1, 0, "Read DTMF %d from %s\n", f->subclass, who->exten); + +@@ -3334,16 +4021,16 @@ + *rc = who; + break; + } +- ++ + #if 0 + if (f->frametype == AST_FRAME_VOICE) { + chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid +1); +- ++ + continue; + } + #endif + +- ast_write(who == c0 ? c1 : c0, f); ++ ast_write((who == c0) ? c1 : c0, f); + } + + chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid + 1); +@@ -3371,11 +4058,11 @@ + chan_misdn_log(1, cl->bc->port, "Not sending Dialtone, because config wants it\n"); + return 0; + } +- ++ + chan_misdn_log(3, cl->bc->port, " --> Dial\n"); + + cl->ts = ast_get_indication_tone(ast->zone, "dial"); +- ++ + if (cl->ts) { + cl->notxtone = 0; + cl->norxtone = 0; +@@ -3429,7 +4116,7 @@ + + cl->notxtone = 1; + cl->norxtone = 1; +- ++ + return 0; + } + +@@ -3438,11 +4125,12 @@ + { + struct chan_list *cl; + +- if (!(cl = ast_calloc(1, sizeof(*cl)))) { ++ cl = ast_calloc(1, sizeof(*cl)); ++ if (!cl) { + chan_misdn_log(-1, 0, "misdn_request: malloc failed!"); + return NULL; + } +- ++ + cl->originator = orig; + cl->need_queue_hangup = 1; + cl->need_hangup = 1; +@@ -3456,38 +4144,54 @@ + { + struct ast_channel *tmp = NULL; + char group[BUFFERSIZE + 1] = ""; +- char buf[128]; +- char *buf2 = ast_strdupa(data), *ext = NULL, *port_str; +- char *tokb = NULL, *p = NULL; +- int channel = 0, port = 0; ++ char dial_str[128]; ++ char *dest_cp; ++ char *p = NULL; ++ int channel = 0; ++ int port = 0; + struct misdn_bchannel *newbc = NULL; + int dec = 0; ++ struct chan_list *cl; + +- struct chan_list *cl = init_chan_list(ORG_AST); ++ AST_DECLARE_APP_ARGS(args, ++ AST_APP_ARG(intf); /* interface token */ ++ AST_APP_ARG(ext); /* extension token */ ++ AST_APP_ARG(opts); /* options token */ ++ ); + +- snprintf(buf, sizeof(buf), "%s/%s", misdn_type, (char*)data); ++ snprintf(dial_str, sizeof(dial_str), "%s/%s", misdn_type, (char *) data); + +- port_str = strtok_r(buf2, "/", &tokb); ++ /* ++ * data is ---v ++ * Dial(mISDN/g:group_name[/extension[/options]]) ++ * Dial(mISDN/port[:preselected_channel][/extension[/options]]) ++ * ++ * The dial extension could be empty if you are using MISDN_KEYPAD ++ * to control ISDN provider features. ++ */ ++ dest_cp = ast_strdupa(data); ++ AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); ++ if (!args.ext) { ++ args.ext = ""; ++ } + +- ext = strtok_r(NULL, "/", &tokb); +- +- if (port_str) { +- if (port_str[0] == 'g' && port_str[1] == ':' ) { ++ if (!ast_strlen_zero(args.intf)) { ++ if (args.intf[0] == 'g' && args.intf[1] == ':' ) { + /* We make a group call lets checkout which ports are in my group */ +- port_str += 2; +- ast_copy_string(group, port_str, sizeof(group)); ++ args.intf += 2; ++ ast_copy_string(group, args.intf, sizeof(group)); + chan_misdn_log(2, 0, " --> Group Call group: %s\n", group); +- } else if ((p = strchr(port_str, ':'))) { ++ } else if ((p = strchr(args.intf, ':'))) { + /* we have a preselected channel */ +- *p = 0; +- channel = atoi(++p); +- port = atoi(port_str); ++ *p++ = 0; ++ channel = atoi(p); ++ port = atoi(args.intf); + chan_misdn_log(2, port, " --> Call on preselected Channel (%d).\n", channel); + } else { +- port = atoi(port_str); ++ port = atoi(args.intf); + } + } else { +- ast_log(LOG_WARNING, " --> ! IND : CALL dad:%s WITHOUT PORT/Group, check extensions.conf\n", ext); ++ ast_log(LOG_WARNING, " --> ! IND : Dial(%s) WITHOUT Port or Group, check extensions.conf\n", dial_str); + return NULL; + } + +@@ -3524,7 +4228,7 @@ + if (port >= port_start) { + next_chan = 1; + } +- ++ + if (port <= port_start && next_chan) { + int maxbchans=misdn_lib_get_maxchans(port); + if (++robin_channel >= maxbchans) { +@@ -3544,7 +4248,7 @@ + if (check && !port_up) { + chan_misdn_log(1, port, "L1 is not Up on this Port\n"); + } +- ++ + if (check && port_up < 0) { + ast_log(LOG_WARNING, "This port (%d) is blocked\n", port); + } +@@ -3564,7 +4268,7 @@ + } + } + } while (!newbc && robin_channel != rr->channel); +- } else { ++ } else { + for (port = misdn_cfg_get_next_port(0); port > 0; + port = misdn_cfg_get_next_port(port)) { + +@@ -3580,17 +4284,18 @@ + chan_misdn_log(4, port, "portup:%d\n", port_up); + + if (port_up > 0) { +- if ((newbc = misdn_lib_get_free_bc(port, 0, 0, dec))) { ++ newbc = misdn_lib_get_free_bc(port, 0, 0, dec); ++ if (newbc) { + break; + } + } + } + } + } +- ++ + /* Group dial failed ?*/ + if (!newbc) { +- ast_log(LOG_WARNING, ++ ast_log(LOG_WARNING, + "Could not Dial out on group '%s'.\n" + "\tEither the L2 and L1 on all of these ports where DOWN (see 'show application misdn_check_l2l1')\n" + "\tOr there was no free channel on none of the ports\n\n" +@@ -3603,34 +4308,38 @@ + chan_misdn_log(1, port, " --> preselected_channel: %d\n", channel); + } + newbc = misdn_lib_get_free_bc(port, channel, 0, dec); +- + if (!newbc) { +- ast_log(LOG_WARNING, "Could not create channel on port:%d with extensions:%s\n", port, ext); ++ ast_log(LOG_WARNING, "Could not create channel on port:%d for Dial(%s)\n", port, dial_str); + return NULL; + } + } +- + ++ + /* create ast_channel and link all the objects together */ ++ cl = init_chan_list(ORG_AST); ++ if (!cl) { ++ ast_log(LOG_WARNING, "Could not create Asterisk channel for Dial(%s)\n", dial_str); ++ return NULL; ++ } + cl->bc = newbc; +- +- tmp = misdn_new(cl, AST_STATE_RESERVED, ext, NULL, format, port, channel); ++ ++ tmp = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, port, channel); + if (!tmp) { + ast_log(LOG_ERROR, "Could not create Asterisk object\n"); + return NULL; + } + + cl->ast = tmp; +- ++ + /* register chan in local list */ + cl_queue_chan(&cl_te, cl); +- ++ + /* fill in the config into the objects */ +- read_config(cl, ORG_AST); ++ read_config(cl); + + /* important */ + cl->need_hangup = 0; +- ++ + return tmp; + } + +@@ -3638,7 +4347,7 @@ + static int misdn_send_text(struct ast_channel *chan, const char *text) + { + struct chan_list *tmp = chan->tech_pvt; +- ++ + if (tmp && tmp->bc) { + ast_copy_string(tmp->bc->display, text, sizeof(tmp->bc->display)); + misdn_lib_send_event(tmp->bc, EVENT_INFORMATION); +@@ -3646,7 +4355,7 @@ + ast_log(LOG_WARNING, "No chan_list but send_text request?\n"); + return -1; + } +- ++ + return 0; + } + +@@ -3658,7 +4367,7 @@ + .send_digit_begin = misdn_digit_begin, + .send_digit_end = misdn_digit_end, + .call = misdn_call, +- .bridge = misdn_bridge, ++ .bridge = misdn_bridge, + .hangup = misdn_hangup, + .answer = misdn_answer, + .read = misdn_read, +@@ -3690,7 +4399,7 @@ + + static int glob_channel = 0; + +-static void update_name(struct ast_channel *tmp, int port, int c) ++static void update_name(struct ast_channel *tmp, int port, int c) + { + int chan_offset = 0; + int tmp_port = misdn_cfg_get_next_port(0); +@@ -3699,7 +4408,7 @@ + if (tmp_port == port) { + break; + } +- chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2; ++ chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2; + } + if (c < 0) { + c = 0; +@@ -3737,7 +4446,7 @@ + + tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", 0, "%s/%s%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++); + if (tmp) { +- chan_misdn_log(2, 0, " --> * NEW CHANNEL dad:%s oad:%s\n", exten, callerid); ++ chan_misdn_log(2, 0, " --> * NEW CHANNEL dialed:%s caller:%s\n", exten, callerid); + + tmp->nativeformats = prefformat; + +@@ -3745,7 +4454,7 @@ + tmp->rawreadformat = format; + tmp->writeformat = format; + tmp->rawwriteformat = format; +- ++ + tmp->tech_pvt = chlist; + + misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); +@@ -3779,7 +4488,7 @@ + } else { + chan_misdn_log(-1, 0, "Unable to allocate channel structure\n"); + } +- ++ + return tmp; + } + +@@ -3792,7 +4501,11 @@ + } + } + +- chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad); ++ chan_misdn_log(6, bc->port, ++ "$$$ find_chan_by_bc: No channel found for dialed:%s caller:\"%s\" <%s>\n", ++ bc->dialed.number, ++ bc->caller.name, ++ bc->caller.number); + + return NULL; + } +@@ -3806,7 +4519,7 @@ + } + } + +- chan_misdn_log(6, 0, "$$$ find_chan: No channel found for pid:%d\n", pid); ++ chan_misdn_log(6, 0, "$$$ find_chan_by_pid: No channel found for pid:%d\n", pid); + + return NULL; + } +@@ -3819,21 +4532,29 @@ + return NULL; + } + +- chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d oad:%s dad:%s\n", bc->channel, bc->oad, bc->dad); ++ chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d dialed:%s caller:\"%s\" <%s>\n", ++ bc->channel, ++ bc->dialed.number, ++ bc->caller.name, ++ bc->caller.number); + for (; help; help = help->next) { + chan_misdn_log(4, bc->port, "$$$ find_holded: --> holded:%d channel:%d\n", help->state == MISDN_HOLDED, help->hold_info.channel); +- if ((help->state == MISDN_HOLDED) && ++ if ((help->state == MISDN_HOLDED) && + (help->hold_info.port == bc->port)) { + return help; + } + } +- chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad); ++ chan_misdn_log(6, bc->port, ++ "$$$ find_holded: No channel found for dialed:%s caller:\"%s\" <%s>\n", ++ bc->dialed.number, ++ bc->caller.name, ++ bc->caller.number); + + return NULL; + } + + +-static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3_id, int w) ++static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3_id, int w) + { + struct chan_list *help = list; + +@@ -3850,20 +4571,20 @@ + static void cl_queue_chan(struct chan_list **list, struct chan_list *chan) + { + chan_misdn_log(4, chan->bc ? chan->bc->port : 0, "* Queuing chan %p\n", chan); +- ++ + ast_mutex_lock(&cl_te_lock); + if (!*list) { + *list = chan; + } else { + struct chan_list *help = *list; +- for (; help->next; help = help->next); ++ for (; help->next; help = help->next); + help->next = chan; + } + chan->next = NULL; + ast_mutex_unlock(&cl_te_lock); + } + +-static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan) ++static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan) + { + struct chan_list *help; + +@@ -3879,13 +4600,13 @@ + ast_mutex_unlock(&cl_te_lock); + return; + } +- ++ + if (*list == chan) { + *list = (*list)->next; + ast_mutex_unlock(&cl_te_lock); + return; + } +- ++ + for (help = *list; help->next; help = help->next) { + if (help->next == chan) { + help->next = help->next->next; +@@ -3893,7 +4614,7 @@ + return; + } + } +- ++ + ast_mutex_unlock(&cl_te_lock); + } + +@@ -3902,7 +4623,7 @@ + + static int pbx_start_chan(struct chan_list *ch) + { +- int ret = ast_pbx_start(ch->ast); ++ int ret = ast_pbx_start(ch->ast); + + ch->need_hangup = (ret >= 0) ? 0 : 1; + +@@ -3948,12 +4669,14 @@ + } + + /** Isdn asks us to release channel, pendant to misdn_hangup **/ +-static void release_chan(struct misdn_bchannel *bc) { ++static void release_chan(struct misdn_bchannel *bc) ++{ + struct ast_channel *ast = NULL; + struct chan_list *ch; + + ast_mutex_lock(&release_lock); +- if (!(ch = find_chan_by_bc(cl_te, bc))) { ++ ch = find_chan_by_bc(cl_te, bc); ++ if (!ch) { + chan_misdn_log(1, bc->port, "release_chan: Ch not found!\n"); + ast_mutex_unlock(&release_lock); + return; +@@ -3961,12 +4684,12 @@ + + if (ch->ast) { + ast = ch->ast; +- } ++ } + + chan_misdn_log(5, bc->port, "release_chan: bc with l3id: %x\n", bc->l3_id); + + /* releasing jitterbuffer */ +- if (ch->jb ) { ++ if (ch->jb) { + misdn_jb_destroy(ch->jb); + ch->jb = NULL; + } else { +@@ -3994,7 +4717,14 @@ + close(ch->pipe[1]); + + if (ast && MISDN_ASTERISK_TECH_PVT(ast)) { +- chan_misdn_log(1, bc->port, "* RELEASING CHANNEL pid:%d ctx:%s dad:%s oad:%s state: %s\n", bc ? bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(ch)); ++ chan_misdn_log(1, bc->port, ++ "* RELEASING CHANNEL pid:%d context:%s dialed:%s caller:\"%s\" <%s> state: %s\n", ++ bc->pid, ++ ast->context, ++ ast->exten, ++ ast->cid.cid_name ? ast->cid.cid_name : "", ++ ast->cid.cid_num ? ast->cid.cid_num : "", ++ misdn_get_ch_state(ch)); + chan_misdn_log(3, bc->port, " --> * State Down\n"); + MISDN_ASTERISK_TECH_PVT(ast) = NULL; + +@@ -4040,7 +4770,7 @@ + + if (!ch->noautorespond_on_setup) { + if (bc->nt) { +- int ret; ++ int ret; + ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); + } else { + int ret; +@@ -4054,10 +4784,15 @@ + ch->state = MISDN_INCOMING_SETUP; + } + +- chan_misdn_log(1, bc->port, "* Starting Ast ctx:%s dad:%s oad:%s with 's' extension\n", ast->context, ast->exten, ast->cid.cid_num); +- +- strncpy(ast->exten, "s", 2); +- ++ chan_misdn_log(1, bc->port, ++ "* Starting Ast context:%s dialed:%s caller:\"%s\" <%s> with 's' extension\n", ++ ast->context, ++ ast->exten, ++ ast->cid.cid_name ? ast->cid.cid_name : "", ++ ast->cid.cid_num ? ast->cid.cid_num : ""); ++ ++ strcpy(ast->exten, "s"); ++ + if (pbx_start_chan(ch) < 0) { + ast = NULL; + hangup_chan(ch); +@@ -4065,8 +4800,8 @@ + + misdn_lib_send_event(bc, bc->nt ? EVENT_RELEASE_COMPLETE : EVENT_DISCONNECT); + } +- +- ++ ++ + while (!ast_strlen_zero(predial) ) { + fr.frametype = AST_FRAME_DTMF; + fr.subclass = *predial; +@@ -4118,7 +4853,7 @@ + * + chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Congestion pid:%d\n", bc ? bc->pid : -1); + ch->state = MISDN_BUSY; +- ++ + ast_queue_control(ast, AST_CONTROL_CONGESTION); + */ + break; +@@ -4133,11 +4868,11 @@ + } + + chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Busy pid:%d\n", bc ? bc->pid : -1); +- ++ + ast_queue_control(ast, AST_CONTROL_BUSY); +- ++ + ch->need_busy = 0; +- ++ + break; + } + } +@@ -4184,6 +4919,7 @@ + void export_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch) + { + char tmp[32]; ++ + chan_misdn_log(3, bc->port, " --> EXPORT_PID: pid:%d\n", bc->pid); + snprintf(tmp, sizeof(tmp), "%d", bc->pid); + pbx_builtin_setvar_helper(chan, "_MISDN_PID", tmp); +@@ -4210,7 +4946,7 @@ + int add_in_calls(int port) + { + int max_in_calls; +- ++ + misdn_cfg_get(port, MISDN_CFG_MAX_IN, &max_in_calls, sizeof(max_in_calls)); + misdn_in_calls[port]++; + +@@ -4218,14 +4954,14 @@ + ast_log(LOG_NOTICE, "Marking Incoming Call on port[%d]\n", port); + return misdn_in_calls[port] - max_in_calls; + } +- ++ + return 0; + } + + int add_out_calls(int port) + { + int max_out_calls; +- ++ + misdn_cfg_get(port, MISDN_CFG_MAX_OUT, &max_out_calls, sizeof(max_out_calls)); + + if (max_out_calls >= 0 && max_out_calls <= misdn_out_calls[port]) { +@@ -4234,11 +4970,12 @@ + } + + misdn_out_calls[port]++; +- ++ + return 0; + } + +-static void start_pbx(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) { ++static void start_pbx(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) ++{ + if (pbx_start_chan(ch) < 0) { + hangup_chan(ch); + chan_misdn_log(-1, bc->port, "ast_pbx_start returned <0 in SETUP\n"); +@@ -4251,10 +4988,11 @@ + } + } + +-static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) { ++static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) ++{ + ch->state = MISDN_WAITING4DIGS; + misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); +- if (bc->nt && !bc->dad[0]) { ++ if (bc->nt && !bc->dialed.number[0]) { + dialtone_indicate(ch); + } + } +@@ -4267,20 +5005,27 @@ + cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) + { + struct chan_list *ch = find_chan_by_bc(cl_te, bc); +- ++ + if (event != EVENT_BCHAN_DATA && event != EVENT_TONE_GENERATE) { /* Debug Only Non-Bchan */ + int debuglevel = 1; + if ( event == EVENT_CLEANUP && !user_data) { + debuglevel = 5; + } + +- chan_misdn_log(debuglevel, bc->port, "I IND :%s oad:%s dad:%s pid:%d state:%s\n", manager_isdn_get_info(event), bc->oad, bc->dad, bc->pid, ch ? misdn_get_ch_state(ch) : "none"); ++ chan_misdn_log(debuglevel, bc->port, ++ "I IND :%s caller:\"%s\" <%s> dialed:%s pid:%d state:%s\n", ++ manager_isdn_get_info(event), ++ bc->caller.name, ++ bc->caller.number, ++ bc->dialed.number, ++ bc->pid, ++ ch ? misdn_get_ch_state(ch) : "none"); + if (debuglevel == 1) { + misdn_lib_log_ies(bc); + chan_misdn_log(4, bc->port, " --> bc_state:%s\n", bc_state2str(bc->bc_state)); + } + } +- ++ + if (!ch) { + switch(event) { + case EVENT_SETUP: +@@ -4302,7 +5047,7 @@ + return -1; + } + } +- ++ + if (ch) { + switch (event) { + case EVENT_TONE_GENERATE: +@@ -4325,8 +5070,8 @@ + } + } + } +- +- ++ ++ + switch (event) { + case EVENT_PORT_ALARM: + { +@@ -4334,17 +5079,17 @@ + misdn_cfg_get(bc->port, MISDN_CFG_ALARM_BLOCK, &boa, sizeof(boa)); + if (boa) { + cb_log(1, bc->port, " --> blocking\n"); +- misdn_lib_port_block(bc->port); ++ misdn_lib_port_block(bc->port); + } + } + break; + case EVENT_BCHAN_ACTIVATED: + break; +- ++ + case EVENT_NEW_CHANNEL: + update_name(ch->ast,bc->port,bc->channel); + break; +- ++ + case EVENT_NEW_L3ID: + ch->l3id=bc->l3_id; + ch->addr=bc->addr; +@@ -4354,7 +5099,7 @@ + if (!ch) { + ch = find_holded(cl_te,bc); + } +- ++ + if (!ch) { + ast_log(LOG_WARNING, "NEW_BC without chan_list?\n"); + break; +@@ -4364,7 +5109,7 @@ + ch->bc = (struct misdn_bchannel *)user_data; + } + break; +- ++ + case EVENT_DTMF_TONE: + { + /* sending INFOS as DTMF-Frames :) */ +@@ -4380,7 +5125,7 @@ + fr.mallocd = 0; + fr.offset = 0; + fr.delivery = ast_tv(0,0); +- ++ + if (!ch->ignore_dtmf) { + chan_misdn_log(2, bc->port, " --> DTMF:%c\n", bc->dtmf); + ast_queue_frame(ch->ast, &fr); +@@ -4391,12 +5136,12 @@ + break; + case EVENT_STATUS: + break; +- ++ + case EVENT_INFORMATION: + if (ch->state != MISDN_CONNECTED) { + stop_indicate(ch); + } +- ++ + if (!ch->ast) { + break; + } +@@ -4408,25 +5153,23 @@ + ast_copy_string(bc->info_dad, bc->keypad, sizeof(bc->info_dad)); + } + +- strncat(bc->dad, bc->info_dad, sizeof(bc->dad) - strlen(bc->dad) - 1); +- ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); ++ strncat(bc->dialed.number, bc->info_dad, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); ++ ast_copy_string(ch->ast->exten, bc->dialed.number, sizeof(ch->ast->exten)); + + /* Check for Pickup Request first */ + if (!strcmp(ch->ast->exten, ast_pickup_ext())) { + if (ast_pickup_call(ch->ast)) { + hangup_chan(ch); + } else { +- struct ast_channel *chan = ch->ast; + ch->state = MISDN_CALLING_ACKNOWLEDGE; +- ast_setstate(chan, AST_STATE_DOWN); + hangup_chan(ch); + ch->ast = NULL; + break; + } + } +- +- if (!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { +- if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) { ++ ++ if (!ast_canmatch_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { ++ if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->caller.number)) { + ast_log(LOG_WARNING, "Extension can never match, So jumping to 'i' extension. port(%d)\n", bc->port); + strcpy(ch->ast->exten, "i"); + +@@ -4454,13 +5197,13 @@ + ch->overlap_tv = ast_tvnow(); + ast_mutex_unlock(&ch->overlap_tv_lock); + if (ch->overlap_dial_task == -1) { +- ch->overlap_dial_task = ++ ch->overlap_dial_task = + misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch); + } + break; + } + +- if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { ++ if (ast_exists_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { + ch->state = MISDN_DIALING; + start_pbx(ch, bc, ch->ast); + } +@@ -4483,11 +5226,11 @@ + misdn_cfg_get(0, MISDN_GEN_APPEND_DIGITS2EXTEN, &digits, sizeof(digits)); + if (ch->state != MISDN_CONNECTED ) { + if (digits) { +- strncat(bc->dad, bc->info_dad, sizeof(bc->dad) - strlen(bc->dad) - 1); +- ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); ++ strncat(bc->dialed.number, bc->info_dad, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); ++ ast_copy_string(ch->ast->exten, bc->dialed.number, sizeof(ch->ast->exten)); + ast_cdr_update(ch->ast); + } +- ++ + ast_queue_frame(ch->ast, &fr); + } + } +@@ -4495,10 +5238,9 @@ + case EVENT_SETUP: + { + struct chan_list *ch = find_chan_by_bc(cl_te, bc); +- int msn_valid = misdn_cfg_is_msn_valid(bc->port, bc->dad); ++ int msn_valid = misdn_cfg_is_msn_valid(bc->port, bc->dialed.number); + struct ast_channel *chan; + int exceed; +- int pres, screen; + int ai; + int im; + +@@ -4538,13 +5280,11 @@ + ch->bc = bc; + ch->l3id = bc->l3_id; + ch->addr = bc->addr; +- ch->originator = ORG_MISDN; + +- chan = misdn_new(ch, AST_STATE_RESERVED, bc->dad, bc->oad, AST_FORMAT_ALAW, bc->port, bc->channel); +- ++ chan = misdn_new(ch, AST_STATE_RESERVED, bc->dialed.number, bc->caller.number, AST_FORMAT_ALAW, bc->port, bc->channel); + if (!chan) { + misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); +- ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n"); ++ ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n"); + return 0; + } + +@@ -4556,50 +5296,45 @@ + pbx_builtin_setvar_helper(chan, "MAX_OVERFLOW", tmp); + } + +- read_config(ch, ORG_MISDN); ++ read_config(ch); + + export_ch(chan, bc, ch); + + ch->ast->rings = 1; + ast_setstate(ch->ast, AST_STATE_RINGING); + +- switch (bc->pres) { +- case 1: +- pres = AST_PRES_RESTRICTED; +- chan_misdn_log(2, bc->port, " --> PRES: Restricted (1)\n"); +- break; +- case 2: +- pres = AST_PRES_UNAVAILABLE; +- chan_misdn_log(2, bc->port, " --> PRES: Unavailable (2)\n"); +- break; +- default: +- pres = AST_PRES_ALLOWED; +- chan_misdn_log(2, bc->port, " --> PRES: Allowed (%d)\n", bc->pres); +- break; +- } ++ /* Update asterisk channel caller information */ ++ chan_misdn_log(2, bc->port, " --> TON: %s(%d)\n", misdn_to_str_ton(bc->caller.number_type), bc->caller.number_type); ++ chan_misdn_log(2, bc->port, " --> PLAN: %s(%d)\n", misdn_to_str_plan(bc->caller.number_plan), bc->caller.number_plan); ++ chan->cid.cid_ton = misdn_to_ast_ton(bc->caller.number_type) ++ | misdn_to_ast_plan(bc->caller.number_plan); + +- switch (bc->screen) { +- default: +- case 0: +- screen = AST_PRES_USER_NUMBER_UNSCREENED; +- chan_misdn_log(2, bc->port, " --> SCREEN: Unscreened (%d)\n", bc->screen); +- break; +- case 1: +- screen = AST_PRES_USER_NUMBER_PASSED_SCREEN; +- chan_misdn_log(2, bc->port, " --> SCREEN: Passed screen (1)\n"); +- break; +- case 2: +- screen = AST_PRES_USER_NUMBER_FAILED_SCREEN; +- chan_misdn_log(2, bc->port, " --> SCREEN: failed screen (2)\n"); +- break; +- case 3: +- screen = AST_PRES_NETWORK_NUMBER; +- chan_misdn_log(2, bc->port, " --> SCREEN: Network Number (3)\n"); +- break; ++ chan_misdn_log(2, bc->port, " --> PRES: %s(%d)\n", misdn_to_str_pres(bc->caller.presentation), bc->caller.presentation); ++ chan_misdn_log(2, bc->port, " --> SCREEN: %s(%d)\n", misdn_to_str_screen(bc->caller.screening), bc->caller.screening); ++ chan->cid.cid_pres = misdn_to_ast_pres(bc->caller.presentation) ++ | misdn_to_ast_screen(bc->caller.screening); ++ ++ ast_set_callerid(chan, bc->caller.number, NULL, bc->caller.number); ++ ++ if (!ast_strlen_zero(bc->redirecting.from.number)) { ++ struct ast_party_redirecting redirecting; ++ ++ /* Add configured prefix to redirecting.from.number */ ++ misdn_add_number_prefix(bc->port, bc->redirecting.from.number_type, bc->redirecting.from.number, sizeof(bc->redirecting.from.number)); ++ ++ /* Update asterisk channel redirecting information */ ++ ast_party_redirecting_set_init(&redirecting, &chan->redirecting); ++ redirecting.from.number = bc->redirecting.from.number; ++ redirecting.from.number_type = ++ misdn_to_ast_ton(bc->redirecting.from.number_type) ++ | misdn_to_ast_plan(bc->redirecting.from.number_plan); ++ redirecting.from.number_presentation = ++ misdn_to_ast_pres(bc->redirecting.from.presentation) ++ | misdn_to_ast_screen(bc->redirecting.from.screening); ++ redirecting.reason = misdn_to_ast_reason(bc->redirecting.reason); ++ ast_channel_set_redirecting(chan, &redirecting); + } + +- chan->cid.cid_pres = pres | screen; +- + pbx_builtin_setvar_helper(chan, "TRANSFERCAPABILITY", ast_transfercapability2str(bc->capability)); + chan->transfercapability = bc->capability; + +@@ -4628,7 +5363,7 @@ + break; + } + } +- } /* end for */ ++ } + if (i == ARRAY_LEN(allowed_bearers_array)) { + /* We did not find the bearer capability */ + chan_misdn_log(0, bc->port, "Bearer capability not allowed: %s(%d)\n", +@@ -4653,7 +5388,6 @@ + hangup_chan(ch); + } else { + ch->state = MISDN_CALLING_ACKNOWLEDGE; +- ast_setstate(chan, AST_STATE_DOWN); + hangup_chan(ch); + ch->ast = NULL; + break; +@@ -4670,16 +5404,16 @@ + break; + } + +- /* check if we should jump into s when we have no dad */ ++ /* check if we should jump into s when we have no dialed.number */ + misdn_cfg_get(bc->port, MISDN_CFG_IMMEDIATE, &im, sizeof(im)); +- if (im && ast_strlen_zero(bc->dad)) { ++ if (im && ast_strlen_zero(bc->dialed.number)) { + do_immediate_setup(bc, ch, chan); + break; + } + + chan_misdn_log(5, bc->port, "CONTEXT:%s\n", ch->context); +- if (!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { +- if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) { ++ if (!ast_canmatch_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { ++ if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->caller.number)) { + ast_log(LOG_WARNING, "Extension can never match, So jumping to 'i' extension. port(%d)\n", bc->port); + strcpy(ch->ast->exten, "i"); + misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE); +@@ -4703,8 +5437,8 @@ + break; + } + +- /* Whatever happens, when sending_complete is set or we are PTMP TE, we will definitely +- * jump into the dialplan, when the dialed extension does not exist, the 's' extension ++ /* Whatever happens, when sending_complete is set or we are PTMP TE, we will definitely ++ * jump into the dialplan, when the dialed extension does not exist, the 's' extension + * will be used by Asterisk automatically. */ + if (bc->sending_complete || (!bc->nt && !misdn_lib_is_ptp(bc->port))) { + if (!ch->noautorespond_on_setup) { +@@ -4719,17 +5453,17 @@ + + + /* +- * When we are NT and overlapdial is set and if ++ * When we are NT and overlapdial is set and if + * the number is empty, we wait for the ISDN timeout + * instead of our own timer. + */ +- if (ch->overlap_dial && bc->nt && !bc->dad[0] ) { ++ if (ch->overlap_dial && bc->nt && !bc->dialed.number[0] ) { + wait_for_digits(ch, bc, chan); + break; + } + +- /* +- * If overlapdial we will definitely send a SETUP_ACKNOWLEDGE and wait for more ++ /* ++ * If overlapdial we will definitely send a SETUP_ACKNOWLEDGE and wait for more + * Infos with a Interdigit Timeout. + * */ + if (ch->overlap_dial) { +@@ -4739,16 +5473,16 @@ + + wait_for_digits(ch, bc, chan); + if (ch->overlap_dial_task == -1) { +- ch->overlap_dial_task = ++ ch->overlap_dial_task = + misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch); + } + break; + } + +- /* If the extension does not exist and we're not TE_PTMP we wait for more digits ++ /* If the extension does not exist and we're not TE_PTMP we wait for more digits + * without interdigit timeout. + * */ +- if (!ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { ++ if (!ast_exists_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { + wait_for_digits(ch, bc, chan); + break; + } +@@ -4756,29 +5490,30 @@ + /* + * If the extension exists let's just jump into it. + * */ +- if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { ++ if (ast_exists_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { + misdn_lib_send_event(bc, bc->need_more_infos ? EVENT_SETUP_ACKNOWLEDGE : EVENT_PROCEEDING); + ch->state = MISDN_DIALING; + start_pbx(ch, bc, chan); + break; + } ++ break; + } +- break; + + case EVENT_SETUP_ACKNOWLEDGE: + ch->state = MISDN_CALLING_ACKNOWLEDGE; + +- if (bc->channel) ++ if (bc->channel) { + update_name(ch->ast,bc->port,bc->channel); +- ++ } ++ + if (!ast_strlen_zero(bc->infos_pending)) { + /* TX Pending Infos */ +- strncat(bc->dad, bc->infos_pending, sizeof(bc->dad) - strlen(bc->dad) - 1); ++ strncat(bc->dialed.number, bc->infos_pending, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); + + if (!ch->ast) { + break; + } +- ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); ++ ast_copy_string(ch->ast->exten, bc->dialed.number, sizeof(ch->ast->exten)); + ast_copy_string(bc->info_dad, bc->infos_pending, sizeof(bc->info_dad)); + ast_copy_string(bc->infos_pending, "", sizeof(bc->infos_pending)); + +@@ -4792,7 +5527,7 @@ + } + + ch->state = MISDN_PROCEEDING; +- ++ + if (!ch->ast) { + break; + } +@@ -4809,7 +5544,7 @@ + misdn_inband_avail(bc)) { + start_bc_tones(ch); + } +- ++ + ch->state = MISDN_PROGRESS; + + if (!ch->ast) { +@@ -4820,16 +5555,16 @@ + break; + case EVENT_ALERTING: + ch->state = MISDN_ALERTING; +- ++ + if (!ch->ast) { + break; + } + + ast_queue_control(ch->ast, AST_CONTROL_RINGING); + ast_setstate(ch->ast, AST_STATE_RINGING); +- ++ + cb_log(7, bc->port, " --> Set State Ringing\n"); +- ++ + if (misdn_cap_is_speech(bc->capability) && misdn_inband_avail(bc)) { + cb_log(1, bc->port, "Starting Tones, we have inband Data\n"); + start_bc_tones(ch); +@@ -4843,38 +5578,41 @@ + } + break; + case EVENT_CONNECT: +- { +- struct ast_channel *bridged; ++ { ++ struct ast_party_connected_line connected; + +- /*we answer when we've got our very new L3 ID from the NT stack */ +- misdn_lib_send_event(bc, EVENT_CONNECT_ACKNOWLEDGE); ++ /* we answer when we've got our very new L3 ID from the NT stack */ ++ misdn_lib_send_event(bc, EVENT_CONNECT_ACKNOWLEDGE); + +- if (!ch->ast) { +- break; +- } ++ if (!ch->ast) { ++ break; ++ } + +- bridged = ast_bridged_channel(ch->ast); +- stop_indicate(ch); ++ stop_indicate(ch); + +- if (bridged && !strcasecmp(bridged->tech->type, "mISDN")) { +- struct chan_list *bridged_ch = MISDN_ASTERISK_TECH_PVT(bridged); ++ /* Add configured prefix to connected.number */ ++ misdn_add_number_prefix(bc->port, bc->connected.number_type, bc->connected.number, sizeof(bc->connected.number)); + +- chan_misdn_log(1, bc->port, " --> copying cpndialplan:%d and cad:%s to the A-Channel\n", bc->cpnnumplan, bc->cad); +- if (bridged_ch) { +- bridged_ch->bc->cpnnumplan = bc->cpnnumplan; +- ast_copy_string(bridged_ch->bc->cad, bc->cad, sizeof(bridged_ch->bc->cad)); +- } +- } +- } ++ /* Update the connected line information on the other channel */ ++ ast_party_connected_line_init(&connected); ++ connected.id.number = bc->connected.number; ++ connected.id.number_type = misdn_to_ast_ton(bc->connected.number_type) ++ | misdn_to_ast_plan(bc->connected.number_plan); ++ connected.id.number_presentation = misdn_to_ast_pres(bc->connected.presentation) ++ | misdn_to_ast_screen(bc->connected.screening); ++ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_queue_connected_line_update(ch->ast, &connected); ++ + ch->l3id = bc->l3_id; + ch->addr = bc->addr; + + start_bc_tones(ch); +- ++ + ch->state = MISDN_CONNECTED; +- ++ + ast_queue_control(ch->ast, AST_CONTROL_ANSWER); + break; ++ } + case EVENT_CONNECT_ACKNOWLEDGE: + ch->l3id = bc->l3_id; + ch->addr = bc->addr; +@@ -4896,7 +5634,7 @@ + alternative number, then play it instead of + immediately releasing the call */ + chan_misdn_log(1, bc->port, " --> Inband Info Avail, not sending RELEASE\n"); +- ++ + ch->state = MISDN_DISCONNECTED; + start_bc_tones(ch); + +@@ -4948,15 +5686,16 @@ + stop_bc_tones(ch); + hangup_chan(ch); + +- if (ch) ++ if (ch) { + ch->state = MISDN_CLEANING; ++ } + + release_chan(bc); + break; + case EVENT_BCHAN_ERROR: + case EVENT_CLEANUP: + stop_bc_tones(ch); +- ++ + switch (ch->state) { + case MISDN_CALLING: + bc->cause = AST_CAUSE_DESTINATION_OUT_OF_ORDER; +@@ -4964,7 +5703,7 @@ + default: + break; + } +- ++ + hangup_chan(ch); + release_chan(bc); + break; +@@ -4997,23 +5736,23 @@ + + res = generate(ast, tmp, tone_len, tone_len); + ast->generatordata = tmp; +- ++ + if (res) { + ast_log(LOG_WARNING, "Auto-deactivating generator\n"); + ast_deactivate_generator(ast); + } else { + bc->tone_cnt = 0; + } ++ break; + } +- break; +- + case EVENT_BCHAN_DATA: + if (ch->bc->AOCD_need_export) { + export_aoc_vars(ch->originator, ch->ast, ch->bc); + } + if (!misdn_cap_is_speech(ch->bc->capability)) { + struct ast_frame frame; +- /*In Data Modes we queue frames*/ ++ ++ /* In Data Modes we queue frames */ + frame.frametype = AST_FRAME_VOICE; /* we have no data frames yet */ + frame.subclass = AST_FORMAT_ALAW; + frame.datalen = bc->bframe_len; +@@ -5024,8 +5763,9 @@ + frame.src = NULL; + frame.data.ptr = bc->bframe; + +- if (ch->ast) ++ if (ch->ast) { + ast_queue_frame(ch->ast, &frame); ++ } + } else { + fd_set wrfs; + struct timeval tv = { 0, 0 }; +@@ -5040,12 +5780,12 @@ + chan_misdn_log(9, bc->port, "Select Timed out\n"); + break; + } +- ++ + if (t < 0) { + chan_misdn_log(-1, bc->port, "Select Error (err=%s)\n", strerror(errno)); + break; + } +- ++ + if (FD_ISSET(ch->pipe[1], &wrfs)) { + chan_misdn_log(9, bc->port, "writing %d bytes to asterisk\n", bc->bframe_len); + if (write(ch->pipe[1], bc->bframe, bc->bframe_len) <= 0) { +@@ -5095,11 +5835,12 @@ + misdn_lib_send_event(bc, EVENT_RELEASE); + } + break; +- case MISDN_CLEANING: ++ case MISDN_CLEANING: + chan_misdn_log(1, bc->port, " --> in state cleaning .. so ignoring, the stack should clean it for us\n"); + break; + default: + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); ++ break; + } + break; + +@@ -5133,13 +5874,13 @@ + if (hold_ast) { + ast_moh_stop(hold_ast); + } +- ++ + if (misdn_lib_send_event(bc, EVENT_RETRIEVE_ACKNOWLEDGE) < 0) { + chan_misdn_log(4, bc->port, " --> RETRIEVE_ACK failed\n"); + misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT); + } ++ break; + } +- break; + case EVENT_HOLD: + { + int hold_allowed; +@@ -5157,7 +5898,7 @@ + chan_misdn_log(2, bc->port, "Bridge Partner is of type: %s\n", bridged->tech->type); + ch->state = MISDN_HOLDED; + ch->l3id = bc->l3_id; +- ++ + misdn_lib_send_event(bc, EVENT_HOLD_ACKNOWLEDGE); + + /* XXX This should queue an AST_CONTROL_HOLD frame on this channel +@@ -5172,11 +5913,11 @@ + misdn_lib_send_event(bc, EVENT_HOLD_REJECT); + chan_misdn_log(0, bc->port, "We aren't bridged to anybody\n"); + } +- } + break; ++ } + case EVENT_FACILITY: + print_facility(&(bc->fac_in), bc); +- ++ + switch (bc->fac_in.Function) { + #ifdef HAVE_MISDN_FAC_RESULT + case Fac_RESULT: +@@ -5190,7 +5931,7 @@ + ch_br = MISDN_ASTERISK_TECH_PVT(bridged); + /*ch->state = MISDN_FACILITY_DEFLECTED;*/ + if (ch_br->bc) { +- if (ast_exists_extension(bridged, ch->context, (char *)bc->fac_in.u.CDeflection.DeflectedToNumber, 1, bc->oad)) { ++ if (ast_exists_extension(bridged, ch->context, (char *) bc->fac_in.u.CDeflection.DeflectedToNumber, 1, bc->caller.number)) { + ch_br->state = MISDN_DIALING; + if (pbx_start_chan(ch_br) < 0) { + chan_misdn_log(-1, ch_br->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n"); +@@ -5199,7 +5940,7 @@ + } + } + misdn_lib_send_event(bc, EVENT_DISCONNECT); +- } ++ } + break; + case Fac_AOCDCurrency: + if (ch) { +@@ -5225,7 +5966,7 @@ + default: + chan_misdn_log(0, bc->port," --> not yet handled: facility type:%d\n", bc->fac_in.Function); + } +- ++ + break; + case EVENT_RESTART: + if (!bc->dummy) { +@@ -5237,7 +5978,7 @@ + chan_misdn_log(1, 0, "Got Unknown Event\n"); + break; + } +- ++ + return RESPONSE_OK; + } + +@@ -5258,11 +5999,11 @@ + ast_log(LOG_VERBOSE, "-- Unregistering mISDN Channel Driver --\n"); + + misdn_tasks_destroy(); +- ++ + if (!g_config_initialized) { + return 0; + } +- ++ + ast_cli_unregister_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry)); + + /* ast_unregister_application("misdn_crypt"); */ +@@ -5275,7 +6016,7 @@ + free_robin_list(); + misdn_cfg_destroy(); + misdn_lib_destroy(); +- ++ + if (misdn_debug) { + ast_free(misdn_debug); + } +@@ -5283,7 +6024,7 @@ + ast_free(misdn_debug_only); + } + ast_free(misdn_ports); +- ++ + return 0; + } + +@@ -5301,18 +6042,18 @@ + }; + + max_ports = misdn_lib_maxports_get(); +- ++ + if (max_ports <= 0) { + ast_log(LOG_ERROR, "Unable to initialize mISDN\n"); + return AST_MODULE_LOAD_DECLINE; + } +- ++ + if (misdn_cfg_init(max_ports, 0)) { + ast_log(LOG_ERROR, "Unable to initialize misdn_config.\n"); + return AST_MODULE_LOAD_DECLINE; + } + g_config_initialized = 1; +- ++ + misdn_debug = ast_malloc(sizeof(int) * (max_ports + 1)); + if (!misdn_debug) { + ast_log(LOG_ERROR, "Out of memory for misdn_debug\n"); +@@ -5369,7 +6110,7 @@ + unload_module(); + return AST_MODULE_LOAD_DECLINE; + } +- ++ + ast_cli_register_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry)); + + ast_register_application("misdn_set_opt", misdn_set_opt_exec, "misdn_set_opt", +@@ -5400,7 +6141,7 @@ + " vt - Tx gain control, optarg is gain\n" + ); + +- ++ + ast_register_application("misdn_facility", misdn_facility_exec, "misdn_facility", + "misdn_facility(<FACILITY_TYPE>|<ARG1>|..)\n" + "Sends the Facility Message FACILITY_TYPE with \n" +@@ -5432,13 +6173,13 @@ + misdn_cfg_get(0, MISDN_GEN_TRACEFILE, global_tracefile, sizeof(global_tracefile)); + + /* start the l1 watchers */ +- ++ + for (port = misdn_cfg_get_next_port(0); port >= 0; port = misdn_cfg_get_next_port(port)) { + int l1timeout; + misdn_cfg_get(port, MISDN_CFG_L1_TIMEOUT, &l1timeout, sizeof(l1timeout)); + if (l1timeout) { + chan_misdn_log(4, 0, "Adding L1watcher task: port:%d timeout:%ds\n", port, l1timeout); +- misdn_tasks_add(l1timeout * 1000, misdn_l1_task, &misdn_ports[port]); ++ misdn_tasks_add(l1timeout * 1000, misdn_l1_task, &misdn_ports[port]); + } + } + +@@ -5462,19 +6203,20 @@ + { + struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan); + char *parse; ++ + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(facility_type); + AST_APP_ARG(arg)[99]; + ); + + chan_misdn_log(0, 0, "TYPE: %s\n", chan->tech->type); +- ++ + if (strcasecmp(chan->tech->type, "mISDN")) { + ast_log(LOG_WARNING, "misdn_facility makes only sense with chan_misdn channels!\n"); + return -1; + } +- +- if (ast_strlen_zero((char *)data)) { ++ ++ if (ast_strlen_zero((char *) data)) { + ast_log(LOG_WARNING, "misdn_facility requires arguments: facility_type[,<args>]\n"); + return -1; + } +@@ -5493,7 +6235,9 @@ + } + + if (strlen(args.arg[0]) >= sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)) { +- ast_log(LOG_WARNING, "Facility: Number argument too long (up to %d digits are allowed). Ignoring.\n", (int)sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)); ++ ast_log(LOG_WARNING, ++ "Facility: Number argument too long (up to %d digits are allowed). Ignoring.\n", ++ (int) sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)); + return 0; + } + ch->bc->fac_out.Function = Fac_CD; +@@ -5517,11 +6261,11 @@ + int port_up; + + AST_DECLARE_APP_ARGS(args, +- AST_APP_ARG(grouppar); +- AST_APP_ARG(timeout); ++ AST_APP_ARG(grouppar); ++ AST_APP_ARG(timeout); + ); + +- if (ast_strlen_zero((char *)data)) { ++ if (ast_strlen_zero((char *) data)) { + ast_log(LOG_WARNING, "misdn_check_l2l1 Requires arguments\n"); + return -1; + } +@@ -5544,7 +6288,7 @@ + ast_copy_string(group, port_str, sizeof(group)); + chan_misdn_log(2, 0, "Checking Ports in group: %s\n", group); + +- for ( port = misdn_cfg_get_next_port(port); ++ for (port = misdn_cfg_get_next_port(port); + port > 0; + port = misdn_cfg_get_next_port(port)) { + char cfg_group[BUFFERSIZE + 1]; +@@ -5555,7 +6299,6 @@ + + if (!strcasecmp(cfg_group, group)) { + port_up = misdn_lib_port_up(port, 1); +- + if (!port_up) { + chan_misdn_log(2, 0, " --> port '%d'\n", port); + misdn_lib_get_port_up(port); +@@ -5595,7 +6338,7 @@ + ast_log(LOG_WARNING, "misdn_set_opt makes only sense with chan_misdn channels!\n"); + return -1; + } +- ++ + if (ast_strlen_zero((char *)data)) { + ast_log(LOG_WARNING, "misdn_set_opt Requires arguments\n"); + return -1; +@@ -5611,14 +6354,14 @@ + neglect = 1; + tok++; + } +- ++ + switch(tok[0]) { +- ++ + case 'd' : + ast_copy_string(ch->bc->display, ++tok, sizeof(ch->bc->display)); + chan_misdn_log(1, ch->bc->port, "SETOPT: Display:%s\n", ch->bc->display); + break; +- ++ + case 'n': + chan_misdn_log(1, ch->bc->port, "SETOPT: No DSP\n"); + ch->bc->nodsp = 1; +@@ -5677,7 +6420,7 @@ + break; + } + break; +- ++ + case 'c': + keyidx = atoi(++tok); + { +@@ -5699,7 +6442,7 @@ + } + case 'e': + chan_misdn_log(1, ch->bc->port, "SETOPT: EchoCancel\n"); +- ++ + if (neglect) { + chan_misdn_log(1, ch->bc->port, " --> disabled\n"); + #ifdef MISDN_1_2 +@@ -5719,11 +6462,11 @@ + } + #endif + } +- ++ + break; + case 'h': + chan_misdn_log(1, ch->bc->port, "SETOPT: Digital\n"); +- ++ + if (strlen(tok) > 1 && tok[1] == '1') { + chan_misdn_log(1, ch->bc->port, "SETOPT: HDLC \n"); + if (!ch->bc->hdlc) { +@@ -5732,12 +6475,12 @@ + } + ch->bc->capability = INFO_CAPABILITY_DIGITAL_UNRESTRICTED; + break; +- ++ + case 's': + chan_misdn_log(1, ch->bc->port, "SETOPT: Send DTMF\n"); + ch->bc->send_dtmf = 1; + break; +- ++ + case 'f': + chan_misdn_log(1, ch->bc->port, "SETOPT: Faxdetect\n"); + ch->faxdetect = 1; +@@ -5753,12 +6496,15 @@ + chan_misdn_log(1, ch->bc->port, "SETOPT: callerpres: %s\n", &tok[1]); + /* CRICH: callingpres!!! */ + if (strstr(tok, "allowed")) { +- ch->bc->pres = 0; ++ ch->bc->presentation = 0; ++ ch->bc->set_presentation = 1; + } else if (strstr(tok, "restricted")) { +- ch->bc->pres = 1; ++ ch->bc->presentation = 1; ++ ch->bc->set_presentation = 1; + } else if (strstr(tok, "not_screened")) { + chan_misdn_log(0, ch->bc->port, "SETOPT: callerpres: not_screened is deprecated\n"); +- ch->bc->pres = 1; ++ ch->bc->presentation = 1; ++ ch->bc->set_presentation = 1; + } + break; + case 'i' : +@@ -5791,19 +6537,19 @@ + ch->bc->nodsp = 1; + ch->bc->nojitter = 1; + } +- ++ + return 0; + } + + +-int chan_misdn_jb_empty ( struct misdn_bchannel *bc, char *buf, int len) ++int chan_misdn_jb_empty ( struct misdn_bchannel *bc, char *buf, int len) + { + struct chan_list *ch = find_chan_by_bc(cl_te, bc); +- ++ + if (ch && ch->jb) { + return misdn_jb_empty(ch->jb, buf, len); + } +- ++ + return -1; + } + +@@ -5860,7 +6606,7 @@ + void misdn_jb_destroy(struct misdn_jb *jb) + { + ast_mutex_destroy(&jb->mutexjb); +- ++ + ast_free(jb->ok); + ast_free(jb->samples); + ast_free(jb); +@@ -5877,10 +6623,10 @@ + } + + ast_mutex_lock(&jb->mutexjb); +- ++ + wp = jb->wp; + rp = jb->rp; +- ++ + for (i = 0; i < len; i++) { + jb->samples[wp] = data[i]; + jb->ok[wp] = 1; +@@ -5924,7 +6670,7 @@ + jb->wp = wp; + + ast_mutex_unlock(&jb->mutexjb); +- ++ + return 0; + } + +@@ -5940,7 +6686,7 @@ + rp = jb->rp; + wp = jb->wp; + +- if (jb->state_empty) { ++ if (jb->state_empty) { + for (i = 0; i < len; i++) { + if (wp == rp) { + jb->rp = rp; +@@ -5976,16 +6722,10 @@ + return read; + } + +- +- +- + /*******************************************************/ + /*************** JITTERBUFFER END *********************/ + /*******************************************************/ + +- +- +- + static void chan_misdn_log(int level, int port, char *tmpl, ...) + { + va_list ap; +@@ -6006,15 +6746,14 @@ + + if (level == -1) { + ast_log(LOG_WARNING, "%s", buf); ++ } else if (misdn_debug_only[port] ? ++ (level == 1 && misdn_debug[port]) || (level == misdn_debug[port]) ++ : level <= misdn_debug[port]) { + +- } else if (misdn_debug_only[port] ? +- (level == 1 && misdn_debug[port]) || (level == misdn_debug[port]) +- : level <= misdn_debug[port]) { +- + ast_console_puts(port_buf); + ast_console_puts(buf); + } +- ++ + if ((level <= misdn_debug[0]) && !ast_strlen_zero(global_tracefile) ) { + char ctimebuf[30]; + time_t tm = time(NULL); +@@ -6022,20 +6761,21 @@ + + FILE *fp = fopen(global_tracefile, "a+"); + +- if ((p = strchr(tmp, '\n'))) { ++ p = strchr(tmp, '\n'); ++ if (p) { + *p = ':'; + } +- ++ + if (!fp) { + ast_console_puts("Error opening Tracefile: [ "); + ast_console_puts(global_tracefile); + ast_console_puts(" ] "); +- ++ + ast_console_puts(strerror(errno)); + ast_console_puts("\n"); + return ; + } +- ++ + fputs(tmp, fp); + fputs(" ", fp); + fputs(port_buf, fp); +Index: channels/chan_skinny.c +=================================================================== +--- a/channels/chan_skinny.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_skinny.c (.../trunk) (revision 186562) +@@ -49,7 +49,7 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" + #include "asterisk/netsock.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" +@@ -1111,8 +1111,8 @@ + struct skinny_subchannel { + ast_mutex_t lock; + struct ast_channel *owner; +- struct ast_rtp *rtp; +- struct ast_rtp *vrtp; ++ struct ast_rtp_instance *rtp; ++ struct ast_rtp_instance *vrtp; + unsigned int callid; + /* time_t lastouttime; */ /* Unused */ + int progress; +@@ -1347,7 +1347,7 @@ + .fixup = skinny_fixup, + .send_digit_begin = skinny_senddigit_begin, + .send_digit_end = skinny_senddigit_end, +- .bridge = ast_rtp_bridge, ++ .bridge = ast_rtp_instance_bridge, + }; + + static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data); +@@ -2516,6 +2516,43 @@ + return 0; + } + ++static void update_connectedline(struct skinny_subchannel *sub, const void *data, size_t datalen) ++{ ++ struct ast_channel *c = sub->owner; ++ struct skinny_line *l = sub->parent; ++ struct skinny_device *d = l->device; ++ ++ if (ast_strlen_zero(c->cid.cid_num) || ast_strlen_zero(c->connected.id.number)) ++ return; ++ ++ if (sub->owner->_state == AST_STATE_UP) { ++ transmit_callstate(d, l->instance, SKINNY_CONNECTED, sub->callid); ++ transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid); ++ if (sub->outgoing) ++ transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1); ++ else ++ transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2); ++ } else { ++ if (sub->outgoing) { ++ transmit_callstate(d, l->instance, SKINNY_RINGIN, sub->callid); ++ transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid); ++ transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1); ++ } else { ++ if (!sub->ringing) { ++ transmit_callstate(d, l->instance, SKINNY_RINGOUT, sub->callid); ++ transmit_displaypromptstatus(d, "Ring-Out", 0, l->instance, sub->callid); ++ sub->ringing = 1; ++ } else { ++ transmit_callstate(d, l->instance, SKINNY_PROGRESS, sub->callid); ++ transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid); ++ sub->progress = 1; ++ } ++ ++ transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2); ++ } ++ } ++} ++ + static void mwi_event_cb(const struct ast_event *event, void *userdata) + { + struct skinny_line *l = userdata; +@@ -2557,46 +2594,48 @@ + /* I do not believe skinny can deal with video. + Anyone know differently? */ + /* Yes, it can. Currently 7985 and Cisco VT Advantage do video. */ +-static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance) + { + struct skinny_subchannel *sub = NULL; + + if (!(sub = c->tech_pvt) || !(sub->vrtp)) +- return AST_RTP_GET_FAILED; ++ return AST_RTP_GLUE_RESULT_FORBID; + +- *rtp = sub->vrtp; ++ ao2_ref(sub->vrtp, +1); ++ *instance = sub->vrtp; + +- return AST_RTP_TRY_NATIVE; ++ return AST_RTP_GLUE_RESULT_REMOTE; + } + +-static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance) + { + struct skinny_subchannel *sub = NULL; + struct skinny_line *l; +- enum ast_rtp_get_result res = AST_RTP_TRY_NATIVE; ++ enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE; + + if (skinnydebug) + ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name); + + + if (!(sub = c->tech_pvt)) +- return AST_RTP_GET_FAILED; ++ return AST_RTP_GLUE_RESULT_FORBID; + + ast_mutex_lock(&sub->lock); + + if (!(sub->rtp)){ + ast_mutex_unlock(&sub->lock); +- return AST_RTP_GET_FAILED; ++ return AST_RTP_GLUE_RESULT_FORBID; + } +- +- *rtp = sub->rtp; + ++ ao2_ref(sub->rtp, +1); ++ *instance = sub->rtp; ++ + l = sub->parent; + + if (!l->canreinvite || l->nat){ +- res = AST_RTP_TRY_PARTIAL; ++ res = AST_RTP_GLUE_RESULT_LOCAL; + if (skinnydebug) +- ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_TRY_PARTIAL \n"); ++ ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n"); + } + + ast_mutex_unlock(&sub->lock); +@@ -2605,7 +2644,7 @@ + + } + +-static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) ++static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) + { + struct skinny_subchannel *sub; + struct skinny_line *l; +@@ -2630,7 +2669,7 @@ + s = d->session; + + if (rtp){ +- ast_rtp_get_peer(rtp, &them); ++ ast_rtp_instance_get_remote_address(rtp, &them); + + /* Shutdown any early-media or previous media on re-invite */ + if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE))) +@@ -2654,7 +2693,7 @@ + req->data.startmedia.conferenceId = htolel(sub->callid); + req->data.startmedia.passThruPartyId = htolel(sub->callid); + if (!(l->canreinvite) || (l->nat)){ +- ast_rtp_get_us(rtp, &us); ++ ast_rtp_instance_get_local_address(rtp, &us); + req->data.startmedia.remoteIp = htolel(d->ourip.s_addr); + req->data.startmedia.remotePort = htolel(ntohs(us.sin_port)); + } else { +@@ -2675,11 +2714,11 @@ + return 0; + } + +-static struct ast_rtp_protocol skinny_rtp = { ++static struct ast_rtp_glue skinny_rtp_glue = { + .type = "Skinny", + .get_rtp_info = skinny_get_rtp_peer, + .get_vrtp_info = skinny_get_vrtp_peer, +- .set_rtp_peer = skinny_set_rtp_peer, ++ .update_peer = skinny_set_rtp_peer, + }; + + static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +@@ -3559,29 +3598,36 @@ + + ast_mutex_lock(&sub->lock); + /* Allocate the RTP */ +- sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); ++ sub->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); + if (hasvideo) +- sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); +- ++ sub->vrtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); ++ ++ if (sub->rtp) { ++ ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1); ++ } ++ if (sub->vrtp) { ++ ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1); ++ } ++ + if (sub->rtp && sub->owner) { +- ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); +- ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp)); ++ ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0)); ++ ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1)); + } + if (hasvideo && sub->vrtp && sub->owner) { +- ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp)); +- ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp)); ++ ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0)); ++ ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1)); + } + if (sub->rtp) { +- ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP"); +- ast_rtp_setnat(sub->rtp, l->nat); ++ ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP"); ++ ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat); + } + if (sub->vrtp) { +- ast_rtp_setqos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP"); +- ast_rtp_setnat(sub->vrtp, l->nat); ++ ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP"); ++ ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat); + } + /* Set Frame packetization */ + if (sub->rtp) +- ast_rtp_codec_setpref(sub->rtp, &l->prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs); + + /* Create the RTP connection */ + transmit_connect(d, sub); +@@ -3601,6 +3647,8 @@ + l->hidecallerid ? "" : l->cid_num, + l->hidecallerid ? "" : l->cid_name, + c->cid.cid_ani ? NULL : l->cid_num); ++ c->connected.id.number = ast_strdup(c->exten); ++ c->connected.id.name = NULL; + ast_setstate(c, AST_STATE_RING); + if (!sub->rtp) { + start_rtp(sub); +@@ -3764,7 +3812,7 @@ + transmit_callstateonly(d, sub, SKINNY_RINGIN); + transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN); + transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid); +- transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1); ++ transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1); + transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK); + transmit_ringer_mode(d, SKINNY_RING_INSIDE); + +@@ -3852,7 +3900,7 @@ + sub->alreadygone = 0; + sub->outgoing = 0; + if (sub->rtp) { +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } + ast_mutex_unlock(&sub->lock); +@@ -3891,7 +3939,7 @@ + /* order matters here... + for some reason, transmit_callinfo must be before transmit_callstate, + or you won't get keypad messages in some situations. */ +- transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); ++ transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); + transmit_callstateonly(d, sub, SKINNY_CONNECTED); + transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED); + transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid); +@@ -3913,16 +3961,16 @@ + + switch(ast->fdno) { + case 0: +- f = ast_rtp_read(sub->rtp); /* RTP Audio */ ++ f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */ + break; + case 1: +- f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */ ++ f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */ + break; + case 2: +- f = ast_rtp_read(sub->vrtp); /* RTP Video */ ++ f = ast_rtp_instance_read(sub->vrtp, 0); /* RTP Video */ + break; + case 3: +- f = ast_rtcp_read(sub->vrtp); /* RTCP Control Channel for video */ ++ f = ast_rtp_instance_read(sub->vrtp, 1); /* RTCP Control Channel for video */ + break; + #if 0 + case 5: +@@ -3979,7 +4027,7 @@ + if (sub) { + ast_mutex_lock(&sub->lock); + if (sub->rtp) { +- res = ast_rtp_write(sub->rtp, frame); ++ res = ast_rtp_instance_write(sub->rtp, frame); + } + ast_mutex_unlock(&sub->lock); + } +@@ -4086,6 +4134,10 @@ + return "Unhold"; + case AST_CONTROL_SRCUPDATE: + return "Media Source Update"; ++ case AST_CONTROL_CONNECTED_LINE: ++ return "Connected Line"; ++ case AST_CONTROL_REDIRECTING: ++ return "Redirecting"; + case -1: + return "Stop tone"; + default: +@@ -4193,7 +4245,7 @@ + transmit_callstateonly(d, sub, SKINNY_RINGOUT); + transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid); + transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid); +- transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); /* 2 = outgoing from phone */ ++ transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */ + sub->ringing = 1; + if (!d->earlyrtp) { + break; +@@ -4234,7 +4286,7 @@ + } + transmit_callstateonly(d, sub, SKINNY_PROGRESS); + transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid); +- transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); /* 2 = outgoing from phone */ ++ transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */ + sub->progress = 1; + if (!d->earlyrtp) { + break; +@@ -4253,8 +4305,11 @@ + case AST_CONTROL_PROCEEDING: + break; + case AST_CONTROL_SRCUPDATE: +- ast_rtp_new_source(sub->rtp); ++ ast_rtp_instance_new_source(sub->rtp); + break; ++ case AST_CONTROL_CONNECTED_LINE: ++ update_connectedline(sub, data, datalen); ++ break; + default: + ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind); + return -1; /* Tell asterisk to provide inband signalling */ +@@ -4312,7 +4367,7 @@ + if (skinnydebug) + ast_verb(1, "skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt); + if (sub->rtp) { +- ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); ++ ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0)); + } + if (state == AST_STATE_RING) { + tmp->rings = 1; +@@ -5537,8 +5592,8 @@ + l = sub->parent; + + if (sub->rtp) { +- ast_rtp_set_peer(sub->rtp, &sin); +- ast_rtp_get_us(sub->rtp, &us); ++ ast_rtp_instance_set_remote_address(sub->rtp, &sin); ++ ast_rtp_instance_get_local_address(sub->rtp, &us); + } else { + ast_log(LOG_ERROR, "No RTP structure, this is very bad\n"); + return 0; +@@ -7289,7 +7344,7 @@ + return -1; + } + +- ast_rtp_proto_register(&skinny_rtp); ++ ast_rtp_glue_register(&skinny_rtp_glue); + ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny)); + + ast_manager_register2("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices, +@@ -7323,7 +7378,7 @@ + struct skinny_subchannel *sub; + struct ast_context *con; + +- ast_rtp_proto_unregister(&skinny_rtp); ++ ast_rtp_glue_unregister(&skinny_rtp_glue); + ast_channel_unregister(&skinny_tech); + ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny)); + +Index: channels/chan_mgcp.c +=================================================================== +--- a/channels/chan_mgcp.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_mgcp.c (.../trunk) (revision 186562) +@@ -52,7 +52,7 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" + #include "asterisk/cli.h" +@@ -282,7 +282,7 @@ + int id; + struct ast_channel *owner; + struct mgcp_endpoint *parent; +- struct ast_rtp *rtp; ++ struct ast_rtp_instance *rtp; + struct sockaddr_in tmpdest; + char txident[80]; /*! \todo FIXME txident is replaced by rqnt_ident in endpoint. + This should be obsoleted */ +@@ -408,7 +408,7 @@ + static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone); + static int transmit_modify_request(struct mgcp_subchannel *sub); + static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callernum, char *callername); +-static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs); ++static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp, int codecs); + static int transmit_connection_del(struct mgcp_subchannel *sub); + static int transmit_audit_endpoint(struct mgcp_endpoint *p); + static void start_rtp(struct mgcp_subchannel *sub); +@@ -447,7 +447,7 @@ + .fixup = mgcp_fixup, + .send_digit_begin = mgcp_senddigit_begin, + .send_digit_end = mgcp_senddigit_end, +- .bridge = ast_rtp_bridge, ++ .bridge = ast_rtp_instance_bridge, + }; + + static void mwi_event_cb(const struct ast_event *event, void *userdata) +@@ -472,7 +472,6 @@ + event = ast_event_get_cached(AST_EVENT_MWI, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mbox, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cntx, +- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, + AST_EVENT_IE_END); + + if (event) { +@@ -504,7 +503,7 @@ + sub->alreadygone = 0; + memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); + if (sub->rtp) { +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } + dump_cmd_queues(NULL, sub); /* SC */ +@@ -916,7 +915,7 @@ + transmit_modify_request(sub->next); + } + +- transmit_notify_request_with_callerid(sub, tone, ast->cid.cid_num, ast->cid.cid_name); ++ transmit_notify_request_with_callerid(sub, tone, ast->connected.id.number, ast->connected.id.name); + ast_setstate(ast, AST_STATE_RINGING); + + if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(sub->next->callid)) { +@@ -1004,7 +1003,7 @@ + /* Reset temporary destination */ + memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); + if (sub->rtp) { +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } + +@@ -1204,7 +1203,7 @@ + /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */ + struct ast_frame *f; + +- f = ast_rtp_read(sub->rtp); ++ f = ast_rtp_instance_read(sub->rtp, 0); + /* Don't send RFC2833 if we're not supposed to */ + if (f && (f->frametype == AST_FRAME_DTMF) && !(sub->parent->dtmfmode & MGCP_DTMF_RFC2833)) + return &ast_null_frame; +@@ -1262,7 +1261,7 @@ + ast_mutex_lock(&sub->lock); + if ((sub->parent->sub == sub) || !sub->parent->singlepath) { + if (sub->rtp) { +- res = ast_rtp_write(sub->rtp, frame); ++ res = ast_rtp_instance_write(sub->rtp, frame); + } + } + ast_mutex_unlock(&sub->lock); +@@ -1298,7 +1297,7 @@ + res = -1; /* Let asterisk play inband indications */ + } else if (p->dtmfmode & MGCP_DTMF_RFC2833) { + ast_log(LOG_DEBUG, "Sending DTMF using RFC2833"); +- ast_rtp_senddigit_begin(sub->rtp, digit); ++ ast_rtp_instance_dtmf_begin(sub->rtp, digit); + } else { + ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); + } +@@ -1325,7 +1324,7 @@ + tmp[2] = digit; + tmp[3] = '\0'; + transmit_notify_request(sub, tmp); +- ast_rtp_senddigit_end(sub->rtp, digit); ++ ast_rtp_instance_dtmf_end(sub->rtp, digit); + } else { + ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); + } +@@ -1454,7 +1453,7 @@ + ast_moh_stop(ast); + break; + case AST_CONTROL_SRCUPDATE: +- ast_rtp_new_source(sub->rtp); ++ ast_rtp_instance_new_source(sub->rtp); + break; + case -1: + transmit_notify_request(sub, ""); +@@ -1482,7 +1481,7 @@ + fmt = ast_best_codec(tmp->nativeformats); + ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); + if (sub->rtp) +- ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); ++ ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0)); + if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) { + i->dsp = ast_dsp_new(); + ast_dsp_set_features(i->dsp, DSP_FEATURE_DIGIT_DETECT); +@@ -1875,12 +1874,12 @@ + sin.sin_family = AF_INET; + memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + sin.sin_port = htons(portno); +- ast_rtp_set_peer(sub->rtp, &sin); ++ ast_rtp_instance_set_remote_address(sub->rtp, &sin); + #if 0 + printf("Peer RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + #endif + /* Scan through the RTP payload types specified in a "m=" line: */ +- ast_rtp_pt_clear(sub->rtp); ++ ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp); + codecs = ast_strdupa(m + len); + while (!ast_strlen_zero(codecs)) { + if (sscanf(codecs, "%d%n", &codec, &len) != 1) { +@@ -1889,7 +1888,7 @@ + ast_log(LOG_WARNING, "Error in codec string '%s' at '%s'\n", m, codecs); + return -1; + } +- ast_rtp_set_m_type(sub->rtp, codec); ++ ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, codec); + codec_count++; + codecs += len; + } +@@ -1902,11 +1901,11 @@ + if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) + continue; + /* Note: should really look at the 'freq' and '#chans' params too */ +- ast_rtp_set_rtpmap_type(sub->rtp, codec, "audio", mimeSubtype, 0); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, codec, "audio", mimeSubtype, 0); + } + + /* Now gather all of the codecs that were asked for: */ +- ast_rtp_get_current_formats(sub->rtp, &peercapability, &peerNonCodecCapability); ++ ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(sub->rtp), &peercapability, &peerNonCodecCapability); + p->capability = capability & peercapability; + if (mgcpdebug) { + ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n", +@@ -2044,7 +2043,7 @@ + } + + +-static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp *rtp) ++static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp) + { + int len; + int codec; +@@ -2067,9 +2066,9 @@ + ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n"); + return -1; + } +- ast_rtp_get_us(sub->rtp, &sin); ++ ast_rtp_instance_get_local_address(sub->rtp, &sin); + if (rtp) { +- ast_rtp_get_peer(rtp, &dest); ++ ast_rtp_instance_get_remote_address(sub->rtp, &dest); + } else { + if (sub->tmpdest.sin_addr.s_addr) { + dest.sin_addr = sub->tmpdest.sin_addr; +@@ -2095,11 +2094,11 @@ + if (mgcpdebug) { + ast_verbose("Answering with capability %d\n", x); + } +- codec = ast_rtp_lookup_code(sub->rtp, 1, x); ++ codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, x); + if (codec > -1) { + snprintf(costr, sizeof(costr), " %d", codec); + strncat(m, costr, sizeof(m) - strlen(m) - 1); +- snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x, 0)); ++ snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype2(1, x, 0)); + strncat(a, costr, sizeof(a) - strlen(a) - 1); + } + } +@@ -2109,11 +2108,11 @@ + if (mgcpdebug) { + ast_verbose("Answering with non-codec capability %d\n", x); + } +- codec = ast_rtp_lookup_code(sub->rtp, 0, x); ++ codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 0, x); + if (codec > -1) { + snprintf(costr, sizeof(costr), " %d", codec); + strncat(m, costr, sizeof(m) - strlen(m) - 1); +- snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x, 0)); ++ snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype2(0, x, 0)); + strncat(a, costr, sizeof(a) - strlen(a) - 1); + if (x == AST_RTP_DTMF) { + /* Indicate we support DTMF... Not sure about 16, +@@ -2137,7 +2136,7 @@ + return 0; + } + +-static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs) ++static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp, int codecs) + { + struct mgcp_request resp; + char local[256]; +@@ -2148,13 +2147,13 @@ + if (ast_strlen_zero(sub->cxident) && rtp) { + /* We don't have a CXident yet, store the destination and + wait a bit */ +- ast_rtp_get_peer(rtp, &sub->tmpdest); ++ ast_rtp_instance_get_remote_address(rtp, &sub->tmpdest); + return 0; + } + ast_copy_string(local, "p:20", sizeof(local)); + for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) { + if (p->capability & x) { +- snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); ++ snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype2(1, x, 0)); + strncat(local, tmp, sizeof(local) - strlen(local) - 1); + } + } +@@ -2173,7 +2172,7 @@ + return send_request(p, sub, &resp, oseq); /* SC */ + } + +-static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp) ++static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp) + { + struct mgcp_request resp; + char local[256]; +@@ -2184,7 +2183,7 @@ + ast_copy_string(local, "p:20", sizeof(local)); + for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) { + if (p->capability & x) { +- snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); ++ snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype2(1, x, 0)); + strncat(local, tmp, sizeof(local) - strlen(local) - 1); + } + } +@@ -2612,21 +2611,17 @@ + ast_mutex_lock(&sub->lock); + /* check again to be on the safe side */ + if (sub->rtp) { +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } + /* Allocate the RTP now */ +- sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); ++ sub->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); + if (sub->rtp && sub->owner) +- ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); ++ ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0)); + if (sub->rtp) { +- ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "MGCP RTP"); +- ast_rtp_setnat(sub->rtp, sub->nat); ++ ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "MGCP RTP"); ++ ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->nat); + } +-#if 0 +- ast_rtp_set_callback(p->rtp, rtpready); +- ast_rtp_set_data(p->rtp, p); +-#endif + /* Make a call*ID */ + snprintf(sub->callid, sizeof(sub->callid), "%08lx%s", ast_random(), sub->txident); + /* Transmit the connection create */ +@@ -3941,22 +3936,22 @@ + return (gw_reload ? NULL : gw); + } + +-static enum ast_rtp_get_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { + struct mgcp_subchannel *sub = NULL; + + if (!(sub = chan->tech_pvt) || !(sub->rtp)) +- return AST_RTP_GET_FAILED; ++ return AST_RTP_GLUE_RESULT_FORBID; + +- *rtp = sub->rtp; ++ *instance = sub->rtp ? ao2_ref(sub->rtp, +1), sub->rtp : NULL; + + if (sub->parent->canreinvite) +- return AST_RTP_TRY_NATIVE; ++ return AST_RTP_GLUE_RESULT_REMOTE; + else +- return AST_RTP_TRY_PARTIAL; ++ return AST_RTP_GLUE_RESULT_LOCAL; + } + +-static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) ++static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) + { + /* XXX Is there such thing as video support with MGCP? XXX */ + struct mgcp_subchannel *sub; +@@ -3968,10 +3963,10 @@ + return -1; + } + +-static struct ast_rtp_protocol mgcp_rtp = { ++static struct ast_rtp_glue mgcp_rtp_glue = { + .type = "MGCP", + .get_rtp_info = mgcp_get_rtp_peer, +- .set_rtp_peer = mgcp_set_rtp_peer, ++ .update_peer = mgcp_set_rtp_peer, + }; + + static void destroy_endpoint(struct mgcp_endpoint *e) +@@ -3985,7 +3980,7 @@ + transmit_connection_del(sub); + } + if (sub->rtp) { +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } + memset(sub->magic, 0, sizeof(sub->magic)); +@@ -4277,7 +4272,7 @@ + return AST_MODULE_LOAD_FAILURE; + } + +- ast_rtp_proto_register(&mgcp_rtp); ++ ast_rtp_glue_register(&mgcp_rtp_glue); + ast_cli_register_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); + + /* And start the monitor for the first time */ +@@ -4380,7 +4375,7 @@ + } + + close(mgcpsock); +- ast_rtp_proto_unregister(&mgcp_rtp); ++ ast_rtp_glue_unregister(&mgcp_rtp_glue); + ast_cli_unregister_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); + sched_context_destroy(sched); + +Index: channels/chan_unistim.c +=================================================================== +--- a/channels/chan_unistim.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_unistim.c (.../trunk) (revision 186562) +@@ -60,7 +60,7 @@ + #include "asterisk/module.h" + #include "asterisk/pbx.h" + #include "asterisk/event.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" + #include "asterisk/netsock.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" +@@ -365,7 +365,7 @@ + /*! Unistim line */ + struct unistim_line *parent; + /*! RTP handle */ +- struct ast_rtp *rtp; ++ struct ast_rtp_instance *rtp; + int alreadygone; + char ringvolume; + char ringstyle; +@@ -711,7 +711,7 @@ + .send_digit_begin = unistim_senddigit_begin, + .send_digit_end = unistim_senddigit_end, + .send_text = unistim_sendtext, +-/* .bridge = ast_rtp_bridge, */ ++ .bridge = ast_rtp_instance_bridge, + }; + + static void display_last_error(const char *sz_msg) +@@ -1854,7 +1854,7 @@ + static void swap_subs(struct unistim_line *p, int a, int b) + { + /* struct ast_channel *towner; */ +- struct ast_rtp *rtp; ++ struct ast_rtp_instance *rtp; + int fds; + + if (unistimdebug) +@@ -2056,30 +2056,29 @@ + /* Allocate the RTP */ + if (unistimdebug) + ast_verb(0, "Starting RTP. Bind on %s\n", ast_inet_ntoa(sout.sin_addr)); +- sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, sout.sin_addr); ++ sub->rtp = ast_rtp_instance_new(NULL, sched, &sout, NULL); + if (!sub->rtp) { + ast_log(LOG_WARNING, "Unable to create RTP session: %s binaddr=%s\n", + strerror(errno), ast_inet_ntoa(sout.sin_addr)); + ast_mutex_unlock(&sub->lock); + return; + } +- if (sub->rtp && sub->owner) { +- sub->owner->fds[0] = ast_rtp_fd(sub->rtp); +- sub->owner->fds[1] = ast_rtcp_fd(sub->rtp); ++ ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1); ++ if (sub->owner) { ++ sub->owner->fds[0] = ast_rtp_instance_fd(sub->rtp, 0); ++ sub->owner->fds[1] = ast_rtp_instance_fd(sub->rtp, 1); + } +- if (sub->rtp) { +- ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP"); +- ast_rtp_setnat(sub->rtp, sub->parent->parent->nat); +- } ++ ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP"); ++ ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->parent->parent->nat); + + /* Create the RTP connection */ +- ast_rtp_get_us(sub->rtp, &us); ++ ast_rtp_instance_get_local_address(sub->rtp, &us); + sin.sin_family = AF_INET; + /* Setting up RTP for our side */ + memcpy(&sin.sin_addr, &sub->parent->parent->session->sin.sin_addr, + sizeof(sin.sin_addr)); + sin.sin_port = htons(sub->parent->parent->rtp_port); +- ast_rtp_set_peer(sub->rtp, &sin); ++ ast_rtp_instance_set_remote_address(sub->rtp, &sin); + if (!(sub->owner->nativeformats & sub->owner->readformat)) { + int fmt; + fmt = ast_best_codec(sub->owner->nativeformats); +@@ -2091,7 +2090,7 @@ + sub->owner->readformat = fmt; + sub->owner->writeformat = fmt; + } +- codec = ast_rtp_lookup_code(sub->rtp, 1, sub->owner->readformat); ++ codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, sub->owner->readformat); + /* Setting up RTP of the phone */ + if (public_ip.sin_family == 0) /* NAT IP override ? */ + memcpy(&public, &us, sizeof(public)); /* No defined, using IP from recvmsg */ +@@ -3672,16 +3671,16 @@ + Sendicon(TEXT_LINE0, FAV_ICON_NONE, session); + + if (sub->owner) { +- if (sub->owner->cid.cid_num) { +- send_text(TEXT_LINE1, TEXT_NORMAL, session, sub->owner->cid.cid_num); +- change_callerid(session, 0, sub->owner->cid.cid_num); ++ if (sub->owner->connected.id.number) { ++ send_text(TEXT_LINE1, TEXT_NORMAL, session, sub->owner->connected.id.number); ++ change_callerid(session, 0, sub->owner->connected.id.number); + } else { + send_text(TEXT_LINE1, TEXT_NORMAL, session, DEFAULTCALLERID); + change_callerid(session, 0, DEFAULTCALLERID); + } +- if (sub->owner->cid.cid_name) { +- send_text(TEXT_LINE0, TEXT_NORMAL, session, sub->owner->cid.cid_name); +- change_callerid(session, 1, sub->owner->cid.cid_name); ++ if (sub->owner->connected.id.name) { ++ send_text(TEXT_LINE0, TEXT_NORMAL, session, sub->owner->connected.id.name); ++ change_callerid(session, 1, sub->owner->connected.id.name); + } else { + send_text(TEXT_LINE0, TEXT_NORMAL, session, DEFAULTCALLERNAME); + change_callerid(session, 1, DEFAULTCALLERNAME); +@@ -3724,7 +3723,7 @@ + if (sub->rtp) { + if (unistimdebug) + ast_verb(0, "Destroying RTP session\n"); +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } + return 0; +@@ -3769,7 +3768,7 @@ + if (sub->rtp) { + if (unistimdebug) + ast_verb(0, "Destroying RTP session\n"); +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } + return 0; +@@ -3794,7 +3793,7 @@ + if (sub->rtp) { + if (unistimdebug) + ast_verb(0, "Destroying RTP session\n"); +- ast_rtp_destroy(sub->rtp); ++ ast_rtp_instance_destroy(sub->rtp); + sub->rtp = NULL; + } else if (unistimdebug) + ast_verb(0, "No RTP session to destroy\n"); +@@ -3921,10 +3920,10 @@ + + switch (ast->fdno) { + case 0: +- f = ast_rtp_read(sub->rtp); /* RTP Audio */ ++ f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */ + break; + case 1: +- f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */ ++ f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */ + break; + default: + f = &ast_null_frame; +@@ -3990,7 +3989,7 @@ + if (sub) { + ast_mutex_lock(&sub->lock); + if (sub->rtp) { +- res = ast_rtp_write(sub->rtp, frame); ++ res = ast_rtp_instance_write(sub->rtp, frame); + } + ast_mutex_unlock(&sub->lock); + } +@@ -4391,7 +4390,6 @@ + event = ast_event_get_cached(AST_EVENT_MWI, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context, +- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, + AST_EVENT_IE_END); + + if (event) { +@@ -4456,8 +4454,8 @@ + if ((sub->rtp) && (sub->subtype == 0)) { + if (unistimdebug) + ast_verb(0, "New unistim channel with a previous rtp handle ?\n"); +- tmp->fds[0] = ast_rtp_fd(sub->rtp); +- tmp->fds[1] = ast_rtcp_fd(sub->rtp); ++ tmp->fds[0] = ast_rtp_instance_fd(sub->rtp, 0); ++ tmp->fds[1] = ast_rtp_instance_fd(sub->rtp, 1); + } + if (sub->rtp) + ast_jb_configure(tmp, &global_jbconf); +@@ -5527,51 +5525,19 @@ + return 0; + } + +-static enum ast_rtp_get_result unistim_get_vrtp_peer(struct ast_channel *chan, +- struct ast_rtp **rtp) ++static enum ast_rtp_glue_result unistim_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { +- return AST_RTP_TRY_NATIVE; +-} ++ struct unistim_subchannel *sub = chan->tech_pvt; + +-static enum ast_rtp_get_result unistim_get_rtp_peer(struct ast_channel *chan, +- struct ast_rtp **rtp) +-{ +- struct unistim_subchannel *sub; +- enum ast_rtp_get_result res = AST_RTP_GET_FAILED; ++ ao2_ref(sub->rtp, +1); ++ *instance = sub->rtp; + +- if (unistimdebug) +- ast_verb(0, "unistim_get_rtp_peer called\n"); +- +- sub = chan->tech_pvt; +- if (sub && sub->rtp) { +- *rtp = sub->rtp; +- res = AST_RTP_TRY_NATIVE; +- } +- +- return res; ++ return AST_RTP_GLUE_RESULT_LOCAL; + } + +-static int unistim_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, +- struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +-{ +- struct unistim_subchannel *sub; +- +- if (unistimdebug) +- ast_verb(0, "unistim_set_rtp_peer called\n"); +- +- sub = chan->tech_pvt; +- +- if (sub) +- return 0; +- +- return -1; +-} +- +-static struct ast_rtp_protocol unistim_rtp = { ++static struct ast_rtp_glue unistim_rtp_glue = { + .type = channel_type, + .get_rtp_info = unistim_get_rtp_peer, +- .get_vrtp_info = unistim_get_vrtp_peer, +- .set_rtp_peer = unistim_set_rtp_peer, + }; + + /*--- load_module: PBX load module - initialization ---*/ +@@ -5604,7 +5570,7 @@ + goto chanreg_failed; + } + +- ast_rtp_proto_register(&unistim_rtp); ++ ast_rtp_glue_register(&unistim_rtp_glue); + + ast_cli_register_multiple(unistim_cli, ARRAY_LEN(unistim_cli)); + +@@ -5635,7 +5601,7 @@ + ast_cli_unregister_multiple(unistim_cli, ARRAY_LEN(unistim_cli)); + + ast_channel_unregister(&unistim_tech); +- ast_rtp_proto_unregister(&unistim_rtp); ++ ast_rtp_glue_unregister(&unistim_rtp_glue); + + ast_mutex_lock(&monlock); + if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { +Index: channels/chan_local.c +=================================================================== +--- a/channels/chan_local.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_local.c (.../trunk) (revision 186562) +@@ -39,7 +39,6 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" + #include "asterisk/file.h" +@@ -407,6 +406,37 @@ + ast_moh_start(ast, data, NULL); + } else if (condition == AST_CONTROL_UNHOLD) { + ast_moh_stop(ast); ++ } else if (condition == AST_CONTROL_CONNECTED_LINE || condition == AST_CONTROL_REDIRECTING) { ++ struct ast_channel *this_channel; ++ struct ast_channel *the_other_channel; ++ /* A connected line update frame may only contain a partial amount of data, such ++ * as just a source, or just a ton, and not the full amount of information. However, ++ * the collected information is all stored in the outgoing channel's connectedline ++ * structure, so when receiving a connected line update on an outgoing local channel, ++ * we need to transmit the collected connected line information instead of whatever ++ * happens to be in this control frame. The same applies for redirecting information, which ++ * is why it is handled here as well.*/ ++ isoutbound = IS_OUTBOUND(ast, p); ++ if (isoutbound) { ++ this_channel = p->chan; ++ the_other_channel = p->owner; ++ } else { ++ this_channel = p->owner; ++ the_other_channel = p->chan; ++ } ++ if (the_other_channel) { ++ unsigned char frame_data[1024]; ++ if (condition == AST_CONTROL_CONNECTED_LINE) { ++ f.datalen = ast_connected_line_build_data(frame_data, sizeof(frame_data), &this_channel->connected); ++ } else { ++ f.datalen = ast_redirecting_build_data(frame_data, sizeof(frame_data), &this_channel->redirecting); ++ } ++ f.subclass = condition; ++ f.data.ptr = frame_data; ++ if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1))) { ++ ast_mutex_unlock(&p->lock); ++ } ++ } + } else { + /* Queue up a frame representing the indication as a control frame */ + ast_mutex_lock(&p->lock); +@@ -510,22 +540,45 @@ + + if (!p) + return -1; +- +- ast_mutex_lock(&p->lock); + ++ /* If you value your sanity, please don't look at this code */ ++start_over: ++ while (ast_channel_trylock(p->chan)) { ++ ast_channel_unlock(p->owner); ++ usleep(1); ++ ast_channel_lock(p->owner); ++ } ++ ++ /* p->owner and p->chan are locked now. Let's get p locked */ ++ if (ast_mutex_trylock(&p->lock)) { ++ /* @#$&$@ */ ++ ast_channel_unlock(p->chan); ++ ast_channel_unlock(p->owner); ++ usleep(1); ++ ast_channel_lock(p->owner); ++ goto start_over; ++ } ++ + /* + * Note that cid_num and cid_name aren't passed in the ast_channel_alloc + * call, so it's done here instead. ++ * ++ * All these failure points just return -1. The individual strings will ++ * be cleared when we destroy the channel. + */ +- p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid); +- p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num); +- p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name); +- p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis); +- p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani); +- p->chan->cid.cid_pres = p->owner->cid.cid_pres; +- p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2; +- p->chan->cid.cid_ton = p->owner->cid.cid_ton; ++ if (!(p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis))) { ++ return -1; ++ } ++ ast_party_redirecting_copy(&p->chan->redirecting, &p->owner->redirecting); ++ ++ if (!(p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid))) { ++ return -1; ++ } + p->chan->cid.cid_tns = p->owner->cid.cid_tns; ++ ++ ast_connected_line_copy_to_caller(&p->chan->cid, &p->owner->connected); ++ ast_connected_line_copy_from_caller(&p->chan->connected, &p->owner->cid); ++ + ast_string_field_set(p->chan, language, p->owner->language); + ast_string_field_set(p->chan, accountcode, p->owner->accountcode); + ast_string_field_set(p->chan, musicclass, p->owner->musicclass); +@@ -561,6 +614,7 @@ + ast_set_flag(p, LOCAL_LAUNCHED_PBX); + + ast_mutex_unlock(&p->lock); ++ ast_channel_unlock(p->chan); + return res; + } + +Index: channels/chan_bridge.c +=================================================================== +--- a/channels/chan_bridge.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_bridge.c (.../trunk) (revision 186562) +@@ -39,7 +39,6 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" + #include "asterisk/file.h" +Index: channels/misdn/isdn_msg_parser.c +=================================================================== +--- a/channels/misdn/isdn_msg_parser.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/isdn_msg_parser.c (.../trunk) (revision 186562) +@@ -11,7 +11,7 @@ + * the GNU General Public License + */ + +-/*! \file ++/*! \file + * \brief Interface to mISDN - message parser + * \author Christian Richter <crich@beronet.com> + */ +@@ -25,21 +25,57 @@ + + #include "ie.c" + ++/*! ++ * \internal ++ * \brief Build the name, number, name/number display message string ++ * ++ * \param display Display buffer to fill in ++ * \param display_length Length of the display buffer to fill in ++ * \param display_format Display format enumeration ++ * \param name Name string to use ++ * \param number Number string to use ++ * ++ * \return Nothing ++ */ ++static void build_display_str(char *display, size_t display_length, int display_format, const char *name, const char *number) ++{ ++ display[0] = 0; ++ switch (display_format) { ++ default: ++ case 0: /* none */ ++ break; + ++ case 1: /* name */ ++ snprintf(display, display_length, "%s", name); ++ break; ++ ++ case 2: /* number */ ++ snprintf(display, display_length, "%s", number); ++ break; ++ ++ case 3: /* both */ ++ if (name[0] || number[0]) { ++ snprintf(display, display_length, "\"%s\" <%s>", name, number); ++ } ++ break; ++ } ++} ++ ++ + static void set_channel(struct misdn_bchannel *bc, int channel) + { + + cb_log(3,bc->port,"set_channel: bc->channel:%d channel:%d\n", bc->channel, channel); +- +- ++ ++ + if (channel==0xff) { + /* any channel */ + channel=-1; + } +- ++ + /* ALERT: is that everytime true ? */ + if (channel > 0 && bc->nt ) { +- ++ + if (bc->channel && ( bc->channel != 0xff) ) { + cb_log(0,bc->port,"We already have a channel (%d)\n", bc->channel); + } else { +@@ -47,179 +83,191 @@ + cb_event(EVENT_NEW_CHANNEL,bc,NULL); + } + } +- ++ + if (channel > 0 && !bc->nt ) { + bc->channel = channel; + cb_event(EVENT_NEW_CHANNEL,bc,NULL); + } + } + +-static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + CALL_PROCEEDING_t *proceeding=(CALL_PROCEEDING_t*)((unsigned long)msg->data+ HEADER_LEN); + //struct misdn_stack *stack=get_stack_by_bc(bc); +- ++ + { + int exclusive, channel; + dec_ie_channel_id(proceeding->CHANNEL_ID, (Q931_info_t *)proceeding, &exclusive, &channel, nt,bc); + + set_channel(bc,channel); +- ++ + } +- ++ + dec_ie_progress(proceeding->PROGRESS, (Q931_info_t *)proceeding, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); +- +- +-#ifdef DEBUG +- printf("Parsing PROCEEDING Msg\n"); ++ ++ ++#ifdef DEBUG ++ printf("Parsing PROCEEDING Msg\n"); + #endif + } + static msg_t *build_proceeding (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + CALL_PROCEEDING_t *proceeding; +- msg_t *msg =(msg_t*)create_l3msg(CC_PROCEEDING | REQUEST, MT_CALL_PROCEEDING, bc?bc->l3_id:-1, sizeof(CALL_PROCEEDING_t) ,nt); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_PROCEEDING | REQUEST, MT_CALL_PROCEEDING, bc?bc->l3_id:-1, sizeof(CALL_PROCEEDING_t) ,nt); ++ + proceeding=(CALL_PROCEEDING_t*)((msg->data+HEADER_LEN)); + + enc_ie_channel_id(&proceeding->CHANNEL_ID, msg, 1,bc->channel, nt,bc); +- +- if (nt) ++ ++ if (nt) + enc_ie_progress(&proceeding->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); +- + +-#ifdef DEBUG +- printf("Building PROCEEDING Msg\n"); ++ ++#ifdef DEBUG ++ printf("Building PROCEEDING Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_alerting (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_alerting (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +- int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; ++ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + ALERTING_t *alerting=(ALERTING_t*)((unsigned long)(msg->data+HEADER_LEN)); + //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); +- ++ + dec_ie_progress(alerting->PROGRESS, (Q931_info_t *)alerting, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); +- +-#ifdef DEBUG +- printf("Parsing ALERTING Msg\n"); ++ ++#ifdef DEBUG ++ printf("Parsing ALERTING Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + ALERTING_t *alerting; +- msg_t *msg =(msg_t*)create_l3msg(CC_ALERTING | REQUEST, MT_ALERTING, bc?bc->l3_id:-1, sizeof(ALERTING_t) ,nt); +- +- alerting=(ALERTING_t*)((msg->data+HEADER_LEN)); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_ALERTING | REQUEST, MT_ALERTING, bc?bc->l3_id:-1, sizeof(ALERTING_t) ,nt); ++ ++ alerting=(ALERTING_t*)((msg->data+HEADER_LEN)); ++ + enc_ie_channel_id(&alerting->CHANNEL_ID, msg, 1,bc->channel, nt,bc); +- +- if (nt) ++ ++ if (nt) + enc_ie_progress(&alerting->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); +-#ifdef DEBUG +- printf("Building ALERTING Msg\n"); ++#ifdef DEBUG ++ printf("Building ALERTING Msg\n"); + #endif +- return msg; ++ return msg; + } + + +-static void parse_progress (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_progress (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; +- PROGRESS_t *progress=(PROGRESS_t*)((unsigned long)(msg->data+HEADER_LEN)); +- //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); +- ++ PROGRESS_t *progress=(PROGRESS_t*)((unsigned long)(msg->data+HEADER_LEN)); ++ //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); ++ + dec_ie_progress(progress->PROGRESS, (Q931_info_t *)progress, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); +- +-#ifdef DEBUG +- printf("Parsing PROGRESS Msg\n"); ++ ++#ifdef DEBUG ++ printf("Parsing PROGRESS Msg\n"); + #endif + } + +-static msg_t *build_progress (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_progress (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + PROGRESS_t *progress; +- msg_t *msg =(msg_t*)create_l3msg(CC_PROGRESS | REQUEST, MT_PROGRESS, bc?bc->l3_id:-1, sizeof(PROGRESS_t) ,nt); +- +- progress=(PROGRESS_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_PROGRESS | REQUEST, MT_PROGRESS, bc?bc->l3_id:-1, sizeof(PROGRESS_t) ,nt); + +-#ifdef DEBUG +- printf("Building PROGRESS Msg\n"); ++ progress=(PROGRESS_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building PROGRESS Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +-{ ++static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++{ + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SETUP_t *setup= (SETUP_t*)((unsigned long)msg->data+HEADER_LEN); + Q931_info_t *qi=(Q931_info_t*)((unsigned long)msg->data+HEADER_LEN); ++ int type; ++ int plan; ++ int present; ++ int screen; ++ int reason; + +-#ifdef DEBUG +- printf("Parsing SETUP Msg\n"); ++#ifdef DEBUG ++ printf("Parsing SETUP Msg\n"); + #endif +- { +- int type,plan,present, screen; +- char id[32]; +- dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, id, sizeof(id)-1, nt,bc); + +- bc->onumplan=type; +- strcpy(bc->oad, id); +- switch (present) { +- case 0: +- bc->pres=0; /* screened */ +- break; +- case 1: +- bc->pres=1; /* not screened */ +- break; +- default: +- bc->pres=0; +- } +- switch (screen) { +- case 0: +- break; +- default: +- ; +- } ++ dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, bc->caller.number, sizeof(bc->caller.number) - 1, nt, bc); ++ bc->caller.number_type = type; ++ bc->caller.number_plan = plan; ++ switch (present) { ++ default: ++ case 0: ++ bc->caller.presentation = 0; /* presentation allowed */ ++ break; ++ case 1: ++ bc->caller.presentation = 1; /* presentation restricted */ ++ break; ++ case 2: ++ bc->caller.presentation = 2; /* Number not available */ ++ break; + } +- { +- int type, plan; +- char number[32]; +- dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *)setup, &type, &plan, number, sizeof(number)-1, nt,bc); +- strcpy(bc->dad, number); +- bc->dnumplan=type; ++ if (0 <= screen) { ++ bc->caller.screening = screen; ++ } else { ++ bc->caller.screening = 0; /* Unscreened */ + } +- { +- char keypad[32]; +- dec_ie_keypad(setup->KEYPAD, (Q931_info_t *)setup, keypad, sizeof(keypad)-1, nt,bc); +- strcpy(bc->keypad, keypad); +- } + +- { +- dec_ie_complete(setup->COMPLETE, (Q931_info_t *)setup, &bc->sending_complete, nt,bc); +- ++ dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *) setup, &type, &plan, bc->dialed.number, sizeof(bc->dialed.number) - 1, nt, bc); ++ bc->dialed.number_type = type; ++ bc->dialed.number_plan = plan; ++ ++ dec_ie_keypad(setup->KEYPAD, (Q931_info_t *) setup, bc->keypad, sizeof(bc->keypad) - 1, nt, bc); ++ ++ dec_ie_complete(setup->COMPLETE, (Q931_info_t *) setup, &bc->sending_complete, nt, bc); ++ ++ dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *) setup, &type, &plan, &present, &screen, &reason, bc->redirecting.from.number, sizeof(bc->redirecting.from.number) - 1, nt, bc); ++ bc->redirecting.from.number_type = type; ++ bc->redirecting.from.number_plan = plan; ++ switch (present) { ++ default: ++ case 0: ++ bc->redirecting.from.presentation = 0; /* presentation allowed */ ++ break; ++ case 1: ++ bc->redirecting.from.presentation = 1; /* presentation restricted */ ++ break; ++ case 2: ++ bc->redirecting.from.presentation = 2; /* Number not available */ ++ break; + } +- +- { +- int type, plan, present, screen, reason; +- char id[32]; +- dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *)setup, &type, &plan, &present, &screen, &reason, id, sizeof(id)-1, nt,bc); +- +- strcpy(bc->rad, id); +- bc->rnumplan=type; ++ if (0 <= screen) { ++ bc->redirecting.from.screening = screen; ++ } else { ++ bc->redirecting.from.screening = 0; /* Unscreened */ + } ++ if (0 <= reason) { ++ bc->redirecting.reason = reason; ++ } else { ++ bc->redirecting.reason = mISDN_REDIRECTING_REASON_UNKNOWN; ++ } ++ + { + int coding, capability, mode, rate, multi, user, async, urate, stopbits, dbits, parity; ++ + dec_ie_bearer(setup->BEARER, (Q931_info_t *)setup, &coding, &capability, &mode, &rate, &multi, &user, &async, &urate, &stopbits, &dbits, &parity, nt,bc); + switch (capability) { +- case -1: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; ++ case -1: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; + break; + case 0: bc->capability=INFO_CAPABILITY_SPEECH; + break; +@@ -228,7 +276,7 @@ + case 8: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; + bc->user1 = user; + bc->urate = urate; +- ++ + bc->rate = rate; + bc->mode = mode; + break; +@@ -237,7 +285,7 @@ + default: + break; + } +- ++ + switch(user) { + case 2: + bc->law=INFO_CODEC_ULAW; +@@ -247,15 +295,15 @@ + break; + default: + bc->law=INFO_CODEC_ALAW; +- ++ + } +- +- bc->capability=capability; ++ ++ bc->capability=capability; + } + { + int exclusive, channel; + dec_ie_channel_id(setup->CHANNEL_ID, (Q931_info_t *)setup, &exclusive, &channel, nt,bc); +- ++ + set_channel(bc,channel); + } + +@@ -266,55 +314,63 @@ + else + cb_log(1,bc->port,"NO USERUESRINFO\n"); + } +- ++ + dec_ie_progress(setup->PROGRESS, (Q931_info_t *)setup, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); +- ++ + } + +-#define ANY_CHANNEL 0xff /* IE attribut for 'any channel' */ +-static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++#define ANY_CHANNEL 0xff /* IE attribute for 'any channel' */ ++static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SETUP_t *setup; +- msg_t *msg =(msg_t*)create_l3msg(CC_SETUP | REQUEST, MT_SETUP, bc?bc->l3_id:-1, sizeof(SETUP_t) ,nt); +- +- setup=(SETUP_t*)((msg->data+HEADER_LEN)); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_SETUP | REQUEST, MT_SETUP, bc?bc->l3_id:-1, sizeof(SETUP_t) ,nt); ++ ++ setup=(SETUP_t*)((msg->data+HEADER_LEN)); ++ + if (bc->channel == 0 || bc->channel == ANY_CHANNEL || bc->channel==-1) + enc_ie_channel_id(&setup->CHANNEL_ID, msg, 0, bc->channel, nt,bc); +- else ++ else + enc_ie_channel_id(&setup->CHANNEL_ID, msg, 1, bc->channel, nt,bc); +- +- +- { +- int type=bc->onumplan,plan=1,present=bc->pres,screen=bc->screen; +- enc_ie_calling_pn(&setup->CALLING_PN, msg, type, plan, present, +- screen, bc->oad, nt, bc); ++ ++ ++ enc_ie_calling_pn(&setup->CALLING_PN, msg, bc->caller.number_type, bc->caller.number_plan, ++ bc->caller.presentation, bc->caller.screening, bc->caller.number, nt, bc); ++ ++ if (bc->dialed.number[0]) { ++ enc_ie_called_pn(&setup->CALLED_PN, msg, bc->dialed.number_type, bc->dialed.number_plan, bc->dialed.number, nt, bc); + } +- +- { +- if (bc->dad[0]) +- enc_ie_called_pn(&setup->CALLED_PN, msg, bc->dnumplan, 1, bc->dad, nt,bc); ++ ++ if (bc->redirecting.from.number[0]) { ++ enc_ie_redir_nr(&setup->REDIR_NR, msg, bc->redirecting.from.number_type, bc->redirecting.from.number_plan, ++ bc->redirecting.from.presentation, bc->redirecting.from.screening, bc->redirecting.reason, ++ bc->redirecting.from.number, nt, bc); + } + +- { +- if (bc->rad[0]) +- enc_ie_redir_nr(&setup->REDIR_NR, msg, 1, 1, bc->pres, bc->screen, 0, bc->rad, nt,bc); ++ if (bc->keypad[0]) { ++ enc_ie_keypad(&setup->KEYPAD, msg, bc->keypad, nt,bc); + } + +- { +- if (bc->keypad[0]) +- enc_ie_keypad(&setup->KEYPAD, msg, bc->keypad, nt,bc); +- } +- +- ++ ++ + if (*bc->display) { +- enc_ie_display(&setup->DISPLAY, msg, bc->display, nt,bc); ++ enc_ie_display(&setup->DISPLAY, msg, bc->display, nt, bc); ++ } else if (nt && bc->caller.presentation == 0) { ++ char display[sizeof(bc->display)]; ++ ++ /* Presentation is allowed */ ++ build_display_str(display, sizeof(display), bc->display_setup, bc->caller.name, bc->caller.number); ++ if (display[0]) { ++ enc_ie_display(&setup->DISPLAY, msg, display, nt, bc); ++ } + } +- ++ + { +- int coding=0, capability, mode=0 /* 2 for packet ! */ +- ,user, rate=0x10; ++ int coding = 0; ++ int capability; ++ int mode = 0; /* 2 for packet! */ ++ int user; ++ int rate = 0x10; + + switch (bc->law) { + case INFO_CODEC_ULAW: user=2; +@@ -324,7 +380,7 @@ + default: + user=3; + } +- ++ + switch (bc->capability) { + case INFO_CAPABILITY_SPEECH: capability = 0; + break; +@@ -337,81 +393,108 @@ + user=-1; + break; + default: +- capability=bc->capability; ++ capability=bc->capability; + } +- +- +- ++ + enc_ie_bearer(&setup->BEARER, msg, coding, capability, mode, rate, -1, user, nt,bc); + } + + if (bc->sending_complete) { + enc_ie_complete(&setup->COMPLETE,msg, bc->sending_complete, nt, bc); + } +- ++ + if (bc->uulen) { + int protocol=4; + enc_ie_useruser(&setup->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); + cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); + } + +-#ifdef DEBUG +- printf("Building SETUP Msg\n"); ++#ifdef DEBUG ++ printf("Building SETUP Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + CONNECT_t *connect=(CONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); +- +- int plan,pres,screen; +- ++ int type; ++ int plan; ++ int pres; ++ int screen; ++ + bc->ces = connect->ces; +- bc->ces = connect->ces; + + dec_ie_progress(connect->PROGRESS, (Q931_info_t *)connect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); + +- dec_ie_connected_pn(connect->CONNECT_PN,(Q931_info_t *)connect, &bc->cpnnumplan, &plan, &pres, &screen, bc->cad, 31, nt, bc); ++ dec_ie_connected_pn(connect->CONNECT_PN, (Q931_info_t *) connect, &type, &plan, ++ &pres, &screen, bc->connected.number, sizeof(bc->connected.number) - 1, nt, bc); ++ bc->connected.number_type = type; ++ bc->connected.number_plan = plan; ++ switch (pres) { ++ default: ++ case 0: ++ bc->connected.presentation = 0; /* presentation allowed */ ++ break; ++ case 1: ++ bc->connected.presentation = 1; /* presentation restricted */ ++ break; ++ case 2: ++ bc->connected.presentation = 2; /* Number not available */ ++ break; ++ } ++ if (0 <= screen) { ++ bc->connected.screening = screen; ++ } else { ++ bc->connected.screening = 0; /* Unscreened */ ++ } + + /* + cb_log(1,bc->port,"CONNETED PN: %s cpn_dialplan:%d\n", connected_pn, type); + */ +- +-#ifdef DEBUG +- printf("Parsing CONNECT Msg\n"); ++ ++#ifdef DEBUG ++ printf("Parsing CONNECT Msg\n"); + #endif + } + +-static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + CONNECT_t *connect; +- msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | REQUEST, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_t) ,nt); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | REQUEST, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_t) ,nt); ++ + cb_log(6,bc->port,"BUILD_CONNECT: bc:%p bc->l3id:%d, nt:%d\n",bc,bc->l3_id,nt); + +- connect=(CONNECT_t*)((msg->data+HEADER_LEN)); ++ connect=(CONNECT_t*)((msg->data+HEADER_LEN)); + + if (nt) { + time_t now; + time(&now); + enc_ie_date(&connect->DATE, msg, now, nt,bc); + } +- +- { +- int type=bc->cpnnumplan, plan=1, present=2, screen=0; +- enc_ie_connected_pn(&connect->CONNECT_PN, msg, type,plan, present, screen, bc->cad, nt , bc); ++ ++ enc_ie_connected_pn(&connect->CONNECT_PN, msg, bc->connected.number_type, bc->connected.number_plan, ++ bc->connected.presentation, bc->connected.screening, bc->connected.number, nt, bc); ++ ++ if (nt && bc->connected.presentation == 0) { ++ char display[sizeof(bc->display)]; ++ ++ /* Presentation is allowed */ ++ build_display_str(display, sizeof(display), bc->display_connected, bc->connected.name, bc->connected.number); ++ if (display[0]) { ++ enc_ie_display(&connect->DISPLAY, msg, display, nt, bc); ++ } + } + +-#ifdef DEBUG +- printf("Building CONNECT Msg\n"); ++#ifdef DEBUG ++ printf("Building CONNECT Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SETUP_ACKNOWLEDGE_t *setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((unsigned long)(msg->data+HEADER_LEN)); +@@ -423,384 +506,384 @@ + + set_channel(bc, channel); + } +- ++ + dec_ie_progress(setup_acknowledge->PROGRESS, (Q931_info_t *)setup_acknowledge, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); +-#ifdef DEBUG +- printf("Parsing SETUP_ACKNOWLEDGE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing SETUP_ACKNOWLEDGE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_setup_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_setup_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SETUP_ACKNOWLEDGE_t *setup_acknowledge; +- msg_t *msg =(msg_t*)create_l3msg(CC_SETUP_ACKNOWLEDGE | REQUEST, MT_SETUP_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SETUP_ACKNOWLEDGE_t) ,nt); +- +- setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_SETUP_ACKNOWLEDGE | REQUEST, MT_SETUP_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SETUP_ACKNOWLEDGE_t) ,nt); ++ ++ setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ + enc_ie_channel_id(&setup_acknowledge->CHANNEL_ID, msg, 1,bc->channel, nt,bc); +- +- if (nt) ++ ++ if (nt) + enc_ie_progress(&setup_acknowledge->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); +- +-#ifdef DEBUG +- printf("Building SETUP_ACKNOWLEDGE Msg\n"); ++ ++#ifdef DEBUG ++ printf("Building SETUP_ACKNOWLEDGE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_connect_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_connect_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing CONNECT_ACKNOWLEDGE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing CONNECT_ACKNOWLEDGE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_connect_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_connect_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + CONNECT_ACKNOWLEDGE_t *connect_acknowledge; +- msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | RESPONSE, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_ACKNOWLEDGE_t) ,nt); +- +- connect_acknowledge=(CONNECT_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | RESPONSE, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_ACKNOWLEDGE_t) ,nt); ++ ++ connect_acknowledge=(CONNECT_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ + enc_ie_channel_id(&connect_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc); +- +-#ifdef DEBUG +- printf("Building CONNECT_ACKNOWLEDGE Msg\n"); ++ ++#ifdef DEBUG ++ printf("Building CONNECT_ACKNOWLEDGE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_user_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_user_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing USER_INFORMATION Msg\n"); ++#ifdef DEBUG ++ printf("Parsing USER_INFORMATION Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_user_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_user_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + USER_INFORMATION_t *user_information; +- msg_t *msg =(msg_t*)create_l3msg(CC_USER_INFORMATION | REQUEST, MT_USER_INFORMATION, bc?bc->l3_id:-1, sizeof(USER_INFORMATION_t) ,nt); +- +- user_information=(USER_INFORMATION_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_USER_INFORMATION | REQUEST, MT_USER_INFORMATION, bc?bc->l3_id:-1, sizeof(USER_INFORMATION_t) ,nt); + +-#ifdef DEBUG +- printf("Building USER_INFORMATION Msg\n"); ++ user_information=(USER_INFORMATION_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building USER_INFORMATION Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_suspend_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_suspend_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing SUSPEND_REJECT Msg\n"); ++#ifdef DEBUG ++ printf("Parsing SUSPEND_REJECT Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_suspend_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_suspend_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SUSPEND_REJECT_t *suspend_reject; +- msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_REJECT | REQUEST, MT_SUSPEND_REJECT, bc?bc->l3_id:-1, sizeof(SUSPEND_REJECT_t) ,nt); +- +- suspend_reject=(SUSPEND_REJECT_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_REJECT | REQUEST, MT_SUSPEND_REJECT, bc?bc->l3_id:-1, sizeof(SUSPEND_REJECT_t) ,nt); + +-#ifdef DEBUG +- printf("Building SUSPEND_REJECT Msg\n"); ++ suspend_reject=(SUSPEND_REJECT_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building SUSPEND_REJECT Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_resume_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_resume_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing RESUME_REJECT Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RESUME_REJECT Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_resume_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_resume_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RESUME_REJECT_t *resume_reject; +- msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_REJECT | REQUEST, MT_RESUME_REJECT, bc?bc->l3_id:-1, sizeof(RESUME_REJECT_t) ,nt); +- +- resume_reject=(RESUME_REJECT_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_REJECT | REQUEST, MT_RESUME_REJECT, bc?bc->l3_id:-1, sizeof(RESUME_REJECT_t) ,nt); + +-#ifdef DEBUG +- printf("Building RESUME_REJECT Msg\n"); ++ resume_reject=(RESUME_REJECT_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building RESUME_REJECT Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_hold (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_hold (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing HOLD Msg\n"); ++#ifdef DEBUG ++ printf("Parsing HOLD Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_hold (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_hold (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + HOLD_t *hold; +- msg_t *msg =(msg_t*)create_l3msg(CC_HOLD | REQUEST, MT_HOLD, bc?bc->l3_id:-1, sizeof(HOLD_t) ,nt); +- +- hold=(HOLD_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_HOLD | REQUEST, MT_HOLD, bc?bc->l3_id:-1, sizeof(HOLD_t) ,nt); + +-#ifdef DEBUG +- printf("Building HOLD Msg\n"); ++ hold=(HOLD_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building HOLD Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_suspend (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_suspend (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing SUSPEND Msg\n"); ++#ifdef DEBUG ++ printf("Parsing SUSPEND Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_suspend (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_suspend (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SUSPEND_t *suspend; +- msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND | REQUEST, MT_SUSPEND, bc?bc->l3_id:-1, sizeof(SUSPEND_t) ,nt); +- +- suspend=(SUSPEND_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND | REQUEST, MT_SUSPEND, bc?bc->l3_id:-1, sizeof(SUSPEND_t) ,nt); + +-#ifdef DEBUG +- printf("Building SUSPEND Msg\n"); ++ suspend=(SUSPEND_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building SUSPEND Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_resume (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_resume (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing RESUME Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RESUME Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_resume (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_resume (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RESUME_t *resume; +- msg_t *msg =(msg_t*)create_l3msg(CC_RESUME | REQUEST, MT_RESUME, bc?bc->l3_id:-1, sizeof(RESUME_t) ,nt); +- +- resume=(RESUME_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_RESUME | REQUEST, MT_RESUME, bc?bc->l3_id:-1, sizeof(RESUME_t) ,nt); + +-#ifdef DEBUG +- printf("Building RESUME Msg\n"); ++ resume=(RESUME_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building RESUME Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_hold_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_hold_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing HOLD_ACKNOWLEDGE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing HOLD_ACKNOWLEDGE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_hold_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_hold_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + HOLD_ACKNOWLEDGE_t *hold_acknowledge; +- msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_ACKNOWLEDGE | REQUEST, MT_HOLD_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(HOLD_ACKNOWLEDGE_t) ,nt); +- +- hold_acknowledge=(HOLD_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_ACKNOWLEDGE | REQUEST, MT_HOLD_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(HOLD_ACKNOWLEDGE_t) ,nt); + +-#ifdef DEBUG +- printf("Building HOLD_ACKNOWLEDGE Msg\n"); ++ hold_acknowledge=(HOLD_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building HOLD_ACKNOWLEDGE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_suspend_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_suspend_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing SUSPEND_ACKNOWLEDGE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing SUSPEND_ACKNOWLEDGE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_suspend_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_suspend_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SUSPEND_ACKNOWLEDGE_t *suspend_acknowledge; +- msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_ACKNOWLEDGE | REQUEST, MT_SUSPEND_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SUSPEND_ACKNOWLEDGE_t) ,nt); +- +- suspend_acknowledge=(SUSPEND_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_ACKNOWLEDGE | REQUEST, MT_SUSPEND_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SUSPEND_ACKNOWLEDGE_t) ,nt); + +-#ifdef DEBUG +- printf("Building SUSPEND_ACKNOWLEDGE Msg\n"); ++ suspend_acknowledge=(SUSPEND_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building SUSPEND_ACKNOWLEDGE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_resume_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_resume_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing RESUME_ACKNOWLEDGE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RESUME_ACKNOWLEDGE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_resume_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_resume_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RESUME_ACKNOWLEDGE_t *resume_acknowledge; +- msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_ACKNOWLEDGE | REQUEST, MT_RESUME_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RESUME_ACKNOWLEDGE_t) ,nt); +- +- resume_acknowledge=(RESUME_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_ACKNOWLEDGE | REQUEST, MT_RESUME_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RESUME_ACKNOWLEDGE_t) ,nt); + +-#ifdef DEBUG +- printf("Building RESUME_ACKNOWLEDGE Msg\n"); ++ resume_acknowledge=(RESUME_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building RESUME_ACKNOWLEDGE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_hold_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_hold_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing HOLD_REJECT Msg\n"); ++#ifdef DEBUG ++ printf("Parsing HOLD_REJECT Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_hold_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_hold_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + HOLD_REJECT_t *hold_reject; +- msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_REJECT | REQUEST, MT_HOLD_REJECT, bc?bc->l3_id:-1, sizeof(HOLD_REJECT_t) ,nt); +- +- hold_reject=(HOLD_REJECT_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_REJECT | REQUEST, MT_HOLD_REJECT, bc?bc->l3_id:-1, sizeof(HOLD_REJECT_t) ,nt); + +-#ifdef DEBUG +- printf("Building HOLD_REJECT Msg\n"); ++ hold_reject=(HOLD_REJECT_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building HOLD_REJECT Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_retrieve (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_retrieve (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing RETRIEVE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RETRIEVE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_retrieve (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_retrieve (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RETRIEVE_t *retrieve; +- msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE | REQUEST, MT_RETRIEVE, bc?bc->l3_id:-1, sizeof(RETRIEVE_t) ,nt); +- +- retrieve=(RETRIEVE_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE | REQUEST, MT_RETRIEVE, bc?bc->l3_id:-1, sizeof(RETRIEVE_t) ,nt); + +-#ifdef DEBUG +- printf("Building RETRIEVE Msg\n"); ++ retrieve=(RETRIEVE_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building RETRIEVE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_retrieve_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_retrieve_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing RETRIEVE_ACKNOWLEDGE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RETRIEVE_ACKNOWLEDGE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_retrieve_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_retrieve_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RETRIEVE_ACKNOWLEDGE_t *retrieve_acknowledge; +- msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_ACKNOWLEDGE | REQUEST, MT_RETRIEVE_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RETRIEVE_ACKNOWLEDGE_t) ,nt); +- +- retrieve_acknowledge=(RETRIEVE_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_ACKNOWLEDGE | REQUEST, MT_RETRIEVE_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RETRIEVE_ACKNOWLEDGE_t) ,nt); + ++ retrieve_acknowledge=(RETRIEVE_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); ++ + enc_ie_channel_id(&retrieve_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc); +-#ifdef DEBUG +- printf("Building RETRIEVE_ACKNOWLEDGE Msg\n"); ++#ifdef DEBUG ++ printf("Building RETRIEVE_ACKNOWLEDGE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_retrieve_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_retrieve_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing RETRIEVE_REJECT Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RETRIEVE_REJECT Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_retrieve_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_retrieve_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RETRIEVE_REJECT_t *retrieve_reject; +- msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_REJECT | REQUEST, MT_RETRIEVE_REJECT, bc?bc->l3_id:-1, sizeof(RETRIEVE_REJECT_t) ,nt); +- +- retrieve_reject=(RETRIEVE_REJECT_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_REJECT | REQUEST, MT_RETRIEVE_REJECT, bc?bc->l3_id:-1, sizeof(RETRIEVE_REJECT_t) ,nt); + +-#ifdef DEBUG +- printf("Building RETRIEVE_REJECT Msg\n"); ++ retrieve_reject=(RETRIEVE_REJECT_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building RETRIEVE_REJECT Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_disconnect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_disconnect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + DISCONNECT_t *disconnect=(DISCONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); + int location; +- int cause; ++ int cause; + dec_ie_cause(disconnect->CAUSE, (Q931_info_t *)(disconnect), &location, &cause, nt,bc); + if (cause>0) bc->cause=cause; + + dec_ie_progress(disconnect->PROGRESS, (Q931_info_t *)disconnect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); +-#ifdef DEBUG +- printf("Parsing DISCONNECT Msg\n"); ++#ifdef DEBUG ++ printf("Parsing DISCONNECT Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + DISCONNECT_t *disconnect; +- msg_t *msg =(msg_t*)create_l3msg(CC_DISCONNECT | REQUEST, MT_DISCONNECT, bc?bc->l3_id:-1, sizeof(DISCONNECT_t) ,nt); +- +- disconnect=(DISCONNECT_t*)((msg->data+HEADER_LEN)); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_DISCONNECT | REQUEST, MT_DISCONNECT, bc?bc->l3_id:-1, sizeof(DISCONNECT_t) ,nt); ++ ++ disconnect=(DISCONNECT_t*)((msg->data+HEADER_LEN)); ++ + enc_ie_cause(&disconnect->CAUSE, msg, (nt)?1:0, bc->out_cause,nt,bc); + if (nt) enc_ie_progress(&disconnect->PROGRESS, msg, 0, nt?1:5, 8 ,nt,bc); + +@@ -809,42 +892,42 @@ + enc_ie_useruser(&disconnect->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); + cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); + } +- +-#ifdef DEBUG +- printf("Building DISCONNECT Msg\n"); ++ ++#ifdef DEBUG ++ printf("Building DISCONNECT Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_restart (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_restart (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RESTART_t *restart=(RESTART_t*)((unsigned long)(msg->data+HEADER_LEN)); + + struct misdn_stack *stack=get_stack_by_bc(bc); +- +-#ifdef DEBUG ++ ++#ifdef DEBUG + printf("Parsing RESTART Msg\n"); + #endif +- ++ + { + int exclusive; + dec_ie_channel_id(restart->CHANNEL_ID, (Q931_info_t *)restart, &exclusive, &bc->restart_channel, nt,bc); + cb_log(3, stack->port, "CC_RESTART Request on channel:%d on this port.\n", bc->restart_channel); + } +- ++ + } + +-static msg_t *build_restart (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_restart (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RESTART_t *restart; +- msg_t *msg =(msg_t*)create_l3msg(CC_RESTART | REQUEST, MT_RESTART, bc?bc->l3_id:-1, sizeof(RESTART_t) ,nt); +- +- restart=(RESTART_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_RESTART | REQUEST, MT_RESTART, bc?bc->l3_id:-1, sizeof(RESTART_t) ,nt); + +-#ifdef DEBUG +- printf("Building RESTART Msg\n"); ++ restart=(RESTART_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building RESTART Msg\n"); + #endif + + if (bc->channel > 0) { +@@ -855,33 +938,33 @@ + } + + cb_log(0,bc->port, "Restarting channel %d\n", bc->channel); +- return msg; ++ return msg; + } + +-static void parse_release (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_release (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RELEASE_t *release=(RELEASE_t*)((unsigned long)(msg->data+HEADER_LEN)); + int location; + int cause; +- ++ + dec_ie_cause(release->CAUSE, (Q931_info_t *)(release), &location, &cause, nt,bc); + if (cause>0) bc->cause=cause; +-#ifdef DEBUG +- printf("Parsing RELEASE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RELEASE Msg\n"); + #endif + +- ++ + } + +-static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RELEASE_t *release; +- msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE | REQUEST, MT_RELEASE, bc?bc->l3_id:-1, sizeof(RELEASE_t) ,nt); +- +- release=(RELEASE_t*)((msg->data+HEADER_LEN)); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE | REQUEST, MT_RELEASE, bc?bc->l3_id:-1, sizeof(RELEASE_t) ,nt); ++ ++ release=(RELEASE_t*)((msg->data+HEADER_LEN)); ++ + if (bc->out_cause>= 0) + enc_ie_cause(&release->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); + +@@ -890,14 +973,14 @@ + enc_ie_useruser(&release->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); + cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); + } +- +-#ifdef DEBUG +- printf("Building RELEASE Msg\n"); ++ ++#ifdef DEBUG ++ printf("Building RELEASE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RELEASE_COMPLETE_t *release_complete=(RELEASE_COMPLETE_t*)((unsigned long)(msg->data+HEADER_LEN)); +@@ -926,19 +1009,19 @@ + dec_ie_cause(release_complete->CAUSE, (Q931_info_t *)(release_complete), &location, &cause, nt,bc); + if (cause>0) bc->cause=cause; + +-#ifdef DEBUG +- printf("Parsing RELEASE_COMPLETE Msg\n"); ++#ifdef DEBUG ++ printf("Parsing RELEASE_COMPLETE Msg\n"); + #endif + } + +-static msg_t *build_release_complete (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_release_complete (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + RELEASE_COMPLETE_t *release_complete; +- msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, bc?bc->l3_id:-1, sizeof(RELEASE_COMPLETE_t) ,nt); +- +- release_complete=(RELEASE_COMPLETE_t*)((msg->data+HEADER_LEN)); +- ++ msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, bc?bc->l3_id:-1, sizeof(RELEASE_COMPLETE_t) ,nt); ++ ++ release_complete=(RELEASE_COMPLETE_t*)((msg->data+HEADER_LEN)); ++ + enc_ie_cause(&release_complete->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); + + if (bc->uulen) { +@@ -946,23 +1029,23 @@ + enc_ie_useruser(&release_complete->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); + cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); + } +- +-#ifdef DEBUG +- printf("Building RELEASE_COMPLETE Msg\n"); ++ ++#ifdef DEBUG ++ printf("Building RELEASE_COMPLETE Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; +- FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); +- Q931_info_t *qi = (Q931_info_t*)(msg->data+HEADER_LEN); ++ FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); ++ Q931_info_t *qi = (Q931_info_t*)(msg->data+HEADER_LEN); + unsigned char *p = NULL; + int err; + +-#ifdef DEBUG +- printf("Parsing FACILITY Msg\n"); ++#ifdef DEBUG ++ printf("Parsing FACILITY Msg\n"); + #endif + + if (!bc->nt) { +@@ -973,31 +1056,37 @@ + } + if (!p) + return; +- ++ + err = decodeFac(p, &(bc->fac_in)); + if (err) { + cb_log(5, bc->port, "Decoding FACILITY failed! (%d)\n", err); + } + } + +-static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { +- int len, +- HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; +- unsigned char *ie_fac, +- fac_tmp[256]; +- msg_t *msg =(msg_t*)create_l3msg(CC_FACILITY | REQUEST, MT_FACILITY, bc?bc->l3_id:-1, sizeof(FACILITY_t) ,nt); +- FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); ++ int len; ++ int HEADER_LEN; ++ unsigned char *ie_fac; ++ unsigned char fac_tmp[256]; ++ msg_t *msg; ++ FACILITY_t *facility; + Q931_info_t *qi; + +-#ifdef DEBUG +- printf("Building FACILITY Msg\n"); ++#ifdef DEBUG ++ printf("Building FACILITY Msg\n"); + #endif +- ++ + len = encodeFac(fac_tmp, &(bc->fac_out)); +- if (len <= 0) ++ if (len <= 0) { ++ /* mISDN does not know how to build the requested facility structure */ + return NULL; ++ } + ++ msg = (msg_t *) create_l3msg(CC_FACILITY | REQUEST, MT_FACILITY, bc ? bc->l3_id : -1, sizeof(FACILITY_t), nt); ++ HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; ++ facility = (FACILITY_t *) (msg->data + HEADER_LEN); ++ + ie_fac = msg_put(msg, len); + if (bc->nt) { + facility->FACILITY = ie_fac + 1; +@@ -1009,147 +1098,144 @@ + memcpy(ie_fac, fac_tmp, len); + + if (*bc->display) { ++#ifdef DEBUG + printf("Sending %s as Display\n", bc->display); ++#endif + enc_ie_display(&facility->DISPLAY, msg, bc->display, nt,bc); + } + +- return msg; ++ return msg; + } + +-static void parse_notify (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_notify (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing NOTIFY Msg\n"); ++#ifdef DEBUG ++ printf("Parsing NOTIFY Msg\n"); + #endif + } + +-static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + NOTIFY_t *notify; +- msg_t *msg =(msg_t*)create_l3msg(CC_NOTIFY | REQUEST, MT_NOTIFY, bc?bc->l3_id:-1, sizeof(NOTIFY_t) ,nt); +- +- notify=(NOTIFY_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_NOTIFY | REQUEST, MT_NOTIFY, bc?bc->l3_id:-1, sizeof(NOTIFY_t) ,nt); + +-#ifdef DEBUG +- printf("Building NOTIFY Msg\n"); ++ notify=(NOTIFY_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building NOTIFY Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_status_enquiry (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_status_enquiry (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing STATUS_ENQUIRY Msg\n"); ++#ifdef DEBUG ++ printf("Parsing STATUS_ENQUIRY Msg\n"); + #endif + } + +-static msg_t *build_status_enquiry (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_status_enquiry (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + STATUS_ENQUIRY_t *status_enquiry; +- msg_t *msg =(msg_t*)create_l3msg(CC_STATUS_ENQUIRY | REQUEST, MT_STATUS_ENQUIRY, bc?bc->l3_id:-1, sizeof(STATUS_ENQUIRY_t) ,nt); +- +- status_enquiry=(STATUS_ENQUIRY_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_STATUS_ENQUIRY | REQUEST, MT_STATUS_ENQUIRY, bc?bc->l3_id:-1, sizeof(STATUS_ENQUIRY_t) ,nt); + +-#ifdef DEBUG +- printf("Building STATUS_ENQUIRY Msg\n"); ++ status_enquiry=(STATUS_ENQUIRY_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building STATUS_ENQUIRY Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + INFORMATION_t *information=(INFORMATION_t*)((unsigned long)(msg->data+HEADER_LEN)); +- { +- int type, plan; +- char number[32]; +- char keypad[32]; +- dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *)information, &type, &plan, number, sizeof(number)-1, nt, bc); +- dec_ie_keypad(information->KEYPAD, (Q931_info_t *)information, keypad, sizeof(keypad)-1, nt, bc); +- strcpy(bc->info_dad, number); +- strcpy(bc->keypad,keypad); +- } +-#ifdef DEBUG +- printf("Parsing INFORMATION Msg\n"); ++ int type, plan; ++ ++ dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *) information, &type, &plan, bc->info_dad, sizeof(bc->info_dad) - 1, nt, bc); ++ dec_ie_keypad(information->KEYPAD, (Q931_info_t *) information, bc->keypad, sizeof(bc->keypad) - 1, nt, bc); ++ ++#ifdef DEBUG ++ printf("Parsing INFORMATION Msg\n"); + #endif + } + +-static msg_t *build_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + INFORMATION_t *information; +- msg_t *msg =(msg_t*)create_l3msg(CC_INFORMATION | REQUEST, MT_INFORMATION, bc?bc->l3_id:-1, sizeof(INFORMATION_t) ,nt); +- +- information=(INFORMATION_t*)((msg->data+HEADER_LEN)); +- +- { +- enc_ie_called_pn(&information->CALLED_PN, msg, 0, 1, bc->info_dad, nt,bc); +- } ++ msg_t *msg =(msg_t*)create_l3msg(CC_INFORMATION | REQUEST, MT_INFORMATION, bc?bc->l3_id:-1, sizeof(INFORMATION_t) ,nt); + ++ information=(INFORMATION_t*)((msg->data+HEADER_LEN)); ++ ++ enc_ie_called_pn(&information->CALLED_PN, msg, 0, 1, bc->info_dad, nt,bc); ++ + { + if (*bc->display) { ++#ifdef DEBUG + printf("Sending %s as Display\n", bc->display); ++#endif + enc_ie_display(&information->DISPLAY, msg, bc->display, nt,bc); + } + } +- +-#ifdef DEBUG +- printf("Building INFORMATION Msg\n"); ++ ++#ifdef DEBUG ++ printf("Building INFORMATION Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_status (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_status (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + STATUS_t *status=(STATUS_t*)((unsigned long)(msg->data+HEADER_LEN)); + int location; + int cause; +- ++ + dec_ie_cause(status->CAUSE, (Q931_info_t *)(status), &location, &cause, nt,bc); + if (cause>0) bc->cause=cause; +- ; + +-#ifdef DEBUG +- printf("Parsing STATUS Msg\n"); ++#ifdef DEBUG ++ printf("Parsing STATUS Msg\n"); + #endif + } + +-static msg_t *build_status (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_status (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + STATUS_t *status; +- msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); +- +- status=(STATUS_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); + +-#ifdef DEBUG +- printf("Building STATUS Msg\n"); ++ status=(STATUS_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building STATUS Msg\n"); + #endif +- return msg; ++ return msg; + } + +-static void parse_timeout (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) ++static void parse_timeout (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) + { +-#ifdef DEBUG +- printf("Parsing STATUS Msg\n"); +-#endif ++#ifdef DEBUG ++ printf("Parsing STATUS Msg\n"); ++#endif + } + +-static msg_t *build_timeout (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) ++static msg_t *build_timeout (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) + { + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + STATUS_t *status; +- msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); +- +- status=(STATUS_t*)((msg->data+HEADER_LEN)); ++ msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); + +-#ifdef DEBUG +- printf("Building STATUS Msg\n"); ++ status=(STATUS_t*)((msg->data+HEADER_LEN)); ++ ++#ifdef DEBUG ++ printf("Building STATUS Msg\n"); + #endif +- return msg; ++ return msg; + } + + +@@ -1161,97 +1247,40 @@ + /** Msg Array **/ + + struct isdn_msg msgs_g[] = { +- {CC_PROCEEDING,L3,EVENT_PROCEEDING, +- parse_proceeding,build_proceeding, +- "PROCEEDING"}, +- {CC_ALERTING,L3,EVENT_ALERTING, +- parse_alerting,build_alerting, +- "ALERTING"}, +- {CC_PROGRESS,L3,EVENT_PROGRESS, +- parse_progress,build_progress, +- "PROGRESS"}, +- {CC_SETUP,L3,EVENT_SETUP, +- parse_setup,build_setup, +- "SETUP"}, +- {CC_CONNECT,L3,EVENT_CONNECT, +- parse_connect,build_connect, +- "CONNECT"}, +- {CC_SETUP_ACKNOWLEDGE,L3,EVENT_SETUP_ACKNOWLEDGE, +- parse_setup_acknowledge,build_setup_acknowledge, +- "SETUP_ACKNOWLEDGE"}, +- {CC_CONNECT_ACKNOWLEDGE ,L3,EVENT_CONNECT_ACKNOWLEDGE , +- parse_connect_acknowledge ,build_connect_acknowledge, +- "CONNECT_ACKNOWLEDGE "}, +- {CC_USER_INFORMATION,L3,EVENT_USER_INFORMATION, +- parse_user_information,build_user_information, +- "USER_INFORMATION"}, +- {CC_SUSPEND_REJECT,L3,EVENT_SUSPEND_REJECT, +- parse_suspend_reject,build_suspend_reject, +- "SUSPEND_REJECT"}, +- {CC_RESUME_REJECT,L3,EVENT_RESUME_REJECT, +- parse_resume_reject,build_resume_reject, +- "RESUME_REJECT"}, +- {CC_HOLD,L3,EVENT_HOLD, +- parse_hold,build_hold, +- "HOLD"}, +- {CC_SUSPEND,L3,EVENT_SUSPEND, +- parse_suspend,build_suspend, +- "SUSPEND"}, +- {CC_RESUME,L3,EVENT_RESUME, +- parse_resume,build_resume, +- "RESUME"}, +- {CC_HOLD_ACKNOWLEDGE,L3,EVENT_HOLD_ACKNOWLEDGE, +- parse_hold_acknowledge,build_hold_acknowledge, +- "HOLD_ACKNOWLEDGE"}, +- {CC_SUSPEND_ACKNOWLEDGE,L3,EVENT_SUSPEND_ACKNOWLEDGE, +- parse_suspend_acknowledge,build_suspend_acknowledge, +- "SUSPEND_ACKNOWLEDGE"}, +- {CC_RESUME_ACKNOWLEDGE,L3,EVENT_RESUME_ACKNOWLEDGE, +- parse_resume_acknowledge,build_resume_acknowledge, +- "RESUME_ACKNOWLEDGE"}, +- {CC_HOLD_REJECT,L3,EVENT_HOLD_REJECT, +- parse_hold_reject,build_hold_reject, +- "HOLD_REJECT"}, +- {CC_RETRIEVE,L3,EVENT_RETRIEVE, +- parse_retrieve,build_retrieve, +- "RETRIEVE"}, +- {CC_RETRIEVE_ACKNOWLEDGE,L3,EVENT_RETRIEVE_ACKNOWLEDGE, +- parse_retrieve_acknowledge,build_retrieve_acknowledge, +- "RETRIEVE_ACKNOWLEDGE"}, +- {CC_RETRIEVE_REJECT,L3,EVENT_RETRIEVE_REJECT, +- parse_retrieve_reject,build_retrieve_reject, +- "RETRIEVE_REJECT"}, +- {CC_DISCONNECT,L3,EVENT_DISCONNECT, +- parse_disconnect,build_disconnect, +- "DISCONNECT"}, +- {CC_RESTART,L3,EVENT_RESTART, +- parse_restart,build_restart, +- "RESTART"}, +- {CC_RELEASE,L3,EVENT_RELEASE, +- parse_release,build_release, +- "RELEASE"}, +- {CC_RELEASE_COMPLETE,L3,EVENT_RELEASE_COMPLETE, +- parse_release_complete,build_release_complete, +- "RELEASE_COMPLETE"}, +- {CC_FACILITY,L3,EVENT_FACILITY, +- parse_facility,build_facility, +- "FACILITY"}, +- {CC_NOTIFY,L3,EVENT_NOTIFY, +- parse_notify,build_notify, +- "NOTIFY"}, +- {CC_STATUS_ENQUIRY,L3,EVENT_STATUS_ENQUIRY, +- parse_status_enquiry,build_status_enquiry, +- "STATUS_ENQUIRY"}, +- {CC_INFORMATION,L3,EVENT_INFORMATION, +- parse_information,build_information, +- "INFORMATION"}, +- {CC_STATUS,L3,EVENT_STATUS, +- parse_status,build_status, +- "STATUS"}, +- {CC_TIMEOUT,L3,EVENT_TIMEOUT, +- parse_timeout,build_timeout, +- "TIMEOUT"}, +- {0,0,0,NULL,NULL,NULL} ++/* *INDENT-OFF* */ ++ /* misdn_msg, event, msg_parser, msg_builder, info */ ++ { CC_PROCEEDING, EVENT_PROCEEDING, parse_proceeding, build_proceeding, "PROCEEDING" }, ++ { CC_ALERTING, EVENT_ALERTING, parse_alerting, build_alerting, "ALERTING" }, ++ { CC_PROGRESS, EVENT_PROGRESS, parse_progress, build_progress, "PROGRESS" }, ++ { CC_SETUP, EVENT_SETUP, parse_setup, build_setup, "SETUP" }, ++ { CC_CONNECT, EVENT_CONNECT, parse_connect, build_connect, "CONNECT" }, ++ { CC_SETUP_ACKNOWLEDGE, EVENT_SETUP_ACKNOWLEDGE, parse_setup_acknowledge, build_setup_acknowledge, "SETUP_ACKNOWLEDGE" }, ++ { CC_CONNECT_ACKNOWLEDGE, EVENT_CONNECT_ACKNOWLEDGE, parse_connect_acknowledge, build_connect_acknowledge, "CONNECT_ACKNOWLEDGE " }, ++ { CC_USER_INFORMATION, EVENT_USER_INFORMATION, parse_user_information, build_user_information, "USER_INFORMATION" }, ++ { CC_SUSPEND_REJECT, EVENT_SUSPEND_REJECT, parse_suspend_reject, build_suspend_reject, "SUSPEND_REJECT" }, ++ { CC_RESUME_REJECT, EVENT_RESUME_REJECT, parse_resume_reject, build_resume_reject, "RESUME_REJECT" }, ++ { CC_HOLD, EVENT_HOLD, parse_hold, build_hold, "HOLD" }, ++ { CC_SUSPEND, EVENT_SUSPEND, parse_suspend, build_suspend, "SUSPEND" }, ++ { CC_RESUME, EVENT_RESUME, parse_resume, build_resume, "RESUME" }, ++ { CC_HOLD_ACKNOWLEDGE, EVENT_HOLD_ACKNOWLEDGE, parse_hold_acknowledge, build_hold_acknowledge, "HOLD_ACKNOWLEDGE" }, ++ { CC_SUSPEND_ACKNOWLEDGE, EVENT_SUSPEND_ACKNOWLEDGE, parse_suspend_acknowledge, build_suspend_acknowledge, "SUSPEND_ACKNOWLEDGE" }, ++ { CC_RESUME_ACKNOWLEDGE, EVENT_RESUME_ACKNOWLEDGE, parse_resume_acknowledge, build_resume_acknowledge, "RESUME_ACKNOWLEDGE" }, ++ { CC_HOLD_REJECT, EVENT_HOLD_REJECT, parse_hold_reject, build_hold_reject, "HOLD_REJECT" }, ++ { CC_RETRIEVE, EVENT_RETRIEVE, parse_retrieve, build_retrieve, "RETRIEVE" }, ++ { CC_RETRIEVE_ACKNOWLEDGE, EVENT_RETRIEVE_ACKNOWLEDGE, parse_retrieve_acknowledge, build_retrieve_acknowledge, "RETRIEVE_ACKNOWLEDGE" }, ++ { CC_RETRIEVE_REJECT, EVENT_RETRIEVE_REJECT, parse_retrieve_reject, build_retrieve_reject, "RETRIEVE_REJECT" }, ++ { CC_DISCONNECT, EVENT_DISCONNECT, parse_disconnect, build_disconnect, "DISCONNECT" }, ++ { CC_RESTART, EVENT_RESTART, parse_restart, build_restart, "RESTART" }, ++ { CC_RELEASE, EVENT_RELEASE, parse_release, build_release, "RELEASE" }, ++ { CC_RELEASE_COMPLETE, EVENT_RELEASE_COMPLETE, parse_release_complete, build_release_complete, "RELEASE_COMPLETE" }, ++ { CC_FACILITY, EVENT_FACILITY, parse_facility, build_facility, "FACILITY" }, ++ { CC_NOTIFY, EVENT_NOTIFY, parse_notify, build_notify, "NOTIFY" }, ++ { CC_STATUS_ENQUIRY, EVENT_STATUS_ENQUIRY, parse_status_enquiry, build_status_enquiry, "STATUS_ENQUIRY" }, ++ { CC_INFORMATION, EVENT_INFORMATION, parse_information, build_information, "INFORMATION" }, ++ { CC_STATUS, EVENT_STATUS, parse_status, build_status, "STATUS" }, ++ { CC_TIMEOUT, EVENT_TIMEOUT, parse_timeout, build_timeout, "TIMEOUT" }, ++ { 0, 0, NULL, NULL, NULL } ++/* *INDENT-ON* */ + }; + + #define msgs_max (sizeof(msgs_g)/sizeof(struct isdn_msg)) +@@ -1263,15 +1292,15 @@ + + if (nt){ + mISDNuser_head_t *hh = (mISDNuser_head_t*)msg->data; +- ++ + for (i=0; i< msgs_max -1; i++) { + if ( (hh->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i; + } +- ++ + } else { + iframe_t *frm = (iframe_t*)msg->data; +- +- for (i=0; i< msgs_max -1; i++) ++ ++ for (i=0; i< msgs_max -1; i++) + if ( (frm->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i; + } + +@@ -1281,11 +1310,11 @@ + int isdn_msg_get_index_by_event(struct isdn_msg msgs[], enum event_e event, int nt) + { + int i; +- for (i=0; i< msgs_max; i++) ++ for (i=0; i< msgs_max; i++) + if ( event == msgs[i].event) return i; + + cb_log(10,0, "get_index: event not found!\n"); +- ++ + return -1; + } + +@@ -1318,9 +1347,9 @@ + char * isdn_get_info(struct isdn_msg msgs[], enum event_e event, int nt) + { + int i=isdn_msg_get_index_by_event(msgs, event, nt); +- ++ + if(i>=0) return msgs[i].info; +- ++ + if (event == EVENT_CLEANUP) return EVENT_CLEAN_INFO; + if (event == EVENT_DTMF_TONE) return EVENT_DTMF_TONE_INFO; + if (event == EVENT_NEW_L3ID) return EVENT_NEW_L3ID_INFO; +@@ -1331,7 +1360,7 @@ + if (event == EVENT_TONE_GENERATE) return EVENT_TONE_GENERATE_INFO; + if (event == EVENT_PORT_ALARM) return EVENT_PORT_ALARM_INFO; + if (event == EVENT_BCHAN_ERROR) return EVENT_BCHAN_ERROR_INFO; +- ++ + return NULL; + } + +@@ -1348,6 +1377,6 @@ + { + int i=isdn_msg_get_index_by_event(msgs, event, nt); + if(i<0) return NULL; +- ++ + return msgs[i].msg_builder(msgs, bc, nt); + } +Index: channels/misdn/portinfo.c +=================================================================== +--- a/channels/misdn/portinfo.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/portinfo.c (.../trunk) (revision 186562) +@@ -1,4 +1,4 @@ +-/*! \file ++/*! \file + * \brief Interface to mISDN - port info + * \author Christian Richter <crich@beronet.com> + */ +Index: channels/misdn/isdn_lib.c +=================================================================== +--- a/channels/misdn/isdn_lib.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/isdn_lib.c (.../trunk) (revision 186562) +@@ -11,7 +11,7 @@ + * the GNU General Public License + */ + +-/*! \file ++/*! \file + * \brief Interface to mISDN + * \author Christian Richter <crich@beronet.com> + */ +@@ -33,7 +33,7 @@ + int (*cb_jb_empty)(struct misdn_bchannel *bc, char *buffer, int len); + + +-/* ++/* + * Define ARRAY_LEN() because I cannot + * #include "asterisk/utils.h" + */ +@@ -62,7 +62,7 @@ + return stack->pri; + } + } +- ++ + return -1; + } + +@@ -74,15 +74,15 @@ + return stack->nt; + } + } +- ++ + return -1; + } + +-void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel) ++void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel) + { + memset (dummybc,0,sizeof(struct misdn_bchannel)); + dummybc->port=port; +- if (l3id==0) ++ if (l3id==0) + dummybc->l3_id = MISDN_ID_DUMMY; + else + dummybc->l3_id=l3id; +@@ -119,7 +119,7 @@ + } + + int misdn_lib_is_port_blocked(int port) +-{ ++{ + struct misdn_stack *stack=get_misdn_stack(); + for ( ; stack; stack=stack->next) { + if (stack->port == port) { +@@ -138,12 +138,12 @@ + return -1; + } + +-int misdn_lib_get_maxchans(int port) ++int misdn_lib_get_maxchans(int port) + { + struct misdn_stack *stack=get_misdn_stack(); + for ( ; stack; stack=stack->next) { + if (stack->port == port) { +- if (stack->pri) ++ if (stack->pri) + return 30; + else + return 2; +@@ -159,7 +159,7 @@ + + if (!bc) + return NULL; +- ++ + for ( ; stack; stack = stack->next) { + if (bc->port == stack->port) + return stack; +@@ -171,19 +171,24 @@ + + void get_show_stack_details(int port, char *buf) + { +- struct misdn_stack *stack=get_misdn_stack(); +- +- for ( ; stack; stack=stack->next) { +- if (stack->port == port) break; ++ struct misdn_stack *stack = get_misdn_stack(); ++ ++ for (; stack; stack = stack->next) { ++ if (stack->port == port) { ++ break; ++ } + } +- ++ + if (stack) { +- sprintf(buf, "* Port %d Type %s Prot. %s L2Link %s L1Link:%s Blocked:%d", +- stack->port, stack->nt ? "NT" : "TE", stack->ptp ? "PTP" : "PMP", +- stack->l2link ? "UP" : "DOWN", stack->l1link ? "UP" : "DOWN", ++ sprintf(buf, "* Port %2d Type %s Prot. %s L2Link %s L1Link:%s Blocked:%d", ++ stack->port, ++ stack->nt ? "NT" : "TE", ++ stack->ptp ? "PTP" : "PMP", ++ stack->l2link ? "UP " : "DOWN", ++ stack->l1link ? "UP " : "DOWN", + stack->blocked); + } else { +- buf[0]=0; ++ buf[0] = 0; + } + } + +@@ -219,10 +224,10 @@ + void *user_data; + + msg_queue_t upqueue; +- msg_queue_t activatequeue; +- ++ msg_queue_t activatequeue; ++ + sem_t new_msg; +- ++ + struct misdn_stack *stack_list; + } ; + +@@ -248,7 +253,7 @@ + int misdn_lib_port_restart(int port); + int misdn_lib_pid_restart(int pid); + +-extern struct isdn_msg msgs_g[]; ++extern struct isdn_msg msgs_g[]; + + #define ISDN_PID_L3_B_USER 0x430000ff + #define ISDN_PID_L4_B_USER 0x440000ff +@@ -304,7 +309,7 @@ + "Res Digital", + "Unknown Bearer" + }; +- ++ + switch (cap) { + case INFO_CAPABILITY_SPEECH: + return bearers[0]; +@@ -330,7 +335,7 @@ + static void init_flip_bits(void) + { + int i,k; +- ++ + for (i = 0 ; i < 256 ; i++) { + unsigned char sample = 0 ; + for (k = 0; k<8; k++) { +@@ -344,11 +349,11 @@ + { + int i; + char * start = buf; +- ++ + for (i = 0 ; i < len; i++) { + buf[i] = flip_table[(unsigned char)buf[i]]; + } +- ++ + return start; + } + +@@ -359,13 +364,13 @@ + { + int i = 0; + msg_t *dmsg; +- ++ + while(i < 10) + { + dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL); + if (dmsg) + return(dmsg); +- ++ + if (!i) + printf("cannot allocate memory, trying again...\n"); + i++; +@@ -383,10 +388,10 @@ + msg_t *dmsg; + Q931_info_t *qi; + iframe_t *frm; +- ++ + if (!ntmode) + size = sizeof(Q931_info_t)+2; +- ++ + while(i < 10) { + if (ntmode) { + dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL); +@@ -406,7 +411,7 @@ + return(dmsg); + } + } +- ++ + if (!i) printf("cannot allocate memory, trying again...\n"); + i++; + usleep(300000); +@@ -425,11 +430,11 @@ + cb_log(0,bc->port,"send_msg: IEK!! no stack\n "); + return -1; + } +- ++ + frm->addr = (stack->upper_id | FLG_MSG_DOWN); + frm->dinfo = bc->l3_id; + frm->len = (dmsg->len) - mISDN_HEADER_LEN; +- ++ + cb_log(4,stack->port,"Sending msg, prim:%x addr:%x dinfo:%x\n",frm->prim,frm->addr,frm->dinfo); + + mISDN_write(midev, dmsg->data, dmsg->len, TIMEOUT_1SEC); +@@ -457,7 +462,7 @@ + /* We have opted to never receive any available inband recorded messages */ + return 0; + } +- ++ + switch (bc->progress_indicator) { + case INFO_PI_INBAND_AVAILABLE: + case INFO_PI_CALL_NOT_E2E_ISDN: +@@ -505,7 +510,7 @@ + cb_log(0,stack->port,"couldn't set channel %d in\n", channel ); + return -1; + } +- ++ + return 0; + } + +@@ -517,9 +522,9 @@ + int chan=0; + int bnums = stack->pri ? stack->b_num : stack->b_num - 1; + +- if (bc->channel_found) ++ if (bc->channel_found) + return 0; +- ++ + bc->channel_found=1; + + cb_log(5,stack->port,"find_free_chan: req_chan:%d\n",channel); +@@ -528,7 +533,7 @@ + cb_log(0, stack->port, " !! out of bound call to find_free_chan_in_stack! (ch:%d)\n", channel); + return 0; + } +- ++ + channel--; + + if (dec) { +@@ -558,7 +563,7 @@ + dump_chan_list(stack); + bc->out_cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; + return -1; +- } ++ } + + if (set_chan_in_stack(stack, chan)<0) { + cb_log (0, stack->port, "Channel Already in use:%d\n", chan); +@@ -576,8 +581,8 @@ + cb_log(0,stack?stack->port:0, "empty_chan_in_stack: cannot empty channel %d\n",channel); + return -1; + } +- +- cb_log (4, stack?stack->port:0, "empty_chan_in_stack: %d\n",channel); ++ ++ cb_log (4, stack?stack->port:0, "empty_chan_in_stack: %d\n",channel); + stack->channels[channel-1] = 0; + dump_chan_list(stack); + return 0; +@@ -585,7 +590,7 @@ + + char *bc_state2str(enum bchannel_state state) { + int i; +- ++ + struct bchan_state_s { + char *n; + enum bchannel_state s; +@@ -604,7 +609,7 @@ + {"BCHAN_CLEAN_REQUEST", BCHAN_CLEAN_REQUEST}, + {"BCHAN_ERROR", BCHAN_ERROR} + }; +- ++ + for (i=0; i< sizeof(states)/sizeof(struct bchan_state_s); i++) + if ( states[i].s == state) + return states[i].n; +@@ -618,7 +623,7 @@ + bc->l3_id, + bc_state2str(bc->bc_state), + bc_state2str(state) ); +- ++ + switch (state) { + case BCHAN_ACTIVATED: + if (bc->next_bc_state == BCHAN_BRIDGED) { +@@ -644,6 +649,29 @@ + + static void empty_bc(struct misdn_bchannel *bc) + { ++ bc->caller.presentation = 0; /* allowed */ ++ bc->caller.number_plan = NUMPLAN_ISDN; ++ bc->caller.number_type = NUMTYPE_UNKNOWN; ++ bc->caller.name[0] = 0; ++ bc->caller.number[0] = 0; ++ bc->caller.subaddress[0] = 0; ++ ++ bc->connected.presentation = 0; /* allowed */ ++ bc->connected.number_plan = NUMPLAN_ISDN; ++ bc->connected.number_type = NUMTYPE_UNKNOWN; ++ bc->connected.name[0] = 0; ++ bc->connected.number[0] = 0; ++ bc->connected.subaddress[0] = 0; ++ ++ bc->redirecting.from.presentation = 0; /* allowed */ ++ bc->redirecting.from.number_plan = NUMPLAN_ISDN; ++ bc->redirecting.from.number_type = NUMTYPE_UNKNOWN; ++ bc->redirecting.from.name[0] = 0; ++ bc->redirecting.from.number[0] = 0; ++ bc->redirecting.from.subaddress[0] = 0; ++ ++ bc->redirecting.reason = mISDN_REDIRECTING_REASON_UNKNOWN; ++ + bc->dummy=0; + + bc->bframe_len=0; +@@ -656,38 +684,32 @@ + bc->sending_complete = 0; + + bc->restart_channel=0; +- ++ + bc->conf_id = 0; + + bc->need_more_infos = 0; +- ++ + bc->send_dtmf=0; + bc->nodsp=0; + bc->nojitter=0; + + bc->time_usec=0; +- ++ + bc->rxgain=0; + bc->txgain=0; + + bc->crypt=0; + bc->curptx=0; bc->curprx=0; +- ++ + bc->crypt_key[0] = 0; +- ++ + bc->generate_tone=0; + bc->tone_cnt=0; +- +- bc->dnumplan=NUMPLAN_UNKNOWN; +- bc->onumplan=NUMPLAN_UNKNOWN; +- bc->rnumplan=NUMPLAN_UNKNOWN; +- bc->cpnnumplan=NUMPLAN_UNKNOWN; +- + + bc->active = 0; + + bc->early_bconnect = 1; +- ++ + #ifdef MISDN_1_2 + *bc->pipeline = 0; + #else +@@ -698,17 +720,22 @@ + bc->AOCD_need_export = 0; + + bc->orig=0; +- ++ + bc->cause = AST_CAUSE_NORMAL_CLEARING; + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; +- bc->pres = 0; /* allowed */ +- ++ ++ bc->display_connected = 0; /* none */ ++ bc->display_setup = 0; /* none */ ++ ++ bc->presentation = 0; /* allowed */ ++ bc->set_presentation = 0; ++ + bc->evq=EVENT_NOTHING; + + bc->progress_coding=0; + bc->progress_location=0; + bc->progress_indicator=0; +- ++ + /** Set Default Bearer Caps **/ + bc->capability=INFO_CAPABILITY_SPEECH; + bc->law=INFO_CODEC_ALAW; +@@ -716,24 +743,23 @@ + bc->rate=0x10; + bc->user1=0; + bc->urate=0; +- ++ + bc->hdlc=0; +- +- ++ ++ bc->dialed.number_plan = NUMPLAN_ISDN; ++ bc->dialed.number_type = NUMTYPE_UNKNOWN; ++ bc->dialed.number[0] = 0; ++ bc->dialed.subaddress[0] = 0; ++ + bc->info_dad[0] = 0; + bc->display[0] = 0; + bc->infos_pending[0] = 0; +- bc->cad[0] = 0; +- bc->oad[0] = 0; +- bc->dad[0] = 0; +- bc->rad[0] = 0; +- bc->orig_dad[0] = 0; + bc->uu[0]=0; + bc->uulen=0; +- ++ + bc->fac_in.Function = Fac_None; + bc->fac_out.Function = Fac_None; +- ++ + bc->te_choose_channel = 0; + bc->channel_found= 0; + +@@ -748,32 +774,32 @@ + struct misdn_stack * stack; + + cb_log(3, bc?bc->port:0, "$$$ CLEANUP CALLED pid:%d\n", bc?bc->pid:-1); +- ++ + if (!bc ) return -1; + stack=get_stack_by_bc(bc); +- ++ + if (!stack) return -1; +- ++ + switch (bc->bc_state ) { + case BCHAN_CLEANED: + cb_log(5, stack->port, "$$$ Already cleaned up bc with stid :%x\n", bc->b_stid); + return -1; +- ++ + default: + break; + } +- ++ + cb_log(2, stack->port, "$$$ Cleaning up bc with stid :%x pid:%d\n", bc->b_stid, bc->pid); +- ++ + manager_ec_disable(bc); + + manager_bchannel_deactivate(bc); + + mISDN_write_frame(stack->midev, buff, bc->layer_id|FLG_MSG_TARGET|FLG_MSG_DOWN, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); +- ++ + bc->b_stid = 0; + bc_state_change(bc, BCHAN_CLEANED); +- ++ + return ret; + } + +@@ -785,14 +811,14 @@ + + for (i=0; i<=stack->b_num; i++) { + if (global_state == MISDN_INITIALIZED) { +- cb_event(EVENT_CLEANUP, &stack->bc[i], NULL); ++ cb_event(EVENT_CLEANUP, &stack->bc[i], NULL); + empty_chan_in_stack(stack,i+1); + empty_bc(&stack->bc[i]); + clean_up_bc(&stack->bc[i]); + stack->bc[i].in_use = 0; + } +- +- } ++ ++ } + } + + static int new_te_id = 0; +@@ -801,9 +827,9 @@ + + static int misdn_lib_get_l1_down(struct misdn_stack *stack) + { +- /* Pull Up L1 */ ++ /* Pull Up L1 */ + iframe_t act; +- act.prim = PH_DEACTIVATE | REQUEST; ++ act.prim = PH_DEACTIVATE | REQUEST; + act.addr = stack->lower_id|FLG_MSG_DOWN; + act.dinfo = 0; + act.len = 0; +@@ -815,38 +841,38 @@ + + static int misdn_lib_get_l2_down(struct misdn_stack *stack) + { +- ++ + if (stack->ptp && (stack->nt) ) { + msg_t *dmsg; + /* L2 */ + dmsg = create_l2msg(DL_RELEASE| REQUEST, 0, 0); +- ++ + if (stack->nst.manager_l3(&stack->nst, dmsg)) + free_msg(dmsg); +- ++ + } else { + iframe_t act; +- ++ + act.prim = DL_RELEASE| REQUEST; + act.addr = (stack->upper_id |FLG_MSG_DOWN) ; +- ++ + act.dinfo = 0; + act.len = 0; + return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); + } +- ++ + return 0; + } + + + static int misdn_lib_get_l1_up(struct misdn_stack *stack) + { +- /* Pull Up L1 */ ++ /* Pull Up L1 */ + iframe_t act; +- act.prim = PH_ACTIVATE | REQUEST; ++ act.prim = PH_ACTIVATE | REQUEST; + act.addr = (stack->upper_id | FLG_MSG_DOWN) ; + +- ++ + act.dinfo = 0; + act.len = 0; + +@@ -856,26 +882,26 @@ + + int misdn_lib_get_l2_up(struct misdn_stack *stack) + { +- ++ + if (stack->ptp && (stack->nt) ) { + msg_t *dmsg; + /* L2 */ + dmsg = create_l2msg(DL_ESTABLISH | REQUEST, 0, 0); +- ++ + if (stack->nst.manager_l3(&stack->nst, dmsg)) + free_msg(dmsg); +- ++ + } else { + iframe_t act; +- ++ + act.prim = DL_ESTABLISH | REQUEST; + act.addr = (stack->upper_id |FLG_MSG_DOWN) ; +- ++ + act.dinfo = 0; + act.len = 0; + return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); + } +- ++ + return 0; + } + +@@ -883,10 +909,10 @@ + static int misdn_lib_get_l2_te_ptp_up(struct misdn_stack *stack) + { + iframe_t act; +- ++ + act.prim = DL_ESTABLISH | REQUEST; + act.addr = (stack->upper_id & ~LAYER_ID_MASK) | 3 | FLG_MSG_DOWN; +- ++ + act.dinfo = 0; + act.len = 0; + return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); +@@ -897,14 +923,14 @@ + static int misdn_lib_get_short_status(struct misdn_stack *stack) + { + iframe_t act; +- +- +- act.prim = MGR_SHORTSTATUS | REQUEST; +- ++ ++ ++ act.prim = MGR_SHORTSTATUS | REQUEST; ++ + act.addr = (stack->upper_id | MSG_BROADCAST) ; + + act.dinfo = SSTATUS_BROADCAST_BIT | SSTATUS_ALL; +- ++ + act.len = 0; + return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); + } +@@ -929,7 +955,7 @@ + if (stack->procids[proc_id] == 0) { + break; + } +- } /* end for */ ++ } + if (proc_id == MAXPROCS) { + cb_log(0, stack->port, "Couldn't Create New ProcId.\n"); + return -1; +@@ -941,7 +967,7 @@ + bc->l3_id = l3_id; + cb_log(3, stack->port, " --> new_l3id %x\n", l3_id); + } else { +- if (stack->ptp || bc->te_choose_channel) { ++ if ((stack->pri && stack->ptp) || bc->te_choose_channel) { + /* we know exactly which channels are in use */ + if (find_free_chan_in_stack(stack, bc, bc->channel_preselected ? bc->channel : 0, bc->dec) < 0) { + return -1; +@@ -996,7 +1022,7 @@ + cb_log(0, bc->port, "setup_bc: NO STACK FOUND!!\n"); + return -1; + } +- ++ + midev = stack->midev; + channel = bc->channel - 1 - (bc->channel > 16); + b_stid = stack->b_stids[channel >= 0 ? channel : 0]; +@@ -1008,9 +1034,9 @@ + cb_log(4, stack->port, "$$$ bc already setup stid :%x (state:%s)\n", b_stid, bc_state2str(bc->bc_state) ); + return -1; + } +- ++ + cb_log(5, stack->port, "$$$ Setting up bc with stid :%x\n", b_stid); +- ++ + /*check if the b_stid is already initialized*/ + for (i=0; i <= stack->b_num; i++) { + if (stack->bc[i].b_stid == b_stid) { +@@ -1018,10 +1044,10 @@ + return -1; + } + } +- ++ + if (b_stid <= 0) { + cb_log(0, stack->port," -- Stid <=0 at the moment in channel:%d\n",channel); +- ++ + bc_state_change(bc,BCHAN_ERROR); + return 1; + } +@@ -1031,10 +1057,10 @@ + { + layer_info_t li; + memset(&li, 0, sizeof(li)); +- ++ + li.object_id = -1; + li.extentions = 0; +- ++ + li.st = bc->b_stid; /* given idx */ + + +@@ -1044,18 +1070,18 @@ + #endif + if ( bc->hdlc || bc->nodsp) { + cb_log(4, stack->port,"setup_bc: without dsp\n"); +- { ++ { + int l = sizeof(li.name); + strncpy(li.name, "B L3", l); + li.name[l-1] = 0; + } + li.pid.layermask = ISDN_LAYER((3)); + li.pid.protocol[3] = ISDN_PID_L3_B_USER; +- ++ + bc->layer=3; + } else { + cb_log(4, stack->port,"setup_bc: with dsp\n"); +- { ++ { + int l = sizeof(li.name); + strncpy(li.name, "B L4", l); + li.name[l-1] = 0; +@@ -1064,8 +1090,8 @@ + li.pid.protocol[4] = ISDN_PID_L4_B_USER; + + bc->layer=4; +- } +- ++ } ++ + ret = mISDN_new_layer(midev, &li); + if (ret ) { + cb_log(0, stack->port,"New Layer Err: %d %s\n",ret,strerror(errno)); +@@ -1073,23 +1099,23 @@ + bc_state_change(bc,BCHAN_ERROR); + return(-EINVAL); + } +- ++ + bc->layer_id = li.id; + } +- ++ + memset(&pid, 0, sizeof(pid)); +- +- +- ++ ++ ++ + cb_log(4, stack->port," --> Channel is %d\n", bc->channel); +- ++ + if (bc->nodsp) { + cb_log(2, stack->port," --> TRANSPARENT Mode (no DSP, no HDLC)\n"); + pid.protocol[1] = ISDN_PID_L1_B_64TRANS; + pid.protocol[2] = ISDN_PID_L2_B_TRANS; + pid.protocol[3] = ISDN_PID_L3_B_USER; + pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)); +- ++ + } else if ( bc->hdlc ) { + cb_log(2, stack->port," --> HDLC Mode\n"); + pid.protocol[1] = ISDN_PID_L1_B_64HDLC ; +@@ -1103,16 +1129,16 @@ + pid.protocol[3] = ISDN_PID_L3_B_DSP; + pid.protocol[4] = ISDN_PID_L4_B_USER; + pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) | ISDN_LAYER((4)); +- +- } + ++ } ++ + ret = mISDN_set_stack(midev, bc->b_stid, &pid); + + if (ret){ + cb_log(0, stack->port,"$$$ Set Stack Err: %d %s\n",ret,strerror(errno)); +- ++ + mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); +- ++ + bc_state_change(bc,BCHAN_ERROR); + cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); + return(-EINVAL); +@@ -1123,7 +1149,7 @@ + if (ret) { + cb_log(0, stack->port,"$$$ Set StackIND Err: %d %s\n",ret,strerror(errno)); + mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); +- ++ + bc_state_change(bc,BCHAN_ERROR); + cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); + return(-EINVAL); +@@ -1136,14 +1162,14 @@ + if (!bc->addr) { + cb_log(0, stack->port,"$$$ Get Layerid Err: %d %s\n",ret,strerror(errno)); + mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); +- ++ + bc_state_change(bc,BCHAN_ERROR); + cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); + return (-EINVAL); + } + + manager_bchannel_activate(bc); +- ++ + bc_state_change(bc,BCHAN_ACTIVATED); + + return 0; +@@ -1157,11 +1183,11 @@ + unsigned char buff[1025] = ""; + iframe_t *frm = (iframe_t *)buff; + int ret; +- ++ + if (!bc) return -1; +- ++ + cb_log(8, port, "Init.BC %d.\n",bidx); +- ++ + memset(bc, 0,sizeof(struct misdn_bchannel)); + + bc->send_lock=malloc(sizeof(struct send_lock)); +@@ -1169,40 +1195,40 @@ + return -1; + } + pthread_mutex_init(&bc->send_lock->lock, NULL); +- ++ + if (msn) { + int l = sizeof(bc->msn); + strncpy(bc->msn,msn, l); + bc->msn[l-1] = 0; + } +- +- ++ ++ + empty_bc(bc); + bc_state_change(bc, BCHAN_CLEANED); +- ++ + bc->port=stack->port; + bc->nt=stack->nt?1:0; + bc->pri=stack->pri; +- ++ + { + ibuffer_t* ibuf= init_ibuffer(MISDN_IBUF_SIZE); + + if (!ibuf) return -1; +- ++ + clear_ibuffer( ibuf); +- ++ + ibuf->rsem=malloc(sizeof(sem_t)); + if (!ibuf->rsem) { + return -1; + } +- ++ + bc->astbuf=ibuf; + + if (sem_init(ibuf->rsem,1,0)<0) + sem_init(ibuf->rsem,0,0); +- ++ + } +- ++ + { + stack_info_t *stinf; + ret = mISDN_get_stack_info(midev, stack->port, buff, sizeof(buff)); +@@ -1210,12 +1236,12 @@ + cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret); + return -1; + } +- ++ + stinf = (stack_info_t *)&frm->data.p; +- ++ + cb_log(8, port, " --> Child %x\n",stinf->child[bidx]); + } +- ++ + return 0; + } + +@@ -1227,7 +1253,7 @@ + unsigned char buff[1025]; + iframe_t *frm = (iframe_t *)buff; + stack_info_t *stinf; +- int i; ++ int i; + layer_info_t li; + + struct misdn_stack *stack = malloc(sizeof(struct misdn_stack)); +@@ -1235,28 +1261,28 @@ + + + cb_log(8, port, "Init. Stack.\n"); +- ++ + memset(stack,0,sizeof(struct misdn_stack)); +- ++ + for (i=0; i<MAX_BCHANS + 1; i++ ) stack->channels[i]=0; +- ++ + stack->port=port; + stack->midev=midev; + stack->ptp=ptp; +- ++ + stack->holding=NULL; + stack->pri=0; +- ++ + msg_queue_init(&stack->downqueue); + msg_queue_init(&stack->upqueue); +- ++ + /* query port's requirements */ + ret = mISDN_get_stack_info(midev, port, buff, sizeof(buff)); + if (ret < 0) { + cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret); + return(NULL); + } +- ++ + stinf = (stack_info_t *)&frm->data.p; + + stack->d_stid = stinf->id; +@@ -1264,7 +1290,7 @@ + + for (i=0; i<=stinf->childcnt; i++) + stack->b_stids[i] = stinf->child[i]; +- ++ + switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) { + case ISDN_PID_L0_TE_S0: + stack->nt=0; +@@ -1283,7 +1309,7 @@ + cb_log(8, port, "TE S2M Stack\n"); + stack->nt=1; + stack->pri=1; +- ++ + break; + default: + cb_log(0, port, "this is a unknown port type 0x%08x\n", stinf->pid.protocol[0]); +@@ -1291,19 +1317,19 @@ + } + + if (!stack->nt) { +- if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP ) { ++ if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP ) { + stack->ptp = 1; + } else { + stack->ptp = 0; + } + } +- ++ + { + int ret; + int nt=stack->nt; + + cb_log(8, port, "Init. Stack.\n"); +- ++ + memset(&li, 0, sizeof(li)); + { + int l = sizeof(li.name); +@@ -1315,15 +1341,15 @@ + li.pid.protocol[nt?2:4] = nt?ISDN_PID_L2_LAPD_NET:ISDN_PID_L4_CAPI20; + li.pid.layermask = ISDN_LAYER((nt?2:4)); + li.st = stack->d_stid; +- +- ++ ++ + ret = mISDN_new_layer(midev, &li); + if (ret) { + cb_log(0, port, "%s: Cannot add layer %d to this port.\n", __FUNCTION__, nt?2:4); + return(NULL); + } +- +- ++ ++ + stack->upper_id = li.id; + ret = mISDN_register_layer(midev, stack->d_stid, stack->upper_id); + if (ret) +@@ -1331,52 +1357,52 @@ + cb_log(0,port,"Cannot register layer %d of this port.\n", nt?2:4); + return(NULL); + } +- +- stack->lower_id = mISDN_get_layerid(midev, stack->d_stid, nt?1:3); ++ ++ stack->lower_id = mISDN_get_layerid(midev, stack->d_stid, nt?1:3); + if (stack->lower_id < 0) { + cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, nt?1:3); + return(NULL); + } +- ++ + stack->upper_id = mISDN_get_layerid(midev, stack->d_stid, nt?2:4); + if (stack->upper_id < 0) { + cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, 2); + return(NULL); + } +- ++ + cb_log(8, port, "NT Stacks upper_id %x\n",stack->upper_id); +- +- ++ ++ + /* create nst (nt-mode only) */ + if (nt) { +- ++ + memset(&stack->nst, 0, sizeof(net_stack_t)); + memset(&stack->mgr, 0, sizeof(manager_t)); +- ++ + stack->mgr.nst = &stack->nst; + stack->nst.manager = &stack->mgr; +- ++ + stack->nst.l3_manager = handle_event_nt; + stack->nst.device = midev; + stack->nst.cardnr = port; + stack->nst.d_stid = stack->d_stid; +- ++ + stack->nst.feature = FEATURE_NET_HOLD; + if (stack->ptp) + stack->nst.feature |= FEATURE_NET_PTP; + if (stack->pri) + stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID; +- ++ + stack->nst.l1_id = stack->lower_id; + stack->nst.l2_id = stack->upper_id; +- ++ + msg_queue_init(&stack->nst.down_queue); +- ++ + Isdnl2Init(&stack->nst); + Isdnl3Init(&stack->nst); +- +- } +- ++ ++ } ++ + if (!stack->nt) { + /*assume L1 is up, we'll get DEACTIVATES soon, for non + * up L1s*/ +@@ -1384,7 +1410,7 @@ + } + stack->l1link=0; + stack->l2link=0; +-#if 0 ++#if 0 + if (!stack->nt) { + misdn_lib_get_short_status(stack); + } else { +@@ -1396,12 +1422,12 @@ + + misdn_lib_get_short_status(stack); + misdn_lib_get_l1_up(stack); +- misdn_lib_get_l2_up(stack); +- ++ misdn_lib_get_l2_up(stack); ++ + } + + cb_log(8,0,"stack_init: port:%d lowerId:%x upperId:%x\n",stack->port,stack->lower_id, stack->upper_id); +- ++ + return stack; + } + +@@ -1415,11 +1441,11 @@ + cleanup_Isdnl2(&stack->nst); + cleanup_Isdnl3(&stack->nst); + } +- +- if (stack->lower_id) ++ ++ if (stack->lower_id) + mISDN_write_frame(stack->midev, buf, stack->lower_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + +- if (stack->upper_id) ++ if (stack->upper_id) + mISDN_write_frame(stack->midev, buf, stack->upper_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + } + +@@ -1427,14 +1453,14 @@ + static struct misdn_stack * find_stack_by_addr(int addr) + { + struct misdn_stack *stack; +- ++ + for (stack=glob_mgr->stack_list; + stack; + stack=stack->next) { + if ( (stack->upper_id&STACK_ID_MASK) == (addr&STACK_ID_MASK)) return stack; + + } +- ++ + return NULL; + } + +@@ -1442,24 +1468,24 @@ + static struct misdn_stack * find_stack_by_port(int port) + { + struct misdn_stack *stack; +- ++ + for (stack=glob_mgr->stack_list; + stack; +- stack=stack->next) ++ stack=stack->next) + if (stack->port == port) return stack; +- ++ + return NULL; + } + + static struct misdn_stack * find_stack_by_mgr(manager_t* mgr_nt) + { + struct misdn_stack *stack; +- ++ + for (stack=glob_mgr->stack_list; + stack; +- stack=stack->next) ++ stack=stack->next) + if ( &stack->mgr == mgr_nt) return stack; +- ++ + return NULL; + } + +@@ -1506,7 +1532,7 @@ + } + } + } +- ++ + return NULL; + } + +@@ -1514,7 +1540,7 @@ + { + struct misdn_stack* stack; + int i; +- ++ + for (stack=glob_mgr->stack_list; + stack; + stack=stack->next) { +@@ -1533,14 +1559,14 @@ + struct misdn_stack* stack=find_stack_by_port(port); + int i; + +- if (!stack) return NULL; +- ++ if (!stack) return NULL; ++ + for (i=0; i<=stack->b_num; i++) { + if ( stack->bc[i].channel== channel ) { + return &stack->bc[i]; + } + } +- ++ + return NULL; + } + +@@ -1551,16 +1577,23 @@ + static int handle_event ( struct misdn_bchannel *bc, enum event_e event, iframe_t *frm) + { + struct misdn_stack *stack=get_stack_by_bc(bc); +- ++ + if (!stack->nt) { +- ++ + switch (event) { + + case EVENT_CONNECT_ACKNOWLEDGE: + setup_bc(bc); + + if ( *bc->crypt_key ) { +- cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); ++ cb_log(4, stack->port, ++ "ENABLING BLOWFISH channel:%d caller%d:\"%s\" <%s> dialed%d:%s\n", ++ bc->channel, ++ bc->caller.number_type, ++ bc->caller.name, ++ bc->caller.number, ++ bc->dialed.number_type, ++ bc->dialed.number); + manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); + } + +@@ -1582,7 +1615,14 @@ + case EVENT_CONNECT: + + if ( *bc->crypt_key ) { +- cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); ++ cb_log(4, stack->port, ++ "ENABLING BLOWFISH channel:%d caller%d:\"%s\" <%s> dialed%d:%s\n", ++ bc->channel, ++ bc->caller.number_type, ++ bc->caller.name, ++ bc->caller.number, ++ bc->dialed.number_type, ++ bc->dialed.number); + manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); + } + case EVENT_ALERTING: +@@ -1602,12 +1642,12 @@ + + if (!bc->channel) + cb_log(0, stack->port, "Any Channel Requested, but we have no more!!\n"); +- else ++ else + cb_log(0, stack->port, "Requested Channel Already in Use releasing this call with cause 34!!!!\n"); + + /* when the channel is already in use, we can't +- * simply clear it, we need to make sure that +- * it will still be marked as in_use in the ++ * simply clear it, we need to make sure that ++ * it will still be marked as in_use in the + * available channels list.*/ + bc->channel=0; + +@@ -1626,7 +1666,7 @@ + break; + } + } else { /** NT MODE **/ +- ++ + } + return 0; + } +@@ -1636,7 +1676,7 @@ + struct misdn_bchannel *bc; + + if (!stack) return -1; +- ++ + switch (frm->prim) { + case CC_NEW_CR|INDICATION: + cb_log(7, stack->port, " --> lib: NEW_CR Ind with l3id:%x on this port.\n",frm->dinfo); +@@ -1646,7 +1686,7 @@ + cb_log(0, stack->port, " --> !! lib: No free channel!\n"); + return -1; + } +- ++ + cb_log(7, stack->port, " --> new_process: New L3Id: %x\n",frm->dinfo); + bc->l3_id=frm->dinfo; + return 1; +@@ -1663,14 +1703,14 @@ + { + struct misdn_bchannel *bc=find_bc_by_l3id(stack, frm->dinfo); + struct misdn_bchannel dummybc; +- ++ + if (!bc) { + cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); + misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0); +- +- bc=&dummybc; ++ ++ bc=&dummybc; + } +- ++ + if (bc) { + int channel = bc->channel; + cb_log(4, stack->port, " --> lib: CLEANING UP l3id: %x\n",frm->dinfo); +@@ -1698,15 +1738,15 @@ + } + } + else { +- if (stack->nt) ++ if (stack->nt) + cb_log(4, stack->port, "BC with dinfo: %x not found.. (prim was %x and addr %x)\n",frm->dinfo, frm->prim, frm->addr); + } +- ++ + return 1; + } + break; + } +- ++ + return 0; + } + +@@ -1720,10 +1760,10 @@ + cb_log(1,0,"misdn_release: No Stack found\n"); + return; + } +- +- if (bc->channel>0) ++ ++ if (bc->channel>0) + empty_chan_in_stack(stack,bc->channel); +- ++ + empty_bc(bc); + clean_up_bc(bc); + bc->in_use=0; +@@ -1732,21 +1772,21 @@ + + + +-int misdn_lib_get_port_up (int port) +-{ /* Pull Up L1 */ ++int misdn_lib_get_port_up (int port) ++{ /* Pull Up L1 */ + struct misdn_stack *stack; +- ++ + for (stack=glob_mgr->stack_list; + stack; + stack=stack->next) { +- ++ + if (stack->port == port) { + + if (!stack->l1link) + misdn_lib_get_l1_up(stack); + if (!stack->l2link) + misdn_lib_get_l2_up(stack); +- ++ + return 0; + } + } +@@ -1754,8 +1794,8 @@ + } + + +-int misdn_lib_get_port_down (int port) +-{ /* Pull Down L1 */ ++int misdn_lib_get_port_down (int port) ++{ /* Pull Down L1 */ + struct misdn_stack *stack; + for (stack=glob_mgr->stack_list; + stack; +@@ -1778,7 +1818,7 @@ + for (stack=glob_mgr->stack_list; + stack; + stack=stack->next) { +- ++ + if (stack->port == port) { + + if (stack->blocked) { +@@ -1805,7 +1845,7 @@ + } + } + } +- ++ + return -1; + } + +@@ -1825,7 +1865,7 @@ + if (!bc) { + cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", hh->dinfo); + misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); +- bc=&dummybc; ++ bc=&dummybc; + } + + if (bc) { +@@ -1858,7 +1898,7 @@ + + hh=(mISDNuser_head_t*)msg->data; + port=stack->port; +- ++ + cb_log(5, stack->port, " --> lib: prim %x dinfo %x\n",hh->prim, hh->dinfo); + { + switch(hh->prim){ +@@ -1873,7 +1913,7 @@ + frm.addr=stack->upper_id | FLG_MSG_DOWN; + + frm.prim = CC_NEW_CR|INDICATION; +- ++ + if (handle_cr( stack, &frm)< 0) { + msg_t *dmsg; + cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); +@@ -1882,7 +1922,7 @@ + free_msg(msg); + return 0; + } +- ++ + bc = find_bc_by_l3id(stack, hh->dinfo); + hold_bc = stack_holder_find(stack, bc->l3_id); + cb_log(4, stack->port, "bc_l3id:%x holded_bc_l3id:%x\n",bc->l3_id, hold_bc->l3_id); +@@ -1898,17 +1938,17 @@ + bc->holded=0; + bc->b_stid=0; + } +- ++ + } +- ++ + break; +- ++ + case CC_SETUP|CONFIRM: + { + struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); + int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE)); + cb_log(4, stack->port, " --> lib: Event_ind:SETUP CONFIRM [NT] : new L3ID is %x\n",l3id ); +- ++ + if (!bc) { cb_log(4, stack->port, "Bc Not found (after SETUP CONFIRM)\n"); return 0; } + cb_log (2,bc->port,"I IND :CC_SETUP|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); + bc->l3_id=l3id; +@@ -1916,11 +1956,11 @@ + } + free_msg(msg); + return 0; +- ++ + case CC_SETUP|INDICATION: + { + struct misdn_bchannel* bc=misdn_lib_get_free_bc(stack->port, 0, 1, 0); +- if (!bc) ++ if (!bc) + ERR_NO_CHANNEL: + { + msg_t *dmsg; +@@ -1930,7 +1970,7 @@ + free_msg(msg); + return 0; + } +- ++ + cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); + bc->l3_id=hh->dinfo; + } +@@ -1938,11 +1978,11 @@ + + case CC_CONNECT_ACKNOWLEDGE|INDICATION: + break; +- ++ + case CC_ALERTING|INDICATION: + case CC_PROCEEDING|INDICATION: + case CC_SETUP_ACKNOWLEDGE|INDICATION: +- if(!stack->ptp) break; ++ if(!stack->ptp) break; + case CC_CONNECT|INDICATION: + break; + case CC_DISCONNECT|INDICATION: +@@ -1950,22 +1990,22 @@ + struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); +- if (bc) { ++ if (bc) { + int myprocid=bc->l3_id&0x0000ffff; + hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; + cb_log(3,stack->port,"Reject dinfo: %x cause:%d\n",hh->dinfo,bc->cause); +- reject=1; ++ reject=1; + } + } + } + break; +- ++ + case CC_FACILITY|INDICATION: + { + struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); +- if (bc) { ++ if (bc) { + int myprocid=bc->l3_id&0x0000ffff; + hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; + cb_log(4,bc->port,"Repaired reject Bug, new dinfo: %x\n",hh->dinfo); +@@ -1973,7 +2013,7 @@ + } + } + break; +- ++ + case CC_RELEASE_COMPLETE|INDICATION: + break; + +@@ -1994,13 +2034,13 @@ + { + struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); + +- if (bc) { ++ if (bc) { + cb_log(1, stack->port, "CC_RELEASE|CONFIRM (l3id:%x), sending RELEASE_COMPLETE\n", hh->dinfo); + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + } + } + break; +- ++ + case CC_RELEASE|INDICATION: + break; + +@@ -2009,7 +2049,7 @@ + free_msg(msg); + return 0 ; + break; +- ++ + case CC_NEW_CR|INDICATION: + /* Got New CR for bchan, for now I handle this one in */ + /* connect_ack, Need to be changed */ +@@ -2022,36 +2062,36 @@ + stack->procids[bc->l3_id&0xff] = 0 ; + } + cb_log(4, stack->port, "lib: Event_ind:CC_NEW_CR : very new L3ID is %x\n",l3id ); +- ++ + bc->l3_id =l3id; + cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); +- ++ + free_msg(msg); + return 0; + } +- ++ + case DL_ESTABLISH | INDICATION: + case DL_ESTABLISH | CONFIRM: + { + cb_log(3, stack->port, "%% GOT L2 Activate Info.\n"); +- ++ + if (stack->ptp && stack->l2link) { + cb_log(0, stack->port, "%% GOT L2 Activate Info. but we're activated already.. this l2 is faulty, blocking port\n"); + cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); + } + + if (stack->ptp && !stack->restart_sent) { +- /* make sure we restart the interface of the ++ /* make sure we restart the interface of the + * other side */ + stack->restart_sent=1; + misdn_lib_send_restart(stack->port, -1); + + } +- ++ + /* when we get the L2 UP, the L1 is UP definitely too*/ + stack->l2link = 1; + stack->l2upcnt=0; +- ++ + free_msg(msg); + return 0; + } +@@ -2075,10 +2115,10 @@ + stack->l2upcnt++; + } + } +- +- } else ++ ++ } else + cb_log(3, stack->port, "%% GOT L2 DeActivate Info.\n"); +- ++ + stack->l2link = 0; + free_msg(msg); + return 0; +@@ -2086,37 +2126,37 @@ + break; + } + } +- ++ + { + /* Parse Events and fire_up to App. */ + struct misdn_bchannel *bc; + struct misdn_bchannel dummybc; +- ++ + enum event_e event = isdn_msg_get_event(msgs_g, msg, 1); +- ++ + bc=find_bc_by_l3id(stack, hh->dinfo); +- ++ + if (!bc) { + cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x).\n", hh->dinfo); + misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); +- bc=&dummybc; ++ bc=&dummybc; + } + if (bc ) { + isdn_msg_parse_event(msgs_g,msg,bc, 1); + + switch (event) { + case EVENT_SETUP: +- if (bc->channel<=0 || bc->channel==0xff) ++ if (bc->channel<=0 || bc->channel==0xff) + bc->channel=0; +- +- if (find_free_chan_in_stack(stack,bc, bc->channel,0)<0) ++ ++ if (find_free_chan_in_stack(stack,bc, bc->channel,0)<0) + goto ERR_NO_CHANNEL; + break; + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: + { + int channel=bc->channel; +- int tmpcause=bc->cause; ++ int tmpcause=bc->cause; + empty_bc(bc); + bc->cause=tmpcause; + clean_up_bc(bc); +@@ -2130,7 +2170,7 @@ + default: + break; + } +- ++ + if(!isdn_get_info(msgs_g,event,1)) { + cb_log(4, stack->port, "Unknown Event Ind: prim %x dinfo %x\n",hh->prim, hh->dinfo); + } else { +@@ -2161,8 +2201,8 @@ + static int handle_timers(msg_t* msg) + { + iframe_t *frm= (iframe_t*)msg->data; +- struct misdn_stack *stack; +- ++ struct misdn_stack *stack; ++ + /* Timer Stuff */ + switch (frm->prim) { + case MGR_INITTIMER | CONFIRM: +@@ -2172,17 +2212,17 @@ + free_msg(msg); + return(1); + } +- +- +- ++ ++ ++ + if (frm->prim==(MGR_TIMER | INDICATION) ) { + for (stack = glob_mgr->stack_list; + stack; + stack = stack->next) { + itimer_t *it; +- ++ + if (!stack->nt) continue; +- ++ + it = stack->nst.tlist; + /* find timer */ + for(it=stack->nst.tlist; +@@ -2201,12 +2241,12 @@ + return 1; + } + } +- ++ + cb_log(0, 0, "Timer Msg without Timer ??\n"); + free_msg(msg); + return 1; + } +- ++ + return 0; + } + +@@ -2226,23 +2266,23 @@ + static int do_tone(struct misdn_bchannel *bc, int len) + { + bc->tone_cnt=len; +- ++ + if (bc->generate_tone) { + cb_event(EVENT_TONE_GENERATE, bc, glob_mgr->user_data); +- ++ + if ( !bc->nojitter ) { + misdn_tx_jitter(bc,len); + } +- ++ + return 1; + } +- ++ + return 0; + } + + + #ifdef MISDN_SAVE_DATA +-static void misdn_save_data(int id, char *p1, int l1, char *p2, int l2) ++static void misdn_save_data(int id, char *p1, int l1, char *p2, int l2) + { + char n1[32],n2[32]; + FILE *rx, *tx; +@@ -2250,17 +2290,17 @@ + sprintf(n1,"/tmp/misdn-rx-%d.raw",id); + sprintf(n2,"/tmp/misdn-tx-%d.raw",id); + +- rx = fopen(n1,"a+"); ++ rx = fopen(n1,"a+"); + tx = fopen(n2,"a+"); + + if (!rx || !tx) { + cb_log(0,0,"Couldn't open files: %s\n",strerror(errno)); + return ; + } +- ++ + fwrite(p1,1,l1,rx); + fwrite(p2,1,l2,tx); +- ++ + fclose(rx); + fclose(tx); + +@@ -2273,25 +2313,25 @@ + char *data=&buf[mISDN_HEADER_LEN]; + iframe_t *txfrm= (iframe_t*)buf; + int jlen, r; +- ++ + jlen=cb_jb_empty(bc,data,len); +- ++ + if (jlen) { + #ifdef MISDN_SAVE_DATA + misdn_save_data((bc->port*100+bc->channel), data, jlen, bc->bframe, bc->bframe_len); + #endif + flip_buf_bits( data, jlen); +- ++ + if (jlen < len) { + cb_log(7,bc->port,"Jitterbuffer Underrun.\n"); + } +- ++ + txfrm->prim = DL_DATA|REQUEST; +- ++ + txfrm->dinfo = 0; +- ++ + txfrm->addr = bc->addr|FLG_MSG_DOWN; /* | IF_DOWN; */ +- ++ + txfrm->len =jlen; + cb_log(9, bc->port, "Transmitting %d samples 2 misdn\n", txfrm->len); + +@@ -2332,25 +2372,25 @@ + iframe_t *frm= (iframe_t*)msg->data; + struct misdn_bchannel *bc=find_bc_by_addr(frm->addr); + struct misdn_stack *stack; +- ++ + if (!bc) { + cb_log(1,0,"handle_bchan: BC not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo); + return 0 ; + } +- ++ + stack = get_stack_by_bc(bc); +- ++ + if (!stack) { + cb_log(0, bc->port,"handle_bchan: STACK not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo); + return 0; + } +- ++ + switch (frm->prim) { + + case MGR_SETSTACK| CONFIRM: + cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|CONFIRM pid:%d\n",bc->pid); + break; +- ++ + case MGR_SETSTACK| INDICATION: + cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|IND pid:%d\n",bc->pid); + break; +@@ -2363,9 +2403,9 @@ + usleep(1000); + goto AGAIN; + } +- ++ + cb_log(0,stack->port,"$$$ Get Layer (%d) Id Error: %s\n",bc->layer,strerror(errno)); +- ++ + /* we kill the channel later, when we received some + data. */ + bc->addr= frm->addr; +@@ -2373,12 +2413,12 @@ + cb_log(0, stack->port,"$$$ bc->addr <0 Error:%s\n",strerror(errno)); + bc->addr=0; + } +- ++ + cb_log(4, stack->port," --> Got Adr %x\n", bc->addr); + + free_msg(msg); +- +- ++ ++ + switch(bc->bc_state) { + case BCHAN_SETUP: + bc_state_change(bc,BCHAN_SETUPED); +@@ -2395,31 +2435,31 @@ + case MGR_DELLAYER| INDICATION: + cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|IND pid:%d\n",bc->pid); + break; +- ++ + case MGR_DELLAYER| CONFIRM: + cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|CNF pid:%d\n",bc->pid); +- ++ + bc->pid=0; + bc->addr=0; +- ++ + free_msg(msg); + return 1; +- ++ + case PH_ACTIVATE | INDICATION: + case DL_ESTABLISH | INDICATION: + cb_log(3, stack->port, "BCHAN: ACT Ind pid:%d\n", bc->pid); + + free_msg(msg); +- return 1; ++ return 1; + + case PH_ACTIVATE | CONFIRM: + case DL_ESTABLISH | CONFIRM: +- ++ + cb_log(3, stack->port, "BCHAN: bchan ACT Confirm pid:%d\n",bc->pid); + free_msg(msg); +- +- return 1; + ++ return 1; ++ + case DL_ESTABLISH | REQUEST: + { + char buf[128]; +@@ -2435,33 +2475,40 @@ + } + free_msg(msg); + return 1; +- ++ + case PH_DEACTIVATE | INDICATION: + case DL_RELEASE | INDICATION: + cb_log (3, stack->port, "BCHAN: DeACT Ind pid:%d\n",bc->pid); +- ++ + free_msg(msg); + return 1; +- ++ + case PH_DEACTIVATE | CONFIRM: + case DL_RELEASE | CONFIRM: + cb_log(3, stack->port, "BCHAN: DeACT Conf pid:%d\n",bc->pid); +- ++ + free_msg(msg); + return 1; +- ++ + case PH_CONTROL|INDICATION: + { + unsigned int *cont = (unsigned int *) &frm->data.p; +- +- cb_log(4, stack->port, "PH_CONTROL: channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); + ++ cb_log(4, stack->port, ++ "PH_CONTROL: channel:%d caller%d:\"%s\" <%s> dialed%d:%s \n", ++ bc->channel, ++ bc->caller.number_type, ++ bc->caller.name, ++ bc->caller.number, ++ bc->dialed.number_type, ++ bc->dialed.number); ++ + if ((*cont & ~DTMF_TONE_MASK) == DTMF_TONE_VAL) { + int dtmf = *cont & DTMF_TONE_MASK; + cb_log(4, stack->port, " --> DTMF TONE: %c\n",dtmf); + bc->dtmf=dtmf; + cb_event(EVENT_DTMF_TONE, bc, glob_mgr->user_data); +- ++ + free_msg(msg); + return 1; + } +@@ -2482,11 +2529,11 @@ + case DL_DATA|REQUEST: + cb_log(0, stack->port, "DL_DATA REQUEST \n"); + do_tone(bc, 64); +- ++ + free_msg(msg); + return 1; +- +- ++ ++ + case PH_DATA|INDICATION: + case DL_DATA|INDICATION: + { +@@ -2494,10 +2541,10 @@ + bc->bframe_len = frm->len; + + /** Anyway flip the bufbits **/ +- if ( misdn_cap_is_speech(bc->capability) ) ++ if ( misdn_cap_is_speech(bc->capability) ) + flip_buf_bits(bc->bframe, bc->bframe_len); +- + ++ + if (!bc->bframe_len) { + cb_log(2, stack->port, "DL_DATA INDICATION bc->addr:%x frm->addr:%x\n", bc->addr, frm->addr); + free_msg(msg); +@@ -2509,12 +2556,12 @@ + free_msg(msg); + return 1; + } +- ++ + #if MISDN_DEBUG + cb_log(0, stack->port, "DL_DATA INDICATION Len %d\n", frm->len); + + #endif +- ++ + if ( (bc->bc_state == BCHAN_ACTIVATED) && frm->len > 0) { + int t; + +@@ -2528,7 +2575,7 @@ + #endif + if ( !t ) { + int i; +- ++ + if ( misdn_cap_is_speech(bc->capability)) { + if ( !bc->nojitter ) { + #ifdef MISDN_B_DEBUG +@@ -2541,15 +2588,15 @@ + } + } + +-#ifdef MISDN_B_DEBUG ++#ifdef MISDN_B_DEBUG + cb_log(0,bc->port,"EVENT_B_DATA START\n"); + #endif +- ++ + i = cb_event(EVENT_BCHAN_DATA, bc, glob_mgr->user_data); +-#ifdef MISDN_B_DEBUG ++#ifdef MISDN_B_DEBUG + cb_log(0,bc->port,"EVENT_B_DATA STOP\n"); + #endif +- ++ + if (i<0) { + cb_log(10,stack->port,"cb_event returned <0\n"); + /*clean_up_bc(bc);*/ +@@ -2582,7 +2629,7 @@ + #endif + break; + } +- ++ + return 0; + } + +@@ -2596,29 +2643,29 @@ + + stack=find_stack_by_addr( frm->addr ); + +- +- ++ ++ + if (!stack || !stack->nt) { + return 0; + } + +- ++ + if ((err=stack->nst.l1_l2(&stack->nst,msg))) { +- ++ + if (nt_err_cnt > 0 ) { + if (nt_err_cnt < 100) { +- nt_err_cnt++; ++ nt_err_cnt++; + cb_log(0, stack->port, "NT Stack sends us error: %d \n", err); + } else if (nt_err_cnt < 105){ + cb_log(0, stack->port, "NT Stack sends us error: %d over 100 times, so I'll stop this message\n", err); +- nt_err_cnt = - 1; ++ nt_err_cnt = - 1; + } + } + free_msg(msg); + return 1; +- ++ + } +- ++ + return 1; + } + +@@ -2626,13 +2673,13 @@ + static int handle_frm(msg_t *msg) + { + iframe_t *frm = (iframe_t*) msg->data; +- ++ + struct misdn_stack *stack=find_stack_by_addr(frm->addr); + + if (!stack || stack->nt) { + return 0; + } +- ++ + cb_log(4,stack?stack->port:0,"handle_frm: frm->addr:%x frm->prim:%x\n",frm->addr,frm->prim); + + { +@@ -2650,7 +2697,7 @@ + free_msg(msg); + return 1; + } +- ++ + bc=find_bc_by_l3id(stack, frm->dinfo); + + if (!bc && (frm->prim==(CC_RESTART|CONFIRM)) ) { +@@ -2670,15 +2717,15 @@ + return 1; + } + +- ++ + handle_frm_bc: + if (bc ) { + enum event_e event = isdn_msg_get_event(msgs_g, msg, 0); + enum event_response_e response=RESPONSE_OK; + int ret; +- ++ + isdn_msg_parse_event(msgs_g,msg,bc, 0); +- ++ + /** Preprocess some Events **/ + ret = handle_event(bc, event, frm); + if (ret<0) { +@@ -2688,8 +2735,8 @@ + } + /* shoot up event to App: */ + cb_log(5, stack->port, "lib Got Prim: Addr %x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); +- +- if(!isdn_get_info(msgs_g,event,0)) ++ ++ if(!isdn_get_info(msgs_g,event,0)) + cb_log(0, stack->port, "Unknown Event Ind: Addr:%x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); + else + response=cb_event(event, bc, glob_mgr->user_data); +@@ -2698,13 +2745,13 @@ + switch (response) { + case RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE: + +- cb_log(0, stack->port, "TOTALLY IGNORING SETUP\n"); +- ++ cb_log(0, stack->port, "TOTALLY IGNORING SETUP\n"); ++ + break; + case RESPONSE_IGNORE_SETUP: + /* I think we should send CC_RELEASE_CR, but am not sure*/ + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; +- ++ + case RESPONSE_RELEASE_SETUP: + misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); + if (bc->channel>0) +@@ -2718,7 +2765,7 @@ + case RESPONSE_OK: + cb_log(4, stack->port, "GOT SETUP OK\n"); + +- ++ + break; + default: + break; +@@ -2728,13 +2775,13 @@ + if (event == EVENT_RELEASE_COMPLETE) { + /* release bchannel only after we've announced the RELEASE_COMPLETE */ + int channel=bc->channel; +- int tmpcause=bc->cause; +- int tmp_out_cause=bc->out_cause; ++ int tmpcause=bc->cause; ++ int tmp_out_cause=bc->out_cause; + empty_bc(bc); + bc->cause=tmpcause; + bc->out_cause=tmp_out_cause; + clean_up_bc(bc); +- ++ + if (tmpcause == AST_CAUSE_REQUESTED_CHAN_UNAVAIL) { + cb_log(0,stack->port,"**** Received CAUSE:%d, so not cleaning up channel %d\n", AST_CAUSE_REQUESTED_CHAN_UNAVAIL, channel); + cb_log(0,stack->port,"**** This channel is now no longer available,\nplease try to restart it with 'misdn send restart <port> <channel>'\n"); +@@ -2755,11 +2802,11 @@ + + cb_log(5, stack->port, "Freeing Msg on prim:%x \n",frm->prim); + +- ++ + free_msg(msg); + return 1; + #endif +- ++ + } else { + struct misdn_bchannel dummybc; + if (frm->prim!=(CC_FACILITY|INDICATION)) +@@ -2770,7 +2817,7 @@ + memset (&dummybc,0,sizeof(dummybc)); + dummybc.port=stack->port; + dummybc.l3_id=frm->dinfo; +- bc=&dummybc; ++ bc=&dummybc; + goto handle_frm_bc; + } + } +@@ -2785,7 +2832,7 @@ + iframe_t *frm = (iframe_t*) msg->data; + struct misdn_stack *stack = find_stack_by_addr(frm->addr); + int i ; +- ++ + if (!stack) return 0 ; + + switch (frm->prim) { +@@ -2793,9 +2840,9 @@ + case PH_ACTIVATE | INDICATION: + cb_log (3, stack->port, "L1: PH L1Link Up!\n"); + stack->l1link=1; +- ++ + if (stack->nt) { +- ++ + if (stack->nst.l1_l2(&stack->nst, msg)) + free_msg(msg); + +@@ -2804,14 +2851,14 @@ + } else { + free_msg(msg); + } +- ++ + for (i=0;i<=stack->b_num; i++) { + if (stack->bc[i].evq != EVENT_NOTHING) { + cb_log(4, stack->port, "Firing Queued Event %s because L1 got up\n", isdn_get_info(msgs_g, stack->bc[i].evq, 0)); + misdn_lib_send_event(&stack->bc[i],stack->bc[i].evq); + stack->bc[i].evq=EVENT_NOTHING; + } +- ++ + } + return 1; + +@@ -2819,16 +2866,16 @@ + free_msg(msg); + cb_log(3,stack->port,"L1: PH_ACTIVATE|REQUEST \n"); + return 1; +- ++ + case PH_DEACTIVATE | REQUEST: + free_msg(msg); + cb_log(3,stack->port,"L1: PH_DEACTIVATE|REQUEST \n"); + return 1; +- ++ + case PH_DEACTIVATE | CONFIRM: + case PH_DEACTIVATE | INDICATION: + cb_log (3, stack->port, "L1: PH L1Link Down! \n"); +- ++ + #if 0 + for (i=0; i<=stack->b_num; i++) { + if (global_state == MISDN_INITIALIZED) { +@@ -2836,19 +2883,19 @@ + } + } + #endif +- ++ + if (stack->nt) { + if (stack->nst.l1_l2(&stack->nst, msg)) + free_msg(msg); + } else { + free_msg(msg); + } +- ++ + stack->l1link=0; + stack->l2link=0; + return 1; + } +- ++ + return 0; + } + +@@ -2857,11 +2904,11 @@ + iframe_t *frm = (iframe_t*) msg->data; + + struct misdn_stack *stack = find_stack_by_addr(frm->addr); +- ++ + if (!stack) { + return 0 ; + } +- ++ + switch(frm->prim) { + + case DL_ESTABLISH | REQUEST: +@@ -2870,13 +2917,13 @@ + case DL_RELEASE | REQUEST: + cb_log(1,stack->port,"DL_RELEASE|REQUEST \n"); + return 1; +- ++ + case DL_ESTABLISH | INDICATION: + case DL_ESTABLISH | CONFIRM: + { + cb_log (3, stack->port, "L2: L2Link Up! \n"); + if (stack->ptp && stack->l2link) { +- cb_log (-1, stack->port, "L2: L2Link Up! but it's already UP.. must be faulty, blocking port\n"); ++ cb_log (-1, stack->port, "L2: L2Link Up! but it's already UP.. must be faulty, blocking port\n"); + cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); + } + stack->l2link=1; +@@ -2884,13 +2931,13 @@ + return 1; + } + break; +- ++ + case DL_RELEASE | INDICATION: + case DL_RELEASE | CONFIRM: + { + cb_log (3, stack->port, "L2: L2Link Down! \n"); + stack->l2link=0; +- ++ + free_msg(msg); + return 1; + } +@@ -2909,9 +2956,9 @@ + free_msg(msg); + return 1; + } +- ++ + stack = find_stack_by_addr(frm->addr); +- ++ + if (!stack) { + if (frm->prim == (MGR_DELLAYER|CONFIRM)) { + cb_log(2, 0, "MGMT: DELLAYER|CONFIRM Addr: %x !\n", +@@ -2919,20 +2966,20 @@ + free_msg(msg); + return 1; + } +- ++ + return 0; + } +- ++ + switch(frm->prim) { + case MGR_SHORTSTATUS | INDICATION: + case MGR_SHORTSTATUS | CONFIRM: + cb_log(5, 0, "MGMT: Short status dinfo %x\n",frm->dinfo); +- ++ + switch (frm->dinfo) { + case SSTATUS_L1_ACTIVATED: + cb_log(3, 0, "MGMT: SSTATUS: L1_ACTIVATED \n"); + stack->l1link=1; +- ++ + break; + case SSTATUS_L1_DEACTIVATED: + cb_log(3, 0, "MGMT: SSTATUS: L1_DEACTIVATED \n"); +@@ -2946,16 +2993,16 @@ + cb_log(3, stack->port, "MGMT: SSTATUS: L2_ESTABLISH \n"); + stack->l2link=1; + break; +- ++ + case SSTATUS_L2_RELEASED: + cb_log(3, stack->port, "MGMT: SSTATUS: L2_RELEASED \n"); + stack->l2link=0; + break; + } +- ++ + free_msg(msg); + return 1; +- ++ + case MGR_SETSTACK | INDICATION: + cb_log(4, stack->port, "MGMT: SETSTACK|IND dinfo %x\n",frm->dinfo); + free_msg(msg); +@@ -2964,21 +3011,21 @@ + cb_log(4, stack->port, "MGMT: DELLAYER|CNF dinfo %x\n",frm->dinfo) ; + free_msg(msg); + return 1; +- ++ + } +- ++ + /* + if ( (frm->prim & 0x0f0000) == 0x0f0000) { + cb_log(5, 0, "$$$ MGMT FRAME: prim %x addr %x dinfo %x\n",frm->prim, frm->addr, frm->dinfo) ; + free_msg(msg); + return 1; + } */ +- ++ + return 0; + } + + +-static msg_t *fetch_msg(int midev) ++static msg_t *fetch_msg(int midev) + { + msg_t *msg=alloc_msg(MAX_MSG_SIZE); + int r; +@@ -2991,7 +3038,7 @@ + AGAIN: + r=mISDN_read(midev,msg->data,MAX_MSG_SIZE, TIMEOUT_10SEC); + msg->len=r; +- ++ + if (r==0) { + free_msg(msg); /* danger, cause usually freeing in main_loop */ + cb_log(6,0,"Got empty Msg..\n"); +@@ -3005,8 +3052,8 @@ + usleep(5000); + goto AGAIN; + } +- +- cb_log(0,0,"mISDN_read returned :%d error:%s (%d)\n",r,strerror(errno),errno); ++ ++ cb_log(0,0,"mISDN_read returned :%d error:%s (%d)\n",r,strerror(errno),errno); + } + + #if 0 +@@ -3024,12 +3071,12 @@ + ; + + if (stack) { +- cb_log(4, port, "Checking L1 State\n"); ++ cb_log(4, port, "Checking L1 State\n"); + if (!stack->l1link) { +- cb_log(4, port, "L1 State Down, trying to get it up again\n"); ++ cb_log(4, port, "L1 State Down, trying to get it up again\n"); + misdn_lib_get_short_status(stack); +- misdn_lib_get_l1_up(stack); +- misdn_lib_get_l2_up(stack); ++ misdn_lib_get_l1_up(stack); ++ misdn_lib_get_l2_up(stack); + } + } + } +@@ -3041,19 +3088,19 @@ + int zero_frm=0 , fff_frm=0 ; + int midev= mgr->midev; + int port=0; +- ++ + while (1) { +- msg_t *msg = fetch_msg(midev); ++ msg_t *msg = fetch_msg(midev); + iframe_t *frm; +- +- ++ ++ + if (!msg) continue; +- ++ + frm = (iframe_t*) msg->data; +- ++ + /** When we make a call from NT2Ast we get these frames **/ + if (frm->len == 0 && frm->addr == 0 && frm->dinfo == 0 && frm->prim == 0 ) { +- zero_frm++; ++ zero_frm++; + free_msg(msg); + continue; + } else { +@@ -3062,10 +3109,10 @@ + zero_frm = 0 ; + } + } +- ++ + /** I get this sometimes after setup_bc **/ + if (frm->len == 0 && frm->dinfo == 0 && frm->prim == 0xffffffff ) { +- fff_frm++; ++ fff_frm++; + free_msg(msg); + continue; + } else { +@@ -3074,7 +3121,7 @@ + fff_frm = 0 ; + } + } +- ++ + manager_isdn_handler(frm, msg); + } + +@@ -3090,24 +3137,24 @@ + int ret; + + if (midev<=0) return midev; +- ++ + /* create entity for layer 3 TE-mode */ + mISDN_write_frame(midev, buff, 0, MGR_NEWENTITY | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + ret = mISDN_read_frame(midev, frm, sizeof(iframe_t), 0, MGR_NEWENTITY | CONFIRM, TIMEOUT_1SEC); +- ++ + if (ret < mISDN_HEADER_LEN) { + noentity: + fprintf(stderr, "cannot request MGR_NEWENTITY from mISDN: %s\n",strerror(errno)); + exit(-1); + } +- ++ + entity = frm->dinfo & 0xffff ; +- ++ + if (!entity) + goto noentity; + + return midev; +- ++ + } + + void te_lib_destroy(int midev) +@@ -3131,14 +3178,14 @@ + { + struct misdn_stack *stack; + int i; +- ++ + for (stack=glob_mgr->stack_list; + stack; + stack=stack->next) { + for (i=0; i<=stack->b_num; i++) + if (stack->bc[i].pid == pid) return &stack->bc[i]; + } +- ++ + return NULL; + } + +@@ -3159,12 +3206,12 @@ + cb_log(2,bc->port, "channel with stid:%x for one second still in use! (n:%d lu:%d)\n", bc->b_stid, (int) now.tv_sec, (int) bc->last_used.tv_sec); + return 1; + } +- + ++ + cb_log(3,bc->port, "channel with stid:%x not in use!\n", bc->b_stid); + return 0; + } +- ++ + cb_log(2,bc->port, "channel with stid:%x in use!\n", bc->b_stid); + return 1; + } +@@ -3194,7 +3241,7 @@ + { + struct misdn_stack *stack; + int i; +- ++ + if (channel < 0 || channel > MAX_BCHANS) { + cb_log(0,port,"Requested channel out of bounds (%d)\n",channel); + return NULL; +@@ -3203,7 +3250,7 @@ + usleep(1000); + + for (stack=glob_mgr->stack_list; stack; stack=stack->next) { +- ++ + if (stack->port == port) { + int maxnum; + +@@ -3211,12 +3258,12 @@ + cb_log(0,port,"Port is blocked\n"); + return NULL; + } +- ++ + if (channel > 0) { + if (channel <= stack->b_num) { + for (i = 0; i < stack->b_num; i++) { + if ( stack->bc[i].channel == channel) { +- if (test_inuse(&stack->bc[i])) { ++ if (test_inuse(&stack->bc[i])) { + cb_log(0,port,"Requested channel:%d on port:%d is already in use\n",channel, port); + return NULL; + +@@ -3240,7 +3287,7 @@ + /* 3. channel on bri means CW*/ + if (!stack->pri && i==stack->b_num) + stack->bc[i].cw=1; +- ++ + prepare_bc(&stack->bc[i], channel); + stack->bc[i].dec=1; + return &stack->bc[i]; +@@ -3268,10 +3315,6 @@ + return NULL; + } + +- +- +- +-/* ******************************************************************* */ + /*! + * \internal + * \brief Convert the facility function enum value into a string. +@@ -3280,36 +3323,30 @@ + */ + static const char *fac2str(enum FacFunction facility) + { +- static const struct { +- enum FacFunction facility; ++ static const struct { ++ enum FacFunction facility; + char *name; + } arr[] = { + /* *INDENT-OFF* */ + { Fac_None, "Fac_None" }, +- { Fac_GetSupportedServices, "Fac_GetSupportedServices" }, +- { Fac_Listen, "Fac_Listen" }, +- { Fac_Suspend, "Fac_Suspend" }, +- { Fac_Resume, "Fac_Resume" }, + { Fac_CFActivate, "Fac_CFActivate" }, + { Fac_CFDeactivate, "Fac_CFDeactivate" }, +- { Fac_CFInterrogateParameters, "Fac_CFInterrogateParameters" }, +- { Fac_CFInterrogateNumbers, "Fac_CFInterrogateNumbers" }, + { Fac_CD, "Fac_CD" }, + { Fac_AOCDCurrency, "Fac_AOCDCurrency" }, + { Fac_AOCDChargingUnit, "Fac_AOCDChargingUnit" }, + /* *INDENT-ON* */ + }; +- ++ + unsigned index; +- ++ + for (index = 0; index < ARRAY_LEN(arr); ++index) { + if (arr[index].facility == facility) { + return arr[index].name; + } +- } /* end for */ ++ } + + return "unknown"; +-} /* end fac2str() */ ++} + + void misdn_lib_log_ies(struct misdn_bchannel *bc) + { +@@ -3321,27 +3358,57 @@ + + if (!stack) return; + +- cb_log(2, stack->port, " --> channel:%d mode:%s cause:%d ocause:%d rad:%s cad:%s\n", bc->channel, stack->nt?"NT":"TE", bc->cause, bc->out_cause, bc->rad, bc->cad); +- + cb_log(2, stack->port, +- " --> info_dad:%s onumplan:%c dnumplan:%c rnumplan:%c cpnnumplan:%c\n", +- bc->info_dad, +- bc->onumplan>=0?'0'+bc->onumplan:' ', +- bc->dnumplan>=0?'0'+bc->dnumplan:' ', +- bc->rnumplan>=0?'0'+bc->rnumplan:' ', +- bc->cpnnumplan>=0?'0'+bc->cpnnumplan:' ' +- ); +- ++ " --> channel:%d mode:%s cause:%d ocause:%d\n", ++ bc->channel, ++ stack->nt ? "NT" : "TE", ++ bc->cause, ++ bc->out_cause); ++ ++ cb_log(2, stack->port, ++ " --> info_dad:%s dialed numtype:%d plan:%d\n", ++ bc->info_dad, ++ bc->dialed.number_type, ++ bc->dialed.number_plan); ++ ++ cb_log(2, stack->port, ++ " --> caller:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", ++ bc->caller.name, ++ bc->caller.number, ++ bc->caller.number_type, ++ bc->caller.number_plan, ++ bc->caller.presentation, ++ bc->caller.screening); ++ ++ cb_log(2, stack->port, ++ " --> redirecting:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d reason:%d\n", ++ bc->redirecting.from.name, ++ bc->redirecting.from.number, ++ bc->redirecting.from.number_type, ++ bc->redirecting.from.number_plan, ++ bc->redirecting.from.presentation, ++ bc->redirecting.from.screening, ++ bc->redirecting.reason); ++ ++ cb_log(2, stack->port, ++ " --> connected:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", ++ bc->connected.name, ++ bc->connected.number, ++ bc->connected.number_type, ++ bc->connected.number_plan, ++ bc->connected.presentation, ++ bc->connected.screening); ++ + cb_log(3, stack->port, " --> caps:%s pi:%x keypad:%s sending_complete:%d\n", bearer2str(bc->capability),bc->progress_indicator, bc->keypad, bc->sending_complete); +- cb_log(4, stack->port, " --> screen:%d --> pres:%d\n", +- bc->screen, bc->pres); +- ++ ++ cb_log(4, stack->port, " --> set_pres:%d pres:%d\n", bc->set_presentation, bc->presentation); ++ + cb_log(4, stack->port, " --> addr:%x l3id:%x b_stid:%x layer_id:%x\n", bc->addr, bc->l3_id, bc->b_stid, bc->layer_id); +- ++ + cb_log(4, stack->port, " --> facility:%s out_facility:%s\n",fac2str(bc->fac_in.Function),fac2str(bc->fac_out.Function)); + + cb_log(5, stack->port, " --> urate:%d rate:%d mode:%d user1:%d\n", bc->urate, bc->rate, bc->mode,bc->user1); +- ++ + cb_log(5, stack->port, " --> bc:%p h:%d sh:%d\n", bc, bc->holded, bc->stack_holder); + } + +@@ -3364,19 +3431,24 @@ + + int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) + { +- msg_t *msg; ++ msg_t *msg; + int retval=0; + struct misdn_stack *stack; +- ++ + if (!bc) RETURN(-1,OUT_POST_UNLOCK); +- ++ + stack = get_stack_by_bc(bc); +- ++ + if (!stack) { +- cb_log(0,bc->port,"SENDEVENT: no Stack for event:%s oad:%s dad:%s \n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad); ++ cb_log(0,bc->port, ++ "SENDEVENT: no Stack for event:%s caller:\"%s\" <%s> dialed:%s \n", ++ isdn_get_info(msgs_g, event, 0), ++ bc->caller.name, ++ bc->caller.number, ++ bc->dialed.number); + RETURN(-1,OUT); + } +- ++ + misdn_send_lock(bc); + + +@@ -3389,11 +3461,17 @@ + misdn_lib_get_l1_up(stack); + RETURN(0,OUT); + } +- +- cb_log(1, stack->port, "I SEND:%s oad:%s dad:%s pid:%d\n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad, bc->pid); ++ ++ cb_log(1, stack->port, ++ "I SEND:%s caller:\"%s\" <%s> dialed:%s pid:%d\n", ++ isdn_get_info(msgs_g, event, 0), ++ bc->caller.name, ++ bc->caller.number, ++ bc->dialed.number, ++ bc->pid); + cb_log(4, stack->port, " --> bc_state:%s\n",bc_state2str(bc->bc_state)); + misdn_lib_log_ies(bc); +- ++ + switch (event) { + case EVENT_SETUP: + if (create_process(glob_mgr->midev, bc)<0) { +@@ -3431,19 +3509,26 @@ + if (misdn_cap_is_speech(bc->capability)) { + if ((event==EVENT_CONNECT)||(event==EVENT_RETRIEVE_ACKNOWLEDGE)) { + if ( *bc->crypt_key ) { +- cb_log(4, stack->port, " --> ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); +- ++ cb_log(4, stack->port, ++ " --> ENABLING BLOWFISH channel:%d caller%d:\"%s\" <%s> dialed%d:%s\n", ++ bc->channel, ++ bc->caller.number_type, ++ bc->caller.name, ++ bc->caller.number, ++ bc->dialed.number_type, ++ bc->dialed.number); ++ + manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); + } +- ++ + if (!bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0); + manager_ec_enable(bc); +- ++ + if (bc->txgain != 0) { + cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain); + manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain); + } +- ++ + if ( bc->rxgain != 0 ) { + cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain); + manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain); +@@ -3466,7 +3551,7 @@ + bc_state_change(holded_bc,BCHAN_CLEANED); + + stack_holder_add(stack,holded_bc); +- ++ + /*kill the bridge and clean the bchannel*/ + if (stack->nt) { + int channel; +@@ -3481,7 +3566,7 @@ + misdn_split_conf(bc2,bc->conf_id); + } + } +- ++ + channel = bc->channel; + + empty_bc(bc); +@@ -3490,9 +3575,9 @@ + if (channel>0) + empty_chan_in_stack(stack,channel); + +- bc->in_use=0; ++ bc->in_use=0; + } +- ++ + } + break; + +@@ -3502,7 +3587,7 @@ + cb_log(0,bc->port," --> we have already send Disconnect\n"); + RETURN(-1,OUT); + } +- ++ + bc->need_disconnect=0; + break; + case EVENT_RELEASE: +@@ -3526,30 +3611,30 @@ + /*create cleanup in TE*/ + int channel=bc->channel; + +- int tmpcause=bc->cause; +- int tmp_out_cause=bc->out_cause; ++ int tmpcause=bc->cause; ++ int tmp_out_cause=bc->out_cause; + empty_bc(bc); + bc->cause=tmpcause; + bc->out_cause=tmp_out_cause; + clean_up_bc(bc); +- ++ + if (channel>0) + empty_chan_in_stack(stack,channel); +- ++ + bc->in_use=0; + } + break; +- ++ + case EVENT_CONNECT_ACKNOWLEDGE: + + if ( bc->nt || misdn_cap_is_speech(bc->capability)) { + int retval=setup_bc(bc); + if (retval == -EINVAL){ + cb_log(0,bc->port,"send_event: setup_bc failed\n"); +- ++ + } + } +- ++ + if (misdn_cap_is_speech(bc->capability)) { + if ( !bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0); + manager_ec_enable(bc); +@@ -3564,21 +3649,32 @@ + } + } + break; +- ++ + default: + break; + } +- ++ + /* Later we should think about sending bchannel data directly to misdn. */ + msg = isdn_msg_build_event(msgs_g, bc, event, stack->nt); +- msg_queue_tail(&stack->downqueue, msg); +- sem_post(&glob_mgr->new_msg); +- ++ if (!msg) { ++ /* ++ * The message was not built. ++ * ++ * NOTE: The only time that the message will fail to build ++ * is because the requested FACILITY message is not supported. ++ * A failed malloc() results in exit() being called. ++ */ ++ RETURN(-1, OUT); ++ } else { ++ msg_queue_tail(&stack->downqueue, msg); ++ sem_post(&glob_mgr->new_msg); ++ } ++ + OUT: + misdn_send_unlock(bc); + + OUT_POST_UNLOCK: +- return retval; ++ return retval; + } + + +@@ -3596,12 +3692,12 @@ + cb_log(0,0,"mISDN Msg without Address pr:%x dinfo:%x (already more than 100 of them)\n",frm->prim,frm->dinfo); + cnt=0; + } +- ++ + free_msg(msg); + return 1; +- ++ + } +- ++ + switch (frm->prim) { + case MGR_SETSTACK|INDICATION: + return handle_bchan(msg); +@@ -3609,7 +3705,7 @@ + + case MGR_SETSTACK|CONFIRM: + case MGR_CLEARSTACK|CONFIRM: +- free_msg(msg) ; ++ free_msg(msg) ; + return 1; + break; + +@@ -3630,13 +3726,13 @@ + struct misdn_bchannel *bc; + + /*we flush the read buffer here*/ +- ++ + cb_log(9,0,"BCHAN DATA without BC: addr:%x port:%d channel:%d\n",frm->addr, port,channel); +- +- free_msg(msg); ++ ++ free_msg(msg); + return 1; +- +- ++ ++ + bc = find_bc_by_channel(port, channel); + + if (!bc) { +@@ -3647,7 +3743,7 @@ + free_msg(msg); + return 1; + } +- ++ + cb_log(0,0," --> bc not found by channel\n"); + if (stack->l2link) + misdn_lib_get_l2_down(stack); +@@ -3658,7 +3754,7 @@ + free_msg(msg); + return 1; + } +- ++ + cb_log(3,port," --> BC in state:%s\n", bc_state2str(bc->bc_state)); + } + } +@@ -3673,7 +3769,7 @@ + struct misdn_stack *stack; + stack=find_stack_by_addr( frm->addr ); + +- ++ + if (!stack) { + return 0; + } +@@ -3685,7 +3781,7 @@ + #endif + + int manager_isdn_handler(iframe_t *frm ,msg_t *msg) +-{ ++{ + + if (frm->dinfo==0xffffffff && frm->prim==(PH_DATA|CONFIRM)) { + cb_log(0,0,"SERIOUS BUG, dinfo == 0xffffffff, prim == PH_DATA | CONFIRM !!!!\n"); +@@ -3696,35 +3792,35 @@ + if (handle_bchan(msg)) { + return 0 ; + } +- ++ + if (unhandled_bmsg_count==1000) { +- cb_log(0, 0, "received 1k Unhandled Bchannel Messages: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); ++ cb_log(0, 0, "received 1k Unhandled Bchannel Messages: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); + unhandled_bmsg_count=0; + } + + unhandled_bmsg_count++; + free_msg(msg); + return 0; +- } ++ } + + #ifdef RECV_FRM_SYSLOG_DEBUG + syslog(LOG_NOTICE,"mISDN recv: P(%02d): ADDR:%x PRIM:%x DINFO:%x\n",stack->port, frm->addr, frm->prim, frm->dinfo); + #endif + +- if (handle_timers(msg)) ++ if (handle_timers(msg)) + return 0 ; + +- +- if (handle_mgmt(msg)) +- return 0 ; +- +- if (handle_l2(msg)) ++ ++ if (handle_mgmt(msg)) + return 0 ; + ++ if (handle_l2(msg)) ++ return 0 ; ++ + /* Its important to handle l1 AFTER l2 */ +- if (handle_l1(msg)) ++ if (handle_l1(msg)) + return 0 ; +- ++ + if (handle_frm_nt(msg)) { + return 0; + } +@@ -3737,10 +3833,10 @@ + return 0 ; + } + +- cb_log(0, 0, "Unhandled Message: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); ++ cb_log(0, 0, "Unhandled Message: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); + free_msg(msg); +- + ++ + return 0; + } + +@@ -3768,16 +3864,16 @@ + + frm->dinfo = 0; + frm->len = 0; +- ++ + msg_queue_tail(&glob_mgr->activatequeue, msg); + sem_post(&glob_mgr->new_msg); + +- +- return 0; ++ ++ return 0; + } + + +-int queue_cleanup_bc(struct misdn_bchannel *bc) ++int queue_cleanup_bc(struct misdn_bchannel *bc) + { + msg_t *msg=alloc_msg(MAX_MSG_SIZE); + iframe_t *frm; +@@ -3794,15 +3890,15 @@ + + frm->dinfo = bc->port; + frm->len = 0; +- ++ + msg_queue_tail(&glob_mgr->activatequeue, msg); + sem_post(&glob_mgr->new_msg); + +- return 0; ++ return 0; + + } + +-int misdn_lib_pid_restart(int pid) ++int misdn_lib_pid_restart(int pid) + { + struct misdn_bchannel *bc=manager_find_bc_by_pid(pid); + +@@ -3819,7 +3915,7 @@ + struct misdn_bchannel dummybc; + /*default is all channels*/ + cb_log(0, port, "Sending Restarts on this port.\n"); +- ++ + misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); + + /*default is all channels*/ +@@ -3855,11 +3951,11 @@ + int misdn_lib_port_restart(int port) + { + struct misdn_stack *stack=find_stack_by_port(port); +- ++ + cb_log(0, port, "Restarting this port.\n"); + if (stack) { + cb_log(0, port, "Stack:%p\n",stack); +- ++ + clear_l3(stack); + { + msg_t *msg=alloc_msg(MAX_MSG_SIZE); +@@ -3869,7 +3965,7 @@ + cb_log(0, port, "port_restart: alloc_msg failed\n"); + return -1; + } +- ++ + frm=(iframe_t*)msg->data; + /* we must activate if we are deactivated */ + /* activate bchannel */ +@@ -3884,7 +3980,7 @@ + + if (stack->nt) + misdn_lib_reinit_nt_stack(stack->port); +- ++ + } + + return 0; +@@ -3892,25 +3988,25 @@ + + + +-static sem_t handler_started; ++static sem_t handler_started; + + /* This is a thread */ + static void manager_event_handler(void *arg) + { +- sem_post(&handler_started); ++ sem_post(&handler_started); + while (1) { + struct misdn_stack *stack; + msg_t *msg; +- ++ + /** wait for events **/ + sem_wait(&glob_mgr->new_msg); +- ++ + for (msg=msg_dequeue(&glob_mgr->activatequeue); + msg; + msg=msg_dequeue(&glob_mgr->activatequeue) + ) + { +- ++ + iframe_t *frm = (iframe_t*) msg->data ; + + switch ( frm->prim) { +@@ -3925,7 +4021,7 @@ + free_msg(msg); + break; + } +- ++ + bc = find_bc_by_l3id(stack, frm->addr); + if (bc) { + cb_log(1,bc->port,"CLEARSTACK queued, cleaning up\n"); +@@ -3934,7 +4030,7 @@ + cb_log(0,stack->port,"bc could not be cleaned correctly !! addr [%x]\n",frm->addr); + } + } +- free_msg(msg); ++ free_msg(msg); + break; + case MGR_SETSTACK | REQUEST : + /* Warning: memory leak here if we get this message */ +@@ -3947,10 +4043,10 @@ + + for (stack=glob_mgr->stack_list; + stack; +- stack=stack->next ) { ++ stack=stack->next ) { + + while ( (msg=msg_dequeue(&stack->upqueue)) ) { +- /** Handle L2/3 Signalling after bchans **/ ++ /** Handle L2/3 Signalling after bchans **/ + if (!handle_frm_nt(msg)) { + /* Maybe it's TE */ + if (!handle_frm(msg)) { +@@ -3960,16 +4056,16 @@ + } + } + +- /* Here we should check if we really want to +- send all the messages we've queued, lets +- assume we've queued a Disconnect, but ++ /* Here we should check if we really want to ++ send all the messages we've queued, lets ++ assume we've queued a Disconnect, but + received it already from the other side!*/ +- ++ + while ( (msg=msg_dequeue(&stack->downqueue)) ) { + if (stack->nt ) { + if (stack->nst.manager_l3(&stack->nst, msg)) + cb_log(0, stack->port, "Error@ Sending Message in NT-Stack.\n"); +- ++ + } else { + iframe_t *frm = (iframe_t *)msg->data; + struct misdn_bchannel *bc = find_bc_by_l3id(stack, frm->dinfo); +@@ -3998,14 +4094,14 @@ + + int i = mISDN_open(); + int max=0; +- ++ + if (i<0) + return -1; + + max = mISDN_get_stack_count(i); +- ++ + mISDN_close(i); +- ++ + return max; + } + +@@ -4022,12 +4118,12 @@ + #endif + } + +-void misdn_lib_nt_debug_init( int flags, char *file ) ++void misdn_lib_nt_debug_init( int flags, char *file ) + { + static int init=0; + char *f; +- +- if (!flags) ++ ++ if (!flags) + f=NULL; + else + f=file; +@@ -4048,50 +4144,50 @@ + char plist[1024]; + int midev; + int port_count=0; +- ++ + cb_log = iface->cb_log; + cb_event = iface->cb_event; + cb_jb_empty = iface->cb_jb_empty; +- ++ + glob_mgr = mgr; +- ++ + msg_init(); + + misdn_lib_nt_debug_init(0,NULL); +- ++ + if (!portlist || (*portlist == 0) ) return 1; +- ++ + init_flip_bits(); +- ++ + { + strncpy(plist,portlist, 1024); + plist[1023] = 0; + } +- ++ + memcpy(tone_425_flip,tone_425,TONE_425_SIZE); + flip_buf_bits(tone_425_flip,TONE_425_SIZE); + + memcpy(tone_silence_flip,tone_SILENCE,TONE_SILENCE_SIZE); + flip_buf_bits(tone_silence_flip,TONE_SILENCE_SIZE); +- ++ + midev=te_lib_init(); + mgr->midev=midev; + + port_count=mISDN_get_stack_count(midev); +- ++ + msg_queue_init(&mgr->activatequeue); +- ++ + if (sem_init(&mgr->new_msg, 1, 0)<0) + sem_init(&mgr->new_msg, 0, 0); +- ++ + for (tok=strtok_r(plist," ,",&tokb ); +- tok; ++ tok; + tok=strtok_r(NULL," ,",&tokb)) { + int port = atoi(tok); + struct misdn_stack *stack; + static int first=1; + int ptp=0; +- ++ + if (strstr(tok, "ptp")) + ptp=1; + +@@ -4100,12 +4196,12 @@ + exit(1); + } + stack=stack_init(midev, port, ptp); +- ++ + if (!stack) { + perror("stack_init"); + exit(1); + } +- ++ + { + int i; + for(i=0;i<=stack->b_num; i++) { +@@ -4122,30 +4218,30 @@ + first=0; + continue; + } +- ++ + if (stack) { + struct misdn_stack * help; +- for ( help=mgr->stack_list; help; help=help->next ) ++ for ( help=mgr->stack_list; help; help=help->next ) + if (help->next == NULL) break; + help->next=stack; + } +- ++ + } +- ++ + if (sem_init(&handler_started, 1, 0)<0) + sem_init(&handler_started, 0, 0); +- ++ + cb_log(8, 0, "Starting Event Handler\n"); + pthread_create( &mgr->event_handler_thread, NULL,(void*)manager_event_handler, mgr); +- ++ + sem_wait(&handler_started) ; + cb_log(8, 0, "Starting Event Catcher\n"); + pthread_create( &mgr->event_thread, NULL, (void*)misdn_lib_isdn_event_catcher, mgr); +- ++ + cb_log(8, 0, "Event Catcher started\n"); + +- global_state= MISDN_INITIALIZED; +- ++ global_state= MISDN_INITIALIZED; ++ + return (mgr == NULL); + } + +@@ -4153,7 +4249,7 @@ + { + struct misdn_stack *help; + int i; +- ++ + for ( help=glob_mgr->stack_list; help; help=help->next ) { + for(i=0;i<=help->b_num; i++) { + char buf[1024]; +@@ -4163,21 +4259,21 @@ + cb_log (1, help->port, "Destroying this port.\n"); + stack_destroy(help); + } +- ++ + if (global_state == MISDN_INITIALIZED) { + cb_log(4, 0, "Killing Handler Thread\n"); + if ( pthread_cancel(glob_mgr->event_handler_thread) == 0 ) { + cb_log(4, 0, "Joining Handler Thread\n"); + pthread_join(glob_mgr->event_handler_thread, NULL); + } +- ++ + cb_log(4, 0, "Killing Main Thread\n"); + if ( pthread_cancel(glob_mgr->event_thread) == 0 ) { + cb_log(4, 0, "Joining Main Thread\n"); + pthread_join(glob_mgr->event_thread, NULL); + } + } +- ++ + cb_log(1, 0, "Closing mISDN device\n"); + te_lib_destroy(glob_mgr->midev); + } +@@ -4197,12 +4293,12 @@ + cb_log(0, bc->port, "bchannel_activate: Stack not found !"); + return ; + } +- ++ + /* we must activate if we are deactivated */ + clear_ibuffer(bc->astbuf); +- ++ + cb_log(5, stack->port, "$$$ Bchan Activated addr %x\n", bc->addr); +- ++ + mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_ESTABLISH | REQUEST, 0,0, NULL, TIMEOUT_1SEC); + + return ; +@@ -4213,7 +4309,7 @@ + { + struct misdn_stack *stack=get_stack_by_bc(bc); + iframe_t dact; +- char buf[128]; ++ char buf[128]; + + switch (bc->bc_state) { + case BCHAN_ACTIVATED: +@@ -4226,11 +4322,11 @@ + return ; + + } +- ++ + cb_log(5, stack->port, "$$$ Bchan deActivated addr %x\n", bc->addr); +- ++ + bc->generate_tone=0; +- ++ + dact.prim = DL_RELEASE | REQUEST; + dact.addr = bc->addr | FLG_MSG_DOWN; + dact.dinfo = 0; +@@ -4238,9 +4334,9 @@ + mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_RELEASE|REQUEST,0,0,NULL, TIMEOUT_1SEC); + + clear_ibuffer(bc->astbuf); +- ++ + bc_state_change(bc,BCHAN_RELEASE); +- ++ + return; + } + +@@ -4260,19 +4356,19 @@ + cb_log(3, bc->port, "BC not yet activated (state:%s)\n",bc_state2str(bc->bc_state)); + return -1; + } +- ++ + frm->prim = DL_DATA|REQUEST; + frm->dinfo = 0; + frm->addr = bc->addr | FLG_MSG_DOWN ; +- ++ + frm->len = len; + memcpy(&buf[mISDN_HEADER_LEN], data,len); +- +- if ( misdn_cap_is_speech(bc->capability) ) ++ ++ if ( misdn_cap_is_speech(bc->capability) ) + flip_buf_bits( &buf[mISDN_HEADER_LEN], len); + else + cb_log(6, stack->port, "Writing %d data bytes\n",len); +- ++ + cb_log(9, stack->port, "Writing %d bytes 2 mISDN\n",len); + r=mISDN_write(stack->midev, buf, frm->len + mISDN_HEADER_LEN, TIMEOUT_INFINIT); + return 0; +@@ -4289,9 +4385,9 @@ + iframe_t *ctrl = (iframe_t *)buffer; /* preload data */ + unsigned int *d = (unsigned int*)&ctrl->data.p; + /*struct misdn_stack *stack=get_stack_by_bc(bc);*/ +- ++ + cb_log(4,bc->port,"ph_control: c1:%x c2:%x\n",c1,c2); +- ++ + ctrl->prim = PH_CONTROL | REQUEST; + ctrl->addr = bc->addr | FLG_MSG_DOWN; + ctrl->dinfo = 0; +@@ -4340,7 +4436,7 @@ + iframe_t *ctrl = (iframe_t *)buffer; + unsigned int *d = (unsigned int *)&ctrl->data.p; + /*struct misdn_stack *stack=get_stack_by_bc(bc);*/ +- ++ + ctrl->prim = PH_CONTROL | REQUEST; + ctrl->addr = bc->addr | FLG_MSG_DOWN; + ctrl->dinfo = 0; +@@ -4356,13 +4452,13 @@ + void manager_clean_bc(struct misdn_bchannel *bc ) + { + struct misdn_stack *stack=get_stack_by_bc(bc); +- ++ + if (bc->channel>0) + empty_chan_in_stack(stack, bc->channel); + empty_bc(bc); + bc->in_use=0; + +- cb_event(EVENT_CLEANUP, bc, NULL); ++ cb_event(EVENT_CLEANUP, bc, NULL); + } + + +@@ -4370,15 +4466,15 @@ + { + struct misdn_bchannel *help; + cb_log(4,stack->port, "*HOLDER: add %x\n",holder->l3_id); +- ++ + holder->stack_holder=1; + holder->next=NULL; +- ++ + if (!stack->holding) { + stack->holding = holder; + return; + } +- ++ + for (help=stack->holding; + help; + help=help->next) { +@@ -4387,7 +4483,7 @@ + break; + } + } +- ++ + } + + void stack_holder_remove(struct misdn_stack *stack, struct misdn_bchannel *holder) +@@ -4395,17 +4491,17 @@ + struct misdn_bchannel *h1; + + if (!holder->stack_holder) return; +- ++ + holder->stack_holder=0; +- ++ + cb_log(4,stack->port, "*HOLDER: remove %x\n",holder->l3_id); + if (!stack || ! stack->holding) return; +- ++ + if (holder == stack->holding) { + stack->holding = stack->holding->next; + return; + } +- ++ + for (h1=stack->holding; + h1; + h1=h1->next) { +@@ -4421,9 +4517,9 @@ + struct misdn_bchannel *help; + + cb_log(4,stack?stack->port:0, "*HOLDER: find_bychan %c\n", chan); +- ++ + if (!stack) return NULL; +- ++ + for (help=stack->holding; + help; + help=help->next) { +@@ -4443,9 +4539,9 @@ + struct misdn_bchannel *help; + + cb_log(4,stack?stack->port:0, "*HOLDER: find %lx\n",l3id); +- ++ + if (!stack) return NULL; +- ++ + for (help=stack->holding; + help; + help=help->next) { +@@ -4461,34 +4557,34 @@ + + + +-void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone) ++void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone) + { + char buf[mISDN_HEADER_LEN + 128] = ""; + iframe_t *frm = (iframe_t*)buf; + + switch(tone) { + case TONE_DIAL: +- manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_DIALTONE); ++ manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_DIALTONE); + break; +- ++ + case TONE_ALERTING: +- manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_RINGING); ++ manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_RINGING); + break; +- ++ + case TONE_HANGUP: +- manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_HANGUP); ++ manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_HANGUP); + break; + + case TONE_NONE: + default: +- manager_ph_control(bc, TONE_PATT_OFF, TONE_GERMAN_HANGUP); ++ manager_ph_control(bc, TONE_PATT_OFF, TONE_GERMAN_HANGUP); + } + + frm->prim=DL_DATA|REQUEST; + frm->addr=bc->addr|FLG_MSG_DOWN; + frm->dinfo=0; + frm->len=128; +- ++ + mISDN_write(glob_mgr->midev, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC); + } + +@@ -4496,7 +4592,7 @@ + void manager_ec_enable(struct misdn_bchannel *bc) + { + struct misdn_stack *stack=get_stack_by_bc(bc); +- ++ + cb_log(4, stack?stack->port:0,"ec_enable\n"); + + if (!misdn_cap_is_speech(bc->capability)) { +@@ -4513,7 +4609,7 @@ + + if (bc->ec_enable) { + cb_log(3, stack?stack->port:0,"Sending Control ECHOCAN_ON taps:%d\n",bc->ec_deftaps); +- ++ + switch (bc->ec_deftaps) { + case 4: + case 8: +@@ -4530,10 +4626,10 @@ + cb_log(0, stack->port, "Taps should be power of 2\n"); + bc->ec_deftaps=128; + } +- ++ + ec_arr[0]=bc->ec_deftaps; + ec_arr[1]=0; +- ++ + manager_ph_control_block(bc, ECHOCAN_ON, ec_arr, sizeof(ec_arr)); + } + #endif +@@ -4545,7 +4641,7 @@ + void manager_ec_disable(struct misdn_bchannel *bc) + { + struct misdn_stack *stack=get_stack_by_bc(bc); +- ++ + cb_log(4, stack?stack->port:0," --> ec_disable\n"); + + if (!misdn_cap_is_speech(bc->capability)) { +@@ -4600,10 +4696,10 @@ + + cb_log(4, bc1->port, "I Send: BRIDGE from:%d to:%d\n",bc1->port,bc2->port); + +- for (bc=bc_list; *bc; bc++) { ++ for (bc=bc_list; *bc; bc++) { + (*bc)->conf_id=conf_id; + cb_log(4, (*bc)->port, " --> bc_addr:%x\n",(*bc)->addr); +- ++ + switch((*bc)->bc_state) { + case BCHAN_ACTIVATED: + misdn_join_conf(*bc,conf_id); +@@ -4622,15 +4718,15 @@ + bc1,bc2,NULL + }; + struct misdn_bchannel **bc; +- +- for (bc=bc_list; *bc; bc++) { ++ ++ for (bc=bc_list; *bc; bc++) { + if ( (*bc)->bc_state == BCHAN_BRIDGED){ + misdn_split_conf( *bc, (*bc)->conf_id); + } else { + cb_log( 2, (*bc)->port, "BC not bridged (state:%s) so not splitting it\n",bc_state2str((*bc)->bc_state)); + } + } +- ++ + } + + +@@ -4646,37 +4742,37 @@ + void misdn_lib_reinit_nt_stack(int port) + { + struct misdn_stack *stack=find_stack_by_port(port); +- ++ + if (stack) { + stack->l2link=0; + stack->blocked=0; +- ++ + cleanup_Isdnl3(&stack->nst); + cleanup_Isdnl2(&stack->nst); + + + memset(&stack->nst, 0, sizeof(net_stack_t)); + memset(&stack->mgr, 0, sizeof(manager_t)); +- ++ + stack->mgr.nst = &stack->nst; + stack->nst.manager = &stack->mgr; +- ++ + stack->nst.l3_manager = handle_event_nt; + stack->nst.device = glob_mgr->midev; + stack->nst.cardnr = port; + stack->nst.d_stid = stack->d_stid; +- ++ + stack->nst.feature = FEATURE_NET_HOLD; + if (stack->ptp) + stack->nst.feature |= FEATURE_NET_PTP; + if (stack->pri) + stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID; +- ++ + stack->nst.l1_id = stack->lower_id; + stack->nst.l2_id = stack->upper_id; +- ++ + msg_queue_init(&stack->nst.down_queue); +- ++ + Isdnl2Init(&stack->nst); + Isdnl3Init(&stack->nst); + +Index: channels/misdn/isdn_lib_intern.h +=================================================================== +--- a/channels/misdn/isdn_lib_intern.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/isdn_lib_intern.h (.../trunk) (revision 186562) +@@ -41,7 +41,6 @@ + struct isdn_msg { + unsigned long misdn_msg; + +- enum layer_e layer; + enum event_e event; + + void (*msg_parser)(struct isdn_msg *msgs, msg_t *msg, struct misdn_bchannel *bc, int nt); +Index: channels/misdn/isdn_lib.h +=================================================================== +--- a/channels/misdn/isdn_lib.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/isdn_lib.h (.../trunk) (revision 186562) +@@ -96,15 +96,23 @@ + ENOCHAN=1 + }; + +- + enum mISDN_NUMBER_PLAN { +- NUMPLAN_UNINITIALIZED=-1, +- NUMPLAN_INTERNATIONAL=0x1, +- NUMPLAN_NATIONAL=0x2, +- NUMPLAN_SUBSCRIBER=0x4, +- NUMPLAN_UNKNOWN=0x0 ++ NUMPLAN_UNKNOWN = 0x0, ++ NUMPLAN_ISDN = 0x1, /* ISDN/Telephony numbering plan E.164 */ ++ NUMPLAN_DATA = 0x3, /* Data numbering plan X.121 */ ++ NUMPLAN_TELEX = 0x4, /* Telex numbering plan F.69 */ ++ NUMPLAN_NATIONAL = 0x8, ++ NUMPLAN_PRIVATE = 0x9 + }; + ++enum mISDN_NUMBER_TYPE { ++ NUMTYPE_UNKNOWN = 0x0, ++ NUMTYPE_INTERNATIONAL = 0x1, ++ NUMTYPE_NATIONAL = 0x2, ++ NUMTYPE_NETWORK_SPECIFIC = 0x3, ++ NUMTYPE_SUBSCRIBER = 0x4, ++ NUMTYPE_ABBREVIATED = 0x5 ++}; + + enum event_response_e { + RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE, +@@ -189,6 +197,19 @@ + INFO_PI_INTERWORKING_NO_RELEASE_POST_ANSWER =0x13 + }; + ++/*! ++ * \brief Q.931 encoded redirecting reason ++ */ ++enum mISDN_REDIRECTING_REASON { ++ mISDN_REDIRECTING_REASON_UNKNOWN = 0x0, ++ mISDN_REDIRECTING_REASON_CALL_FWD_BUSY = 0x1, /* Call forwarding busy or called DTE busy */ ++ mISDN_REDIRECTING_REASON_NO_REPLY = 0x2, /* Call forwarding no reply */ ++ mISDN_REDIRECTING_REASON_DEFLECTION = 0x4, /* Call deflection */ ++ mISDN_REDIRECTING_REASON_OUT_OF_ORDER = 0x9, /* Called DTE out of order */ ++ mISDN_REDIRECTING_REASON_CALL_FWD_DTE = 0xA, /* Call forwarding by the called DTE */ ++ mISDN_REDIRECTING_REASON_CALL_FWD = 0xF /* Call forwarding unconditional or systematic call redirection */ ++}; ++ + enum { /*CODECS*/ + INFO_CODEC_ULAW=2, + INFO_CODEC_ALAW=3 +@@ -202,12 +223,81 @@ + UNKNOWN + }; + ++/* Maximum phone number (address) length plus null terminator */ ++#define MISDN_MAX_NUMBER_LEN (31 + 1) + ++/* Maximum name length plus null terminator (From ECMA-164) */ ++#define MISDN_MAX_NAME_LEN (50 + 1) + ++/* Maximum subaddress length plus null terminator */ ++#define MISDN_MAX_SUBADDRESS_LEN (23 + 1) ++ ++/* Maximum keypad facility content length plus null terminator */ ++#define MISDN_MAX_KEYPAD_LEN (31 + 1) ++ ++/*! \brief Connected-Line/Calling/Redirecting ID info struct */ ++struct misdn_party_id { ++ /*! \brief Number presentation restriction code ++ * 0=Allowed, 1=Restricted, 2=Unavailable ++ */ ++ int presentation; ++ ++ /*! \brief Number screening code ++ * 0=Unscreened, 1=Passed Screen, 2=Failed Screen, 3=Network Number ++ */ ++ int screening; ++ ++ /*! \brief Type-of-number in ISDN terms for the number */ ++ enum mISDN_NUMBER_TYPE number_type; ++ ++ /*! \brief Type-of-number numbering plan. */ ++ enum mISDN_NUMBER_PLAN number_plan; ++ ++ /*! \brief Subscriber Name ++ * \note The name is currently obtained from Asterisk for ++ * potential use in display ie's since basic ISDN does ++ * not support names directly. ++ */ ++ char name[MISDN_MAX_NAME_LEN]; ++ ++ /*! \brief Phone number (Address) */ ++ char number[MISDN_MAX_NUMBER_LEN]; ++ ++ /*! \brief Subaddress number */ ++ char subaddress[MISDN_MAX_SUBADDRESS_LEN]; ++}; ++ ++/*! \brief Redirecting information struct */ ++struct misdn_party_redirecting { ++ /*! \brief Who is redirecting the call (Sent to the party the call is redirected toward) */ ++ struct misdn_party_id from; ++ ++ /*! \brief Reason a call is being redirected (Q.931 field value) */ ++ enum mISDN_REDIRECTING_REASON reason; ++}; ++ ++ ++/*! \brief B channel control structure */ + struct misdn_bchannel { + /*! \brief B channel send locking structure */ + struct send_lock *send_lock; + ++ /*! \brief Originating/Caller ID information struct ++ * \note The number_type element is set to "localdialplan" in /etc/asterisk/misdn.conf for outgoing calls ++ * \note The number element can be set to "callerid" in /etc/asterisk/misdn.conf for outgoing calls ++ */ ++ struct misdn_party_id caller; ++ ++ /*! \brief Connected-Party/Connected-Line ID information struct ++ * \note The number_type element is set to "cpndialplan" in /etc/asterisk/misdn.conf for outgoing calls ++ */ ++ struct misdn_party_id connected; ++ ++ /*! \brief Redirecting information struct (Where a call diversion or transfer was invoked) ++ * \note The redirecting subaddress is not defined in Q.931 so it is not used. ++ */ ++ struct misdn_party_redirecting redirecting; ++ + /*! \brief TRUE if this is a dummy BC record */ + int dummy; + +@@ -326,26 +416,6 @@ + /*! \brief TRUE if we will not use the jitter buffer system */ + int nojitter; + +- /*! \brief Type-of-number in ISDN terms for the dialed/called number +- * \note This value is set to "dialplan" in /etc/asterisk/misdn.conf for outgoing calls +- */ +- enum mISDN_NUMBER_PLAN dnumplan; +- +- /*! \brief Type-of-number in ISDN terms for the redirecting number which a call diversion or transfer was invoked. +- * \note Collected from the incoming SETUP message but not used. +- */ +- enum mISDN_NUMBER_PLAN rnumplan; +- +- /*! \brief Type-of-number in ISDN terms for the originating/calling number (Caller-ID) +- * \note This value is set to "localdialplan" in /etc/asterisk/misdn.conf for outgoing calls +- */ +- enum mISDN_NUMBER_PLAN onumplan; +- +- /*! \brief Type-of-number in ISDN terms for the connected party number +- * \note This value is set to "cpndialplan" in /etc/asterisk/misdn.conf for outgoing calls +- */ +- enum mISDN_NUMBER_PLAN cpnnumplan; +- + /*! \brief Progress Indicator IE coding standard field. + * \note Collected from the incoming messages but not used. + */ +@@ -421,16 +491,38 @@ + */ + int stack_holder; + +- /*! \brief Caller ID presentation restriction code ++ /*! ++ * \brief Put a display ie in the CONNECT message ++ * \details ++ * Put a display ie in the CONNECT message containing the following ++ * information if it is available (nt port only): ++ * 0 - Do not put the connected line information in the display ie. ++ * 1 - Put the available connected line name in the display ie. ++ * 2 - Put the available connected line number in the display ie. ++ * 3 - Put the available connected line name and number in the display ie. ++ */ ++ int display_connected; ++ ++ /*! ++ * \brief Put a display ie in the SETUP message ++ * \details ++ * Put a display ie in the SETUP message containing the following ++ * information if it is available (nt port only): ++ * 0 - Do not put the caller information in the display ie. ++ * 1 - Put the available caller name in the display ie. ++ * 2 - Put the available caller number in the display ie. ++ * 3 - Put the available caller name and number in the display ie. ++ */ ++ int display_setup; ++ ++ /*! \brief User set presentation restriction code + * 0=Allowed, 1=Restricted, 2=Unavailable + * \note It is settable by the misdn_set_opt() application. + */ +- int pres; ++ int presentation; + +- /*! \brief Caller ID screening code +- * 0=Unscreened, 1=Passed Screen, 2=Failed Screen, 3=Network Number +- */ +- int screen; ++ /*! \brief TRUE if the user set the presentation restriction code */ ++ int set_presentation; + + /*! \brief SETUP message bearer capability field code value */ + int capability; +@@ -462,6 +554,23 @@ + int hdlc; + /* V110 */ + ++ /*! \brief Dialed/Called information struct */ ++ struct { ++ /*! \brief Type-of-number in ISDN terms for the dialed/called number ++ * \note This value is set to "dialplan" in /etc/asterisk/misdn.conf for outgoing calls ++ */ ++ enum mISDN_NUMBER_TYPE number_type; ++ ++ /*! \brief Type-of-number numbering plan. */ ++ enum mISDN_NUMBER_PLAN number_plan; ++ ++ /*! \brief Dialed/Called Phone Number (Address) */ ++ char number[MISDN_MAX_NUMBER_LEN]; ++ ++ /*! \brief Dialed/Called Subaddress number */ ++ char subaddress[MISDN_MAX_SUBADDRESS_LEN]; ++ } dialed; ++ + /*! \brief Display message that can be displayed by the user phone. + * \note Maximum displayable length is 34 or 82 octets. + * It is also settable by the misdn_set_opt() application. +@@ -469,39 +578,20 @@ + char display[84]; + + /*! \brief Not used. Contents are setup but not used. */ +- char msn[32]; ++ char msn[MISDN_MAX_NUMBER_LEN]; + +- /*! \brief Originating/Calling Phone Number (Address) +- * \note This value can be set to "callerid" in /etc/asterisk/misdn.conf for outgoing calls +- */ +- char oad[32]; +- +- /*! \brief Redirecting Phone Number (Address) where a call diversion or transfer was invoked */ +- char rad[32]; +- +- /*! \brief Dialed/Called Phone Number (Address) */ +- char dad[32]; +- +- /*! \brief Connected Party/Line Phone Number (Address) */ +- char cad[32]; +- +- /*! \brief Original Dialed/Called Phone Number (Address) before national/international dialing prefix added. +- * \note Not used. Contents are setup but not used. +- */ +- char orig_dad[32]; +- + /*! \brief Q.931 Keypad Facility IE contents + * \note Contents exported and imported to Asterisk variable MISDN_KEYPAD + */ +- char keypad[32]; ++ char keypad[MISDN_MAX_KEYPAD_LEN]; + + /*! \brief Current overlap dialing digits to/from INFORMATION messages */ +- char info_dad[64]; ++ char info_dad[MISDN_MAX_NUMBER_LEN]; + + /*! \brief Collected digits to go into info_dad[] while waiting for a SETUP_ACKNOWLEDGE to come in. */ +- char infos_pending[64]; ++ char infos_pending[MISDN_MAX_NUMBER_LEN]; + +-/* unsigned char info_keypad[32]; */ ++/* unsigned char info_keypad[MISDN_MAX_KEYPAD_LEN]; */ + /* unsigned char clisub[24]; */ + /* unsigned char cldsub[24]; */ + +Index: channels/misdn/Makefile +=================================================================== +--- a/channels/misdn/Makefile (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/Makefile (.../trunk) (revision 186562) +@@ -13,5 +13,5 @@ + portinfo: portinfo.o + $(CC) -o $@ $^ -lisdnnet -lmISDN -lpthread + +-clean: ++clean: + rm -rf *.a *.o *.so portinfo *.i +Index: channels/misdn/chan_misdn_config.h +=================================================================== +--- a/channels/misdn/chan_misdn_config.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/chan_misdn_config.h (.../trunk) (revision 186562) +@@ -11,7 +11,7 @@ + * the GNU General Public License + */ + +-/*! \file ++/*! \file + * \brief Interface to mISDN - Config + * \author Christian Richter <crich@beronet.com> + */ +@@ -46,10 +46,16 @@ + MISDN_CFG_DIALPLAN, /* int */ + MISDN_CFG_LOCALDIALPLAN, /* int */ + MISDN_CFG_CPNDIALPLAN, /* int */ +- MISDN_CFG_NATPREFIX, /* char[] */ +- MISDN_CFG_INTERNATPREFIX, /* char[] */ ++ MISDN_CFG_TON_PREFIX_UNKNOWN, /* char[] */ ++ MISDN_CFG_TON_PREFIX_INTERNATIONAL, /* char[] */ ++ MISDN_CFG_TON_PREFIX_NATIONAL, /* char[] */ ++ MISDN_CFG_TON_PREFIX_NETWORK_SPECIFIC,/* char[] */ ++ MISDN_CFG_TON_PREFIX_SUBSCRIBER, /* char[] */ ++ MISDN_CFG_TON_PREFIX_ABBREVIATED, /* char[] */ + MISDN_CFG_PRES, /* int */ + MISDN_CFG_SCREEN, /* int */ ++ MISDN_CFG_DISPLAY_CONNECTED, /* int */ ++ MISDN_CFG_DISPLAY_SETUP, /* int */ + MISDN_CFG_ALWAYS_IMMEDIATE, /* int (bool) */ + MISDN_CFG_NODIALTONE, /* int (bool) */ + MISDN_CFG_IMMEDIATE, /* int (bool) */ +@@ -89,7 +95,7 @@ + MISDN_CFG_FAXDETECT_TIMEOUT, /* int */ + MISDN_CFG_PTP, /* int (bool) */ + MISDN_CFG_LAST, +- ++ + /* general config items */ + MISDN_GEN_FIRST, + #ifndef MISDN_1_2 +@@ -116,14 +122,14 @@ + }; + + /* you must call misdn_cfg_init before any other function of this header file */ +-int misdn_cfg_init(int max_ports, int reload); ++int misdn_cfg_init(int max_ports, int reload); + void misdn_cfg_reload(void); + void misdn_cfg_destroy(void); + + void misdn_cfg_update_ptp( void ); + +-/* if you requst a general config element, the port value is ignored. if the requested +- * value is not available, or the buffer is too small, the buffer will be nulled (in ++/* if you requst a general config element, the port value is ignored. if the requested ++ * value is not available, or the buffer is too small, the buffer will be nulled (in + * case of a char* only its first byte will be nulled). */ + void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void* buf, int bufsize); + +Index: channels/misdn/ie.c +=================================================================== +--- a/channels/misdn/ie.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/misdn/ie.c (.../trunk) (revision 186562) +@@ -15,7 +15,7 @@ + * the GNU General Public License + */ + +-/*! \file ++/*! \file + * \brief Interface to mISDN + * \author Christian Richter <crich@beronet.com> + */ +@@ -153,7 +153,7 @@ + p[4+(multi>=0)] = 0xa0 + user; + } + +-static void dec_ie_bearer(unsigned char *p, Q931_info_t *qi, int *coding, int *capability, int *mode, int *rate, int *multi, int *user, ++static void dec_ie_bearer(unsigned char *p, Q931_info_t *qi, int *coding, int *capability, int *mode, int *rate, int *multi, int *user, + int *async, int *urate, int *stopbits, int *dbits, int *parity, int nt, struct misdn_bchannel *bc) + { + int octet; +@@ -168,13 +168,13 @@ + *stopbits = -1; + *dbits = -1; + *parity = -1; +- ++ + if (!nt) + { + p = NULL; + #ifdef LLC_SUPPORT + if (qi->QI_ELEMENT(llc)) { +- ++ + p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(llc) + 1; + } + #endif +@@ -189,7 +189,7 @@ + printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); + return; + } +- ++ + *coding = (p[1]&0x60) >> 5; + *capability = p[1] & 0x1f; + octet = 2; +@@ -221,7 +221,7 @@ + + if (p[0] <= octet) + goto done; +- ++ + if (p[octet++] & 0x80) + goto l2; + +@@ -231,7 +231,7 @@ + + if (p[0] <= octet) + goto done; +- ++ + if (p[octet++] & 0x80) + goto l2; + +@@ -239,7 +239,7 @@ + + if (p[0] <= octet) + goto done; +- ++ + if (p[octet++] & 0x80) + goto l2; + +@@ -247,20 +247,20 @@ + + if (p[0] <= octet) + goto done; +- ++ + if (!p[octet++] & 0x80) + goto l2; + + /* Wheee. V.110 speed information */ + + *stopbits = (p[octet] & 0x60) >> 5; +- *dbits = (p[octet] & 0x18) >> 3; ++ *dbits = (p[octet] & 0x18) >> 3; + *parity = p[octet] & 7; + + octet++; + } + l2: /* Nobody seems to want the rest so we don't bother (yet) */ +- done: ++ done: + if (MISDN_IE_DEBG) printf(" coding=%d capability=%d mode=%d rate=%d multi=%d user=%d async=%d urate=%d stopbits=%d dbits=%d parity=%d\n", *coding, *capability, *mode, *rate, *multi, *user, *async, *urate, *stopbits, *dbits, *parity); + } + +@@ -292,7 +292,7 @@ + if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]); + i++; + } +- ++ + if (MISDN_IE_DEBG) printf(" callid%s\n", debug); + + l = callid_len; +@@ -338,7 +338,7 @@ + if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]); + i++; + } +- ++ + if (MISDN_IE_DEBG) printf(" callid%s\n", debug); + } + #endif +@@ -501,7 +501,7 @@ + } else + { + strnncpy(number, (char *)p+2, p[0]-1, number_len); +- /* SPECIAL workarround for IBT software bug */ ++ /* SPECIAL workarround for IBT software bug */ + /* if (number[0]==0x80) */ + /* strcpy((char *)number, (char *)number+1); */ + } +@@ -691,7 +691,7 @@ + int l; + struct misdn_stack *stack=get_stack_by_bc(bc); + int pri = stack->pri; +- ++ + if (exclusive<0 || exclusive>1) + { + printf("%s: ERROR: exclusive(%d) is out of range.\n", __FUNCTION__, exclusive); +@@ -707,8 +707,8 @@ + } + + /* if (MISDN_IE_DEBG) printf(" exclusive=%d channel=%d\n", exclusive, channel); */ +- + ++ + if (!pri) + { + /* BRI */ +@@ -1086,7 +1086,7 @@ + *location = -1; + //*progress = -1; + *progress = 0; +- ++ + if (!nt) + { + p = NULL; +@@ -1350,7 +1350,7 @@ + if (MISDN_IE_DEBG) sprintf(debug+(i*3), " %02x", user[i]); + i++; + } +- ++ + if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", protocol, debug); + + l = user_len+1; +@@ -1397,7 +1397,7 @@ + i++; + } + debug[i*3] = '\0'; +- ++ + if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", *protocol, debug); + } + #endif +Index: channels/chan_gtalk.c +=================================================================== +--- a/channels/chan_gtalk.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/channels/chan_gtalk.c (.../trunk) (revision 186562) +@@ -52,7 +52,8 @@ + #include "asterisk/pbx.h" + #include "asterisk/sched.h" + #include "asterisk/io.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" ++#include "asterisk/stun.h" + #include "asterisk/acl.h" + #include "asterisk/callerid.h" + #include "asterisk/file.h" +@@ -112,8 +113,8 @@ + char cid_name[80]; /*!< Caller ID name */ + char exten[80]; /*!< Called extension */ + struct ast_channel *owner; /*!< Master Channel */ +- struct ast_rtp *rtp; /*!< RTP audio session */ +- struct ast_rtp *vrtp; /*!< RTP video session */ ++ struct ast_rtp_instance *rtp; /*!< RTP audio session */ ++ struct ast_rtp_instance *vrtp; /*!< RTP video session */ + int jointcapability; /*!< Supported capability at both ends (codecs ) */ + int peercapability; + struct gtalk_pvt *next; /* Next entity */ +@@ -183,11 +184,6 @@ + static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid); + static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); + static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); +-/*----- RTP interface functions */ +-static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, +- struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); +-static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); +-static int gtalk_get_codec(struct ast_channel *chan); + + /*! \brief PBX interface structure for channel registration */ + static const struct ast_channel_tech gtalk_tech = { +@@ -197,7 +193,7 @@ + .requester = gtalk_request, + .send_digit_begin = gtalk_digit_begin, + .send_digit_end = gtalk_digit_end, +- .bridge = ast_rtp_bridge, ++ .bridge = ast_rtp_instance_bridge, + .call = gtalk_call, + .hangup = gtalk_hangup, + .answer = gtalk_answer, +@@ -216,14 +212,6 @@ + static struct io_context *io; /*!< The IO context */ + static struct in_addr __ourip; + +-/*! \brief RTP driver interface */ +-static struct ast_rtp_protocol gtalk_rtp = { +- type: "Gtalk", +- get_rtp_info: gtalk_get_rtp_peer, +- set_rtp_peer: gtalk_set_rtp_peer, +- get_codec: gtalk_get_codec, +-}; +- + static struct ast_cli_entry gtalk_cli[] = { + AST_CLI_DEFINE(gtalk_do_reload, "Reload GoogleTalk configuration"), + AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"), +@@ -371,7 +359,7 @@ + iks_insert_node(dcodecs, payload_gsm); + res++; + } +- ast_rtp_lookup_code(p->rtp, 1, codec); ++ + return res; + } + +@@ -523,18 +511,19 @@ + return res; + } + +-static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) ++static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) + { + struct gtalk_pvt *p = chan->tech_pvt; +- enum ast_rtp_get_result res = AST_RTP_GET_FAILED; ++ enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; + + if (!p) + return res; + + ast_mutex_lock(&p->lock); + if (p->rtp){ +- *rtp = p->rtp; +- res = AST_RTP_TRY_PARTIAL; ++ ao2_ref(p->rtp, +1); ++ *instance = p->rtp; ++ res = AST_RTP_GLUE_RESULT_LOCAL; + } + ast_mutex_unlock(&p->lock); + +@@ -547,7 +536,7 @@ + return p->peercapability; + } + +-static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) ++static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) + { + struct gtalk_pvt *p; + +@@ -567,6 +556,13 @@ + return 0; + } + ++static struct ast_rtp_glue gtalk_rtp_glue = { ++ .type = "Gtalk", ++ .get_rtp_info = gtalk_get_rtp_peer, ++ .get_codec = gtalk_get_codec, ++ .update_peer = gtalk_set_rtp_peer, ++}; ++ + static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2) + { + iks *response = NULL, *error = NULL, *reason = NULL; +@@ -615,15 +611,15 @@ + } + + /* codec points to the first <payload-type/> tag */ +- codec = iks_child(iks_child(iks_child(pak->x))); ++ codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x))); + while (codec) { +- ast_rtp_set_m_type(tmp->rtp, atoi(iks_find_attrib(codec, "id"))); +- ast_rtp_set_rtpmap_type(tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); +- codec = iks_next(codec); ++ ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp, atoi(iks_find_attrib(codec, "id"))); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); ++ codec = iks_next_tag(codec); + } + + /* Now gather all of the codecs that we are asked for */ +- ast_rtp_get_current_formats(tmp->rtp, &tmp->peercapability, &peernoncodeccapability); ++ ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), &tmp->peercapability, &peernoncodeccapability); + + /* at this point, we received an awser from the remote Gtalk client, + which allows us to compare capabilities */ +@@ -810,7 +806,7 @@ + goto safeout; + } + +- ast_rtp_get_us(p->rtp, &sin); ++ ast_rtp_instance_get_local_address(p->rtp, &sin); + ast_find_ourip(&us, bindaddr); + if (!strcmp(ast_inet_ntoa(us), "127.0.0.1")) { + ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute."); +@@ -951,8 +947,9 @@ + tmp->initiator = 1; + } + /* clear codecs */ +- tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); +- ast_rtp_pt_clear(tmp->rtp); ++ tmp->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); ++ ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1); ++ ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp); + + /* add user configured codec capabilites */ + if (client->capability) +@@ -1014,20 +1011,20 @@ + + /* Set Frame packetization */ + if (i->rtp) +- ast_rtp_codec_setpref(i->rtp, &i->prefs); ++ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs); + + tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK); + fmt = ast_best_codec(tmp->nativeformats); + + if (i->rtp) { +- ast_rtp_setstun(i->rtp, 1); +- ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); +- ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); ++ ast_rtp_instance_set_prop(i->rtp, AST_RTP_PROPERTY_STUN, 1); ++ ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); ++ ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); + } + if (i->vrtp) { +- ast_rtp_setstun(i->rtp, 1); +- ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); +- ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); ++ ast_rtp_instance_set_prop(i->vrtp, AST_RTP_PROPERTY_STUN, 1); ++ ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); ++ ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); + } + if (state == AST_STATE_RING) + tmp->rings = 1; +@@ -1142,9 +1139,9 @@ + if (p->owner) + ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); + if (p->rtp) +- ast_rtp_destroy(p->rtp); ++ ast_rtp_instance_destroy(p->rtp); + if (p->vrtp) +- ast_rtp_destroy(p->vrtp); ++ ast_rtp_instance_destroy(p->vrtp); + gtalk_free_candidates(p->theircandidates); + ast_free(p); + } +@@ -1204,16 +1201,16 @@ + } + + /* codec points to the first <payload-type/> tag */ +- codec = iks_child(iks_child(iks_child(pak->x))); ++ codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x))); + + while (codec) { +- ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); +- ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); +- codec = iks_next(codec); ++ ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); ++ ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); ++ codec = iks_next_tag(codec); + } + + /* Now gather all of the codecs that we are asked for */ +- ast_rtp_get_current_formats(p->rtp, &p->peercapability, &peernoncodeccapability); ++ ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability); + p->jointcapability = p->capability & p->peercapability; + ast_mutex_unlock(&p->lock); + +@@ -1277,16 +1274,16 @@ + p->ourcandidates->username); + + /* Find out the result of the STUN */ +- ast_rtp_get_peer(p->rtp, &aux); ++ ast_rtp_instance_get_remote_address(p->rtp, &aux); + + /* If the STUN result is different from the IP of the hostname, + lock on the stun IP of the hostname advertised by the + remote client */ + if (aux.sin_addr.s_addr && + aux.sin_addr.s_addr != sin.sin_addr.s_addr) +- ast_rtp_stun_request(p->rtp, &aux, username); ++ ast_rtp_instance_stun_request(p->rtp, &aux, username); + else +- ast_rtp_stun_request(p->rtp, &sin, username); ++ ast_rtp_instance_stun_request(p->rtp, &sin, username); + + if (aux.sin_addr.s_addr) { + ast_debug(4, "Receiving RTP traffic from IP %s, matches with remote candidate's IP %s\n", ast_inet_ntoa(aux.sin_addr), tmp->ip); +@@ -1323,11 +1320,11 @@ + traversenodes = pak->query; + while(traversenodes) { + if(!strcasecmp(iks_name(traversenodes), "session")) { +- traversenodes = iks_child(traversenodes); ++ traversenodes = iks_first_tag(traversenodes); + continue; + } + if(!strcasecmp(iks_name(traversenodes), "transport")) { +- traversenodes = iks_child(traversenodes); ++ traversenodes = iks_first_tag(traversenodes); + continue; + } + if(!strcasecmp(iks_name(traversenodes), "candidate")) { +@@ -1366,7 +1363,7 @@ + gtalk_update_stun(p->parent, p); + newcandidate = NULL; + } +- traversenodes = iks_next(traversenodes); ++ traversenodes = iks_next_tag(traversenodes); + } + + receipt = iks_new("iq"); +@@ -1387,7 +1384,7 @@ + + if (!p->rtp) + return &ast_null_frame; +- f = ast_rtp_read(p->rtp); ++ f = ast_rtp_instance_read(p->rtp, 0); + gtalk_update_stun(p->parent, p); + if (p->owner) { + /* We already hold the channel lock */ +@@ -1438,7 +1435,7 @@ + if (p) { + ast_mutex_lock(&p->lock); + if (p->rtp) { +- res = ast_rtp_write(p->rtp, frame); ++ res = ast_rtp_instance_write(p->rtp, frame); + } + ast_mutex_unlock(&p->lock); + } +@@ -1447,7 +1444,7 @@ + if (p) { + ast_mutex_lock(&p->lock); + if (p->vrtp) { +- res = ast_rtp_write(p->vrtp, frame); ++ res = ast_rtp_instance_write(p->vrtp, frame); + } + ast_mutex_unlock(&p->lock); + } +@@ -2062,7 +2059,7 @@ + return 0; + } + +- ast_rtp_proto_register(>alk_rtp); ++ ast_rtp_glue_register(>alk_rtp_glue); + ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli)); + + /* Make sure we can register our channel type */ +@@ -2086,7 +2083,7 @@ + ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli)); + /* First, take us out of the channel loop */ + ast_channel_unregister(>alk_tech); +- ast_rtp_proto_unregister(>alk_rtp); ++ ast_rtp_glue_unregister(>alk_rtp_glue); + + if (!ast_mutex_lock(>alklock)) { + /* Hangup all interfaces if they have an owner */ +Index: configure.ac +=================================================================== +--- a/configure.ac (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/configure.ac (.../trunk) (revision 186562) +@@ -609,8 +609,6 @@ + AC_MSG_RESULT(no) + ) + +-AST_C_DEFINE_CHECK([RTLD_NOLOAD], [RTLD_NOLOAD], [dlfcn.h]) +- + AST_C_DEFINE_CHECK([IP_MTU_DISCOVER], [IP_MTU_DISCOVER], [netinet/in.h]) + + AC_CHECK_HEADER([libkern/OSAtomic.h], +Index: apps/app_senddtmf.c +=================================================================== +--- a/apps/app_senddtmf.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_senddtmf.c (.../trunk) (revision 186562) +@@ -106,7 +106,7 @@ + astman_send_error(s, m, "Channel not specified"); + return 0; + } +- if (!digit) { ++ if (ast_strlen_zero(digit)) { + astman_send_error(s, m, "No digit specified"); + ast_channel_unlock(chan); + return 0; +Index: apps/app_test.c +=================================================================== +--- a/apps/app_test.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_test.c (.../trunk) (revision 186562) +@@ -132,7 +132,7 @@ + return (noise / samples); + } + +-static int sendnoise(struct ast_channel *chan, int ms) ++static int sendnoise(struct ast_channel *chan, int ms) + { + int res; + res = ast_tonepair_start(chan, 1537, 2195, ms, 8192); +@@ -140,7 +140,7 @@ + res = ast_waitfordigit(chan, ms); + ast_tonepair_stop(chan); + } +- return res; ++ return res; + } + + static int testclient_exec(struct ast_channel *chan, void *data) +@@ -150,41 +150,41 @@ + char fn[80]; + char serverver[80]; + FILE *f; +- ++ + /* Check for test id */ + if (ast_strlen_zero(testid)) { + ast_log(LOG_WARNING, "TestClient requires an argument - the test id\n"); + return -1; + } +- ++ + if (chan->_state != AST_STATE_UP) + res = ast_answer(chan); +- ++ + /* Wait a few just to be sure things get started */ + res = ast_safe_sleep(chan, 3000); + /* Transmit client version */ + if (!res) + res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0); + ast_debug(1, "Transmit client version\n"); +- ++ + /* Read server version */ + ast_debug(1, "Read server version\n"); +- if (!res) ++ if (!res) + res = ast_app_getdata(chan, NULL, serverver, sizeof(serverver) - 1, 0); + if (res > 0) + res = 0; + ast_debug(1, "server version: %s\n", serverver); +- ++ + if (res > 0) + res = 0; + + if (!res) + res = ast_safe_sleep(chan, 1000); + /* Send test id */ +- if (!res) +- res = ast_dtmf_stream(chan, NULL, testid, 0, 0); +- if (!res) +- res = ast_dtmf_stream(chan, NULL, "#", 0, 0); ++ if (!res) ++ res = ast_dtmf_stream(chan, NULL, testid, 0, 0); ++ if (!res) ++ res = ast_dtmf_stream(chan, NULL, "#", 0, 0); + ast_debug(1, "send test identifier: %s\n", testid); + + if ((res >=0) && (!ast_strlen_zero(testid))) { +@@ -198,7 +198,7 @@ + fprintf(f, "CLIENTTEST ID: %s\n", testid); + fprintf(f, "ANSWER: PASS\n"); + res = 0; +- ++ + if (!res) { + /* Step 1: Wait for "1" */ + ast_debug(1, "TestClient: 2. Wait DTMF 1\n"); +@@ -209,8 +209,9 @@ + else + res = -1; + } +- if (!res) ++ if (!res) { + res = ast_safe_sleep(chan, 1000); ++ } + if (!res) { + /* Step 2: Send "2" */ + ast_debug(1, "TestClient: 2. Send DTMF 2\n"); +@@ -226,7 +227,7 @@ + fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS"); + if (res > 0) + res = 0; +- } ++ } + if (!res) { + /* Step 4: Measure noise */ + ast_debug(1, "TestClient: 4. Measure noise\n"); +@@ -272,7 +273,7 @@ + } + if (!res) { + /* Step 9: Measure noise */ +- ast_debug(1, "TestClient: 6. Measure tone\n"); ++ ast_debug(1, "TestClient: 9. Measure tone\n"); + res = measurenoise(chan, 4000, "TestClient"); + fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res); + if (res > 0) +@@ -280,7 +281,7 @@ + } + if (!res) { + /* Step 10: Send "7" */ +- ast_debug(1, "TestClient: 7. Send DTMF 7\n"); ++ ast_debug(1, "TestClient: 10. Send DTMF 7\n"); + res = ast_dtmf_stream(chan, NULL, "7", 0, 0); + fprintf(f, "SEND DTMF 7: %s\n", (res < 0) ? "FAIL" : "PASS"); + if (res > 0) +@@ -297,6 +298,9 @@ + res = -1; + } + if (!res) { ++ res = ast_safe_sleep(chan, 1000); ++ } ++ if (!res) { + /* Step 12: Hangup! */ + ast_debug(1, "TestClient: 12. Hangup\n"); + } +@@ -324,7 +328,7 @@ + res = ast_answer(chan); + /* Read version */ + ast_debug(1, "Read client version\n"); +- if (!res) ++ if (!res) + res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0); + if (res > 0) + res = 0; +@@ -338,8 +342,8 @@ + if (res > 0) + res = 0; + +- if (!res) +- res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0); ++ if (!res) ++ res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0); + ast_debug(1, "read test identifier: %s\n", testid); + /* Check for sneakyness */ + if (strchr(testid, '/')) +@@ -391,7 +395,6 @@ + if (res > 0) + res = 0; + } +- + if (!res) { + /* Step 5: Wait one second */ + ast_debug(1, "TestServer: 5. Wait one second\n"); +@@ -400,7 +403,6 @@ + if (res > 0) + res = 0; + } +- + if (!res) { + /* Step 6: Measure noise */ + ast_debug(1, "TestServer: 6. Measure tone\n"); +@@ -409,7 +411,6 @@ + if (res > 0) + res = 0; + } +- + if (!res) { + /* Step 7: Send "5" */ + ast_debug(1, "TestServer: 7. Send DTMF 5\n"); +@@ -418,14 +419,13 @@ + if (res > 0) + res = 0; + } +- + if (!res) { + /* Step 8: Transmit tone noise */ + ast_debug(1, "TestServer: 8. Transmit tone\n"); + res = sendnoise(chan, 6000); + fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS"); + } +- ++ + if (!res || (res == '7')) { + /* Step 9: Wait for "7" */ + ast_debug(1, "TestServer: 9. Wait DTMF 7\n"); +@@ -437,8 +437,9 @@ + else + res = -1; + } +- if (!res) ++ if (!res) { + res = ast_safe_sleep(chan, 1000); ++ } + if (!res) { + /* Step 10: Send "8" */ + ast_debug(1, "TestServer: 10. Send DTMF 8\n"); +@@ -474,7 +475,7 @@ + res = ast_unregister_application(testc_app); + res |= ast_unregister_application(tests_app); + +- return res; ++ return res; + } + + static int load_module(void) +Index: apps/app_ices.c +=================================================================== +--- a/apps/app_ices.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_ices.c (.../trunk) (revision 186562) +@@ -58,7 +58,7 @@ + <description> + <para>Streams to an icecast server using ices (available separately). + A configuration file must be supplied for ices (see contrib/asterisk-ices.xml).</para> +- <note><para>ICES version 2 cient and server required.</para></note> ++ <note><para>ICES version 2 client and server required.</para></note> + </description> + </application> + +Index: apps/app_directed_pickup.c +=================================================================== +--- a/apps/app_directed_pickup.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_directed_pickup.c (.../trunk) (revision 186562) +@@ -40,6 +40,7 @@ + #include "asterisk/lock.h" + #include "asterisk/app.h" + #include "asterisk/features.h" ++#include "asterisk/callerid.h" + + #define PICKUPMARK "PICKUPMARK" + +@@ -91,9 +92,21 @@ + static int pickup_do(struct ast_channel *chan, struct ast_channel *target) + { + int res = 0; ++ struct ast_party_connected_line connected_caller; + + ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name); + ++ connected_caller = target->connected; ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_update_connected_line(chan, &connected_caller); ++ ++ ast_channel_lock(chan); ++ ast_connected_line_copy_from_caller(&connected_caller, &chan->cid); ++ ast_channel_unlock(chan); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_queue_connected_line_update(chan, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ + if ((res = ast_answer(chan))) { + ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); + return -1; +Index: apps/app_minivm.c +=================================================================== +--- a/apps/app_minivm.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_minivm.c (.../trunk) (revision 186562) +@@ -1177,7 +1177,7 @@ + } + ast_debug(4, "Fromstring now: %s\n", ast_strlen_zero(passdata) ? "-default-" : passdata); + +- fprintf(p, "Message-ID: <Asterisk-%d-%s-%d-%s>\n", (unsigned int)rand(), vmu->username, (int)getpid(), who); ++ fprintf(p, "Message-ID: <Asterisk-%d-%s-%d-%s>\n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who); + len_passdata = strlen(vmu->fullname) * 2 + 3; + passdata2 = alloca(len_passdata); + if (!ast_strlen_zero(vmu->email)) +@@ -1210,7 +1210,7 @@ + fprintf(p, "MIME-Version: 1.0\n"); + + /* Something unique. */ +- snprintf(bound, sizeof(bound), "voicemail_%s%d%d", vmu->username, (int)getpid(), (unsigned int)rand()); ++ snprintf(bound, sizeof(bound), "voicemail_%s%d%d", vmu->username, (int)getpid(), (unsigned int)ast_random()); + + fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound); + +@@ -1785,13 +1785,10 @@ + AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent), + AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old, + AST_EVENT_IE_END))) { +- return; ++ return; + } + +- ast_event_queue_and_cache(event, +- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_END); ++ ast_event_queue_and_cache(event); + } + + /*! \brief Send MWI using interal Asterisk event subsystem */ +Index: apps/app_dumpchan.c +=================================================================== +--- a/apps/app_dumpchan.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_dumpchan.c (.../trunk) (revision 186562) +@@ -149,7 +149,7 @@ + + static int dumpchan_exec(struct ast_channel *chan, void *data) + { +- struct ast_str *vars = ast_str_thread_get(&global_app_buf, 16); ++ struct ast_str *vars = ast_str_thread_get(&ast_str_thread_global_buf, 16); + char info[1024]; + int level = 0; + static char *line = "================================================================================"; +Index: apps/app_voicemail.c +=================================================================== +--- a/apps/app_voicemail.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_voicemail.c (.../trunk) (revision 186562) +@@ -325,7 +325,7 @@ + static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len); + static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu); + static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len); +-static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive); ++static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive); + static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive); + static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu); + static void vmstate_insert(struct vm_state *vms); +@@ -518,11 +518,6 @@ + Spanish also uses: + \arg \b vm-youhaveno + +-Ukrainian requires the following additional soundfile: +-\arg \b vm-nove 'nove' +-\arg \b vm-stare 'stare' +-\arg \b digits/ua/1e 'odne' +- + Italian requires the following additional soundfile: + + For vm_intro_it: +@@ -809,7 +804,7 @@ + static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain); + static int vm_play_folder_name(struct ast_channel *chan, char *mbox); + static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag); +-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag); ++static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag); + static void apply_options(struct ast_vm_user *vmu, const char *options); + static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum); + static int is_valid_dtmf(const char *key); +@@ -1916,9 +1911,9 @@ + } + imap_delete_old_greeting(fn, vms); + } +- +- make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag); +- /* read mail file to memory */ ++ ++ make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX", S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag); ++ /* read mail file to memory */ + len = ftell(p); + rewind(p); + if (!(buf = ast_malloc(len + 1))) { +@@ -2299,22 +2294,14 @@ + + static void update_messages_by_imapuser(const char *user, unsigned long number) + { +- struct vmstate *vlist = NULL; ++ struct vm_state *vms = get_vm_state_by_imapuser(user, 1); + +- AST_LIST_LOCK(&vmstates); +- AST_LIST_TRAVERSE(&vmstates, vlist, list) { +- if (!vlist->vms) { +- ast_debug(3, "error: vms is NULL for %s\n", user); +- continue; +- } +- if (!vlist->vms->imapuser) { +- ast_debug(3, "error: imapuser is NULL for %s\n", user); +- continue; +- } +- ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vlist->vms->vmArrayIndex, vlist->vms->interactive); +- vlist->vms->msgArray[vlist->vms->vmArrayIndex++] = number; ++ if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) { ++ return; + } +- AST_LIST_UNLOCK(&vmstates); ++ ++ ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive); ++ vms->msgArray[vms->vmArrayIndex++] = number; + } + + void mm_searched(MAILSTREAM *stream, unsigned long number) +@@ -2605,7 +2592,7 @@ + return vms_p; + } + +-static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive) ++static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive) + { + struct vmstate *vlist = NULL; + +@@ -3856,9 +3843,16 @@ + return 1; + } + +-static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag) ++static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag) + { + char callerid[256]; ++ char fromdir[256], fromfile[256]; ++ struct ast_config *msg_cfg; ++ const char *origcallerid, *origtime; ++ char origcidname[80], origcidnum[80], origdate[80]; ++ int inttime; ++ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE }; ++ + /* Prepare variables for substitution in email body and subject */ + pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname); + pbx_builtin_setvar_helper(ast, "VM_DUR", dur); +@@ -3873,6 +3867,35 @@ + pbx_builtin_setvar_helper(ast, "VM_DATE", date); + pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category"); + pbx_builtin_setvar_helper(ast, "VM_FLAG", flag); ++ ++ /* Retrieve info from VM attribute file */ ++ make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder); ++ make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1); ++ if (strlen(fromfile) < sizeof(fromfile) - 5) { ++ strcat(fromfile, ".txt"); ++ } ++ if (!(msg_cfg = ast_config_load(fromfile, config_flags))) { ++ if (option_debug > 0) { ++ ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile); ++ } ++ return; ++ } ++ ++ if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) { ++ pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid); ++ ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum)); ++ pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname); ++ pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum); ++ } ++ ++ if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%d", &inttime) == 1) { ++ struct timeval tv = { inttime, }; ++ struct ast_tm tm; ++ ast_localtime(&tv, &tm, NULL); ++ ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm); ++ pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate); ++ } ++ ast_config_destroy(msg_cfg); + } + + /*! +@@ -4006,7 +4029,7 @@ + * + * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function. + */ +-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag) ++static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag) + { + char date[256]; + char host[MAXHOSTNAMELEN] = ""; +@@ -4066,7 +4089,7 @@ + if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { + char *ptr; + memset(passdata2, 0, len_passdata2); +- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag); ++ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag); + pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2); + len_passdata = strlen(passdata2) * 3 + 300; + passdata = alloca(len_passdata); +@@ -4117,7 +4140,7 @@ + } + + memset(passdata, 0, len_passdata); +- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, len_passdata, category, flag); ++ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag); + pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata); + if (check_mime(passdata)) { + int first_line = 1; +@@ -4203,17 +4226,57 @@ + int vmlen = strlen(e_body) * 3 + 200; + passdata = alloca(vmlen); + memset(passdata, 0, vmlen); +- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag); ++ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); + pbx_substitute_variables_helper(ast, e_body, passdata, vmlen); + fprintf(p, "%s" ENDL, passdata); + ast_channel_free(ast); + } else + ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n"); +- } else if (msgnum > -1){ +- fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long %s message (number %d)" ENDL +- "in mailbox %s from %s, on %s so you might" ENDL +- "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, +- dur, flag, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date); ++ } else if (msgnum > -1) { ++ if (strcmp(vmu->mailbox, mailbox)) { ++ /* Forwarded type */ ++ struct ast_config *msg_cfg; ++ const char *v; ++ int inttime; ++ char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = ""; ++ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE }; ++ /* Retrieve info from VM attribute file */ ++ make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder); ++ make_file(fromfile, sizeof(fromfile), fromdir, msgnum); ++ if (strlen(fromfile) < sizeof(fromfile) - 5) { ++ strcat(fromfile, ".txt"); ++ } ++ if ((msg_cfg = ast_config_load(fromfile, config_flags))) { ++ if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) { ++ ast_copy_string(origcallerid, v, sizeof(origcallerid)); ++ } ++ ++ /* You might be tempted to do origdate, except that a) it's in the wrong ++ * format, and b) it's missing for IMAP recordings. */ ++ if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%d", &inttime) == 1) { ++ struct timeval tv = { inttime, }; ++ struct ast_tm tm; ++ ast_localtime(&tv, &tm, NULL); ++ ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm); ++ } ++ fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded" ++ " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL ++ "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a" ++ " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur, ++ msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), ++ date, origcallerid, origdate); ++ ast_config_destroy(msg_cfg); ++ } else { ++ goto plain_message; ++ } ++ } else { ++plain_message: ++ fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a " ++ "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL ++ "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ++ ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox, ++ (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date); ++ } + } else { + fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL + "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date); +@@ -4288,7 +4351,7 @@ + } + #undef ENDL + +-static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag) ++static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag) + { + FILE *p=NULL; + char tmp[80] = "/tmp/astmail-XXXXXX"; +@@ -4307,7 +4370,7 @@ + ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd); + return -1; + } else { +- make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag); ++ make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag); + fclose(p); + snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp); + ast_safe_system(tmp2); +@@ -4316,7 +4379,7 @@ + return 0; + } + +-static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag) ++static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag) + { + char date[256]; + char host[MAXHOSTNAMELEN] = ""; +@@ -4347,7 +4410,7 @@ + int vmlen = strlen(fromstring)*3 + 200; + passdata = alloca(vmlen); + memset(passdata, 0, vmlen); +- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag); ++ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); + pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen); + fprintf(p, "From: %s <%s>\n", passdata, who); + ast_channel_free(ast); +@@ -4363,7 +4426,7 @@ + int vmlen = strlen(pagersubject) * 3 + 200; + passdata = alloca(vmlen); + memset(passdata, 0, vmlen); +- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag); ++ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); + pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen); + fprintf(p, "Subject: %s\n\n", passdata); + ast_channel_free(ast); +@@ -4385,7 +4448,7 @@ + int vmlen = strlen(pagerbody) * 3 + 200; + passdata = alloca(vmlen); + memset(passdata, 0, vmlen); +- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag); ++ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); + pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen); + fprintf(p, "%s\n", passdata); + ast_channel_free(ast); +@@ -6215,10 +6278,7 @@ + return; + } + +- ast_event_queue_and_cache(event, +- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_END); ++ ast_event_queue_and_cache(event); + } + + /*! +@@ -6275,14 +6335,15 @@ + RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context); + + /* XXX possible imap issue, should category be NULL XXX */ +- sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag); ++ sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag); + + if (attach_user_voicemail) + DISPOSE(todir, msgnum); + } + +- if (!ast_strlen_zero(vmu->pager)) +- sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category, flag); ++ if (!ast_strlen_zero(vmu->pager)) { ++ sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag); ++ } + + if (ast_test_flag(vmu, VM_DELETE)) + DELETE(todir, msgnum, fn, vmu); +@@ -6462,6 +6523,7 @@ + while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) { + free_user(receiver); + } ++ ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s); + valid_extensions = 0; + break; + } +@@ -6535,7 +6597,7 @@ + myserveremail = vmtmp->serveremail; + attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH); + /* NULL category for IMAP storage */ +- sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan, NULL, urgent_str); ++ sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, dstvms->curbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan, NULL, urgent_str); + #else + copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str); + #endif +@@ -7323,7 +7385,8 @@ + if (!res) { + if (lastnum == 0) { + res = ast_play_and_wait(chan, "vm-no"); +- } else { ++ } ++ if (!res) { + res = ast_say_counted_noun(chan, lastnum, "vm-message"); + } + } +@@ -10270,7 +10333,7 @@ + char *current; + + /* Add 16 for fudge factor */ +- struct ast_str *str = ast_str_thread_get(&global_app_buf, strlen(value) + 16); ++ struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16); + + ast_str_reset(str); + +Index: apps/app_dial.c +=================================================================== +--- a/apps/app_dial.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_dial.c (.../trunk) (revision 186562) +@@ -54,7 +54,7 @@ + #include "asterisk/utils.h" + #include "asterisk/app.h" + #include "asterisk/causes.h" +-#include "asterisk/rtp.h" ++#include "asterisk/rtp_engine.h" + #include "asterisk/cdr.h" + #include "asterisk/manager.h" + #include "asterisk/privacy.h" +@@ -109,11 +109,13 @@ + <option name="D" argsep=":"> + <argument name="called" /> + <argument name="calling" /> ++ <argument name="progress" /> + <para>Send the specified DTMF strings <emphasis>after</emphasis> the called + party has answered, but before the call gets bridged. The + <replaceable>called</replaceable> DTMF string is sent to the called party, and the + <replaceable>calling</replaceable> DTMF string is sent to the calling party. Both arguments +- can be used alone.</para> ++ can be used alone. If <replaceable>progress</replaceable> is specified, its DTMF is sent ++ immediately after receiving a PROGRESS message.</para> + </option> + <option name="e"> + <para>Execute the <literal>h</literal> extension for peer after the call ends</para> +@@ -155,6 +157,10 @@ + <option name="i"> + <para>Asterisk will ignore any forwarding requests it may receive on this dial attempt.</para> + </option> ++ <option name="I"> ++ <para>Asterisk will ignore any connected line update requests or redirecting party update ++ requests it may receiveon this dial attempt.</para> ++ </option> + <option name="k"> + <para>Allow the called party to enable parking of the call by sending + the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para> +@@ -380,7 +386,6 @@ + This application will report normal termination if the originating channel + hangs up, or if the call is bridged and either of the parties in the bridge + ends the call.</para> +- + <para>If the <variable>OUTBOUND_GROUP</variable> variable is set, all peer channels created by this + application will be put into that group (as in Set(GROUP()=...). + If the <variable>OUTBOUND_GROUP_ONCE</variable> variable is set, all peer channels created by this +@@ -462,12 +467,13 @@ + OPT_GO_ON = (1 << 5), + OPT_CALLEE_HANGUP = (1 << 6), + OPT_CALLER_HANGUP = (1 << 7), ++ OPT_ORIGINAL_CLID = (1 << 8), + OPT_DURATION_LIMIT = (1 << 9), + OPT_MUSICBACK = (1 << 10), + OPT_CALLEE_MACRO = (1 << 11), + OPT_SCREEN_NOINTRO = (1 << 12), +- OPT_SCREEN_NOCLID = (1 << 13), +- OPT_ORIGINAL_CLID = (1 << 14), ++ OPT_SCREEN_NOCALLERID = (1 << 13), ++ OPT_IGNORE_CONNECTEDLINE = (1 << 14), + OPT_SCREENING = (1 << 15), + OPT_PRIVACY = (1 << 16), + OPT_RINGBACK = (1 << 17), +@@ -488,9 +494,10 @@ + + #define DIAL_STILLGOING (1 << 31) + #define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */ +-#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33) +-#define OPT_PEER_H ((uint64_t)1 << 34) +-#define OPT_CALLEE_GO_ON ((uint64_t)1 << 35) ++#define DIAL_NOCONNECTEDLINE ((uint64_t)1 << 33) ++#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 34) ++#define OPT_PEER_H ((uint64_t)1 << 35) ++#define OPT_CALLEE_GO_ON ((uint64_t)1 << 36) + + enum { + OPT_ARG_ANNOUNCE = 0, +@@ -522,13 +529,14 @@ + AST_APP_OPTION('h', OPT_CALLEE_HANGUP), + AST_APP_OPTION('H', OPT_CALLER_HANGUP), + AST_APP_OPTION('i', OPT_IGNORE_FORWARDING), ++ AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE), + AST_APP_OPTION('k', OPT_CALLEE_PARK), + AST_APP_OPTION('K', OPT_CALLER_PARK), + AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT), + AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK), + AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO), + AST_APP_OPTION('n', OPT_SCREEN_NOINTRO), +- AST_APP_OPTION('N', OPT_SCREEN_NOCLID), ++ AST_APP_OPTION('N', OPT_SCREEN_NOCALLERID), + AST_APP_OPTION('o', OPT_ORIGINAL_CLID), + AST_APP_OPTION_ARG('O', OPT_OPERMODE, OPT_ARG_OPERMODE), + AST_APP_OPTION('p', OPT_SCREENING), +@@ -556,8 +564,10 @@ + struct chanlist *next; + struct ast_channel *chan; + uint64_t flags; ++ struct ast_party_connected_line connected; + }; + ++static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode); + + static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere) + { +@@ -651,7 +661,6 @@ + return 0; + } + +- + static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan) + { + const char *context = S_OR(chan->macrocontext, chan->context); +@@ -699,6 +708,8 @@ + struct ast_channel *original = o->chan; + struct ast_channel *c = o->chan; /* the winner */ + struct ast_channel *in = num->chan; /* the input channel */ ++ struct ast_party_redirecting *apr = &o->chan->redirecting; ++ struct ast_party_connected_line *apc = &o->chan->connected; + char *stuff; + char *tech; + int cause; +@@ -739,28 +750,38 @@ + handle_cause(cause, num); + ast_hangup(original); + } else { +- char *new_cid_num, *new_cid_name; +- struct ast_channel *src; ++ if (single) { ++ ast_rtp_instance_early_bridge_make_compatible(c, in); ++ } + +- ast_rtp_make_compatible(c, in, single); ++ c->cdrflags = in->cdrflags; ++ ++ ast_channel_set_redirecting(c, apr); ++ ast_channel_lock(c); ++ while (ast_channel_trylock(in)) { ++ CHANNEL_DEADLOCK_AVOIDANCE(c); ++ } ++ S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(original->cid.cid_rdnis, S_OR(in->macroexten, in->exten)))); ++ ++ c->cid.cid_tns = in->cid.cid_tns; ++ + if (ast_test_flag64(o, OPT_FORCECLID)) { +- new_cid_num = ast_strdup(S_OR(in->macroexten, in->exten)); +- new_cid_name = NULL; /* XXX no name ? */ +- src = c; /* XXX possible bug in previous code, which used 'winner' ? it may have changed */ ++ S_REPLACE(c->cid.cid_num, ast_strdupa(S_OR(in->macroexten, in->exten))); ++ S_REPLACE(c->cid.cid_name, NULL); ++ ast_string_field_set(c, accountcode, c->accountcode); + } else { +- new_cid_num = ast_strdup(in->cid.cid_num); +- new_cid_name = ast_strdup(in->cid.cid_name); +- src = in; ++ ast_party_caller_copy(&c->cid, &in->cid); ++ ast_string_field_set(c, accountcode, in->accountcode); + } +- ast_string_field_set(c, accountcode, src->accountcode); +- c->cdrflags = src->cdrflags; +- S_REPLACE(c->cid.cid_num, new_cid_num); +- S_REPLACE(c->cid.cid_name, new_cid_name); ++ ast_party_connected_line_copy(&c->connected, apc); + +- if (in->cid.cid_ani) { /* XXX or maybe unconditional ? */ +- S_REPLACE(c->cid.cid_ani, ast_strdup(in->cid.cid_ani)); +- } +- S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(in->macroexten, in->exten))); ++ S_REPLACE(in->cid.cid_rdnis, ast_strdup(c->cid.cid_rdnis)); ++ ast_channel_unlock(in); ++ ast_channel_unlock(c); ++ ast_channel_update_redirecting(in, apr); ++ ++ ast_clear_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE); ++ + if (ast_call(c, tmpchan, 0)) { + ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); + ast_clear_flag64(o, DIAL_STILLGOING); +@@ -770,7 +791,6 @@ + num->nochan++; + } else { + senddialevent(in, c, stuff); +- /* After calling, set callerid to extension */ + if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { + char cidname[AST_MAX_EXTENSION] = ""; + ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL); +@@ -796,23 +816,35 @@ + static struct ast_channel *wait_for_answer(struct ast_channel *in, + struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags, + struct privacy_args *pa, +- const struct cause_args *num_in, int *result) ++ const struct cause_args *num_in, int *result, char *dtmf_progress) + { + struct cause_args num = *num_in; + int prestart = num.busy + num.congestion + num.nochan; + int orig = *to; + struct ast_channel *peer = NULL; + /* single is set if only one destination is enabled */ +- int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK); ++ int single = outgoing && !outgoing->next; + #ifdef HAVE_EPOLL + struct chanlist *epollo; + #endif +- ++ struct ast_party_connected_line connected_caller; ++ struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1); + if (single) { + /* Turn off hold music, etc */ +- ast_deactivate_generator(in); ++ if (!ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK)) ++ ast_deactivate_generator(in); ++ + /* If we are calling a single channel, make them compatible for in-band tone purpose */ + ast_channel_make_compatible(outgoing->chan, in); ++ ++ if (!ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE) && !ast_test_flag64(outgoing, DIAL_NOCONNECTEDLINE)) { ++ ast_channel_lock(outgoing->chan); ++ ast_connected_line_copy_from_caller(&connected_caller, &outgoing->chan->cid); ++ ast_channel_unlock(outgoing->chan); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_update_connected_line(in, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ } + } + + #ifdef HAVE_EPOLL +@@ -859,6 +891,18 @@ + if (ast_test_flag64(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) { + if (!peer) { + ast_verb(3, "%s answered %s\n", c->name, in->name); ++ if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { ++ if (o->connected.id.number) { ++ ast_channel_update_connected_line(in, &o->connected); ++ } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) { ++ ast_channel_lock(c); ++ ast_connected_line_copy_from_caller(&connected_caller, &c->cid); ++ ast_channel_unlock(c); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_update_connected_line(in, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ } ++ } + peer = c; + ast_copy_flags64(peerflags, o, + OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | +@@ -897,6 +941,18 @@ + /* This is our guy if someone answered. */ + if (!peer) { + ast_verb(3, "%s answered %s\n", c->name, in->name); ++ if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { ++ if (o->connected.id.number) { ++ ast_channel_update_connected_line(in, &o->connected); ++ } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) { ++ ast_channel_lock(c); ++ ast_connected_line_copy_from_caller(&connected_caller, &c->cid); ++ ast_channel_unlock(c); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_update_connected_line(in, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ } ++ } + peer = c; + if (peer->cdr) { + peer->cdr->answer = ast_tvnow(); +@@ -952,6 +1008,10 @@ + ast_channel_early_bridge(in, c); + if (!ast_test_flag64(outgoing, OPT_RINGBACK)) + ast_indicate(in, AST_CONTROL_PROGRESS); ++ if(!ast_strlen_zero(dtmf_progress)) { ++ ast_verb(3, "Sending DTMF '%s' to the called party as result of receiving a PROGRESS message.\n", dtmf_progress); ++ ast_dtmf_stream(c, in, dtmf_progress, 250, 0); ++ } + break; + case AST_CONTROL_VIDUPDATE: + ast_verb(3, "%s requested a video update, passing it to %s\n", c->name, in->name); +@@ -961,6 +1021,29 @@ + ast_verb(3, "%s requested a source update, passing it to %s\n", c->name, in->name); + ast_indicate(in, AST_CONTROL_SRCUPDATE); + break; ++ case AST_CONTROL_CONNECTED_LINE: ++ if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { ++ ast_verb(3, "Connected line update to %s prevented.\n", in->name); ++ } else if (!single) { ++ struct ast_party_connected_line connected; ++ ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", c->name, in->name); ++ ast_party_connected_line_set_init(&connected, &o->connected); ++ ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); ++ ast_party_connected_line_set(&o->connected, &connected); ++ ast_party_connected_line_free(&connected); ++ } else { ++ ast_verb(3, "%s connected line has changed, passing it to %s\n", c->name, in->name); ++ ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); ++ } ++ break; ++ case AST_CONTROL_REDIRECTING: ++ if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { ++ ast_verb(3, "Redirecting update to %s prevented.\n", in->name); ++ } else { ++ ast_verb(3, "%s redirecting info has changed, passing it to %s\n", c->name, in->name); ++ ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); ++ } ++ break; + case AST_CONTROL_PROCEEDING: + ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name); + if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) +@@ -1052,8 +1135,8 @@ + } + + if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) && +- (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */ +- ast_verb(3, "User hit %c to disconnect call.\n", f->subclass); ++ detect_disconnect(in, f->subclass, featurecode)) { ++ ast_verb(3, "User requested call disconnect.\n"); + *to = 0; + strcpy(pa->status, "CANCEL"); + ast_cdr_noanswer(in->cdr); +@@ -1075,7 +1158,9 @@ + ((f->subclass == AST_CONTROL_HOLD) || + (f->subclass == AST_CONTROL_UNHOLD) || + (f->subclass == AST_CONTROL_VIDUPDATE) || +- (f->subclass == AST_CONTROL_SRCUPDATE))) { ++ (f->subclass == AST_CONTROL_SRCUPDATE) || ++ (f->subclass == AST_CONTROL_CONNECTED_LINE) || ++ (f->subclass == AST_CONTROL_REDIRECTING))) { + ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name); + ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen); + } +@@ -1097,6 +1182,26 @@ + return peer; + } + ++static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode) ++{ ++ struct ast_flags features = { AST_FEATURE_DISCONNECT }; /* only concerned with disconnect feature */ ++ struct ast_call_feature feature = { 0, }; ++ int res; ++ ++ ast_str_append(&featurecode, 1, "%c", code); ++ ++ res = ast_feature_detect(chan, &features, ast_str_buffer(featurecode), &feature); ++ ++ if (res != AST_FEATURE_RETURN_STOREDIGITS) { ++ ast_str_reset(featurecode); ++ } ++ if (feature.feature_mask & AST_FEATURE_DISCONNECT) { ++ return 1; ++ } ++ ++ return 0; ++} ++ + static void replace_macro_delimiter(char *s) + { + for (; *s; s++) +@@ -1394,11 +1499,11 @@ + + ast_copy_string(pa->privcid, l, sizeof(pa->privcid)); + +- if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCLID)) { +- /* if callerid is set and OPT_SCREEN_NOCLID is set also */ ++ if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCALLERID)) { ++ /* if callerid is set and OPT_SCREEN_NOCALLERID is set also */ + ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid); + pa->privdb_val = AST_PRIVACY_ALLOW; +- } else if (ast_test_flag64(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { ++ } else if (ast_test_flag64(opts, OPT_SCREEN_NOCALLERID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { + ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val); + } + +@@ -1502,7 +1607,7 @@ + + struct ast_bridge_config config = { { 0, } }; + struct timeval calldurationlimit = { 0, }; +- char *dtmfcalled = NULL, *dtmfcalling = NULL; ++ char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress=NULL; + struct privacy_args pa = { + .sentringing = 0, + .privdb_val = 0, +@@ -1553,12 +1658,11 @@ + goto done; + } + +- + if (ast_test_flag64(&opts, OPT_OPERMODE)) { + opermode = ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]) ? 1 : atoi(opt_args[OPT_ARG_OPERMODE]); + ast_verb(3, "Setting operator services mode to %d.\n", opermode); + } +- ++ + if (ast_test_flag64(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) { + calldurationlimit.tv_sec = atoi(opt_args[OPT_ARG_DURATION_STOP]); + if (!calldurationlimit.tv_sec) { +@@ -1570,8 +1674,9 @@ + } + + if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) { +- dtmfcalling = opt_args[OPT_ARG_SENDDTMF]; +- dtmfcalled = strsep(&dtmfcalling, ":"); ++ dtmf_progress = opt_args[OPT_ARG_SENDDTMF]; ++ dtmfcalled = strsep(&dtmf_progress, ":"); ++ dtmfcalling = strsep(&dtmf_progress, ":"); + } + + if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) { +@@ -1591,7 +1696,7 @@ + res = -1; /* reset default */ + } + +- if (ast_test_flag64(&opts, OPT_DTMF_EXIT)) { ++ if (ast_test_flag64(&opts, OPT_DTMF_EXIT) || ast_test_flag64(&opts, OPT_CALLER_HANGUP)) { + __ast_answer(chan, 0, 0); + } + +@@ -1608,7 +1713,7 @@ + outbound_group = ast_strdupa(outbound_group); + } + ast_channel_unlock(chan); +- ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING); ++ ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_IGNORE_CONNECTEDLINE); + + /* loop through the list of dial destinations */ + rest = args.peers; +@@ -1645,6 +1750,14 @@ + + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); ++ /* If the incoming channel has previously had connected line information ++ * set on it (perhaps through the CONNECTED_LINE dialplan function) then ++ * seed the calllist's connected line information with this previously ++ * acquired info ++ */ ++ if (chan->connected.id.number) { ++ ast_party_connected_line_copy(&tmp->connected, &chan->connected); ++ } + ast_channel_unlock(chan); + + if (datastore) +@@ -1717,8 +1830,14 @@ + } + pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst); + ++ ast_channel_lock(tc); ++ while (ast_channel_trylock(chan)) { ++ CHANNEL_DEADLOCK_AVOIDANCE(tc); ++ } + /* Setup outgoing SDP to match incoming one */ +- ast_rtp_make_compatible(tc, chan, !outgoing && !rest); ++ if (!outgoing && !rest) { ++ ast_rtp_instance_early_bridge_make_compatible(tc, chan); ++ } + + /* Inherit specially named variables from parent channel */ + ast_channel_inherit_variables(chan, tc); +@@ -1728,20 +1847,31 @@ + tc->data = "(Outgoing Line)"; + memset(&tc->whentohangup, 0, sizeof(tc->whentohangup)); + +- S_REPLACE(tc->cid.cid_num, ast_strdup(chan->cid.cid_num)); +- S_REPLACE(tc->cid.cid_name, ast_strdup(chan->cid.cid_name)); +- S_REPLACE(tc->cid.cid_ani, ast_strdup(chan->cid.cid_ani)); ++ /* If the new channel has no callerid, try to guess what it should be */ ++ if (ast_strlen_zero(tc->cid.cid_num)) { ++ if (!ast_strlen_zero(chan->connected.id.number)) { ++ ast_set_callerid(tc, chan->connected.id.number, chan->connected.id.name, chan->connected.ani); ++ } else if (!ast_strlen_zero(chan->cid.cid_dnid)) { ++ ast_set_callerid(tc, chan->cid.cid_dnid, NULL, NULL); ++ } else if (!ast_strlen_zero(S_OR(chan->macroexten, chan->exten))) { ++ ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), NULL, NULL); ++ } ++ ast_set_flag64(tmp, DIAL_NOCONNECTEDLINE); ++ } ++ ++ ast_connected_line_copy_from_caller(&tc->connected, &chan->cid); ++ + S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis)); +- ++ ast_party_redirecting_copy(&tc->redirecting, &chan->redirecting); ++ ++ tc->cid.cid_tns = chan->cid.cid_tns; ++ + ast_string_field_set(tc, accountcode, chan->accountcode); + tc->cdrflags = chan->cdrflags; + if (ast_strlen_zero(tc->musicclass)) + ast_string_field_set(tc, musicclass, chan->musicclass); +- /* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */ +- tc->cid.cid_pres = chan->cid.cid_pres; +- tc->cid.cid_ton = chan->cid.cid_ton; +- tc->cid.cid_tns = chan->cid.cid_tns; +- tc->cid.cid_ani2 = chan->cid.cid_ani2; ++ ++ /* Pass ADSI CPE and transfer capability */ + tc->adsicpe = chan->adsicpe; + tc->transfercapability = chan->transfercapability; + +@@ -1778,6 +1908,8 @@ + if (tc->hangupcause) { + chan->hangupcause = tc->hangupcause; + } ++ ast_channel_unlock(chan); ++ ast_channel_unlock(tc); + ast_hangup(tc); + tc = NULL; + ast_free(tmp); +@@ -1785,8 +1917,11 @@ + } else { + senddialevent(chan, tc, numsubst); + ast_verb(3, "Called %s\n", numsubst); +- if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) ++ if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { + ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL); ++ } ++ ast_channel_unlock(chan); ++ ast_channel_unlock(tc); + } + /* Put them in the list of outgoing thingies... We're ready now. + XXX If we're forcibly removed, these outgoing calls won't get +@@ -1838,7 +1973,7 @@ + } + } + +- peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result); ++ peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result, dtmf_progress); + + /* The ast_channel_datastore_remove() function could fail here if the + * datastore was moved to another channel during a masquerade. If this is +Index: apps/app_queue.c +=================================================================== +--- a/apps/app_queue.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_queue.c (.../trunk) (revision 186562) +@@ -94,6 +94,7 @@ + #include "asterisk/strings.h" + #include "asterisk/global_datastores.h" + #include "asterisk/taskprocessor.h" ++#include "asterisk/callerid.h" + + /*! + * \par Please read before modifying this file. +@@ -141,6 +142,10 @@ + <para>Ignore call forward requests from queue members and do nothing + when they are requested.</para> + </option> ++ <option name="I"> ++ <para>Asterisk will ignore any connected line update requests or any redirecting party ++ update requests it may receive on this dial attempt.</para> ++ </option> + <option name="r"> + <para>Ring instead of playing MOH. Periodic Announcements are still made, if applicable.</para> + </option> +@@ -625,6 +630,8 @@ + time_t lastcall; + struct call_queue *lastqueue; + struct member *member; ++ unsigned int update_connectedline:1; ++ struct ast_party_connected_line connected; + }; + + +@@ -1630,7 +1637,7 @@ + + if ((q = ao2_alloc(sizeof(*q), destroy_queue))) { + if (ast_string_field_init(q, 64)) { +- free(q); ++ ao2_ref(q, -1); + return NULL; + } + ast_string_field_set(q, name, queuename); +@@ -2268,11 +2275,56 @@ + } + } + +-/*! +- * \brief traverse all defined queues which have calls waiting and contain this member +- * \retval 0 if no other queue has precedence (higher weight) +- * \retval 1 if found +-*/ ++/*! ++ * \brief Get the number of members available to accept a call. ++ * ++ * \note The queue passed in should be locked prior to this function call ++ * ++ * \param[in] q The queue for which we are couting the number of available members ++ * \return Return the number of available members in queue q ++ */ ++static int num_available_members(struct call_queue *q) ++{ ++ struct member *mem; ++ int avl = 0; ++ struct ao2_iterator mem_iter; ++ ++ mem_iter = ao2_iterator_init(q->members, 0); ++ while ((mem = ao2_iterator_next(&mem_iter))) { ++ switch (mem->status) { ++ case AST_DEVICE_INUSE: ++ if (!q->ringinuse) ++ break; ++ /* else fall through */ ++ case AST_DEVICE_NOT_INUSE: ++ case AST_DEVICE_UNKNOWN: ++ if (!mem->paused) { ++ avl++; ++ } ++ break; ++ } ++ ao2_ref(mem, -1); ++ ++ /* If autofill is not enabled or if the queue's strategy is ringall, then ++ * we really don't care about the number of available members so much as we ++ * do that there is at least one available. ++ * ++ * In fact, we purposely will return from this function stating that only ++ * one member is available if either of those conditions hold. That way, ++ * functions which determine what action to take based on the number of available ++ * members will operate properly. The reasoning is that even if multiple ++ * members are available, only the head caller can actually be serviced. ++ */ ++ if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { ++ break; ++ } ++ } ++ ++ return avl; ++} ++ ++/* traverse all defined queues which have calls waiting and contain this member ++ return 0 if no other queue has precedence (higher weight) or 1 if found */ + static int compare_weight(struct call_queue *rq, struct member *member) + { + struct call_queue *q; +@@ -2292,7 +2344,7 @@ + if (q->count && q->members) { + if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { + ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name); +- if (q->weight > rq->weight) { ++ if (q->weight > rq->weight && q->count >= num_available_members(q)) { + ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count); + found = 1; + } +@@ -2319,7 +2371,7 @@ + /*! \brief convert "\n" to "\nVariable: " ready for manager to use */ + static char *vars2manager(struct ast_channel *chan, char *vars, size_t len) + { +- struct ast_str *buf = ast_str_thread_get(&global_app_buf, len + 1); ++ struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1); + char *tmp; + + if (pbx_builtin_serialize_variables(chan, &buf)) { +@@ -2434,23 +2486,41 @@ + (*busies)++; + return 0; + } +- ++ ++ ast_channel_lock(tmp->chan); ++ while (ast_channel_trylock(qe->chan)) { ++ CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan); ++ } ++ + if (qe->cancel_answered_elsewhere) { + ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE); + } + tmp->chan->appl = "AppQueue"; + tmp->chan->data = "(Outgoing Line)"; + memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); +- if (tmp->chan->cid.cid_num) +- ast_free(tmp->chan->cid.cid_num); +- tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); +- if (tmp->chan->cid.cid_name) +- ast_free(tmp->chan->cid.cid_name); +- tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); +- if (tmp->chan->cid.cid_ani) +- ast_free(tmp->chan->cid.cid_ani); +- tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); + ++ /* If the new channel has no callerid, try to guess what it should be */ ++ if (ast_strlen_zero(tmp->chan->cid.cid_num)) { ++ if (!ast_strlen_zero(qe->chan->connected.id.number)) { ++ ast_set_callerid(tmp->chan, qe->chan->connected.id.number, qe->chan->connected.id.name, qe->chan->connected.ani); ++ tmp->chan->cid.cid_pres = qe->chan->connected.id.number_presentation; ++ } else if (!ast_strlen_zero(qe->chan->cid.cid_dnid)) { ++ ast_set_callerid(tmp->chan, qe->chan->cid.cid_dnid, NULL, NULL); ++ } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) { ++ ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); ++ } ++ tmp->update_connectedline = 0; ++ } ++ ++ if (tmp->chan->cid.cid_rdnis) ++ ast_free(tmp->chan->cid.cid_rdnis); ++ tmp->chan->cid.cid_rdnis = ast_strdup(qe->chan->cid.cid_rdnis); ++ ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting); ++ ++ tmp->chan->cid.cid_tns = qe->chan->cid.cid_tns; ++ ++ ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->cid); ++ + /* Inherit specially named variables from parent channel */ + ast_channel_inherit_variables(qe->chan, tmp->chan); + +@@ -2458,7 +2528,6 @@ + tmp->chan->adsicpe = qe->chan->adsicpe; + + /* Inherit context and extension */ +- ast_channel_lock(qe->chan); + macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); + ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); + macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); +@@ -2466,13 +2535,14 @@ + ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); + else + ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); +- ast_channel_unlock(qe->chan); + + /* Place the call, but don't wait on the answer */ + if ((res = ast_call(tmp->chan, location, 0))) { + /* Again, keep going even if there's an error */ + ast_debug(1, "ast call on peer returned %d\n", res); + ast_verb(3, "Couldn't call %s\n", tmp->interface); ++ ast_channel_unlock(tmp->chan); ++ ast_channel_unlock(qe->chan); + do_hang(tmp); + (*busies)++; + update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); +@@ -2500,6 +2570,8 @@ + qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); + ast_verb(3, "Called %s\n", tmp->interface); + } ++ ast_channel_unlock(tmp->chan); ++ ast_channel_unlock(qe->chan); + + update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); + return 1; +@@ -2730,7 +2802,7 @@ + * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call + * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue() + */ +-static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed) ++static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed, int update_connectedline) + { + const char *queue = qe->parent->name; + struct callattempt *o, *start = NULL, *prev = NULL; +@@ -2750,7 +2822,13 @@ + #ifdef HAVE_EPOLL + struct callattempt *epollo; + #endif ++ struct ast_party_connected_line connected_caller; ++ char *inchan_name; + ++ ast_channel_lock(qe->chan); ++ inchan_name = ast_strdupa(qe->chan->name); ++ ast_channel_unlock(qe->chan); ++ + starttime = (long) time(NULL); + #ifdef HAVE_EPOLL + for (epollo = outgoing; epollo; epollo = epollo->q_next) { +@@ -2800,9 +2878,28 @@ + } + winner = ast_waitfor_n(watchers, pos, to); + for (o = start; o; o = o->call_next) { ++ /* We go with a static buffer here instead of using ast_strdupa. Using ++ * ast_strdupa in a loop like this one can cause a stack overflow ++ */ ++ char ochan_name[AST_CHANNEL_NAME]; ++ ast_channel_lock(o->chan); ++ ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name)); ++ ast_channel_unlock(o->chan); + if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { + if (!peer) { +- ast_verb(3, "%s answered %s\n", o->chan->name, in->name); ++ ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); ++ if (update_connectedline) { ++ if (o->connected.id.number) { ++ ast_channel_update_connected_line(in, &o->connected); ++ } else if (o->update_connectedline) { ++ ast_channel_lock(o->chan); ++ ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid); ++ ast_channel_unlock(o->chan); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_update_connected_line(in, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ } ++ } + peer = o; + } + } else if (o->chan && (o->chan == winner)) { +@@ -2811,12 +2908,15 @@ + ast_copy_string(membername, o->member->membername, sizeof(membername)); + + if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { +- ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); ++ ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward); + numnochan++; + do_hang(o); + winner = NULL; + continue; + } else if (!ast_strlen_zero(o->chan->call_forward)) { ++ struct ast_party_redirecting *apr = &o->chan->redirecting; ++ struct ast_party_connected_line *apc = &o->chan->connected; ++ struct ast_channel *original = o->chan; + char tmpchan[256]; + char *stuff; + char *tech; +@@ -2831,7 +2931,7 @@ + tech = "Local"; + } + /* Before processing channel, go ahead and check for forwarding */ +- ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); ++ ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name); + /* Setup parameters */ + o->chan = ast_request(tech, in->nativeformats, stuff, &status); + if (!o->chan) { +@@ -2839,32 +2939,42 @@ + o->stillgoing = 0; + numnochan++; + } else { ++ ast_channel_lock(o->chan); ++ while (ast_channel_trylock(in)) { ++ CHANNEL_DEADLOCK_AVOIDANCE(o->chan); ++ } + ast_channel_inherit_variables(in, o->chan); + ast_channel_datastore_inherit(in, o->chan); +- if (o->chan->cid.cid_num) +- ast_free(o->chan->cid.cid_num); +- o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); + +- if (o->chan->cid.cid_name) +- ast_free(o->chan->cid.cid_name); +- o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); +- + ast_string_field_set(o->chan, accountcode, in->accountcode); + o->chan->cdrflags = in->cdrflags; + +- if (in->cid.cid_ani) { +- if (o->chan->cid.cid_ani) +- ast_free(o->chan->cid.cid_ani); +- o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); +- } ++ ast_channel_set_redirecting(o->chan, apr); ++ + if (o->chan->cid.cid_rdnis) + ast_free(o->chan->cid.cid_rdnis); +- o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); ++ o->chan->cid.cid_rdnis = ast_strdup(S_OR(original->cid.cid_rdnis,S_OR(in->macroexten, in->exten))); ++ ++ o->chan->cid.cid_tns = in->cid.cid_tns; ++ ++ ast_party_caller_copy(&o->chan->cid, &in->cid); ++ ast_party_connected_line_copy(&o->chan->connected, apc); ++ ++ ast_channel_update_redirecting(in, apr); ++ if (in->cid.cid_rdnis) { ++ ast_free(in->cid.cid_rdnis); ++ } ++ in->cid.cid_rdnis = ast_strdup(o->chan->cid.cid_rdnis); ++ ++ update_connectedline = 1; ++ + if (ast_call(o->chan, tmpchan, 0)) { + ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); + do_hang(o); + numnochan++; + } ++ ast_channel_unlock(in); ++ ast_channel_unlock(o->chan); + } + /* Hangup the original channel now, in case we needed it */ + ast_hangup(winner); +@@ -2877,12 +2987,24 @@ + case AST_CONTROL_ANSWER: + /* This is our guy if someone answered. */ + if (!peer) { +- ast_verb(3, "%s answered %s\n", o->chan->name, in->name); ++ ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); ++ if (update_connectedline) { ++ if (o->connected.id.number) { ++ ast_channel_update_connected_line(in, &o->connected); ++ } else if (o->update_connectedline) { ++ ast_channel_lock(o->chan); ++ ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid); ++ ast_channel_unlock(o->chan); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_update_connected_line(in, &connected_caller); ++ ast_party_connected_line_free(&connected_caller); ++ } ++ } + peer = o; + } + break; + case AST_CONTROL_BUSY: +- ast_verb(3, "%s is busy\n", o->chan->name); ++ ast_verb(3, "%s is busy\n", ochan_name); + if (in->cdr) + ast_cdr_busy(in->cdr); + do_hang(o); +@@ -2897,7 +3019,7 @@ + numbusies++; + break; + case AST_CONTROL_CONGESTION: +- ast_verb(3, "%s is circuit-busy\n", o->chan->name); ++ ast_verb(3, "%s is circuit-busy\n", ochan_name); + if (in->cdr) + ast_cdr_busy(in->cdr); + endtime = (long) time(NULL); +@@ -2912,13 +3034,37 @@ + numbusies++; + break; + case AST_CONTROL_RINGING: +- ast_verb(3, "%s is ringing\n", o->chan->name); ++ ast_verb(3, "%s is ringing\n", ochan_name); + break; + case AST_CONTROL_OFFHOOK: + /* Ignore going off hook */ + break; ++ case AST_CONTROL_CONNECTED_LINE: ++ if (!update_connectedline) { ++ ast_verb(3, "Connected line update to %s prevented.\n", inchan_name); ++ } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { ++ struct ast_party_connected_line connected; ++ ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name); ++ ast_party_connected_line_set_init(&connected, &o->connected); ++ ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); ++ ast_party_connected_line_set(&o->connected, &connected); ++ ast_party_connected_line_free(&connected); ++ } else { ++ ast_verb(3, "%s connected line has changed, passing it to %s\n", ochan_name, inchan_name); ++ ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); ++ } ++ break; ++ case AST_CONTROL_REDIRECTING: ++ if (!update_connectedline) { ++ ast_verb(3, "Redirecting update to %s prevented\n", inchan_name); ++ } else { ++ ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name); ++ ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); ++ } ++ break; + default: + ast_debug(1, "Dunno what to do with control type %d\n", f->subclass); ++ break; + } + } + ast_frfree(f); +@@ -2981,80 +3127,46 @@ + /*! + * \brief Check if we should start attempting to call queue members. + * +- * The behavior of this function is dependent first on whether autofill is enabled +- * and second on whether the ring strategy is ringall. If autofill is not enabled, +- * then return true if we're the head of the queue. If autofill is enabled, then +- * we count the available members and see if the number of available members is enough +- * that given our position in the queue, we would theoretically be able to connect to +- * one of those available members ++ * A simple process, really. Count the number of members who are available ++ * to take our call and then see if we are in a position in the queue at ++ * which a member could accept our call. ++ * ++ * \param[in] qe The caller who wants to know if it is his turn ++ * \retval 0 It is not our turn ++ * \retval 1 It is our turn + */ + static int is_our_turn(struct queue_ent *qe) + { + struct queue_ent *ch; +- struct member *cur; +- int avl = 0; ++ int res; ++ int avl; + int idx = 0; +- int res; ++ /* This needs a lock. How many members are available to be served? */ ++ ao2_lock(qe->parent); + +- if (!qe->parent->autofill) { +- /* Atomically read the parent head -- does not need a lock */ +- ch = qe->parent->head; +- /* If we are now at the top of the head, break out */ +- if (ch == qe) { +- ast_debug(1, "It's our turn (%s).\n", qe->chan->name); +- res = 1; +- } else { +- ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); +- res = 0; +- } ++ avl = num_available_members(qe->parent); + +- } else { +- /* This needs a lock. How many members are available to be served? */ +- ao2_lock(qe->parent); +- +- ch = qe->parent->head; +- +- if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { +- ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n"); +- avl = 1; +- } else { +- struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0); +- while ((cur = ao2_iterator_next(&mem_iter))) { +- switch (cur->status) { +- case AST_DEVICE_INUSE: +- if (!qe->parent->ringinuse) +- break; +- /* else fall through */ +- case AST_DEVICE_NOT_INUSE: +- case AST_DEVICE_UNKNOWN: +- if (!cur->paused) +- avl++; +- break; +- } +- ao2_ref(cur, -1); +- } +- } ++ ch = qe->parent->head; + +- ast_debug(1, "There are %d available members.\n", avl); +- +- while ((idx < avl) && (ch) && (ch != qe)) { +- if (!ch->pending) +- idx++; +- ch = ch->next; +- } +- +- /* If the queue entry is within avl [the number of available members] calls from the top ... */ +- if (ch && idx < avl) { +- ast_debug(1, "It's our turn (%s).\n", qe->chan->name); +- res = 1; +- } else { +- ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); +- res = 0; +- } +- +- ao2_unlock(qe->parent); ++ ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); ++ ++ while ((idx < avl) && (ch) && (ch != qe)) { ++ if (!ch->pending) ++ idx++; ++ ch = ch->next; + } + ++ ao2_unlock(qe->parent); ++ ++ /* If the queue entry is within avl [the number of available members] calls from the top ... */ ++ if (ch && idx < avl) { ++ ast_debug(1, "It's our turn (%s).\n", qe->chan->name); ++ res = 1; ++ } else { ++ ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); ++ res = 0; ++ } ++ + return res; + } + +@@ -3506,6 +3618,7 @@ + char *p; + char vars[2048]; + int forwardsallowed = 1; ++ int update_connectedline = 1; + int callcompletedinsl; + struct ao2_iterator memi; + struct ast_datastore *datastore, *transfer_ds; +@@ -3571,6 +3684,9 @@ + case 'i': + forwardsallowed = 0; + break; ++ case 'I': ++ update_connectedline = 0; ++ break; + case 'x': + ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); + break; +@@ -3580,22 +3696,8 @@ + case 'C': + qe->cancel_answered_elsewhere = 1; + break; +- + } + +- if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) { +- queue_end_bridge->q = qe->parent; +- queue_end_bridge->chan = qe->chan; +- bridge_config.end_bridge_callback = end_bridge_callback; +- bridge_config.end_bridge_callback_data = queue_end_bridge; +- bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; +- /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need +- * to make sure to increase the refcount of this queue so it cannot be freed until we +- * are done with it. We remove this reference in end_bridge_callback. +- */ +- queue_ref(qe->parent); +- } +- + /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. + (this is mainly to support chan_local) + */ +@@ -3663,6 +3765,17 @@ + } + } + AST_LIST_UNLOCK(dialed_interfaces); ++ ++ ast_channel_lock(qe->chan); ++ /* If any pre-existing connected line information exists on this ++ * channel, like from the CONNECTED_LINE dialplan function, use this ++ * to seed the connected line information. It may, of course, be updated ++ * during the call ++ */ ++ if (qe->chan->connected.id.number) { ++ ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected); ++ } ++ ast_channel_unlock(qe->chan); + + if (di) { + free(tmp); +@@ -3694,6 +3807,7 @@ + tmp->oldstatus = cur->status; + tmp->lastcall = cur->lastcall; + tmp->lastqueue = cur->lastqueue; ++ tmp->update_connectedline = 1; + ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); + /* Special case: If we ring everyone, go ahead and ring them, otherwise + just calculate their metric for the appropriate strategy */ +@@ -3734,7 +3848,7 @@ + ring_one(qe, outgoing, &numbusies); + if (use_weight) + ao2_unlock(queues); +- lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); ++ lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline); + /* The ast_channel_datastore_remove() function could fail here if the + * datastore was moved to another channel during a masquerade. If this is + * the case, don't free the datastore here because later, when the channel +@@ -4147,6 +4261,20 @@ + qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); + ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); + ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); ++ ++ if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) { ++ queue_end_bridge->q = qe->parent; ++ queue_end_bridge->chan = qe->chan; ++ bridge_config.end_bridge_callback = end_bridge_callback; ++ bridge_config.end_bridge_callback_data = queue_end_bridge; ++ bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; ++ /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need ++ * to make sure to increase the refcount of this queue so it cannot be freed until we ++ * are done with it. We remove this reference in end_bridge_callback. ++ */ ++ queue_ref(qe->parent); ++ } ++ + time(&callstart); + transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); + bridge = ast_bridge_call(qe->chan,peer, &bridge_config); +@@ -5591,6 +5719,11 @@ + AST_APP_ARG(state_interface); + ); + ++ if (ast_strlen_zero(memberdata)) { ++ ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n"); ++ return; ++ } ++ + /* Add a new member */ + parse = ast_strdupa(memberdata); + +Index: apps/app_followme.c +=================================================================== +--- a/apps/app_followme.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/apps/app_followme.c (.../trunk) (revision 186562) +@@ -998,7 +998,7 @@ + + static int app_exec(struct ast_channel *chan, void *data) + { +- struct fm_args targs; ++ struct fm_args targs = { 0, }; + struct ast_bridge_config config; + struct call_followme *f; + struct number *nm, *newnm; +Index: UPGRADE.txt +=================================================================== +--- a/UPGRADE.txt (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/UPGRADE.txt (.../trunk) (revision 186562) +@@ -18,6 +18,14 @@ + === + =========================================================== + ++From 1.6.2 to 1.6.3: ++ ++* The usage of RTP inside of Asterisk has now become modularized. This means ++ the Asterisk RTP stack now exists as a loadable module, res_rtp_asterisk. ++ If you are not using autoload=yes in modules.conf you will need to ensure ++ it is set to load. If not, then any module which uses RTP (such as chan_sip) ++ will not be able to send or receive calls. ++ + From 1.6.1 to 1.6.2: + + * The res_indications module has been removed. Its functionality was important +Index: CHANGES +=================================================================== +--- a/CHANGES (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/CHANGES (.../trunk) (revision 186562) +@@ -7,7 +7,67 @@ + === and the other UPGRADE files for older releases. + === + ====================================================================== ++------------------------------------------------------------------------------ ++--- Functionality changes from Asterisk 1.6.2 to Asterisk 1.6.3 ------------- ++------------------------------------------------------------------------------ + ++SIP Changes ++----------- ++ * Added preferred_codec_only option in sip.conf. This feature limits the joint ++ codecs sent in response to an INVITE to the single most preferred codec. ++ ++Applications ++------------ ++ * Added progress option to the app_dial D() option. When progress DTMF is ++ present, those values are sent immediatly upon receiving a PROGRESS message ++ regardless if the call has been answered or not. ++ ++Dialplan Functions ++------------------ ++ * Added new dialplan functions CONNECTEDLINE and REDIRECTING which permits ++ setting various connected line and redirecting party information. ++ * The CHANNEL() function now supports the "name" option. ++ ++Queue changes ++------------- ++ * A new option, 'I' has been added to both app_queue and app_dial. ++ By setting this option, Asterisk will not update the caller with ++ connected line changes or redirecting party changes when they occur. ++ ++mISDN channel driver (chan_misdn) changes ++---------------------------------------- ++ * Added display_connected parameter to misdn.conf to put a display string ++ in the CONNECT message containing the connected name and/or number if ++ the presentation setting permits it. ++ * Added display_setup parameter to misdn.conf to put a display string ++ in the SETUP message containing the caller name and/or number if the ++ presentation setting permits it. ++ * Made misdn.conf parameters localdialplan and cpndialplan take a -1 to ++ indicate the dialplan settings are to be obtained from the asterisk ++ channel. ++ * Made misdn.conf parameter callerid accept the "name" <number> format ++ used by the rest of the system. ++ * Made use the nationalprefix and internationalprefix misdn.conf ++ parameters to prefix any received number from the ISDN link if that ++ number has the corresponding Type-Of-Number. ++ * Added the following new parameters: unknownprefix, netspecificprefix, ++ subscriberprefix, and abbreviatedprefix in misdn.conf to prefix any ++ received number from the ISDN link if that number has the corresponding ++ Type-Of-Number. ++ ++ ++SIP channel driver (chan_sip) changes ++------------------------------------------- ++ * The sendrpid parameter has been expanded to include the options ++ 'rpid' and 'pai'. Setting sendrpid to 'rpid' will cause Remote-Party-ID ++ header to be sent (equivalent to setting sendrpid=yes) and setting ++ sendrpid to 'pai' will cause P-Asserted-Identity header to be sent. ++ ++Asterisk Manager Interface ++-------------------------- ++ * The Hangup action now accepts a Cause header which may be used to ++ set the channel's hangup cause. ++ + ------------------------------------------------------------------------------ + --- Functionality changes from Asterisk 1.6.1 to Asterisk 1.6.2 ------------- + ------------------------------------------------------------------------------ +Index: sounds/Makefile +=================================================================== +--- a/sounds/Makefile (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/sounds/Makefile (.../trunk) (revision 186562) +@@ -17,8 +17,8 @@ + + SOUNDS_DIR:=$(DESTDIR)$(ASTDATADIR)/sounds + MOH_DIR:=$(DESTDIR)$(ASTDATADIR)/moh +-CORE_SOUNDS_VERSION:=1.4.14 +-EXTRA_SOUNDS_VERSION:=1.4.8 ++CORE_SOUNDS_VERSION:=1.4.15 ++EXTRA_SOUNDS_VERSION:=1.4.9 + SOUNDS_URL:=http://downloads.digium.com/pub/telephony/sounds/releases + MCS:=$(subst -EN-,-en-,$(MENUSELECT_CORE_SOUNDS)) + MCS:=$(subst -FR-,-fr-,$(MCS)) +Index: autoconf/ast_ext_lib.m4 +=================================================================== +--- a/autoconf/ast_ext_lib.m4 (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/autoconf/ast_ext_lib.m4 (.../trunk) (revision 186562) +@@ -11,7 +11,7 @@ + $1_DESCRIP="$2" + $1_OPTION="$3" + PBX_$1=0 +- AC_ARG_WITH([$3], AC_HELP_STRING([--with-$3=PATH],[use $2 files in PATH $4]), ++ AC_ARG_WITH([$3], AC_HELP_STRING([--with-$3=PATH],[use $2 files in PATH$4]), + [ + case ${withval} in + n|no) +Index: funcs/func_channel.c +=================================================================== +--- a/funcs/func_channel.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/funcs/func_channel.c (.../trunk) (revision 186562) +@@ -83,6 +83,9 @@ + <enum name="musicclass"> + <para>R/W class (from musiconhold.conf) for hold music.</para> + </enum> ++ <enum name="name"> ++ <para>The name of the channel</para> ++ </enum> + <enum name="parkinglot"> + <para>R/W parkinglot for parking.</para> + </enum> +@@ -249,7 +252,9 @@ + locked_copy_string(chan, buf, chan->language, len); + else if (!strcasecmp(data, "musicclass")) + locked_copy_string(chan, buf, chan->musicclass, len); +- else if (!strcasecmp(data, "parkinglot")) ++ else if (!strcasecmp(data, "name")) { ++ locked_copy_string(chan, buf, chan->name, len); ++ } else if (!strcasecmp(data, "parkinglot")) + locked_copy_string(chan, buf, chan->parkinglot, len); + else if (!strcasecmp(data, "state")) + locked_copy_string(chan, buf, ast_state2str(chan->_state), len); +Index: include/asterisk/rtp.h +=================================================================== +--- a/include/asterisk/rtp.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/rtp.h (.../trunk) (revision 186562) +@@ -1,416 +0,0 @@ +-/* +- * Asterisk -- An open source telephony toolkit. +- * +- * Copyright (C) 1999 - 2006, Digium, Inc. +- * +- * Mark Spencer <markster@digium.com> +- * +- * See http://www.asterisk.org for more information about +- * the Asterisk project. Please do not directly contact +- * any of the maintainers of this project for assistance; +- * the project provides a web site, mailing lists and IRC +- * channels for your use. +- * +- * 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. +- */ +- +-/*! +- * \file rtp.h +- * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal. +- * +- * RTP is defined in RFC 3550. +- */ +- +-#ifndef _ASTERISK_RTP_H +-#define _ASTERISK_RTP_H +- +-#include "asterisk/network.h" +- +-#include "asterisk/frame.h" +-#include "asterisk/io.h" +-#include "asterisk/sched.h" +-#include "asterisk/channel.h" +-#include "asterisk/linkedlists.h" +- +-#if defined(__cplusplus) || defined(c_plusplus) +-extern "C" { +-#endif +- +-/* Codes for RTP-specific data - not defined by our AST_FORMAT codes */ +-/*! DTMF (RFC2833) */ +-#define AST_RTP_DTMF (1 << 0) +-/*! 'Comfort Noise' (RFC3389) */ +-#define AST_RTP_CN (1 << 1) +-/*! DTMF (Cisco Proprietary) */ +-#define AST_RTP_CISCO_DTMF (1 << 2) +-/*! Maximum RTP-specific code */ +-#define AST_RTP_MAX AST_RTP_CISCO_DTMF +- +-/*! Maxmum number of payload defintions for a RTP session */ +-#define MAX_RTP_PT 256 +- +-/*! T.140 Redundancy Maxium number of generations */ +-#define RED_MAX_GENERATION 5 +- +-#define FLAG_3389_WARNING (1 << 0) +- +-enum ast_rtp_options { +- AST_RTP_OPT_G726_NONSTANDARD = (1 << 0), +-}; +- +-enum ast_rtp_get_result { +- /*! Failed to find the RTP structure */ +- AST_RTP_GET_FAILED = 0, +- /*! RTP structure exists but true native bridge can not occur so try partial */ +- AST_RTP_TRY_PARTIAL, +- /*! RTP structure exists and native bridge can occur */ +- AST_RTP_TRY_NATIVE, +-}; +- +-/*! \brief Variables used in ast_rtcp_get function */ +-enum ast_rtp_qos_vars { +- AST_RTP_TXCOUNT, +- AST_RTP_RXCOUNT, +- AST_RTP_TXJITTER, +- AST_RTP_RXJITTER, +- AST_RTP_RXPLOSS, +- AST_RTP_TXPLOSS, +- AST_RTP_RTT +-}; +- +-struct ast_rtp; +-/*! T.140 Redundancy structure*/ +-struct rtp_red; +- +-/*! \brief The value of each payload format mapping: */ +-struct rtpPayloadType { +- int isAstFormat; /*!< whether the following code is an AST_FORMAT */ +- int code; +-}; +- +-/*! \brief This is the structure that binds a channel (SIP/Jingle/H.323) to the RTP subsystem +-*/ +-struct ast_rtp_protocol { +- /*! Get RTP struct, or NULL if unwilling to transfer */ +- enum ast_rtp_get_result (* const get_rtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); +- /*! Get RTP struct, or NULL if unwilling to transfer */ +- enum ast_rtp_get_result (* const get_vrtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); +- /*! Get RTP struct, or NULL if unwilling to transfer */ +- enum ast_rtp_get_result (* const get_trtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); +- /*! Set RTP peer */ +- int (* const set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer, struct ast_rtp *tpeer, int codecs, int nat_active); +- int (* const get_codec)(struct ast_channel *chan); +- const char * const type; +- AST_LIST_ENTRY(ast_rtp_protocol) list; +-}; +- +-enum ast_rtp_quality_type { +- RTPQOS_SUMMARY = 0, +- RTPQOS_JITTER, +- RTPQOS_LOSS, +- RTPQOS_RTT +-}; +- +-/*! \brief RTCP quality report storage */ +-struct ast_rtp_quality { +- unsigned int local_ssrc; /*!< Our SSRC */ +- unsigned int local_lostpackets; /*!< Our lost packets */ +- double local_jitter; /*!< Our calculated jitter */ +- unsigned int local_count; /*!< Number of received packets */ +- unsigned int remote_ssrc; /*!< Their SSRC */ +- unsigned int remote_lostpackets; /*!< Their lost packets */ +- double remote_jitter; /*!< Their reported jitter */ +- unsigned int remote_count; /*!< Number of transmitted packets */ +- double rtt; /*!< Round trip time */ +-}; +- +-/*! RTP callback structure */ +-typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data); +- +-/*! +- * \brief Get the amount of space required to hold an RTP session +- * \return number of bytes required +- */ +-size_t ast_rtp_alloc_size(void); +- +-/*! +- * \brief Initializate a RTP session. +- * +- * \param sched +- * \param io +- * \param rtcpenable +- * \param callbackmode +- * \return A representation (structure) of an RTP session. +- */ +-struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode); +- +-/*! +- * \brief Initializate a RTP session using an in_addr structure. +- * +- * This fuction gets called by ast_rtp_new(). +- * +- * \param sched +- * \param io +- * \param rtcpenable +- * \param callbackmode +- * \param in +- * \return A representation (structure) of an RTP session. +- */ +-struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr in); +- +-void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them); +- +-/* Copies from rtp to them and returns 1 if there was a change or 0 if it was already the same */ +-int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them); +- +-void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us); +- +-struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp); +- +-/*! Destroy RTP session */ +-void ast_rtp_destroy(struct ast_rtp *rtp); +- +-void ast_rtp_reset(struct ast_rtp *rtp); +- +-/*! Stop RTP session, do not destroy structure */ +-void ast_rtp_stop(struct ast_rtp *rtp); +- +-void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback); +- +-void ast_rtp_set_data(struct ast_rtp *rtp, void *data); +- +-int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *f); +- +-struct ast_frame *ast_rtp_read(struct ast_rtp *rtp); +- +-struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp); +- +-int ast_rtp_fd(struct ast_rtp *rtp); +- +-int ast_rtcp_fd(struct ast_rtp *rtp); +- +-int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit); +- +-int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit); +- +-int ast_rtp_sendcng(struct ast_rtp *rtp, int level); +- +-int ast_rtp_setqos(struct ast_rtp *rtp, int tos, int cos, char *desc); +- +-void ast_rtp_new_source(struct ast_rtp *rtp); +- +-/*! \brief Setting RTP payload types from lines in a SDP description: */ +-void ast_rtp_pt_clear(struct ast_rtp* rtp); +-/*! \brief Set payload types to defaults */ +-void ast_rtp_pt_default(struct ast_rtp* rtp); +- +-/*! \brief Copy payload types between RTP structures */ +-void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src); +- +-/*! \brief Activate payload type */ +-void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt); +- +-/*! \brief clear payload type */ +-void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt); +- +-/*! \brief Set payload type to a known MIME media type for a codec +- * +- * \param rtp RTP structure to modify +- * \param pt Payload type entry to modify +- * \param mimeType top-level MIME type of media stream (typically "audio", "video", "text", etc.) +- * \param mimeSubtype MIME subtype of media stream (typically a codec name) +- * \param options Zero or more flags from the ast_rtp_options enum +- * +- * This function 'fills in' an entry in the list of possible formats for +- * a media stream associated with an RTP structure. +- * +- * \retval 0 on success +- * \retval -1 if the payload type is out of range +- * \retval -2 if the mimeType/mimeSubtype combination was not found +- */ +-int ast_rtp_set_rtpmap_type(struct ast_rtp* rtp, int pt, +- char *mimeType, char *mimeSubtype, +- enum ast_rtp_options options); +- +-/*! \brief Set payload type to a known MIME media type for a codec with a specific sample rate +- * +- * \param rtp RTP structure to modify +- * \param pt Payload type entry to modify +- * \param mimeType top-level MIME type of media stream (typically "audio", "video", "text", etc.) +- * \param mimeSubtype MIME subtype of media stream (typically a codec name) +- * \param options Zero or more flags from the ast_rtp_options enum +- * \param sample_rate The sample rate of the media stream +- * +- * This function 'fills in' an entry in the list of possible formats for +- * a media stream associated with an RTP structure. +- * +- * \retval 0 on success +- * \retval -1 if the payload type is out of range +- * \retval -2 if the mimeType/mimeSubtype combination was not found +- */ +-int ast_rtp_set_rtpmap_type_rate(struct ast_rtp* rtp, int pt, +- char *mimeType, char *mimeSubtype, +- enum ast_rtp_options options, +- unsigned int sample_rate); +- +-/*! \brief Mapping between RTP payload format codes and Asterisk codes: */ +-struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt); +-int ast_rtp_lookup_code(struct ast_rtp* rtp, int isAstFormat, int code); +- +-void ast_rtp_get_current_formats(struct ast_rtp* rtp, +- int* astFormats, int* nonAstFormats); +- +-/*! \brief Mapping an Asterisk code into a MIME subtype (string): */ +-const char *ast_rtp_lookup_mime_subtype(int isAstFormat, int code, +- enum ast_rtp_options options); +- +-/*! \brief Get the sample rate associated with known RTP payload types +- * +- * \param isAstFormat True if the value in the 'code' parameter is an AST_FORMAT value +- * \param code Format code, either from AST_FORMAT list or from AST_RTP list +- * +- * \return the sample rate if the format was found, zero if it was not found +- */ +-unsigned int ast_rtp_lookup_sample_rate(int isAstFormat, int code); +- +-/*! \brief Build a string of MIME subtype names from a capability list */ +-char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability, +- const int isAstFormat, enum ast_rtp_options options); +- +-void ast_rtp_setnat(struct ast_rtp *rtp, int nat); +- +-int ast_rtp_getnat(struct ast_rtp *rtp); +- +-/*! \brief Indicate whether this RTP session is carrying DTMF or not */ +-void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf); +- +-/*! \brief Compensate for devices that send RFC2833 packets all at once */ +-void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate); +- +-/*! \brief Enable STUN capability */ +-void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable); +- +-/*! \brief Generic STUN request +- * send a generic stun request to the server specified. +- * \param s the socket used to send the request +- * \param dst the address of the STUN server +- * \param username if non null, add the username in the request +- * \param answer if non null, the function waits for a response and +- * puts here the externally visible address. +- * \return 0 on success, other values on error. +- * The interface it may change in the future. +- */ +-int ast_stun_request(int s, struct sockaddr_in *dst, +- const char *username, struct sockaddr_in *answer); +- +-/*! \brief Send STUN request for an RTP socket +- * Deprecated, this is just a wrapper for ast_rtp_stun_request() +- */ +-void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username); +- +-/*! \brief The RTP bridge. +- \arg \ref AstRTPbridge +-*/ +-int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); +- +-/*! \brief Register an RTP channel client */ +-int ast_rtp_proto_register(struct ast_rtp_protocol *proto); +- +-/*! \brief Unregister an RTP channel client */ +-void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto); +- +-int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media); +- +-/*! \brief If possible, create an early bridge directly between the devices without +- having to send a re-invite later */ +-int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1); +- +-/*! \brief Get QOS stats on a RTP channel +- * \since 1.6.1 +- */ +-int ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, char *buf, unsigned int buflen); +- +-/*! \brief Return RTP and RTCP QoS values +- * \since 1.6.1 +- */ +-unsigned int ast_rtp_get_qosvalue(struct ast_rtp *rtp, enum ast_rtp_qos_vars value); +- +-/*! \brief Set RTPAUDIOQOS(...) variables on a channel when it is being hung up +- * \since 1.6.1 +- */ +-void ast_rtp_set_vars(struct ast_channel *chan, struct ast_rtp *rtp); +- +-/*! \brief Return RTCP quality string +- * +- * \param rtp An rtp structure to get qos information about. +- * +- * \param qual An (optional) rtp quality structure that will be +- * filled with the quality information described in +- * the ast_rtp_quality structure. This structure is +- * not dependent on any qtype, so a call for any +- * type of information would yield the same results +- * because ast_rtp_quality is not a data type +- * specific to any qos type. +- * +- * \param qtype The quality type you'd like, default should be +- * RTPQOS_SUMMARY which returns basic information +- * about the call. The return from RTPQOS_SUMMARY +- * is basically ast_rtp_quality in a string. The +- * other types are RTPQOS_JITTER, RTPQOS_LOSS and +- * RTPQOS_RTT which will return more specific +- * statistics. +- * \version 1.6.1 added qtype parameter +- */ +-char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual, enum ast_rtp_quality_type qtype); +-/*! \brief Send an H.261 fast update request. Some devices need this rather than the XML message in SIP */ +-int ast_rtcp_send_h261fur(void *data); +- +-void ast_rtp_init(void); /*! Initialize RTP subsystem */ +-int ast_rtp_reload(void); /*! reload rtp configuration */ +-void ast_rtp_new_init(struct ast_rtp *rtp); +- +-/*! \brief Set codec preference */ +-void ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs); +- +-/*! \brief Get codec preference */ +-struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp); +- +-/*! \brief get format from predefined dynamic payload format */ +-int ast_rtp_codec_getformat(int pt); +- +-/*! \brief Set rtp timeout */ +-void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout); +-/*! \brief Set rtp hold timeout */ +-void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout); +-/*! \brief set RTP keepalive interval */ +-void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period); +-/*! \brief Get RTP keepalive interval */ +-int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp); +-/*! \brief Get rtp hold timeout */ +-int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp); +-/*! \brief Get rtp timeout */ +-int ast_rtp_get_rtptimeout(struct ast_rtp *rtp); +-/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */ +-void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp); +- +-/*! \brief Initalize t.140 redudancy +- * \param ti time between each t140red frame is sent +- * \param red_pt payloadtype for RTP packet +- * \param pt payloadtype numbers for each generation including primary data +- * \param num_gen number of redundant generations, primary data excluded +- * \since 1.6.1 +- */ +-int rtp_red_init(struct ast_rtp *rtp, int ti, int *pt, int num_gen); +- +-/*! \brief Buffer t.140 data */ +-void red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f); +- +- +- +-#if defined(__cplusplus) || defined(c_plusplus) +-} +-#endif +- +-#endif /* _ASTERISK_RTP_H */ +Index: include/asterisk/rtp_engine.h +=================================================================== +--- a/include/asterisk/rtp_engine.h (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/include/asterisk/rtp_engine.h (.../trunk) (revision 186562) +@@ -0,0 +1,1594 @@ ++/* ++ * Asterisk -- An open source telephony toolkit. ++ * ++ * Copyright (C) 1999 - 2009, Digium, Inc. ++ * ++ * Mark Spencer <markster@digium.com> ++ * Joshua Colp <jcolp@digium.com> ++ * ++ * See http://www.asterisk.org for more information about ++ * the Asterisk project. Please do not directly contact ++ * any of the maintainers of this project for assistance; ++ * the project provides a web site, mailing lists and IRC ++ * channels for your use. ++ * ++ * 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. ++ */ ++ ++/*! \file ++ * \brief Pluggable RTP Architecture ++ * \author Joshua Colp <jcolp@digium.com> ++ * \ref AstRTPEngine ++ */ ++ ++/*! ++ * \page AstRTPEngine Asterisk RTP Engine API ++ * ++ * The purpose of this API is to provide a way for multiple RTP stacks to be used inside ++ * of Asterisk without any module that uses RTP knowing any different. To the module each RTP ++ * stack behaves the same. ++ * ++ * An RTP session is called an instance and is made up of a combination of codec information, ++ * RTP engine, RTP properties, and address information. An engine name may be passed in to explicitly ++ * choose an RTP stack to be used but a default one will be used if none is provided. An address to use ++ * for RTP may also be provided but the underlying RTP engine may choose a different address depending on ++ * it's configuration. ++ * ++ * An RTP engine is the layer between the RTP engine core and the RTP stack itself. The RTP engine core provides ++ * a set of callbacks to do various things (such as write audio out) that the RTP engine has to have implemented. ++ * ++ * Glue is what binds an RTP instance to a channel. It is used to retrieve RTP instance information when ++ * performing remote or local bridging and is used to have the channel driver tell the remote side to change ++ * destination of the RTP stream. ++ * ++ * Statistics from an RTP instance can be retrieved using the ast_rtp_instance_get_stats API call. This essentially ++ * asks the RTP engine in use to fill in a structure with the requested values. It is not required for an RTP engine ++ * to support all statistic values. ++ * ++ * Properties allow behavior of the RTP engine and RTP engine core to be changed. For example, there is a property named ++ * AST_RTP_PROPERTY_NAT which is used to tell the RTP engine to enable symmetric RTP if it supports it. It is not required ++ * for an RTP engine to support all properties. ++ * ++ * Codec information is stored using a separate data structure which has it's own set of API calls to add/remove/retrieve ++ * information. They are used by the module after an RTP instance is created so that payload information is available for ++ * the RTP engine. ++ */ ++ ++#ifndef _ASTERISK_RTP_ENGINE_H ++#define _ASTERISK_RTP_ENGINE_H ++ ++#if defined(__cplusplus) || defined(c_plusplus) ++extern "C" { ++#endif ++ ++#include "asterisk/astobj2.h" ++ ++/* Maximum number of payloads supported */ ++#define AST_RTP_MAX_PT 256 ++ ++/* Maximum number of generations */ ++#define AST_RED_MAX_GENERATION 5 ++ ++struct ast_rtp_instance; ++struct ast_rtp_glue; ++ ++/*! RTP Properties that can be set on an RTP instance */ ++enum ast_rtp_property { ++ /*! Enable symmetric RTP support */ ++ AST_RTP_PROPERTY_NAT = 0, ++ /*! RTP instance will be carrying DTMF (using RFC2833) */ ++ AST_RTP_PROPERTY_DTMF, ++ /*! Expect unreliable DTMF from remote party */ ++ AST_RTP_PROPERTY_DTMF_COMPENSATE, ++ /*! Enable STUN support */ ++ AST_RTP_PROPERTY_STUN, ++ /*! Enable RTCP support */ ++ AST_RTP_PROPERTY_RTCP, ++ /*! Maximum number of RTP properties supported */ ++ AST_RTP_PROPERTY_MAX, ++}; ++ ++/*! Additional RTP options */ ++enum ast_rtp_options { ++ /*! Remote side is using non-standard G.726 */ ++ AST_RTP_OPT_G726_NONSTANDARD = (1 << 0), ++}; ++ ++/*! RTP DTMF Modes */ ++enum ast_rtp_dtmf_mode { ++ /*! No DTMF is being carried over the RTP stream */ ++ AST_RTP_DTMF_MODE_NONE = 0, ++ /*! DTMF is being carried out of band using RFC2833 */ ++ AST_RTP_DTMF_MODE_RFC2833, ++ /*! DTMF is being carried inband over the RTP stream */ ++ AST_RTP_DTMF_MODE_INBAND, ++}; ++ ++/*! Result codes when RTP glue is queried for information */ ++enum ast_rtp_glue_result { ++ /*! No remote or local bridging is permitted */ ++ AST_RTP_GLUE_RESULT_FORBID = 0, ++ /*! Move RTP stream to be remote between devices directly */ ++ AST_RTP_GLUE_RESULT_REMOTE, ++ /*! Perform RTP engine level bridging if possible */ ++ AST_RTP_GLUE_RESULT_LOCAL, ++}; ++ ++/*! Field statistics that can be retrieved from an RTP instance */ ++enum ast_rtp_instance_stat_field { ++ /*! Retrieve quality information */ ++ AST_RTP_INSTANCE_STAT_FIELD_QUALITY = 0, ++ /*! Retrieve quality information about jitter */ ++ AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, ++ /*! Retrieve quality information about packet loss */ ++ AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, ++ /*! Retrieve quality information about round trip time */ ++ AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, ++}; ++ ++/*! Statistics that can be retrieved from an RTP instance */ ++enum ast_rtp_instance_stat { ++ /*! Retrieve all statistics */ ++ AST_RTP_INSTANCE_STAT_ALL = 0, ++ /*! Retrieve number of packets transmitted */ ++ AST_RTP_INSTANCE_STAT_TXCOUNT, ++ /*! Retrieve number of packets received */ ++ AST_RTP_INSTANCE_STAT_RXCOUNT, ++ /*! Retrieve ALL statistics relating to packet loss */ ++ AST_RTP_INSTANCE_STAT_COMBINED_LOSS, ++ /*! Retrieve number of packets lost for transmitting */ ++ AST_RTP_INSTANCE_STAT_TXPLOSS, ++ /*! Retrieve number of packets lost for receiving */ ++ AST_RTP_INSTANCE_STAT_RXPLOSS, ++ /*! Retrieve maximum number of packets lost on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_MAXRXPLOSS, ++ /*! Retrieve minimum number of packets lost on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_MINRXPLOSS, ++ /*! Retrieve average number of packets lost on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVRXPLOSS, ++ /*! Retrieve standard deviation of packets lost on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_STDEVRXPLOSS, ++ /*! Retrieve maximum number of packets lost on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_MAXRXPLOSS, ++ /*! Retrieve minimum number of packets lost on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_MINRXPLOSS, ++ /*! Retrieve average number of packets lost on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVRXPLOSS, ++ /*! Retrieve standard deviation of packets lost on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_STDEVRXPLOSS, ++ /*! Retrieve ALL statistics relating to jitter */ ++ AST_RTP_INSTANCE_STAT_COMBINED_JITTER, ++ /*! Retrieve jitter on transmitted packets */ ++ AST_RTP_INSTANCE_STAT_TXJITTER, ++ /*! Retrieve jitter on received packets */ ++ AST_RTP_INSTANCE_STAT_RXJITTER, ++ /*! Retrieve maximum jitter on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_MAXJITTER, ++ /*! Retrieve minimum jitter on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_MINJITTER, ++ /*! Retrieve average jitter on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVJITTER, ++ /*! Retrieve standard deviation jitter on remote side */ ++ AST_RTP_INSTANCE_STAT_REMOTE_STDEVJITTER, ++ /*! Retrieve maximum jitter on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_MAXJITTER, ++ /*! Retrieve minimum jitter on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_MINJITTER, ++ /*! Retrieve average jitter on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVJITTER, ++ /*! Retrieve standard deviation jitter on local side */ ++ AST_RTP_INSTANCE_STAT_LOCAL_STDEVJITTER, ++ /*! Retrieve ALL statistics relating to round trip time */ ++ AST_RTP_INSTANCE_STAT_COMBINED_RTT, ++ /*! Retrieve round trip time */ ++ AST_RTP_INSTANCE_STAT_RTT, ++ /*! Retrieve maximum round trip time */ ++ AST_RTP_INSTANCE_STAT_MAX_RTT, ++ /*! Retrieve minimum round trip time */ ++ AST_RTP_INSTANCE_STAT_MIN_RTT, ++ /*! Retrieve average round trip time */ ++ AST_RTP_INSTANCE_STAT_NORMDEVRTT, ++ /*! Retrieve standard deviation round trip time */ ++ AST_RTP_INSTANCE_STAT_STDEVRTT, ++ /*! Retrieve local SSRC */ ++ AST_RTP_INSTANCE_STAT_LOCAL_SSRC, ++ /*! Retrieve remote SSRC */ ++ AST_RTP_INSTANCE_STAT_REMOTE_SSRC, ++}; ++ ++/* Codes for RTP-specific data - not defined by our AST_FORMAT codes */ ++/*! DTMF (RFC2833) */ ++#define AST_RTP_DTMF (1 << 0) ++/*! 'Comfort Noise' (RFC3389) */ ++#define AST_RTP_CN (1 << 1) ++/*! DTMF (Cisco Proprietary) */ ++#define AST_RTP_CISCO_DTMF (1 << 2) ++/*! Maximum RTP-specific code */ ++#define AST_RTP_MAX AST_RTP_CISCO_DTMF ++ ++/*! Structure that represents a payload */ ++struct ast_rtp_payload_type { ++ /*! Is this an Asterisk value */ ++ int asterisk_format; ++ /*! Actual internal value of the payload */ ++ int code; ++}; ++ ++/*! Structure that represents statistics from an RTP instance */ ++struct ast_rtp_instance_stats { ++ /*! Number of packets transmitted */ ++ unsigned int txcount; ++ /*! Number of packets received */ ++ unsigned int rxcount; ++ /*! Jitter on transmitted packets */ ++ unsigned int txjitter; ++ /*! Jitter on received packets */ ++ unsigned int rxjitter; ++ /*! Maximum jitter on remote side */ ++ double remote_maxjitter; ++ /*! Minimum jitter on remote side */ ++ double remote_minjitter; ++ /*! Average jitter on remote side */ ++ double remote_normdevjitter; ++ /*! Standard deviation jitter on remote side */ ++ double remote_stdevjitter; ++ /*! Maximum jitter on local side */ ++ double local_maxjitter; ++ /*! Minimum jitter on local side */ ++ double local_minjitter; ++ /*! Average jitter on local side */ ++ double local_normdevjitter; ++ /*! Standard deviation jitter on local side */ ++ double local_stdevjitter; ++ /*! Number of transmitted packets lost */ ++ unsigned int txploss; ++ /*! Number of received packets lost */ ++ unsigned int rxploss; ++ /*! Maximum number of packets lost on remote side */ ++ double remote_maxrxploss; ++ /*! Minimum number of packets lost on remote side */ ++ double remote_minrxploss; ++ /*! Average number of packets lost on remote side */ ++ double remote_normdevrxploss; ++ /*! Standard deviation packets lost on remote side */ ++ double remote_stdevrxploss; ++ /*! Maximum number of packets lost on local side */ ++ double local_maxrxploss; ++ /*! Minimum number of packets lost on local side */ ++ double local_minrxploss; ++ /*! Average number of packets lost on local side */ ++ double local_normdevrxploss; ++ /*! Standard deviation packets lost on local side */ ++ double local_stdevrxploss; ++ /*! Total round trip time */ ++ unsigned int rtt; ++ /*! Maximum round trip time */ ++ double maxrtt; ++ /*! Minimum round trip time */ ++ double minrtt; ++ /*! Average round trip time */ ++ double normdevrtt; ++ /*! Standard deviation round trip time */ ++ double stdevrtt; ++ /*! Our SSRC */ ++ unsigned int local_ssrc; ++ /*! Their SSRC */ ++ unsigned int remote_ssrc; ++}; ++ ++#define AST_RTP_STAT_SET(current_stat, combined, placement, value) \ ++if (stat == current_stat || stat == AST_RTP_INSTANCE_STAT_ALL || (combined >= 0 && combined == current_stat)) { \ ++placement = value; \ ++if (stat == current_stat) { \ ++return 0; \ ++} \ ++} ++ ++#define AST_RTP_STAT_TERMINATOR(combined) \ ++if (stat == combined) { \ ++return 0; \ ++} ++ ++/*! Structure that represents an RTP stack (engine) */ ++struct ast_rtp_engine { ++ /*! Name of the RTP engine, used when explicitly requested */ ++ const char *name; ++ /*! Module this RTP engine came from, used for reference counting */ ++ struct ast_module *mod; ++ /*! Callback for setting up a new RTP instance */ ++ int (*new)(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data); ++ /*! Callback for destroying an RTP instance */ ++ int (*destroy)(struct ast_rtp_instance *instance); ++ /*! Callback for writing out a frame */ ++ int (*write)(struct ast_rtp_instance *instance, struct ast_frame *frame); ++ /*! Callback for stopping the RTP instance */ ++ void (*stop)(struct ast_rtp_instance *instance); ++ /*! Callback for starting RFC2833 DTMF transmission */ ++ int (*dtmf_begin)(struct ast_rtp_instance *instance, char digit); ++ /*! Callback for stopping RFC2833 DTMF transmission */ ++ int (*dtmf_end)(struct ast_rtp_instance *instance, char digit); ++ /*! Callback to indicate that a new source of media has come in */ ++ void (*new_source)(struct ast_rtp_instance *instance); ++ /*! Callback for setting an extended RTP property */ ++ int (*extended_prop_set)(struct ast_rtp_instance *instance, int property, void *value); ++ /*! Callback for getting an extended RTP property */ ++ void *(*extended_prop_get)(struct ast_rtp_instance *instance, int property); ++ /*! Callback for setting an RTP property */ ++ void (*prop_set)(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value); ++ /*! Callback for setting a payload */ ++ void (*payload_set)(struct ast_rtp_instance *instance, int payload, int astformat, int format); ++ /*! Callback for setting packetization preferences */ ++ void (*packetization_set)(struct ast_rtp_instance *instance, struct ast_codec_pref *pref); ++ /*! Callback for setting the remote address that RTP is to be sent to */ ++ void (*remote_address_set)(struct ast_rtp_instance *instance, struct sockaddr_in *sin); ++ /*! Callback for changing DTMF mode */ ++ int (*dtmf_mode_set)(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode); ++ /*! Callback for retrieving statistics */ ++ int (*get_stat)(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat); ++ /*! Callback for setting QoS values */ ++ int (*qos)(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); ++ /*! Callback for retrieving a file descriptor to poll on, not always required */ ++ int (*fd)(struct ast_rtp_instance *instance, int rtcp); ++ /*! Callback for initializing RED support */ ++ int (*red_init)(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations); ++ /*! Callback for buffering a frame using RED */ ++ int (*red_buffer)(struct ast_rtp_instance *instance, struct ast_frame *frame); ++ /*! Callback for reading a frame from the RTP engine */ ++ struct ast_frame *(*read)(struct ast_rtp_instance *instance, int rtcp); ++ /*! Callback to locally bridge two RTP instances */ ++ int (*local_bridge)(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1); ++ /*! Callback to set the read format */ ++ int (*set_read_format)(struct ast_rtp_instance *instance, int format); ++ /*! Callback to set the write format */ ++ int (*set_write_format)(struct ast_rtp_instance *instance, int format); ++ /*! Callback to make two instances compatible */ ++ int (*make_compatible)(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1); ++ /*! Callback to see if two instances are compatible with DTMF */ ++ int (*dtmf_compatible)(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1); ++ /*! Callback to indicate that packets will now flow */ ++ int (*activate)(struct ast_rtp_instance *instance); ++ /*! Callback to request that the RTP engine send a STUN BIND request */ ++ void (*stun_request)(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username); ++ /*! Linked list information */ ++ AST_RWLIST_ENTRY(ast_rtp_engine) entry; ++}; ++ ++/*! Structure that represents codec and packetization information */ ++struct ast_rtp_codecs { ++ /*! Codec packetization preferences */ ++ struct ast_codec_pref pref; ++ /*! Payloads present */ ++ struct ast_rtp_payload_type payloads[AST_RTP_MAX_PT]; ++}; ++ ++/*! Structure that represents the glue that binds an RTP instance to a channel */ ++struct ast_rtp_glue { ++ /*! Name of the channel driver that this glue is responsible for */ ++ const char *type; ++ /*! Module that the RTP glue came from */ ++ struct ast_module *mod; ++ /*! ++ * \brief Callback for retrieving the RTP instance carrying audio ++ * \note This function increases the reference count on the returned RTP instance. ++ */ ++ enum ast_rtp_glue_result (*get_rtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); ++ /*! ++ * \brief Callback for retrieving the RTP instance carrying video ++ * \note This function increases the reference count on the returned RTP instance. ++ */ ++ enum ast_rtp_glue_result (*get_vrtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); ++ /*! ++ * \brief Callback for retrieving the RTP instance carrying text ++ * \note This function increases the reference count on the returned RTP instance. ++ */ ++ enum ast_rtp_glue_result (*get_trtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); ++ /*! Callback for updating the destination that the remote side should send RTP to */ ++ int (*update_peer)(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active); ++ /*! Callback for retrieving codecs that the channel can do */ ++ int (*get_codec)(struct ast_channel *chan); ++ /*! Linked list information */ ++ AST_RWLIST_ENTRY(ast_rtp_glue) entry; ++}; ++ ++#define ast_rtp_engine_register(engine) ast_rtp_engine_register2(engine, ast_module_info->self) ++ ++/*! ++ * \brief Register an RTP engine ++ * ++ * \param engine Structure of the RTP engine to register ++ * \param module Module that the RTP engine is part of ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_engine_register2(&example_rtp_engine, NULL); ++ * \endcode ++ * ++ * This registers the RTP engine declared as example_rtp_engine with the RTP engine core, but does not ++ * associate a module with it. ++ * ++ * \note It is recommended that you use the ast_rtp_engine_register macro so that the module is ++ * associated with the RTP engine and use counting is performed. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module); ++ ++/*! ++ * \brief Unregister an RTP engine ++ * ++ * \param engine Structure of the RTP engine to unregister ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_engine_unregister(&example_rtp_engine); ++ * \endcode ++ * ++ * This unregisters the RTP engine declared as example_rtp_engine from the RTP engine core. If a module ++ * reference was provided when it was registered then this will only be called once the RTP engine is no longer in use. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_engine_unregister(struct ast_rtp_engine *engine); ++ ++#define ast_rtp_glue_register(glue) ast_rtp_glue_register2(glue, ast_module_info->self) ++ ++/*! ++ * \brief Register RTP glue ++ * ++ * \param glue The glue to register ++ * \param module Module that the RTP glue is part of ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_glue_register2(&example_rtp_glue, NULL); ++ * \endcode ++ * ++ * This registers the RTP glue declared as example_rtp_glue with the RTP engine core, but does not ++ * associate a module with it. ++ * ++ * \note It is recommended that you use the ast_rtp_glue_register macro so that the module is ++ * associated with the RTP glue and use counting is performed. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_glue_register2(struct ast_rtp_glue *glue, struct ast_module *module); ++ ++/*! ++ * \brief Unregister RTP glue ++ * ++ * \param glue The glue to unregister ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_glue_unregister(&example_rtp_glue); ++ * \endcode ++ * ++ * This unregisters the RTP glue declared as example_rtp_gkue from the RTP engine core. If a module ++ * reference was provided when it was registered then this will only be called once the RTP engine is no longer in use. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_glue_unregister(struct ast_rtp_glue *glue); ++ ++/*! ++ * \brief Create a new RTP instance ++ * ++ * \param engine_name Name of the engine to use for the RTP instance ++ * \param sched Scheduler context that the RTP engine may want to use ++ * \param sin Address we want to bind to ++ * \param data Unique data for the engine ++ * ++ * \retval non-NULL success ++ * \retval NULL failure ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_instance *instance = NULL; ++ * instance = ast_rtp_instance_new(NULL, sched, &sin, NULL); ++ * \endcode ++ * ++ * This creates a new RTP instance using the default engine and asks the RTP engine to bind to the address given ++ * in the sin structure. ++ * ++ * \note The RTP engine does not have to use the address provided when creating an RTP instance. It may choose to use ++ * another depending on it's own configuration. ++ * ++ * \since 1.6.3 ++ */ ++struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name, struct sched_context *sched, struct sockaddr_in *sin, void *data); ++ ++/*! ++ * \brief Destroy an RTP instance ++ * ++ * \param instance The RTP instance to destroy ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_destroy(instance); ++ * \endcode ++ * ++ * This destroys the RTP instance pointed to by instance. Once this function returns instance no longer points to valid ++ * memory and may not be used again. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_destroy(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Set the data portion of an RTP instance ++ * ++ * \param instance The RTP instance to manipulate ++ * \param data Pointer to data ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_data(instance, blob); ++ * \endcode ++ * ++ * This sets the data pointer on the RTP instance pointed to by 'instance' to ++ * blob. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_set_data(struct ast_rtp_instance *instance, void *data); ++ ++/*! ++ * \brief Get the data portion of an RTP instance ++ * ++ * \param instance The RTP instance we want the data portion from ++ * ++ * Example usage: ++ * ++ * \code ++ * struct *blob = ast_rtp_instance_get_data(instance); ++ ( \endcode ++ * ++ * This gets the data pointer on the RTP instance pointed to by 'instance'. ++ * ++ * \since 1.6.3 ++ */ ++void *ast_rtp_instance_get_data(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Send a frame out over RTP ++ * ++ * \param instance The RTP instance to send frame out on ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_write(instance, frame); ++ * \endcode ++ * ++ * This gives the frame pointed to by frame to the RTP engine being used for the instance ++ * and asks that it be transmitted to the current remote address set on the RTP instance. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *frame); ++ ++/*! ++ * \brief Receive a frame over RTP ++ * ++ * \param instance The RTP instance to receive frame on ++ * \param rtcp Whether to read in RTCP or not ++ * ++ * \retval non-NULL success ++ * \retval NULL failure ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_frame *frame; ++ * frame = ast_rtp_instance_read(instance, 0); ++ * \endcode ++ * ++ * This asks the RTP engine to read in RTP from the instance and return it as an Asterisk frame. ++ * ++ * \since 1.6.3 ++ */ ++struct ast_frame *ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp); ++ ++/*! ++ * \brief Set the address of the remote endpoint that we are sending RTP to ++ * ++ * \param instance The RTP instance to change the address on ++ * \param address Address to set it to ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_remote_address(instance, &sin); ++ * \endcode ++ * ++ * This changes the remote address that RTP will be sent to on instance to the address given in the sin ++ * structure. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_set_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); ++ ++/*! ++ * \brief Set the address that we are expecting to receive RTP on ++ * ++ * \param instance The RTP instance to change the address on ++ * \param address Address to set it to ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_local_address(instance, &sin); ++ * \endcode ++ * ++ * This changes the local address that RTP is expected on to the address given in the sin ++ * structure. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_set_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); ++ ++/*! ++ * \brief Get the local address that we are expecting RTP on ++ * ++ * \param instance The RTP instance to get the address from ++ * \param address The variable to store the address in ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * struct sockaddr_in sin; ++ * ast_rtp_instance_get_local_address(instance, &sin); ++ * \endcode ++ * ++ * This gets the local address that we are expecting RTP on and stores it in the 'sin' structure. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); ++ ++/*! ++ * \brief Get the address of the remote endpoint that we are sending RTP to ++ * ++ * \param instance The instance that we want to get the remote address for ++ * \param address A structure to put the address into ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * struct sockaddr_in sin; ++ * ast_rtp_instance_get_remote_address(instance, &sin); ++ * \endcode ++ * ++ * This retrieves the current remote address set on the instance pointed to by instance and puts the value ++ * into the sin structure. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); ++ ++/*! ++ * \brief Set the value of an RTP instance extended property ++ * ++ * \param instance The RTP instance to set the extended property on ++ * \param property The extended property to set ++ * \param value The value to set the extended property to ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_set_extended_prop(struct ast_rtp_instance *instance, int property, void *value); ++ ++/*! ++ * \brief Get the value of an RTP instance extended property ++ * ++ * \param instance The RTP instance to get the extended property on ++ * \param property The extended property to get ++ * ++ * \since 1.6.3 ++ */ ++void *ast_rtp_instance_get_extended_prop(struct ast_rtp_instance *instance, int property); ++ ++/*! ++ * \brief Set the value of an RTP instance property ++ * ++ * \param instance The RTP instance to set the property on ++ * \param property The property to modify ++ * \param value The value to set the property to ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_prop(instance, AST_RTP_PROPERTY_NAT, 1); ++ * \endcode ++ * ++ * This enables the AST_RTP_PROPERTY_NAT property on the instance pointed to by instance. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_set_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value); ++ ++/*! ++ * \brief Get the value of an RTP instance property ++ * ++ * \param instance The RTP instance to get the property from ++ * \param property The property to get ++ * ++ * \retval Current value of the property ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT); ++ * \endcode ++ * ++ * This returns the current value of the NAT property on the instance pointed to by instance. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_get_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property); ++ ++/*! ++ * \brief Get the codecs structure of an RTP instance ++ * ++ * \param instance The RTP instance to get the codecs structure from ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_codecs *codecs = ast_rtp_instance_get_codecs(instance); ++ * \endcode ++ * ++ * This gets the codecs structure on the RTP instance pointed to by 'instance'. ++ * ++ * \since 1.6.3 ++ */ ++struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Clear payload information from an RTP instance ++ * ++ * \param codecs The codecs structure that payloads will be cleared from ++ * \param instance Optionally the instance that the codecs structure belongs to ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_codecs codecs; ++ * ast_rtp_codecs_payloads_clear(&codecs, NULL); ++ * \endcode ++ * ++ * This clears the codecs structure and puts it into a pristine state. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Set payload information on an RTP instance to the default ++ * ++ * \param codecs The codecs structure to set defaults on ++ * \param instance Optionally the instance that the codecs structure belongs to ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_codecs codecs; ++ * ast_rtp_codecs_payloads_default(&codecs, NULL); ++ * \endcode ++ * ++ * This sets the default payloads on the codecs structure. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Copy payload information from one RTP instance to another ++ * ++ * \param src The source codecs structure ++ * \param dst The destination codecs structure that the values from src will be copied to ++ * \param instance Optionally the instance that the dst codecs structure belongs to ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_codecs_payloads_copy(&codecs0, &codecs1, NULL); ++ * \endcode ++ * ++ * This copies the payloads from the codecs0 structure to the codecs1 structure, overwriting any current values. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Record payload information that was seen in an m= SDP line ++ * ++ * \param codecs The codecs structure to muck with ++ * \param instance Optionally the instance that the codecs structure belongs to ++ * \param payload Numerical payload that was seen in the m= SDP line ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, 0); ++ * \endcode ++ * ++ * This records that the numerical payload '0' was seen in the codecs structure. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload); ++ ++/*! ++ * \brief Record payload information that was seen in an a=rtpmap: SDP line ++ * ++ * \param codecs The codecs structure to muck with ++ * \param instance Optionally the instance that the codecs structure belongs to ++ * \param payload Numerical payload that was seen in the a=rtpmap: SDP line ++ * \param mimetype The string mime type that was seen ++ * \param mimesubtype The strin mime sub type that was seen ++ * \param options Optional options that may change the behavior of this specific payload ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_codecs_payloads_set_rtpmap_type(&codecs, NULL, 0, "audio", "PCMU", 0); ++ * \endcode ++ * ++ * This records that the numerical payload '0' was seen with mime type 'audio' and sub mime type 'PCMU' in the codecs structure. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload, char *mimetype, char *mimesubtype, enum ast_rtp_options options); ++ ++/*! ++ * \brief Set payload type to a known MIME media type for a codec with a specific sample rate ++ * ++ * \param rtp RTP structure to modify ++ * \param instance Optionally the instance that the codecs structure belongs to ++ * \param pt Payload type entry to modify ++ * \param mimetype top-level MIME type of media stream (typically "audio", "video", "text", etc.) ++ * \param mimesubtype MIME subtype of media stream (typically a codec name) ++ * \param options Zero or more flags from the ast_rtp_options enum ++ * \param sample_rate The sample rate of the media stream ++ * ++ * This function 'fills in' an entry in the list of possible formats for ++ * a media stream associated with an RTP structure. ++ * ++ * \retval 0 on success ++ * \retval -1 if the payload type is out of range ++ * \retval -2 if the mimeType/mimeSubtype combination was not found ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt, ++ char *mimetype, char *mimesubtype, ++ enum ast_rtp_options options, ++ unsigned int sample_rate); ++ ++/*! ++ * \brief Remove payload information ++ * ++ * \param codecs The codecs structure to muck with ++ * \param instance Optionally the instance that the codecs structure belongs to ++ * \param payload Numerical payload to unset ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_codecs_payloads_unset(&codecs, NULL, 0); ++ * \endcode ++ * ++ * This clears the payload '0' from the codecs structure. It will be as if it was never set. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload); ++ ++/*! ++ * \brief Retrieve payload information by payload ++ * ++ * \param codecs Codecs structure to look in ++ * \param payload Numerical payload to look up ++ * ++ * \retval Payload information ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_payload_type payload_type; ++ * payload_type = ast_rtp_codecs_payload_lookup(&codecs, 0); ++ * \endcode ++ * ++ * This looks up the information for payload '0' from the codecs structure. ++ * ++ * \since 1.6.3 ++ */ ++struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs *codecs, int payload); ++ ++/*! ++ * \brief Get the sample rate associated with known RTP payload types ++ * ++ * \param asterisk_format True if the value in the 'code' parameter is an AST_FORMAT value ++ * \param code Format code, either from AST_FORMAT list or from AST_RTP list ++ * ++ * \return the sample rate if the format was found, zero if it was not found ++ * ++ * \since 1.6.3 ++ */ ++unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, int code); ++ ++/*! ++ * \brief Retrieve all formats that were found ++ * ++ * \param codecs Codecs structure to look in ++ * \param astFormats An integer to put the Asterisk formats in ++ * \param nonastformats An integer to put the non-Asterisk formats in ++ * ++ * Example usage: ++ * ++ * \code ++ * int astformats, nonastformats; ++ * ast_rtp_codecs_payload_Formats(&codecs, &astformats, &nonastformats); ++ * \endcode ++ * ++ * This retrieves all the formats known about in the codecs structure and puts the Asterisk ones in the integer ++ * pointed to by astformats and the non-Asterisk ones in the integer pointed to by nonastformats. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, int *astformats, int *nonastformats); ++ ++/*! ++ * \brief Retrieve a payload based on whether it is an Asterisk format and the code ++ * ++ * \param codecs Codecs structure to look in ++ * \param asterisk_format Non-zero if the given code is an Asterisk format value ++ * \param code The format to look for ++ * ++ * \retval Numerical payload ++ * ++ * Example usage: ++ * ++ * \code ++ * int payload = ast_rtp_codecs_payload_code(&codecs, 1, AST_FORMAT_ULAW); ++ * \endcode ++ * ++ * This looks for the numerical payload for ULAW in the codecs structure. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, const int asterisk_format, const int code); ++ ++/*! ++ * \brief Retrieve mime subtype information on a payload ++ * ++ * \param asterisk_format Non-zero if the given code is an Asterisk format value ++ * \param code Format to look up ++ * \param options Additional options that may change the result ++ * ++ * \retval Mime subtype success ++ * \retval NULL failure ++ * ++ * Example usage: ++ * ++ * \code ++ * const char *subtype = ast_rtp_lookup_mime_subtype2(1, AST_FORMAT_ULAW, 0); ++ * \endcode ++ * ++ * This looks up the mime subtype for the ULAW format. ++ * ++ * \since 1.6.3 ++ */ ++const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, const int code, enum ast_rtp_options options); ++ ++/*! ++ * \brief Convert formats into a string and put them into a buffer ++ * ++ * \param buf Buffer to put the mime output into ++ * \param capability Formats that we are looking up ++ * \param asterisk_format Non-zero if the given capability are Asterisk format capabilities ++ * \param options Additional options that may change the result ++ * ++ * \retval non-NULL success ++ * \retval NULL failure ++ * ++ * Example usage: ++ * ++ * \code ++ * char buf[256] = ""; ++ * char *mime = ast_rtp_lookup_mime_multiple2(&buf, sizeof(buf), AST_FORMAT_ULAW | AST_FORMAT_ALAW, 1, 0); ++ * \endcode ++ * ++ * This returns the mime values for ULAW and ALAW in the buffer pointed to by buf. ++ * ++ * \since 1.6.3 ++ */ ++char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, const int capability, const int asterisk_format, enum ast_rtp_options options); ++ ++/*! ++ * \brief Set codec packetization preferences ++ * ++ * \param codecs Codecs structure to muck with ++ * \param instance Optionally the instance that the codecs structure belongs to ++ * \param prefs Codec packetization preferences ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_codecs_packetization_set(&codecs, NULL, &prefs); ++ * \endcode ++ * ++ * This sets the packetization preferences pointed to by prefs on the codecs structure pointed to by codecs. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_codecs_packetization_set(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, struct ast_codec_pref *prefs); ++ ++/*! ++ * \brief Begin sending a DTMF digit ++ * ++ * \param instance The RTP instance to send the DTMF on ++ * \param digit What DTMF digit to send ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_dtmf_begin(instance, '1'); ++ * \endcode ++ * ++ * This starts sending the DTMF '1' on the RTP instance pointed to by instance. It will ++ * continue being sent until it is ended. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_dtmf_begin(struct ast_rtp_instance *instance, char digit); ++ ++/*! ++ * \brief Stop sending a DTMF digit ++ * ++ * \param instance The RTP instance to stop the DTMF on ++ * \param digit What DTMF digit to stop ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_dtmf_end(instance, '1'); ++ * \endcode ++ * ++ * This stops sending the DTMF '1' on the RTP instance pointed to by instance. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_dtmf_end(struct ast_rtp_instance *instance, char digit); ++ ++/*! ++ * \brief Set the DTMF mode that should be used ++ * ++ * \param instance the RTP instance to set DTMF mode on ++ * \param dtmf_mode The DTMF mode that is in use ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_dtmf_mode_set(instance, AST_RTP_DTMF_MODE_RFC2833); ++ * \endcode ++ * ++ * This sets the RTP instance to use RFC2833 for DTMF transmission and receiving. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_dtmf_mode_set(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode); ++ ++/*! ++ * \brief Get the DTMF mode of an RTP instance ++ * ++ * \param instance The RTP instance to get the DTMF mode of ++ * ++ * \retval DTMF mode ++ * ++ * Example usage: ++ * ++ * \code ++ * enum ast_rtp_dtmf_mode dtmf_mode = ast_rtp_instance_dtmf_mode_get(instance); ++ * \endcode ++ * ++ * This gets the DTMF mode set on the RTP instance pointed to by 'instance'. ++ * ++ * \since 1.6.3 ++ */ ++enum ast_rtp_dtmf_mode ast_rtp_instance_dtmf_mode_get(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Indicate a new source of audio has dropped in ++ * ++ * \param instance Instance that the new media source is feeding into ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_new_source(instance); ++ * \endcode ++ * ++ * This indicates that a new source of media is feeding the instance pointed to by ++ * instance. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_new_source(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Set QoS parameters on an RTP session ++ * ++ * \param instance Instance to set the QoS parameters on ++ * \param tos Terms of service value ++ * \param cos Class of service value ++ * \param desc What is setting the QoS values ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_qos(instance, 0, 0, "Example"); ++ * \endcode ++ * ++ * This sets the TOS and COS values to 0 on the instance pointed to by instance. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); ++ ++/*! ++ * \brief Stop an RTP instance ++ * ++ * \param instance Instance that media is no longer going to at this time ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_stop(instance); ++ * \endcode ++ * ++ * This tells the RTP engine being used for the instance pointed to by instance ++ * that media is no longer going to it at this time, but may in the future. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_stop(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Get the file descriptor for an RTP session (or RTCP) ++ * ++ * \param instance Instance to get the file descriptor for ++ * \param rtcp Whether to retrieve the file descriptor for RTCP or not ++ * ++ * \retval fd success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * int rtp_fd = ast_rtp_instance_fd(instance, 0); ++ * \endcode ++ * ++ * This retrieves the file descriptor for the socket carrying media on the instance ++ * pointed to by instance. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_fd(struct ast_rtp_instance *instance, int rtcp); ++ ++/*! ++ * \brief Get the RTP glue that binds a channel to the RTP engine ++ * ++ * \param type Name of the glue we want ++ * ++ * \retval non-NULL success ++ * \retval NULL failure ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_glue *glue = ast_rtp_instance_get_glue("Example"); ++ * \endcode ++ * ++ * This retrieves the RTP glue that has the name 'Example'. ++ * ++ * \since 1.6.3 ++ */ ++struct ast_rtp_glue *ast_rtp_instance_get_glue(const char *type); ++ ++/*! ++ * \brief Bridge two channels that use RTP instances ++ * ++ * \param c0 First channel part of the bridge ++ * \param c1 Second channel part of the bridge ++ * \param flags Bridging flags ++ * \param fo If a frame needs to be passed up it is stored here ++ * \param rc Channel that passed the above frame up ++ * \param timeoutms How long the channels should be bridged for ++ * ++ * \retval Bridge result ++ * ++ * \note This should only be used by channel drivers in their technology declaration. ++ * ++ * \since 1.6.3 ++ */ ++enum ast_bridge_result ast_rtp_instance_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); ++ ++/*! ++ * \brief Get the other RTP instance that an instance is bridged to ++ * ++ * \param instance The RTP instance that we want ++ * ++ * \retval non-NULL success ++ * \retval NULL failure ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_instance *bridged = ast_rtp_instance_get_bridged(instance0); ++ * \endcode ++ * ++ * This gets the RTP instance that instance0 is bridged to. ++ * ++ * \since 1.6.3 ++ */ ++struct ast_rtp_instance *ast_rtp_instance_get_bridged(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Make two channels compatible for early bridging ++ * ++ * \param c0 First channel part of the bridge ++ * \param c1 Second channel part of the bridge ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struct ast_channel *c1); ++ ++/*! ++ * \brief Early bridge two channels that use RTP instances ++ * ++ * \param c0 First channel part of the bridge ++ * \param c1 Second channel part of the bridge ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * \note This should only be used by channel drivers in their technology declaration. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1); ++ ++/*! ++ * \brief Initialize RED support on an RTP instance ++ * ++ * \param instance The instance to initialize RED support on ++ * \param buffer_time How long to buffer before sending ++ * \param payloads Payload values ++ * \param generations Number of generations ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations); ++ ++/*! ++ * \brief Buffer a frame in an RTP instance for RED ++ * ++ * \param instance The instance to buffer the frame on ++ * \param frame Frame that we want to buffer ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame); ++ ++/*! ++ * \brief Retrieve statistics about an RTP instance ++ * ++ * \param instance Instance to get statistics on ++ * \param stats Structure to put results into ++ * \param stat What statistic(s) to retrieve ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * struct ast_rtp_instance_stats stats; ++ * ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_ALL); ++ * \endcode ++ * ++ * This retrieves all statistics the underlying RTP engine supports and puts the values into the ++ * stats structure. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_get_stats(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat); ++ ++/*! ++ * \brief Set standard statistics from an RTP instance on a channel ++ * ++ * \param chan Channel to set the statistics on ++ * \param instance The RTP instance that statistics will be retrieved from ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_stats_vars(chan, rtp); ++ * \endcode ++ * ++ * This retrieves standard statistics from the RTP instance rtp and sets it on the channel pointed to ++ * by chan. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_set_stats_vars(struct ast_channel *chan, struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Retrieve quality statistics about an RTP instance ++ * ++ * \param instance Instance to get statistics on ++ * \param field What quality statistic to retrieve ++ * \param buf What buffer to put the result into ++ * \param size Size of the above buffer ++ * ++ * \retval non-NULL success ++ * \retval NULL failure ++ * ++ * Example usage: ++ * ++ * \code ++ * char quality[AST_MAX_USER_FIELD]; ++ * ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, &buf, sizeof(buf)); ++ * \endcode ++ * ++ * This retrieves general quality statistics and places a text representation into the buf pointed to by buf. ++ * ++ * \since 1.6.3 ++ */ ++char *ast_rtp_instance_get_quality(struct ast_rtp_instance *instance, enum ast_rtp_instance_stat_field field, char *buf, size_t size); ++ ++/*! ++ * \brief Request that the underlying RTP engine provide audio frames in a specific format ++ * ++ * \param instance The RTP instance to change read format on ++ * \param format Format that frames are wanted in ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_read_format(instance, AST_FORMAT_ULAW); ++ * \endcode ++ * ++ * This requests that the RTP engine provide audio frames in the ULAW format. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_set_read_format(struct ast_rtp_instance *instance, int format); ++ ++/*! ++ * \brief Tell underlying RTP engine that audio frames will be provided in a specific format ++ * ++ * \param instance The RTP instance to change write format on ++ * \param format Format that frames will be provided in ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_write_format(instance, AST_FORMAT_ULAW); ++ * \endcode ++ * ++ * This tells the underlying RTP engine that audio frames will be provided to it in ULAW format. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_set_write_format(struct ast_rtp_instance *instance, int format); ++ ++/*! ++ * \brief Request that the underlying RTP engine make two RTP instances compatible with eachother ++ * ++ * \param chan Our own Asterisk channel ++ * \param instance The first RTP instance ++ * \param peer The peer Asterisk channel ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_make_compatible(instance, peer); ++ * \endcode ++ * ++ * This makes the RTP instance for 'peer' compatible with 'instance' and vice versa. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_make_compatible(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_channel *peer); ++ ++/*! ++ * \brief Indicate to the RTP engine that packets are now expected to be sent/received on the RTP instance ++ * ++ * \param instance The RTP instance ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_activate(instance); ++ * \endcode ++ * ++ * This tells the underlying RTP engine of instance that packets will now flow. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_activate(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Request that the underlying RTP engine send a STUN BIND request ++ * ++ * \param instance The RTP instance ++ * \param suggestion The suggested destination ++ * \param username Optionally a username for the request ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_stun_request(instance, NULL, NULL); ++ * \endcode ++ * ++ * This requests that the RTP engine send a STUN BIND request on the session pointed to by ++ * 'instance'. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username); ++ ++/*! ++ * \brief Set the RTP timeout value ++ * ++ * \param instance The RTP instance ++ * \param timeout Value to set the timeout to ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_timeout(instance, 5000); ++ * \endcode ++ * ++ * This sets the RTP timeout value on 'instance' to be 5000. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_set_timeout(struct ast_rtp_instance *instance, int timeout); ++ ++/*! ++ * \brief Set the RTP timeout value for when the instance is on hold ++ * ++ * \param instance The RTP instance ++ * \param timeout Value to set the timeout to ++ * ++ * Example usage: ++ * ++ * \code ++ * ast_rtp_instance_set_hold_timeout(instance, 5000); ++ * \endcode ++ * ++ * This sets the RTP hold timeout value on 'instance' to be 5000. ++ * ++ * \since 1.6.3 ++ */ ++void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int timeout); ++ ++/*! ++ * \brief Get the RTP timeout value ++ * ++ * \param instance The RTP instance ++ * ++ * \retval timeout value ++ * ++ * Example usage: ++ * ++ * \code ++ * int timeout = ast_rtp_instance_get_timeout(instance); ++ * \endcode ++ * ++ * This gets the RTP timeout value for the RTP instance pointed to by 'instance'. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance); ++ ++/*! ++ * \brief Get the RTP timeout value for when an RTP instance is on hold ++ * ++ * \param instance The RTP instance ++ * ++ * \retval timeout value ++ * ++ * Example usage: ++ * ++ * \code ++ * int timeout = ast_rtp_instance_get_hold_timeout(instance); ++ * \endcode ++ * ++ * This gets the RTP hold timeout value for the RTP instance pointed to by 'instance'. ++ * ++ * \since 1.6.3 ++ */ ++int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance); ++ ++#if defined(__cplusplus) || defined(c_plusplus) ++} ++#endif ++ ++#endif /* _ASTERISK_RTP_ENGINE_H */ + +Property changes on: include/asterisk/rtp_engine.h +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: include/asterisk/utils.h +=================================================================== +--- a/include/asterisk/utils.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/utils.h (.../trunk) (revision 186562) +@@ -688,7 +688,7 @@ + * This is set in asterisk.conf, or determined automatically by taking the mac + * address of an Ethernet interface on the system. + */ +-extern struct ast_eid g_eid; ++extern struct ast_eid ast_eid_default; + + /*! + * \brief Fill in an ast_eid with the default eid of this machine +Index: include/asterisk/channel.h +=================================================================== +--- a/include/asterisk/channel.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/channel.h (.../trunk) (revision 186562) +@@ -102,7 +102,7 @@ + can create a native bridge without sending media through the + core. + +- Native briding can be disabled by a number of reasons, ++ Native bridging can be disabled by a number of reasons, + like DTMF being needed by the core or codecs being incompatible + so a transcoding module is needed. + +@@ -183,45 +183,173 @@ + void (*digit)(struct ast_channel *chan, char digit); + }; + +-/*! \brief Structure for all kinds of caller ID identifications. ++/*! ++ * \brief Structure for all kinds of caller ID identifications. + * \note All string fields here are malloc'ed, so they need to be + * freed when the structure is deleted. + * Also, NULL and "" must be considered equivalent. + * +- * SIP and IAX2 has utf8 encoded Unicode caller ID names. ++ * \note SIP and IAX2 has utf8 encoded Unicode caller ID names. + * In some cases, we also have an alternative (RPID) E.164 number that can be used +- * as caller ID on numeric E.164 phone networks (DAHDI or SIP/IAX2 to pstn gateway). ++ * as caller ID on numeric E.164 phone networks (DAHDI or SIP/IAX2 to PSTN gateway). + * + * \todo Implement settings for transliteration between UTF8 caller ID names in + * to Ascii Caller ID's (DAHDI). Östen Åsklund might be transliterated into +- * Osten Asklund or Oesten Aasklund depending upon language and person... +- * We need automatic routines for incoming calls and static settings for +- * our own accounts. ++ * Osten Asklund or Oesten Aasklund depending upon language and person... ++ * We need automatic routines for incoming calls and static settings for ++ * our own accounts. + */ + struct ast_callerid { +- char *cid_dnid; /*!< Malloc'd Dialed Number Identifier */ +- char *cid_num; /*!< Malloc'd Caller Number */ +- char *cid_name; /*!< Malloc'd Caller Name (ASCII) */ +- char *cid_ani; /*!< Malloc'd ANI */ +- char *cid_rdnis; /*!< Malloc'd RDNIS */ +- int cid_pres; /*!< Callerid presentation/screening */ +- int cid_ani2; /*!< Callerid ANI 2 (Info digits) */ +- int cid_ton; /*!< Callerid Type of Number */ +- int cid_tns; /*!< Callerid Transit Network Select */ ++ /*! ++ * \brief Malloc'd Dialed Number Identifier ++ * (Field will eventually move to struct ast_channel.dialed.number) ++ */ ++ char *cid_dnid; ++ ++ /*! ++ * \brief Malloc'd Caller Number ++ * (Field will eventually move to struct ast_channel.caller.id.number) ++ */ ++ char *cid_num; ++ ++ /*! ++ * \brief Malloc'd Caller Name (ASCII) ++ * (Field will eventually move to struct ast_channel.caller.id.name) ++ */ ++ char *cid_name; ++ ++ /*! ++ * \brief Malloc'd Automatic Number Identification (ANI) ++ * (Field will eventually move to struct ast_channel.caller.ani) ++ */ ++ char *cid_ani; ++ ++ /*! ++ * \brief Malloc'd Redirecting Directory Number Information Service (RDNIS) ++ * (Field will eventually move to struct ast_channel.redirecting.from.number) ++ */ ++ char *cid_rdnis; ++ ++ /*! ++ * \brief Callerid Q.931 encoded number presentation/screening fields ++ * (Field will eventually move to struct ast_channel.caller.id.number_presentation) ++ */ ++ int cid_pres; ++ ++ /*! ++ * \brief Callerid ANI 2 (Info digits) ++ * (Field will eventually move to struct ast_channel.caller.ani2) ++ */ ++ int cid_ani2; ++ ++ /*! ++ * \brief Callerid Q.931 encoded type-of-number/numbering-plan fields ++ * \note Currently this value is mostly just passed around the system. ++ * The H.323 interfaces set the value from received messages and uses the value for sent messages. ++ * The DAHDI PRI interfaces set the value from received messages but does not use it for sent messages. ++ * You can read it and set it but only H.323 uses it. ++ * (Field will eventually move to struct ast_channel.caller.id.number_type) ++ */ ++ int cid_ton; ++ ++ /*! ++ * \brief Callerid Transit Network Select ++ * \note Currently this value is just passed around the system. ++ * You can read it and set it but it is never used for anything. ++ * (Field will eventually move to struct ast_channel.dialed.transit_network_select) ++ */ ++ int cid_tns; + }; + +-/*! \brief +- Structure to describe a channel "technology", ie a channel driver +- See for examples: +- \arg chan_iax2.c - The Inter-Asterisk exchange protocol +- \arg chan_sip.c - The SIP channel driver +- \arg chan_dahdi.c - PSTN connectivity (TDM, PRI, T1/E1, FXO, FXS) ++/*! ++ * \since 1.6.3 ++ * \brief Information needed to identify an endpoint in a call. ++ * \note All string fields here are malloc'ed, so they need to be ++ * freed when the structure is deleted. ++ * \note NULL and "" must be considered equivalent. ++ */ ++struct ast_party_id { ++ /*! \brief Subscriber phone number (Malloced) */ ++ char *number; + +- If you develop your own channel driver, this is where you +- tell the PBX at registration of your driver what properties +- this driver supports and where different callbacks are +- implemented. +-*/ ++ /*! \brief Subscriber name (Malloced) */ ++ char *name; ++ ++ /*! \brief Q.931 encoded type-of-number/numbering-plan fields */ ++ int number_type; ++ ++ /*! \brief Q.931 encoded number presentation/screening fields */ ++ int number_presentation; ++}; ++ ++/*! ++ * \since 1.6.3 ++ * \brief Connected Line/Party information. ++ * \note All string fields here are malloc'ed, so they need to be ++ * freed when the structure is deleted. ++ * \note NULL and "" must be considered equivalent. ++ */ ++struct ast_party_connected_line { ++ struct ast_party_id id; /*! \brief Connected party ID */ ++ ++ /*! ++ * \brief Automatic Number Identification (ANI) (Malloced) ++ * \note Not really part of connected line data but needed to ++ * save the corresponding caller id value. ++ */ ++ char *ani; ++ ++ /*! ++ * \brief Automatic Number Identification 2 (Info Digits) ++ * \note Not really part of connected line data but needed to ++ * save the corresponding caller id value. ++ */ ++ int ani2; ++ ++ /*! \brief Information about the source of an update (Q.SIG/ISDN requirement). ++ * \note enum AST_CONNECTED_LINE_UPDATE_SOURCE values ++ * for Normal-Answer, Call-transfer, Call-diversion ++ */ ++ int source; ++}; ++ ++/*! ++ * \since 1.6.3 ++ * \brief Redirecting Line information. ++ * RDNIS (Redirecting Directory Number Information Service) ++ * Where a call diversion or transfer was invoked. ++ * \note All string fields here are malloc'ed, so they need to be ++ * freed when the structure is deleted. ++ * \note NULL and "" must be considered equivalent. ++ */ ++struct ast_party_redirecting { ++ /*! \brief Who is redirecting the call (Sent to the party the call is redirected toward) */ ++ struct ast_party_id from; ++ ++ /*! \brief Call is redirecting to a new party (Sent to the caller) */ ++ struct ast_party_id to; ++ ++ /*! \brief Number of times the call was redirected */ ++ int count; ++ ++ /*! \brief enum AST_REDIRECTING_REASON value for redirection */ ++ int reason; ++}; ++ ++/*! ++ * \brief ++ * Structure to describe a channel "technology", ie a channel driver ++ * See for examples: ++ * \arg chan_iax2.c - The Inter-Asterisk exchange protocol ++ * \arg chan_sip.c - The SIP channel driver ++ * \arg chan_dahdi.c - PSTN connectivity (TDM, PRI, T1/E1, FXO, FXS) ++ * ++ * \details ++ * If you develop your own channel driver, this is where you ++ * tell the PBX at registration of your driver what properties ++ * this driver supports and where different callbacks are ++ * implemented. ++ */ + struct ast_channel_tech { + const char * const type; + const char * const description; +@@ -250,7 +378,7 @@ + int (* const send_digit_end)(struct ast_channel *chan, char digit, unsigned int duration); + + /*! \brief Call a given phone number (address, etc), but don't +- take longer than timeout seconds to do so. */ ++ * take longer than timeout seconds to do so. */ + int (* const call)(struct ast_channel *chan, char *addr, int timeout); + + /*! \brief Hangup (and possibly destroy) the channel */ +@@ -381,7 +509,8 @@ + T38_STATE_NEGOTIATED, /*!< T38 established */ + }; + +-/*! \brief Main Channel structure associated with a channel. ++/*! ++ * \brief Main Channel structure associated with a channel. + * This is the side of it mostly used by the pbx and call management. + * + * \note XXX It is important to remember to increment .cleancount each time +@@ -395,7 +524,6 @@ + * and 8-byte fields causes 4 bytes of padding to be added before many + * 8-byte fields. + */ +- + struct ast_channel { + const struct ast_channel_tech *tech; /*!< Technology (point to channel driver) */ + void *tech_pvt; /*!< Private data used by the technology driver */ +@@ -403,8 +531,8 @@ + void *generatordata; /*!< Current generator data if there is any */ + struct ast_generator *generator; /*!< Current active data generator */ + struct ast_channel *_bridge; /*!< Who are we bridged to, if we're bridged. +- Who is proxying for us, if we are proxied (i.e. chan_agent). +- Do not access directly, use ast_bridged_channel(chan) */ ++ * Who is proxying for us, if we are proxied (i.e. chan_agent). ++ * Do not access directly, use ast_bridged_channel(chan) */ + struct ast_channel *masq; /*!< Channel that will masquerade as us */ + struct ast_channel *masqr; /*!< Who we are masquerading as */ + const char *blockproc; /*!< Procedure causing blocking */ +@@ -421,7 +549,7 @@ + struct ast_audiohook_list *audiohooks; + struct ast_cdr *cdr; /*!< Call Detail Record */ + struct ast_tone_zone *zone; /*!< Tone zone as set in indications.conf or +- in the CHANNEL dialplan function */ ++ * in the CHANNEL dialplan function */ + struct ast_channel_monitor *monitor; /*!< Channel monitoring */ + #ifdef HAVE_EPOLL + struct ast_epoll_data *epfd_data[AST_MAX_FDS]; +@@ -441,7 +569,29 @@ + struct timeval whentohangup; /*!< Non-zero, set to actual time when channel is to be hung up */ + pthread_t blocker; /*!< If anyone is blocking, this is them */ + ast_mutex_t lock_dont_use; /*!< Lock a channel for some operations. See ast_channel_lock() */ +- struct ast_callerid cid; /*!< Caller ID, name, presentation etc */ ++ ++ /*! ++ * \brief Channel Caller ID information. ++ * \note The caller id information is the caller id of this ++ * channel when it is used to initiate a call. ++ */ ++ struct ast_callerid cid; ++ ++ /*! ++ * \brief Channel Connected Line ID information. ++ * \note The connected line information identifies the channel ++ * connected/bridged to this channel. ++ */ ++ struct ast_party_connected_line connected; ++ ++ /*! ++ * \brief Redirecting/Diversion information ++ * \note Until struct ast_channel.cid.cid_rdnis is replaced ++ * with ast_channel.redirecting.from.number, the ++ * ast_channel.redirecting.from.number field is not used. ++ */ ++ struct ast_party_redirecting redirecting; ++ + struct ast_frame dtmff; /*!< DTMF frame */ + struct varshead varshead; /*!< A linked list for channel variables. See \ref AstChanVar */ + ast_group_t callgroup; /*!< Call group for call pickups */ +@@ -456,11 +606,11 @@ + unsigned long outsmpl; /*!< Track the read/written samples for monitor use */ + + int fds[AST_MAX_FDS]; /*!< File descriptors for channel -- Drivers will poll on +- these file descriptors, so at least one must be non -1. +- See \arg \ref AstFileDesc */ ++ * these file descriptors, so at least one must be non -1. ++ * See \arg \ref AstFileDesc */ + int cdrflags; /*!< Call Detail Record Flags */ + int _softhangup; /*!< Whether or not we have been hung up... Do not set this value +- directly, use ast_softhangup() */ ++ * directly, use ast_softhangup() */ + int fdno; /*!< Which fd had an event detected on */ + int streamid; /*!< For streaming playback, the schedule ID */ + int vstreamid; /*!< For streaming video playback, the schedule ID */ +@@ -473,9 +623,9 @@ + int amaflags; /*!< Set BEFORE PBX is started to determine AMA flags */ + enum ast_channel_adsicpe adsicpe; /*!< Whether or not ADSI is detected on CPE */ + unsigned int fin; /*!< Frames in counters. The high bit is a debug mask, so +- the counter is only in the remaining bits */ ++ * the counter is only in the remaining bits */ + unsigned int fout; /*!< Frames out counters. The high bit is a debug mask, so +- the counter is only in the remaining bits */ ++ * the counter is only in the remaining bits */ + int hangupcause; /*!< Why is the channel hanged up. See causes.h */ + unsigned int flags; /*!< channel flags of AST_FLAG_ type */ + int alertpipe[2]; +@@ -490,12 +640,13 @@ + #endif + int visible_indication; /*!< Indication currently playing on the channel */ + +- unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */ ++ unsigned short transfercapability; /*!< ISDN Transfer Capability - AST_FLAG_DIGITAL is not enough */ + + union { + char unused_old_dtmfq[AST_MAX_EXTENSION]; /*!< (deprecated, use readq instead) Any/all queued DTMF characters */ + struct { + struct ast_bridge *bridge; /*!< Bridge this channel is participating in */ ++ struct ast_timer *timer; /*!< timer object that provided timingfd */ + }; + }; + +@@ -508,17 +659,21 @@ + + /*! \brief ast_channel_tech Properties */ + enum { +- /*! \brief Channels have this property if they can accept input with jitter; +- * i.e. most VoIP channels */ ++ /*! ++ * \brief Channels have this property if they can accept input with jitter; ++ * i.e. most VoIP channels ++ */ + AST_CHAN_TP_WANTSJITTER = (1 << 0), +- /*! \brief Channels have this property if they can create jitter; +- * i.e. most VoIP channels */ ++ /*! ++ * \brief Channels have this property if they can create jitter; ++ * i.e. most VoIP channels ++ */ + AST_CHAN_TP_CREATESJITTER = (1 << 1), + }; + + /*! \brief ast_channel flags */ + enum { +- /*! Queue incoming dtmf, to be released when this flag is turned off */ ++ /*! Queue incoming DTMF, to be released when this flag is turned off */ + AST_FLAG_DEFER_DTMF = (1 << 1), + /*! write should be interrupt generator */ + AST_FLAG_WRITE_INT = (1 << 2), +@@ -549,7 +704,7 @@ + * to instead only generate END frames. */ + AST_FLAG_END_DTMF_ONLY = (1 << 14), + /*! Flag to show channels that this call is hangup due to the fact that the call +- was indeed anwered, but in another channel */ ++ was indeed answered, but in another channel */ + AST_FLAG_ANSWERED_ELSEWHERE = (1 << 15), + /*! This flag indicates that on a masquerade, an active stream should not + * be carried over */ +@@ -674,7 +829,6 @@ + * \retval 0 success + * \retval non-zero failure + */ +- + int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore); + + /*! +@@ -777,6 +931,7 @@ + * \retval 0 success + * \retval non-zero failure + * ++ * \details + * The supplied payload data is copied into the frame, so the caller's copy + * is not modified nor freed, and the resulting frame will retain a copy of + * the data even if the caller frees their local copy. +@@ -811,6 +966,7 @@ + * \param data data to pass to the channel requester + * \param status status + * ++ * \details + * Request a channel of a given type, with data as optional information used + * by the low level module + * +@@ -854,54 +1010,63 @@ + struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, + int timeout, int *reason, const char *cid_num, const char *cid_name, struct outgoing_helper *oh); + +-/*!\brief Register a channel technology (a new channel driver) ++/*! ++ * \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" + * \return Returns 0 on success, -1 on failure. + */ + int ast_channel_register(const struct ast_channel_tech *tech); + +-/*! \brief Unregister a channel technology ++/*! ++ * \brief Unregister a channel technology + * \param tech Structure defining channel technology or "type" that was previously registered + * \return No return value. + */ + void ast_channel_unregister(const struct ast_channel_tech *tech); + +-/*! \brief Get a channel technology structure by name ++/*! ++ * \brief Get a channel technology structure by name + * \param name name of technology to find + * \return a pointer to the structure, or NULL if no matching technology found + */ + const struct ast_channel_tech *ast_get_channel_tech(const char *name); + + #ifdef CHANNEL_TRACE +-/*! \brief Update the context backtrace if tracing is enabled ++/*! ++ * \brief Update the context backtrace if tracing is enabled + * \return Returns 0 on success, -1 on failure + */ + int ast_channel_trace_update(struct ast_channel *chan); + +-/*! \brief Enable context tracing in the channel ++/*! ++ * \brief Enable context tracing in the channel + * \return Returns 0 on success, -1 on failure + */ + int ast_channel_trace_enable(struct ast_channel *chan); + +-/*! \brief Disable context tracing in the channel. ++/*! ++ * \brief Disable context tracing in the channel. + * \note Does not remove current trace entries + * \return Returns 0 on success, -1 on failure + */ + int ast_channel_trace_disable(struct ast_channel *chan); + +-/*! \brief Whether or not context tracing is enabled ++/*! ++ * \brief Whether or not context tracing is enabled + * \return Returns -1 when the trace is enabled. 0 if not. + */ + int ast_channel_trace_is_enabled(struct ast_channel *chan); + +-/*! \brief Put the channel backtrace in a string ++/*! ++ * \brief Put the channel backtrace in a string + * \return Returns the amount of lines in the backtrace. -1 on error. + */ + int ast_channel_trace_serialize(struct ast_channel *chan, struct ast_str **out); + #endif + +-/*! \brief Hang up a channel ++/*! ++ * \brief Hang up a channel + * \note This function performs a hard hangup on a channel. Unlike the soft-hangup, this function + * performs all stream stopping, etc, on the channel that needs to end. + * chan is no longer valid after this call. +@@ -916,6 +1081,7 @@ + * \param chan channel to be soft-hung-up + * \param cause Ast hangupcause for hangup + * ++ * \details + * Call the protocol layer, but don't destroy the channel structure + * (use this if you are trying to + * safely hangup a channel managed by another thread. +@@ -926,9 +1092,11 @@ + */ + int ast_softhangup(struct ast_channel *chan, int cause); + +-/*! \brief Softly hangup up a channel (no channel lock) ++/*! ++ * \brief Softly hangup up a channel (no channel lock) + * \param chan channel to be soft-hung-up +- * \param cause Ast hangupcause for hangup (see cause.h) */ ++ * \param cause Ast hangupcause for hangup (see cause.h) ++ */ + int ast_softhangup_nolock(struct ast_channel *chan, int cause); + + /*! \brief Check to see if a channel is needing hang up +@@ -943,6 +1111,7 @@ + * \param chan channel on which to check for hang up + * \param offset offset in seconds from current time + * \return 1, 0, or -1 ++ * \details + * This function compares a offset from current time with the absolute time + * out on a channel (when to hang up). If the absolute time out on a channel + * is earlier than current time plus the offset, it returns 1, if the two +@@ -965,11 +1134,13 @@ + */ + int ast_channel_cmpwhentohangup_tv(struct ast_channel *chan, struct timeval offset); + +-/*! \brief Set when to hang a channel up ++/*! ++ * \brief Set when to hang a channel up + * + * \param chan channel on which to check for hang up + * \param offset offset in seconds relative to the current time of when to hang up + * ++ * \details + * This function sets the absolute time out on a channel (when to hang up). + * + * \note This function does not require that the channel is locked before +@@ -981,7 +1152,8 @@ + */ + void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset) __attribute__((deprecated)); + +-/*! \brief Set when to hang a channel up ++/*! ++ * \brief Set when to hang a channel up + * + * \param chan channel on which to check for hang up + * \param offset offset in seconds and useconds relative to the current time of when to hang up +@@ -1001,6 +1173,7 @@ + * + * \param chan channel to answer + * ++ * \details + * This function answers a channel and handles all necessary call + * setup functions. + * +@@ -1061,18 +1234,21 @@ + */ + int __ast_answer(struct ast_channel *chan, unsigned int delay, int cdr_answer); + +-/*! \brief Make a call ++/*! ++ * \brief Make a call + * \param chan which channel to make the call on + * \param addr destination of the call + * \param timeout time to wait on for connect ++ * \details + * Place a call, take no longer than timeout ms. +- \return Returns -1 on failure, 0 on not enough time +- (does not automatically stop ringing), and +- the number of seconds the connect took otherwise. +- */ ++ * \return -1 on failure, 0 on not enough time ++ * (does not automatically stop ringing), and ++ * the number of seconds the connect took otherwise. ++ */ + int ast_call(struct ast_channel *chan, char *addr, int timeout); + +-/*! \brief Indicates condition of channel ++/*! ++ * \brief Indicates condition of channel + * \note Indicate a condition such as AST_CONTROL_BUSY, AST_CONTROL_RINGING, or AST_CONTROL_CONGESTION on a channel + * \param chan channel to change the indication + * \param condition which condition to indicate on the channel +@@ -1080,7 +1256,8 @@ + */ + int ast_indicate(struct ast_channel *chan, int condition); + +-/*! \brief Indicates condition of channel, with payload ++/*! ++ * \brief Indicates condition of channel, with payload + * \note Indicate a condition such as AST_CONTROL_HOLD with payload being music on hold class + * \param chan channel to change the indication + * \param condition which condition to indicate on the channel +@@ -1092,33 +1269,43 @@ + + /* Misc stuff ------------------------------------------------ */ + +-/*! \brief Wait for input on a channel ++/*! ++ * \brief Wait for input on a channel + * \param chan channel to wait on + * \param ms length of time to wait on the channel ++ * \details + * Wait for input on a channel for a given # of milliseconds (<0 for indefinite). +- \return Returns < 0 on failure, 0 if nothing ever arrived, and the # of ms remaining otherwise */ ++ * \retval < 0 on failure ++ * \retval 0 if nothing ever arrived ++ * \retval the # of ms remaining otherwise ++ */ + int ast_waitfor(struct ast_channel *chan, int ms); + +-/*! \brief Wait for a specified amount of time, looking for hangups ++/*! ++ * \brief Wait for a specified amount of time, looking for hangups + * \param chan channel to wait for + * \param ms length of time in milliseconds to sleep ++ * \details + * Waits for a specified amount of time, servicing the channel as required. + * \return returns -1 on hangup, otherwise 0. + */ + int ast_safe_sleep(struct ast_channel *chan, int ms); + +-/*! \brief Wait for a specified amount of time, looking for hangups and a condition argument ++/*! ++ * \brief Wait for a specified amount of time, looking for hangups and a condition argument + * \param chan channel to wait for + * \param ms length of time in milliseconds to sleep + * \param cond a function pointer for testing continue condition + * \param data argument to be passed to the condition test function + * \return returns -1 on hangup, otherwise 0. ++ * \details + * Waits for a specified amount of time, servicing the channel as required. If cond + * returns 0, this function returns. + */ + int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data ); + +-/*! \brief Waits for activity on a group of channels ++/*! ++ * \brief Waits for activity on a group of channels + * \param chan an array of pointers to channels + * \param n number of channels that are to be waited upon + * \param fds an array of fds to wait upon +@@ -1126,44 +1313,55 @@ + * \param exception exception flag + * \param outfd fd that had activity on it + * \param ms how long the wait was ++ * \details + * Big momma function here. Wait for activity on any of the n channels, or any of the nfds +- file descriptors. +- \return Returns the channel with activity, or NULL on error or if an FD +- came first. If the FD came first, it will be returned in outfd, otherwise, outfd +- will be -1 */ ++ * file descriptors. ++ * \return Returns the channel with activity, or NULL on error or if an FD ++ * came first. If the FD came first, it will be returned in outfd, otherwise, outfd ++ * will be -1 ++ */ + struct ast_channel *ast_waitfor_nandfds(struct ast_channel **chan, int n, + int *fds, int nfds, int *exception, int *outfd, int *ms); + +-/*! \brief Waits for input on a group of channels +- Wait for input on an array of channels for a given # of milliseconds. +- \return Return channel with activity, or NULL if none has activity. +- \param chan an array of pointers to channels +- \param n number of channels that are to be waited upon +- \param ms time "ms" is modified in-place, if applicable */ ++/*! ++ * \brief Waits for input on a group of channels ++ * Wait for input on an array of channels for a given # of milliseconds. ++ * \return Return channel with activity, or NULL if none has activity. ++ * \param chan an array of pointers to channels ++ * \param n number of channels that are to be waited upon ++ * \param ms time "ms" is modified in-place, if applicable ++ */ + struct ast_channel *ast_waitfor_n(struct ast_channel **chan, int n, int *ms); + +-/*! \brief Waits for input on an fd +- This version works on fd's only. Be careful with it. */ ++/*! ++ * \brief Waits for input on an fd ++ * \note This version works on fd's only. Be careful with it. ++ */ + int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception); + + +-/*! \brief Reads a frame ++/*! ++ * \brief Reads a frame + * \param chan channel to read a frame from + * \return Returns a frame, or NULL on error. If it returns NULL, you +- best just stop reading frames and assume the channel has been +- disconnected. */ ++ * best just stop reading frames and assume the channel has been ++ * disconnected. ++ */ + struct ast_frame *ast_read(struct ast_channel *chan); + +-/*! \brief Reads a frame, returning AST_FRAME_NULL frame if audio. +- \param chan channel to read a frame from +- \return Returns a frame, or NULL on error. If it returns NULL, you +- best just stop reading frames and assume the channel has been +- disconnected. +- \note Audio is replaced with AST_FRAME_NULL to avoid +- transcode when the resulting audio is not necessary. */ ++/*! ++ * \brief Reads a frame, returning AST_FRAME_NULL frame if audio. ++ * \param chan channel to read a frame from ++ * \return Returns a frame, or NULL on error. If it returns NULL, you ++ * best just stop reading frames and assume the channel has been ++ * disconnected. ++ * \note Audio is replaced with AST_FRAME_NULL to avoid ++ * transcode when the resulting audio is not necessary. ++ */ + struct ast_frame *ast_read_noaudio(struct ast_channel *chan); + +-/*! \brief Write a frame to a channel ++/*! ++ * \brief Write a frame to a channel + * This function writes the given frame to the indicated channel. + * \param chan destination channel of the frame + * \param frame frame that will be written +@@ -1171,7 +1369,8 @@ + */ + int ast_write(struct ast_channel *chan, struct ast_frame *frame); + +-/*! \brief Write video frame to a channel ++/*! ++ * \brief Write video frame to a channel + * This function writes the given frame to the indicated channel. + * \param chan destination channel of the frame + * \param frame frame that will be written +@@ -1179,7 +1378,8 @@ + */ + int ast_write_video(struct ast_channel *chan, struct ast_frame *frame); + +-/*! \brief Write text frame to a channel ++/*! ++ * \brief Write text frame to a channel + * This function writes the given frame to the indicated channel. + * \param chan destination channel of the frame + * \param frame frame that will be written +@@ -1190,7 +1390,8 @@ + /*! \brief Send empty audio to prime a channel driver */ + int ast_prod(struct ast_channel *chan); + +-/*! \brief Sets read format on channel chan ++/*! ++ * \brief Sets read format on channel chan + * Set read format for channel to whichever component of "format" is best. + * \param chan channel to change + * \param format format to change to +@@ -1198,7 +1399,8 @@ + */ + int ast_set_read_format(struct ast_channel *chan, int format); + +-/*! \brief Sets write format on channel chan ++/*! ++ * \brief Sets write format on channel chan + * Set write format for channel to whichever component of "format" is best. + * \param chan channel to change + * \param format new format for writing +@@ -1212,6 +1414,7 @@ + * \param chan channel to act upon + * \param text string of text to send on the channel + * ++ * \details + * Write text to a display on a channel + * + * \note The channel does not need to be locked before calling this function. +@@ -1221,34 +1424,35 @@ + */ + int ast_sendtext(struct ast_channel *chan, const char *text); + +-/*! \brief Receives a text character from a channel ++/*! ++ * \brief Receives a text character from a channel + * \param chan channel to act upon + * \param timeout timeout in milliseconds (0 for infinite wait) ++ * \details + * Read a char of text from a channel +- * Returns 0 on success, -1 on failure ++ * \return 0 on success, -1 on failure + */ + int ast_recvchar(struct ast_channel *chan, int timeout); + +-/*! \brief Send a DTMF digit to a channel +- * Send a DTMF digit to a channel. ++/*! ++ * \brief Send a DTMF digit to a channel. + * \param chan channel to act upon + * \param digit the DTMF digit to send, encoded in ASCII + * \param duration the duration of the digit ending in ms +- * \return Returns 0 on success, -1 on failure ++ * \return 0 on success, -1 on failure + */ + int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration); + +-/*! \brief Send a DTMF digit to a channel +- * Send a DTMF digit to a channel. ++/*! ++ * \brief Send a DTMF digit to a channel. + * \param chan channel to act upon + * \param digit the DTMF digit to send, encoded in ASCII +- * \return Returns 0 on success, -1 on failure ++ * \return 0 on success, -1 on failure + */ + int ast_senddigit_begin(struct ast_channel *chan, char digit); + +-/*! \brief Send a DTMF digit to a channel +- +- * Send a DTMF digit to a channel. ++/*! ++ * \brief Send a DTMF digit to a channel. + * \param chan channel to act upon + * \param digit the DTMF digit to send, encoded in ASCII + * \param duration the duration of the digit ending in ms +@@ -1256,7 +1460,8 @@ + */ + int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration); + +-/*! \brief Receives a text string from a channel ++/*! ++ * \brief Receives a text string from a channel + * Read a string of text from a channel + * \param chan channel to act upon + * \param timeout timeout in milliseconds (0 for infinite wait) +@@ -1264,11 +1469,12 @@ + */ + char *ast_recvtext(struct ast_channel *chan, int timeout); + +-/*! \brief Browse channels in use ++/*! ++ * \brief Browse channels in use + * Browse the channels currently in use + * \param prev where you want to start in the channel list + * \return Returns the next channel in the list, NULL on end. +- * If it returns a channel, that channel *has been locked*! ++ * If it returns a channel, that channel *has been locked*! + */ + struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev); + +@@ -1288,7 +1494,8 @@ + struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten, + const char *context); + +-/*! \brief Search for a channel based on the passed channel matching callback ++/*! ++ * \brief Search for a channel based on the passed channel matching callback + * Search for a channel based on the specified is_match callback, and return the + * first channel that we match. When returned, the channel will be locked. Note + * that the is_match callback is called with the passed channel locked, and should +@@ -1300,33 +1507,41 @@ + */ + struct ast_channel *ast_channel_search_locked(int (*is_match)(struct ast_channel *, void *), void *data); + +-/*! ! \brief Waits for a digit ++/*! ++ * \brief Waits for a digit + * \param c channel to wait for a digit on + * \param ms how many milliseconds to wait +- * \return Returns <0 on error, 0 on no entry, and the digit on success. */ ++ * \return Returns <0 on error, 0 on no entry, and the digit on success. ++ */ + int ast_waitfordigit(struct ast_channel *c, int ms); + +-/*! \brief Wait for a digit +- Same as ast_waitfordigit() with audio fd for outputting read audio and ctrlfd to monitor for reading. ++/*! ++ * \brief Wait for a digit ++ * Same as ast_waitfordigit() with audio fd for outputting read audio and ctrlfd to monitor for reading. + * \param c channel to wait for a digit on + * \param ms how many milliseconds to wait + * \param audiofd audio file descriptor to write to if audio frames are received + * \param ctrlfd control file descriptor to monitor for reading +- * \return Returns 1 if ctrlfd becomes available */ ++ * \return Returns 1 if ctrlfd becomes available ++ */ + int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int ctrlfd); + +-/*! Reads multiple digits ++/*! ++ * \brief Reads multiple digits + * \param c channel to read from + * \param s string to read in to. Must be at least the size of your length + * \param len how many digits to read (maximum) + * \param timeout how long to timeout between digits + * \param rtimeout timeout to wait on the first digit + * \param enders digits to end the string ++ * \details + * Read in a digit string "s", max length "len", maximum timeout between +- digits "timeout" (-1 for none), terminated by anything in "enders". Give them rtimeout +- for the first digit. Returns 0 on normal return, or 1 on a timeout. In the case of +- a timeout, any digits that were read before the timeout will still be available in s. +- RETURNS 2 in full version when ctrlfd is available, NOT 1*/ ++ * digits "timeout" (-1 for none), terminated by anything in "enders". Give them rtimeout ++ * for the first digit. ++ * \return Returns 0 on normal return, or 1 on a timeout. In the case of ++ * a timeout, any digits that were read before the timeout will still be available in s. ++ * RETURNS 2 in full version when ctrlfd is available, NOT 1 ++ */ + int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders); + int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders, int audiofd, int ctrlfd); + +@@ -1342,28 +1557,37 @@ + #define AST_BRIDGE_IGNORE_SIGS (1 << 4) + + +-/*! \brief Makes two channel formats compatible ++/*! ++ * \brief Makes two channel formats compatible + * \param c0 first channel to make compatible + * \param c1 other channel to make compatible +- * Set two channels to compatible formats -- call before ast_channel_bridge in general . +- * \return Returns 0 on success and -1 if it could not be done */ ++ * \details ++ * Set two channels to compatible formats -- call before ast_channel_bridge in general. ++ * \return Returns 0 on success and -1 if it could not be done ++ */ + int ast_channel_make_compatible(struct ast_channel *c0, struct ast_channel *c1); + +-/*! Bridge two channels together (early) ++/*! ++ * \brief Bridge two channels together (early) + * \param c0 first channel to bridge + * \param c1 second channel to bridge ++ * \details + * Bridge two channels (c0 and c1) together early. This implies either side may not be answered yet. +- * \return Returns 0 on success and -1 if it could not be done */ ++ * \return Returns 0 on success and -1 if it could not be done ++ */ + int ast_channel_early_bridge(struct ast_channel *c0, struct ast_channel *c1); + +-/*! Bridge two channels together ++/*! ++ * \brief Bridge two channels together + * \param c0 first channel to bridge + * \param c1 second channel to bridge + * \param config config for the channels + * \param fo destination frame(?) + * \param rc destination channel(?) ++ * \details + * Bridge two channels (c0 and c1) together. If an important frame occurs, we return that frame in +- *rf (remember, it could be NULL) and which channel (0 or 1) in rc */ ++ * *rf (remember, it could be NULL) and which channel (0 or 1) in rc ++ */ + /* int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc); */ + int ast_channel_bridge(struct ast_channel *c0,struct ast_channel *c1, + struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc); +@@ -1374,6 +1598,7 @@ + * \param original channel to make a copy of + * \param clone copy of the original channel + * ++ * \details + * This is a very strange and freaky function used primarily for transfer. Suppose that + * "original" and "clone" are two channels in random situations. This function takes + * the guts out of "clone" and puts them into the "original" channel, then alerts the +@@ -1385,100 +1610,114 @@ + */ + int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone); + +-/*! Gives the string form of a given cause code */ + /*! ++ * \brief Gives the string form of a given cause code. ++ * + * \param state cause to get the description of +- * Give a name to a cause code +- * Returns the text form of the binary cause code given ++ * \return the text form of the binary cause code given + */ + const char *ast_cause2str(int state) attribute_pure; + +-/*! Convert the string form of a cause code to a number */ + /*! ++ * \brief Convert the string form of a cause code to a number ++ * + * \param name string form of the cause +- * Returns the cause code ++ * \return the cause code + */ + int ast_str2cause(const char *name) attribute_pure; + +-/*! Gives the string form of a given channel state */ + /*! ++ * \brief Gives the string form of a given channel state ++ * + * \param ast_channel_state state to get the name of +- * Give a name to a state +- * Returns the text form of the binary state given ++ * \return the text form of the binary state given + */ + const char *ast_state2str(enum ast_channel_state); + +-/*! Gives the string form of a given transfer capability */ + /*! +- * \param transfercapability transfercapabilty to get the name of +- * Give a name to a transfercapbility +- * See above +- * Returns the text form of the binary transfer capability ++ * \brief Gives the string form of a given transfer capability ++ * ++ * \param transfercapability transfer capability to get the name of ++ * \return the text form of the binary transfer capability + */ + char *ast_transfercapability2str(int transfercapability) attribute_const; + +-/* Options: Some low-level drivers may implement "options" allowing fine tuning of the +- low level channel. See frame.h for options. Note that many channel drivers may support +- none or a subset of those features, and you should not count on this if you want your +- asterisk application to be portable. They're mainly useful for tweaking performance */ ++/* ++ * Options: Some low-level drivers may implement "options" allowing fine tuning of the ++ * low level channel. See frame.h for options. Note that many channel drivers may support ++ * none or a subset of those features, and you should not count on this if you want your ++ * asterisk application to be portable. They're mainly useful for tweaking performance ++ */ + +-/*! Sets an option on a channel */ + /*! ++ * \brief Sets an option on a channel ++ * + * \param channel channel to set options on + * \param option option to change + * \param data data specific to option + * \param datalen length of the data + * \param block blocking or not ++ * \details + * Set an option on a channel (see frame.h), optionally blocking awaiting the reply +- * Returns 0 on success and -1 on failure ++ * \return 0 on success and -1 on failure + */ + int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block); + +-/*! Pick the best codec */ +-/* Choose the best codec... Uhhh... Yah. */ ++/*! Pick the best codec ++ * Choose the best codec... Uhhh... Yah. */ + int ast_best_codec(int fmts); + + +-/*! Checks the value of an option */ + /*! ++ * \brief Checks the value of an option ++ * + * Query the value of an option + * Works similarly to setoption except only reads the options. + */ + int ast_channel_queryoption(struct ast_channel *channel, int option, void *data, int *datalen, int block); + +-/*! Checks for HTML support on a channel */ +-/*! Returns 0 if channel does not support HTML or non-zero if it does */ ++/*! ++ * \brief Checks for HTML support on a channel ++ * \return 0 if channel does not support HTML or non-zero if it does ++ */ + int ast_channel_supports_html(struct ast_channel *channel); + +-/*! Sends HTML on given channel */ +-/*! Send HTML or URL on link. Returns 0 on success or -1 on failure */ ++/*! ++ * \brief Sends HTML on given channel ++ * Send HTML or URL on link. ++ * \return 0 on success or -1 on failure ++ */ + int ast_channel_sendhtml(struct ast_channel *channel, int subclass, const char *data, int datalen); + +-/*! Sends a URL on a given link */ +-/*! Send URL on link. Returns 0 on success or -1 on failure */ ++/*! ++ * \brief Sends a URL on a given link ++ * Send URL on link. ++ * \return 0 on success or -1 on failure ++ */ + int ast_channel_sendurl(struct ast_channel *channel, const char *url); + +-/*! Defers DTMF */ +-/*! Defer DTMF so that you only read things like hangups and audio. Returns +- non-zero if channel was already DTMF-deferred or 0 if channel is just now +- being DTMF-deferred */ ++/*! ++ * \brief Defers DTMF so that you only read things like hangups and audio. ++ * \return non-zero if channel was already DTMF-deferred or ++ * 0 if channel is just now being DTMF-deferred ++ */ + int ast_channel_defer_dtmf(struct ast_channel *chan); + +-/*! Undo defer. ast_read will return any dtmf characters that were queued */ ++/*! Undo defer. ast_read will return any DTMF characters that were queued */ + void ast_channel_undefer_dtmf(struct ast_channel *chan); + + /*! Initiate system shutdown -- prevents new channels from being allocated. +- If "hangup" is non-zero, all existing channels will receive soft +- hangups */ ++ * \param hangup If "hangup" is non-zero, all existing channels will receive soft ++ * hangups */ + void ast_begin_shutdown(int hangup); + + /*! Cancels an existing shutdown and returns to normal operation */ + void ast_cancel_shutdown(void); + +-/*! Returns number of active/allocated channels */ ++/*! \return number of active/allocated channels */ + int ast_active_channels(void); + +-/*! Returns non-zero if Asterisk is being shut down */ ++/*! \return non-zero if Asterisk is being shut down */ + int ast_shutting_down(void); + + /*! Activate a given generator */ +@@ -1536,105 +1775,119 @@ + * + * \param rate number of timer ticks per second + * ++ * \details + * If timers are supported, force a scheduled expiration on the + * timer fd, at which point we call the callback function / data + * +- * Call this function with a rate of 0 to turn off the timer ticks ++ * \note Call this function with a rate of 0 to turn off the timer ticks + * + * \version 1.6.1 changed samples parameter to rate, accomodates new timing methods + */ + int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data); + +-/*! \brief Transfer a channel (if supported). Returns -1 on error, 0 if not supported +- and 1 if supported and requested +- \param chan current channel +- \param dest destination extension for transfer +-*/ ++/*! ++ * \brief Transfer a channel (if supported). ++ * \retval -1 on error ++ * \retval 0 if not supported ++ * \retval 1 if supported and requested ++ * \param chan current channel ++ * \param dest destination extension for transfer ++ */ + int ast_transfer(struct ast_channel *chan, char *dest); + +-/*! \brief Start masquerading a channel +- XXX This is a seriously whacked out operation. We're essentially putting the guts of +- the clone channel into the original channel. Start by killing off the original +- channel's backend. I'm not sure we're going to keep this function, because +- while the features are nice, the cost is very high in terms of pure nastiness. XXX +- \param chan Channel to masquerade +-*/ ++/*! ++ * \brief Start masquerading a channel ++ * \details ++ * XXX This is a seriously whacked out operation. We're essentially putting the guts of ++ * the clone channel into the original channel. Start by killing off the original ++ * channel's backend. I'm not sure we're going to keep this function, because ++ * while the features are nice, the cost is very high in terms of pure nastiness. XXX ++ * \param chan Channel to masquerade ++ */ + int ast_do_masquerade(struct ast_channel *chan); + +-/*! \brief Find bridged channel +- \param chan Current channel +-*/ ++/*! ++ * \brief Find bridged channel ++ * \param chan Current channel ++ */ + struct ast_channel *ast_bridged_channel(struct ast_channel *chan); + + /*! +- \brief Inherits channel variable from parent to child channel +- \param parent Parent channel +- \param child Child channel +- +- Scans all channel variables in the parent channel, looking for those +- that should be copied into the child channel. +- Variables whose names begin with a single '_' are copied into the +- child channel with the prefix removed. +- Variables whose names begin with '__' are copied into the child +- channel with their names unchanged. +-*/ ++ * \brief Inherits channel variable from parent to child channel ++ * \param parent Parent channel ++ * \param child Child channel ++ * ++ * \details ++ * Scans all channel variables in the parent channel, looking for those ++ * that should be copied into the child channel. ++ * Variables whose names begin with a single '_' are copied into the ++ * child channel with the prefix removed. ++ * Variables whose names begin with '__' are copied into the child ++ * channel with their names unchanged. ++ */ + void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child); + + /*! +- \brief adds a list of channel variables to a channel +- \param chan the channel +- \param vars a linked list of variables +- +- Variable names can be for a regular channel variable or a dialplan function +- that has the ability to be written to. +-*/ ++ * \brief adds a list of channel variables to a channel ++ * \param chan the channel ++ * \param vars a linked list of variables ++ * ++ * \details ++ * Variable names can be for a regular channel variable or a dialplan function ++ * that has the ability to be written to. ++ */ + void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars); + + /*! +- \brief An opaque 'object' structure use by silence generators on channels. ++ * \brief An opaque 'object' structure use by silence generators on channels. + */ + struct ast_silence_generator; + + /*! +- \brief Starts a silence generator on the given channel. +- \param chan The channel to generate silence on +- \return An ast_silence_generator pointer, or NULL if an error occurs +- +- This function will cause SLINEAR silence to be generated on the supplied +- channel until it is disabled; if the channel cannot be put into SLINEAR +- mode then the function will fail. +- +- The pointer returned by this function must be preserved and passed to +- ast_channel_stop_silence_generator when you wish to stop the silence +- generation. ++ * \brief Starts a silence generator on the given channel. ++ * \param chan The channel to generate silence on ++ * \return An ast_silence_generator pointer, or NULL if an error occurs ++ * ++ * \details ++ * This function will cause SLINEAR silence to be generated on the supplied ++ * channel until it is disabled; if the channel cannot be put into SLINEAR ++ * mode then the function will fail. ++ * ++ * \note ++ * The pointer returned by this function must be preserved and passed to ++ * ast_channel_stop_silence_generator when you wish to stop the silence ++ * generation. + */ + struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan); + + /*! +- \brief Stops a previously-started silence generator on the given channel. +- \param chan The channel to operate on +- \param state The ast_silence_generator pointer return by a previous call to +- ast_channel_start_silence_generator. +- \return nothing +- +- This function will stop the operating silence generator and return the channel +- to its previous write format. ++ * \brief Stops a previously-started silence generator on the given channel. ++ * \param chan The channel to operate on ++ * \param state The ast_silence_generator pointer return by a previous call to ++ * ast_channel_start_silence_generator. ++ * \return nothing ++ * ++ * \details ++ * This function will stop the operating silence generator and return the channel ++ * to its previous write format. + */ + void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state); + + /*! +- \brief Check if the channel can run in internal timing mode. +- \param chan The channel to check +- \return boolean +- +- This function will return 1 if internal timing is enabled and the timing +- device is available. ++ * \brief Check if the channel can run in internal timing mode. ++ * \param chan The channel to check ++ * \return boolean ++ * ++ * \details ++ * This function will return 1 if internal timing is enabled and the timing ++ * device is available. + */ + int ast_internal_timing_enabled(struct ast_channel *chan); + + /* Misc. functions below */ + +-/*! \brief if fd is a valid descriptor, set *pfd with the descriptor ++/*! ++ * \brief if fd is a valid descriptor, set *pfd with the descriptor + * \return Return 1 (not -1!) if added, 0 otherwise (so we can add the + * return value to the index into the array) + */ +@@ -1677,12 +1930,14 @@ + } + #endif + +-/*! \brief Waits for activity on a group of channels ++/*! ++ * \brief Waits for activity on a group of channels + * \param nfds the maximum number of file descriptors in the sets + * \param rfds file descriptors to check for read availability + * \param wfds file descriptors to check for write availability + * \param efds file descriptors to check for exceptions (OOB data) + * \param tvp timeout while waiting for events ++ * \details + * This is the same as a standard select(), except it guarantees the + * behaviour where the passed struct timeval is updated with how much + * time was not slept while waiting for the specified events +@@ -1739,23 +1994,23 @@ + /*! \brief print call- and pickup groups into buffer */ + char *ast_print_group(char *buf, int buflen, ast_group_t group); + +-/*! \brief Convert enum channelreloadreason to text string for manager event +- \param reason Enum channelreloadreason - reason for reload (manager, cli, start etc) +-*/ ++/*! ++ * \brief Convert enum channelreloadreason to text string for manager event ++ * \param reason The reason for reload (manager, cli, start etc) ++ */ + const char *channelreloadreason2txt(enum channelreloadreason reason); + + /*! \brief return an ast_variable list of channeltypes */ + struct ast_variable *ast_channeltype_list(void); + + /*! +- \brief return an english explanation of the code returned thru __ast_request_and_dial's 'outstate' argument +- \param reason The integer argument, usually taken from AST_CONTROL_ macros +- \return char pointer explaining the code ++ * \brief return an english explanation of the code returned thru __ast_request_and_dial's 'outstate' argument ++ * \param reason The integer argument, usually taken from AST_CONTROL_ macros ++ * \return char pointer explaining the code + */ + const char *ast_channel_reason2str(int reason); + +-/*! \brief channel group info +- */ ++/*! \brief channel group info */ + struct ast_group_info { + struct ast_channel *chan; + char *category; +@@ -1764,6 +2019,295 @@ + }; + + ++/*! ++ * \since 1.6.3 ++ * \brief Copy the source caller information to the destination caller. ++ * ++ * \param dest Destination caller ++ * \param src Source caller ++ * ++ * \return Nothing ++ */ ++void ast_party_caller_copy(struct ast_callerid *dest, const struct ast_callerid *src); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Initialize the given connected line structure. ++ * ++ * \param init Connected line structure to initialize. ++ * ++ * \return Nothing ++ */ ++void ast_party_connected_line_init(struct ast_party_connected_line *init); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Copy the source connected line information to the destination connected line. ++ * ++ * \param dest Destination connected line ++ * \param src Source connected line ++ * ++ * \return Nothing ++ */ ++void ast_party_connected_line_copy(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Initialize the given connected line structure using the given ++ * guide for a set update operation. ++ * ++ * \details ++ * The initialization is needed to allow a set operation to know if a ++ * value needs to be updated. Simple integers need the guide's original ++ * value in case the set operation is not trying to set a new value. ++ * String values are simply set to NULL pointers if they are not going ++ * to be updated. ++ * ++ * \param init Connected line structure to initialize. ++ * \param guide Source connected line to use as a guide in initializing. ++ * ++ * \return Nothing ++ */ ++void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Set the connected line information based on another connected line source ++ * ++ * This is similar to ast_party_connected_line_copy, except that NULL values for ++ * strings in the src parameter indicate not to update the corresponding dest values. ++ * ++ * \param src The source connected line to use as a guide to set the dest ++ * \param dest The connected line one wishes to update ++ * ++ * \return Nada ++ */ ++void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Collect the caller party information into a connected line structure. ++ * ++ * \param connected Collected caller information for the connected line ++ * \param cid Caller information. ++ * ++ * \return Nothing ++ * ++ * \warning This is a shallow copy. ++ * \warning DO NOT call ast_party_connected_line_free() on the filled in ++ * connected line structure! ++ */ ++void ast_party_connected_line_collect_caller(struct ast_party_connected_line *connected, struct ast_callerid *cid); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Destroy the connected line information contents ++ * ++ * \param doomed The connected line information to destroy. ++ * ++ * \return Nothing ++ */ ++void ast_party_connected_line_free(struct ast_party_connected_line *doomed); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Copy the source redirecting information to the destination redirecting. ++ * ++ * \param dest Destination redirecting ++ * \param src Source redirecting ++ * ++ * \return Nothing ++ */ ++void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Initialize the given redirecting id structure using the given guide ++ * for a set update operation. ++ * ++ * \details ++ * The initialization is needed to allow a set operation to know if a ++ * value needs to be updated. Simple integers need the guide's original ++ * value in case the set operation is not trying to set a new value. ++ * String values are simply set to NULL pointers if they are not going ++ * to be updated. ++ * ++ * \param init Redirecting id structure to initialize. ++ * \param guide Source redirecting id to use as a guide in initializing. ++ * ++ * \return Nothing ++ */ ++void ast_party_redirecting_set_init(struct ast_party_redirecting *init, const struct ast_party_redirecting *guide); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Destroy the redirecting information contents ++ * ++ * \param doomed The redirecting information to destroy. ++ * ++ * \return Nothing ++ */ ++void ast_party_redirecting_free(struct ast_party_redirecting *doomed); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Copy the caller information to the connected line information. ++ * ++ * \param dest Destination connected line information ++ * \param src Source caller information ++ * ++ * \return Nothing ++ * ++ * \note Assumes locks are already acquired ++ */ ++void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_callerid *src); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Copy the connected line information to the caller information. ++ * ++ * \param dest Destination caller information ++ * \param src Source connected line information ++ * ++ * \return Nothing ++ * ++ * \note Assumes locks are already acquired ++ */ ++void ast_connected_line_copy_to_caller(struct ast_callerid *dest, const struct ast_party_connected_line *src); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Set the connected line information in the Asterisk channel ++ * ++ * \param chan Asterisk channel to set connected line information ++ * \param connected Connected line information ++ * ++ * \return Nothing ++ * ++ * \note The channel does not need to be locked before calling this function. ++ */ ++void ast_channel_set_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Build the connected line information data frame. ++ * ++ * \param data Buffer to fill with the frame data ++ * \param datalen Size of the buffer to fill ++ * \param connected Connected line information ++ * ++ * \retval -1 if error ++ * \retval Amount of data buffer used ++ */ ++int ast_connected_line_build_data(unsigned char *data, size_t datalen, const struct ast_party_connected_line *connected); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Parse connected line indication frame data ++ * ++ * \param data Buffer with the frame data to parse ++ * \param datalen Size of the buffer ++ * \param connected Extracted connected line information ++ * ++ * \retval 0 on success. ++ * \retval -1 on error. ++ * ++ * \note The filled in connected line structure needs to be initialized by ++ * ast_party_connected_line_set_init() before calling. If defaults are not ++ * required use ast_party_connected_line_init(). ++ * \note The filled in connected line structure needs to be destroyed by ++ * ast_party_connected_line_free() when it is no longer needed. ++ */ ++int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Indicate that the connected line information has changed ++ * ++ * \param chan Asterisk channel to indicate connected line information ++ * \param connected Connected line information ++ * ++ * \return Nothing ++ */ ++void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Queue a connected line update frame on a channel ++ * ++ * \param chan Asterisk channel to indicate connected line information ++ * \param connected Connected line information ++ * ++ * \return Nothing ++ */ ++void ast_channel_queue_connected_line_update(struct ast_channel *chan, const struct ast_party_connected_line *connected); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Set the redirecting id information in the Asterisk channel ++ * ++ * \param chan Asterisk channel to set redirecting id information ++ * \param redirecting Redirecting id information ++ * ++ * \return Nothing ++ * ++ * \note The channel does not need to be locked before calling this function. ++ */ ++void ast_channel_set_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Build the redirecting id data frame. ++ * ++ * \param data Buffer to fill with the frame data ++ * \param datalen Size of the buffer to fill ++ * \param redirecting Redirecting id information ++ * ++ * \retval -1 if error ++ * \retval Amount of data buffer used ++ */ ++int ast_redirecting_build_data(unsigned char *data, size_t datalen, const struct ast_party_redirecting *redirecting); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Parse redirecting indication frame data ++ * ++ * \param data Buffer with the frame data to parse ++ * \param datalen Size of the buffer ++ * \param redirecting Extracted redirecting id information ++ * ++ * \retval 0 on success. ++ * \retval -1 on error. ++ * ++ * \note The filled in id structure needs to be initialized by ++ * ast_party_redirecting_set_init() before calling. ++ * \note The filled in id structure needs to be destroyed by ++ * ast_party_redirecting_free() when it is no longer needed. ++ */ ++int ast_redirecting_parse_data(const unsigned char *data, size_t datalen, struct ast_party_redirecting *redirecting); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Indicate that the redirecting id has changed ++ * ++ * \param chan Asterisk channel to indicate redirecting id information ++ * \param redirecting Redirecting id information ++ * ++ * \return Nothing ++ */ ++void ast_channel_update_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Queue a redirecting update frame on a channel ++ * ++ * \param chan Asterisk channel to indicate redirecting id information ++ * \param redirecting Redirecting id information ++ * ++ * \return Nothing ++ */ ++void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct ast_party_redirecting *redirecting); ++ + #if defined(__cplusplus) || defined(c_plusplus) + } + #endif +Index: include/asterisk/_private.h +=================================================================== +--- a/include/asterisk/_private.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/_private.h (.../trunk) (revision 186562) +@@ -29,7 +29,7 @@ + void dnsmgr_start_refresh(void); /*!< Provided by dnsmgr.c */ + int dnsmgr_reload(void); /*!< Provided by dnsmgr.c */ + void threadstorage_init(void); /*!< Provided by threadstorage.c */ +-void ast_event_init(void); /*!< Provided by event.c */ ++int ast_event_init(void); /*!< Provided by event.c */ + int ast_device_state_engine_init(void); /*!< Provided by devicestate.c */ + int astobj2_init(void); /*!< Provided by astobj2.c */ + int ast_file_init(void); /*!< Provided by file.c */ +@@ -41,6 +41,7 @@ + int ast_timing_init(void); /*!< Provided by timing.c */ + int ast_indications_init(void); /*!< Provided by indications.c */ + int ast_indications_reload(void);/*!< Provided by indications.c */ ++void ast_stun_init(void); /*!< Provided by stun.c */ + + /*! + * \brief Reload asterisk modules. +Index: include/asterisk/features.h +=================================================================== +--- a/include/asterisk/features.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/features.h (.../trunk) (revision 186562) +@@ -36,6 +36,21 @@ + + #define PARK_APP_NAME "Park" + ++#define AST_FEATURE_RETURN_HANGUP -1 ++#define AST_FEATURE_RETURN_SUCCESSBREAK 0 ++#define AST_FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE ++#define AST_FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER ++#define AST_FEATURE_RETURN_PASSDIGITS 21 ++#define AST_FEATURE_RETURN_STOREDIGITS 22 ++#define AST_FEATURE_RETURN_SUCCESS 23 ++#define AST_FEATURE_RETURN_KEEPTRYING 24 ++#define AST_FEATURE_RETURN_PARKFAILED 25 ++ ++#define FEATURE_SENSE_CHAN (1 << 0) ++#define FEATURE_SENSE_PEER (1 << 1) ++ ++typedef int (*ast_feature_operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data); ++ + /*! \brief main call feature structure */ + + enum { +@@ -53,7 +68,7 @@ + char sname[FEATURE_SNAME_LEN]; + char exten[FEATURE_MAX_LEN]; + char default_exten[FEATURE_MAX_LEN]; +- int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data); ++ ast_feature_operation operation; + unsigned int flags; + char app[FEATURE_APP_LEN]; + char app_args[FEATURE_APP_ARGS_LEN]; +@@ -61,14 +76,6 @@ + AST_LIST_ENTRY(ast_call_feature) feature_entry; + }; + +-#define AST_FEATURE_RETURN_HANGUP -1 +-#define AST_FEATURE_RETURN_SUCCESSBREAK 0 +-#define AST_FEATURE_RETURN_PASSDIGITS 21 +-#define AST_FEATURE_RETURN_STOREDIGITS 22 +-#define AST_FEATURE_RETURN_SUCCESS 23 +-#define AST_FEATURE_RETURN_KEEPTRYING 24 +-#define AST_FEATURE_RETURN_PARKFAILED 25 +- + /*! + * \brief Park a call and read back parked location + * \param chan the channel to actually be parked +@@ -123,6 +130,14 @@ + \param feature the ast_call_feature object which was registered before*/ + void ast_unregister_feature(struct ast_call_feature *feature); + ++/*! \brief detect a feature before bridging ++ \param chan ++ \param ast_flags ptr ++ \param char ptr of input code ++ \retval ast_call_feature ptr to be set if found ++ \return result, was feature found or not */ ++int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, char *code, struct ast_call_feature *feature); ++ + /*! \brief look for a call feature entry by its sname + \param name a string ptr, should match "automon", "blindxfer", "atxfer", etc. */ + struct ast_call_feature *ast_find_call_feature(const char *name); +Index: include/asterisk/app.h +=================================================================== +--- a/include/asterisk/app.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/app.h (.../trunk) (revision 186562) +@@ -32,7 +32,7 @@ + extern "C" { + #endif + +-AST_THREADSTORAGE_EXTERNAL(global_app_buf); ++AST_THREADSTORAGE_EXTERNAL(ast_str_thread_global_buf); + + /* IVR stuff */ + +Index: include/asterisk/res_odbc.h +=================================================================== +--- a/include/asterisk/res_odbc.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/res_odbc.h (.../trunk) (revision 186562) +@@ -35,7 +35,7 @@ + + typedef enum { ODBC_SUCCESS=0, ODBC_FAIL=-1} odbc_status; + +-/*! \brief Flags for use with ast_odbc_request_obj2 */ ++/*! \brief Flags for use with \see ast_odbc_request_obj2 */ + enum { + RES_ODBC_SANITY_CHECK = (1 << 0), + RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1), +@@ -102,7 +102,7 @@ + /*! + * \brief Retrieves a connected ODBC object + * \param name The name of the ODBC class for which a connection is needed. +- * \param check Whether to ensure that a connection is valid before returning the handle. Usually unnecessary. ++ * \param flags Set of flags used to control which connection is returned. + * \retval ODBC object + * \retval NULL if there is no connection available with the requested name. + * +@@ -129,7 +129,7 @@ + * \brief Retrieve a stored ODBC object, if a transaction has been started. + * \param chan Channel associated with the transaction. + * \param objname Name of the database handle. This name corresponds to the name passed +- * to ast_odbc_request_obj2 (or formerly, to ast_odbc_request_obj). Note that the ++ * to \see ast_odbc_request_obj2 (or formerly, to ast_odbc_request_obj). Note that the + * existence of this parameter name explicitly allows for multiple transactions to be open + * at once, albeit to different databases. + * \retval A stored ODBC object, if a transaction was already started. +@@ -180,7 +180,7 @@ + /*! + * \brief Find or create an entry describing the table specified. + * \param database Name of an ODBC class on which to query the table +- * \param table Tablename to describe ++ * \param tablename Tablename to describe + * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs. + * When a structure is returned, the contained columns list will be + * rdlock'ed, to ensure that it will be retained in memory. +@@ -200,7 +200,7 @@ + /*! + * \brief Remove a cache entry from memory + * \param database Name of an ODBC class (used to ensure like-named tables in different databases are not confused) +- * \param table Tablename for which a cached record should be removed ++ * \param tablename Tablename for which a cached record should be removed + * \retval 0 if the cache entry was removed, or -1 if no matching entry was found. + * \since 1.6.1 + */ +@@ -213,7 +213,7 @@ + + /*!\brief Wrapper for SQLGetData to use with dynamic strings + * \param buf Address of the pointer to the ast_str structure. +- * \param maxlen The maximum size of the resulting string, or 0 for no limit. ++ * \param pmaxlen The maximum size of the resulting string, or 0 for no limit. + * \param StatementHandle The statement handle from which to retrieve data. + * \param ColumnNumber Column number (1-based offset) for which to retrieve data. + * \param TargetType The SQL constant indicating what kind of data is to be retrieved (usually SQL_CHAR) +Index: include/asterisk/event.h +=================================================================== +--- a/include/asterisk/event.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/event.h (.../trunk) (revision 186562) +@@ -358,42 +358,18 @@ + * + * \param event the event to be queued and cached + * +- * The rest of the arguments to this function specify information elements to +- * use for determining which events in the cache that this event should replace. +- * All events in the cache that match the specified criteria will be removed from +- * the cache and then this one will be added. The arguments are specified in +- * the form: +- * +- * \code +- * <enum ast_event_ie_type>, [enum ast_event_ie_pltype] +- * \endcode +- * and must end with AST_EVENT_IE_END. +- * +- * If the ie_type specified is *not* AST_EVENT_IE_END, then it must be followed +- * by a valid IE payload type. If the payload type given is EXISTS, then all +- * events that contain that information element will be removed from the cache. +- * Otherwise, all events in the cache that contain an information element with +- * the same value as the new event will be removed. +- * +- * \note If more than one IE parameter is specified, they *all* must match for +- * the event to be removed from the cache. +- * +- * Example usage: +- * +- * \code +- * ast_event_queue_and_cache(event, +- * AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, +- * AST_EVENT_IE_END); +- * \endcode +- * +- * This example queues and caches an event. Any events in the cache that have +- * the same MAILBOX information element as this event will be removed. +- * ++ * \details + * The purpose of caching events is so that the core can retain the last known + * information for events that represent some sort of state. That way, when + * code needs to find out the current state, it can query the cache. ++ * ++ * The event API already knows which events can be cached and how to cache them. ++ * ++ * \retval 0 success ++ * \retval non-zero failure. If failure is returned, the event must be destroyed ++ * by the caller of this function. + */ +-int ast_event_queue_and_cache(struct ast_event *event, ...); ++int ast_event_queue_and_cache(struct ast_event *event); + + /*! + * \brief Retrieve an event from the cache +@@ -511,6 +487,18 @@ + const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type); + + /*! ++ * \brief Get the hash for the string payload of an IE ++ * ++ * \param event The event to get the IE from ++ * \param ie_type the type of information element to retrieve the hash for ++ * ++ * \return This function returns the hash value as calculated by ast_str_hash() ++ * for the string payload. This is stored in the event to avoid ++ * unnecessary string comparisons. ++ */ ++uint32_t ast_event_get_ie_str_hash(const struct ast_event *event, enum ast_event_ie_type ie_type); ++ ++/*! + * \brief Get the value of an information element that has a raw payload + * + * \param event The event to get the IE from +Index: include/asterisk/compat.h +=================================================================== +--- a/include/asterisk/compat.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/compat.h (.../trunk) (revision 186562) +@@ -67,7 +67,7 @@ + #include <string.h> + #endif + +-#ifdef HAVE_SYS_POLL_H ++#ifndef AST_POLL_COMPAT + #include <sys/poll.h> + #else + #include "asterisk/poll-compat.h" +Index: include/asterisk/timing.h +=================================================================== +--- a/include/asterisk/timing.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/timing.h (.../trunk) (revision 186562) +@@ -45,9 +45,6 @@ + 4) Multiple 'event types', so that the code using the timer can + know whether the wakeup it received was due to a periodic trigger + or a continuous trigger. +- +- \todo Create an implementation of this API for Linux based on the +- following API: http://www.kernel.org/doc/man-pages/online/pages/man2/timerfd_create.2.html + */ + + #ifndef _ASTERISK_TIMING_H +@@ -96,7 +93,7 @@ + */ + #define ast_register_timing_interface(i) _ast_register_timing_interface(i, ast_module_info->self) + void *_ast_register_timing_interface(struct ast_timing_interface *funcs, +- struct ast_module *mod); ++ struct ast_module *mod); + + /*! + * \brief Unregister a previously registered timing interface. +@@ -110,45 +107,57 @@ + */ + int ast_unregister_timing_interface(void *handle); + ++struct ast_timer; ++ + /*! +- * \brief Open a timing fd ++ * \brief Open a timer + * +- * \retval -1 error, with errno set +- * \retval >=0 success ++ * \retval NULL on error, with errno set ++ * \retval non-NULL timer handle on success + * \since 1.6.1 + */ +-int ast_timer_open(void); ++struct ast_timer *ast_timer_open(void); + + /*! + * \brief Close an opened timing handle + * +- * \param handle timing fd returned from timer_open() ++ * \param handle timer handle returned from timer_open() + * + * \return nothing + * \since 1.6.1 + */ +-void ast_timer_close(int handle); ++void ast_timer_close(struct ast_timer *handle); + + /*! ++ * \brief Get a poll()-able file descriptor for a timer ++ * ++ * \param handle timer handle returned from timer_open() ++ * ++ * \return file descriptor which can be used with poll() to wait for events ++ * \since 1.6.1 ++ */ ++int ast_timer_fd(const struct ast_timer *handle); ++ ++/*! + * \brief Set the timing tick rate + * +- * \param handle timing fd returned from timer_open() ++ * \param handle timer handle returned from timer_open() + * \param rate ticks per second, 0 turns the ticks off if needed + * +- * Use this function if you want the timing fd to show input at a certain +- * rate. The other alternative use of a timing fd, is using the continuous ++ * Use this function if you want the timer to show input at a certain ++ * rate. The other alternative use of a timer is the continuous + * mode. + * + * \retval -1 error, with errno set + * \retval 0 success + * \since 1.6.1 + */ +-int ast_timer_set_rate(int handle, unsigned int rate); ++int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate); + + /*! + * \brief Acknowledge a timer event + * +- * \param handle timing fd returned from timer_open() ++ * \param handle timer handle returned from timer_open() + * \param quantity number of timer events to acknowledge + * + * \note This function should only be called if timer_get_event() +@@ -157,55 +166,55 @@ + * \return nothing + * \since 1.6.1 + */ +-void ast_timer_ack(int handle, unsigned int quantity); ++void ast_timer_ack(const struct ast_timer *handle, unsigned int quantity); + + /*! + * \brief Enable continuous mode + * +- * \param handle timing fd returned from timer_open() ++ * \param handle timer handle returned from timer_open() + * +- * Continuous mode causes poll() on the timing fd to immediately return ++ * Continuous mode causes poll() on the timer's fd to immediately return + * always until continuous mode is disabled. + * + * \retval -1 failure, with errno set + * \retval 0 success + * \since 1.6.1 + */ +-int ast_timer_enable_continuous(int handle); ++int ast_timer_enable_continuous(const struct ast_timer *handle); + + /*! + * \brief Disable continuous mode + * +- * \param handle timing fd returned from timer_close() ++ * \param handle timer handle returned from timer_close() + * + * \retval -1 failure, with errno set + * \retval 0 success + * \since 1.6.1 + */ +-int ast_timer_disable_continuous(int handle); ++int ast_timer_disable_continuous(const struct ast_timer *handle); + + /*! +- * \brief Determine timing event ++ * \brief Retrieve timing event + * +- * \param handle timing fd returned by timer_open() ++ * \param handle timer handle returned by timer_open() + * +- * After poll() indicates that there is input on the timing fd, this will ++ * After poll() indicates that there is input on the timer's fd, this will + * be called to find out what triggered it. + * +- * \return which event triggered the timing fd ++ * \return which event triggered the timer + * \since 1.6.1 + */ +-enum ast_timer_event ast_timer_get_event(int handle); ++enum ast_timer_event ast_timer_get_event(const struct ast_timer *handle); + + /*! +- * \brief Get maximum rate supported for a timing handle ++ * \brief Get maximum rate supported for a timer + * +- * \param handle timing fd returned by timer_open() ++ * \param handle timer handle returned by timer_open() + * +- * \return maximum rate supported for timing handle ++ * \return maximum rate supported by timer + * \since 1.6.1 + */ +-unsigned int ast_timer_get_max_rate(int handle); ++unsigned int ast_timer_get_max_rate(const struct ast_timer *handle); + + #if defined(__cplusplus) || defined(c_plusplus) + } +Index: include/asterisk/frame.h +=================================================================== +--- a/include/asterisk/frame.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/frame.h (.../trunk) (revision 186562) +@@ -39,54 +39,56 @@ + char framing[32]; + }; + +-/*! \page Def_Frame AST Multimedia and signalling frames +- \section Def_AstFrame What is an ast_frame ? +- A frame of data read used to communicate between +- between channels and applications. +- Frames are divided into frame types and subclasses. ++/*! ++ * \page Def_Frame AST Multimedia and signalling frames ++ * \section Def_AstFrame What is an ast_frame ? ++ * A frame of data read used to communicate between ++ * between channels and applications. ++ * Frames are divided into frame types and subclasses. ++ * ++ * \par Frame types ++ * \arg \b VOICE: Voice data, subclass is codec (AST_FORMAT_*) ++ * \arg \b VIDEO: Video data, subclass is codec (AST_FORMAT_*) ++ * \arg \b DTMF: A DTMF digit, subclass is the digit ++ * \arg \b IMAGE: Image transport, mostly used in IAX ++ * \arg \b TEXT: Text messages and character by character (real time text) ++ * \arg \b HTML: URL's and web pages ++ * \arg \b MODEM: Modulated data encodings, such as T.38 and V.150 ++ * \arg \b IAX: Private frame type for the IAX protocol ++ * \arg \b CNG: Comfort noice frames ++ * \arg \b CONTROL:A control frame, subclass defined as AST_CONTROL_ ++ * \arg \b NULL: Empty, useless frame ++ * ++ * \par Files ++ * \arg frame.h Definitions ++ * \arg frame.c Function library ++ * \arg \ref Def_Channel Asterisk channels ++ * \section Def_ControlFrame Control Frames ++ * Control frames send signalling information between channels ++ * and devices. They are prefixed with AST_CONTROL_, like AST_CONTROL_FRAME_HANGUP ++ * \arg \b HANGUP The other end has hungup ++ * \arg \b RING Local ring ++ * \arg \b RINGING The other end is ringing ++ * \arg \b ANSWER The other end has answered ++ * \arg \b BUSY Remote end is busy ++ * \arg \b TAKEOFFHOOK Make it go off hook (what's "it" ? ) ++ * \arg \b OFFHOOK Line is off hook ++ * \arg \b CONGESTION Congestion (circuit is busy, not available) ++ * \arg \b FLASH Other end sends flash hook ++ * \arg \b WINK Other end sends wink ++ * \arg \b OPTION Send low-level option ++ * \arg \b RADIO_KEY Key radio (see app_rpt.c) ++ * \arg \b RADIO_UNKEY Un-key radio (see app_rpt.c) ++ * \arg \b PROGRESS Other end indicates call progress ++ * \arg \b PROCEEDING Indicates proceeding ++ * \arg \b HOLD Call is placed on hold ++ * \arg \b UNHOLD Call is back from hold ++ * \arg \b VIDUPDATE Video update requested ++ * \arg \b SRCUPDATE The source of media has changed ++ * \arg \b CONNECTED_LINE Connected line has changed ++ * \arg \b REDIRECTING Call redirecting information has changed. ++ */ + +- \par Frame types +- \arg \b VOICE: Voice data, subclass is codec (AST_FORMAT_*) +- \arg \b VIDEO: Video data, subclass is codec (AST_FORMAT_*) +- \arg \b DTMF: A DTMF digit, subclass is the digit +- \arg \b IMAGE: Image transport, mostly used in IAX +- \arg \b TEXT: Text messages and character by character (real time text) +- \arg \b HTML: URL's and web pages +- \arg \b MODEM: Modulated data encodings, such as T.38 and V.150 +- \arg \b IAX: Private frame type for the IAX protocol +- \arg \b CNG: Comfort noice frames +- \arg \b CONTROL: A control frame, subclass defined as AST_CONTROL_ +- \arg \b NULL: Empty, useless frame +- +- \par Files +- \arg frame.h Definitions +- \arg frame.c Function library +- \arg \ref Def_Channel Asterisk channels +- \section Def_ControlFrame Control Frames +- Control frames send signalling information between channels +- and devices. They are prefixed with AST_CONTROL_, like AST_CONTROL_FRAME_HANGUP +- \arg \b HANGUP The other end has hungup +- \arg \b RING Local ring +- \arg \b RINGING The other end is ringing +- \arg \b ANSWER The other end has answered +- \arg \b BUSY Remote end is busy +- \arg \b TAKEOFFHOOK Make it go off hook (what's "it" ? ) +- \arg \b OFFHOOK Line is off hook +- \arg \b CONGESTION Congestion (circuit is busy, not available) +- \arg \b FLASH Other end sends flash hook +- \arg \b WINK Other end sends wink +- \arg \b OPTION Send low-level option +- \arg \b RADIO_KEY Key radio (see app_rpt.c) +- \arg \b RADIO_UNKEY Un-key radio (see app_rpt.c) +- \arg \b PROGRESS Other end indicates call progress +- \arg \b PROCEEDING Indicates proceeding +- \arg \b HOLD Call is placed on hold +- \arg \b UNHOLD Call is back from hold +- \arg \b VIDUPDATE Video update requested +- \arg \b SRCUPDATE The source of media has changed +- +-*/ +- + /*! + * \brief Frame types + * +@@ -319,6 +321,9 @@ + AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */ + AST_CONTROL_T38 = 19, /*!< T38 state change request/notification */ + AST_CONTROL_SRCUPDATE = 20, /*!< Indicate source of media has changed */ ++ AST_CONTROL_TRANSFER = 21, /*!< Indicate status of a transfer request */ ++ AST_CONTROL_CONNECTED_LINE = 22, /*!< Indicate connected line has changed */ ++ AST_CONTROL_REDIRECTING = 23 /*!< Indicate redirecting id has changed */ + }; + + enum ast_control_t38 { +@@ -329,6 +334,11 @@ + AST_T38_REFUSED /*!< T38 refused for some reason (usually rejected by remote end) */ + }; + ++enum ast_control_transfer { ++ AST_TRANSFER_SUCCESS = 0, /*!< Transfer request on the channel worked */ ++ AST_TRANSFER_FAILED, /*!< Transfer request on the channel failed */ ++}; ++ + #define AST_SMOOTHER_FLAG_G729 (1 << 0) + #define AST_SMOOTHER_FLAG_BE (1 << 1) + +Index: include/asterisk/devicestate.h +=================================================================== +--- a/include/asterisk/devicestate.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/devicestate.h (.../trunk) (revision 186562) +@@ -37,6 +37,8 @@ + #ifndef _ASTERISK_DEVICESTATE_H + #define _ASTERISK_DEVICESTATE_H + ++#include "asterisk/channel.h" ++ + #if defined(__cplusplus) || defined(c_plusplus) + extern "C" { + #endif +@@ -260,6 +262,21 @@ + unsigned int ring:1; + }; + ++/*! ++ * \brief Enable distributed device state processing. ++ * ++ * \details ++ * By default, Asterisk assumes that device state change events will only be ++ * originating from one instance. If a module gets loaded and configured such ++ * that multiple instances of Asterisk will be sharing device state, this ++ * function should be called to enable distributed device state processing. ++ * It is off by default to save on unnecessary processing. ++ * ++ * \retval 0 success ++ * \retval -1 failure ++ */ ++int ast_enable_distributed_devstate(void); ++ + #if defined(__cplusplus) || defined(c_plusplus) + } + #endif +Index: include/asterisk/astobj2.h +=================================================================== +--- a/include/asterisk/astobj2.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/astobj2.h (.../trunk) (revision 186562) +@@ -388,6 +388,7 @@ + * + * \param data_size The sizeof() of the user-defined structure. + * \param destructor_fn The destructor function (can be NULL) ++ * \param debug_msg + * \return A pointer to user-data. + * + * Allocates a struct astobj2 with sufficient space for the +@@ -397,24 +398,26 @@ + * - the refcount of the object just created is 1 + * - the returned pointer cannot be free()'d or realloc()'ed; + * rather, we just call ao2_ref(o, -1); ++ * ++ * @{ + */ + + #ifdef REF_DEBUG + + +-#define ao2_t_alloc(arg1, arg2, arg3) _ao2_alloc_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_alloc(arg1, arg2) _ao2_alloc_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_alloc(data_size, destructor_fn, debug_msg) __ao2_alloc_debug((data_size), (destructor_fn), (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_alloc(data_size, destructor_fn) __ao2_alloc_debug((data_size), (destructor_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + + #else + +-#define ao2_t_alloc(arg1,arg2,arg3) _ao2_alloc((arg1), (arg2)) +-#define ao2_alloc(arg1,arg2) _ao2_alloc((arg1), (arg2)) ++#define ao2_t_alloc(data_size, destructor_fn, debug_msg) __ao2_alloc((data_size), (destructor_fn)) ++#define ao2_alloc(data_size, destructor_fn) __ao2_alloc((data_size), (destructor_fn)) + + #endif +-void *_ao2_alloc_debug(const size_t data_size, ao2_destructor_fn destructor_fn, char *tag, char *file, int line, const char *funcname); +-void *_ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn); ++void *__ao2_alloc_debug(const size_t data_size, ao2_destructor_fn destructor_fn, char *tag, char *file, int line, const char *funcname); ++void *__ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn); ++/*! @} */ + +- + /*! \brief + * Reference/unreference an object and return the old refcount. + * +@@ -434,17 +437,20 @@ + * have a reference count to it, so the only case when the object + * can go away is when we release our reference, and it is + * the last one in existence. ++ * ++ * @{ + */ + + #ifdef REF_DEBUG +-#define ao2_t_ref(arg1,arg2,arg3) _ao2_ref_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_ref(arg1,arg2) _ao2_ref_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_ref(o,delta,tag) __ao2_ref_debug((o), (delta), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_ref(o,delta) __ao2_ref_debug((o), (delta), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_ref(arg1,arg2,arg3) _ao2_ref((arg1), (arg2)) +-#define ao2_ref(arg1,arg2) _ao2_ref((arg1), (arg2)) ++#define ao2_t_ref(o,delta,tag) __ao2_ref((o), (delta)) ++#define ao2_ref(o,delta) __ao2_ref((o), (delta)) + #endif +-int _ao2_ref_debug(void *o, int delta, char *tag, char *file, int line, const char *funcname); +-int _ao2_ref(void *o, int delta); ++int __ao2_ref_debug(void *o, int delta, char *tag, char *file, int line, const char *funcname); ++int __ao2_ref(void *o, int delta); ++/*! @} */ + + /*! \brief + * Lock an object. +@@ -455,8 +461,8 @@ + #ifndef DEBUG_THREADS + int ao2_lock(void *a); + #else +-#define ao2_lock(a) _ao2_lock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a) +-int _ao2_lock(void *a, const char *file, const char *func, int line, const char *var); ++#define ao2_lock(a) __ao2_lock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a) ++int __ao2_lock(void *a, const char *file, const char *func, int line, const char *var); + #endif + + /*! \brief +@@ -468,8 +474,8 @@ + #ifndef DEBUG_THREADS + int ao2_unlock(void *a); + #else +-#define ao2_unlock(a) _ao2_unlock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a) +-int _ao2_unlock(void *a, const char *file, const char *func, int line, const char *var); ++#define ao2_unlock(a) __ao2_unlock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a) ++int __ao2_unlock(void *a, const char *file, const char *func, int line, const char *var); + #endif + + /*! \brief +@@ -481,8 +487,8 @@ + #ifndef DEBUG_THREADS + int ao2_trylock(void *a); + #else +-#define ao2_trylock(a) _ao2_trylock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a) +-int _ao2_trylock(void *a, const char *file, const char *func, int line, const char *var); ++#define ao2_trylock(a) __ao2_trylock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a) ++int __ao2_trylock(void *a, const char *file, const char *func, int line, const char *var); + #endif + + /*! +@@ -686,15 +692,15 @@ + */ + + #ifdef REF_DEBUG +-#define ao2_t_container_alloc(arg1,arg2,arg3,arg4) _ao2_container_alloc_debug((arg1), (arg2), (arg3), (arg4), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_container_alloc(arg1,arg2,arg3) _ao2_container_alloc_debug((arg1), (arg2), (arg3), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_container_alloc(arg1,arg2,arg3,arg4) __ao2_container_alloc_debug((arg1), (arg2), (arg3), (arg4), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_container_alloc(arg1,arg2,arg3) __ao2_container_alloc_debug((arg1), (arg2), (arg3), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_container_alloc(arg1,arg2,arg3,arg4) _ao2_container_alloc((arg1), (arg2), (arg3)) +-#define ao2_container_alloc(arg1,arg2,arg3) _ao2_container_alloc((arg1), (arg2), (arg3)) ++#define ao2_t_container_alloc(arg1,arg2,arg3,arg4) __ao2_container_alloc((arg1), (arg2), (arg3)) ++#define ao2_container_alloc(arg1,arg2,arg3) __ao2_container_alloc((arg1), (arg2), (arg3)) + #endif +-struct ao2_container *_ao2_container_alloc(const unsigned int n_buckets, ++struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, + ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn); +-struct ao2_container *_ao2_container_alloc_debug(const unsigned int n_buckets, ++struct ao2_container *__ao2_container_alloc_debug(const unsigned int n_buckets, + ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn, + char *tag, char *file, int line, const char *funcname); + +@@ -730,14 +736,14 @@ + */ + #ifdef REF_DEBUG + +-#define ao2_t_link(arg1, arg2, arg3) _ao2_link_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_link(arg1, arg2) _ao2_link_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_link(arg1, arg2, arg3) __ao2_link_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_link(arg1, arg2) __ao2_link_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_link(arg1, arg2, arg3) _ao2_link((arg1), (arg2)) +-#define ao2_link(arg1, arg2) _ao2_link((arg1), (arg2)) ++#define ao2_t_link(arg1, arg2, arg3) __ao2_link((arg1), (arg2)) ++#define ao2_link(arg1, arg2) __ao2_link((arg1), (arg2)) + #endif +-void *_ao2_link_debug(struct ao2_container *c, void *new_obj, char *tag, char *file, int line, const char *funcname); +-void *_ao2_link(struct ao2_container *c, void *newobj); ++void *__ao2_link_debug(struct ao2_container *c, void *new_obj, char *tag, char *file, int line, const char *funcname); ++void *__ao2_link(struct ao2_container *c, void *newobj); + + /*! + * \brief Remove an object from the container +@@ -756,14 +762,14 @@ + * refcount will be decremented). + */ + #ifdef REF_DEBUG +-#define ao2_t_unlink(arg1, arg2, arg3) _ao2_unlink_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_unlink(arg1, arg2) _ao2_unlink_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_unlink(arg1, arg2, arg3) __ao2_unlink_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_unlink(arg1, arg2) __ao2_unlink_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_unlink(arg1, arg2, arg3) _ao2_unlink((arg1), (arg2)) +-#define ao2_unlink(arg1, arg2) _ao2_unlink((arg1), (arg2)) ++#define ao2_t_unlink(arg1, arg2, arg3) __ao2_unlink((arg1), (arg2)) ++#define ao2_unlink(arg1, arg2) __ao2_unlink((arg1), (arg2)) + #endif +-void *_ao2_unlink_debug(struct ao2_container *c, void *obj, char *tag, char *file, int line, const char *funcname); +-void *_ao2_unlink(struct ao2_container *c, void *obj); ++void *__ao2_unlink_debug(struct ao2_container *c, void *obj, char *tag, char *file, int line, const char *funcname); ++void *__ao2_unlink(struct ao2_container *c, void *obj); + + + /*! \brief Used as return value if the flag OBJ_MULTIPLE is set */ +@@ -849,20 +855,23 @@ + * + * \note When the returned object is no longer in use, ao2_ref() should + * be used to free the additional reference possibly created by this function. ++ * ++ * @{ + */ + #ifdef REF_DEBUG +-#define ao2_t_callback(arg1,arg2,arg3,arg4,arg5) _ao2_callback_debug((arg1), (arg2), (arg3), (arg4), (arg5), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_callback(arg1,arg2,arg3,arg4) _ao2_callback_debug((arg1), (arg2), (arg3), (arg4), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_callback(c,flags,cb_fn,arg,tag) __ao2_callback_debug((c), (flags), (cb_fn), (arg), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_callback(c,flags,cb_fn,arg) __ao2_callback_debug((c), (flags), (cb_fn), (arg), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_callback(arg1,arg2,arg3,arg4,arg5) _ao2_callback((arg1), (arg2), (arg3), (arg4)) +-#define ao2_callback(arg1,arg2,arg3,arg4) _ao2_callback((arg1), (arg2), (arg3), (arg4)) ++#define ao2_t_callback(c,flags,cb_fn,arg,tag) __ao2_callback((c), (flags), (cb_fn), (arg)) ++#define ao2_callback(c,flags,cb_fn,arg) __ao2_callback((c), (flags), (cb_fn), (arg)) + #endif +-void *_ao2_callback_debug(struct ao2_container *c, enum search_flags flags, ++void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags, + ao2_callback_fn *cb_fn, void *arg, char *tag, + char *file, int line, const char *funcname); +-void *_ao2_callback(struct ao2_container *c, ++void *__ao2_callback(struct ao2_container *c, + enum search_flags flags, + ao2_callback_fn *cb_fn, void *arg); ++/*! @} */ + + /*! \brief + * ao2_callback_data() is a generic function that applies cb_fn() to all objects +@@ -880,16 +889,16 @@ + * \see ao2_callback() + */ + #ifdef REF_DEBUG +-#define ao2_t_callback_data(arg1,arg2,arg3,arg4,arg5,arg6) _ao2_callback_data_debug((arg1), (arg2), (arg3), (arg4), (arg5), (arg6), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_callback_data(arg1,arg2,arg3,arg4,arg5) _ao2_callback_data_debug((arg1), (arg2), (arg3), (arg4), (arg5), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_callback_data(arg1,arg2,arg3,arg4,arg5,arg6) __ao2_callback_data_debug((arg1), (arg2), (arg3), (arg4), (arg5), (arg6), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_callback_data(arg1,arg2,arg3,arg4,arg5) __ao2_callback_data_debug((arg1), (arg2), (arg3), (arg4), (arg5), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_callback_data(arg1,arg2,arg3,arg4,arg5,arg6) _ao2_callback_data((arg1), (arg2), (arg3), (arg4), (arg5)) +-#define ao2_callback_data(arg1,arg2,arg3,arg4,arg5) _ao2_callback_data((arg1), (arg2), (arg3), (arg4), (arg5)) ++#define ao2_t_callback_data(arg1,arg2,arg3,arg4,arg5,arg6) __ao2_callback_data((arg1), (arg2), (arg3), (arg4), (arg5)) ++#define ao2_callback_data(arg1,arg2,arg3,arg4,arg5) __ao2_callback_data((arg1), (arg2), (arg3), (arg4), (arg5)) + #endif +-void *_ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags, ++void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags, + ao2_callback_data_fn *cb_fn, void *arg, void *data, char *tag, + char *file, int line, const char *funcname); +-void *_ao2_callback_data(struct ao2_container *c, ++void *__ao2_callback_data(struct ao2_container *c, + enum search_flags flags, + ao2_callback_data_fn *cb_fn, void *arg, void *data); + +@@ -897,14 +906,14 @@ + * XXX possibly change order of arguments ? + */ + #ifdef REF_DEBUG +-#define ao2_t_find(arg1,arg2,arg3,arg4) _ao2_find_debug((arg1), (arg2), (arg3), (arg4), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_find(arg1,arg2,arg3) _ao2_find_debug((arg1), (arg2), (arg3), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_find(arg1,arg2,arg3,arg4) __ao2_find_debug((arg1), (arg2), (arg3), (arg4), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_find(arg1,arg2,arg3) __ao2_find_debug((arg1), (arg2), (arg3), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_find(arg1,arg2,arg3,arg4) _ao2_find((arg1), (arg2), (arg3)) +-#define ao2_find(arg1,arg2,arg3) _ao2_find((arg1), (arg2), (arg3)) ++#define ao2_t_find(arg1,arg2,arg3,arg4) __ao2_find((arg1), (arg2), (arg3)) ++#define ao2_find(arg1,arg2,arg3) __ao2_find((arg1), (arg2), (arg3)) + #endif +-void *_ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname); +-void *_ao2_find(struct ao2_container *c, void *arg, enum search_flags flags); ++void *__ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname); ++void *__ao2_find(struct ao2_container *c, void *arg, enum search_flags flags); + + /*! \brief + * +@@ -1003,14 +1012,14 @@ + + struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags); + #ifdef REF_DEBUG +-#define ao2_t_iterator_next(arg1, arg2) _ao2_iterator_next_debug((arg1), (arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__) +-#define ao2_iterator_next(arg1) _ao2_iterator_next_debug((arg1), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_t_iterator_next(arg1, arg2) __ao2_iterator_next_debug((arg1), (arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__) ++#define ao2_iterator_next(arg1) __ao2_iterator_next_debug((arg1), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #else +-#define ao2_t_iterator_next(arg1, arg2) _ao2_iterator_next((arg1)) +-#define ao2_iterator_next(arg1) _ao2_iterator_next((arg1)) ++#define ao2_t_iterator_next(arg1, arg2) __ao2_iterator_next((arg1)) ++#define ao2_iterator_next(arg1) __ao2_iterator_next((arg1)) + #endif +-void *_ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname); +-void *_ao2_iterator_next(struct ao2_iterator *a); ++void *__ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname); ++void *__ao2_iterator_next(struct ao2_iterator *a); + + /* extra functions */ + void ao2_bt(void); /* backtrace */ +Index: include/asterisk/heap.h +=================================================================== +--- a/include/asterisk/heap.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/heap.h (.../trunk) (revision 186562) +@@ -209,6 +209,8 @@ + */ + size_t ast_heap_size(struct ast_heap *h); + ++#ifndef DEBUG_THREADS ++ + /*! + * \brief Write-Lock a heap + * +@@ -247,6 +249,17 @@ + */ + int ast_heap_unlock(struct ast_heap *h); + ++#else /* DEBUG_THREADS */ ++ ++#define ast_heap_wrlock(h) __ast_heap_wrlock(h, __FILE__, __PRETTY_FUNCTION__, __LINE__) ++int __ast_heap_wrlock(struct ast_heap *h, const char *file, const char *func, int line); ++#define ast_heap_rdlock(h) __ast_heap_rdlock(h, __FILE__, __PRETTY_FUNCTION__, __LINE__) ++int __ast_heap_rdlock(struct ast_heap *h, const char *file, const char *func, int line); ++#define ast_heap_unlock(h) __ast_heap_unlock(h, __FILE__, __PRETTY_FUNCTION__, __LINE__) ++int __ast_heap_unlock(struct ast_heap *h, const char *file, const char *func, int line); ++ ++#endif /* DEBUG_THREADS */ ++ + /*! + * \brief Verify that a heap has been properly constructed + * +Index: include/asterisk/lock.h +=================================================================== +--- a/include/asterisk/lock.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/lock.h (.../trunk) (revision 186562) +@@ -500,8 +500,10 @@ + if (t->tracking) { + #ifdef HAVE_BKTR + ast_reentrancy_lock(lt); +- ast_bt_get_addresses(<->backtrace[lt->reentrancy]); +- bt = <->backtrace[lt->reentrancy]; ++ if (lt->reentrancy != AST_MAX_REENTRANCY) { ++ ast_bt_get_addresses(<->backtrace[lt->reentrancy]); ++ bt = <->backtrace[lt->reentrancy]; ++ } + ast_reentrancy_unlock(lt); + ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt); + #else +@@ -622,8 +624,10 @@ + if (t->tracking) { + #ifdef HAVE_BKTR + ast_reentrancy_lock(lt); +- ast_bt_get_addresses(<->backtrace[lt->reentrancy]); +- bt = <->backtrace[lt->reentrancy]; ++ if (lt->reentrancy != AST_MAX_REENTRANCY) { ++ ast_bt_get_addresses(<->backtrace[lt->reentrancy]); ++ bt = <->backtrace[lt->reentrancy]; ++ } + ast_reentrancy_unlock(lt); + ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt); + #else +@@ -1069,6 +1073,7 @@ + #ifdef HAVE_BKTR + struct ast_bt *bt = NULL; + #endif ++ int lock_found = 0; + + + #ifdef AST_MUTEX_INIT_W_CONSTRUCTORS +@@ -1086,44 +1091,35 @@ + + ast_reentrancy_lock(lt); + if (lt->reentrancy) { +- int lock_found = 0; + int i; + pthread_t self = pthread_self(); +- for (i = lt->reentrancy-1; i >= 0; --i) { ++ for (i = lt->reentrancy - 1; i >= 0; --i) { + if (lt->thread[i] == self) { + lock_found = 1; +- if (i != lt->reentrancy-1) { +- lt->file[i] = lt->file[lt->reentrancy-1]; +- lt->lineno[i] = lt->lineno[lt->reentrancy-1]; +- lt->func[i] = lt->func[lt->reentrancy-1]; +- lt->thread[i] = lt->thread[lt->reentrancy-1]; ++ if (i != lt->reentrancy - 1) { ++ lt->file[i] = lt->file[lt->reentrancy - 1]; ++ lt->lineno[i] = lt->lineno[lt->reentrancy - 1]; ++ lt->func[i] = lt->func[lt->reentrancy - 1]; ++ lt->thread[i] = lt->thread[lt->reentrancy - 1]; + } ++#ifdef HAVE_BKTR ++ bt = <->backtrace[i]; ++#endif ++ lt->file[lt->reentrancy - 1] = NULL; ++ lt->lineno[lt->reentrancy - 1] = 0; ++ lt->func[lt->reentrancy - 1] = NULL; ++ lt->thread[lt->reentrancy - 1] = AST_PTHREADT_NULL; + break; + } + } +- if (!lock_found) { +- __ast_mutex_logger("%s line %d (%s): attempted unlock rwlock '%s' without owning it!\n", +- filename, line, func, name); +- __ast_mutex_logger("%s line %d (%s): '%s' was last locked here.\n", +- lt->file[lt->reentrancy-1], lt->lineno[lt->reentrancy-1], lt->func[lt->reentrancy-1], name); +-#ifdef HAVE_BKTR +- __dump_backtrace(<->backtrace[lt->reentrancy-1], canlog); +-#endif +- DO_THREAD_CRASH; +- } + } + +- if (--lt->reentrancy < 0) { ++ if (lock_found && --lt->reentrancy < 0) { + __ast_mutex_logger("%s line %d (%s): rwlock '%s' freed more times than we've locked!\n", + filename, line, func, name); + lt->reentrancy = 0; + } + +-#ifdef HAVE_BKTR +- if (lt->reentrancy) { +- bt = <->backtrace[lt->reentrancy - 1]; +- } +-#endif + ast_reentrancy_unlock(lt); + + if (t->tracking) { +@@ -1171,8 +1167,10 @@ + if (t->tracking) { + #ifdef HAVE_BKTR + ast_reentrancy_lock(lt); +- ast_bt_get_addresses(<->backtrace[lt->reentrancy]); +- bt = <->backtrace[lt->reentrancy]; ++ if (lt->reentrancy != AST_MAX_REENTRANCY) { ++ ast_bt_get_addresses(<->backtrace[lt->reentrancy]); ++ bt = <->backtrace[lt->reentrancy]; ++ } + ast_reentrancy_unlock(lt); + ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t, bt); + #else +@@ -1220,9 +1218,6 @@ + lt->func[lt->reentrancy] = func; + lt->thread[lt->reentrancy] = pthread_self(); + lt->reentrancy++; +- } else { +- __ast_mutex_logger("%s line %d (%s): read lock '%s' really deep reentrancy!\n", +- filename, line, func, name); + } + ast_reentrancy_unlock(lt); + if (t->tracking) { +@@ -1280,8 +1275,10 @@ + if (t->tracking) { + #ifdef HAVE_BKTR + ast_reentrancy_lock(lt); +- ast_bt_get_addresses(<->backtrace[lt->reentrancy]); +- bt = <->backtrace[lt->reentrancy]; ++ if (lt->reentrancy != AST_MAX_REENTRANCY) { ++ ast_bt_get_addresses(<->backtrace[lt->reentrancy]); ++ bt = <->backtrace[lt->reentrancy]; ++ } + ast_reentrancy_unlock(lt); + ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt); + #else +@@ -1328,9 +1325,6 @@ + lt->func[lt->reentrancy] = func; + lt->thread[lt->reentrancy] = pthread_self(); + lt->reentrancy++; +- } else { +- __ast_mutex_logger("%s line %d (%s): write lock '%s' really deep reentrancy!\n", +- filename, line, func, name); + } + ast_reentrancy_unlock(lt); + if (t->tracking) { +@@ -1364,7 +1358,6 @@ + { + int res; + struct ast_lock_track *lt = &t->track; +- int canlog = strcmp(filename, "logger.c") & t->tracking; + #ifdef HAVE_BKTR + struct ast_bt *bt = NULL; + #endif +@@ -1388,8 +1381,10 @@ + if (t->tracking) { + #ifdef HAVE_BKTR + ast_reentrancy_lock(lt); +- ast_bt_get_addresses(<->backtrace[lt->reentrancy]); +- bt = <->backtrace[lt->reentrancy]; ++ if (lt->reentrancy != AST_MAX_REENTRANCY) { ++ ast_bt_get_addresses(<->backtrace[lt->reentrancy]); ++ bt = <->backtrace[lt->reentrancy]; ++ } + ast_reentrancy_unlock(lt); + ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t, bt); + #else +@@ -1405,9 +1400,6 @@ + lt->func[lt->reentrancy] = func; + lt->thread[lt->reentrancy] = pthread_self(); + lt->reentrancy++; +- } else { +- __ast_mutex_logger("%s line %d (%s): read lock '%s' really deep reentrancy!\n", +- filename, line, func, name); + } + ast_reentrancy_unlock(lt); + if (t->tracking) { +@@ -1424,7 +1416,6 @@ + { + int res; + struct ast_lock_track *lt= &t->track; +- int canlog = strcmp(filename, "logger.c") & t->tracking; + #ifdef HAVE_BKTR + struct ast_bt *bt = NULL; + #endif +@@ -1448,8 +1439,10 @@ + if (t->tracking) { + #ifdef HAVE_BKTR + ast_reentrancy_lock(lt); +- ast_bt_get_addresses(<->backtrace[lt->reentrancy]); +- bt = <->backtrace[lt->reentrancy]; ++ if (lt->reentrancy != AST_MAX_REENTRANCY) { ++ ast_bt_get_addresses(<->backtrace[lt->reentrancy]); ++ bt = <->backtrace[lt->reentrancy]; ++ } + ast_reentrancy_unlock(lt); + ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt); + #else +@@ -1465,9 +1458,6 @@ + lt->func[lt->reentrancy] = func; + lt->thread[lt->reentrancy] = pthread_self(); + lt->reentrancy++; +- } else { +- __ast_mutex_logger("%s line %d (%s): write lock '%s' really deep reentrancy!\n", +- filename, line, func, name); + } + ast_reentrancy_unlock(lt); + if (t->tracking) { +Index: include/asterisk/pbx.h +=================================================================== +--- a/include/asterisk/pbx.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/pbx.h (.../trunk) (revision 186562) +@@ -37,12 +37,13 @@ + #define AST_PBX_KEEP 0 + #define AST_PBX_REPLACE 1 + +-/*! \brief Special return values from applications to the PBX { */ ++/*! \brief Special return values from applications to the PBX ++ * @{ */ + #define AST_PBX_HANGUP -1 /*!< Jump to the 'h' exten */ + #define AST_PBX_OK 0 /*!< No errors */ + #define AST_PBX_ERROR 1 /*!< Jump to the 'e' exten */ + #define AST_PBX_INCOMPLETE 12 /*!< Return to PBX matching, allowing more digits for the extension */ +-/*! } */ ++/*! @} */ + + #define PRIORITY_HINT -1 /*!< Special Priority for a hint */ + +@@ -134,7 +135,8 @@ + + /*!\brief Deallocates memory structures associated with a timing bitmap. + * \param i Pointer to an ast_timing structure. +- * \retval Returns 0 on success or a number suitable for passing into strerror, otherwise. ++ * \retval 0 success ++ * \retval non-zero failure (number suitable to pass to \see strerror) + */ + int ast_destroy_timing(struct ast_timing *i); + +@@ -152,7 +154,8 @@ + * This function registers a populated ast_switch structure with the + * asterisk switching architecture. + * +- * \return 0 on success, and other than 0 on failure ++ * \retval 0 success ++ * \retval non-zero failure + */ + int ast_register_switch(struct ast_switch *sw); + +@@ -191,7 +194,8 @@ + * saves the stack and executes the given application passing in + * the given data. + * +- * \return 0 on success, and -1 on failure ++ * \retval 0 success ++ * \retval -1 failure + */ + int pbx_exec(struct ast_channel *c, struct ast_app *app, void *data); + +@@ -251,7 +255,7 @@ + */ + struct ast_context *ast_context_find(const char *name); + +-/*! \brief The result codes when starting the PBX on a channelwith \see ast_pbx_start. ++/*! \brief The result codes when starting the PBX on a channel with \see ast_pbx_start. + AST_PBX_CALL_LIMIT refers to the maxcalls call limit in asterisk.conf + */ + enum ast_pbx_result { +@@ -403,18 +407,18 @@ + * \brief If an extension hint exists, return non-zero + * + * \param hint buffer for hint +- * \param maxlen size of hint buffer ++ * \param hintsize size of hint buffer, in bytes + * \param name buffer for name portion of hint +- * \param maxnamelen size of name buffer +- * \param c this is not important ++ * \param namesize size of name buffer ++ * \param c Channel from which to return the hint. This is only important when the hint or name contains an expression to be expanded. + * \param context which context to look in + * \param exten which extension to search for + * + * \return If an extension within the given context with the priority PRIORITY_HINT +- * is found a non zero value will be returned. ++ * is found, a non zero value will be returned. + * Otherwise, 0 is returned. + */ +-int ast_get_hint(char *hint, int maxlen, char *name, int maxnamelen, ++int ast_get_hint(char *hint, int hintsize, char *name, int namesize, + struct ast_channel *c, const char *context, const char *exten); + + /*! +@@ -679,6 +683,8 @@ + * + * \retval 0 on success + * \retval -1 on failure ++ * ++ * @{ + */ + int ast_context_remove_extension(const char *context, const char *extension, int priority, + const char *registrar); +@@ -692,6 +698,7 @@ + int ast_context_remove_extension_callerid2(struct ast_context *con, const char *extension, + int priority, const char *callerid, int matchcid, const char *registrar, + int already_locked); ++/*! @} */ + + /*! + * \brief Add an ignorepat +@@ -818,8 +825,13 @@ + */ + int ast_context_unlockmacro(const char *macrocontext); + ++/*!\brief Set the channel to next execute the specified dialplan location. ++ * \see ast_async_parseable_goto, ast_async_goto_if_exists ++ */ + int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority); + ++/*!\brief Set the channel to next execute the specified dialplan location. ++ */ + int ast_async_goto_by_name(const char *chan, const char *context, const char *exten, int priority); + + /*! Synchronously or asynchronously make an outbound call and send it to a +@@ -873,7 +885,8 @@ + const char *ast_get_switch_registrar(struct ast_sw *sw); + /*! @} */ + +-/* Walking functions ... */ ++/*! @name Walking functions ... */ ++/*! @{ */ + struct ast_context *ast_walk_contexts(struct ast_context *con); + struct ast_exten *ast_walk_context_extensions(struct ast_context *con, + struct ast_exten *priority); +@@ -884,13 +897,16 @@ + struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con, + struct ast_ignorepat *ip); + struct ast_sw *ast_walk_context_switches(struct ast_context *con, struct ast_sw *sw); ++/*! @} */ + +-/*! ++/*!\brief Create a human-readable string, specifying all variables and their corresponding values. ++ * \param chan Channel from which to read variables ++ * \param buf Dynamic string in which to place the result (should be allocated with \see ast_str_create). + * \note Will lock the channel. + */ + int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf); + +-/*! ++/*!\brief Return a pointer to the value of the corresponding channel variable. + * \note Will lock the channel. + * + * \note This function will return a pointer to the buffer inside the channel +@@ -909,43 +925,51 @@ + */ + const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name); + +-/*! ++/*!\brief Add a variable to the channel variable stack, without removing any previously set value. + * \note Will lock the channel. + */ + void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value); + +-/*! +- * \note Will lock the channel. ++/*!\brief Add a variable to the channel variable stack, removing the most recently set value for the same name. ++ * \note Will lock the channel. May also be used to set a channel dialplan function to a particular value. ++ * \see ast_func_write + */ + void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value); + +-/*! ++/*!\brief Retrieve the value of a builtin variable or variable from the channel variable stack. + * \note Will lock the channel. + */ + void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp); + void pbx_builtin_clear_globals(void); + +-/*! ++/*!\brief Parse and set a single channel variable, where the name and value are separated with an '=' character. + * \note Will lock the channel. + */ + int pbx_builtin_setvar(struct ast_channel *chan, void *data); ++ ++/*!\brief Parse and set multiple channel variables, where the pairs are separated by the ',' character, and name and value are separated with an '=' character. ++ * \note Will lock the channel. ++ */ + int pbx_builtin_setvar_multiple(struct ast_channel *chan, void *data); + + int pbx_builtin_raise_exception(struct ast_channel *chan, void *data); + +-void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count); ++/*! @name Substitution routines, using static string buffers ++ * @{ */ ++void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count); + void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count); + void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used); + void ast_str_substitute_variables(struct ast_str **buf, size_t maxlen, struct ast_channel *chan, const char *templ); ++/*! @} */ + + int ast_extension_patmatch(const char *pattern, const char *data); + +-/*! Set "autofallthrough" flag, if newval is <0, does not acutally set. If ++/*! Set "autofallthrough" flag, if newval is <0, does not actually set. If + set to 1, sets to auto fall through. If newval set to 0, sets to no auto + fall through (reads extension instead). Returns previous value. */ + int pbx_set_autofallthrough(int newval); + +-/*! Set "extenpatternmatchnew" flag, if newval is <0, does not acutally set. If ++/*! Set "extenpatternmatchnew" flag, if newval is <0, does not actually set. If + set to 1, sets to use the new Trie-based pattern matcher. If newval set to 0, sets to use + the old linear-search algorithm. Returns previous value. */ + int pbx_set_extenpatternmatchnew(int newval); +@@ -963,10 +987,6 @@ + int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority); + + /*! +- * \note I can find neither parsable nor parseable at dictionary.com, +- * but google gives me 169000 hits for parseable and only 49,800 +- * for parsable +- * + * \note This function will handle locking the channel as needed. + */ + int ast_parseable_goto(struct ast_channel *chan, const char *goto_string); +@@ -1023,7 +1043,8 @@ + * + * This application executes a function in read mode on a given channel. + * +- * \return zero on success, non-zero on failure ++ * \retval 0 success ++ * \retval non-zero failure + */ + int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len); + +@@ -1036,7 +1057,8 @@ + * + * This application executes a function in write mode on a given channel. + * +- * \return zero on success, non-zero on failure ++ * \retval 0 success ++ * \retval non-zero failure + */ + int ast_func_write(struct ast_channel *chan, const char *function, const char *value); + +@@ -1092,9 +1114,11 @@ + int ast_wrlock_contexts_version(void); + + +-/* hashtable functions for contexts */ ++/*!\brief hashtable functions for contexts */ ++/*! @{ */ + int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b); + unsigned int ast_hashtab_hash_contexts(const void *obj); ++/*! @} */ + + #if defined(__cplusplus) || defined(c_plusplus) + } +Index: include/asterisk/strings.h +=================================================================== +--- a/include/asterisk/strings.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/strings.h (.../trunk) (revision 186562) +@@ -451,7 +451,7 @@ + ) + + /*!\brief Returns the current length of the string stored within buf. +- * \param A pointer to the ast_str string. ++ * \param buf A pointer to the ast_str structure. + */ + AST_INLINE_API( + size_t attribute_pure ast_str_strlen(struct ast_str *buf), +@@ -461,7 +461,8 @@ + ) + + /*!\brief Returns the current maximum length (without reallocation) of the current buffer. +- * \param A pointer to the ast_str string. ++ * \param buf A pointer to the ast_str structure. ++ * \retval Current maximum length of the buffer. + */ + AST_INLINE_API( + size_t attribute_pure ast_str_size(struct ast_str *buf), +@@ -471,7 +472,8 @@ + ) + + /*!\brief Returns the string buffer within the ast_str buf. +- * \param A pointer to the ast_str string. ++ * \param buf A pointer to the ast_str structure. ++ * \retval A pointer to the enclosed string. + */ + AST_INLINE_API( + char * attribute_pure ast_str_buffer(struct ast_str *buf), +@@ -480,6 +482,11 @@ + } + ) + ++/*!\brief Truncates the enclosed string to the given length. ++ * \param buf A pointer to the ast_str structure. ++ * \param len Maximum length of the string. ++ * \retval A pointer to the resulting string. ++ */ + AST_INLINE_API( + char *ast_str_truncate(struct ast_str *buf, ssize_t len), + { +@@ -852,6 +859,29 @@ + } + + /*! ++ * \brief Compute a hash value on a string ++ * ++ * \param[in] str The string to add to the hash ++ * \param[in] hash The hash value to add to ++ * ++ * \details ++ * This version of the function is for when you need to compute a ++ * string hash of more than one string. ++ * ++ * This famous hash algorithm was written by Dan Bernstein and is ++ * commonly used. ++ * ++ * \sa http://www.cse.yorku.ca/~oz/hash.html ++ */ ++static force_inline int ast_str_hash_add(const char *str, int hash) ++{ ++ while (*str) ++ hash = hash * 33 ^ *str++; ++ ++ return abs(hash); ++} ++ ++/*! + * \brief Compute a hash value on a case-insensitive string + * + * Uses the same hash algorithm as ast_str_hash, but converts +Index: include/asterisk/stun.h +=================================================================== +--- a/include/asterisk/stun.h (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/include/asterisk/stun.h (.../trunk) (revision 186562) +@@ -0,0 +1,71 @@ ++/* ++ * Asterisk -- An open source telephony toolkit. ++ * ++ * Copyright (C) 1999 - 2008, Digium, Inc. ++ * ++ * Mark Spencer <markster@digium.com> ++ * ++ * See http://www.asterisk.org for more information about ++ * the Asterisk project. Please do not directly contact ++ * any of the maintainers of this project for assistance; ++ * the project provides a web site, mailing lists and IRC ++ * channels for your use. ++ * ++ * 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. ++ */ ++ ++/*! ++ * \file stun.h ++ * \brief STUN support. ++ * ++ * STUN is defined in RFC 3489. ++ */ ++ ++#ifndef _ASTERISK_STUN_H ++#define _ASTERISK_STUN_H ++ ++#include "asterisk/network.h" ++ ++#if defined(__cplusplus) || defined(c_plusplus) ++extern "C" { ++#endif ++ ++enum ast_stun_result { ++ AST_STUN_IGNORE = 0, ++ AST_STUN_ACCEPT, ++}; ++ ++struct stun_attr; ++ ++/*! \brief Generic STUN request ++ * send a generic stun request to the server specified. ++ * \param s the socket used to send the request ++ * \param dst the address of the STUN server ++ * \param username if non null, add the username in the request ++ * \param answer if non null, the function waits for a response and ++ * puts here the externally visible address. ++ * \return 0 on success, other values on error. ++ * The interface it may change in the future. ++ */ ++int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer); ++ ++/*! \brief callback type to be invoked on stun responses. */ ++typedef int (stun_cb_f)(struct stun_attr *attr, void *arg); ++ ++/*! \brief handle an incoming STUN message. ++ * ++ * Do some basic sanity checks on packet size and content, ++ * try to extract a bit of information, and possibly reply. ++ * At the moment this only processes BIND requests, and returns ++ * the externally visible address of the request. ++ * If a callback is specified, invoke it with the attribute. ++ */ ++int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg); ++ ++#if defined(__cplusplus) || defined(c_plusplus) ++} ++#endif ++ ++#endif /* _ASTERISK_STUN_H */ + +Property changes on: include/asterisk/stun.h +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: include/asterisk/stringfields.h +=================================================================== +--- a/include/asterisk/stringfields.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/stringfields.h (.../trunk) (revision 186562) +@@ -57,25 +57,23 @@ + + Fields will default to pointing to an empty string, and will revert to + that when ast_string_field_set() is called with a NULL argument. +- A string field will \b never contain NULL (this feature is not used +- in this code, but comes from external requirements). ++ A string field will \b never contain NULL. + + ast_string_field_init(x, 0) will reset fields to the + initial value while keeping the pool allocated. + + Reading the fields is much like using 'const char * const' fields in the +- structure: you cannot write to the field or to the memory it points to +- (XXX perhaps the latter is too much of a restriction since values +- are not shared). ++ structure: you cannot write to the field or to the memory it points to. + + Writing to the fields must be done using the wrapper macros listed below; + and assignments are always by value (i.e. strings are copied): + * ast_string_field_set() stores a simple value; +- * ast_string_field_build() builds the string using a printf-style; ++ * ast_string_field_build() builds the string using a printf-style format; + * ast_string_field_build_va() is the varargs version of the above (for +- portability reasons it uses two vararg); ++ portability reasons it uses two vararg arguments); + * variants of these function allow passing a pointer to the field + as an argument. ++ + \code + ast_string_field_set(x, foo, "infinite loop"); + ast_string_field_set(x, foo, NULL); // set to an empty string +@@ -110,6 +108,9 @@ + + Don't declare instances of this type directly; use the AST_STRING_FIELD() + macro instead. ++ ++ In addition to the string itself, the amount of space allocated for the ++ field is stored in the two bytes immediately preceding it. + */ + typedef const char * ast_string_field; + +@@ -117,7 +118,7 @@ + \internal + \brief A constant empty string used for fields that have no other value + */ +-extern const char __ast_string_field_empty[]; ++extern const char *__ast_string_field_empty; + + /*! + \internal +@@ -125,18 +126,17 @@ + */ + struct ast_string_field_pool { + struct ast_string_field_pool *prev; /*!< pointer to the previous pool, if any */ ++ size_t size; /*!< the total size of the pool */ ++ size_t used; /*!< the space used in the pool */ ++ size_t active; /*!< the amount of space actively in use by fields */ + char base[0]; /*!< storage space for the fields */ + }; + + /*! + \internal + \brief Structure used to manage the storage for a set of string fields. +- Because of the way pools are managed, we can only allocate from the topmost +- pool, so the numbers here reflect just that. + */ + struct ast_string_field_mgr { +- size_t size; /*!< the total size of the current pool */ +- size_t used; /*!< the space used in the current pool */ + ast_string_field last_alloc; /*!< the last field allocated */ + }; + +@@ -154,7 +154,8 @@ + the pool has enough space available. If so, the additional space will be + allocated to this field and the field's address will not be changed. + */ +-int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, size_t needed, ++int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, ++ struct ast_string_field_pool **pool_head, size_t needed, + const ast_string_field *ptr); + + /*! +@@ -176,7 +177,7 @@ + \internal + \brief Set a field to a complex (built) value + \param mgr Pointer to the pool manager structure +- \param fields Pointer to the first entry of the field array ++ \param pool_head Pointer to the current pool + \param ptr Pointer to a field within the structure + \param format printf-style format string + \return nothing +@@ -189,7 +190,7 @@ + \internal + \brief Set a field to a complex (built) value + \param mgr Pointer to the pool manager structure +- \param fields Pointer to the first entry of the field array ++ \param pool_head Pointer to the current pool + \param ptr Pointer to a field within the structure + \param format printf-style format string + \param args va_list of the args for the format_string +@@ -242,29 +243,55 @@ + + /*! \internal \brief internal version of ast_string_field_init */ + int __ast_string_field_init(struct ast_string_field_mgr *mgr, +- struct ast_string_field_pool **pool_head, int needed); ++ struct ast_string_field_pool **pool_head, int needed); + + /*! ++ \internal ++ \brief Release a field's allocation from a pool ++ \param pool_head Pointer to the current pool ++ \param ptr Field to be released ++ \return nothing ++ ++ This function will search the pool list to find the pool that contains ++ the allocation for the specified field, then remove the field's allocation ++ from that pool's 'active' count. If the pool's active count reaches zero, ++ and it is not the current pool, then it will be freed. ++ */ ++void __ast_string_field_release_active(struct ast_string_field_pool *pool_head, ++ const ast_string_field ptr); ++ ++/* the type of storage used to track how many bytes were allocated for a field */ ++ ++typedef uint16_t ast_string_field_allocation; ++ ++/*! ++ \brief Macro to provide access to the allocation field that lives immediately in front of a string field ++ \param x Pointer to the string field ++*/ ++#define AST_STRING_FIELD_ALLOCATION(x) *((ast_string_field_allocation *) (x - sizeof(ast_string_field_allocation))) ++ ++/*! + \brief Set a field to a simple string value + \param x Pointer to a structure containing fields + \param ptr Pointer to a field within the structure +- \param data String value to be copied into the field ++ \param data String value to be copied into the field + \return nothing + */ +-#define ast_string_field_ptr_set(x, ptr, data) do { \ +- const char *__d__ = (data); \ +- size_t __dlen__ = (__d__) ? strlen(__d__) + 1 : 1; \ +- const char **__p__ = (const char **) (ptr); \ +- char *__q__; \ +- if (__dlen__ == 1) \ +- *__p__ = __ast_string_field_empty; \ +- else if (!__ast_string_field_ptr_grow(&(x)->__field_mgr, __dlen__, ptr)) { \ +- __q__ = (char *) *__p__; \ +- memcpy(__q__, __d__, __dlen__); \ +- } else if ((*__p__ = __ast_string_field_alloc_space(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__))) { \ +- __q__ = (char *) *__p__; \ +- memcpy(__q__, __d__, __dlen__); \ +- } \ ++#define ast_string_field_ptr_set(x, ptr, data) do { \ ++ const char *__d__ = (data); \ ++ size_t __dlen__ = (__d__) ? strlen(__d__) + 1 : 1; \ ++ ast_string_field *__p__ = (ast_string_field *) (ptr); \ ++ if (__dlen__ == 1) { \ ++ __ast_string_field_release_active((x)->__field_mgr_pool, *__p__); \ ++ *__p__ = __ast_string_field_empty; \ ++ } else if ((__dlen__ <= AST_STRING_FIELD_ALLOCATION(*__p__)) || \ ++ (!__ast_string_field_ptr_grow(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__, __p__)) || \ ++ (*__p__ = __ast_string_field_alloc_space(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__))) { \ ++ if (*__p__ != (*ptr)) { \ ++ __ast_string_field_release_active((x)->__field_mgr_pool, (*ptr)); \ ++ } \ ++ memcpy(* (void **) __p__, __d__, __dlen__); \ ++ } \ + } while (0) + + /*! +Index: include/asterisk/autoconfig.h.in +=================================================================== +--- a/include/asterisk/autoconfig.h.in (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/autoconfig.h.in (.../trunk) (revision 186562) +@@ -767,12 +767,6 @@ + /* Define to indicate the ${ROUND_DESCRIP} library version */ + #undef HAVE_ROUND_VERSION + +-/* Define if your system has the RTLD_NOLOAD headers. */ +-#undef HAVE_RTLD_NOLOAD +- +-/* Define RTLD_NOLOAD headers version */ +-#undef HAVE_RTLD_NOLOAD_VERSION +- + /* Define to 1 if your system has /sbin/launchd. */ + #undef HAVE_SBIN_LAUNCHD + +Index: include/asterisk/callerid.h +=================================================================== +--- a/include/asterisk/callerid.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/callerid.h (.../trunk) (revision 186562) +@@ -86,21 +86,24 @@ + void callerid_init(void); + + /*! \brief Generates a CallerID FSK stream in ulaw format suitable for transmission. +- * \param buf Buffer to use. If "buf" is supplied, it will use that buffer instead of allocating its own. "buf" must be at least 32000 bytes in size of you want to be sure you don't have an overrun. ++ * \param buf Buffer to use. If "buf" is supplied, it will use that buffer instead of allocating its own. ++ * "buf" must be at least 32000 bytes in size of you want to be sure you don't have an overrun. + * \param number Use NULL for no number or "P" for "private" + * \param name name to be used + * \param flags passed flags + * \param callwaiting callwaiting flag + * \param codec -- either AST_FORMAT_ULAW or AST_FORMAT_ALAW ++ * \details + * This function creates a stream of callerid (a callerid spill) data in ulaw format. + * \return It returns the size + * (in bytes) of the data (if it returns a size of 0, there is probably an error) +-*/ ++ */ + int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, int codec); + + /*! \brief Create a callerID state machine + * \param cid_signalling Type of signalling in use + * ++ * \details + * This function returns a malloc'd instance of the callerid_state data structure. + * \return Returns a pointer to a malloc'd callerid_state structure, or NULL on error. + */ +@@ -112,9 +115,11 @@ + * \param samples number of samples contained within the buffer. + * \param codec which codec (AST_FORMAT_ALAW or AST_FORMAT_ULAW) + * ++ * \details + * Send received audio to the Caller*ID demodulator. +- * \return Returns -1 on error, 0 for "needs more samples", +- * and 1 if the CallerID spill reception is complete. ++ * \retval -1 on error ++ * \retval 0 for "needs more samples" ++ * \retval 1 if the CallerID spill reception is complete. + */ + int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int samples, int codec); + +@@ -124,9 +129,11 @@ + * \param samples number of samples contained within the buffer. + * \param codec which codec (AST_FORMAT_ALAW or AST_FORMAT_ULAW) + * ++ * \details + * Send received audio to the Caller*ID demodulator (for japanese style lines). +- * \return Returns -1 on error, 0 for "needs more samples", +- * and 1 if the CallerID spill reception is complete. ++ * \retval -1 on error ++ * \retval 0 for "needs more samples" ++ * \retval 1 if the CallerID spill reception is complete. + */ + int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int samples, int codec); + +@@ -136,6 +143,7 @@ + * \param name Pass the address of a pointer-to-char (will contain the name) + * \param flags Pass the address of an int variable(will contain the various callerid flags) + * ++ * \details + * This function extracts a callerid string out of a callerid_state state machine. + * If no number is found, *number will be set to NULL. Likewise for the name. + * Flags can contain any of the following: +@@ -144,18 +152,16 @@ + */ + void callerid_get(struct callerid_state *cid, char **number, char **name, int *flags); + +-/*! Get and parse DTMF-based callerid */ + /*! ++ * \brief Get and parse DTMF-based callerid + * \param cidstring The actual transmitted string. + * \param number The cid number is returned here. + * \param flags The cid flags are returned here. +- * This function parses DTMF callerid. + */ + void callerid_get_dtmf(char *cidstring, char *number, int *flags); + +-/*! \brief Free a callerID state ++/*! \brief This function frees callerid_state cid. + * \param cid This is the callerid_state state machine to free +- * This function frees callerid_state cid. + */ + void callerid_free(struct callerid_state *cid); + +@@ -165,36 +171,44 @@ + * \param number Caller-ID Number + * \param codec Asterisk codec (either AST_FORMAT_ALAW or AST_FORMAT_ULAW) + * ++ * \details + * Acts like callerid_generate except uses an asterisk format callerid string. + */ + int ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int codec); + +-/*! \brief Generate message waiting indicator +- * \param active The message indicator state ++/*! ++ * \brief Generate message waiting indicator ++ * \param active The message indicator state + * -- either 0 no messages in mailbox or 1 messages in mailbox +- * \param type Format of message (any of CID_MWI_TYPE_*) +- * \see callerid_generate() for more info as it use the same encoding +- * \version 1.6.1 changed mdmf parameter to type, added name, number and flags for caller id message generation +-*/ +-int vmwi_generate(unsigned char *buf, int active, int type, int codec, const char *name, ++ * \param type Format of message (any of CID_MWI_TYPE_*) ++ * \see callerid_generate() for more info as it uses the same encoding ++ * \version 1.6.1 changed mdmf parameter to type, added name, number and flags for caller id message generation ++ */ ++int ast_callerid_vmwi_generate(unsigned char *buf, int active, int type, int codec, const char *name, + const char *number, int flags); + + /*! \brief Generate Caller-ID spill but in a format suitable for Call Waiting(tm)'s Caller*ID(tm) +- * See ast_callerid_generate() for other details ++ * \see ast_callerid_generate() for other details + */ + int ast_callerid_callwaiting_generate(unsigned char *buf, const char *name, const char *number, int codec); + + /*! \brief Destructively parse inbuf into name and location (or number) ++ * \details + * Parses callerid stream from inbuf and changes into useable form, outputed in name and location. + * \param instr buffer of callerid stream (in audio form) to be parsed. Warning, data in buffer is changed. + * \param name address of a pointer-to-char for the name value of the stream. + * \param location address of a pointer-to-char for the phone number value of the stream. ++ * \note XXX 'name' is not parsed consistently e.g. we have ++ * input location name ++ * " foo bar " <123> 123 ' foo bar ' (with spaces around) ++ * " foo bar " NULL 'foo bar' (without spaces around) ++ * The parsing of leading and trailing space/quotes should be more consistent. + * \return Returns 0 on success, -1 on failure. + */ + int ast_callerid_parse(char *instr, char **name, char **location); + +-/*! Generate a CAS (CPE Alert Signal) tone for 'n' samples */ + /*! ++ * \brief Generate a CAS (CPE Alert Signal) tone for 'n' samples + * \param outbuf Allocated buffer for data. Must be at least 2400 bytes unless no SAS is desired + * \param sas Non-zero if CAS should be preceeded by SAS + * \param len How many samples to generate. +@@ -203,23 +217,26 @@ + */ + int ast_gen_cas(unsigned char *outbuf, int sas, int len, int codec); + +-/*! \brief Shrink a phone number in place to just digits (more accurately it just removes ()'s, .'s, and -'s... */ + /*! ++ * \brief Shrink a phone number in place to just digits (more accurately it just removes ()'s, .'s, and -'s... + * \param n The number to be stripped/shrunk + * \return Returns nothing important + */ + void ast_shrink_phone_number(char *n); + +-/*! \brief Check if a string consists only of digits and + \# +- \param n number to be checked. +- \return Returns 0 if n is a number, 1 if it's not. ++/*! ++ * \brief Check if a string consists only of digits and + \# ++ * \param n number to be checked. ++ * \return Returns 0 if n is a number, 1 if it's not. + */ + int ast_isphonenumber(const char *n); + +-/*! \brief Check if a string consists only of digits and and + \# ( ) - . +- (meaning it can be cleaned with ast_shrink_phone_number) +- \param exten The extension (or URI) to be checked. +- \return Returns 0 if n is a number, 1 if it's not. ++/*! ++ * \brief Check if a string consists only of digits and and + \# ( ) - . ++ * (meaning it can be cleaned with ast_shrink_phone_number) ++ * \param exten The extension (or URI) to be checked. ++ * \retval 1 if string is valid AST shrinkable phone number ++ * \retval 0 if not + */ + int ast_is_shrinkable_phonenumber(const char *exten); + +@@ -289,71 +306,171 @@ + + /* Various defines and bits for handling PRI- and SS7-type restriction */ + +-#define AST_PRES_NUMBER_TYPE 0x03 ++#define AST_PRES_NUMBER_TYPE 0x03 + #define AST_PRES_USER_NUMBER_UNSCREENED 0x00 + #define AST_PRES_USER_NUMBER_PASSED_SCREEN 0x01 + #define AST_PRES_USER_NUMBER_FAILED_SCREEN 0x02 +-#define AST_PRES_NETWORK_NUMBER 0x03 ++#define AST_PRES_NETWORK_NUMBER 0x03 + +-#define AST_PRES_RESTRICTION 0x60 +-#define AST_PRES_ALLOWED 0x00 +-#define AST_PRES_RESTRICTED 0x20 +-#define AST_PRES_UNAVAILABLE 0x40 +-#define AST_PRES_RESERVED 0x60 ++#define AST_PRES_RESTRICTION 0x60 ++#define AST_PRES_ALLOWED 0x00 ++#define AST_PRES_RESTRICTED 0x20 ++#define AST_PRES_UNAVAILABLE 0x40 ++#define AST_PRES_RESERVED 0x60 + + #define AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED \ +- AST_PRES_USER_NUMBER_UNSCREENED + AST_PRES_ALLOWED ++ (AST_PRES_ALLOWED | AST_PRES_USER_NUMBER_UNSCREENED) + + #define AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN \ +- AST_PRES_USER_NUMBER_PASSED_SCREEN + AST_PRES_ALLOWED ++ (AST_PRES_ALLOWED | AST_PRES_USER_NUMBER_PASSED_SCREEN) + + #define AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN \ +- AST_PRES_USER_NUMBER_FAILED_SCREEN + AST_PRES_ALLOWED ++ (AST_PRES_ALLOWED | AST_PRES_USER_NUMBER_FAILED_SCREEN) + + #define AST_PRES_ALLOWED_NETWORK_NUMBER \ +- AST_PRES_NETWORK_NUMBER + AST_PRES_ALLOWED ++ (AST_PRES_ALLOWED | AST_PRES_NETWORK_NUMBER) + + #define AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED \ +- AST_PRES_USER_NUMBER_UNSCREENED + AST_PRES_RESTRICTED ++ (AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_UNSCREENED) + + #define AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN \ +- AST_PRES_USER_NUMBER_PASSED_SCREEN + AST_PRES_RESTRICTED ++ (AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_PASSED_SCREEN) + + #define AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN \ +- AST_PRES_USER_NUMBER_FAILED_SCREEN + AST_PRES_RESTRICTED ++ (AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_FAILED_SCREEN) + + #define AST_PRES_PROHIB_NETWORK_NUMBER \ +- AST_PRES_NETWORK_NUMBER + AST_PRES_RESTRICTED ++ (AST_PRES_RESTRICTED | AST_PRES_NETWORK_NUMBER) + + #define AST_PRES_NUMBER_NOT_AVAILABLE \ +- AST_PRES_NETWORK_NUMBER + AST_PRES_UNAVAILABLE ++ (AST_PRES_UNAVAILABLE | AST_PRES_NETWORK_NUMBER) + + int ast_parse_caller_presentation(const char *data); + const char *ast_describe_caller_presentation(int data); + const char *ast_named_caller_presentation(int data); + +-/*! \page Def_CallerPres Caller ID Presentation ++/*! ++ * \page Def_CallerPres Caller ID Presentation ++ * ++ * Caller ID presentation values are used to set properties to a ++ * caller ID in PSTN networks, and as RPID value in SIP transactions. ++ * ++ * The following values are available to use: ++ * \arg \b Defined value, text string in config file, explanation ++ * ++ * \arg \b AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "allowed_not_screened", Presentation Allowed, Not Screened, ++ * \arg \b AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen", Presentation Allowed, Passed Screen, ++ * \arg \b AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen", Presentation Allowed, Failed Screen, ++ * \arg \b AST_PRES_ALLOWED_NETWORK_NUMBER, "allowed", Presentation Allowed, Network Number, ++ * \arg \b AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "prohib_not_screened", Presentation Prohibited, Not Screened, ++ * \arg \b AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen", Presentation Prohibited, Passed Screen, ++ * \arg \b AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen", Presentation Prohibited, Failed Screen, ++ * \arg \b AST_PRES_PROHIB_NETWORK_NUMBER, "prohib", Presentation Prohibited, Network Number, ++ * ++ * \par References ++ * \arg \ref callerid.h Definitions ++ * \arg \ref callerid.c Functions ++ * \arg \ref CID Caller ID names and numbers ++ */ + +- Caller ID presentation values are used to set properties to a +- caller ID in PSTN networks, and as RPID value in SIP transactions. ++/*! ++ * \brief redirecting reason codes. ++ * ++ * This list attempts to encompass redirecting reasons ++ * as defined by several channel technologies. ++ */ ++enum AST_REDIRECTING_REASON { ++ AST_REDIRECTING_REASON_UNKNOWN, ++ AST_REDIRECTING_REASON_USER_BUSY, ++ AST_REDIRECTING_REASON_NO_ANSWER, ++ AST_REDIRECTING_REASON_UNAVAILABLE, ++ AST_REDIRECTING_REASON_UNCONDITIONAL, ++ AST_REDIRECTING_REASON_TIME_OF_DAY, ++ AST_REDIRECTING_REASON_DO_NOT_DISTURB, ++ AST_REDIRECTING_REASON_DEFLECTION, ++ AST_REDIRECTING_REASON_FOLLOW_ME, ++ AST_REDIRECTING_REASON_OUT_OF_ORDER, ++ AST_REDIRECTING_REASON_AWAY, ++ AST_REDIRECTING_REASON_CALL_FWD_DTE, /* This is something defined in Q.931, and no I don't know what it means */ ++}; + +- The following values are available to use: +- \arg \b Defined value, text string in config file, explanation ++/*! ++ * \since 1.6.3 ++ * \brief Convert redirecting reason text code to value (used in config file parsing) ++ * ++ * \param data text string from config file ++ * ++ * \retval Q931_REDIRECTING_REASON from callerid.h ++ * \retval -1 if not in table ++ */ ++int ast_redirecting_reason_parse(const char *data); + +- \arg \b AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "allowed_not_screened", Presentation Allowed, Not Screened, +- \arg \b AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen", Presentation Allowed, Passed Screen, +- \arg \b AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen", Presentation Allowed, Failed Screen, +- \arg \b AST_PRES_ALLOWED_NETWORK_NUMBER, "allowed", Presentation Allowed, Network Number, +- \arg \b AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "prohib_not_screened", Presentation Prohibited, Not Screened, +- \arg \b AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen", Presentation Prohibited, Passed Screen, +- \arg \b AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen", Presentation Prohibited, Failed Screen, +- \arg \b AST_PRES_PROHIB_NETWORK_NUMBER, "prohib", Presentation Prohibited, Network Number, ++/*! ++ * \since 1.6.3 ++ * \brief Convert redirecting reason value to explanatory string ++ * ++ * \param data Q931_REDIRECTING_REASON from callerid.h ++ * ++ * \return string for human presentation ++ */ ++const char *ast_redirecting_reason_describe(int data); + +- \par References +- \arg \ref callerid.h Definitions +- \arg \ref callerid.c Functions +- \arg \ref CID Caller ID names and numbers +-*/ ++/*! ++ * \since 1.6.3 ++ * \brief Convert redirecting reason value to text code ++ * ++ * \param data Q931_REDIRECTING_REASON from callerid.h ++ * ++ * \return string for config file ++ */ ++const char *ast_redirecting_reason_name(int data); + ++/*! ++ * \brief Connected line update source code ++ */ ++enum AST_CONNECTED_LINE_UPDATE_SOURCE { ++ /*! Update for unknown reason (May be interpreted to mean from answer) */ ++ AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN, ++ /*! Update from normal call answering */ ++ AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, ++ /*! Update from call diversion (Deprecated, use REDIRECTING updates instead.) */ ++ AST_CONNECTED_LINE_UPDATE_SOURCE_DIVERSION, ++ /*! Update from call transfer(active) (Party has already answered) */ ++ AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER, ++ /*! Update from call transfer(alerting) (Party has not answered yet) */ ++ AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER_ALERTING ++}; + ++/*! ++ * \since 1.6.3 ++ * \brief Convert connected line update source text code to value (used in config file parsing) ++ * ++ * \param data text string from config file ++ * ++ * \retval AST_CONNECTED_LINE_UPDATE_SOURCE from callerid.h ++ * \retval -1 if not in table ++ */ ++int ast_connected_line_source_parse(const char *data); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Convert connected line update source value to explanatory string ++ * ++ * \param data AST_CONNECTED_LINE_UPDATE_SOURCE from callerid.h ++ * ++ * \return string for human presentation ++ */ ++const char *ast_connected_line_source_describe(int data); ++ ++/*! ++ * \since 1.6.3 ++ * \brief Convert connected line update source value to text code ++ * ++ * \param data AST_CONNECTED_LINE_UPDATE_SOURCE from callerid.h ++ * ++ * \return string for config file ++ */ ++const char *ast_connected_line_source_name(int data); ++ ++ + #endif /* _ASTERISK_CALLERID_H */ +Index: include/asterisk/doxyref.h +=================================================================== +--- a/include/asterisk/doxyref.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/doxyref.h (.../trunk) (revision 186562) +@@ -360,7 +360,8 @@ + * Some commit history viewers treat the first line of commit messages as the + * summary for the commit. So, an effort should be made to format our commit + * messages in that fashion. The verbose description may contain multiple +- * paragraphs, itemized lists, etc. ++ * paragraphs, itemized lists, etc. Always end the first sentence (and any ++ * subsequent sentences) with punctuation. + * + * Commit messages should be wrapped at 80 %columns. + * +Index: include/asterisk/crypto.h +=================================================================== +--- a/include/asterisk/crypto.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk/crypto.h (.../trunk) (revision 186562) +@@ -40,7 +40,7 @@ + * \retval the key on success. + * \retval NULL on failure. + */ +-struct ast_key *(*ast_key_get)(const char *key, int type); ++extern struct ast_key *(*ast_key_get)(const char *key, int type); + + /*! + * \brief Check the authenticity of a message signature using a given public key +@@ -52,7 +52,7 @@ + * \retval -1 otherwise. + * + */ +-int (*ast_check_signature)(struct ast_key *key, const char *msg, const char *sig); ++extern int (*ast_check_signature)(struct ast_key *key, const char *msg, const char *sig); + + /*! + * \brief Check the authenticity of a message signature using a given public key +@@ -64,7 +64,7 @@ + * \retval -1 otherwise. + * + */ +-int (*ast_check_signature_bin)(struct ast_key *key, const char *msg, int msglen, const unsigned char *sig); ++extern int (*ast_check_signature_bin)(struct ast_key *key, const char *msg, int msglen, const unsigned char *sig); + + /*! + * \brief Sign a message signature using a given private key +@@ -77,7 +77,7 @@ + * \retval -1 on failure. + * + */ +-int (*ast_sign)(struct ast_key *key, char *msg, char *sig); ++extern int (*ast_sign)(struct ast_key *key, char *msg, char *sig); + + /*! + * \brief Sign a message signature using a given private key +@@ -90,7 +90,7 @@ + * \retval -1 on failure. + * + */ +-int (*ast_sign_bin)(struct ast_key *key, const char *msg, int msglen, unsigned char *sig); ++extern int (*ast_sign_bin)(struct ast_key *key, const char *msg, int msglen, unsigned char *sig); + + /*! + * \brief Encrypt a message using a given private key +@@ -104,7 +104,7 @@ + * \retval -1 on failure. + * + */ +-int (*ast_encrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key); ++extern int (*ast_encrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key); + + /*! + * \brief Decrypt a message using a given private key +@@ -118,7 +118,7 @@ + * \retval -1 on failure. + * + */ +-int (*ast_decrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key); ++extern int (*ast_decrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key); + #if defined(__cplusplus) || defined(c_plusplus) + } + #endif +Index: include/asterisk.h +=================================================================== +--- a/include/asterisk.h (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/include/asterisk.h (.../trunk) (revision 186562) +@@ -146,10 +146,10 @@ + * + * (note, this must be documented a lot more) + * ast_add_profile allocates a generic 'counter' with a given name, +- * which can be shown with the command 'show profile <name>' ++ * which can be shown with the command 'core show profile <name>' + * + * The counter accumulates positive or negative values supplied by +- * ast_add_profile(), dividing them by the 'scale' value passed in the ++ * \see ast_add_profile(), dividing them by the 'scale' value passed in the + * create call, and also counts the number of 'events'. + * Values can also be taked by the TSC counter on ia32 architectures, + * in which case you can mark the start of an event calling ast_mark(id, 1) +Index: main/rtp.c +=================================================================== +--- a/main/rtp.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/rtp.c (.../trunk) (revision 186562) +-/* +- * Asterisk -- An open source telephony toolkit. +- * +- * Copyright (C) 1999 - 2006, Digium, Inc. +- * +- * Mark Spencer <markster@digium.com> +- * +- * See http://www.asterisk.org for more information about +- * the Asterisk project. Please do not directly contact +- * any of the maintainers of this project for assistance; +- * the project provides a web site, mailing lists and IRC +- * channels for your use. +- * +- * 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. +- */ +- +-/*! +- * \file +- * +- * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal. +- * +- * \author Mark Spencer <markster@digium.com> +- * +- * \note RTP is defined in RFC 3550. +- */ +- +-#include "asterisk.h" +- +-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 180373 $") +- +-#include <sys/time.h> +-#include <signal.h> +-#include <fcntl.h> +-#include <math.h> +- +-#include "asterisk/rtp.h" +-#include "asterisk/pbx.h" +-#include "asterisk/frame.h" +-#include "asterisk/channel.h" +-#include "asterisk/acl.h" +-#include "asterisk/config.h" +-#include "asterisk/lock.h" +-#include "asterisk/utils.h" +-#include "asterisk/netsock.h" +-#include "asterisk/cli.h" +-#include "asterisk/manager.h" +-#include "asterisk/unaligned.h" +- +-#define MAX_TIMESTAMP_SKEW 640 +- +-#define RTP_SEQ_MOD (1<<16) /*!< A sequence number can't be more than 16 bits */ +-#define RTCP_DEFAULT_INTERVALMS 5000 /*!< Default milli-seconds between RTCP reports we send */ +-#define RTCP_MIN_INTERVALMS 500 /*!< Min milli-seconds between RTCP reports we send */ +-#define RTCP_MAX_INTERVALMS 60000 /*!< Max milli-seconds between RTCP reports we send */ +- +-#define RTCP_PT_FUR 192 +-#define RTCP_PT_SR 200 +-#define RTCP_PT_RR 201 +-#define RTCP_PT_SDES 202 +-#define RTCP_PT_BYE 203 +-#define RTCP_PT_APP 204 +- +-#define RTP_MTU 1200 +- +-#define DEFAULT_DTMF_TIMEOUT 3000 /*!< samples */ +- +-static int dtmftimeout = DEFAULT_DTMF_TIMEOUT; +- +-static int rtpstart = 5000; /*!< First port for RTP sessions (set in rtp.conf) */ +-static int rtpend = 31000; /*!< Last port for RTP sessions (set in rtp.conf) */ +-static int rtpdebug; /*!< Are we debugging? */ +-static int rtcpdebug; /*!< Are we debugging RTCP? */ +-static int rtcpstats; /*!< Are we debugging RTCP? */ +-static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */ +-static int stundebug; /*!< Are we debugging stun? */ +-static struct sockaddr_in rtpdebugaddr; /*!< Debug packets to/from this host */ +-static struct sockaddr_in rtcpdebugaddr; /*!< Debug RTCP packets to/from this host */ +-#ifdef SO_NO_CHECK +-static int nochecksums; +-#endif +-static int strictrtp; +- +-enum strict_rtp_state { +- STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */ +- STRICT_RTP_LEARN, /*! Accept next packet as source */ +- STRICT_RTP_CLOSED, /*! Drop all RTP packets not coming from source that was learned */ +-}; +- +-/* Uncomment this to enable more intense native bridging, but note: this is currently buggy */ +-/* #define P2P_INTENSE */ +- +-/*! +- * \brief Structure representing a RTP session. +- * +- * RTP session is defined on page 9 of RFC 3550: "An association among a set of participants communicating with RTP. A participant may be involved in multiple RTP sessions at the same time [...]" +- * +- */ +- +-/*! \brief RTP session description */ +-struct ast_rtp { +- int s; +- struct ast_frame f; +- unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; +- unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */ +- unsigned int themssrc; /*!< Their SSRC */ +- unsigned int rxssrc; +- unsigned int lastts; +- unsigned int lastrxts; +- unsigned int lastividtimestamp; +- unsigned int lastovidtimestamp; +- unsigned int lastitexttimestamp; +- unsigned int lastotexttimestamp; +- unsigned int lasteventseqn; +- int lastrxseqno; /*!< Last received sequence number */ +- unsigned short seedrxseqno; /*!< What sequence number did they start with?*/ +- unsigned int seedrxts; /*!< What RTP timestamp did they start with? */ +- unsigned int rxcount; /*!< How many packets have we received? */ +- unsigned int rxoctetcount; /*!< How many octets have we received? should be rxcount *160*/ +- unsigned int txcount; /*!< How many packets have we sent? */ +- unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/ +- unsigned int cycles; /*!< Shifted count of sequence number cycles */ +- double rxjitter; /*!< Interarrival jitter at the moment */ +- double rxtransit; /*!< Relative transit time for previous packet */ +- int lasttxformat; +- int lastrxformat; +- +- int rtptimeout; /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */ +- int rtpholdtimeout; /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ +- int rtpkeepalive; /*!< Send RTP comfort noice packets for keepalive */ +- +- /* DTMF Reception Variables */ +- char resp; +- unsigned int lastevent; +- int dtmfcount; +- unsigned int dtmfsamples; +- /* DTMF Transmission Variables */ +- unsigned int lastdigitts; +- char sending_digit; /*!< boolean - are we sending digits */ +- char send_digit; /*!< digit we are sending */ +- int send_payload; +- int send_duration; +- int nat; +- unsigned int flags; +- struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ +- struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ +- struct timeval rxcore; +- struct timeval txcore; +- double drxcore; /*!< The double representation of the first received packet */ +- struct timeval lastrx; /*!< timeval when we last received a packet */ +- struct timeval dtmfmute; +- struct ast_smoother *smoother; +- int *ioid; +- unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */ +- unsigned short rxseqno; +- struct sched_context *sched; +- struct io_context *io; +- void *data; +- ast_rtp_callback callback; +-#ifdef P2P_INTENSE +- ast_mutex_t bridge_lock; +-#endif +- struct rtpPayloadType current_RTP_PT[MAX_RTP_PT]; +- int rtp_lookup_code_cache_isAstFormat; /*!< a cache for the result of rtp_lookup_code(): */ +- int rtp_lookup_code_cache_code; +- int rtp_lookup_code_cache_result; +- struct ast_rtcp *rtcp; +- struct ast_codec_pref pref; +- struct ast_rtp *bridged; /*!< Who we are Packet bridged to */ +- +- enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */ +- struct sockaddr_in strict_rtp_address; /*!< Remote address information for strict RTP purposes */ +- +- int set_marker_bit:1; /*!< Whether to set the marker bit or not */ +- struct rtp_red *red; +-}; +- +-static struct ast_frame *red_t140_to_red(struct rtp_red *red); +-static int red_write(const void *data); +- +-struct rtp_red { +- struct ast_frame t140; /*!< Primary data */ +- struct ast_frame t140red; /*!< Redundant t140*/ +- unsigned char pt[RED_MAX_GENERATION]; /*!< Payload types for redundancy data */ +- unsigned char ts[RED_MAX_GENERATION]; /*!< Time stamps */ +- unsigned char len[RED_MAX_GENERATION]; /*!< length of each generation */ +- int num_gen; /*!< Number of generations */ +- int schedid; /*!< Timer id */ +- int ti; /*!< How long to buffer data before send */ +- unsigned char t140red_data[64000]; +- unsigned char buf_data[64000]; /*!< buffered primary data */ +- int hdrlen; +- long int prev_ts; +-}; +- +-/* Forward declarations */ +-static int ast_rtcp_write(const void *data); +-static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw); +-static int ast_rtcp_write_sr(const void *data); +-static int ast_rtcp_write_rr(const void *data); +-static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp); +-static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp); +-int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit); +- +-#define FLAG_3389_WARNING (1 << 0) +-#define FLAG_NAT_ACTIVE (3 << 1) +-#define FLAG_NAT_INACTIVE (0 << 1) +-#define FLAG_NAT_INACTIVE_NOWARN (1 << 1) +-#define FLAG_HAS_DTMF (1 << 3) +-#define FLAG_P2P_SENT_MARK (1 << 4) +-#define FLAG_P2P_NEED_DTMF (1 << 5) +-#define FLAG_CALLBACK_MODE (1 << 6) +-#define FLAG_DTMF_COMPENSATE (1 << 7) +-#define FLAG_HAS_STUN (1 << 8) +- +-/*! +- * \brief Structure defining an RTCP session. +- * +- * The concept "RTCP session" is not defined in RFC 3550, but since +- * this structure is analogous to ast_rtp, which tracks a RTP session, +- * it is logical to think of this as a RTCP session. +- * +- * RTCP packet is defined on page 9 of RFC 3550. +- * +- */ +-struct ast_rtcp { +- int rtcp_info; +- int s; /*!< Socket */ +- struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ +- struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ +- unsigned int soc; /*!< What they told us */ +- unsigned int spc; /*!< What they told us */ +- unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/ +- struct timeval rxlsr; /*!< Time when we got their last SR */ +- struct timeval txlsr; /*!< Time when we sent or last SR*/ +- unsigned int expected_prior; /*!< no. packets in previous interval */ +- unsigned int received_prior; /*!< no. packets received in previous interval */ +- int schedid; /*!< Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/ +- unsigned int rr_count; /*!< number of RRs we've sent, not including report blocks in SR's */ +- unsigned int sr_count; /*!< number of SRs we've sent */ +- unsigned int lastsrtxcount; /*!< Transmit packet count when last SR sent */ +- double accumulated_transit; /*!< accumulated a-dlsr-lsr */ +- double rtt; /*!< Last reported rtt */ +- unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */ +- unsigned int reported_lost; /*!< Reported lost packets in their RR */ +- char quality[AST_MAX_USER_FIELD]; +- char quality_jitter[AST_MAX_USER_FIELD]; +- char quality_loss[AST_MAX_USER_FIELD]; +- char quality_rtt[AST_MAX_USER_FIELD]; +- +- double reported_maxjitter; +- double reported_minjitter; +- double reported_normdev_jitter; +- double reported_stdev_jitter; +- unsigned int reported_jitter_count; +- +- double reported_maxlost; +- double reported_minlost; +- double reported_normdev_lost; +- double reported_stdev_lost; +- +- double rxlost; +- double maxrxlost; +- double minrxlost; +- double normdev_rxlost; +- double stdev_rxlost; +- unsigned int rxlost_count; +- +- double maxrxjitter; +- double minrxjitter; +- double normdev_rxjitter; +- double stdev_rxjitter; +- unsigned int rxjitter_count; +- double maxrtt; +- double minrtt; +- double normdevrtt; +- double stdevrtt; +- unsigned int rtt_count; +- int sendfur; +-}; +- +-/*! +- * \brief STUN support code +- * +- * This code provides some support for doing STUN transactions. +- * Eventually it should be moved elsewhere as other protocols +- * than RTP can benefit from it - e.g. SIP. +- * STUN is described in RFC3489 and it is based on the exchange +- * of UDP packets between a client and one or more servers to +- * determine the externally visible address (and port) of the client +- * once it has gone through the NAT boxes that connect it to the +- * outside. +- * The simplest request packet is just the header defined in +- * struct stun_header, and from the response we may just look at +- * one attribute, STUN_MAPPED_ADDRESS, that we find in the response. +- * By doing more transactions with different server addresses we +- * may determine more about the behaviour of the NAT boxes, of +- * course - the details are in the RFC. +- * +- * All STUN packets start with a simple header made of a type, +- * length (excluding the header) and a 16-byte random transaction id. +- * Following the header we may have zero or more attributes, each +- * structured as a type, length and a value (whose format depends +- * on the type, but often contains addresses). +- * Of course all fields are in network format. +- */ +- +-typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id; +- +-struct stun_header { +- unsigned short msgtype; +- unsigned short msglen; +- stun_trans_id id; +- unsigned char ies[0]; +-} __attribute__((packed)); +- +-struct stun_attr { +- unsigned short attr; +- unsigned short len; +- unsigned char value[0]; +-} __attribute__((packed)); +- +-/* +- * The format normally used for addresses carried by STUN messages. +- */ +-struct stun_addr { +- unsigned char unused; +- unsigned char family; +- unsigned short port; +- unsigned int addr; +-} __attribute__((packed)); +- +-#define STUN_IGNORE (0) +-#define STUN_ACCEPT (1) +- +-/*! \brief STUN message types +- * 'BIND' refers to transactions used to determine the externally +- * visible addresses. 'SEC' refers to transactions used to establish +- * a session key for subsequent requests. +- * 'SEC' functionality is not supported here. +- */ +- +-#define STUN_BINDREQ 0x0001 +-#define STUN_BINDRESP 0x0101 +-#define STUN_BINDERR 0x0111 +-#define STUN_SECREQ 0x0002 +-#define STUN_SECRESP 0x0102 +-#define STUN_SECERR 0x0112 +- +-/*! \brief Basic attribute types in stun messages. +- * Messages can also contain custom attributes (codes above 0x7fff) +- */ +-#define STUN_MAPPED_ADDRESS 0x0001 +-#define STUN_RESPONSE_ADDRESS 0x0002 +-#define STUN_CHANGE_REQUEST 0x0003 +-#define STUN_SOURCE_ADDRESS 0x0004 +-#define STUN_CHANGED_ADDRESS 0x0005 +-#define STUN_USERNAME 0x0006 +-#define STUN_PASSWORD 0x0007 +-#define STUN_MESSAGE_INTEGRITY 0x0008 +-#define STUN_ERROR_CODE 0x0009 +-#define STUN_UNKNOWN_ATTRIBUTES 0x000a +-#define STUN_REFLECTED_FROM 0x000b +- +-/*! \brief helper function to print message names */ +-static const char *stun_msg2str(int msg) +-{ +- switch (msg) { +- case STUN_BINDREQ: +- return "Binding Request"; +- case STUN_BINDRESP: +- return "Binding Response"; +- case STUN_BINDERR: +- return "Binding Error Response"; +- case STUN_SECREQ: +- return "Shared Secret Request"; +- case STUN_SECRESP: +- return "Shared Secret Response"; +- case STUN_SECERR: +- return "Shared Secret Error Response"; +- } +- return "Non-RFC3489 Message"; +-} +- +-/*! \brief helper function to print attribute names */ +-static const char *stun_attr2str(int msg) +-{ +- switch (msg) { +- case STUN_MAPPED_ADDRESS: +- return "Mapped Address"; +- case STUN_RESPONSE_ADDRESS: +- return "Response Address"; +- case STUN_CHANGE_REQUEST: +- return "Change Request"; +- case STUN_SOURCE_ADDRESS: +- return "Source Address"; +- case STUN_CHANGED_ADDRESS: +- return "Changed Address"; +- case STUN_USERNAME: +- return "Username"; +- case STUN_PASSWORD: +- return "Password"; +- case STUN_MESSAGE_INTEGRITY: +- return "Message Integrity"; +- case STUN_ERROR_CODE: +- return "Error Code"; +- case STUN_UNKNOWN_ATTRIBUTES: +- return "Unknown Attributes"; +- case STUN_REFLECTED_FROM: +- return "Reflected From"; +- } +- return "Non-RFC3489 Attribute"; +-} +- +-/*! \brief here we store credentials extracted from a message */ +-struct stun_state { +- const char *username; +- const char *password; +-}; +- +-static int stun_process_attr(struct stun_state *state, struct stun_attr *attr) +-{ +- if (stundebug) +- ast_verbose("Found STUN Attribute %s (%04x), length %d\n", +- stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); +- switch (ntohs(attr->attr)) { +- case STUN_USERNAME: +- state->username = (const char *) (attr->value); +- break; +- case STUN_PASSWORD: +- state->password = (const char *) (attr->value); +- break; +- default: +- if (stundebug) +- ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n", +- stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); +- } +- return 0; +-} +- +-/*! \brief append a string to an STUN message */ +-static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left) +-{ +- int size = sizeof(**attr) + strlen(s); +- if (*left > size) { +- (*attr)->attr = htons(attrval); +- (*attr)->len = htons(strlen(s)); +- memcpy((*attr)->value, s, strlen(s)); +- (*attr) = (struct stun_attr *)((*attr)->value + strlen(s)); +- *len += size; +- *left -= size; +- } +-} +- +-/*! \brief append an address to an STUN message */ +-static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sock_in, int *len, int *left) +-{ +- int size = sizeof(**attr) + 8; +- struct stun_addr *addr; +- if (*left > size) { +- (*attr)->attr = htons(attrval); +- (*attr)->len = htons(8); +- addr = (struct stun_addr *)((*attr)->value); +- addr->unused = 0; +- addr->family = 0x01; +- addr->port = sock_in->sin_port; +- addr->addr = sock_in->sin_addr.s_addr; +- (*attr) = (struct stun_attr *)((*attr)->value + 8); +- *len += size; +- *left -= size; +- } +-} +- +-/*! \brief wrapper to send an STUN message */ +-static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp) +-{ +- return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0, +- (struct sockaddr *)dst, sizeof(*dst)); +-} +- +-/*! \brief helper function to generate a random request id */ +-static void stun_req_id(struct stun_header *req) +-{ +- int x; +- for (x = 0; x < 4; x++) +- req->id.id[x] = ast_random(); +-} +- +-size_t ast_rtp_alloc_size(void) +-{ +- return sizeof(struct ast_rtp); +-} +- +-/*! \brief callback type to be invoked on stun responses. */ +-typedef int (stun_cb_f)(struct stun_attr *attr, void *arg); +- +-/*! \brief handle an incoming STUN message. +- * +- * Do some basic sanity checks on packet size and content, +- * try to extract a bit of information, and possibly reply. +- * At the moment this only processes BIND requests, and returns +- * the externally visible address of the request. +- * If a callback is specified, invoke it with the attribute. +- */ +-static int stun_handle_packet(int s, struct sockaddr_in *src, +- unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg) +-{ +- struct stun_header *hdr = (struct stun_header *)data; +- struct stun_attr *attr; +- struct stun_state st; +- int ret = STUN_IGNORE; +- int x; +- +- /* On entry, 'len' is the length of the udp payload. After the +- * initial checks it becomes the size of unprocessed options, +- * while 'data' is advanced accordingly. +- */ +- if (len < sizeof(struct stun_header)) { +- ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header)); +- return -1; +- } +- len -= sizeof(struct stun_header); +- data += sizeof(struct stun_header); +- x = ntohs(hdr->msglen); /* len as advertised in the message */ +- if (stundebug) +- ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x); +- if (x > len) { +- ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len); +- } else +- len = x; +- memset(&st, 0, sizeof(st)); +- while (len) { +- if (len < sizeof(struct stun_attr)) { +- ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr)); +- break; +- } +- attr = (struct stun_attr *)data; +- /* compute total attribute length */ +- x = ntohs(attr->len) + sizeof(struct stun_attr); +- if (x > len) { +- ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len); +- break; +- } +- if (stun_cb) +- stun_cb(attr, arg); +- if (stun_process_attr(&st, attr)) { +- ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr)); +- break; +- } +- /* Clear attribute id: in case previous entry was a string, +- * this will act as the terminator for the string. +- */ +- attr->attr = 0; +- data += x; +- len -= x; +- } +- /* Null terminate any string. +- * XXX NOTE, we write past the size of the buffer passed by the +- * caller, so this is potentially dangerous. The only thing that +- * saves us is that usually we read the incoming message in a +- * much larger buffer in the struct ast_rtp +- */ +- *data = '\0'; +- +- /* Now prepare to generate a reply, which at the moment is done +- * only for properly formed (len == 0) STUN_BINDREQ messages. +- */ +- if (len == 0) { +- unsigned char respdata[1024]; +- struct stun_header *resp = (struct stun_header *)respdata; +- int resplen = 0; /* len excluding header */ +- int respleft = sizeof(respdata) - sizeof(struct stun_header); +- +- resp->id = hdr->id; +- resp->msgtype = 0; +- resp->msglen = 0; +- attr = (struct stun_attr *)resp->ies; +- switch (ntohs(hdr->msgtype)) { +- case STUN_BINDREQ: +- if (stundebug) +- ast_verbose("STUN Bind Request, username: %s\n", +- st.username ? st.username : "<none>"); +- if (st.username) +- append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft); +- append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft); +- resp->msglen = htons(resplen); +- resp->msgtype = htons(STUN_BINDRESP); +- stun_send(s, src, resp); +- ret = STUN_ACCEPT; +- break; +- default: +- if (stundebug) +- ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype))); +- } +- } +- return ret; +-} +- +-/*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response. +- * This is used as a callback for stun_handle_response +- * when called from ast_stun_request. +- */ +-static int stun_get_mapped(struct stun_attr *attr, void *arg) +-{ +- struct stun_addr *addr = (struct stun_addr *)(attr + 1); +- struct sockaddr_in *sa = (struct sockaddr_in *)arg; +- +- if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) +- return 1; /* not us. */ +- sa->sin_port = addr->port; +- sa->sin_addr.s_addr = addr->addr; +- return 0; +-} +- +-/*! \brief Generic STUN request +- * Send a generic stun request to the server specified, +- * possibly waiting for a reply and filling the 'reply' field with +- * the externally visible address. Note that in this case the request +- * will be blocking. +- * (Note, the interface may change slightly in the future). +- * +- * \param s the socket used to send the request +- * \param dst the address of the STUN server +- * \param username if non null, add the username in the request +- * \param answer if non null, the function waits for a response and +- * puts here the externally visible address. +- * \return 0 on success, other values on error. +- */ +-int ast_stun_request(int s, struct sockaddr_in *dst, +- const char *username, struct sockaddr_in *answer) +-{ +- struct stun_header *req; +- unsigned char reqdata[1024]; +- int reqlen, reqleft; +- struct stun_attr *attr; +- int res = 0; +- int retry; +- +- req = (struct stun_header *)reqdata; +- stun_req_id(req); +- reqlen = 0; +- reqleft = sizeof(reqdata) - sizeof(struct stun_header); +- req->msgtype = 0; +- req->msglen = 0; +- attr = (struct stun_attr *)req->ies; +- if (username) +- append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft); +- req->msglen = htons(reqlen); +- req->msgtype = htons(STUN_BINDREQ); +- for (retry = 0; retry < 3; retry++) { /* XXX make retries configurable */ +- /* send request, possibly wait for reply */ +- unsigned char reply_buf[1024]; +- fd_set rfds; +- struct timeval to = { 3, 0 }; /* timeout, make it configurable */ +- struct sockaddr_in src; +- socklen_t srclen; +- +- res = stun_send(s, dst, req); +- if (res < 0) { +- ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n", +- retry, res); +- continue; +- } +- if (answer == NULL) +- break; +- FD_ZERO(&rfds); +- FD_SET(s, &rfds); +- res = ast_select(s + 1, &rfds, NULL, NULL, &to); +- if (res <= 0) /* timeout or error */ +- continue; +- memset(&src, '\0', sizeof(src)); +- srclen = sizeof(src); +- /* XXX pass -1 in the size, because stun_handle_packet might +- * write past the end of the buffer. +- */ +- res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1, +- 0, (struct sockaddr *)&src, &srclen); +- if (res < 0) { +- ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n", +- retry, res); +- continue; +- } +- memset(answer, '\0', sizeof(struct sockaddr_in)); +- stun_handle_packet(s, &src, reply_buf, res, +- stun_get_mapped, answer); +- res = 0; /* signal regular exit */ +- break; +- } +- return res; +-} +- +-/*! \brief send a STUN BIND request to the given destination. +- * Optionally, add a username if specified. +- */ +-void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username) +-{ +- ast_stun_request(rtp->s, suggestion, username, NULL); +-} +- +-/*! \brief List of current sessions */ +-static AST_RWLIST_HEAD_STATIC(protos, ast_rtp_protocol); +- +-static void timeval2ntp(struct timeval when, unsigned int *msw, unsigned int *lsw) +-{ +- unsigned int sec, usec, frac; +- sec = when.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */ +- usec = when.tv_usec; +- frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6); +- *msw = sec; +- *lsw = frac; +-} +- +-int ast_rtp_fd(struct ast_rtp *rtp) +-{ +- return rtp->s; +-} +- +-int ast_rtcp_fd(struct ast_rtp *rtp) +-{ +- if (rtp->rtcp) +- return rtp->rtcp->s; +- return -1; +-} +- +-unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp) +-{ +- unsigned int interval; +- /*! \todo XXX Do a more reasonable calculation on this one +- * Look in RFC 3550 Section A.7 for an example*/ +- interval = rtcpinterval; +- return interval; +-} +- +-/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */ +-void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp) +-{ +- rtp->rtptimeout = (-1) * rtp->rtptimeout; +- rtp->rtpholdtimeout = (-1) * rtp->rtpholdtimeout; +-} +- +-/*! \brief Set rtp timeout */ +-void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout) +-{ +- rtp->rtptimeout = timeout; +-} +- +-/*! \brief Set rtp hold timeout */ +-void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout) +-{ +- rtp->rtpholdtimeout = timeout; +-} +- +-/*! \brief set RTP keepalive interval */ +-void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period) +-{ +- rtp->rtpkeepalive = period; +-} +- +-/*! \brief Get rtp timeout */ +-int ast_rtp_get_rtptimeout(struct ast_rtp *rtp) +-{ +- if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */ +- return 0; +- return rtp->rtptimeout; +-} +- +-/*! \brief Get rtp hold timeout */ +-int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp) +-{ +- if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */ +- return 0; +- return rtp->rtpholdtimeout; +-} +- +-/*! \brief Get RTP keepalive interval */ +-int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp) +-{ +- return rtp->rtpkeepalive; +-} +- +-void ast_rtp_set_data(struct ast_rtp *rtp, void *data) +-{ +- rtp->data = data; +-} +- +-void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback) +-{ +- rtp->callback = callback; +-} +- +-void ast_rtp_setnat(struct ast_rtp *rtp, int nat) +-{ +- rtp->nat = nat; +-} +- +-int ast_rtp_getnat(struct ast_rtp *rtp) +-{ +- return ast_test_flag(rtp, FLAG_NAT_ACTIVE); +-} +- +-void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf) +-{ +- ast_set2_flag(rtp, dtmf ? 1 : 0, FLAG_HAS_DTMF); +-} +- +-void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate) +-{ +- ast_set2_flag(rtp, compensate ? 1 : 0, FLAG_DTMF_COMPENSATE); +-} +- +-void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable) +-{ +- ast_set2_flag(rtp, stun_enable ? 1 : 0, FLAG_HAS_STUN); +-} +- +-static void rtp_bridge_lock(struct ast_rtp *rtp) +-{ +-#ifdef P2P_INTENSE +- ast_mutex_lock(&rtp->bridge_lock); +-#endif +- return; +-} +- +-static void rtp_bridge_unlock(struct ast_rtp *rtp) +-{ +-#ifdef P2P_INTENSE +- ast_mutex_unlock(&rtp->bridge_lock); +-#endif +- return; +-} +- +-/*! \brief Calculate normal deviation */ +-static double normdev_compute(double normdev, double sample, unsigned int sample_count) +-{ +- normdev = normdev * sample_count + sample; +- sample_count++; +- +- return normdev / sample_count; +-} +- +-static double stddev_compute(double stddev, double sample, double normdev, double normdev_curent, unsigned int sample_count) +-{ +-/* +- for the formula check http://www.cs.umd.edu/~austinjp/constSD.pdf +- return sqrt( (sample_count*pow(stddev,2) + sample_count*pow((sample-normdev)/(sample_count+1),2) + pow(sample-normdev_curent,2)) / (sample_count+1)); +- we can compute the sigma^2 and that way we would have to do the sqrt only 1 time at the end and would save another pow 2 compute +- optimized formula +-*/ +-#define SQUARE(x) ((x) * (x)) +- +- stddev = sample_count * stddev; +- sample_count++; +- +- return stddev + +- ( sample_count * SQUARE( (sample - normdev) / sample_count ) ) + +- ( SQUARE(sample - normdev_curent) / sample_count ); +- +-#undef SQUARE +-} +- +-static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type) +-{ +- if (((ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && type == AST_FRAME_DTMF_END) || +- (type == AST_FRAME_DTMF_BEGIN)) && ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) { +- ast_debug(1, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr)); +- rtp->resp = 0; +- rtp->dtmfsamples = 0; +- return &ast_null_frame; +- } +- ast_debug(1, "Sending dtmf: %d (%c), at %s\n", rtp->resp, rtp->resp, ast_inet_ntoa(rtp->them.sin_addr)); +- if (rtp->resp == 'X') { +- rtp->f.frametype = AST_FRAME_CONTROL; +- rtp->f.subclass = AST_CONTROL_FLASH; +- } else { +- rtp->f.frametype = type; +- rtp->f.subclass = rtp->resp; +- } +- rtp->f.datalen = 0; +- rtp->f.samples = 0; +- rtp->f.mallocd = 0; +- rtp->f.src = "RTP"; +- return &rtp->f; +- +-} +- +-static inline int rtp_debug_test_addr(struct sockaddr_in *addr) +-{ +- if (rtpdebug == 0) +- return 0; +- if (rtpdebugaddr.sin_addr.s_addr) { +- if (((ntohs(rtpdebugaddr.sin_port) != 0) +- && (rtpdebugaddr.sin_port != addr->sin_port)) +- || (rtpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) +- return 0; +- } +- return 1; +-} +- +-static inline int rtcp_debug_test_addr(struct sockaddr_in *addr) +-{ +- if (rtcpdebug == 0) +- return 0; +- if (rtcpdebugaddr.sin_addr.s_addr) { +- if (((ntohs(rtcpdebugaddr.sin_port) != 0) +- && (rtcpdebugaddr.sin_port != addr->sin_port)) +- || (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) +- return 0; +- } +- return 1; +-} +- +- +-static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *data, int len) +-{ +- unsigned int event; +- char resp = 0; +- struct ast_frame *f = NULL; +- unsigned char seq; +- unsigned int flags; +- unsigned int power; +- +- /* We should have at least 4 bytes in RTP data */ +- if (len < 4) +- return f; +- +- /* The format of Cisco RTP DTMF packet looks like next: +- +0 - sequence number of DTMF RTP packet (begins from 1, +- wrapped to 0) +- +1 - set of flags +- +1 (bit 0) - flaps by different DTMF digits delimited by audio +- or repeated digit without audio??? +- +2 (+4,+6,...) - power level? (rises from 0 to 32 at begin of tone +- then falls to 0 at its end) +- +3 (+5,+7,...) - detected DTMF digit (0..9,*,#,A-D,...) +- Repeated DTMF information (bytes 4/5, 6/7) is history shifted right +- by each new packet and thus provides some redudancy. +- +- Sample of Cisco RTP DTMF packet is (all data in hex): +- 19 07 00 02 12 02 20 02 +- showing end of DTMF digit '2'. +- +- The packets +- 27 07 00 02 0A 02 20 02 +- 28 06 20 02 00 02 0A 02 +- shows begin of new digit '2' with very short pause (20 ms) after +- previous digit '2'. Bit +1.0 flips at begin of new digit. +- +- Cisco RTP DTMF packets comes as replacement of audio RTP packets +- so its uses the same sequencing and timestamping rules as replaced +- audio packets. Repeat interval of DTMF packets is 20 ms and not rely +- on audio framing parameters. Marker bit isn't used within stream of +- DTMFs nor audio stream coming immediately after DTMF stream. Timestamps +- are not sequential at borders between DTMF and audio streams, +- */ +- +- seq = data[0]; +- flags = data[1]; +- power = data[2]; +- event = data[3] & 0x1f; +- +- if (option_debug > 2 || rtpdebug) +- ast_debug(0, "Cisco DTMF Digit: %02x (len=%d, seq=%d, flags=%02x, power=%d, history count=%d)\n", event, len, seq, flags, power, (len - 4) / 2); +- if (event < 10) { +- resp = '0' + event; +- } else if (event < 11) { +- resp = '*'; +- } else if (event < 12) { +- resp = '#'; +- } else if (event < 16) { +- resp = 'A' + (event - 12); +- } else if (event < 17) { +- resp = 'X'; +- } +- if ((!rtp->resp && power) || (rtp->resp && (rtp->resp != resp))) { +- rtp->resp = resp; +- /* Why we should care on DTMF compensation at reception? */ +- if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) { +- f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN); +- rtp->dtmfsamples = 0; +- } +- } else if ((rtp->resp == resp) && !power) { +- f = send_dtmf(rtp, AST_FRAME_DTMF_END); +- f->samples = rtp->dtmfsamples * 8; +- rtp->resp = 0; +- } else if (rtp->resp == resp) +- rtp->dtmfsamples += 20 * 8; +- rtp->dtmfcount = dtmftimeout; +- return f; +-} +- +-/*! +- * \brief Process RTP DTMF and events according to RFC 2833. +- * +- * RFC 2833 is "RTP Payload for DTMF Digits, Telephony Tones and Telephony Signals". +- * +- * \param rtp +- * \param data +- * \param len +- * \param seqno +- * \param timestamp +- * \returns +- */ +-static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp) +-{ +- unsigned int event; +- unsigned int event_end; +- unsigned int samples; +- char resp = 0; +- struct ast_frame *f = NULL; +- +- /* Figure out event, event end, and samples */ +- event = ntohl(*((unsigned int *)(data))); +- event >>= 24; +- event_end = ntohl(*((unsigned int *)(data))); +- event_end <<= 8; +- event_end >>= 24; +- samples = ntohl(*((unsigned int *)(data))); +- samples &= 0xFFFF; +- +- /* Print out debug if turned on */ +- if (rtpdebug || option_debug > 2) +- ast_debug(0, "- RTP 2833 Event: %08x (len = %d)\n", event, len); +- +- /* Figure out what digit was pressed */ +- if (event < 10) { +- resp = '0' + event; +- } else if (event < 11) { +- resp = '*'; +- } else if (event < 12) { +- resp = '#'; +- } else if (event < 16) { +- resp = 'A' + (event - 12); +- } else if (event < 17) { /* Event 16: Hook flash */ +- resp = 'X'; +- } else { +- /* Not a supported event */ +- ast_log(LOG_DEBUG, "Ignoring RTP 2833 Event: %08x. Not a DTMF Digit.\n", event); +- return &ast_null_frame; +- } +- +- if (ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) { +- if ((rtp->lastevent != timestamp) || (rtp->resp && rtp->resp != resp)) { +- rtp->resp = resp; +- rtp->dtmfcount = 0; +- f = send_dtmf(rtp, AST_FRAME_DTMF_END); +- f->len = 0; +- rtp->lastevent = timestamp; +- } +- } else { +- if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) { +- rtp->resp = resp; +- f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN); +- rtp->dtmfcount = dtmftimeout; +- } else if ((event_end & 0x80) && (rtp->lastevent != seqno) && rtp->resp) { +- f = send_dtmf(rtp, AST_FRAME_DTMF_END); +- f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */ +- rtp->resp = 0; +- rtp->dtmfcount = 0; +- rtp->lastevent = seqno; +- } +- } +- +- rtp->dtmfsamples = samples; +- +- return f; +-} +- +-/*! +- * \brief Process Comfort Noise RTP. +- * +- * This is incomplete at the moment. +- * +-*/ +-static struct ast_frame *process_rfc3389(struct ast_rtp *rtp, unsigned char *data, int len) +-{ +- struct ast_frame *f = NULL; +- /* Convert comfort noise into audio with various codecs. Unfortunately this doesn't +- totally help us out becuase we don't have an engine to keep it going and we are not +- guaranteed to have it every 20ms or anything */ +- if (rtpdebug) +- ast_debug(0, "- RTP 3389 Comfort noise event: Level %d (len = %d)\n", rtp->lastrxformat, len); +- +- if (!(ast_test_flag(rtp, FLAG_3389_WARNING))) { +- ast_log(LOG_NOTICE, "Comfort noise support incomplete in Asterisk (RFC 3389). Please turn off on client if possible. Client IP: %s\n", +- ast_inet_ntoa(rtp->them.sin_addr)); +- ast_set_flag(rtp, FLAG_3389_WARNING); +- } +- +- /* Must have at least one byte */ +- if (!len) +- return NULL; +- if (len < 24) { +- rtp->f.data.ptr = rtp->rawdata + AST_FRIENDLY_OFFSET; +- rtp->f.datalen = len - 1; +- rtp->f.offset = AST_FRIENDLY_OFFSET; +- memcpy(rtp->f.data.ptr, data + 1, len - 1); +- } else { +- rtp->f.data.ptr = NULL; +- rtp->f.offset = 0; +- rtp->f.datalen = 0; +- } +- rtp->f.frametype = AST_FRAME_CNG; +- rtp->f.subclass = data[0] & 0x7f; +- rtp->f.datalen = len - 1; +- rtp->f.samples = 0; +- rtp->f.delivery.tv_usec = rtp->f.delivery.tv_sec = 0; +- f = &rtp->f; +- return f; +-} +- +-static int rtpread(int *id, int fd, short events, void *cbdata) +-{ +- struct ast_rtp *rtp = cbdata; +- struct ast_frame *f; +- f = ast_rtp_read(rtp); +- if (f) { +- if (rtp->callback) +- rtp->callback(rtp, f, rtp->data); +- } +- return 1; +-} +- +-struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp) +-{ +- socklen_t len; +- int position, i, packetwords; +- int res; +- struct sockaddr_in sock_in; +- unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET]; +- unsigned int *rtcpheader; +- int pt; +- struct timeval now; +- unsigned int length; +- int rc; +- double rttsec; +- uint64_t rtt = 0; +- unsigned int dlsr; +- unsigned int lsr; +- unsigned int msw; +- unsigned int lsw; +- unsigned int comp; +- struct ast_frame *f = &ast_null_frame; +- +- double reported_jitter; +- double reported_normdev_jitter_current; +- double normdevrtt_current; +- double reported_lost; +- double reported_normdev_lost_current; +- +- if (!rtp || !rtp->rtcp) +- return &ast_null_frame; +- +- len = sizeof(sock_in); +- +- res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET, +- 0, (struct sockaddr *)&sock_in, &len); +- rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET); +- +- if (res < 0) { +- ast_assert(errno != EBADF); +- if (errno != EAGAIN) { +- ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", strerror(errno)); +- return NULL; +- } +- return &ast_null_frame; +- } +- +- packetwords = res / 4; +- +- if (rtp->nat) { +- /* Send to whoever sent to us */ +- if ((rtp->rtcp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) || +- (rtp->rtcp->them.sin_port != sock_in.sin_port)) { +- memcpy(&rtp->rtcp->them, &sock_in, sizeof(rtp->rtcp->them)); +- if (option_debug || rtpdebug) +- ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); +- } +- } +- +- ast_debug(1, "Got RTCP report of %d bytes\n", res); +- +- /* Process a compound packet */ +- position = 0; +- while (position < packetwords) { +- i = position; +- length = ntohl(rtcpheader[i]); +- pt = (length & 0xff0000) >> 16; +- rc = (length & 0x1f000000) >> 24; +- length &= 0xffff; +- +- if ((i + length) > packetwords) { +- if (option_debug || rtpdebug) +- ast_log(LOG_DEBUG, "RTCP Read too short\n"); +- return &ast_null_frame; +- } +- +- if (rtcp_debug_test_addr(&sock_in)) { +- ast_verbose("\n\nGot RTCP from %s:%d\n", ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port)); +- ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown"); +- ast_verbose("Reception reports: %d\n", rc); +- ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]); +- } +- +- i += 2; /* Advance past header and ssrc */ +- +- switch (pt) { +- case RTCP_PT_SR: +- gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */ +- rtp->rtcp->spc = ntohl(rtcpheader[i+3]); +- rtp->rtcp->soc = ntohl(rtcpheader[i + 4]); +- rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/ +- +- if (rtcp_debug_test_addr(&sock_in)) { +- ast_verbose("NTP timestamp: %lu.%010lu\n", (unsigned long) ntohl(rtcpheader[i]), (unsigned long) ntohl(rtcpheader[i + 1]) * 4096); +- ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2])); +- ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4])); +- } +- i += 5; +- if (rc < 1) +- break; +- /* Intentional fall through */ +- case RTCP_PT_RR: +- /* Don't handle multiple reception reports (rc > 1) yet */ +- /* Calculate RTT per RFC */ +- gettimeofday(&now, NULL); +- timeval2ntp(now, &msw, &lsw); +- if (ntohl(rtcpheader[i + 4]) && ntohl(rtcpheader[i + 5])) { /* We must have the LSR && DLSR */ +- comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16); +- lsr = ntohl(rtcpheader[i + 4]); +- dlsr = ntohl(rtcpheader[i + 5]); +- rtt = comp - lsr - dlsr; +- +- /* Convert end to end delay to usec (keeping the calculation in 64bit space) +- sess->ee_delay = (eedelay * 1000) / 65536; */ +- if (rtt < 4294) { +- rtt = (rtt * 1000000) >> 16; +- } else { +- rtt = (rtt * 1000) >> 16; +- rtt *= 1000; +- } +- rtt = rtt / 1000.; +- rttsec = rtt / 1000.; +- rtp->rtcp->rtt = rttsec; +- +- if (comp - dlsr >= lsr) { +- rtp->rtcp->accumulated_transit += rttsec; +- +- if (rtp->rtcp->rtt_count == 0) +- rtp->rtcp->minrtt = rttsec; +- +- if (rtp->rtcp->maxrtt<rttsec) +- rtp->rtcp->maxrtt = rttsec; +- +- if (rtp->rtcp->minrtt>rttsec) +- rtp->rtcp->minrtt = rttsec; +- +- normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt, rttsec, rtp->rtcp->rtt_count); +- +- rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt, rttsec, rtp->rtcp->normdevrtt, normdevrtt_current, rtp->rtcp->rtt_count); +- +- rtp->rtcp->normdevrtt = normdevrtt_current; +- +- rtp->rtcp->rtt_count++; +- } else if (rtcp_debug_test_addr(&sock_in)) { +- ast_verbose("Internal RTCP NTP clock skew detected: " +- "lsr=%u, now=%u, dlsr=%u (%d:%03dms), " +- "diff=%d\n", +- lsr, comp, dlsr, dlsr / 65536, +- (dlsr % 65536) * 1000 / 65536, +- dlsr - (comp - lsr)); +- } +- } +- +- rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]); +- reported_jitter = (double) rtp->rtcp->reported_jitter; +- +- if (rtp->rtcp->reported_jitter_count == 0) +- rtp->rtcp->reported_minjitter = reported_jitter; +- +- if (reported_jitter < rtp->rtcp->reported_minjitter) +- rtp->rtcp->reported_minjitter = reported_jitter; +- +- if (reported_jitter > rtp->rtcp->reported_maxjitter) +- rtp->rtcp->reported_maxjitter = reported_jitter; +- +- reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count); +- +- rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count); +- +- rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current; +- +- rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff; +- +- reported_lost = (double) rtp->rtcp->reported_lost; +- +- /* using same counter as for jitter */ +- if (rtp->rtcp->reported_jitter_count == 0) +- rtp->rtcp->reported_minlost = reported_lost; +- +- if (reported_lost < rtp->rtcp->reported_minlost) +- rtp->rtcp->reported_minlost = reported_lost; +- +- if (reported_lost > rtp->rtcp->reported_maxlost) +- rtp->rtcp->reported_maxlost = reported_lost; +- +- reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count); +- +- rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count); +- +- rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current; +- +- rtp->rtcp->reported_jitter_count++; +- +- if (rtcp_debug_test_addr(&sock_in)) { +- ast_verbose(" Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24)); +- ast_verbose(" Packets lost so far: %d\n", rtp->rtcp->reported_lost); +- ast_verbose(" Highest sequence number: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff)); +- ast_verbose(" Sequence number cycles: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16); +- ast_verbose(" Interarrival jitter: %u\n", rtp->rtcp->reported_jitter); +- ast_verbose(" Last SR(our NTP): %lu.%010lu\n",(unsigned long) ntohl(rtcpheader[i + 4]) >> 16,((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096); +- ast_verbose(" DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i + 5])/65536.0); +- if (rtt) +- ast_verbose(" RTT: %lu(sec)\n", (unsigned long) rtt); +- } +- +- if (rtt) { +- manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From: %s:%d\r\n" +- "PT: %d(%s)\r\n" +- "ReceptionReports: %d\r\n" +- "SenderSSRC: %u\r\n" +- "FractionLost: %ld\r\n" +- "PacketsLost: %d\r\n" +- "HighestSequence: %ld\r\n" +- "SequenceNumberCycles: %ld\r\n" +- "IAJitter: %u\r\n" +- "LastSR: %lu.%010lu\r\n" +- "DLSR: %4.4f(sec)\r\n" +- "RTT: %llu(sec)\r\n", +- ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), +- pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", +- rc, +- rtcpheader[i + 1], +- (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), +- rtp->rtcp->reported_lost, +- (long) (ntohl(rtcpheader[i + 2]) & 0xffff), +- (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, +- rtp->rtcp->reported_jitter, +- (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, +- ntohl(rtcpheader[i + 5])/65536.0, +- (unsigned long long)rtt); +- } else { +- manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From: %s:%d\r\n" +- "PT: %d(%s)\r\n" +- "ReceptionReports: %d\r\n" +- "SenderSSRC: %u\r\n" +- "FractionLost: %ld\r\n" +- "PacketsLost: %d\r\n" +- "HighestSequence: %ld\r\n" +- "SequenceNumberCycles: %ld\r\n" +- "IAJitter: %u\r\n" +- "LastSR: %lu.%010lu\r\n" +- "DLSR: %4.4f(sec)\r\n", +- ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), +- pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", +- rc, +- rtcpheader[i + 1], +- (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), +- rtp->rtcp->reported_lost, +- (long) (ntohl(rtcpheader[i + 2]) & 0xffff), +- (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, +- rtp->rtcp->reported_jitter, +- (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, +- ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, +- ntohl(rtcpheader[i + 5])/65536.0); +- } +- break; +- case RTCP_PT_FUR: +- if (rtcp_debug_test_addr(&sock_in)) +- ast_verbose("Received an RTCP Fast Update Request\n"); +- rtp->f.frametype = AST_FRAME_CONTROL; +- rtp->f.subclass = AST_CONTROL_VIDUPDATE; +- rtp->f.datalen = 0; +- rtp->f.samples = 0; +- rtp->f.mallocd = 0; +- rtp->f.src = "RTP"; +- f = &rtp->f; +- break; +- case RTCP_PT_SDES: +- if (rtcp_debug_test_addr(&sock_in)) +- ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); +- break; +- case RTCP_PT_BYE: +- if (rtcp_debug_test_addr(&sock_in)) +- ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); +- break; +- default: +- ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); +- break; +- } +- position += (length + 1); +- } +- rtp->rtcp->rtcp_info = 1; +- return f; +-} +- +-static void calc_rxstamp(struct timeval *when, struct ast_rtp *rtp, unsigned int timestamp, int mark) +-{ +- struct timeval now; +- double transit; +- double current_time; +- double d; +- double dtv; +- double prog; +- +- double normdev_rxjitter_current; +- if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) { +- gettimeofday(&rtp->rxcore, NULL); +- rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000; +- /* map timestamp to a real time */ +- rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */ +- rtp->rxcore.tv_sec -= timestamp / 8000; +- rtp->rxcore.tv_usec -= (timestamp % 8000) * 125; +- /* Round to 0.1ms for nice, pretty timestamps */ +- rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100; +- if (rtp->rxcore.tv_usec < 0) { +- /* Adjust appropriately if necessary */ +- rtp->rxcore.tv_usec += 1000000; +- rtp->rxcore.tv_sec -= 1; +- } +- } +- +- gettimeofday(&now,NULL); +- /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */ +- when->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000; +- when->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125; +- if (when->tv_usec >= 1000000) { +- when->tv_usec -= 1000000; +- when->tv_sec += 1; +- } +- prog = (double)((timestamp-rtp->seedrxts)/8000.); +- dtv = (double)rtp->drxcore + (double)(prog); +- current_time = (double)now.tv_sec + (double)now.tv_usec/1000000; +- transit = current_time - dtv; +- d = transit - rtp->rxtransit; +- rtp->rxtransit = transit; +- if (d<0) +- d=-d; +- rtp->rxjitter += (1./16.) * (d - rtp->rxjitter); +- if (rtp->rtcp && rtp->rxjitter > rtp->rtcp->maxrxjitter) +- rtp->rtcp->maxrxjitter = rtp->rxjitter; +- if (rtp->rtcp->rxjitter_count == 1) +- rtp->rtcp->minrxjitter = rtp->rxjitter; +- if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter) +- rtp->rtcp->minrxjitter = rtp->rxjitter; +- +- normdev_rxjitter_current = normdev_compute(rtp->rtcp->normdev_rxjitter,rtp->rxjitter,rtp->rtcp->rxjitter_count); +- rtp->rtcp->stdev_rxjitter = stddev_compute(rtp->rtcp->stdev_rxjitter,rtp->rxjitter,rtp->rtcp->normdev_rxjitter,normdev_rxjitter_current,rtp->rtcp->rxjitter_count); +- +- rtp->rtcp->normdev_rxjitter = normdev_rxjitter_current; +- rtp->rtcp->rxjitter_count++; +-} +- +-/*! \brief Perform a Packet2Packet RTP write */ +-static int bridge_p2p_rtp_write(struct ast_rtp *rtp, struct ast_rtp *bridged, unsigned int *rtpheader, int len, int hdrlen) +-{ +- int res = 0, payload = 0, bridged_payload = 0, mark; +- struct rtpPayloadType rtpPT; +- int reconstruct = ntohl(rtpheader[0]); +- +- /* Get fields from packet */ +- payload = (reconstruct & 0x7f0000) >> 16; +- mark = (((reconstruct & 0x800000) >> 23) != 0); +- +- /* Check what the payload value should be */ +- rtpPT = ast_rtp_lookup_pt(rtp, payload); +- +- /* If the payload is DTMF, and we are listening for DTMF - then feed it into the core */ +- if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) && !rtpPT.isAstFormat && rtpPT.code == AST_RTP_DTMF) +- return -1; +- +- /* Otherwise adjust bridged payload to match */ +- bridged_payload = ast_rtp_lookup_code(bridged, rtpPT.isAstFormat, rtpPT.code); +- +- /* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */ +- if (!bridged->current_RTP_PT[bridged_payload].code) +- return -1; +- +- +- /* If the mark bit has not been sent yet... do it now */ +- if (!ast_test_flag(rtp, FLAG_P2P_SENT_MARK)) { +- mark = 1; +- ast_set_flag(rtp, FLAG_P2P_SENT_MARK); +- } +- +- /* Reconstruct part of the packet */ +- reconstruct &= 0xFF80FFFF; +- reconstruct |= (bridged_payload << 16); +- reconstruct |= (mark << 23); +- rtpheader[0] = htonl(reconstruct); +- +- /* Send the packet back out */ +- res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them)); +- if (res < 0) { +- if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { +- ast_debug(1, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno)); +- } else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) { +- if (option_debug || rtpdebug) +- ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port)); +- ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN); +- } +- return 0; +- } else if (rtp_debug_test_addr(&bridged->them)) +- ast_verbose("Sent RTP P2P packet to %s:%u (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen); +- +- return 0; +-} +- +-struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) +-{ +- int res; +- struct sockaddr_in sock_in; +- socklen_t len; +- unsigned int seqno; +- int version; +- int payloadtype; +- int hdrlen = 12; +- int padding; +- int mark; +- int ext; +- int cc; +- unsigned int ssrc; +- unsigned int timestamp; +- unsigned int *rtpheader; +- struct rtpPayloadType rtpPT; +- struct ast_rtp *bridged = NULL; +- int prev_seqno; +- +- /* If time is up, kill it */ +- if (rtp->sending_digit) +- ast_rtp_senddigit_continuation(rtp); +- +- len = sizeof(sock_in); +- +- /* Cache where the header will go */ +- res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, +- 0, (struct sockaddr *)&sock_in, &len); +- +- /* If strict RTP protection is enabled see if we need to learn this address or if the packet should be dropped */ +- if (rtp->strict_rtp_state == STRICT_RTP_LEARN) { +- /* Copy over address that this packet was received on */ +- memcpy(&rtp->strict_rtp_address, &sock_in, sizeof(rtp->strict_rtp_address)); +- /* Now move over to actually protecting the RTP port */ +- rtp->strict_rtp_state = STRICT_RTP_CLOSED; +- ast_debug(1, "Learned remote address is %s:%d for strict RTP purposes, now protecting the port.\n", ast_inet_ntoa(rtp->strict_rtp_address.sin_addr), ntohs(rtp->strict_rtp_address.sin_port)); +- } else if (rtp->strict_rtp_state == STRICT_RTP_CLOSED) { +- /* If the address we previously learned doesn't match the address this packet came in on simply drop it */ +- if ((rtp->strict_rtp_address.sin_addr.s_addr != sock_in.sin_addr.s_addr) || (rtp->strict_rtp_address.sin_port != sock_in.sin_port)) { +- ast_debug(1, "Received RTP packet from %s:%d, dropping due to strict RTP protection. Expected it to be from %s:%d\n", ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), ast_inet_ntoa(rtp->strict_rtp_address.sin_addr), ntohs(rtp->strict_rtp_address.sin_port)); +- return &ast_null_frame; +- } +- } +- +- rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); +- if (res < 0) { +- ast_assert(errno != EBADF); +- if (errno != EAGAIN) { +- ast_log(LOG_WARNING, "RTP Read error: %s. Hanging up.\n", strerror(errno)); +- return NULL; +- } +- return &ast_null_frame; +- } +- +- if (res < hdrlen) { +- ast_log(LOG_WARNING, "RTP Read too short\n"); +- return &ast_null_frame; +- } +- +- /* Get fields */ +- seqno = ntohl(rtpheader[0]); +- +- /* Check RTP version */ +- version = (seqno & 0xC0000000) >> 30; +- if (!version) { +- /* If the two high bits are 0, this might be a +- * STUN message, so process it. stun_handle_packet() +- * answers to requests, and it returns STUN_ACCEPT +- * if the request is valid. +- */ +- if ((stun_handle_packet(rtp->s, &sock_in, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == STUN_ACCEPT) && +- (!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) { +- memcpy(&rtp->them, &sock_in, sizeof(rtp->them)); +- } +- return &ast_null_frame; +- } +- +-#if 0 /* Allow to receive RTP stream with closed transmission path */ +- /* If we don't have the other side's address, then ignore this */ +- if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) +- return &ast_null_frame; +-#endif +- +- /* Send to whoever send to us if NAT is turned on */ +- if (rtp->nat) { +- if ((rtp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) || +- (rtp->them.sin_port != sock_in.sin_port)) { +- rtp->them = sock_in; +- if (rtp->rtcp) { +- int h = 0; +- memcpy(&rtp->rtcp->them, &sock_in, sizeof(rtp->rtcp->them)); +- h = ntohs(rtp->them.sin_port); +- rtp->rtcp->them.sin_port = htons(h + 1); +- } +- rtp->rxseqno = 0; +- ast_set_flag(rtp, FLAG_NAT_ACTIVE); +- if (option_debug || rtpdebug) +- ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); +- } +- } +- +- /* If we are bridged to another RTP stream, send direct */ +- if ((bridged = ast_rtp_get_bridged(rtp)) && !bridge_p2p_rtp_write(rtp, bridged, rtpheader, res, hdrlen)) +- return &ast_null_frame; +- +- if (version != 2) +- return &ast_null_frame; +- +- payloadtype = (seqno & 0x7f0000) >> 16; +- padding = seqno & (1 << 29); +- mark = seqno & (1 << 23); +- ext = seqno & (1 << 28); +- cc = (seqno & 0xF000000) >> 24; +- seqno &= 0xffff; +- timestamp = ntohl(rtpheader[1]); +- ssrc = ntohl(rtpheader[2]); +- +- if (!mark && rtp->rxssrc && rtp->rxssrc != ssrc) { +- if (option_debug || rtpdebug) +- ast_debug(0, "Forcing Marker bit, because SSRC has changed\n"); +- mark = 1; +- } +- +- rtp->rxssrc = ssrc; +- +- if (padding) { +- /* Remove padding bytes */ +- res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1]; +- } +- +- if (cc) { +- /* CSRC fields present */ +- hdrlen += cc*4; +- } +- +- if (ext) { +- /* RTP Extension present */ +- hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2; +- hdrlen += 4; +- if (option_debug) { +- int profile; +- profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16; +- if (profile == 0x505a) +- ast_debug(1, "Found Zfone extension in RTP stream - zrtp - not supported.\n"); +- else +- ast_debug(1, "Found unknown RTP Extensions %x\n", profile); +- } +- } +- +- if (res < hdrlen) { +- ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d)\n", res, hdrlen); +- return &ast_null_frame; +- } +- +- rtp->rxcount++; /* Only count reasonably valid packets, this'll make the rtcp stats more accurate */ +- +- if (rtp->rxcount==1) { +- /* This is the first RTP packet successfully received from source */ +- rtp->seedrxseqno = seqno; +- } +- +- /* Do not schedule RR if RTCP isn't run */ +- if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) { +- /* Schedule transmission of Receiver Report */ +- rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); +- } +- if ((int)rtp->lastrxseqno - (int)seqno > 100) /* if so it would indicate that the sender cycled; allow for misordering */ +- rtp->cycles += RTP_SEQ_MOD; +- +- prev_seqno = rtp->lastrxseqno; +- +- rtp->lastrxseqno = seqno; +- +- if (!rtp->themssrc) +- rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */ +- +- if (rtp_debug_test_addr(&sock_in)) +- ast_verbose("Got RTP packet from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", +- ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), payloadtype, seqno, timestamp,res - hdrlen); +- +- rtpPT = ast_rtp_lookup_pt(rtp, payloadtype); +- if (!rtpPT.isAstFormat) { +- struct ast_frame *f = NULL; +- +- /* This is special in-band data that's not one of our codecs */ +- if (rtpPT.code == AST_RTP_DTMF) { +- /* It's special -- rfc2833 process it */ +- if (rtp_debug_test_addr(&sock_in)) { +- unsigned char *data; +- unsigned int event; +- unsigned int event_end; +- unsigned int duration; +- data = rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen; +- event = ntohl(*((unsigned int *)(data))); +- event >>= 24; +- event_end = ntohl(*((unsigned int *)(data))); +- event_end <<= 8; +- event_end >>= 24; +- duration = ntohl(*((unsigned int *)(data))); +- duration &= 0xFFFF; +- ast_verbose("Got RTP RFC2833 from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), payloadtype, seqno, timestamp, res - hdrlen, (mark?1:0), event, ((event_end & 0x80)?1:0), duration); +- } +- f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp); +- } else if (rtpPT.code == AST_RTP_CISCO_DTMF) { +- /* It's really special -- process it the Cisco way */ +- if (rtp->lastevent <= seqno || (rtp->lastevent >= 65530 && seqno <= 6)) { +- f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); +- rtp->lastevent = seqno; +- } +- } else if (rtpPT.code == AST_RTP_CN) { +- /* Comfort Noise */ +- f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); +- } else { +- ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, ast_inet_ntoa(rtp->them.sin_addr)); +- } +- return f ? f : &ast_null_frame; +- } +- rtp->lastrxformat = rtp->f.subclass = rtpPT.code; +- rtp->f.frametype = (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) ? AST_FRAME_VOICE : (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) ? AST_FRAME_VIDEO : AST_FRAME_TEXT; +- +- rtp->rxseqno = seqno; +- +- if (rtp->dtmfcount) { +- rtp->dtmfcount -= (timestamp - rtp->lastrxts); +- +- if (rtp->dtmfcount < 0) { +- rtp->dtmfcount = 0; +- } +- +- if (rtp->resp && !rtp->dtmfcount) { +- struct ast_frame *f; +- f = send_dtmf(rtp, AST_FRAME_DTMF_END); +- rtp->resp = 0; +- return f; +- } +- } +- +- /* Record received timestamp as last received now */ +- rtp->lastrxts = timestamp; +- +- rtp->f.mallocd = 0; +- rtp->f.datalen = res - hdrlen; +- rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; +- rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; +- rtp->f.seqno = seqno; +- +- if (rtp->f.subclass == AST_FORMAT_T140 && (int)seqno - (prev_seqno+1) > 0 && (int)seqno - (prev_seqno+1) < 10) { +- unsigned char *data = rtp->f.data.ptr; +- +- memmove(rtp->f.data.ptr+3, rtp->f.data.ptr, rtp->f.datalen); +- rtp->f.datalen +=3; +- *data++ = 0xEF; +- *data++ = 0xBF; +- *data = 0xBD; +- } +- +- if (rtp->f.subclass == AST_FORMAT_T140RED) { +- unsigned char *data = rtp->f.data.ptr; +- unsigned char *header_end; +- int num_generations; +- int header_length; +- int length; +- int diff =(int)seqno - (prev_seqno+1); /* if diff = 0, no drop*/ +- int x; +- +- rtp->f.subclass = AST_FORMAT_T140; +- header_end = memchr(data, ((*data) & 0x7f), rtp->f.datalen); +- header_end++; +- +- header_length = header_end - data; +- num_generations = header_length / 4; +- length = header_length; +- +- if (!diff) { +- for (x = 0; x < num_generations; x++) +- length += data[x * 4 + 3]; +- +- if (!(rtp->f.datalen - length)) +- return &ast_null_frame; +- +- rtp->f.data.ptr += length; +- rtp->f.datalen -= length; +- } else if (diff > num_generations && diff < 10) { +- length -= 3; +- rtp->f.data.ptr += length; +- rtp->f.datalen -= length; +- +- data = rtp->f.data.ptr; +- *data++ = 0xEF; +- *data++ = 0xBF; +- *data = 0xBD; +- } else { +- for ( x = 0; x < num_generations - diff; x++) +- length += data[x * 4 + 3]; +- +- rtp->f.data.ptr += length; +- rtp->f.datalen -= length; +- } +- } +- +- if (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) { +- rtp->f.samples = ast_codec_get_samples(&rtp->f); +- if (rtp->f.subclass == AST_FORMAT_SLINEAR) +- ast_frame_byteswap_be(&rtp->f); +- calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); +- /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */ +- ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO); +- rtp->f.ts = timestamp / 8; +- rtp->f.len = rtp->f.samples / ((ast_format_rate(rtp->f.subclass) / 1000)); +- } else if (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) { +- /* Video -- samples is # of samples vs. 90000 */ +- if (!rtp->lastividtimestamp) +- rtp->lastividtimestamp = timestamp; +- rtp->f.samples = timestamp - rtp->lastividtimestamp; +- rtp->lastividtimestamp = timestamp; +- rtp->f.delivery.tv_sec = 0; +- rtp->f.delivery.tv_usec = 0; +- /* Pass the RTP marker bit as bit 0 in the subclass field. +- * This is ok because subclass is actually a bitmask, and +- * the low bits represent audio formats, that are not +- * involved here since we deal with video. +- */ +- if (mark) +- rtp->f.subclass |= 0x1; +- } else { +- /* TEXT -- samples is # of samples vs. 1000 */ +- if (!rtp->lastitexttimestamp) +- rtp->lastitexttimestamp = timestamp; +- rtp->f.samples = timestamp - rtp->lastitexttimestamp; +- rtp->lastitexttimestamp = timestamp; +- rtp->f.delivery.tv_sec = 0; +- rtp->f.delivery.tv_usec = 0; +- } +- rtp->f.src = "RTP"; +- return &rtp->f; +-} +- +-/* The following array defines the MIME Media type (and subtype) for each +- of our codecs, or RTP-specific data type. */ +-static const struct mimeType { +- struct rtpPayloadType payloadType; +- char *type; +- char *subtype; +- unsigned int sample_rate; +-} mimeTypes[] = { +- {{1, AST_FORMAT_G723_1}, "audio", "G723", 8000}, +- {{1, AST_FORMAT_GSM}, "audio", "GSM", 8000}, +- {{1, AST_FORMAT_ULAW}, "audio", "PCMU", 8000}, +- {{1, AST_FORMAT_ULAW}, "audio", "G711U", 8000}, +- {{1, AST_FORMAT_ALAW}, "audio", "PCMA", 8000}, +- {{1, AST_FORMAT_ALAW}, "audio", "G711A", 8000}, +- {{1, AST_FORMAT_G726}, "audio", "G726-32", 8000}, +- {{1, AST_FORMAT_ADPCM}, "audio", "DVI4", 8000}, +- {{1, AST_FORMAT_SLINEAR}, "audio", "L16", 8000}, +- {{1, AST_FORMAT_LPC10}, "audio", "LPC", 8000}, +- {{1, AST_FORMAT_G729A}, "audio", "G729", 8000}, +- {{1, AST_FORMAT_G729A}, "audio", "G729A", 8000}, +- {{1, AST_FORMAT_G729A}, "audio", "G.729", 8000}, +- {{1, AST_FORMAT_SPEEX}, "audio", "speex", 8000}, +- {{1, AST_FORMAT_ILBC}, "audio", "iLBC", 8000}, +- /* this is the sample rate listed in the RTP profile for the G.722 +- codec, *NOT* the actual sample rate of the media stream +- */ +- {{1, AST_FORMAT_G722}, "audio", "G722", 8000}, +- {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32", 8000}, +- {{0, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, +- {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, +- {{0, AST_RTP_CN}, "audio", "CN", 8000}, +- {{1, AST_FORMAT_JPEG}, "video", "JPEG", 90000}, +- {{1, AST_FORMAT_PNG}, "video", "PNG", 90000}, +- {{1, AST_FORMAT_H261}, "video", "H261", 90000}, +- {{1, AST_FORMAT_H263}, "video", "H263", 90000}, +- {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998", 90000}, +- {{1, AST_FORMAT_H264}, "video", "H264", 90000}, +- {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES", 90000}, +- {{1, AST_FORMAT_T140RED}, "text", "RED", 1000}, +- {{1, AST_FORMAT_T140}, "text", "T140", 1000}, +- {{1, AST_FORMAT_SIREN7}, "audio", "G7221", 16000}, +- {{1, AST_FORMAT_SIREN14}, "audio", "G7221", 32000}, +-}; +- +-/*! +- * \brief Mapping between Asterisk codecs and rtp payload types +- * +- * Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s: +- * also, our own choices for dynamic payload types. This is our master +- * table for transmission +- * +- * See http://www.iana.org/assignments/rtp-parameters for a list of +- * assigned values +- */ +-static const struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = { +- [0] = {1, AST_FORMAT_ULAW}, +-#ifdef USE_DEPRECATED_G726 +- [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */ +-#endif +- [3] = {1, AST_FORMAT_GSM}, +- [4] = {1, AST_FORMAT_G723_1}, +- [5] = {1, AST_FORMAT_ADPCM}, /* 8 kHz */ +- [6] = {1, AST_FORMAT_ADPCM}, /* 16 kHz */ +- [7] = {1, AST_FORMAT_LPC10}, +- [8] = {1, AST_FORMAT_ALAW}, +- [9] = {1, AST_FORMAT_G722}, +- [10] = {1, AST_FORMAT_SLINEAR}, /* 2 channels */ +- [11] = {1, AST_FORMAT_SLINEAR}, /* 1 channel */ +- [13] = {0, AST_RTP_CN}, +- [16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */ +- [17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */ +- [18] = {1, AST_FORMAT_G729A}, +- [19] = {0, AST_RTP_CN}, /* Also used for CN */ +- [26] = {1, AST_FORMAT_JPEG}, +- [31] = {1, AST_FORMAT_H261}, +- [34] = {1, AST_FORMAT_H263}, +- [97] = {1, AST_FORMAT_ILBC}, +- [98] = {1, AST_FORMAT_H263_PLUS}, +- [99] = {1, AST_FORMAT_H264}, +- [101] = {0, AST_RTP_DTMF}, +- [102] = {1, AST_FORMAT_SIREN7}, +- [103] = {1, AST_FORMAT_H263_PLUS}, +- [104] = {1, AST_FORMAT_MP4_VIDEO}, +- [105] = {1, AST_FORMAT_T140RED}, /* Real time text chat (with redundancy encoding) */ +- [106] = {1, AST_FORMAT_T140}, /* Real time text chat */ +- [110] = {1, AST_FORMAT_SPEEX}, +- [111] = {1, AST_FORMAT_G726}, +- [112] = {1, AST_FORMAT_G726_AAL2}, +- [115] = {1, AST_FORMAT_SIREN14}, +- [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ +-}; +- +-void ast_rtp_pt_clear(struct ast_rtp* rtp) +-{ +- int i; +- +- if (!rtp) +- return; +- +- rtp_bridge_lock(rtp); +- +- for (i = 0; i < MAX_RTP_PT; ++i) { +- rtp->current_RTP_PT[i].isAstFormat = 0; +- rtp->current_RTP_PT[i].code = 0; +- } +- +- rtp->rtp_lookup_code_cache_isAstFormat = 0; +- rtp->rtp_lookup_code_cache_code = 0; +- rtp->rtp_lookup_code_cache_result = 0; +- +- rtp_bridge_unlock(rtp); +-} +- +-void ast_rtp_pt_default(struct ast_rtp* rtp) +-{ +- int i; +- +- rtp_bridge_lock(rtp); +- +- /* Initialize to default payload types */ +- for (i = 0; i < MAX_RTP_PT; ++i) { +- rtp->current_RTP_PT[i].isAstFormat = static_RTP_PT[i].isAstFormat; +- rtp->current_RTP_PT[i].code = static_RTP_PT[i].code; +- } +- +- rtp->rtp_lookup_code_cache_isAstFormat = 0; +- rtp->rtp_lookup_code_cache_code = 0; +- rtp->rtp_lookup_code_cache_result = 0; +- +- rtp_bridge_unlock(rtp); +-} +- +-void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src) +-{ +- unsigned int i; +- +- rtp_bridge_lock(dest); +- rtp_bridge_lock(src); +- +- for (i = 0; i < MAX_RTP_PT; ++i) { +- dest->current_RTP_PT[i].isAstFormat = +- src->current_RTP_PT[i].isAstFormat; +- dest->current_RTP_PT[i].code = +- src->current_RTP_PT[i].code; +- } +- dest->rtp_lookup_code_cache_isAstFormat = 0; +- dest->rtp_lookup_code_cache_code = 0; +- dest->rtp_lookup_code_cache_result = 0; +- +- rtp_bridge_unlock(src); +- rtp_bridge_unlock(dest); +-} +- +-/*! \brief Get channel driver interface structure */ +-static struct ast_rtp_protocol *get_proto(struct ast_channel *chan) +-{ +- struct ast_rtp_protocol *cur = NULL; +- +- AST_RWLIST_RDLOCK(&protos); +- AST_RWLIST_TRAVERSE(&protos, cur, list) { +- if (cur->type == chan->tech->type) +- break; +- } +- AST_RWLIST_UNLOCK(&protos); +- +- return cur; +-} +- +-int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1) +-{ +- struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */ +- struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */ +- struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */ +- struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL; +- enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED; +- enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED; +- int srccodec, destcodec, nat_active = 0; +- +- /* Lock channels */ +- ast_channel_lock(c0); +- if (c1) { +- while (ast_channel_trylock(c1)) { +- ast_channel_unlock(c0); +- usleep(1); +- ast_channel_lock(c0); +- } +- } +- +- /* Find channel driver interfaces */ +- destpr = get_proto(c0); +- if (c1) +- srcpr = get_proto(c1); +- if (!destpr) { +- ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c0->name); +- ast_channel_unlock(c0); +- if (c1) +- ast_channel_unlock(c1); +- return -1; +- } +- if (!srcpr) { +- ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c1 ? c1->name : "<unspecified>"); +- ast_channel_unlock(c0); +- if (c1) +- ast_channel_unlock(c1); +- return -1; +- } +- +- /* Get audio, video and text interface (if native bridge is possible) */ +- audio_dest_res = destpr->get_rtp_info(c0, &destp); +- video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(c0, &vdestp) : AST_RTP_GET_FAILED; +- text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(c0, &tdestp) : AST_RTP_GET_FAILED; +- if (srcpr) { +- audio_src_res = srcpr->get_rtp_info(c1, &srcp); +- video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(c1, &vsrcp) : AST_RTP_GET_FAILED; +- text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(c1, &tsrcp) : AST_RTP_GET_FAILED; +- } +- +- /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */ +- if (audio_dest_res != AST_RTP_TRY_NATIVE || (video_dest_res != AST_RTP_GET_FAILED && video_dest_res != AST_RTP_TRY_NATIVE)) { +- /* Somebody doesn't want to play... */ +- ast_channel_unlock(c0); +- if (c1) +- ast_channel_unlock(c1); +- return -1; +- } +- if (audio_src_res == AST_RTP_TRY_NATIVE && (video_src_res == AST_RTP_GET_FAILED || video_src_res == AST_RTP_TRY_NATIVE) && srcpr->get_codec) +- srccodec = srcpr->get_codec(c1); +- else +- srccodec = 0; +- if (audio_dest_res == AST_RTP_TRY_NATIVE && (video_dest_res == AST_RTP_GET_FAILED || video_dest_res == AST_RTP_TRY_NATIVE) && destpr->get_codec) +- destcodec = destpr->get_codec(c0); +- else +- destcodec = 0; +- /* Ensure we have at least one matching codec */ +- if (srcp && !(srccodec & destcodec)) { +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return 0; +- } +- /* Consider empty media as non-existent */ +- if (audio_src_res == AST_RTP_TRY_NATIVE && !srcp->them.sin_addr.s_addr) +- srcp = NULL; +- if (srcp && (srcp->nat || ast_test_flag(srcp, FLAG_NAT_ACTIVE))) +- nat_active = 1; +- /* Bridge media early */ +- if (destpr->set_rtp_peer(c0, srcp, vsrcp, tsrcp, srccodec, nat_active)) +- ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); +- ast_channel_unlock(c0); +- if (c1) +- ast_channel_unlock(c1); +- ast_debug(1, "Setting early bridge SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); +- return 0; +-} +- +-int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media) +-{ +- struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */ +- struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */ +- struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */ +- struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL; +- enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED; +- enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED; +- int srccodec, destcodec; +- +- /* Lock channels */ +- ast_channel_lock(dest); +- while (ast_channel_trylock(src)) { +- ast_channel_unlock(dest); +- usleep(1); +- ast_channel_lock(dest); +- } +- +- /* Find channel driver interfaces */ +- if (!(destpr = get_proto(dest))) { +- ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", dest->name); +- ast_channel_unlock(dest); +- ast_channel_unlock(src); +- return 0; +- } +- if (!(srcpr = get_proto(src))) { +- ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", src->name); +- ast_channel_unlock(dest); +- ast_channel_unlock(src); +- return 0; +- } +- +- /* Get audio and video interface (if native bridge is possible) */ +- audio_dest_res = destpr->get_rtp_info(dest, &destp); +- video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(dest, &vdestp) : AST_RTP_GET_FAILED; +- text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(dest, &tdestp) : AST_RTP_GET_FAILED; +- audio_src_res = srcpr->get_rtp_info(src, &srcp); +- video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(src, &vsrcp) : AST_RTP_GET_FAILED; +- text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(src, &tsrcp) : AST_RTP_GET_FAILED; +- +- /* Ensure we have at least one matching codec */ +- if (srcpr->get_codec) +- srccodec = srcpr->get_codec(src); +- else +- srccodec = 0; +- if (destpr->get_codec) +- destcodec = destpr->get_codec(dest); +- else +- destcodec = 0; +- +- /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */ +- if (audio_dest_res != AST_RTP_TRY_NATIVE || (video_dest_res != AST_RTP_GET_FAILED && video_dest_res != AST_RTP_TRY_NATIVE) || audio_src_res != AST_RTP_TRY_NATIVE || (video_src_res != AST_RTP_GET_FAILED && video_src_res != AST_RTP_TRY_NATIVE) || !(srccodec & destcodec)) { +- /* Somebody doesn't want to play... */ +- ast_channel_unlock(dest); +- ast_channel_unlock(src); +- return 0; +- } +- ast_rtp_pt_copy(destp, srcp); +- if (vdestp && vsrcp) +- ast_rtp_pt_copy(vdestp, vsrcp); +- if (tdestp && tsrcp) +- ast_rtp_pt_copy(tdestp, tsrcp); +- if (media) { +- /* Bridge early */ +- if (destpr->set_rtp_peer(dest, srcp, vsrcp, tsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE))) +- ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", dest->name, src->name); +- } +- ast_channel_unlock(dest); +- ast_channel_unlock(src); +- ast_debug(1, "Seeded SDP of '%s' with that of '%s'\n", dest->name, src->name); +- return 1; +-} +- +-/*! \brief Make a note of a RTP payload type that was seen in a SDP "m=" line. +- * By default, use the well-known value for this type (although it may +- * still be set to a different value by a subsequent "a=rtpmap:" line) +- */ +-void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt) +-{ +- if (pt < 0 || pt > MAX_RTP_PT || static_RTP_PT[pt].code == 0) +- return; /* bogus payload type */ +- +- rtp_bridge_lock(rtp); +- rtp->current_RTP_PT[pt] = static_RTP_PT[pt]; +- rtp_bridge_unlock(rtp); +-} +- +-/*! \brief remove setting from payload type list if the rtpmap header indicates +- an unknown media type */ +-void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt) +-{ +- if (pt < 0 || pt > MAX_RTP_PT) +- return; /* bogus payload type */ +- +- rtp_bridge_lock(rtp); +- rtp->current_RTP_PT[pt].isAstFormat = 0; +- rtp->current_RTP_PT[pt].code = 0; +- rtp_bridge_unlock(rtp); +-} +- +-/*! \brief Make a note of a RTP payload type (with MIME type) that was seen in +- * an SDP "a=rtpmap:" line. +- * \return 0 if the MIME type was found and set, -1 if it wasn't found +- */ +-int ast_rtp_set_rtpmap_type_rate(struct ast_rtp *rtp, int pt, +- char *mimeType, char *mimeSubtype, +- enum ast_rtp_options options, +- unsigned int sample_rate) +-{ +- unsigned int i; +- int found = 0; +- +- if (pt < 0 || pt > MAX_RTP_PT) +- return -1; /* bogus payload type */ +- +- rtp_bridge_lock(rtp); +- +- for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { +- const struct mimeType *t = &mimeTypes[i]; +- +- if (strcasecmp(mimeSubtype, t->subtype)) { +- continue; +- } +- +- if (strcasecmp(mimeType, t->type)) { +- continue; +- } +- +- /* if both sample rates have been supplied, and they don't match, +- then this not a match; if one has not been supplied, then the +- rates are not compared */ +- if (sample_rate && t->sample_rate && +- (sample_rate != t->sample_rate)) { +- continue; +- } +- +- found = 1; +- rtp->current_RTP_PT[pt] = t->payloadType; +- +- if ((t->payloadType.code == AST_FORMAT_G726) && +- t->payloadType.isAstFormat && +- (options & AST_RTP_OPT_G726_NONSTANDARD)) { +- rtp->current_RTP_PT[pt].code = AST_FORMAT_G726_AAL2; +- } +- +- break; +- } +- +- rtp_bridge_unlock(rtp); +- +- return (found ? 0 : -2); +-} +- +-int ast_rtp_set_rtpmap_type(struct ast_rtp *rtp, int pt, +- char *mimeType, char *mimeSubtype, +- enum ast_rtp_options options) +-{ +- return ast_rtp_set_rtpmap_type_rate(rtp, pt, mimeType, mimeSubtype, options, 0); +-} +- +-/*! \brief Return the union of all of the codecs that were set by rtp_set...() calls +- * They're returned as two distinct sets: AST_FORMATs, and AST_RTPs */ +-void ast_rtp_get_current_formats(struct ast_rtp* rtp, +- int* astFormats, int* nonAstFormats) +-{ +- int pt; +- +- rtp_bridge_lock(rtp); +- +- *astFormats = *nonAstFormats = 0; +- for (pt = 0; pt < MAX_RTP_PT; ++pt) { +- if (rtp->current_RTP_PT[pt].isAstFormat) { +- *astFormats |= rtp->current_RTP_PT[pt].code; +- } else { +- *nonAstFormats |= rtp->current_RTP_PT[pt].code; +- } +- } +- +- rtp_bridge_unlock(rtp); +-} +- +-struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt) +-{ +- struct rtpPayloadType result; +- +- result.isAstFormat = result.code = 0; +- +- if (pt < 0 || pt > MAX_RTP_PT) +- return result; /* bogus payload type */ +- +- /* Start with negotiated codecs */ +- rtp_bridge_lock(rtp); +- result = rtp->current_RTP_PT[pt]; +- rtp_bridge_unlock(rtp); +- +- /* If it doesn't exist, check our static RTP type list, just in case */ +- if (!result.code) +- result = static_RTP_PT[pt]; +- +- return result; +-} +- +-/*! \brief Looks up an RTP code out of our *static* outbound list */ +-int ast_rtp_lookup_code(struct ast_rtp* rtp, const int isAstFormat, const int code) +-{ +- int pt = 0; +- +- rtp_bridge_lock(rtp); +- +- if (isAstFormat == rtp->rtp_lookup_code_cache_isAstFormat && +- code == rtp->rtp_lookup_code_cache_code) { +- /* Use our cached mapping, to avoid the overhead of the loop below */ +- pt = rtp->rtp_lookup_code_cache_result; +- rtp_bridge_unlock(rtp); +- return pt; +- } +- +- /* Check the dynamic list first */ +- for (pt = 0; pt < MAX_RTP_PT; ++pt) { +- if (rtp->current_RTP_PT[pt].code == code && rtp->current_RTP_PT[pt].isAstFormat == isAstFormat) { +- rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat; +- rtp->rtp_lookup_code_cache_code = code; +- rtp->rtp_lookup_code_cache_result = pt; +- rtp_bridge_unlock(rtp); +- return pt; +- } +- } +- +- /* Then the static list */ +- for (pt = 0; pt < MAX_RTP_PT; ++pt) { +- if (static_RTP_PT[pt].code == code && static_RTP_PT[pt].isAstFormat == isAstFormat) { +- rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat; +- rtp->rtp_lookup_code_cache_code = code; +- rtp->rtp_lookup_code_cache_result = pt; +- rtp_bridge_unlock(rtp); +- return pt; +- } +- } +- +- rtp_bridge_unlock(rtp); +- +- return -1; +-} +- +-const char *ast_rtp_lookup_mime_subtype(const int isAstFormat, const int code, +- enum ast_rtp_options options) +-{ +- unsigned int i; +- +- for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { +- if ((mimeTypes[i].payloadType.code == code) && (mimeTypes[i].payloadType.isAstFormat == isAstFormat)) { +- if (isAstFormat && +- (code == AST_FORMAT_G726_AAL2) && +- (options & AST_RTP_OPT_G726_NONSTANDARD)) +- return "G726-32"; +- else +- return mimeTypes[i].subtype; +- } +- } +- +- return ""; +-} +- +-unsigned int ast_rtp_lookup_sample_rate(int isAstFormat, int code) +-{ +- unsigned int i; +- +- for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { +- if ((mimeTypes[i].payloadType.code == code) && (mimeTypes[i].payloadType.isAstFormat == isAstFormat)) { +- return mimeTypes[i].sample_rate; +- } +- } +- +- return 0; +-} +- +-char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability, +- const int isAstFormat, enum ast_rtp_options options) +-{ +- int format; +- unsigned len; +- char *end = buf; +- char *start = buf; +- +- if (!buf || !size) +- return NULL; +- +- snprintf(end, size, "0x%x (", capability); +- +- len = strlen(end); +- end += len; +- size -= len; +- start = end; +- +- for (format = 1; format < AST_RTP_MAX; format <<= 1) { +- if (capability & format) { +- const char *name = ast_rtp_lookup_mime_subtype(isAstFormat, format, options); +- +- snprintf(end, size, "%s|", name); +- len = strlen(end); +- end += len; +- size -= len; +- } +- } +- +- if (start == end) +- ast_copy_string(start, "nothing)", size); +- else if (size > 1) +- *(end -1) = ')'; +- +- return buf; +-} +- +-/*! \brief Open RTP or RTCP socket for a session. +- * Print a message on failure. +- */ +-static int rtp_socket(const char *type) +-{ +- int s = socket(AF_INET, SOCK_DGRAM, 0); +- if (s < 0) { +- if (type == NULL) +- type = "RTP/RTCP"; +- ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno)); +- } else { +- long flags = fcntl(s, F_GETFL); +- fcntl(s, F_SETFL, flags | O_NONBLOCK); +-#ifdef SO_NO_CHECK +- if (nochecksums) +- setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums)); +-#endif +- } +- return s; +-} +- +-/*! +- * \brief Initialize a new RTCP session. +- * +- * \returns The newly initialized RTCP session. +- */ +-static struct ast_rtcp *ast_rtcp_new(void) +-{ +- struct ast_rtcp *rtcp; +- +- if (!(rtcp = ast_calloc(1, sizeof(*rtcp)))) +- return NULL; +- rtcp->s = rtp_socket("RTCP"); +- rtcp->us.sin_family = AF_INET; +- rtcp->them.sin_family = AF_INET; +- rtcp->schedid = -1; +- +- if (rtcp->s < 0) { +- ast_free(rtcp); +- return NULL; +- } +- +- return rtcp; +-} +- +-/*! +- * \brief Initialize a new RTP structure. +- * +- */ +-void ast_rtp_new_init(struct ast_rtp *rtp) +-{ +-#ifdef P2P_INTENSE +- ast_mutex_init(&rtp->bridge_lock); +-#endif +- +- rtp->them.sin_family = AF_INET; +- rtp->us.sin_family = AF_INET; +- rtp->ssrc = ast_random(); +- rtp->seqno = ast_random() & 0xffff; +- ast_set_flag(rtp, FLAG_HAS_DTMF); +- rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN); +-} +- +-struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr addr) +-{ +- struct ast_rtp *rtp; +- int x; +- int startplace; +- +- if (!(rtp = ast_calloc(1, sizeof(*rtp)))) +- return NULL; +- +- ast_rtp_new_init(rtp); +- +- rtp->s = rtp_socket("RTP"); +- if (rtp->s < 0) +- goto fail; +- if (sched && rtcpenable) { +- rtp->sched = sched; +- rtp->rtcp = ast_rtcp_new(); +- } +- +- /* +- * Try to bind the RTP port, x, and possibly the RTCP port, x+1 as well. +- * Start from a random (even, by RTP spec) port number, and +- * iterate until success or no ports are available. +- * Note that the requirement of RTP port being even, or RTCP being the +- * next one, cannot be enforced in presence of a NAT box because the +- * mapping is not under our control. +- */ +- x = (rtpend == rtpstart) ? rtpstart : (ast_random() % (rtpend - rtpstart)) + rtpstart; +- x = x & ~1; /* make it an even number */ +- startplace = x; /* remember the starting point */ +- /* this is constant across the loop */ +- rtp->us.sin_addr = addr; +- if (rtp->rtcp) +- rtp->rtcp->us.sin_addr = addr; +- for (;;) { +- rtp->us.sin_port = htons(x); +- if (!bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) { +- /* bind succeeded, if no rtcp then we are done */ +- if (!rtp->rtcp) +- break; +- /* have rtcp, try to bind it */ +- rtp->rtcp->us.sin_port = htons(x + 1); +- if (!bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))) +- break; /* success again, we are really done */ +- /* +- * RTCP bind failed, so close and recreate the +- * already bound RTP socket for the next round. +- */ +- close(rtp->s); +- rtp->s = rtp_socket("RTP"); +- if (rtp->s < 0) +- goto fail; +- } +- /* +- * If we get here, there was an error in one of the bind() +- * calls, so make sure it is nothing unexpected. +- */ +- if (errno != EADDRINUSE) { +- /* We got an error that wasn't expected, abort! */ +- ast_log(LOG_ERROR, "Unexpected bind error: %s\n", strerror(errno)); +- goto fail; +- } +- /* +- * One of the ports is in use. For the next iteration, +- * increment by two and handle wraparound. +- * If we reach the starting point, then declare failure. +- */ +- x += 2; +- if (x > rtpend) +- x = (rtpstart + 1) & ~1; +- if (x == startplace) { +- ast_log(LOG_ERROR, "No RTP ports remaining. Can't setup media stream for this call.\n"); +- goto fail; +- } +- } +- rtp->sched = sched; +- rtp->io = io; +- if (callbackmode) { +- rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp); +- ast_set_flag(rtp, FLAG_CALLBACK_MODE); +- } +- ast_rtp_pt_default(rtp); +- return rtp; +- +-fail: +- if (rtp->s >= 0) +- close(rtp->s); +- if (rtp->rtcp) { +- close(rtp->rtcp->s); +- ast_free(rtp->rtcp); +- } +- ast_free(rtp); +- return NULL; +-} +- +-struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode) +-{ +- struct in_addr ia; +- +- memset(&ia, 0, sizeof(ia)); +- return ast_rtp_new_with_bindaddr(sched, io, rtcpenable, callbackmode, ia); +-} +- +-int ast_rtp_setqos(struct ast_rtp *rtp, int type_of_service, int class_of_service, char *desc) +-{ +- return ast_netsock_set_qos(rtp->s, type_of_service, class_of_service, desc); +-} +- +-void ast_rtp_new_source(struct ast_rtp *rtp) +-{ +- if (rtp) { +- rtp->set_marker_bit = 1; +- } +- return; +-} +- +-void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them) +-{ +- rtp->them.sin_port = them->sin_port; +- rtp->them.sin_addr = them->sin_addr; +- if (rtp->rtcp) { +- int h = ntohs(them->sin_port); +- rtp->rtcp->them.sin_port = htons(h + 1); +- rtp->rtcp->them.sin_addr = them->sin_addr; +- } +- rtp->rxseqno = 0; +- /* If strict RTP protection is enabled switch back to the learn state so we don't drop packets from above */ +- if (strictrtp) +- rtp->strict_rtp_state = STRICT_RTP_LEARN; +-} +- +-int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them) +-{ +- if ((them->sin_family != AF_INET) || +- (them->sin_port != rtp->them.sin_port) || +- (them->sin_addr.s_addr != rtp->them.sin_addr.s_addr)) { +- them->sin_family = AF_INET; +- them->sin_port = rtp->them.sin_port; +- them->sin_addr = rtp->them.sin_addr; +- return 1; +- } +- return 0; +-} +- +-void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us) +-{ +- *us = rtp->us; +-} +- +-struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp) +-{ +- struct ast_rtp *bridged = NULL; +- +- rtp_bridge_lock(rtp); +- bridged = rtp->bridged; +- rtp_bridge_unlock(rtp); +- +- return bridged; +-} +- +-void ast_rtp_stop(struct ast_rtp *rtp) +-{ +- if (rtp->rtcp) { +- AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); +- } +- if (rtp->red) { +- AST_SCHED_DEL(rtp->sched, rtp->red->schedid); +- free(rtp->red); +- rtp->red = NULL; +- } +- +- memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr)); +- memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port)); +- if (rtp->rtcp) { +- memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr)); +- memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port)); +- } +- +- ast_clear_flag(rtp, FLAG_P2P_SENT_MARK); +-} +- +-void ast_rtp_reset(struct ast_rtp *rtp) +-{ +- memset(&rtp->rxcore, 0, sizeof(rtp->rxcore)); +- memset(&rtp->txcore, 0, sizeof(rtp->txcore)); +- memset(&rtp->dtmfmute, 0, sizeof(rtp->dtmfmute)); +- rtp->lastts = 0; +- rtp->lastdigitts = 0; +- rtp->lastrxts = 0; +- rtp->lastividtimestamp = 0; +- rtp->lastovidtimestamp = 0; +- rtp->lastitexttimestamp = 0; +- rtp->lastotexttimestamp = 0; +- rtp->lasteventseqn = 0; +- rtp->lastevent = 0; +- rtp->lasttxformat = 0; +- rtp->lastrxformat = 0; +- rtp->dtmfcount = 0; +- rtp->dtmfsamples = 0; +- rtp->seqno = 0; +- rtp->rxseqno = 0; +-} +- +-/*! Get QoS values from RTP and RTCP data (used in "sip show channelstats") */ +-unsigned int ast_rtp_get_qosvalue(struct ast_rtp *rtp, enum ast_rtp_qos_vars value) +-{ +- if (rtp == NULL) { +- if (option_debug > 1) +- ast_log(LOG_DEBUG, "NO RTP Structure? Kidding me? \n"); +- return 0; +- } +- if (option_debug > 1 && rtp->rtcp == NULL) { +- ast_log(LOG_DEBUG, "NO RTCP structure. Maybe in RTP p2p bridging mode? \n"); +- } +- +- switch (value) { +- case AST_RTP_TXCOUNT: +- return (unsigned int) rtp->txcount; +- case AST_RTP_RXCOUNT: +- return (unsigned int) rtp->rxcount; +- case AST_RTP_TXJITTER: +- return (unsigned int) (rtp->rxjitter * 100.0); +- case AST_RTP_RXJITTER: +- return (unsigned int) (rtp->rtcp ? (rtp->rtcp->reported_jitter / (unsigned int) 65536.0) : 0); +- case AST_RTP_RXPLOSS: +- return rtp->rtcp ? (rtp->rtcp->expected_prior - rtp->rtcp->received_prior) : 0; +- case AST_RTP_TXPLOSS: +- return rtp->rtcp ? rtp->rtcp->reported_lost : 0; +- case AST_RTP_RTT: +- return (unsigned int) (rtp->rtcp ? (rtp->rtcp->rtt * 100) : 0); +- } +- return 0; /* To make the compiler happy */ +-} +- +-static double __ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, int *found) +-{ +- *found = 1; +- +- if (!strcasecmp(qos, "remote_maxjitter")) +- return rtp->rtcp->reported_maxjitter * 1000.0; +- if (!strcasecmp(qos, "remote_minjitter")) +- return rtp->rtcp->reported_minjitter * 1000.0; +- if (!strcasecmp(qos, "remote_normdevjitter")) +- return rtp->rtcp->reported_normdev_jitter * 1000.0; +- if (!strcasecmp(qos, "remote_stdevjitter")) +- return sqrt(rtp->rtcp->reported_stdev_jitter) * 1000.0; +- +- if (!strcasecmp(qos, "local_maxjitter")) +- return rtp->rtcp->maxrxjitter * 1000.0; +- if (!strcasecmp(qos, "local_minjitter")) +- return rtp->rtcp->minrxjitter * 1000.0; +- if (!strcasecmp(qos, "local_normdevjitter")) +- return rtp->rtcp->normdev_rxjitter * 1000.0; +- if (!strcasecmp(qos, "local_stdevjitter")) +- return sqrt(rtp->rtcp->stdev_rxjitter) * 1000.0; +- +- if (!strcasecmp(qos, "maxrtt")) +- return rtp->rtcp->maxrtt * 1000.0; +- if (!strcasecmp(qos, "minrtt")) +- return rtp->rtcp->minrtt * 1000.0; +- if (!strcasecmp(qos, "normdevrtt")) +- return rtp->rtcp->normdevrtt * 1000.0; +- if (!strcasecmp(qos, "stdevrtt")) +- return sqrt(rtp->rtcp->stdevrtt) * 1000.0; +- +- *found = 0; +- +- return 0.0; +-} +- +-int ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, char *buf, unsigned int buflen) +-{ +- double value; +- int found; +- +- value = __ast_rtp_get_qos(rtp, qos, &found); +- +- if (!found) +- return -1; +- +- snprintf(buf, buflen, "%.0lf", value); +- +- return 0; +-} +- +-void ast_rtp_set_vars(struct ast_channel *chan, struct ast_rtp *rtp) { +- char *audioqos; +- char *audioqos_jitter; +- char *audioqos_loss; +- char *audioqos_rtt; +- struct ast_channel *bridge; +- +- if (!rtp || !chan) +- return; +- +- bridge = ast_bridged_channel(chan); +- +- audioqos = ast_rtp_get_quality(rtp, NULL, RTPQOS_SUMMARY); +- audioqos_jitter = ast_rtp_get_quality(rtp, NULL, RTPQOS_JITTER); +- audioqos_loss = ast_rtp_get_quality(rtp, NULL, RTPQOS_LOSS); +- audioqos_rtt = ast_rtp_get_quality(rtp, NULL, RTPQOS_RTT); +- +- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOS", audioqos); +- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSJITTER", audioqos_jitter); +- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSLOSS", audioqos_loss); +- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSRTT", audioqos_rtt); +- +- if (!bridge) +- return; +- +- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSBRIDGED", audioqos); +- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSJITTERBRIDGED", audioqos_jitter); +- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSLOSSBRIDGED", audioqos_loss); +- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSRTTBRIDGED", audioqos_rtt); +-} +- +-static char *__ast_rtp_get_quality_jitter(struct ast_rtp *rtp) +-{ +- /* +- *ssrc our ssrc +- *themssrc their ssrc +- *lp lost packets +- *rxjitter our calculated jitter(rx) +- *rxcount no. received packets +- *txjitter reported jitter of the other end +- *txcount transmitted packets +- *rlp remote lost packets +- *rtt round trip time +- */ +-#define RTCP_JITTER_FORMAT1 \ +- "minrxjitter=%f;" \ +- "maxrxjitter=%f;" \ +- "avgrxjitter=%f;" \ +- "stdevrxjitter=%f;" \ +- "reported_minjitter=%f;" \ +- "reported_maxjitter=%f;" \ +- "reported_avgjitter=%f;" \ +- "reported_stdevjitter=%f;" +- +-#define RTCP_JITTER_FORMAT2 \ +- "rxjitter=%f;" +- +- if (rtp->rtcp && rtp->rtcp->rtcp_info) { +- snprintf(rtp->rtcp->quality_jitter, sizeof(rtp->rtcp->quality_jitter), RTCP_JITTER_FORMAT1, +- rtp->rtcp->minrxjitter, +- rtp->rtcp->maxrxjitter, +- rtp->rtcp->normdev_rxjitter, +- sqrt(rtp->rtcp->stdev_rxjitter), +- rtp->rtcp->reported_minjitter, +- rtp->rtcp->reported_maxjitter, +- rtp->rtcp->reported_normdev_jitter, +- sqrt(rtp->rtcp->reported_stdev_jitter) +- ); +- } else { +- snprintf(rtp->rtcp->quality_jitter, sizeof(rtp->rtcp->quality_jitter), RTCP_JITTER_FORMAT2, +- rtp->rxjitter +- ); +- } +- +- return rtp->rtcp->quality_jitter; +- +-#undef RTCP_JITTER_FORMAT1 +-#undef RTCP_JITTER_FORMAT2 +-} +- +-static char *__ast_rtp_get_quality_loss(struct ast_rtp *rtp) +-{ +- unsigned int lost; +- unsigned int extended; +- unsigned int expected; +- int fraction; +- +-#define RTCP_LOSS_FORMAT1 \ +- "minrxlost=%f;" \ +- "maxrxlost=%f;" \ +- "avgrxlostr=%f;" \ +- "stdevrxlost=%f;" \ +- "reported_minlost=%f;" \ +- "reported_maxlost=%f;" \ +- "reported_avglost=%f;" \ +- "reported_stdevlost=%f;" +- +-#define RTCP_LOSS_FORMAT2 \ +- "lost=%d;" \ +- "expected=%d;" +- +- if (rtp->rtcp && rtp->rtcp->rtcp_info && rtp->rtcp->maxrxlost > 0) { +- snprintf(rtp->rtcp->quality_loss, sizeof(rtp->rtcp->quality_loss), RTCP_LOSS_FORMAT1, +- rtp->rtcp->minrxlost, +- rtp->rtcp->maxrxlost, +- rtp->rtcp->normdev_rxlost, +- sqrt(rtp->rtcp->stdev_rxlost), +- rtp->rtcp->reported_minlost, +- rtp->rtcp->reported_maxlost, +- rtp->rtcp->reported_normdev_lost, +- sqrt(rtp->rtcp->reported_stdev_lost) +- ); +- } else { +- extended = rtp->cycles + rtp->lastrxseqno; +- expected = extended - rtp->seedrxseqno + 1; +- if (rtp->rxcount > expected) +- expected += rtp->rxcount - expected; +- lost = expected - rtp->rxcount; +- +- if (!expected || lost <= 0) +- fraction = 0; +- else +- fraction = (lost << 8) / expected; +- +- snprintf(rtp->rtcp->quality_loss, sizeof(rtp->rtcp->quality_loss), RTCP_LOSS_FORMAT2, +- lost, +- expected +- ); +- } +- +- return rtp->rtcp->quality_loss; +- +-#undef RTCP_LOSS_FORMAT1 +-#undef RTCP_LOSS_FORMAT2 +-} +- +-static char *__ast_rtp_get_quality_rtt(struct ast_rtp *rtp) +-{ +- if (rtp->rtcp && rtp->rtcp->rtcp_info) { +- snprintf(rtp->rtcp->quality_rtt, sizeof(rtp->rtcp->quality_rtt), "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;", +- rtp->rtcp->minrtt, +- rtp->rtcp->maxrtt, +- rtp->rtcp->normdevrtt, +- sqrt(rtp->rtcp->stdevrtt) +- ); +- } else { +- snprintf(rtp->rtcp->quality_rtt, sizeof(rtp->rtcp->quality_rtt), "Not available"); +- } +- +- return rtp->rtcp->quality_rtt; +-} +- +-static char *__ast_rtp_get_quality(struct ast_rtp *rtp) +-{ +- /* +- *ssrc our ssrc +- *themssrc their ssrc +- *lp lost packets +- *rxjitter our calculated jitter(rx) +- *rxcount no. received packets +- *txjitter reported jitter of the other end +- *txcount transmitted packets +- *rlp remote lost packets +- *rtt round trip time +- */ +- +- if (rtp->rtcp && rtp->rtcp->rtcp_info) { +- snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), +- "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f", +- rtp->ssrc, +- rtp->themssrc, +- rtp->rtcp->expected_prior - rtp->rtcp->received_prior, +- rtp->rxjitter, +- rtp->rxcount, +- (double)rtp->rtcp->reported_jitter / 65536.0, +- rtp->txcount, +- rtp->rtcp->reported_lost, +- rtp->rtcp->rtt +- ); +- } else { +- snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), "ssrc=%u;themssrc=%u;rxjitter=%f;rxcount=%u;txcount=%u;", +- rtp->ssrc, +- rtp->themssrc, +- rtp->rxjitter, +- rtp->rxcount, +- rtp->txcount +- ); +- } +- +- return rtp->rtcp->quality; +-} +- +-char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual, enum ast_rtp_quality_type qtype) +-{ +- if (qual && rtp) { +- qual->local_ssrc = rtp->ssrc; +- qual->local_jitter = rtp->rxjitter; +- qual->local_count = rtp->rxcount; +- qual->remote_ssrc = rtp->themssrc; +- qual->remote_count = rtp->txcount; +- +- if (rtp->rtcp) { +- qual->local_lostpackets = rtp->rtcp->expected_prior - rtp->rtcp->received_prior; +- qual->remote_lostpackets = rtp->rtcp->reported_lost; +- qual->remote_jitter = rtp->rtcp->reported_jitter / 65536.0; +- qual->rtt = rtp->rtcp->rtt; +- } +- } +- +- switch (qtype) { +- case RTPQOS_SUMMARY: +- return __ast_rtp_get_quality(rtp); +- case RTPQOS_JITTER: +- return __ast_rtp_get_quality_jitter(rtp); +- case RTPQOS_LOSS: +- return __ast_rtp_get_quality_loss(rtp); +- case RTPQOS_RTT: +- return __ast_rtp_get_quality_rtt(rtp); +- } +- +- return NULL; +-} +- +-void ast_rtp_destroy(struct ast_rtp *rtp) +-{ +- if (rtcp_debug_test_addr(&rtp->them) || rtcpstats) { +- /*Print some info on the call here */ +- ast_verbose(" RTP-stats\n"); +- ast_verbose("* Our Receiver:\n"); +- ast_verbose(" SSRC: %u\n", rtp->themssrc); +- ast_verbose(" Received packets: %u\n", rtp->rxcount); +- ast_verbose(" Lost packets: %u\n", rtp->rtcp ? (rtp->rtcp->expected_prior - rtp->rtcp->received_prior) : 0); +- ast_verbose(" Jitter: %.4f\n", rtp->rxjitter); +- ast_verbose(" Transit: %.4f\n", rtp->rxtransit); +- ast_verbose(" RR-count: %u\n", rtp->rtcp ? rtp->rtcp->rr_count : 0); +- ast_verbose("* Our Sender:\n"); +- ast_verbose(" SSRC: %u\n", rtp->ssrc); +- ast_verbose(" Sent packets: %u\n", rtp->txcount); +- ast_verbose(" Lost packets: %u\n", rtp->rtcp ? rtp->rtcp->reported_lost : 0); +- ast_verbose(" Jitter: %u\n", rtp->rtcp ? (rtp->rtcp->reported_jitter / (unsigned int)65536.0) : 0); +- ast_verbose(" SR-count: %u\n", rtp->rtcp ? rtp->rtcp->sr_count : 0); +- ast_verbose(" RTT: %f\n", rtp->rtcp ? rtp->rtcp->rtt : 0); +- } +- +- manager_event(EVENT_FLAG_REPORTING, "RTPReceiverStat", "SSRC: %u\r\n" +- "ReceivedPackets: %u\r\n" +- "LostPackets: %u\r\n" +- "Jitter: %.4f\r\n" +- "Transit: %.4f\r\n" +- "RRCount: %u\r\n", +- rtp->themssrc, +- rtp->rxcount, +- rtp->rtcp ? (rtp->rtcp->expected_prior - rtp->rtcp->received_prior) : 0, +- rtp->rxjitter, +- rtp->rxtransit, +- rtp->rtcp ? rtp->rtcp->rr_count : 0); +- manager_event(EVENT_FLAG_REPORTING, "RTPSenderStat", "SSRC: %u\r\n" +- "SentPackets: %u\r\n" +- "LostPackets: %u\r\n" +- "Jitter: %u\r\n" +- "SRCount: %u\r\n" +- "RTT: %f\r\n", +- rtp->ssrc, +- rtp->txcount, +- rtp->rtcp ? rtp->rtcp->reported_lost : 0, +- rtp->rtcp ? rtp->rtcp->reported_jitter : 0, +- rtp->rtcp ? rtp->rtcp->sr_count : 0, +- rtp->rtcp ? rtp->rtcp->rtt : 0); +- if (rtp->smoother) +- ast_smoother_free(rtp->smoother); +- if (rtp->ioid) +- ast_io_remove(rtp->io, rtp->ioid); +- if (rtp->s > -1) +- close(rtp->s); +- if (rtp->rtcp) { +- AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); +- close(rtp->rtcp->s); +- ast_free(rtp->rtcp); +- rtp->rtcp=NULL; +- } +-#ifdef P2P_INTENSE +- ast_mutex_destroy(&rtp->bridge_lock); +-#endif +- ast_free(rtp); +-} +- +-static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery) +-{ +- struct timeval t; +- long ms; +- if (ast_tvzero(rtp->txcore)) { +- rtp->txcore = ast_tvnow(); +- /* Round to 20ms for nice, pretty timestamps */ +- rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000; +- } +- /* Use previous txcore if available */ +- t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow(); +- ms = ast_tvdiff_ms(t, rtp->txcore); +- if (ms < 0) +- ms = 0; +- /* Use what we just got for next time */ +- rtp->txcore = t; +- return (unsigned int) ms; +-} +- +-/*! \brief Send begin frames for DTMF */ +-int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit) +-{ +- unsigned int *rtpheader; +- int hdrlen = 12, res = 0, i = 0, payload = 0; +- char data[256]; +- +- if ((digit <= '9') && (digit >= '0')) +- digit -= '0'; +- else if (digit == '*') +- digit = 10; +- else if (digit == '#') +- digit = 11; +- else if ((digit >= 'A') && (digit <= 'D')) +- digit = digit - 'A' + 12; +- else if ((digit >= 'a') && (digit <= 'd')) +- digit = digit - 'a' + 12; +- else { +- ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); +- return 0; +- } +- +- /* If we have no peer, return immediately */ +- if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) +- return 0; +- +- payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF); +- +- rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); +- rtp->send_duration = 160; +- rtp->lastdigitts = rtp->lastts + rtp->send_duration; +- +- /* Get a pointer to the header */ +- rtpheader = (unsigned int *)data; +- rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno)); +- rtpheader[1] = htonl(rtp->lastdigitts); +- rtpheader[2] = htonl(rtp->ssrc); +- +- for (i = 0; i < 2; i++) { +- rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); +- res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); +- if (res < 0) +- ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n", +- ast_inet_ntoa(rtp->them.sin_addr), +- ntohs(rtp->them.sin_port), strerror(errno)); +- if (rtp_debug_test_addr(&rtp->them)) +- ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", +- ast_inet_ntoa(rtp->them.sin_addr), +- ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); +- /* Increment sequence number */ +- rtp->seqno++; +- /* Increment duration */ +- rtp->send_duration += 160; +- /* Clear marker bit and set seqno */ +- rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno)); +- } +- +- /* Since we received a begin, we can safely store the digit and disable any compensation */ +- rtp->sending_digit = 1; +- rtp->send_digit = digit; +- rtp->send_payload = payload; +- +- return 0; +-} +- +-/*! \brief Send continuation frame for DTMF */ +-static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp) +-{ +- unsigned int *rtpheader; +- int hdrlen = 12, res = 0; +- char data[256]; +- +- if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) +- return 0; +- +- /* Setup packet to send */ +- rtpheader = (unsigned int *)data; +- rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); +- rtpheader[1] = htonl(rtp->lastdigitts); +- rtpheader[2] = htonl(rtp->ssrc); +- rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (rtp->send_duration)); +- rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); +- +- /* Transmit */ +- res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); +- if (res < 0) +- ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", +- ast_inet_ntoa(rtp->them.sin_addr), +- ntohs(rtp->them.sin_port), strerror(errno)); +- if (rtp_debug_test_addr(&rtp->them)) +- ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", +- ast_inet_ntoa(rtp->them.sin_addr), +- ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); +- +- /* Increment sequence number */ +- rtp->seqno++; +- /* Increment duration */ +- rtp->send_duration += 160; +- +- return 0; +-} +- +-/*! \brief Send end packets for DTMF */ +-int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit) +-{ +- unsigned int *rtpheader; +- int hdrlen = 12, res = 0, i = 0; +- char data[256]; +- +- /* If no address, then bail out */ +- if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) +- return 0; +- +- if ((digit <= '9') && (digit >= '0')) +- digit -= '0'; +- else if (digit == '*') +- digit = 10; +- else if (digit == '#') +- digit = 11; +- else if ((digit >= 'A') && (digit <= 'D')) +- digit = digit - 'A' + 12; +- else if ((digit >= 'a') && (digit <= 'd')) +- digit = digit - 'a' + 12; +- else { +- ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); +- return 0; +- } +- +- rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); +- +- rtpheader = (unsigned int *)data; +- rtpheader[1] = htonl(rtp->lastdigitts); +- rtpheader[2] = htonl(rtp->ssrc); +- rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); +- /* Set end bit */ +- rtpheader[3] |= htonl((1 << 23)); +- +- /* Send 3 termination packets */ +- for (i = 0; i < 3; i++) { +- rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); +- res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); +- rtp->seqno++; +- if (res < 0) +- ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", +- ast_inet_ntoa(rtp->them.sin_addr), +- ntohs(rtp->them.sin_port), strerror(errno)); +- if (rtp_debug_test_addr(&rtp->them)) +- ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", +- ast_inet_ntoa(rtp->them.sin_addr), +- ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); +- } +- rtp->lastts += rtp->send_duration; +- rtp->sending_digit = 0; +- rtp->send_digit = 0; +- +- return res; +-} +- +-/*! \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */ +-int ast_rtcp_send_h261fur(void *data) +-{ +- struct ast_rtp *rtp = data; +- int res; +- +- rtp->rtcp->sendfur = 1; +- res = ast_rtcp_write(data); +- +- return res; +-} +- +-/*! \brief Send RTCP sender's report */ +-static int ast_rtcp_write_sr(const void *data) +-{ +- struct ast_rtp *rtp = (struct ast_rtp *)data; +- int res; +- int len = 0; +- struct timeval now; +- unsigned int now_lsw; +- unsigned int now_msw; +- unsigned int *rtcpheader; +- unsigned int lost; +- unsigned int extended; +- unsigned int expected; +- unsigned int expected_interval; +- unsigned int received_interval; +- int lost_interval; +- int fraction; +- struct timeval dlsr; +- char bdata[512]; +- +- /* Commented condition is always not NULL if rtp->rtcp is not NULL */ +- if (!rtp || !rtp->rtcp/* || (&rtp->rtcp->them.sin_addr == 0)*/) +- return 0; +- +- if (!rtp->rtcp->them.sin_addr.s_addr) { /* This'll stop rtcp for this rtp session */ +- ast_verbose("RTCP SR transmission error, rtcp halted\n"); +- AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); +- return 0; +- } +- +- gettimeofday(&now, NULL); +- timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/ +- rtcpheader = (unsigned int *)bdata; +- rtcpheader[1] = htonl(rtp->ssrc); /* Our SSRC */ +- rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/ +- rtcpheader[3] = htonl(now_lsw); /* now, LSW */ +- rtcpheader[4] = htonl(rtp->lastts); /* FIXME shouldn't be that, it should be now */ +- rtcpheader[5] = htonl(rtp->txcount); /* No. packets sent */ +- rtcpheader[6] = htonl(rtp->txoctetcount); /* No. bytes sent */ +- len += 28; +- +- extended = rtp->cycles + rtp->lastrxseqno; +- expected = extended - rtp->seedrxseqno + 1; +- if (rtp->rxcount > expected) +- expected += rtp->rxcount - expected; +- lost = expected - rtp->rxcount; +- expected_interval = expected - rtp->rtcp->expected_prior; +- rtp->rtcp->expected_prior = expected; +- received_interval = rtp->rxcount - rtp->rtcp->received_prior; +- rtp->rtcp->received_prior = rtp->rxcount; +- lost_interval = expected_interval - received_interval; +- if (expected_interval == 0 || lost_interval <= 0) +- fraction = 0; +- else +- fraction = (lost_interval << 8) / expected_interval; +- timersub(&now, &rtp->rtcp->rxlsr, &dlsr); +- rtcpheader[7] = htonl(rtp->themssrc); +- rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); +- rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); +- rtcpheader[10] = htonl((unsigned int)(rtp->rxjitter * 65536.)); +- rtcpheader[11] = htonl(rtp->rtcp->themrxlsr); +- rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); +- len += 24; +- +- rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1)); +- +- if (rtp->rtcp->sendfur) { +- rtcpheader[13] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); +- rtcpheader[14] = htonl(rtp->ssrc); /* Our SSRC */ +- len += 8; +- rtp->rtcp->sendfur = 0; +- } +- +- /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ +- /* it can change mid call, and SDES can't) */ +- rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); +- rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ +- rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ +- len += 12; +- +- res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); +- if (res < 0) { +- ast_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno)); +- AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); +- return 0; +- } +- +- /* FIXME Don't need to get a new one */ +- gettimeofday(&rtp->rtcp->txlsr, NULL); +- rtp->rtcp->sr_count++; +- +- rtp->rtcp->lastsrtxcount = rtp->txcount; +- +- if (rtcp_debug_test_addr(&rtp->rtcp->them)) { +- ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); +- ast_verbose(" Our SSRC: %u\n", rtp->ssrc); +- ast_verbose(" Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096); +- ast_verbose(" Sent(RTP): %u\n", rtp->lastts); +- ast_verbose(" Sent packets: %u\n", rtp->txcount); +- ast_verbose(" Sent octets: %u\n", rtp->txoctetcount); +- ast_verbose(" Report block:\n"); +- ast_verbose(" Fraction lost: %u\n", fraction); +- ast_verbose(" Cumulative loss: %u\n", lost); +- ast_verbose(" IA jitter: %.4f\n", rtp->rxjitter); +- ast_verbose(" Their last SR: %u\n", rtp->rtcp->themrxlsr); +- ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0)); +- } +- manager_event(EVENT_FLAG_REPORTING, "RTCPSent", "To: %s:%d\r\n" +- "OurSSRC: %u\r\n" +- "SentNTP: %u.%010u\r\n" +- "SentRTP: %u\r\n" +- "SentPackets: %u\r\n" +- "SentOctets: %u\r\n" +- "ReportBlock:\r\n" +- "FractionLost: %u\r\n" +- "CumulativeLoss: %u\r\n" +- "IAJitter: %.4f\r\n" +- "TheirLastSR: %u\r\n" +- "DLSR: %4.4f (sec)\r\n", +- ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), +- rtp->ssrc, +- (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096, +- rtp->lastts, +- rtp->txcount, +- rtp->txoctetcount, +- fraction, +- lost, +- rtp->rxjitter, +- rtp->rtcp->themrxlsr, +- (double)(ntohl(rtcpheader[12])/65536.0)); +- return res; +-} +- +-/*! \brief Send RTCP recipient's report */ +-static int ast_rtcp_write_rr(const void *data) +-{ +- struct ast_rtp *rtp = (struct ast_rtp *)data; +- int res; +- int len = 32; +- unsigned int lost; +- unsigned int extended; +- unsigned int expected; +- unsigned int expected_interval; +- unsigned int received_interval; +- int lost_interval; +- struct timeval now; +- unsigned int *rtcpheader; +- char bdata[1024]; +- struct timeval dlsr; +- int fraction; +- +- double rxlost_current; +- +- if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0)) +- return 0; +- +- if (!rtp->rtcp->them.sin_addr.s_addr) { +- ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted\n"); +- AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); +- return 0; +- } +- +- extended = rtp->cycles + rtp->lastrxseqno; +- expected = extended - rtp->seedrxseqno + 1; +- lost = expected - rtp->rxcount; +- expected_interval = expected - rtp->rtcp->expected_prior; +- rtp->rtcp->expected_prior = expected; +- received_interval = rtp->rxcount - rtp->rtcp->received_prior; +- rtp->rtcp->received_prior = rtp->rxcount; +- lost_interval = expected_interval - received_interval; +- +- if (lost_interval <= 0) +- rtp->rtcp->rxlost = 0; +- else rtp->rtcp->rxlost = rtp->rtcp->rxlost; +- if (rtp->rtcp->rxlost_count == 0) +- rtp->rtcp->minrxlost = rtp->rtcp->rxlost; +- if (lost_interval < rtp->rtcp->minrxlost) +- rtp->rtcp->minrxlost = rtp->rtcp->rxlost; +- if (lost_interval > rtp->rtcp->maxrxlost) +- rtp->rtcp->maxrxlost = rtp->rtcp->rxlost; +- +- rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->rxlost_count); +- rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->normdev_rxlost, rxlost_current, rtp->rtcp->rxlost_count); +- rtp->rtcp->normdev_rxlost = rxlost_current; +- rtp->rtcp->rxlost_count++; +- +- if (expected_interval == 0 || lost_interval <= 0) +- fraction = 0; +- else +- fraction = (lost_interval << 8) / expected_interval; +- gettimeofday(&now, NULL); +- timersub(&now, &rtp->rtcp->rxlsr, &dlsr); +- rtcpheader = (unsigned int *)bdata; +- rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1)); +- rtcpheader[1] = htonl(rtp->ssrc); +- rtcpheader[2] = htonl(rtp->themssrc); +- rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); +- rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); +- rtcpheader[5] = htonl((unsigned int)(rtp->rxjitter * 65536.)); +- rtcpheader[6] = htonl(rtp->rtcp->themrxlsr); +- rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); +- +- if (rtp->rtcp->sendfur) { +- rtcpheader[8] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); /* Header from page 36 in RFC 3550 */ +- rtcpheader[9] = htonl(rtp->ssrc); /* Our SSRC */ +- len += 8; +- rtp->rtcp->sendfur = 0; +- } +- +- /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos +- it can change mid call, and SDES can't) */ +- rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); +- rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ +- rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ +- len += 12; +- +- res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); +- +- if (res < 0) { +- ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno)); +- /* Remove the scheduler */ +- AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); +- return 0; +- } +- +- rtp->rtcp->rr_count++; +- +- if (rtcp_debug_test_addr(&rtp->rtcp->them)) { +- ast_verbose("\n* Sending RTCP RR to %s:%d\n" +- " Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n" +- " IA jitter: %.4f\n" +- " Their last SR: %u\n" +- " DLSR: %4.4f (sec)\n\n", +- ast_inet_ntoa(rtp->rtcp->them.sin_addr), +- ntohs(rtp->rtcp->them.sin_port), +- rtp->ssrc, rtp->themssrc, fraction, lost, +- rtp->rxjitter, +- rtp->rtcp->themrxlsr, +- (double)(ntohl(rtcpheader[7])/65536.0)); +- } +- +- return res; +-} +- +-/*! \brief Write and RTCP packet to the far end +- * \note Decide if we are going to send an SR (with Reception Block) or RR +- * RR is sent if we have not sent any rtp packets in the previous interval */ +-static int ast_rtcp_write(const void *data) +-{ +- struct ast_rtp *rtp = (struct ast_rtp *)data; +- int res; +- +- if (!rtp || !rtp->rtcp) +- return 0; +- +- if (rtp->txcount > rtp->rtcp->lastsrtxcount) +- res = ast_rtcp_write_sr(data); +- else +- res = ast_rtcp_write_rr(data); +- +- return res; +-} +- +-/*! \brief generate comfort noice (CNG) */ +-int ast_rtp_sendcng(struct ast_rtp *rtp, int level) +-{ +- unsigned int *rtpheader; +- int hdrlen = 12; +- int res; +- int payload; +- char data[256]; +- level = 127 - (level & 0x7f); +- payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_CN); +- +- /* If we have no peer, return immediately */ +- if (!rtp->them.sin_addr.s_addr) +- return 0; +- +- rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); +- +- /* Get a pointer to the header */ +- rtpheader = (unsigned int *)data; +- rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno++)); +- rtpheader[1] = htonl(rtp->lastts); +- rtpheader[2] = htonl(rtp->ssrc); +- data[12] = level; +- if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { +- res = sendto(rtp->s, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); +- if (res <0) +- ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); +- if (rtp_debug_test_addr(&rtp->them)) +- ast_verbose("Sent Comfort Noise RTP packet to %s:%u (type %d, seq %u, ts %u, len %d)\n" +- , ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen); +- +- } +- return 0; +-} +- +-/*! \brief Write RTP packet with audio or video media frames into UDP packet */ +-static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec) +-{ +- unsigned char *rtpheader; +- int hdrlen = 12; +- int res; +- unsigned int ms; +- int pred; +- int mark = 0; +- +- if (rtp->sending_digit) { +- return 0; +- } +- +- ms = calc_txstamp(rtp, &f->delivery); +- /* Default prediction */ +- if (f->frametype == AST_FRAME_VOICE) { +- pred = rtp->lastts + f->samples; +- +- /* Re-calculate last TS */ +- rtp->lastts = rtp->lastts + ms * 8; +- if (ast_tvzero(f->delivery)) { +- /* If this isn't an absolute delivery time, Check if it is close to our prediction, +- and if so, go with our prediction */ +- if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW) +- rtp->lastts = pred; +- else { +- ast_debug(3, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms); +- mark = 1; +- } +- } +- } else if (f->frametype == AST_FRAME_VIDEO) { +- mark = f->subclass & 0x1; +- pred = rtp->lastovidtimestamp + f->samples; +- /* Re-calculate last TS */ +- rtp->lastts = rtp->lastts + ms * 90; +- /* If it's close to our prediction, go for it */ +- if (ast_tvzero(f->delivery)) { +- if (abs(rtp->lastts - pred) < 7200) { +- rtp->lastts = pred; +- rtp->lastovidtimestamp += f->samples; +- } else { +- ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples); +- rtp->lastovidtimestamp = rtp->lastts; +- } +- } +- } else { +- pred = rtp->lastotexttimestamp + f->samples; +- /* Re-calculate last TS */ +- rtp->lastts = rtp->lastts + ms * 90; +- /* If it's close to our prediction, go for it */ +- if (ast_tvzero(f->delivery)) { +- if (abs(rtp->lastts - pred) < 7200) { +- rtp->lastts = pred; +- rtp->lastotexttimestamp += f->samples; +- } else { +- ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples); +- rtp->lastotexttimestamp = rtp->lastts; +- } +- } +- } +- +- /* If we have been explicitly told to set the marker bit do so */ +- if (rtp->set_marker_bit) { +- mark = 1; +- rtp->set_marker_bit = 0; +- } +- +- /* If the timestamp for non-digit packets has moved beyond the timestamp +- for digits, update the digit timestamp. +- */ +- if (rtp->lastts > rtp->lastdigitts) +- rtp->lastdigitts = rtp->lastts; +- +- if (ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO)) +- rtp->lastts = f->ts * 8; +- +- /* Get a pointer to the header */ +- rtpheader = (unsigned char *)(f->data.ptr - hdrlen); +- +- put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23))); +- put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts)); +- put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); +- +- if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { +- res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); +- if (res < 0) { +- if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { +- ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); +- } else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) { +- /* Only give this error message once if we are not RTP debugging */ +- if (option_debug || rtpdebug) +- ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); +- ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN); +- } +- } else { +- rtp->txcount++; +- rtp->txoctetcount +=(res - hdrlen); +- +- /* Do not schedule RR if RTCP isn't run */ +- if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) { +- rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); +- } +- } +- +- if (rtp_debug_test_addr(&rtp->them)) +- ast_verbose("Sent RTP packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", +- ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), codec, rtp->seqno, rtp->lastts,res - hdrlen); +- } +- +- rtp->seqno++; +- +- return 0; +-} +- +-void ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs) +-{ +- struct ast_format_list current_format_old, current_format_new; +- +- /* if no packets have been sent through this session yet, then +- * changing preferences does not require any extra work +- */ +- if (rtp->lasttxformat == 0) { +- rtp->pref = *prefs; +- return; +- } +- +- current_format_old = ast_codec_pref_getsize(&rtp->pref, rtp->lasttxformat); +- +- rtp->pref = *prefs; +- +- current_format_new = ast_codec_pref_getsize(&rtp->pref, rtp->lasttxformat); +- +- /* if the framing desired for the current format has changed, we may have to create +- * or adjust the smoother for this session +- */ +- if ((current_format_new.inc_ms != 0) && +- (current_format_new.cur_ms != current_format_old.cur_ms)) { +- int new_size = (current_format_new.cur_ms * current_format_new.fr_len) / current_format_new.inc_ms; +- +- if (rtp->smoother) { +- ast_smoother_reconfigure(rtp->smoother, new_size); +- if (option_debug) { +- ast_log(LOG_DEBUG, "Adjusted smoother to %d ms and %d bytes\n", current_format_new.cur_ms, new_size); +- } +- } else { +- if (!(rtp->smoother = ast_smoother_new(new_size))) { +- ast_log(LOG_WARNING, "Unable to create smoother: format: %d ms: %d len: %d\n", rtp->lasttxformat, current_format_new.cur_ms, new_size); +- return; +- } +- if (current_format_new.flags) { +- ast_smoother_set_flags(rtp->smoother, current_format_new.flags); +- } +- if (option_debug) { +- ast_log(LOG_DEBUG, "Created smoother: format: %d ms: %d len: %d\n", rtp->lasttxformat, current_format_new.cur_ms, new_size); +- } +- } +- } +- +-} +- +-struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp) +-{ +- return &rtp->pref; +-} +- +-int ast_rtp_codec_getformat(int pt) +-{ +- if (pt < 0 || pt > MAX_RTP_PT) +- return 0; /* bogus payload type */ +- +- if (static_RTP_PT[pt].isAstFormat) +- return static_RTP_PT[pt].code; +- else +- return 0; +-} +- +-int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) +-{ +- struct ast_frame *f; +- int codec; +- int hdrlen = 12; +- int subclass; +- +- +- /* If we have no peer, return immediately */ +- if (!rtp->them.sin_addr.s_addr) +- return 0; +- +- /* If there is no data length, return immediately */ +- if (!_f->datalen && !rtp->red) +- return 0; +- +- /* Make sure we have enough space for RTP header */ +- if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO) && (_f->frametype != AST_FRAME_TEXT)) { +- ast_log(LOG_WARNING, "RTP can only send voice, video and text\n"); +- return -1; +- } +- +- if (rtp->red) { +- /* return 0; */ +- /* no primary data or generations to send */ +- if ((_f = red_t140_to_red(rtp->red)) == NULL) +- return 0; +- } +- +- /* The bottom bit of a video subclass contains the marker bit */ +- subclass = _f->subclass; +- if (_f->frametype == AST_FRAME_VIDEO) +- subclass &= ~0x1; +- +- codec = ast_rtp_lookup_code(rtp, 1, subclass); +- if (codec < 0) { +- ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(_f->subclass)); +- return -1; +- } +- +- if (rtp->lasttxformat != subclass) { +- /* New format, reset the smoother */ +- ast_debug(1, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass)); +- rtp->lasttxformat = subclass; +- if (rtp->smoother) +- ast_smoother_free(rtp->smoother); +- rtp->smoother = NULL; +- } +- +- if (!rtp->smoother) { +- struct ast_format_list fmt = ast_codec_pref_getsize(&rtp->pref, subclass); +- +- switch (subclass) { +- case AST_FORMAT_SPEEX: +- case AST_FORMAT_G723_1: +- case AST_FORMAT_SIREN7: +- case AST_FORMAT_SIREN14: +- /* these are all frame-based codecs and cannot be safely run through +- a smoother */ +- break; +- default: +- if (fmt.inc_ms) { /* if codec parameters is set / avoid division by zero */ +- if (!(rtp->smoother = ast_smoother_new((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms))) { +- ast_log(LOG_WARNING, "Unable to create smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); +- return -1; +- } +- if (fmt.flags) +- ast_smoother_set_flags(rtp->smoother, fmt.flags); +- ast_debug(1, "Created smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); +- } +- } +- } +- if (rtp->smoother) { +- if (ast_smoother_test_flag(rtp->smoother, AST_SMOOTHER_FLAG_BE)) { +- ast_smoother_feed_be(rtp->smoother, _f); +- } else { +- ast_smoother_feed(rtp->smoother, _f); +- } +- +- while ((f = ast_smoother_read(rtp->smoother)) && (f->data.ptr)) { +- if (f->subclass == AST_FORMAT_G722) { +- /* G.722 is silllllllllllllly */ +- f->samples /= 2; +- } +- +- ast_rtp_raw_write(rtp, f, codec); +- } +- } else { +- /* Don't buffer outgoing frames; send them one-per-packet: */ +- if (_f->offset < hdrlen) +- f = ast_frdup(_f); /*! \bug XXX this might never be free'd. Why do we do this? */ +- else +- f = _f; +- if (f->data.ptr) +- ast_rtp_raw_write(rtp, f, codec); +- if (f != _f) +- ast_frfree(f); +- } +- +- return 0; +-} +- +-/*! \brief Unregister interface to channel driver */ +-void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto) +-{ +- AST_RWLIST_WRLOCK(&protos); +- AST_RWLIST_REMOVE(&protos, proto, list); +- AST_RWLIST_UNLOCK(&protos); +-} +- +-/*! \brief Register interface to channel driver */ +-int ast_rtp_proto_register(struct ast_rtp_protocol *proto) +-{ +- struct ast_rtp_protocol *cur; +- +- AST_RWLIST_WRLOCK(&protos); +- AST_RWLIST_TRAVERSE(&protos, cur, list) { +- if (!strcmp(cur->type, proto->type)) { +- ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type); +- AST_RWLIST_UNLOCK(&protos); +- return -1; +- } +- } +- AST_RWLIST_INSERT_HEAD(&protos, proto, list); +- AST_RWLIST_UNLOCK(&protos); +- +- return 0; +-} +- +-/*! \brief Bridge loop for true native bridge (reinvite) */ +-static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp *tp0, struct ast_rtp *tp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) +-{ +- struct ast_frame *fr = NULL; +- struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, }; +- int oldcodec0 = codec0, oldcodec1 = codec1; +- struct sockaddr_in ac1 = {0,}, vac1 = {0,}, tac1 = {0,}, ac0 = {0,}, vac0 = {0,}, tac0 = {0,}; +- struct sockaddr_in t1 = {0,}, vt1 = {0,}, tt1 = {0,}, t0 = {0,}, vt0 = {0,}, tt0 = {0,}; +- +- /* Set it up so audio goes directly between the two endpoints */ +- +- /* Test the first channel */ +- if (!(pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) { +- ast_rtp_get_peer(p1, &ac1); +- if (vp1) +- ast_rtp_get_peer(vp1, &vac1); +- if (tp1) +- ast_rtp_get_peer(tp1, &tac1); +- } else +- ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name); +- +- /* Test the second channel */ +- if (!(pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) { +- ast_rtp_get_peer(p0, &ac0); +- if (vp0) +- ast_rtp_get_peer(vp0, &vac0); +- if (tp0) +- ast_rtp_get_peer(tp0, &tac0); +- } else +- ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name); +- +- /* Now we can unlock and move into our loop */ +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- +- ast_poll_channel_add(c0, c1); +- +- /* Throw our channels into the structure and enter the loop */ +- cs[0] = c0; +- cs[1] = c1; +- cs[2] = NULL; +- for (;;) { +- /* Check if anything changed */ +- if ((c0->tech_pvt != pvt0) || +- (c1->tech_pvt != pvt1) || +- (c0->masq || c0->masqr || c1->masq || c1->masqr) || +- (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { +- ast_debug(1, "Oooh, something is weird, backing out\n"); +- if (c0->tech_pvt == pvt0) +- if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); +- if (c1->tech_pvt == pvt1) +- if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); +- ast_poll_channel_del(c0, c1); +- return AST_BRIDGE_RETRY; +- } +- +- /* Check if they have changed their address */ +- ast_rtp_get_peer(p1, &t1); +- if (vp1) +- ast_rtp_get_peer(vp1, &vt1); +- if (tp1) +- ast_rtp_get_peer(tp1, &tt1); +- if (pr1->get_codec) +- codec1 = pr1->get_codec(c1); +- ast_rtp_get_peer(p0, &t0); +- if (vp0) +- ast_rtp_get_peer(vp0, &vt0); +- if (tp0) +- ast_rtp_get_peer(tp0, &tt0); +- if (pr0->get_codec) +- codec0 = pr0->get_codec(c0); +- if ((inaddrcmp(&t1, &ac1)) || +- (vp1 && inaddrcmp(&vt1, &vac1)) || +- (tp1 && inaddrcmp(&tt1, &tac1)) || +- (codec1 != oldcodec1)) { +- ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n", +- c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1); +- ast_debug(2, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n", +- c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1); +- ast_debug(2, "Oooh, '%s' changed end taddress to %s:%d (format %d)\n", +- c1->name, ast_inet_ntoa(tt1.sin_addr), ntohs(tt1.sin_port), codec1); +- ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", +- c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1); +- ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", +- c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1); +- ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", +- c1->name, ast_inet_ntoa(tac1.sin_addr), ntohs(tac1.sin_port), oldcodec1); +- if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, tt1.sin_addr.s_addr ? tp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE))) +- ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name); +- memcpy(&ac1, &t1, sizeof(ac1)); +- memcpy(&vac1, &vt1, sizeof(vac1)); +- memcpy(&tac1, &tt1, sizeof(tac1)); +- oldcodec1 = codec1; +- } +- if ((inaddrcmp(&t0, &ac0)) || +- (vp0 && inaddrcmp(&vt0, &vac0)) || +- (tp0 && inaddrcmp(&tt0, &tac0))) { +- ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n", +- c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0); +- ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", +- c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0); +- if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, tt0.sin_addr.s_addr ? tp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE))) +- ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name); +- memcpy(&ac0, &t0, sizeof(ac0)); +- memcpy(&vac0, &vt0, sizeof(vac0)); +- memcpy(&tac0, &tt0, sizeof(tac0)); +- oldcodec0 = codec0; +- } +- +- /* Wait for frame to come in on the channels */ +- if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { +- if (!timeoutms) { +- if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); +- if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); +- return AST_BRIDGE_RETRY; +- } +- ast_debug(1, "Ooh, empty read...\n"); +- if (ast_check_hangup(c0) || ast_check_hangup(c1)) +- break; +- continue; +- } +- fr = ast_read(who); +- other = (who == c0) ? c1 : c0; +- if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && +- (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || +- ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) { +- /* Break out of bridge */ +- *fo = fr; +- *rc = who; +- ast_debug(1, "Oooh, got a %s\n", fr ? "digit" : "hangup"); +- if (c0->tech_pvt == pvt0) +- if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); +- if (c1->tech_pvt == pvt1) +- if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); +- ast_poll_channel_del(c0, c1); +- return AST_BRIDGE_COMPLETE; +- } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { +- if ((fr->subclass == AST_CONTROL_HOLD) || +- (fr->subclass == AST_CONTROL_UNHOLD) || +- (fr->subclass == AST_CONTROL_VIDUPDATE) || +- (fr->subclass == AST_CONTROL_T38) || +- (fr->subclass == AST_CONTROL_SRCUPDATE)) { +- if (fr->subclass == AST_CONTROL_HOLD) { +- /* If we someone went on hold we want the other side to reinvite back to us */ +- if (who == c0) +- pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0); +- else +- pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0); +- } else if (fr->subclass == AST_CONTROL_UNHOLD) { +- /* If they went off hold they should go back to being direct */ +- if (who == c0) +- pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)); +- else +- pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)); +- } +- /* Update local address information */ +- ast_rtp_get_peer(p0, &t0); +- memcpy(&ac0, &t0, sizeof(ac0)); +- ast_rtp_get_peer(p1, &t1); +- memcpy(&ac1, &t1, sizeof(ac1)); +- /* Update codec information */ +- if (pr0->get_codec && c0->tech_pvt) +- oldcodec0 = codec0 = pr0->get_codec(c0); +- if (pr1->get_codec && c1->tech_pvt) +- oldcodec1 = codec1 = pr1->get_codec(c1); +- ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); +- ast_frfree(fr); +- } else { +- *fo = fr; +- *rc = who; +- ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); +- return AST_BRIDGE_COMPLETE; +- } +- } else { +- if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || +- (fr->frametype == AST_FRAME_DTMF_END) || +- (fr->frametype == AST_FRAME_VOICE) || +- (fr->frametype == AST_FRAME_VIDEO) || +- (fr->frametype == AST_FRAME_IMAGE) || +- (fr->frametype == AST_FRAME_HTML) || +- (fr->frametype == AST_FRAME_MODEM) || +- (fr->frametype == AST_FRAME_TEXT)) { +- ast_write(other, fr); +- } +- ast_frfree(fr); +- } +- /* Swap priority */ +-#ifndef HAVE_EPOLL +- cs[2] = cs[0]; +- cs[0] = cs[1]; +- cs[1] = cs[2]; +-#endif +- } +- +- ast_poll_channel_del(c0, c1); +- +- if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); +- if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) +- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); +- +- return AST_BRIDGE_FAILED; +-} +- +-/*! \brief P2P RTP Callback */ +-#ifdef P2P_INTENSE +-static int p2p_rtp_callback(int *id, int fd, short events, void *cbdata) +-{ +- int res = 0, hdrlen = 12; +- struct sockaddr_in sin; +- socklen_t len; +- unsigned int *header; +- struct ast_rtp *rtp = cbdata, *bridged = NULL; +- +- if (!rtp) +- return 1; +- +- len = sizeof(sin); +- if ((res = recvfrom(fd, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0) +- return 1; +- +- header = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); +- +- /* If NAT support is turned on, then see if we need to change their address */ +- if ((rtp->nat) && +- ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || +- (rtp->them.sin_port != sin.sin_port))) { +- rtp->them = sin; +- rtp->rxseqno = 0; +- ast_set_flag(rtp, FLAG_NAT_ACTIVE); +- if (option_debug || rtpdebug) +- ast_debug(0, "P2P RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); +- } +- +- /* Write directly out to other RTP stream if bridged */ +- if ((bridged = ast_rtp_get_bridged(rtp))) +- bridge_p2p_rtp_write(rtp, bridged, header, res, hdrlen); +- +- return 1; +-} +- +-/*! \brief Helper function to switch a channel and RTP stream into callback mode */ +-static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod) +-{ +- /* If we need DTMF, are looking for STUN, or we have no IO structure then we can't do direct callback */ +- if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) || ast_test_flag(rtp, FLAG_HAS_STUN) || !rtp->io) +- return 0; +- +- /* If the RTP structure is already in callback mode, remove it temporarily */ +- if (rtp->ioid) { +- ast_io_remove(rtp->io, rtp->ioid); +- rtp->ioid = NULL; +- } +- +- /* Steal the file descriptors from the channel */ +- chan->fds[0] = -1; +- +- /* Now, fire up callback mode */ +- iod[0] = ast_io_add(rtp->io, ast_rtp_fd(rtp), p2p_rtp_callback, AST_IO_IN, rtp); +- +- return 1; +-} +-#else +-static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod) +-{ +- return 0; +-} +-#endif +- +-/*! \brief Helper function to switch a channel and RTP stream out of callback mode */ +-static int p2p_callback_disable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod) +-{ +- ast_channel_lock(chan); +- +- /* Remove the callback from the IO context */ +- ast_io_remove(rtp->io, iod[0]); +- +- /* Restore file descriptors */ +- chan->fds[0] = ast_rtp_fd(rtp); +- ast_channel_unlock(chan); +- +- /* Restore callback mode if previously used */ +- if (ast_test_flag(rtp, FLAG_CALLBACK_MODE)) +- rtp->ioid = ast_io_add(rtp->io, ast_rtp_fd(rtp), rtpread, AST_IO_IN, rtp); +- +- return 0; +-} +- +-/*! \brief Helper function that sets what an RTP structure is bridged to */ +-static void p2p_set_bridge(struct ast_rtp *rtp0, struct ast_rtp *rtp1) +-{ +- rtp_bridge_lock(rtp0); +- rtp0->bridged = rtp1; +- rtp_bridge_unlock(rtp0); +-} +- +-/*! \brief Bridge loop for partial native bridge (packet2packet) +- +- In p2p mode, Asterisk is a very basic RTP proxy, just forwarding whatever +- rtp/rtcp we get in to the channel. +- \note this currently only works for Audio +-*/ +-static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) +-{ +- struct ast_frame *fr = NULL; +- struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, }; +- int *p0_iod[2] = {NULL, NULL}, *p1_iod[2] = {NULL, NULL}; +- int p0_callback = 0, p1_callback = 0; +- enum ast_bridge_result res = AST_BRIDGE_FAILED; +- +- /* Okay, setup each RTP structure to do P2P forwarding */ +- ast_clear_flag(p0, FLAG_P2P_SENT_MARK); +- p2p_set_bridge(p0, p1); +- ast_clear_flag(p1, FLAG_P2P_SENT_MARK); +- p2p_set_bridge(p1, p0); +- +- /* Activate callback modes if possible */ +- p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]); +- p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]); +- +- /* Now let go of the channel locks and be on our way */ +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- +- ast_poll_channel_add(c0, c1); +- +- /* Go into a loop forwarding frames until we don't need to anymore */ +- cs[0] = c0; +- cs[1] = c1; +- cs[2] = NULL; +- for (;;) { +- /* If the underlying formats have changed force this bridge to break */ +- if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) { +- ast_debug(3, "p2p-rtp-bridge: Oooh, formats changed, backing out\n"); +- res = AST_BRIDGE_FAILED_NOWARN; +- break; +- } +- /* Check if anything changed */ +- if ((c0->tech_pvt != pvt0) || +- (c1->tech_pvt != pvt1) || +- (c0->masq || c0->masqr || c1->masq || c1->masqr) || +- (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { +- ast_debug(3, "p2p-rtp-bridge: Oooh, something is weird, backing out\n"); +- /* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */ +- if ((c0->masq || c0->masqr) && (fr = ast_read(c0))) +- ast_frfree(fr); +- if ((c1->masq || c1->masqr) && (fr = ast_read(c1))) +- ast_frfree(fr); +- res = AST_BRIDGE_RETRY; +- break; +- } +- /* Wait on a channel to feed us a frame */ +- if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { +- if (!timeoutms) { +- res = AST_BRIDGE_RETRY; +- break; +- } +- if (option_debug > 2) +- ast_log(LOG_NOTICE, "p2p-rtp-bridge: Ooh, empty read...\n"); +- if (ast_check_hangup(c0) || ast_check_hangup(c1)) +- break; +- continue; +- } +- /* Read in frame from channel */ +- fr = ast_read(who); +- other = (who == c0) ? c1 : c0; +- /* Depending on the frame we may need to break out of our bridge */ +- if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && +- ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) | +- ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) { +- /* Record received frame and who */ +- *fo = fr; +- *rc = who; +- ast_debug(3, "p2p-rtp-bridge: Ooh, got a %s\n", fr ? "digit" : "hangup"); +- res = AST_BRIDGE_COMPLETE; +- break; +- } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { +- if ((fr->subclass == AST_CONTROL_HOLD) || +- (fr->subclass == AST_CONTROL_UNHOLD) || +- (fr->subclass == AST_CONTROL_VIDUPDATE) || +- (fr->subclass == AST_CONTROL_T38) || +- (fr->subclass == AST_CONTROL_SRCUPDATE)) { +- /* If we are going on hold, then break callback mode and P2P bridging */ +- if (fr->subclass == AST_CONTROL_HOLD) { +- if (p0_callback) +- p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]); +- if (p1_callback) +- p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]); +- p2p_set_bridge(p0, NULL); +- p2p_set_bridge(p1, NULL); +- } else if (fr->subclass == AST_CONTROL_UNHOLD) { +- /* If we are off hold, then go back to callback mode and P2P bridging */ +- ast_clear_flag(p0, FLAG_P2P_SENT_MARK); +- p2p_set_bridge(p0, p1); +- ast_clear_flag(p1, FLAG_P2P_SENT_MARK); +- p2p_set_bridge(p1, p0); +- p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]); +- p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]); +- } +- ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); +- ast_frfree(fr); +- } else { +- *fo = fr; +- *rc = who; +- ast_debug(3, "p2p-rtp-bridge: Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); +- res = AST_BRIDGE_COMPLETE; +- break; +- } +- } else { +- if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || +- (fr->frametype == AST_FRAME_DTMF_END) || +- (fr->frametype == AST_FRAME_VOICE) || +- (fr->frametype == AST_FRAME_VIDEO) || +- (fr->frametype == AST_FRAME_IMAGE) || +- (fr->frametype == AST_FRAME_HTML) || +- (fr->frametype == AST_FRAME_MODEM) || +- (fr->frametype == AST_FRAME_TEXT)) { +- ast_write(other, fr); +- } +- +- ast_frfree(fr); +- } +- /* Swap priority */ +-#ifndef HAVE_EPOLL +- cs[2] = cs[0]; +- cs[0] = cs[1]; +- cs[1] = cs[2]; +-#endif +- } +- +- /* If we are totally avoiding the core, then restore our link to it */ +- if (p0_callback) +- p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]); +- if (p1_callback) +- p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]); +- +- /* Break out of the direct bridge */ +- p2p_set_bridge(p0, NULL); +- p2p_set_bridge(p1, NULL); +- +- ast_poll_channel_del(c0, c1); +- +- return res; +-} +- +-/*! \page AstRTPbridge The Asterisk RTP bridge +- The RTP bridge is called from the channel drivers that are using the RTP +- subsystem in Asterisk - like SIP, H.323 and Jingle/Google Talk. +- +- This bridge aims to offload the Asterisk server by setting up +- the media stream directly between the endpoints, keeping the +- signalling in Asterisk. +- +- It checks with the channel driver, using a callback function, if +- there are possibilities for a remote bridge. +- +- If this fails, the bridge hands off to the core bridge. Reasons +- can be NAT support needed, DTMF features in audio needed by +- the PBX for transfers or spying/monitoring on channels. +- +- If transcoding is needed - we can't do a remote bridge. +- If only NAT support is needed, we're using Asterisk in +- RTP proxy mode with the p2p RTP bridge, basically +- forwarding incoming audio packets to the outbound +- stream on a network level. +- +- References: +- - ast_rtp_bridge() +- - ast_channel_early_bridge() +- - ast_channel_bridge() +- - rtp.c +- - rtp.h +-*/ +-/*! \brief Bridge calls. If possible and allowed, initiate +- re-invite so the peers exchange media directly outside +- of Asterisk. +-*/ +-enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) +-{ +- struct ast_rtp *p0 = NULL, *p1 = NULL; /* Audio RTP Channels */ +- struct ast_rtp *vp0 = NULL, *vp1 = NULL; /* Video RTP channels */ +- struct ast_rtp *tp0 = NULL, *tp1 = NULL; /* Text RTP channels */ +- struct ast_rtp_protocol *pr0 = NULL, *pr1 = NULL; +- enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED, text_p0_res = AST_RTP_GET_FAILED; +- enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED, text_p1_res = AST_RTP_GET_FAILED; +- enum ast_bridge_result res = AST_BRIDGE_FAILED; +- int codec0 = 0, codec1 = 0; +- void *pvt0 = NULL, *pvt1 = NULL; +- +- /* Lock channels */ +- ast_channel_lock(c0); +- while (ast_channel_trylock(c1)) { +- ast_channel_unlock(c0); +- usleep(1); +- ast_channel_lock(c0); +- } +- +- /* Ensure neither channel got hungup during lock avoidance */ +- if (ast_check_hangup(c0) || ast_check_hangup(c1)) { +- ast_log(LOG_WARNING, "Got hangup while attempting to bridge '%s' and '%s'\n", c0->name, c1->name); +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED; +- } +- +- /* Find channel driver interfaces */ +- if (!(pr0 = get_proto(c0))) { +- ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name); +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED; +- } +- if (!(pr1 = get_proto(c1))) { +- ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name); +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED; +- } +- +- /* Get channel specific interface structures */ +- pvt0 = c0->tech_pvt; +- pvt1 = c1->tech_pvt; +- +- /* Get audio and video interface (if native bridge is possible) */ +- audio_p0_res = pr0->get_rtp_info(c0, &p0); +- video_p0_res = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0, &vp0) : AST_RTP_GET_FAILED; +- text_p0_res = pr0->get_trtp_info ? pr0->get_trtp_info(c0, &vp0) : AST_RTP_GET_FAILED; +- audio_p1_res = pr1->get_rtp_info(c1, &p1); +- video_p1_res = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1, &vp1) : AST_RTP_GET_FAILED; +- text_p1_res = pr1->get_trtp_info ? pr1->get_trtp_info(c1, &vp1) : AST_RTP_GET_FAILED; +- +- /* If we are carrying video, and both sides are not reinviting... then fail the native bridge */ +- if (video_p0_res != AST_RTP_GET_FAILED && (audio_p0_res != AST_RTP_TRY_NATIVE || video_p0_res != AST_RTP_TRY_NATIVE)) +- audio_p0_res = AST_RTP_GET_FAILED; +- if (video_p1_res != AST_RTP_GET_FAILED && (audio_p1_res != AST_RTP_TRY_NATIVE || video_p1_res != AST_RTP_TRY_NATIVE)) +- audio_p1_res = AST_RTP_GET_FAILED; +- +- /* Check if a bridge is possible (partial/native) */ +- if (audio_p0_res == AST_RTP_GET_FAILED || audio_p1_res == AST_RTP_GET_FAILED) { +- /* Somebody doesn't want to play... */ +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED_NOWARN; +- } +- +- /* If we need to feed DTMF frames into the core then only do a partial native bridge */ +- if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) { +- ast_set_flag(p0, FLAG_P2P_NEED_DTMF); +- audio_p0_res = AST_RTP_TRY_PARTIAL; +- } +- +- if (ast_test_flag(p1, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) { +- ast_set_flag(p1, FLAG_P2P_NEED_DTMF); +- audio_p1_res = AST_RTP_TRY_PARTIAL; +- } +- +- /* If both sides are not using the same method of DTMF transmission +- * (ie: one is RFC2833, other is INFO... then we can not do direct media. +- * -------------------------------------------------- +- * | DTMF Mode | HAS_DTMF | Accepts Begin Frames | +- * |-----------|------------|-----------------------| +- * | Inband | False | True | +- * | RFC2833 | True | True | +- * | SIP INFO | False | False | +- * -------------------------------------------------- +- * However, if DTMF from both channels is being monitored by the core, then +- * we can still do packet-to-packet bridging, because passing through the +- * core will handle DTMF mode translation. +- */ +- if ((ast_test_flag(p0, FLAG_HAS_DTMF) != ast_test_flag(p1, FLAG_HAS_DTMF)) || +- (!c0->tech->send_digit_begin != !c1->tech->send_digit_begin)) { +- if (!ast_test_flag(p0, FLAG_P2P_NEED_DTMF) || !ast_test_flag(p1, FLAG_P2P_NEED_DTMF)) { +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED_NOWARN; +- } +- audio_p0_res = AST_RTP_TRY_PARTIAL; +- audio_p1_res = AST_RTP_TRY_PARTIAL; +- } +- +- /* If we need to feed frames into the core don't do a P2P bridge */ +- if ((audio_p0_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p0, FLAG_P2P_NEED_DTMF)) || +- (audio_p1_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p1, FLAG_P2P_NEED_DTMF))) { +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED_NOWARN; +- } +- +- /* Get codecs from both sides */ +- codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0; +- codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0; +- if (codec0 && codec1 && !(codec0 & codec1)) { +- /* Hey, we can't do native bridging if both parties speak different codecs */ +- ast_debug(3, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1); +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED_NOWARN; +- } +- +- /* If either side can only do a partial bridge, then don't try for a true native bridge */ +- if (audio_p0_res == AST_RTP_TRY_PARTIAL || audio_p1_res == AST_RTP_TRY_PARTIAL) { +- struct ast_format_list fmt0, fmt1; +- +- /* In order to do Packet2Packet bridging both sides must be in the same rawread/rawwrite */ +- if (c0->rawreadformat != c1->rawwriteformat || c1->rawreadformat != c0->rawwriteformat) { +- ast_debug(1, "Cannot packet2packet bridge - raw formats are incompatible\n"); +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED_NOWARN; +- } +- /* They must also be using the same packetization */ +- fmt0 = ast_codec_pref_getsize(&p0->pref, c0->rawreadformat); +- fmt1 = ast_codec_pref_getsize(&p1->pref, c1->rawreadformat); +- if (fmt0.cur_ms != fmt1.cur_ms) { +- ast_debug(1, "Cannot packet2packet bridge - packetization settings prevent it\n"); +- ast_channel_unlock(c0); +- ast_channel_unlock(c1); +- return AST_BRIDGE_FAILED_NOWARN; +- } +- +- ast_verb(3, "Packet2Packet bridging %s and %s\n", c0->name, c1->name); +- res = bridge_p2p_loop(c0, c1, p0, p1, timeoutms, flags, fo, rc, pvt0, pvt1); +- } else { +- ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name); +- res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, tp0, tp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1); +- } +- +- return res; +-} +- +-static char *rtp_do_debug_ip(struct ast_cli_args *a) +-{ +- struct hostent *hp; +- struct ast_hostent ahp; +- int port = 0; +- char *p, *arg; +- +- arg = a->argv[3]; +- p = strstr(arg, ":"); +- if (p) { +- *p = '\0'; +- p++; +- port = atoi(p); +- } +- hp = ast_gethostbyname(arg, &ahp); +- if (hp == NULL) { +- ast_cli(a->fd, "Lookup failed for '%s'\n", arg); +- return CLI_FAILURE; +- } +- rtpdebugaddr.sin_family = AF_INET; +- memcpy(&rtpdebugaddr.sin_addr, hp->h_addr, sizeof(rtpdebugaddr.sin_addr)); +- rtpdebugaddr.sin_port = htons(port); +- if (port == 0) +- ast_cli(a->fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtpdebugaddr.sin_addr)); +- else +- ast_cli(a->fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtpdebugaddr.sin_addr), port); +- rtpdebug = 1; +- return CLI_SUCCESS; +-} +- +-static char *rtcp_do_debug_ip(struct ast_cli_args *a) +-{ +- struct hostent *hp; +- struct ast_hostent ahp; +- int port = 0; +- char *p, *arg; +- +- arg = a->argv[3]; +- p = strstr(arg, ":"); +- if (p) { +- *p = '\0'; +- p++; +- port = atoi(p); +- } +- hp = ast_gethostbyname(arg, &ahp); +- if (hp == NULL) { +- ast_cli(a->fd, "Lookup failed for '%s'\n", arg); +- return CLI_FAILURE; +- } +- rtcpdebugaddr.sin_family = AF_INET; +- memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr)); +- rtcpdebugaddr.sin_port = htons(port); +- if (port == 0) +- ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr)); +- else +- ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port); +- rtcpdebug = 1; +- return CLI_SUCCESS; +-} +- +-static char *handle_cli_rtp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +-{ +- switch (cmd) { +- case CLI_INIT: +- e->command = "rtp set debug {on|off|ip}"; +- e->usage = +- "Usage: rtp set debug {on|off|ip host[:port]}\n" +- " Enable/Disable dumping of all RTP packets. If 'ip' is\n" +- " specified, limit the dumped packets to those to and from\n" +- " the specified 'host' with optional port.\n"; +- return NULL; +- case CLI_GENERATE: +- return NULL; +- } +- +- if (a->argc == e->args) { /* set on or off */ +- if (!strncasecmp(a->argv[e->args-1], "on", 2)) { +- rtpdebug = 1; +- memset(&rtpdebugaddr, 0, sizeof(rtpdebugaddr)); +- ast_cli(a->fd, "RTP Debugging Enabled\n"); +- return CLI_SUCCESS; +- } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { +- rtpdebug = 0; +- ast_cli(a->fd, "RTP Debugging Disabled\n"); +- return CLI_SUCCESS; +- } +- } else if (a->argc == e->args +1) { /* ip */ +- return rtp_do_debug_ip(a); +- } +- +- return CLI_SHOWUSAGE; /* default, failure */ +-} +- +-static char *handle_cli_rtcp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +-{ +- switch (cmd) { +- case CLI_INIT: +- e->command = "rtcp set debug {on|off|ip}"; +- e->usage = +- "Usage: rtcp set debug {on|off|ip host[:port]}\n" +- " Enable/Disable dumping of all RTCP packets. If 'ip' is\n" +- " specified, limit the dumped packets to those to and from\n" +- " the specified 'host' with optional port.\n"; +- return NULL; +- case CLI_GENERATE: +- return NULL; +- } +- +- if (a->argc == e->args) { /* set on or off */ +- if (!strncasecmp(a->argv[e->args-1], "on", 2)) { +- rtcpdebug = 1; +- memset(&rtcpdebugaddr, 0, sizeof(rtcpdebugaddr)); +- ast_cli(a->fd, "RTCP Debugging Enabled\n"); +- return CLI_SUCCESS; +- } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { +- rtcpdebug = 0; +- ast_cli(a->fd, "RTCP Debugging Disabled\n"); +- return CLI_SUCCESS; +- } +- } else if (a->argc == e->args +1) { /* ip */ +- return rtcp_do_debug_ip(a); +- } +- +- return CLI_SHOWUSAGE; /* default, failure */ +-} +- +-static char *handle_cli_rtcp_set_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +-{ +- switch (cmd) { +- case CLI_INIT: +- e->command = "rtcp set stats {on|off}"; +- e->usage = +- "Usage: rtcp set stats {on|off}\n" +- " Enable/Disable dumping of RTCP stats.\n"; +- return NULL; +- case CLI_GENERATE: +- return NULL; +- } +- +- if (a->argc != e->args) +- return CLI_SHOWUSAGE; +- +- if (!strncasecmp(a->argv[e->args-1], "on", 2)) +- rtcpstats = 1; +- else if (!strncasecmp(a->argv[e->args-1], "off", 3)) +- rtcpstats = 0; +- else +- return CLI_SHOWUSAGE; +- +- ast_cli(a->fd, "RTCP Stats %s\n", rtcpstats ? "Enabled" : "Disabled"); +- return CLI_SUCCESS; +-} +- +-static char *handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +-{ +- switch (cmd) { +- case CLI_INIT: +- e->command = "stun set debug {on|off}"; +- e->usage = +- "Usage: stun set debug {on|off}\n" +- " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n" +- " debugging\n"; +- return NULL; +- case CLI_GENERATE: +- return NULL; +- } +- +- if (a->argc != e->args) +- return CLI_SHOWUSAGE; +- +- if (!strncasecmp(a->argv[e->args-1], "on", 2)) +- stundebug = 1; +- else if (!strncasecmp(a->argv[e->args-1], "off", 3)) +- stundebug = 0; +- else +- return CLI_SHOWUSAGE; +- +- ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled"); +- return CLI_SUCCESS; +-} +- +-static struct ast_cli_entry cli_rtp[] = { +- AST_CLI_DEFINE(handle_cli_rtp_set_debug, "Enable/Disable RTP debugging"), +- AST_CLI_DEFINE(handle_cli_rtcp_set_debug, "Enable/Disable RTCP debugging"), +- AST_CLI_DEFINE(handle_cli_rtcp_set_stats, "Enable/Disable RTCP stats"), +- AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"), +-}; +- +-static int __ast_rtp_reload(int reload) +-{ +- struct ast_config *cfg; +- const char *s; +- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; +- +- cfg = ast_config_load2("rtp.conf", "rtp", config_flags); +- if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { +- return 0; +- } +- +- rtpstart = 5000; +- rtpend = 31000; +- dtmftimeout = DEFAULT_DTMF_TIMEOUT; +- strictrtp = STRICT_RTP_OPEN; +- if (cfg) { +- if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) { +- rtpstart = atoi(s); +- if (rtpstart < 1024) +- rtpstart = 1024; +- if (rtpstart > 65535) +- rtpstart = 65535; +- } +- if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) { +- rtpend = atoi(s); +- if (rtpend < 1024) +- rtpend = 1024; +- if (rtpend > 65535) +- rtpend = 65535; +- } +- if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) { +- rtcpinterval = atoi(s); +- if (rtcpinterval == 0) +- rtcpinterval = 0; /* Just so we're clear... it's zero */ +- if (rtcpinterval < RTCP_MIN_INTERVALMS) +- rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */ +- if (rtcpinterval > RTCP_MAX_INTERVALMS) +- rtcpinterval = RTCP_MAX_INTERVALMS; +- } +- if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) { +-#ifdef SO_NO_CHECK +- if (ast_false(s)) +- nochecksums = 1; +- else +- nochecksums = 0; +-#else +- if (ast_false(s)) +- ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n"); +-#endif +- } +- if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) { +- dtmftimeout = atoi(s); +- if ((dtmftimeout < 0) || (dtmftimeout > 20000)) { +- ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n", +- dtmftimeout, DEFAULT_DTMF_TIMEOUT); +- dtmftimeout = DEFAULT_DTMF_TIMEOUT; +- }; +- } +- if ((s = ast_variable_retrieve(cfg, "general", "strictrtp"))) { +- strictrtp = ast_true(s); +- } +- ast_config_destroy(cfg); +- } +- if (rtpstart >= rtpend) { +- ast_log(LOG_WARNING, "Unreasonable values for RTP start/end port in rtp.conf\n"); +- rtpstart = 5000; +- rtpend = 31000; +- } +- ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend); +- return 0; +-} +- +-int ast_rtp_reload(void) +-{ +- return __ast_rtp_reload(1); +-} +- +-/*! \brief Initialize the RTP system in Asterisk */ +-void ast_rtp_init(void) +-{ +- ast_cli_register_multiple(cli_rtp, sizeof(cli_rtp) / sizeof(struct ast_cli_entry)); +- __ast_rtp_reload(0); +-} +- +-/*! \brief Write t140 redundacy frame +- * \param data primary data to be buffered +- */ +-static int red_write(const void *data) +-{ +- struct ast_rtp *rtp = (struct ast_rtp*) data; +- +- ast_rtp_write(rtp, &rtp->red->t140); +- +- return 1; +-} +- +-/*! \brief Construct a redundant frame +- * \param red redundant data structure +- */ +-static struct ast_frame *red_t140_to_red(struct rtp_red *red) { +- unsigned char *data = red->t140red.data.ptr; +- int len = 0; +- int i; +- +- /* replace most aged generation */ +- if (red->len[0]) { +- for (i = 1; i < red->num_gen+1; i++) +- len += red->len[i]; +- +- memmove(&data[red->hdrlen], &data[red->hdrlen+red->len[0]], len); +- } +- +- /* Store length of each generation and primary data length*/ +- for (i = 0; i < red->num_gen; i++) +- red->len[i] = red->len[i+1]; +- red->len[i] = red->t140.datalen; +- +- /* write each generation length in red header */ +- len = red->hdrlen; +- for (i = 0; i < red->num_gen; i++) +- len += data[i*4+3] = red->len[i]; +- +- /* add primary data to buffer */ +- memcpy(&data[len], red->t140.data.ptr, red->t140.datalen); +- red->t140red.datalen = len + red->t140.datalen; +- +- /* no primary data and no generations to send */ +- if (len == red->hdrlen && !red->t140.datalen) +- return NULL; +- +- /* reset t.140 buffer */ +- red->t140.datalen = 0; +- +- return &red->t140red; +-} +- +-/*! \brief Initialize t140 redundancy +- * \param rtp +- * \param ti buffer t140 for ti (msecs) before sending redundant frame +- * \param red_data_pt Payloadtypes for primary- and generation-data +- * \param num_gen numbers of generations (primary generation not encounted) +- * +-*/ +-int rtp_red_init(struct ast_rtp *rtp, int ti, int *red_data_pt, int num_gen) +-{ +- struct rtp_red *r; +- int x; +- +- if (!(r = ast_calloc(1, sizeof(struct rtp_red)))) +- return -1; +- +- r->t140.frametype = AST_FRAME_TEXT; +- r->t140.subclass = AST_FORMAT_T140RED; +- r->t140.data.ptr = &r->buf_data; +- +- r->t140.ts = 0; +- r->t140red = r->t140; +- r->t140red.data.ptr = &r->t140red_data; +- r->t140red.datalen = 0; +- r->ti = ti; +- r->num_gen = num_gen; +- r->hdrlen = num_gen * 4 + 1; +- r->prev_ts = 0; +- +- for (x = 0; x < num_gen; x++) { +- r->pt[x] = red_data_pt[x]; +- r->pt[x] |= 1 << 7; /* mark redundant generations pt */ +- r->t140red_data[x*4] = r->pt[x]; +- } +- r->t140red_data[x*4] = r->pt[x] = red_data_pt[x]; /* primary pt */ +- r->schedid = ast_sched_add(rtp->sched, ti, red_write, rtp); +- rtp->red = r; +- +- r->t140.datalen = 0; +- +- return 0; +-} +- +-/*! \brief Buffer t140 from chan_sip +- * \param rtp +- * \param f frame +- */ +-void red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f) +-{ +- if (f->datalen > -1) { +- struct rtp_red *red = rtp->red; +- memcpy(&red->buf_data[red->t140.datalen], f->data.ptr, f->datalen); +- red->t140.datalen += f->datalen; +- red->t140.ts = f->ts; +- } +-} +Index: main/utils.c +=================================================================== +--- a/main/utils.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/utils.c (.../trunk) (revision 186562) +@@ -1469,8 +1469,33 @@ + * stringfields support routines. + */ + +-const char __ast_string_field_empty[] = ""; /*!< the empty string */ ++/* this is a little complex... string fields are stored with their ++ allocated size in the bytes preceding the string; even the ++ constant 'empty' string has to be this way, so the code that ++ checks to see if there is enough room for a new string doesn't ++ have to have any special case checks ++*/ + ++static const struct { ++ ast_string_field_allocation allocation; ++ char string[1]; ++} __ast_string_field_empty_buffer; ++ ++ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string; ++ ++#define ALLOCATOR_OVERHEAD 48 ++ ++static size_t optimal_alloc_size(size_t size) ++{ ++ unsigned int count; ++ ++ size += ALLOCATOR_OVERHEAD; ++ ++ for (count = 1; size; size >>= 1, count++); ++ ++ return (1 << count) - ALLOCATOR_OVERHEAD; ++} ++ + /*! \brief add a new block to the pool. + * We can only allocate from the topmost pool, so the + * fields in *mgr reflect the size of that only. +@@ -1480,14 +1505,15 @@ + size_t size) + { + struct ast_string_field_pool *pool; ++ size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size); + +- if (!(pool = ast_calloc(1, sizeof(*pool) + size))) ++ if (!(pool = ast_calloc(1, alloc_size))) { + return -1; +- ++ } ++ + pool->prev = *pool_head; ++ pool->size = alloc_size - sizeof(*pool); + *pool_head = pool; +- mgr->size = size; +- mgr->used = 0; + mgr->last_alloc = NULL; + + return 0; +@@ -1511,13 +1537,16 @@ + struct ast_string_field_pool *cur = *pool_head; + + /* clear fields - this is always necessary */ +- while ((struct ast_string_field_mgr *) p != mgr) ++ while ((struct ast_string_field_mgr *) p != mgr) { + *p++ = __ast_string_field_empty; ++ } ++ + mgr->last_alloc = NULL; + if (size > 0) { /* allocate the initial pool */ + *pool_head = NULL; + return add_string_pool(mgr, pool_head, size); + } ++ + if (size < 0) { /* reset all pools */ + *pool_head = NULL; + } else { /* preserve the first pool */ +@@ -1527,7 +1556,7 @@ + } + cur = cur->prev; + (*pool_head)->prev = NULL; +- mgr->used = 0; ++ (*pool_head)->used = (*pool_head)->active = 0; + } + + while (cur) { +@@ -1544,34 +1573,37 @@ + struct ast_string_field_pool **pool_head, size_t needed) + { + char *result = NULL; +- size_t space = mgr->size - mgr->used; ++ size_t space = (*pool_head)->size - (*pool_head)->used; ++ size_t to_alloc = needed + sizeof(ast_string_field_allocation); + +- if (__builtin_expect(needed > space, 0)) { +- size_t new_size = mgr->size * 2; ++ if (__builtin_expect(to_alloc > space, 0)) { ++ size_t new_size = (*pool_head)->size; + +- while (new_size < needed) ++ while (new_size < to_alloc) { + new_size *= 2; ++ } + + if (add_string_pool(mgr, pool_head, new_size)) + return NULL; + } + +- result = (*pool_head)->base + mgr->used; +- mgr->used += needed; ++ result = (*pool_head)->base + (*pool_head)->used; ++ (*pool_head)->used += to_alloc; ++ (*pool_head)->active += needed; ++ result += sizeof(ast_string_field_allocation); ++ AST_STRING_FIELD_ALLOCATION(result) = needed; + mgr->last_alloc = result; ++ + return result; + } + +-int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, size_t needed, ++int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, ++ struct ast_string_field_pool **pool_head, size_t needed, + const ast_string_field *ptr) + { +- int grow = needed - (strlen(*ptr) + 1); +- size_t space = mgr->size - mgr->used; ++ ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr); ++ size_t space = (*pool_head)->size - (*pool_head)->used; + +- if (grow <= 0) { +- return 0; +- } +- + if (*ptr != mgr->last_alloc) { + return 1; + } +@@ -1580,30 +1612,57 @@ + return 1; + } + +- mgr->used += grow; ++ (*pool_head)->used += grow; ++ (*pool_head)->active += grow; ++ AST_STRING_FIELD_ALLOCATION(*ptr) += grow; + + return 0; + } + ++void __ast_string_field_release_active(struct ast_string_field_pool *pool_head, ++ const ast_string_field ptr) ++{ ++ struct ast_string_field_pool *pool, *prev; ++ ++ if (ptr == __ast_string_field_empty) { ++ return; ++ } ++ ++ for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) { ++ if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) { ++ pool->active -= AST_STRING_FIELD_ALLOCATION(ptr); ++ if ((pool->active == 0) && prev) { ++ prev->prev = pool->prev; ++ ast_free(pool); ++ } ++ break; ++ } ++ } ++} ++ + void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr, + struct ast_string_field_pool **pool_head, + ast_string_field *ptr, const char *format, va_list ap1, va_list ap2) + { + size_t needed; + size_t available; +- size_t space = mgr->size - mgr->used; ++ size_t space = (*pool_head)->size - (*pool_head)->used; ++ ssize_t grow; + char *target; + + /* if the field already has space allocated, try to reuse it; +- otherwise, use the empty space at the end of the current ++ otherwise, try to use the empty space at the end of the current + pool + */ +- if ((*ptr)[0] != '\0') { ++ if (*ptr != __ast_string_field_empty) { + target = (char *) *ptr; +- available = strlen(target) + 1; ++ available = AST_STRING_FIELD_ALLOCATION(*ptr); ++ if (*ptr == mgr->last_alloc) { ++ available += space; ++ } + } else { +- target = (*pool_head)->base + mgr->used; +- available = space; ++ target = (*pool_head)->base + (*pool_head)->used + sizeof(ast_string_field_allocation); ++ available = space - sizeof(ast_string_field_allocation); + } + + needed = vsnprintf(target, available, format, ap1) + 1; +@@ -1611,28 +1670,32 @@ + va_end(ap1); + + if (needed > available) { +- /* if the space needed can be satisfied by using the current +- pool (which could only occur if we tried to use the field's +- allocated space and failed), then use that space; otherwise +- allocate a new pool ++ /* the allocation could not be satisfied using the field's current allocation ++ (if it has one), or the space available in the pool (if it does not). allocate ++ space for it, adding a new string pool if necessary. + */ +- if (needed > space) { +- size_t new_size = mgr->size * 2; +- +- while (new_size < needed) +- new_size *= 2; +- +- if (add_string_pool(mgr, pool_head, new_size)) +- return; ++ if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) { ++ return; + } +- +- target = (*pool_head)->base + mgr->used; + vsprintf(target, format, ap2); +- } +- +- if (*ptr != target) { ++ __ast_string_field_release_active(*pool_head, *ptr); ++ *ptr = target; ++ } else if (*ptr != target) { ++ /* the allocation was satisfied using available space in the pool, but not ++ using the space already allocated to the field ++ */ ++ __ast_string_field_release_active(*pool_head, *ptr); + mgr->last_alloc = *ptr = target; +- mgr->used += needed; ++ AST_STRING_FIELD_ALLOCATION(target) = needed; ++ (*pool_head)->used += needed + sizeof(ast_string_field_allocation); ++ (*pool_head)->active += needed; ++ } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) { ++ /* the allocation was satisfied by using available space in the pool *and* ++ the field was the last allocated field from the pool, so it grew ++ */ ++ (*pool_head)->used += grow; ++ (*pool_head)->active += grow; ++ AST_STRING_FIELD_ALLOCATION(*ptr) += grow; + } + } + +Index: main/loader.c +=================================================================== +--- a/main/loader.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/loader.c (.../trunk) (revision 186562) +@@ -43,7 +43,6 @@ + #include "asterisk/manager.h" + #include "asterisk/cdr.h" + #include "asterisk/enum.h" +-#include "asterisk/rtp.h" + #include "asterisk/http.h" + #include "asterisk/lock.h" + #include "asterisk/features.h" +@@ -243,7 +242,6 @@ + { "extconfig", read_config_maps }, + { "enum", ast_enum_reload }, + { "manager", reload_manager }, +- { "rtp", ast_rtp_reload }, + { "http", ast_http_reload }, + { "logger", logger_reload }, + { "features", ast_features_reload }, +@@ -403,18 +401,6 @@ + return NULL; + } + +- /* if the system supports RTLD_NOLOAD, we can just 'promote' the flags +- on the already-opened library to what we want... if not, we have to +- close it and start over +- */ +-#if defined(HAVE_RTLD_NOLOAD) && !defined(__Darwin__) +- if (!dlopen(fn, RTLD_NOLOAD | (wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) { +- ast_log(LOG_WARNING, "Unable to promote flags on module '%s': %s\n", resource_in, dlerror()); +- while (!dlclose(lib)); +- ast_free(resource_being_loaded); +- return NULL; +- } +-#else + while (!dlclose(lib)); + resource_being_loaded = NULL; + +@@ -435,7 +421,6 @@ + /* since the module was successfully opened, and it registered itself + the previous time we did that, we're going to assume it worked this + time too :) */ +-#endif + + AST_LIST_LAST(&module_list)->lib = lib; + resource_being_loaded = NULL; +Index: main/channel.c +=================================================================== +--- a/main/channel.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/channel.c (.../trunk) (revision 186562) +@@ -718,6 +718,8 @@ + AST_FORMAT_ULAW, + /*! Unless of course, you're a silly European, so then prefer ALAW */ + AST_FORMAT_ALAW, ++ AST_FORMAT_SIREN14, ++ AST_FORMAT_SIREN7, + /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */ + AST_FORMAT_G722, + /*! Okay, well, signed linear is easy to translate into other stuff */ +@@ -794,6 +796,13 @@ + return NULL; + } + ++ if (!(tmp->cid.cid_name = ast_strdup(cid_name)) || !(tmp->cid.cid_num = ast_strdup(cid_num))) { ++ ast_string_field_free_memory(tmp); ++ sched_context_destroy(tmp->sched); ++ ast_free(tmp); ++ return NULL; ++ } ++ + #ifdef HAVE_EPOLL + tmp->epfd = epoll_create(25); + #endif +@@ -805,17 +814,19 @@ + #endif + } + +- tmp->timingfd = ast_timer_open(); +- if (tmp->timingfd > -1) { ++ if ((tmp->timer = ast_timer_open())) { + needqueue = 0; ++ tmp->timingfd = ast_timer_fd(tmp->timer); ++ } else { ++ tmp->timingfd = -1; + } + + if (needqueue) { + if (pipe(tmp->alertpipe)) { + ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe!\n"); + alertpipe_failed: +- if (tmp->timingfd > -1) { +- ast_timer_close(tmp->timingfd); ++ if (tmp->timer) { ++ ast_timer_close(tmp->timer); + } + + sched_context_destroy(tmp->sched); +@@ -862,9 +873,6 @@ + ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME, + (long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1)); + } +- +- tmp->cid.cid_name = ast_strdup(cid_name); +- tmp->cid.cid_num = ast_strdup(cid_num); + + if (!ast_strlen_zero(name_fmt)) { + /* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call. +@@ -1008,7 +1016,7 @@ + chan->name, f->frametype, f->subclass, qlen, strerror(errno)); + } + } else if (chan->timingfd > -1) { +- ast_timer_enable_continuous(chan->timingfd); ++ ast_timer_enable_continuous(chan->timer); + } else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) { + pthread_kill(chan->blocker, SIGURG); + } +@@ -1307,6 +1315,279 @@ + cid->cid_dnid = cid->cid_num = cid->cid_name = cid->cid_ani = cid->cid_rdnis = NULL; + } + ++/*! ++ * \internal ++ * \brief Initialize the given party id structure. ++ * ++ * \param init Party id structure to initialize. ++ * ++ * \return Nothing ++ */ ++static void ast_party_id_init(struct ast_party_id *init) ++{ ++ init->number = NULL; ++ init->name = NULL; ++ init->number_type = 0; /* Unknown */ ++ init->number_presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; ++} ++ ++/*! ++ * \internal ++ * \brief Copy the source party id information to the destination party id. ++ * ++ * \param dest Destination party id ++ * \param src Source party id ++ * ++ * \return Nothing ++ */ ++static void ast_party_id_copy(struct ast_party_id *dest, const struct ast_party_id *src) ++{ ++ if (dest == src) { ++ /* Don't copy to self */ ++ return; ++ } ++ ++ if (dest->number) { ++ ast_free(dest->number); ++ } ++ dest->number = ast_strdup(src->number); ++ ++ if (dest->name) { ++ ast_free(dest->name); ++ } ++ dest->name = ast_strdup(src->name); ++ ++ dest->number_type = src->number_type; ++ dest->number_presentation = src->number_presentation; ++} ++ ++/*! ++ * \internal ++ * \brief Initialize the given party id structure using the given guide ++ * for a set update operation. ++ * ++ * \details ++ * The initialization is needed to allow a set operation to know if a ++ * value needs to be updated. Simple integers need the guide's original ++ * value in case the set operation is not trying to set a new value. ++ * String values are simply set to NULL pointers if they are not going ++ * to be updated. ++ * ++ * \param init Party id structure to initialize. ++ * \param guide Source party id to use as a guide in initializing. ++ * ++ * \return Nothing ++ */ ++static void ast_party_id_set_init(struct ast_party_id *init, const struct ast_party_id *guide) ++{ ++ init->number = NULL; ++ init->name = NULL; ++ init->number_type = guide->number_type; ++ init->number_presentation = guide->number_presentation; ++} ++ ++/*! ++ * \internal ++ * \brief Set the source party id information into the destination party id. ++ * ++ * \param dest Destination party id ++ * \param src Source party id ++ * ++ * \return Nothing ++ */ ++static void ast_party_id_set(struct ast_party_id *dest, const struct ast_party_id *src) ++{ ++ if (dest == src) { ++ /* Don't set to self */ ++ return; ++ } ++ ++ if (src->name && src->name != dest->name) { ++ if (dest->name) { ++ ast_free(dest->name); ++ } ++ dest->name = ast_strdup(src->name); ++ } ++ ++ if (src->number && src->number != dest->number) { ++ if (dest->number) { ++ ast_free(dest->number); ++ } ++ dest->number = ast_strdup(src->number); ++ } ++ ++ dest->number_type = src->number_type; ++ dest->number_presentation = src->number_presentation; ++} ++ ++/*! ++ * \internal ++ * \brief Destroy the party id contents ++ * ++ * \param doomed The party id to destroy. ++ * ++ * \return Nothing ++ */ ++static void ast_party_id_free(struct ast_party_id *doomed) ++{ ++ if (doomed->number) { ++ ast_free(doomed->number); ++ doomed->number = NULL; ++ } ++ ++ if (doomed->name) { ++ ast_free(doomed->name); ++ doomed->name = NULL; ++ } ++} ++ ++void ast_party_caller_copy(struct ast_callerid *dest, const struct ast_callerid *src) ++{ ++ if (dest == src) { ++ /* Don't copy to self */ ++ return; ++ } ++ ++#if 1 ++ /* Copy caller-id specific information ONLY from struct ast_callerid */ ++ if (dest->cid_num) ++ { ++ ast_free(dest->cid_num); ++ } ++ dest->cid_num = ast_strdup(src->cid_num); ++ ++ if (dest->cid_name) ++ { ++ ast_free(dest->cid_name); ++ } ++ dest->cid_name = ast_strdup(src->cid_name); ++ ++ dest->cid_ton = src->cid_ton; ++ dest->cid_pres = src->cid_pres; ++ ++ ++ if (dest->cid_ani) ++ { ++ ast_free(dest->cid_ani); ++ } ++ dest->cid_ani = ast_strdup(src->cid_ani); ++ ++ dest->cid_ani2 = src->cid_ani2; ++ ++#else ++ ++ /* The src and dest parameter types will become struct ast_party_caller ptrs. */ ++ /* This is future code */ ++ ++ ast_party_id_copy(&dest->id, &src->id); ++ ++ if (dest->ani) { ++ ast_free(dest->ani); ++ } ++ dest->ani = ast_strdup(src->ani); ++ ++ dest->ani2 = src->ani2; ++#endif ++} ++ ++void ast_party_connected_line_init(struct ast_party_connected_line *init) ++{ ++ ast_party_id_init(&init->id); ++ init->ani = NULL; ++ init->ani2 = 0; ++ init->source = AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN; ++} ++ ++void ast_party_connected_line_copy(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src) ++{ ++ if (dest == src) { ++ /* Don't copy to self */ ++ return; ++ } ++ ++ ast_party_id_copy(&dest->id, &src->id); ++ ++ if (dest->ani) { ++ ast_free(dest->ani); ++ } ++ dest->ani = ast_strdup(src->ani); ++ ++ dest->ani2 = src->ani2; ++ dest->source = src->source; ++} ++ ++void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide) ++{ ++ ast_party_id_set_init(&init->id, &guide->id); ++ init->ani = NULL; ++ init->ani2 = guide->ani2; ++ init->source = guide->source; ++} ++ ++void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src) ++{ ++ ast_party_id_set(&dest->id, &src->id); ++ ++ if (src->ani && src->ani != dest->ani) { ++ if (dest->ani) { ++ ast_free(dest->ani); ++ } ++ dest->ani = ast_strdup(src->ani); ++ } ++ ++ dest->ani2 = src->ani2; ++ dest->source = src->source; ++} ++ ++void ast_party_connected_line_collect_caller(struct ast_party_connected_line *connected, struct ast_callerid *cid) ++{ ++ connected->id.number = cid->cid_num; ++ connected->id.name = cid->cid_name; ++ connected->id.number_type = cid->cid_ton; ++ connected->id.number_presentation = cid->cid_pres; ++ ++ connected->ani = cid->cid_ani; ++ connected->ani2 = cid->cid_ani2; ++ connected->source = AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN; ++} ++ ++void ast_party_connected_line_free(struct ast_party_connected_line *doomed) ++{ ++ ast_party_id_free(&doomed->id); ++ ++ if (doomed->ani) { ++ ast_free(doomed->ani); ++ doomed->ani = NULL; ++ } ++} ++ ++void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src) ++{ ++ if (dest == src) { ++ /* Don't copy to self */ ++ return; ++ } ++ ++ ast_party_id_copy(&dest->from, &src->from); ++ ast_party_id_copy(&dest->to, &src->to); ++ dest->count = src->count; ++ dest->reason = src->reason; ++} ++ ++void ast_party_redirecting_set_init(struct ast_party_redirecting *init, const struct ast_party_redirecting *guide) ++{ ++ ast_party_id_set_init(&init->from, &guide->from); ++ ast_party_id_set_init(&init->to, &guide->to); ++ init->count = guide->count; ++ init->reason = guide->reason; ++} ++ ++void ast_party_redirecting_free(struct ast_party_redirecting *doomed) ++{ ++ ast_party_id_free(&doomed->from); ++ ast_party_id_free(&doomed->to); ++} ++ + /*! \brief Free a channel structure */ + void ast_channel_free(struct ast_channel *chan) + { +@@ -1370,14 +1651,19 @@ + ast_translator_free_path(chan->writetrans); + if (chan->pbx) + ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name); ++ + free_cid(&chan->cid); ++ ast_party_connected_line_free(&chan->connected); ++ ast_party_redirecting_free(&chan->redirecting); ++ + /* Close pipes if appropriate */ + if ((fd = chan->alertpipe[0]) > -1) + close(fd); + if ((fd = chan->alertpipe[1]) > -1) + close(fd); +- if ((fd = chan->timingfd) > -1) +- ast_timer_close(fd); ++ if (chan->timer) { ++ ast_timer_close(chan->timer); ++ } + #ifdef HAVE_EPOLL + for (i = 0; i < AST_MAX_FDS; i++) { + if (chan->epfd_data[i]) +@@ -2323,13 +2609,13 @@ + data = NULL; + } + +- if (rate && rate > (max_rate = ast_timer_get_max_rate(c->timingfd))) { ++ if (rate && rate > (max_rate = ast_timer_get_max_rate(c->timer))) { + real_rate = max_rate; + } + + ast_debug(1, "Scheduling timer at (%u requested / %u actual) timer ticks per second\n", rate, real_rate); + +- res = ast_timer_set_rate(c->timingfd, real_rate); ++ res = ast_timer_set_rate(c->timer, real_rate); + + c->timingfunc = func; + c->timingdata = data; +@@ -2391,6 +2677,8 @@ + case AST_CONTROL_RINGING: + case AST_CONTROL_ANSWER: + case AST_CONTROL_SRCUPDATE: ++ case AST_CONTROL_CONNECTED_LINE: ++ case AST_CONTROL_REDIRECTING: + /* Unimportant */ + break; + default: +@@ -2582,11 +2870,11 @@ + + ast_clear_flag(chan, AST_FLAG_EXCEPTION); + +- res = ast_timer_get_event(chan->timingfd); ++ res = ast_timer_get_event(chan->timer); + + switch (res) { + case AST_TIMING_EVENT_EXPIRED: +- ast_timer_ack(chan->timingfd, 1); ++ ast_timer_ack(chan->timer, 1); + + if (chan->timingfunc) { + /* save a copy of func/data before unlocking the channel */ +@@ -2596,7 +2884,7 @@ + ast_channel_unlock(chan); + func(data); + } else { +- ast_timer_set_rate(chan->timingfd, 0); ++ ast_timer_set_rate(chan->timer, 0); + chan->fdno = -1; + ast_channel_unlock(chan); + } +@@ -2607,7 +2895,7 @@ + case AST_TIMING_EVENT_CONTINUOUS: + if (AST_LIST_EMPTY(&chan->readq) || + !AST_LIST_NEXT(AST_LIST_FIRST(&chan->readq), frame_list)) { +- ast_timer_disable_continuous(chan->timingfd); ++ ast_timer_disable_continuous(chan->timer); + } + break; + } +@@ -2823,6 +3111,13 @@ + ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF); + chan->emulate_dtmf_digit = 0; + ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name); ++ if (chan->audiohooks) { ++ struct ast_frame *old_frame = f; ++ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f); ++ if (old_frame != f) { ++ ast_frfree(old_frame); ++ } ++ } + } + } + break; +@@ -2982,7 +3277,10 @@ + case AST_CONTROL_ANSWER: + case AST_CONTROL_HANGUP: + case AST_CONTROL_T38: +- return 0; ++ case AST_CONTROL_CONNECTED_LINE: ++ case AST_CONTROL_REDIRECTING: ++ case AST_CONTROL_TRANSFER: ++ break; + + case AST_CONTROL_CONGESTION: + case AST_CONTROL_BUSY: +@@ -3003,7 +3301,7 @@ + * in switch statements. */ + enum ast_control_frame_type condition = _condition; + struct ast_tone_zone_sound *ts = NULL; +- int res = -1; ++ int res; + + ast_channel_lock(chan); + +@@ -3012,10 +3310,41 @@ + ast_channel_unlock(chan); + return -1; + } ++ switch (condition) { ++ case AST_CONTROL_CONNECTED_LINE: ++ { ++ struct ast_party_connected_line connected; + ++ ast_party_connected_line_set_init(&connected, &chan->connected); ++ res = ast_connected_line_parse_data(data, datalen, &connected); ++ if (!res) { ++ ast_channel_set_connected_line(chan, &connected); ++ } ++ ast_party_connected_line_free(&connected); ++ } ++ break; ++ ++ case AST_CONTROL_REDIRECTING: ++ { ++ struct ast_party_redirecting redirecting; ++ ++ ast_party_redirecting_set_init(&redirecting, &chan->redirecting); ++ res = ast_redirecting_parse_data(data, datalen, &redirecting); ++ if (!res) { ++ ast_channel_set_redirecting(chan, &redirecting); ++ } ++ ast_party_redirecting_free(&redirecting); ++ } ++ break; ++ ++ default: ++ break; ++ } + if (chan->tech->indicate) { + /* See if the channel driver can handle this condition. */ + res = chan->tech->indicate(chan, condition, data, datalen); ++ } else { ++ res = -1; + } + + ast_channel_unlock(chan); +@@ -3068,6 +3397,9 @@ + case AST_CONTROL_HOLD: + case AST_CONTROL_UNHOLD: + case AST_CONTROL_T38: ++ case AST_CONTROL_TRANSFER: ++ case AST_CONTROL_CONNECTED_LINE: ++ case AST_CONTROL_REDIRECTING: + /* Nothing left to do for these. */ + res = 0; + break; +@@ -3530,6 +3862,7 @@ + struct ast_channel *chan; + int res = 0; + int last_subclass = 0; ++ struct ast_party_connected_line connected; + + if (outstate) + *outstate = 0; +@@ -3560,7 +3893,13 @@ + if (oh->account) + ast_cdr_setaccount(chan, oh->account); + } ++ + ast_set_callerid(chan, cid_num, cid_name, cid_num); ++ ast_party_connected_line_set_init(&connected, &chan->connected); ++ connected.id.number = (char *) cid_num; ++ connected.id.name = (char *) cid_name; ++ connected.id.number_presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; ++ ast_channel_set_connected_line(chan, &connected); + + if (ast_call(chan, data, 0)) { /* ast_call failed... */ + ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data); +@@ -3599,6 +3938,8 @@ + case AST_CONTROL_UNHOLD: + case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_SRCUPDATE: ++ case AST_CONTROL_CONNECTED_LINE: ++ case AST_CONTROL_REDIRECTING: + case -1: /* Ignore -- just stopping indications */ + break; + +@@ -3747,6 +4088,37 @@ + res = 0; + } + ast_channel_unlock(chan); ++ ++ if (res < 0) { ++ return res; ++ } ++ ++ for (;;) { ++ struct ast_frame *fr; ++ ++ res = ast_waitfor(chan, -1); ++ ++ if (res < 0 || !(fr = ast_read(chan))) { ++ res = -1; ++ break; ++ } ++ ++ if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_TRANSFER) { ++ enum ast_control_transfer *message = fr->data.ptr; ++ ++ if (*message == AST_TRANSFER_SUCCESS) { ++ res = 1; ++ } else { ++ res = -1; ++ } ++ ++ ast_frfree(fr); ++ break; ++ } ++ ++ ast_frfree(fr); ++ } ++ + return res; + } + +@@ -4043,7 +4415,11 @@ + struct ast_frame *current; + const struct ast_channel_tech *t; + void *t_pvt; +- struct ast_callerid tmpcid; ++ union { ++ struct ast_callerid cid; ++ struct ast_party_connected_line connected; ++ struct ast_party_redirecting redirecting; ++ } exchange; + struct ast_channel *clonechan = original->masq; + struct ast_cdr *cdr; + int rformat = original->readformat; +@@ -4224,11 +4600,19 @@ + /* Stream stuff stays the same */ + /* Keep the original state. The fixup code will need to work with it most likely */ + +- /* Just swap the whole structures, nevermind the allocations, they'll work themselves +- out. */ +- tmpcid = original->cid; ++ /* ++ * Just swap the whole structures, nevermind the allocations, ++ * they'll work themselves out. ++ */ ++ exchange.cid = original->cid; + original->cid = clonechan->cid; +- clonechan->cid = tmpcid; ++ clonechan->cid = exchange.cid; ++ exchange.connected = original->connected; ++ original->connected = clonechan->connected; ++ clonechan->connected = exchange.connected; ++ exchange.redirecting = original->redirecting; ++ original->redirecting = clonechan->redirecting; ++ clonechan->redirecting = exchange.redirecting; + + /* Restore original timing file descriptor */ + ast_channel_set_fd(original, AST_TIMING_FD, original->timingfd); +@@ -4521,8 +4905,10 @@ + case AST_CONTROL_HOLD: + case AST_CONTROL_UNHOLD: + case AST_CONTROL_VIDUPDATE: ++ case AST_CONTROL_T38: + case AST_CONTROL_SRCUPDATE: +- case AST_CONTROL_T38: ++ case AST_CONTROL_CONNECTED_LINE: ++ case AST_CONTROL_REDIRECTING: + ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen); + if (jb_in_use) { + ast_jb_empty_and_reset(c0, c1); +@@ -5485,3 +5871,576 @@ + + return ast_say_digit_str_full(chan, buf, ints, lang, audiofd, ctrlfd); + } ++ ++void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_callerid *src) ++{ ++#if 1 ++ /* Must manually fill in struct ast_party_id until struct ast_callerid goes away */ ++ if (dest->id.number) { ++ ast_free(dest->id.number); ++ } ++ dest->id.number = ast_strdup(src->cid_num); ++ ++ if (dest->id.name) { ++ ast_free(dest->id.name); ++ } ++ dest->id.name = ast_strdup(src->cid_name); ++ ++ dest->id.number_type = src->cid_ton; ++ dest->id.number_presentation = src->cid_pres; ++ ++ ++ if (dest->ani) { ++ ast_free(dest->ani); ++ } ++ dest->ani = ast_strdup(src->cid_ani); ++ ++ dest->ani2 = src->cid_ani2; ++ ++#else ++ ++ /* The src parameter type will become a struct ast_party_caller ptr. */ ++ /* This is future code */ ++ ++ ast_party_id_copy(&dest->id, &src->id); ++ ++ if (dest->ani) { ++ ast_free(dest->ani); ++ } ++ dest->ani = ast_strdup(src->ani); ++ ++ dest->ani2 = src->ani2; ++#endif ++} ++ ++void ast_connected_line_copy_to_caller(struct ast_callerid *dest, const struct ast_party_connected_line *src) ++{ ++#if 1 ++ /* Must manually extract from struct ast_party_id until struct ast_callerid goes away */ ++ if (dest->cid_num) { ++ ast_free(dest->cid_num); ++ } ++ dest->cid_num = ast_strdup(src->id.number); ++ ++ if (dest->cid_name) { ++ ast_free(dest->cid_name); ++ } ++ dest->cid_name = ast_strdup(src->id.name); ++ ++ dest->cid_ton = src->id.number_type; ++ dest->cid_pres = src->id.number_presentation; ++ ++ ++ if (dest->cid_ani) { ++ ast_free(dest->cid_ani); ++ } ++ dest->cid_ani = ast_strdup(src->ani); ++ ++ dest->cid_ani2 = src->ani2; ++ ++#else ++ ++ /* The dest parameter type will become a struct ast_party_caller ptr. */ ++ /* This is future code */ ++ ++ ast_party_id_copy(&dest->id, &src->id); ++ ++ if (dest->ani) { ++ ast_free(dest->ani); ++ } ++ dest->ani = ast_strdup(src->ani); ++ ++ dest->ani2 = src->ani2; ++#endif ++} ++ ++void ast_channel_set_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected) ++{ ++ if (&chan->connected == connected) { ++ /* Don't set to self */ ++ return; ++ } ++ ++ ast_channel_lock(chan); ++ ast_party_connected_line_set(&chan->connected, connected); ++ ast_channel_unlock(chan); ++} ++ ++/*! ++ * \brief Element identifiers for connected line indication frame data ++ * \note Only add to the end of this enum. ++ */ ++enum { ++ AST_CONNECTED_LINE_NUMBER, ++ AST_CONNECTED_LINE_NAME, ++ AST_CONNECTED_LINE_NUMBER_TYPE, ++ AST_CONNECTED_LINE_NUMBER_PRESENTATION, ++ AST_CONNECTED_LINE_SOURCE ++}; ++ ++int ast_connected_line_build_data(unsigned char *data, size_t datalen, const struct ast_party_connected_line *connected) ++{ ++ int32_t value; ++ size_t length; ++ size_t pos = 0; ++ ++ /* ++ * The size of integer values must be fixed in case the frame is ++ * shipped to another machine. ++ */ ++ ++ /* *************** Connected line party id *************** */ ++ if (connected->id.number) { ++ length = strlen(connected->id.number); ++ if (datalen < pos + (sizeof(data[0]) * 2) + length) { ++ ast_log(LOG_WARNING, "No space left for connected line number\n"); ++ return -1; ++ } ++ data[pos++] = AST_CONNECTED_LINE_NUMBER; ++ data[pos++] = length; ++ memcpy(data + pos, connected->id.number, length); ++ pos += length; ++ } ++ ++ if (connected->id.name) { ++ length = strlen(connected->id.name); ++ if (datalen < pos + (sizeof(data[0]) * 2) + length) { ++ ast_log(LOG_WARNING, "No space left for connected line name\n"); ++ return -1; ++ } ++ data[pos++] = AST_CONNECTED_LINE_NAME; ++ data[pos++] = length; ++ memcpy(data + pos, connected->id.name, length); ++ pos += length; ++ } ++ ++ if (datalen < pos + (sizeof(data[0]) * 2) + 1) { ++ ast_log(LOG_WARNING, "No space left for connected line type of number\n"); ++ return -1; ++ } ++ data[pos++] = AST_CONNECTED_LINE_NUMBER_TYPE; ++ data[pos++] = 1; ++ data[pos++] = connected->id.number_type; ++ ++ if (datalen < pos + (sizeof(data[0]) * 2) + 1) { ++ ast_log(LOG_WARNING, "No space left for connected line presentation\n"); ++ return -1; ++ } ++ data[pos++] = AST_CONNECTED_LINE_NUMBER_PRESENTATION; ++ data[pos++] = 1; ++ data[pos++] = connected->id.number_presentation; ++ ++ /* Connected line source */ ++ if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) { ++ ast_log(LOG_WARNING, "No space left for connected line source\n"); ++ return -1; ++ } ++ data[pos++] = AST_CONNECTED_LINE_SOURCE; ++ data[pos++] = sizeof(value); ++ value = htonl(connected->source); ++ memcpy(data + pos, &value, sizeof(value)); ++ pos += sizeof(value); ++ ++ return pos; ++} ++ ++int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected) ++{ ++ size_t pos; ++ unsigned char ie_len; ++ unsigned char ie_id; ++ int32_t value; ++ ++ for (pos = 0; pos < datalen; pos += ie_len) { ++ if (datalen < pos + sizeof(ie_id) + sizeof(ie_len)) { ++ ast_log(LOG_WARNING, "Invalid connected line update\n"); ++ return -1; ++ } ++ ie_id = data[pos++]; ++ ie_len = data[pos++]; ++ if (datalen < pos + ie_len) { ++ ast_log(LOG_WARNING, "Invalid connected line update\n"); ++ return -1; ++ } ++ ++ switch (ie_id) { ++ case AST_CONNECTED_LINE_NUMBER: ++ if (connected->id.number) { ++ ast_free(connected->id.number); ++ } ++ connected->id.number = ast_malloc(ie_len + 1); ++ if (connected->id.number) { ++ memcpy(connected->id.number, data + pos, ie_len); ++ connected->id.number[ie_len] = 0; ++ } ++ break; ++ case AST_CONNECTED_LINE_NAME: ++ if (connected->id.name) { ++ ast_free(connected->id.name); ++ } ++ connected->id.name = ast_malloc(ie_len + 1); ++ if (connected->id.name) { ++ memcpy(connected->id.name, data + pos, ie_len); ++ connected->id.name[ie_len] = 0; ++ } ++ break; ++ case AST_CONNECTED_LINE_NUMBER_TYPE: ++ if (ie_len != 1) { ++ ast_log(LOG_WARNING, "Invalid connected line type of number (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ connected->id.number_type = data[pos]; ++ break; ++ case AST_CONNECTED_LINE_NUMBER_PRESENTATION: ++ if (ie_len != 1) { ++ ast_log(LOG_WARNING, "Invalid connected line presentation (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ connected->id.number_presentation = data[pos]; ++ break; ++ case AST_CONNECTED_LINE_SOURCE: ++ if (ie_len != sizeof(value)) { ++ ast_log(LOG_WARNING, "Invalid connected line source (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ memcpy(&value, data + pos, sizeof(value)); ++ connected->source = ntohl(value); ++ break; ++ default: ++ ast_log(LOG_DEBUG, "Unknown connected line element: %u (%u)\n", (unsigned) ie_id, (unsigned) ie_len); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected) ++{ ++ unsigned char data[1024]; /* This should be large enough */ ++ size_t datalen; ++ ++ datalen = ast_connected_line_build_data(data, sizeof(data), connected); ++ if (datalen == (size_t) -1) { ++ return; ++ } ++ ++ ast_indicate_data(chan, AST_CONTROL_CONNECTED_LINE, data, datalen); ++} ++ ++void ast_channel_queue_connected_line_update(struct ast_channel *chan, const struct ast_party_connected_line *connected) ++{ ++ unsigned char data[1024]; /* This should be large enough */ ++ size_t datalen; ++ ++ datalen = ast_connected_line_build_data(data, sizeof(data), connected); ++ if (datalen == (size_t) -1) { ++ return; ++ } ++ ++ ast_queue_control_data(chan, AST_CONTROL_CONNECTED_LINE, data, datalen); ++} ++ ++void ast_channel_set_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting) ++{ ++ if (&chan->redirecting == redirecting) { ++ /* Don't set to self */ ++ return; ++ } ++ ++ ast_channel_lock(chan); ++ ++ ast_party_id_set(&chan->redirecting.from, &redirecting->from); ++ if (redirecting->from.number ++ && redirecting->from.number != chan->redirecting.from.number) { ++ /* ++ * Must move string to ast_channel.cid.cid_rdnis until it goes away. ++ */ ++ if (chan->cid.cid_rdnis) { ++ ast_free(chan->cid.cid_rdnis); ++ } ++ chan->cid.cid_rdnis = chan->redirecting.from.number; ++ chan->redirecting.from.number = NULL; ++ } ++ ++ ast_party_id_set(&chan->redirecting.to, &redirecting->to); ++ chan->redirecting.reason = redirecting->reason; ++ chan->redirecting.count = redirecting->count; ++ ++ ast_channel_unlock(chan); ++} ++ ++/*! ++ * \brief Element identifiers for redirecting indication frame data ++ * \note Only add to the end of this enum. ++ */ ++enum { ++ AST_REDIRECTING_FROM_NUMBER, ++ AST_REDIRECTING_FROM_NAME, ++ AST_REDIRECTING_FROM_NUMBER_TYPE, ++ AST_REDIRECTING_FROM_NUMBER_PRESENTATION, ++ AST_REDIRECTING_TO_NUMBER, ++ AST_REDIRECTING_TO_NAME, ++ AST_REDIRECTING_TO_NUMBER_TYPE, ++ AST_REDIRECTING_TO_NUMBER_PRESENTATION, ++ AST_REDIRECTING_REASON, ++ AST_REDIRECTING_COUNT ++}; ++ ++int ast_redirecting_build_data(unsigned char *data, size_t datalen, const struct ast_party_redirecting *redirecting) ++{ ++ int32_t value; ++ size_t length; ++ size_t pos = 0; ++ ++ /* ++ * The size of integer values must be fixed in case the frame is ++ * shipped to another machine. ++ */ ++ ++ /* *************** Redirecting from party id *************** */ ++ if (redirecting->from.number) { ++ length = strlen(redirecting->from.number); ++ if (datalen < pos + (sizeof(data[0]) * 2) + length) { ++ ast_log(LOG_WARNING, "No space left for redirecting from number\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_FROM_NUMBER; ++ data[pos++] = length; ++ memcpy(data + pos, redirecting->from.number, length); ++ pos += length; ++ } ++ ++ if (redirecting->from.name) { ++ length = strlen(redirecting->from.name); ++ if (datalen < pos + (sizeof(data[0]) * 2) + length) { ++ ast_log(LOG_WARNING, "No space left for redirecting from name\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_FROM_NAME; ++ data[pos++] = length; ++ memcpy(data + pos, redirecting->from.name, length); ++ pos += length; ++ } ++ ++ if (datalen < pos + (sizeof(data[0]) * 2) + 1) { ++ ast_log(LOG_WARNING, "No space left for redirecting from type of number\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_FROM_NUMBER_TYPE; ++ data[pos++] = 1; ++ data[pos++] = redirecting->from.number_type; ++ ++ if (datalen < pos + (sizeof(data[0]) * 2) + 1) { ++ ast_log(LOG_WARNING, "No space left for redirecting from presentation\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_FROM_NUMBER_PRESENTATION; ++ data[pos++] = 1; ++ data[pos++] = redirecting->from.number_presentation; ++ ++ /* *************** Redirecting to party id *************** */ ++ if (redirecting->to.number) { ++ length = strlen(redirecting->to.number); ++ if (datalen < pos + (sizeof(data[0]) * 2) + length) { ++ ast_log(LOG_WARNING, "No space left for redirecting to number\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_TO_NUMBER; ++ data[pos++] = length; ++ memcpy(data + pos, redirecting->to.number, length); ++ pos += length; ++ } ++ ++ if (redirecting->to.name) { ++ length = strlen(redirecting->to.name); ++ if (datalen < pos + (sizeof(data[0]) * 2) + length) { ++ ast_log(LOG_WARNING, "No space left for redirecting to name\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_TO_NAME; ++ data[pos++] = length; ++ memcpy(data + pos, redirecting->to.name, length); ++ pos += length; ++ } ++ ++ if (datalen < pos + (sizeof(data[0]) * 2) + 1) { ++ ast_log(LOG_WARNING, "No space left for redirecting to type of number\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_TO_NUMBER_TYPE; ++ data[pos++] = 1; ++ data[pos++] = redirecting->to.number_type; ++ ++ if (datalen < pos + (sizeof(data[0]) * 2) + 1) { ++ ast_log(LOG_WARNING, "No space left for redirecting to presentation\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_TO_NUMBER_PRESENTATION; ++ data[pos++] = 1; ++ data[pos++] = redirecting->to.number_presentation; ++ ++ /* Redirecting reason */ ++ if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) { ++ ast_log(LOG_WARNING, "No space left for redirecting reason\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_REASON; ++ data[pos++] = sizeof(value); ++ value = htonl(redirecting->reason); ++ memcpy(data + pos, &value, sizeof(value)); ++ pos += sizeof(value); ++ ++ /* Redirecting count */ ++ if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) { ++ ast_log(LOG_WARNING, "No space left for redirecting count\n"); ++ return -1; ++ } ++ data[pos++] = AST_REDIRECTING_COUNT; ++ data[pos++] = sizeof(value); ++ value = htonl(redirecting->count); ++ memcpy(data + pos, &value, sizeof(value)); ++ pos += sizeof(value); ++ ++ return pos; ++} ++ ++int ast_redirecting_parse_data(const unsigned char *data, size_t datalen, struct ast_party_redirecting *redirecting) ++{ ++ size_t pos; ++ unsigned char ie_len; ++ unsigned char ie_id; ++ int32_t value; ++ ++ for (pos = 0; pos < datalen; pos += ie_len) { ++ if (datalen < pos + sizeof(ie_id) + sizeof(ie_len)) { ++ ast_log(LOG_WARNING, "Invalid redirecting update\n"); ++ return -1; ++ } ++ ie_id = data[pos++]; ++ ie_len = data[pos++]; ++ if (datalen < pos + ie_len) { ++ ast_log(LOG_WARNING, "Invalid redirecting update\n"); ++ return -1; ++ } ++ ++ switch (ie_id) { ++ case AST_REDIRECTING_FROM_NUMBER: ++ if (redirecting->from.number) { ++ ast_free(redirecting->from.number); ++ } ++ redirecting->from.number = ast_malloc(ie_len + 1); ++ if (redirecting->from.number) { ++ memcpy(redirecting->from.number, data + pos, ie_len); ++ redirecting->from.number[ie_len] = 0; ++ } ++ break; ++ case AST_REDIRECTING_FROM_NAME: ++ if (redirecting->from.name) { ++ ast_free(redirecting->from.name); ++ } ++ redirecting->from.name = ast_malloc(ie_len + 1); ++ if (redirecting->from.name) { ++ memcpy(redirecting->from.name, data + pos, ie_len); ++ redirecting->from.name[ie_len] = 0; ++ } ++ break; ++ case AST_REDIRECTING_FROM_NUMBER_TYPE: ++ if (ie_len != 1) { ++ ast_log(LOG_WARNING, "Invalid redirecting from type of number (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ redirecting->from.number_type = data[pos]; ++ break; ++ case AST_REDIRECTING_FROM_NUMBER_PRESENTATION: ++ if (ie_len != 1) { ++ ast_log(LOG_WARNING, "Invalid redirecting from presentation (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ redirecting->from.number_presentation = data[pos]; ++ break; ++ case AST_REDIRECTING_TO_NUMBER: ++ if (redirecting->to.number) { ++ ast_free(redirecting->to.number); ++ } ++ redirecting->to.number = ast_malloc(ie_len + 1); ++ if (redirecting->to.number) { ++ memcpy(redirecting->to.number, data + pos, ie_len); ++ redirecting->to.number[ie_len] = 0; ++ } ++ break; ++ case AST_REDIRECTING_TO_NAME: ++ if (redirecting->to.name) { ++ ast_free(redirecting->to.name); ++ } ++ redirecting->to.name = ast_malloc(ie_len + 1); ++ if (redirecting->to.name) { ++ memcpy(redirecting->to.name, data + pos, ie_len); ++ redirecting->to.name[ie_len] = 0; ++ } ++ break; ++ case AST_REDIRECTING_TO_NUMBER_TYPE: ++ if (ie_len != 1) { ++ ast_log(LOG_WARNING, "Invalid redirecting to type of number (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ redirecting->to.number_type = data[pos]; ++ break; ++ case AST_REDIRECTING_TO_NUMBER_PRESENTATION: ++ if (ie_len != 1) { ++ ast_log(LOG_WARNING, "Invalid redirecting to presentation (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ redirecting->to.number_presentation = data[pos]; ++ break; ++ case AST_REDIRECTING_REASON: ++ if (ie_len != sizeof(value)) { ++ ast_log(LOG_WARNING, "Invalid redirecting reason (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ memcpy(&value, data + pos, sizeof(value)); ++ redirecting->reason = ntohl(value); ++ break; ++ case AST_REDIRECTING_COUNT: ++ if (ie_len != sizeof(value)) { ++ ast_log(LOG_WARNING, "Invalid redirecting count (%u)\n", (unsigned) ie_len); ++ break; ++ } ++ memcpy(&value, data + pos, sizeof(value)); ++ redirecting->count = ntohl(value); ++ break; ++ default: ++ ast_log(LOG_DEBUG, "Unknown redirecting element: %u (%u)\n", (unsigned) ie_id, (unsigned) ie_len); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++void ast_channel_update_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting) ++{ ++ unsigned char data[1024]; /* This should be large enough */ ++ size_t datalen; ++ ++ datalen = ast_redirecting_build_data(data, sizeof(data), redirecting); ++ if (datalen == (size_t) -1) { ++ return; ++ } ++ ++ ast_indicate_data(chan, AST_CONTROL_REDIRECTING, data, datalen); ++} ++ ++void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct ast_party_redirecting *redirecting) ++{ ++ unsigned char data[1024]; /* This should be large enough */ ++ size_t datalen; ++ ++ datalen = ast_redirecting_build_data(data, sizeof(data), redirecting); ++ if (datalen == (size_t) -1) { ++ return; ++ } ++ ++ ast_queue_control_data(chan, AST_CONTROL_REDIRECTING, data, datalen); ++} ++ +Index: main/manager.c +=================================================================== +--- a/main/manager.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/manager.c (.../trunk) (revision 186562) +@@ -499,7 +499,7 @@ + } + + /*! \brief Get displayconnects config option. +- * \param s manager session to get parameter from. ++ * \param session manager session to get parameter from. + * \return displayconnects config option value. + */ + static int manager_displayconnects (struct mansession_session *session) +@@ -1759,22 +1759,39 @@ + static char mandescr_hangup[] = + "Description: Hangup a channel\n" + "Variables: \n" +-" Channel: The channel name to be hungup\n"; ++" Channel: The channel name to be hungup\n" ++" Cause: numeric hangup cause\n"; + + static int action_hangup(struct mansession *s, const struct message *m) + { + struct ast_channel *c = NULL; ++ int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */ + const char *name = astman_get_header(m, "Channel"); ++ const char *cause = astman_get_header(m, "Cause"); + if (ast_strlen_zero(name)) { + astman_send_error(s, m, "No channel specified"); + return 0; + } ++ if (!ast_strlen_zero(cause)) { ++ char *endptr; ++ causecode = strtol(cause, &endptr, 10); ++ if (causecode < 0 || causecode > 127 || *endptr != '\0') { ++ ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause); ++ /* keep going, better to hangup without cause than to not hang up at all */ ++ causecode = 0; /* do not set channel's hangupcause */ ++ } ++ } + c = ast_get_channel_by_name_locked(name); + if (!c) { + astman_send_error(s, m, "No such channel"); + return 0; + } +- ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT); ++ if (causecode > 0) { ++ ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n", ++ c->name, causecode, c->hangupcause); ++ c->hangupcause = causecode; ++ } ++ ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT); + ast_channel_unlock(c); + astman_send_ack(s, m, "Channel Hungup"); + return 0; +@@ -3803,7 +3820,7 @@ + * properties of the rand() function (and the constantcy of s), that + * won't happen twice in a row. + */ +- while ((session->managerid = rand() ^ (unsigned long) session) == 0); ++ while ((session->managerid = ast_random() ^ (unsigned long) session) == 0); + session->last_ev = grab_last(); + AST_LIST_HEAD_INIT_NOLOCK(&session->datastores); + AST_LIST_LOCK(&sessions); +Index: main/tdd.c +=================================================================== +--- a/main/tdd.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/tdd.c (.../trunk) (revision 186562) +@@ -274,7 +274,9 @@ + PUT_TDD_STOP; /* Stop bit */ \ + } while(0); + +-/*! Generate TDD hold tone */ ++/*! Generate TDD hold tone ++ * \param buf Result buffer ++ * \todo How big should this be??? */ + int tdd_gen_holdtone(unsigned char *buf) + { + int bytes = 0; +Index: main/ast_expr2f.c +=================================================================== +--- a/main/ast_expr2f.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/ast_expr2f.c (.../trunk) (revision 186562) +@@ -1962,8 +1962,8 @@ + + /** Setup the input buffer state to scan the given bytes. The next call to ast_yylex() will + * scan from a @e copy of @a bytes. +- * @param bytes the byte buffer to scan +- * @param len the number of bytes in the buffer pointed to by @a bytes. ++ * @param yybytes the byte buffer to scan ++ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +@@ -2124,7 +2124,7 @@ + } + + /** Set the current column. +- * @param line_number ++ * @param column_no + * @param yyscanner The scanner object. + */ + void ast_yyset_column (int column_no , yyscan_t yyscanner) +@@ -2387,19 +2387,12 @@ + + int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan) + { +- struct parse_io io; ++ struct parse_io io = { .string = expr, .chan = chan }; + int return_value = 0; +- +- memset(&io, 0, sizeof(io)); +- io.string = expr; /* to pass to the error routine */ +- io.chan = chan; +- ++ + ast_yylex_init(&io.scanner); +- + ast_yy_scan_string(expr, io.scanner); +- + ast_yyparse ((void *) &io); +- + ast_yylex_destroy(io.scanner); + + if (!io.val) { +Index: main/features.c +=================================================================== +--- a/main/features.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/features.c (.../trunk) (revision 186562) +@@ -889,9 +889,6 @@ + return masq_park_call(rchan, peer, timeout, extout, 1, NULL); + } + +-#define FEATURE_SENSE_CHAN (1 << 0) +-#define FEATURE_SENSE_PEER (1 << 1) +- + /*! + * \brief set caller and callee according to the direction + * \param caller, callee, peer, chan, sense +@@ -1390,6 +1387,7 @@ + struct ast_bridge_config bconfig; + struct ast_frame *f; + int l; ++ struct ast_party_connected_line connected_line = {{0,},}; + struct ast_datastore *features_datastore; + struct ast_dial_features *dialfeatures = NULL; + +@@ -1481,6 +1479,17 @@ + memset(&bconfig,0,sizeof(struct ast_bridge_config)); + ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT); + ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT); ++ /* We need to get the transferer's connected line information copied ++ * at this point because he is likely to hang up during the bridge with ++ * newchan. This info will be used down below before bridging the ++ * transferee and newchan ++ * ++ * As a result, we need to be sure to free this data before returning ++ * or overwriting it. ++ */ ++ ast_channel_lock(transferer); ++ ast_party_connected_line_copy(&connected_line, &transferer->connected); ++ ast_channel_unlock(transferer); + res = ast_bridge_call(transferer, newchan, &bconfig); + if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) { + ast_hangup(newchan); +@@ -1488,10 +1497,12 @@ + ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); + finishup(transferee); + transferer->_softhangup = 0; ++ ast_party_connected_line_free(&connected_line); + return AST_FEATURE_RETURN_SUCCESS; + } + if (check_compat(transferee, newchan)) { + finishup(transferee); ++ ast_party_connected_line_free(&connected_line); + return -1; + } + ast_indicate(transferee, AST_CONTROL_UNHOLD); +@@ -1502,11 +1513,13 @@ + || ast_check_hangup(transferee) + || ast_check_hangup(newchan)) { + ast_hangup(newchan); ++ ast_party_connected_line_free(&connected_line); + return -1; + } + xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name); + if (!xferchan) { + ast_hangup(newchan); ++ ast_party_connected_line_free(&connected_line); + return -1; + } + /* Make formats okay */ +@@ -1526,6 +1539,7 @@ + if (!(tobj = ast_calloc(1, sizeof(*tobj)))) { + ast_hangup(xferchan); + ast_hangup(newchan); ++ ast_party_connected_line_free(&connected_line); + return -1; + } + +@@ -1560,6 +1574,18 @@ + tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan); + } + ++ /* Due to a limitation regarding when callerID is set on a Local channel, ++ * we use the transferer's connected line information here. ++ */ ++ connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_update_connected_line(xferchan, &connected_line); ++ ast_channel_lock(xferchan); ++ ast_connected_line_copy_from_caller(&connected_line, &xferchan->cid); ++ ast_channel_unlock(xferchan); ++ connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_update_connected_line(newchan, &connected_line); ++ ast_party_connected_line_free(&connected_line); ++ + if (ast_stream_and_wait(newchan, xfersound, "")) + ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); + bridge_call_thread_launch(tobj); +@@ -1656,11 +1682,24 @@ + tobj->chan = newchan; + tobj->peer = xferchan; + tobj->bconfig = *config; +- ++ + if (tobj->bconfig.end_bridge_callback_data_fixup) { + tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan); + } + ++ ast_channel_lock(newchan); ++ ast_connected_line_copy_from_caller(&connected_line, &newchan->cid); ++ ast_channel_unlock(newchan); ++ connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_update_connected_line(xferchan, &connected_line); ++ ast_channel_lock(xferchan); ++ ast_connected_line_copy_from_caller(&connected_line, &xferchan->cid); ++ ast_channel_unlock(xferchan); ++ connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; ++ ast_channel_update_connected_line(newchan, &connected_line); ++ ++ ast_party_connected_line_free(&connected_line); ++ + if (ast_stream_and_wait(newchan, xfersound, "")) + ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); + bridge_call_thread_launch(tobj); +@@ -1969,53 +2008,42 @@ + } + + /*! +- * \brief Check the dynamic features +- * \param chan,peer,config,code,sense ++ * \brief Helper function for feature_interpret and ast_feature_detect ++ * \param chan,peer,config,code,sense,dynamic_features char buf,feature flags,operation,feature + * + * Lock features list, browse for code, unlock list ++ * If a feature is found and the operation variable is set, that feature's ++ * operation is executed. The first feature found is copied to the feature parameter. + * \retval res on success. + * \retval -1 on failure. + */ +-static int feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense) ++static int feature_interpret_helper(struct ast_channel *chan, struct ast_channel *peer, ++ struct ast_bridge_config *config, char *code, int sense, char *dynamic_features_buf, ++ struct ast_flags *features, int operation, struct ast_call_feature *feature) + { + int x; +- struct ast_flags features; +- struct ast_call_feature *feature; + struct feature_group *fg = NULL; + struct feature_group_exten *fge; +- const char *peer_dynamic_features, *chan_dynamic_features; +- char dynamic_features_buf[128]; ++ struct ast_call_feature *tmpfeature; + char *tmp, *tok; + int res = AST_FEATURE_RETURN_PASSDIGITS; + int feature_detected = 0; + +- if (sense == FEATURE_SENSE_CHAN) { +- ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL); ++ if (!(peer && chan && config) && operation) { ++ return -1; /* can not run feature operation */ + } +- else { +- ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL); +- } + +- ast_channel_lock(peer); +- peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),"")); +- ast_channel_unlock(peer); +- +- ast_channel_lock(chan); +- chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),"")); +- ast_channel_unlock(chan); +- +- snprintf(dynamic_features_buf, sizeof(dynamic_features_buf), "%s%s%s", S_OR(chan_dynamic_features, ""), chan_dynamic_features && peer_dynamic_features ? "#" : "", S_OR(peer_dynamic_features,"")); +- +- ast_debug(3, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features_buf); +- + ast_rwlock_rdlock(&features_lock); + for (x = 0; x < FEATURES_COUNT; x++) { +- if ((ast_test_flag(&features, builtin_features[x].feature_mask)) && ++ if ((ast_test_flag(features, builtin_features[x].feature_mask)) && + !ast_strlen_zero(builtin_features[x].exten)) { + /* Feature is up for consideration */ + if (!strcmp(builtin_features[x].exten, code)) { + ast_debug(3, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten); +- res = builtin_features[x].operation(chan, peer, config, code, sense, NULL); ++ if (operation) { ++ res = builtin_features[x].operation(chan, peer, config, code, sense, NULL); ++ } ++ memcpy(feature, &builtin_features[x], sizeof(feature)); + feature_detected = 1; + break; + } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) { +@@ -2026,8 +2054,9 @@ + } + ast_rwlock_unlock(&features_lock); + +- if (ast_strlen_zero(dynamic_features_buf) || feature_detected) ++ if (ast_strlen_zero(dynamic_features_buf) || feature_detected) { + return res; ++ } + + tmp = dynamic_features_buf; + +@@ -2040,8 +2069,10 @@ + AST_LIST_TRAVERSE(&fg->features, fge, entry) { + if (strcasecmp(fge->exten, code)) + continue; +- +- res = fge->feature->operation(chan, peer, config, code, sense, fge->feature); ++ if (operation) { ++ res = fge->feature->operation(chan, peer, config, code, sense, fge->feature); ++ } ++ memcpy(feature, fge->feature, sizeof(feature)); + if (res != AST_FEATURE_RETURN_KEEPTRYING) { + AST_RWLIST_UNLOCK(&feature_groups); + break; +@@ -2056,33 +2087,78 @@ + + AST_RWLIST_RDLOCK(&feature_list); + +- if (!(feature = find_dynamic_feature(tok))) { ++ if (!(tmpfeature = find_dynamic_feature(tok))) { + AST_RWLIST_UNLOCK(&feature_list); + continue; + } +- ++ + /* Feature is up for consideration */ +- if (!strcmp(feature->exten, code)) { +- ast_verb(3, " Feature Found: %s exten: %s\n",feature->sname, tok); +- res = feature->operation(chan, peer, config, code, sense, feature); ++ if (!strcmp(tmpfeature->exten, code)) { ++ ast_verb(3, " Feature Found: %s exten: %s\n",tmpfeature->sname, tok); ++ if (operation) { ++ res = tmpfeature->operation(chan, peer, config, code, sense, tmpfeature); ++ } ++ memcpy(feature, tmpfeature, sizeof(feature)); + if (res != AST_FEATURE_RETURN_KEEPTRYING) { + AST_RWLIST_UNLOCK(&feature_list); + break; + } + res = AST_FEATURE_RETURN_PASSDIGITS; +- } else if (!strncmp(feature->exten, code, strlen(code))) ++ } else if (!strncmp(tmpfeature->exten, code, strlen(code))) + res = AST_FEATURE_RETURN_STOREDIGITS; + + AST_RWLIST_UNLOCK(&feature_list); + } +- ++ + return res; + } + ++/*! ++ * \brief Check the dynamic features ++ * \param chan,peer,config,code,sense ++ * ++ * \retval res on success. ++ * \retval -1 on failure. ++*/ ++ ++static int feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense) { ++ ++ char dynamic_features_buf[128]; ++ const char *peer_dynamic_features, *chan_dynamic_features; ++ struct ast_flags features; ++ struct ast_call_feature feature; ++ if (sense == FEATURE_SENSE_CHAN) { ++ ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL); ++ } ++ else { ++ ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL); ++ } ++ ++ ast_channel_lock(peer); ++ peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),"")); ++ ast_channel_unlock(peer); ++ ++ ast_channel_lock(chan); ++ chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),"")); ++ ast_channel_unlock(chan); ++ ++ snprintf(dynamic_features_buf, sizeof(dynamic_features_buf), "%s%s%s", S_OR(chan_dynamic_features, ""), chan_dynamic_features && peer_dynamic_features ? "#" : "", S_OR(peer_dynamic_features,"")); ++ ++ ast_debug(3, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features_buf); ++ ++ return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, 1, &feature); ++} ++ ++ ++int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, char *code, struct ast_call_feature *feature) { ++ ++ return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, 0, feature); ++} ++ + static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) + { + int x; +- ++ + ast_clear_flag(config, AST_FLAGS_ALL); + + ast_rwlock_rdlock(&features_lock); +@@ -2097,7 +2173,7 @@ + ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1); + } + ast_rwlock_unlock(&features_lock); +- ++ + if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) { + const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"); + +@@ -2163,6 +2239,10 @@ + ast_channel_inherit_variables(caller, chan); + pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller->name); + ++ ast_channel_lock(chan); ++ ast_connected_line_copy_from_caller(&chan->connected, &caller->cid); ++ ast_channel_unlock(chan); ++ + if (ast_call(chan, data, timeout)) { + ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data); + goto done; +@@ -2232,6 +2312,8 @@ + f = NULL; + ready=1; + break; ++ } else if (f->subclass == AST_CONTROL_CONNECTED_LINE) { ++ ast_indicate_data(caller, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); + } else if (f->subclass != -1) { + ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass); + } +@@ -4410,8 +4492,19 @@ + struct ast_channel *cur = ast_channel_search_locked(find_channel_by_group, chan); + + if (cur) { ++ struct ast_party_connected_line connected_caller; ++ + int res = -1; + ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name); ++ ++ connected_caller = cur->connected; ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_update_connected_line(chan, &connected_caller); ++ ++ ast_party_connected_line_collect_caller(&connected_caller, &chan->cid); ++ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ++ ast_channel_queue_connected_line_update(chan, &connected_caller); ++ + res = ast_answer(chan); + if (res) + ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); +Index: main/http.c +=================================================================== +--- a/main/http.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/http.c (.../trunk) (revision 186562) +@@ -213,7 +213,7 @@ + "Server: Asterisk/%s\r\n" + "Date: %s\r\n" + "Connection: close\r\n" +- "Cache-Control: no-cache, no-store\r\n" ++ "Cache-Control: private\r\n" + "Content-Length: %d\r\n" + "Content-type: %s\r\n\r\n", + ast_get_version(), buf, (int) st.st_size, mtype); +Index: main/app.c +=================================================================== +--- a/main/app.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/app.c (.../trunk) (revision 186562) +@@ -52,7 +52,7 @@ + #include "asterisk/linkedlists.h" + #include "asterisk/threadstorage.h" + +-AST_THREADSTORAGE_PUBLIC(global_app_buf); ++AST_THREADSTORAGE_PUBLIC(ast_str_thread_global_buf); + + + #define MAX_OTHER_FORMATS 10 +Index: main/bridging.c +=================================================================== +--- a/main/bridging.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/bridging.c (.../trunk) (revision 186562) +@@ -319,7 +319,7 @@ + /* Move channels around for priority reasons if we have more than one channel in our array */ + if (bridge->array_num > 1) { + struct ast_channel *first = bridge->array[0]; +- memmove(bridge->array, bridge->array + 1, sizeof(bridge->array) - 1); ++ memmove(bridge->array, bridge->array + 1, sizeof(struct ast_channel *) * (bridge->array_num - 1)); + bridge->array[(bridge->array_num - 1)] = first; + } + +Index: main/stdtime/localtime.c +=================================================================== +--- a/main/stdtime/localtime.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/stdtime/localtime.c (.../trunk) (revision 186562) +@@ -310,7 +310,11 @@ + sp->wd[1] = -1; + } + /* or if the symlink itself changes (or the real file is here, if path is not a symlink) */ +- sp->wd[0] = inotify_add_watch(inotify_fd, path, IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_CLOSE_WRITE | IN_DONT_FOLLOW); ++ sp->wd[0] = inotify_add_watch(inotify_fd, path, IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_CLOSE_WRITE ++#ifdef IN_DONT_FOLLOW /* Only defined in glibc 2.5 and above */ ++ | IN_DONT_FOLLOW ++#endif ++ ); + } + } + #else +Index: main/Makefile +=================================================================== +--- a/main/Makefile (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/Makefile (.../trunk) (revision 186562) +@@ -20,7 +20,7 @@ + OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \ + translate.o file.o pbx.o cli.o md5.o term.o heap.o \ + ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \ +- cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \ ++ cdr.o tdd.o acl.o udptl.o manager.o asterisk.o \ + dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \ + astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \ + utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \ +@@ -29,7 +29,7 @@ + strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \ + astobj2.o hashtab.o global_datastores.o version.o \ + features.o taskprocessor.o timing.o datastore.o xml.o xmldoc.o \ +- strings.o bridging.o poll.o ++ strings.o bridging.o poll.o rtp_engine.o stun.o + + # we need to link in the objects statically, not as a library, because + # otherwise modules will not have them available if none of the static +@@ -103,6 +103,10 @@ + endif + endif + ++ifeq ($(GNU_LD),1) ++ASTLINK+=-Wl,--version-script,asterisk.exports ++endif ++ + CHECK_SUBDIR: # do nothing, just make sure that we recurse in the subdir/ + + editline/libedit.a: CHECK_SUBDIR +@@ -163,15 +167,14 @@ + GMIMELDFLAGS+=$(GMIME_LIB) + endif + +-$(MAIN_TGT): $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS) ++$(MAIN_TGT): $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS) asterisk.exports + @$(CC) -c -o buildinfo.o $(ASTCFLAGS) buildinfo.c +- $(ECHO_PREFIX) echo " [LD] $^ -> $@" ++ $(ECHO_PREFIX) echo " [LD] $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS) -> $@" + ifneq ($(findstring chan_h323,$(MENUSELECT_CHANNELS)),) +- $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(GMIMELDFLAGS) ++ $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS) buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(GMIMELDFLAGS) + else +- $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $(H323LDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(H323LDLIBS) $(GMIMELDFLAGS) ++ $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $(H323LDFLAGS) $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS) buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(H323LDLIBS) $(GMIMELDFLAGS) + endif +- $(CMD_PREFIX) $(ASTTOPDIR)/build_tools/strip_nonapi $@ || rm $@ + + clean:: + rm -f asterisk +Index: main/event.c +=================================================================== +--- a/main/event.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/event.c (.../trunk) (revision 186562) +@@ -28,6 +28,7 @@ + ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + + #include "asterisk/_private.h" ++ + #include "asterisk/event.h" + #include "asterisk/linkedlists.h" + #include "asterisk/dlinkedlists.h" +@@ -36,6 +37,7 @@ + #include "asterisk/unaligned.h" + #include "asterisk/utils.h" + #include "asterisk/taskprocessor.h" ++#include "asterisk/astobj2.h" + + struct ast_taskprocessor *event_dispatcher; + +@@ -55,6 +57,16 @@ + } __attribute__((packed)); + + /*! ++ * \brief The payload for a string information element ++ */ ++struct ast_event_ie_str_payload { ++ /*! \brief A hash calculated with ast_str_hash(), to speed up comparisons */ ++ uint32_t hash; ++ /*! \brief The actual string, null terminated */ ++ char str[1]; ++} __attribute__((packed)); ++ ++/*! + * \brief An event + * + * An ast_event consists of an event header (this structure), and zero or +@@ -74,9 +86,18 @@ + unsigned char payload[0]; + } __attribute__((packed)); + ++ ++/*! ++ * \brief A holder for an event ++ * ++ * \details This struct used to have more of a purpose than it does now. ++ * It is used to hold events in the event cache. It can be completely removed ++ * if one of these two things is done: ++ * - ast_event gets changed such that it never has to be realloc()d ++ * - astobj2 is updated so that you can realloc() an astobj2 object ++ */ + struct ast_event_ref { + struct ast_event *event; +- AST_LIST_ENTRY(ast_event_ref) entry; + }; + + struct ast_event_ie_val { +@@ -85,7 +106,10 @@ + enum ast_event_ie_pltype ie_pltype; + union { + uint32_t uint; +- const char *str; ++ struct { ++ uint32_t hash; ++ const char *str; ++ }; + void *raw; + } payload; + size_t raw_datalen; +@@ -107,13 +131,57 @@ + * The event subscribers are indexed by which event they are subscribed to */ + static AST_RWDLLIST_HEAD(ast_event_sub_list, ast_event_sub) ast_event_subs[AST_EVENT_TOTAL]; + +-/*! \brief Cached events +- * The event cache is indexed on the event type. The purpose of this is +- * for events that express some sort of state. So, when someone first +- * needs to know this state, it can get the last known state from the cache. */ +-static AST_RWLIST_HEAD(ast_event_ref_list, ast_event_ref) ast_event_cache[AST_EVENT_TOTAL]; ++static int ast_event_cmp(void *obj, void *arg, int flags); ++static int ast_event_hash_mwi(const void *obj, const int flags); ++static int ast_event_hash_devstate(const void *obj, const int flags); ++static int ast_event_hash_devstate_change(const void *obj, const int flags); + ++#ifdef LOW_MEMORY ++#define NUM_CACHE_BUCKETS 17 ++#else ++#define NUM_CACHE_BUCKETS 563 ++#endif ++ ++#define MAX_CACHE_ARGS 8 ++ + /*! ++ * \brief Event types that are kept in the cache. ++ */ ++static struct { ++ /*! ++ * \brief Container of cached events ++ * ++ * \details This gets allocated in ast_event_init() when Asterisk starts ++ * for the event types declared as using the cache. ++ */ ++ struct ao2_container *container; ++ /*! \brief Event type specific hash function */ ++ ao2_hash_fn *hash_fn; ++ /*! ++ * \brief Information Elements used for caching ++ * ++ * \details This array is the set of information elements that will be unique ++ * among all events in the cache for this event type. When a new event gets ++ * cached, a previous event with the same values for these information elements ++ * will be replaced. ++ */ ++ enum ast_event_ie_type cache_args[MAX_CACHE_ARGS]; ++} ast_event_cache[AST_EVENT_TOTAL] = { ++ [AST_EVENT_MWI] = { ++ .hash_fn = ast_event_hash_mwi, ++ .cache_args = { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_CONTEXT }, ++ }, ++ [AST_EVENT_DEVICE_STATE] = { ++ .hash_fn = ast_event_hash_devstate, ++ .cache_args = { AST_EVENT_IE_DEVICE, }, ++ }, ++ [AST_EVENT_DEVICE_STATE_CHANGE] = { ++ .hash_fn = ast_event_hash_devstate_change, ++ .cache_args = { AST_EVENT_IE_DEVICE, AST_EVENT_IE_EID, }, ++ }, ++}; ++ ++/*! + * The index of each entry _must_ match the event type number! + */ + static struct event_name { +@@ -237,6 +305,8 @@ + { + switch (ie_val->ie_pltype) { + case AST_EVENT_IE_PLTYPE_STR: ++ ast_free((char *) ie_val->payload.str); ++ break; + case AST_EVENT_IE_PLTYPE_RAW: + ast_free(ie_val->payload.raw); + break; +@@ -328,7 +398,8 @@ + return res; + } + +-static int match_ie_val(struct ast_event *event, struct ast_event_ie_val *ie_val, struct ast_event *event2) ++static int match_ie_val(const struct ast_event *event, ++ const struct ast_event_ie_val *ie_val, const struct ast_event *event2) + { + if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT) { + uint32_t val = event2 ? ast_event_get_ie_uint(event2, ie_val->ie_type) : ie_val->payload.uint; +@@ -338,9 +409,19 @@ + } + + if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR) { +- const char *str = event2 ? ast_event_get_ie_str(event2, ie_val->ie_type) : ie_val->payload.str; +- if (str && !strcmp(str, ast_event_get_ie_str(event, ie_val->ie_type))) ++ const char *str; ++ uint32_t hash; ++ ++ hash = event2 ? ast_event_get_ie_str_hash(event2, ie_val->ie_type) : ie_val->payload.hash; ++ if (hash != ast_event_get_ie_str_hash(event, ie_val->ie_type)) { ++ return 0; ++ } ++ ++ str = event2 ? ast_event_get_ie_str(event2, ie_val->ie_type) : ie_val->payload.str; ++ if (str && !strcmp(str, ast_event_get_ie_str(event, ie_val->ie_type))) { + return 1; ++ } ++ + return 0; + } + +@@ -360,28 +441,34 @@ + return 0; + } + +-/*! \brief Dump the event cache for the subscribed event type */ +-void ast_event_dump_cache(const struct ast_event_sub *event_sub) ++static int dump_cache_cb(void *obj, void *arg, int flags) + { +- struct ast_event_ref *event_ref; +- enum ast_event_type type = event_sub->type; ++ const struct ast_event_ref *event_ref = obj; ++ const struct ast_event *event = event_ref->event; ++ const struct ast_event_sub *event_sub = arg; ++ struct ast_event_ie_val *ie_val = NULL; + +- AST_RWLIST_RDLOCK(&ast_event_cache[type]); +- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[type], event_ref, entry) { +- struct ast_event_ie_val *ie_val; +- AST_LIST_TRAVERSE(&event_sub->ie_vals, ie_val, entry) { +- if (!match_ie_val(event_ref->event, ie_val, NULL)) +- break; ++ AST_LIST_TRAVERSE(&event_sub->ie_vals, ie_val, entry) { ++ if (!match_ie_val(event, ie_val, NULL)) { ++ break; + } +- if (!ie_val) { +- /* All parameters were matched on this cache entry, so dump it */ +- event_sub->cb(event_ref->event, event_sub->userdata); +- } + } +- AST_RWLIST_TRAVERSE_SAFE_END +- AST_RWLIST_UNLOCK(&ast_event_cache[type]); ++ ++ if (!ie_val) { ++ /* All parameters were matched on this cache entry, so dump it */ ++ event_sub->cb(event, event_sub->userdata); ++ } ++ ++ return 0; + } + ++/*! \brief Dump the event cache for the subscribed event type */ ++void ast_event_dump_cache(const struct ast_event_sub *event_sub) ++{ ++ ao2_callback(ast_event_cache[event_sub->type].container, OBJ_NODATA, ++ dump_cache_cb, (void *) event_sub); ++} ++ + static struct ast_event *gen_sub_event(struct ast_event_sub *sub) + { + struct ast_event_ie_val *ie_val; +@@ -536,6 +623,8 @@ + return -1; + } + ++ ie_val->payload.hash = ast_str_hash(str); ++ + AST_LIST_INSERT_TAIL(&sub->ie_vals, ie_val, entry); + + return 0; +@@ -703,7 +792,11 @@ + + const char *ast_event_iterator_get_ie_str(struct ast_event_iterator *iterator) + { +- return (const char*)iterator->ie->ie_payload; ++ const struct ast_event_ie_str_payload *str_payload; ++ ++ str_payload = (struct ast_event_ie_str_payload *) iterator->ie->ie_payload; ++ ++ return str_payload->str; + } + + void *ast_event_iterator_get_ie_raw(struct ast_event_iterator *iterator) +@@ -725,9 +818,22 @@ + return ie_val ? ntohl(get_unaligned_uint32(ie_val)) : 0; + } + ++uint32_t ast_event_get_ie_str_hash(const struct ast_event *event, enum ast_event_ie_type ie_type) ++{ ++ const struct ast_event_ie_str_payload *str_payload; ++ ++ str_payload = ast_event_get_ie_raw(event, ie_type); ++ ++ return str_payload->hash; ++} ++ + const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type) + { +- return ast_event_get_ie_raw(event, ie_type); ++ const struct ast_event_ie_str_payload *str_payload; ++ ++ str_payload = ast_event_get_ie_raw(event, ie_type); ++ ++ return str_payload->str; + } + + const void *ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type) +@@ -746,7 +852,16 @@ + int ast_event_append_ie_str(struct ast_event **event, enum ast_event_ie_type ie_type, + const char *str) + { +- return ast_event_append_ie_raw(event, ie_type, str, strlen(str) + 1); ++ struct ast_event_ie_str_payload *str_payload; ++ size_t payload_len; ++ ++ payload_len = sizeof(*str_payload) + strlen(str); ++ str_payload = alloca(payload_len); ++ ++ strcpy(str_payload->str, str); ++ str_payload->hash = ast_str_hash(str); ++ ++ return ast_event_append_ie_raw(event, ie_type, str_payload, payload_len); + } + + int ast_event_append_ie_uint(struct ast_event **event, enum ast_event_ie_type ie_type, +@@ -839,7 +954,7 @@ + if (!ast_event_get_ie_raw(event, AST_EVENT_IE_EID)) { + /* If the event is originating on this server, add the server's + * entity ID to the event. */ +- ast_event_append_ie_raw(&event, AST_EVENT_IE_EID, &g_eid, sizeof(g_eid)); ++ ast_event_append_ie_raw(&event, AST_EVENT_IE_EID, &ast_eid_default, sizeof(ast_eid_default)); + } + + return event; +@@ -850,10 +965,11 @@ + ast_free(event); + } + +-static void ast_event_ref_destroy(struct ast_event_ref *event_ref) ++static void ast_event_ref_destroy(void *obj) + { ++ struct ast_event_ref *event_ref = obj; ++ + ast_event_destroy(event_ref->event); +- ast_free(event_ref); + } + + static struct ast_event *ast_event_dup(const struct ast_event *event) +@@ -863,9 +979,10 @@ + + event_len = ast_event_get_size(event); + +- if (!(dup_event = ast_calloc(1, event_len))) ++ if (!(dup_event = ast_calloc(1, event_len))) { + return NULL; +- ++ } ++ + memcpy(dup_event, event, event_len); + + return dup_event; +@@ -876,139 +993,122 @@ + va_list ap; + enum ast_event_ie_type ie_type; + struct ast_event *dup_event = NULL; +- struct ast_event_ref *event_ref; +- struct ast_event_ie_val *cache_arg; +- AST_LIST_HEAD_NOLOCK_STATIC(cache_args, ast_event_ie_val); ++ struct ast_event_ref *cached_event_ref; ++ struct ast_event *cache_arg_event; ++ struct ast_event_ref tmp_event_ref = { ++ .event = NULL, ++ }; ++ struct ao2_container *container = NULL; + + if (type >= AST_EVENT_TOTAL) { + ast_log(LOG_ERROR, "%u is an invalid type!\n", type); + return NULL; + } + ++ if (!(container = ast_event_cache[type].container)) { ++ ast_log(LOG_ERROR, "%u is not a cached event type\n", type); ++ return NULL; ++ } ++ ++ if (!(cache_arg_event = ast_event_new(type, AST_EVENT_IE_END))) { ++ return NULL; ++ } ++ + va_start(ap, type); + for (ie_type = va_arg(ap, enum ast_event_type); + ie_type != AST_EVENT_IE_END; + ie_type = va_arg(ap, enum ast_event_type)) + { +- cache_arg = alloca(sizeof(*cache_arg)); +- memset(cache_arg, 0, sizeof(*cache_arg)); +- cache_arg->ie_type = ie_type; +- cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype); +- if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT) +- cache_arg->payload.uint = va_arg(ap, uint32_t); +- else if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR) +- cache_arg->payload.str = ast_strdupa(va_arg(ap, const char *)); +- else if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_RAW) { ++ enum ast_event_ie_pltype ie_pltype; ++ ++ ie_pltype = va_arg(ap, enum ast_event_ie_pltype); ++ ++ switch (ie_pltype) { ++ case AST_EVENT_IE_PLTYPE_UINT: ++ ast_event_append_ie_uint(&cache_arg_event, ie_type, va_arg(ap, uint32_t)); ++ break; ++ case AST_EVENT_IE_PLTYPE_STR: ++ ast_event_append_ie_str(&cache_arg_event, ie_type, va_arg(ap, const char *)); ++ break; ++ case AST_EVENT_IE_PLTYPE_RAW: ++ { + void *data = va_arg(ap, void *); + size_t datalen = va_arg(ap, size_t); +- cache_arg->payload.raw = alloca(datalen); +- memcpy(cache_arg->payload.raw, data, datalen); +- cache_arg->raw_datalen = datalen; ++ ast_event_append_ie_raw(&cache_arg_event, ie_type, data, datalen); + } +- AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry); ++ case AST_EVENT_IE_PLTYPE_EXISTS: ++ ast_log(LOG_WARNING, "PLTYPE_EXISTS not supported by this function\n"); ++ break; ++ case AST_EVENT_IE_PLTYPE_UNKNOWN: ++ break; ++ } + } + va_end(ap); + +- if (AST_LIST_EMPTY(&cache_args)) { +- ast_log(LOG_ERROR, "Events can not be retrieved from the cache without " +- "specifying at least one IE type!\n"); +- return NULL; +- } ++ tmp_event_ref.event = cache_arg_event; + +- AST_RWLIST_RDLOCK(&ast_event_cache[type]); +- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[type], event_ref, entry) { +- AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) { +- if (!match_ie_val(event_ref->event, cache_arg, NULL)) +- break; +- } +- if (!cache_arg) { +- /* All parameters were matched on this cache entry, so return it */ +- dup_event = ast_event_dup(event_ref->event); +- break; +- } ++ cached_event_ref = ao2_find(container, &tmp_event_ref, OBJ_POINTER); ++ ++ ast_event_destroy(cache_arg_event); ++ cache_arg_event = NULL; ++ ++ if (cached_event_ref) { ++ dup_event = ast_event_dup(cached_event_ref->event); ++ ao2_ref(cached_event_ref, -1); ++ cached_event_ref = NULL; + } +- AST_RWLIST_TRAVERSE_SAFE_END +- AST_RWLIST_UNLOCK(&ast_event_cache[type]); + + return dup_event; + } + ++static struct ast_event_ref *alloc_event_ref(void) ++{ ++ return ao2_alloc(sizeof(struct ast_event_ref), ast_event_ref_destroy); ++} ++ + /*! \brief Duplicate an event and add it to the cache + * \note This assumes this index in to the cache is locked */ +-static int ast_event_dup_and_cache(const struct ast_event *event) ++static int attribute_unused ast_event_dup_and_cache(const struct ast_event *event) + { + struct ast_event *dup_event; + struct ast_event_ref *event_ref; + +- if (!(dup_event = ast_event_dup(event))) ++ if (!(dup_event = ast_event_dup(event))) { + return -1; +- if (!(event_ref = ast_calloc(1, sizeof(*event_ref)))) ++ } ++ ++ if (!(event_ref = alloc_event_ref())) { ++ ast_event_destroy(dup_event); + return -1; +- ++ } ++ + event_ref->event = dup_event; + +- AST_LIST_INSERT_TAIL(&ast_event_cache[ntohs(event->type)], event_ref, entry); ++ ao2_link(ast_event_cache[ast_event_get_type(event)].container, event_ref); + ++ ao2_ref(event_ref, -1); ++ + return 0; + } + +-int ast_event_queue_and_cache(struct ast_event *event, ...) ++int ast_event_queue_and_cache(struct ast_event *event) + { +- va_list ap; +- enum ast_event_type ie_type; +- uint16_t host_event_type; +- struct ast_event_ref *event_ref; +- int res; +- struct ast_event_ie_val *cache_arg; +- AST_LIST_HEAD_NOLOCK_STATIC(cache_args, ast_event_ie_val); ++ struct ao2_container *container; ++ struct ast_event_ref tmp_event_ref = { ++ .event = event, ++ }; + +- host_event_type = ntohs(event->type); +- +- /* Invalid type */ +- if (host_event_type >= AST_EVENT_TOTAL) { +- ast_log(LOG_WARNING, "Someone tried to queue an event of invalid " +- "type '%d'!\n", host_event_type); +- return -1; ++ if (!(container = ast_event_cache[ast_event_get_type(event)].container)) { ++ ast_log(LOG_WARNING, "cache requested for non-cached event type\n"); ++ goto queue_event; + } + +- va_start(ap, event); +- for (ie_type = va_arg(ap, enum ast_event_type); +- ie_type != AST_EVENT_IE_END; +- ie_type = va_arg(ap, enum ast_event_type)) +- { +- cache_arg = alloca(sizeof(*cache_arg)); +- memset(cache_arg, 0, sizeof(*cache_arg)); +- cache_arg->ie_type = ie_type; +- cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype); +- if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_RAW) +- cache_arg->raw_datalen = va_arg(ap, size_t); +- AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry); +- } +- va_end(ap); ++ /* Remove matches from the cache */ ++ ao2_callback(container, OBJ_POINTER | OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, ++ ast_event_cmp, &tmp_event_ref); + +- if (AST_LIST_EMPTY(&cache_args)) { +- ast_log(LOG_ERROR, "Events can not be cached without specifying at " +- "least one IE type!\n"); +- return ast_event_queue(event); +- } +- +- AST_RWLIST_WRLOCK(&ast_event_cache[host_event_type]); +- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[host_event_type], event_ref, entry) { +- AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) { +- if (!match_ie_val(event_ref->event, cache_arg, event)) +- break; +- } +- if (!cache_arg) { +- /* All parameters were matched on this cache entry, so remove it */ +- AST_LIST_REMOVE_CURRENT(entry); +- ast_event_ref_destroy(event_ref); +- } +- } +- AST_RWLIST_TRAVERSE_SAFE_END; +- res = ast_event_dup_and_cache(event); +- AST_RWLIST_UNLOCK(&ast_event_cache[host_event_type]); +- +- return (ast_event_queue(event) || res) ? -1 : 0; ++queue_event: ++ return ast_event_queue(event); + } + + static int handle_event(void *data) +@@ -1024,22 +1124,25 @@ + AST_RWDLLIST_TRAVERSE(&ast_event_subs[host_event_type], sub, entry) { + struct ast_event_ie_val *ie_val; + AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) { +- if (!match_ie_val(event_ref->event, ie_val, NULL)) ++ if (!match_ie_val(event_ref->event, ie_val, NULL)) { + break; ++ } + } +- if (ie_val) ++ if (ie_val) { + continue; ++ } + sub->cb(event_ref->event, sub->userdata); + } + AST_RWDLLIST_UNLOCK(&ast_event_subs[host_event_type]); + + /* Now to subscribers to all event types */ + AST_RWDLLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]); +- AST_RWDLLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry) ++ AST_RWDLLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry) { + sub->cb(event_ref->event, sub->userdata); ++ } + AST_RWDLLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]); + +- ast_event_ref_destroy(event_ref); ++ ao2_ref(event_ref, -1); + + return 0; + } +@@ -1059,29 +1162,149 @@ + } + + /* If nobody has subscribed to this event type, throw it away now */ +- if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END) +- == AST_EVENT_SUB_NONE) { ++ if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END) ++ == AST_EVENT_SUB_NONE) { + ast_event_destroy(event); + return 0; + } + +- if (!(event_ref = ast_calloc(1, sizeof(*event_ref)))) ++ if (!(event_ref = alloc_event_ref())) { + return -1; ++ } + + event_ref->event = event; + + return ast_taskprocessor_push(event_dispatcher, handle_event, event_ref); + } + +-void ast_event_init(void) ++static int ast_event_hash_mwi(const void *obj, const int flags) + { ++ const struct ast_event *event = obj; ++ const char *mailbox = ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX); ++ const char *context = ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT); ++ ++ return ast_str_hash_add(context, ast_str_hash(mailbox)); ++} ++ ++/*! ++ * \internal ++ * \brief Hash function for AST_EVENT_DEVICE_STATE ++ * ++ * \param[in] obj an ast_event ++ * \param[in] flags unused ++ * ++ * \return hash value ++ */ ++static int ast_event_hash_devstate(const void *obj, const int flags) ++{ ++ const struct ast_event *event = obj; ++ ++ return ast_str_hash(ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE)); ++} ++ ++/*! ++ * \internal ++ * \brief Hash function for AST_EVENT_DEVICE_STATE_CHANGE ++ * ++ * \param[in] obj an ast_event ++ * \param[in] flags unused ++ * ++ * \return hash value ++ */ ++static int ast_event_hash_devstate_change(const void *obj, const int flags) ++{ ++ const struct ast_event *event = obj; ++ ++ return ast_str_hash(ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE)); ++} ++ ++static int ast_event_hash(const void *obj, const int flags) ++{ ++ const struct ast_event_ref *event_ref; ++ const struct ast_event *event; ++ ao2_hash_fn *hash_fn; ++ ++ event_ref = obj; ++ event = event_ref->event; ++ ++ if (!(hash_fn = ast_event_cache[ast_event_get_type(event)].hash_fn)) { ++ return 0; ++ } ++ ++ return hash_fn(event, flags); ++} ++ ++/*! ++ * \internal ++ * \brief Compare two events ++ * ++ * \param[in] obj the first event, as an ast_event_ref ++ * \param[in] arg the second event, as an ast_event_ref ++ * \param[in] flags unused ++ * ++ * \pre Both events must be the same type. ++ * \pre The event type must be declared as a cached event type in ast_event_cache ++ * ++ * \details This function takes two events, and determines if they are considered ++ * equivalent. The values of information elements specified in the cache arguments ++ * for the event type are used to determine if the events are equivalent. ++ * ++ * \retval 0 No match ++ * \retval CMP_MATCH The events are considered equivalent based on the cache arguments ++ */ ++static int ast_event_cmp(void *obj, void *arg, int flags) ++{ ++ struct ast_event_ref *event_ref, *event_ref2; ++ struct ast_event *event, *event2; ++ int res = CMP_MATCH; + int i; ++ enum ast_event_ie_type *cache_args; + +- for (i = 0; i < AST_EVENT_TOTAL; i++) ++ event_ref = obj; ++ event = event_ref->event; ++ ++ event_ref2 = arg; ++ event2 = event_ref2->event; ++ ++ cache_args = ast_event_cache[ast_event_get_type(event)].cache_args; ++ ++ for (i = 0; i < ARRAY_LEN(ast_event_cache[0].cache_args) && cache_args[i]; i++) { ++ struct ast_event_ie_val ie_val = { ++ .ie_type = cache_args[i], ++ }; ++ ++ if (!match_ie_val(event, &ie_val, event2)) { ++ res = 0; ++ break; ++ } ++ } ++ ++ return res; ++} ++ ++int ast_event_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < AST_EVENT_TOTAL; i++) { + AST_RWDLLIST_HEAD_INIT(&ast_event_subs[i]); ++ } + +- for (i = 0; i < AST_EVENT_TOTAL; i++) +- AST_RWLIST_HEAD_INIT(&ast_event_cache[i]); ++ for (i = 0; i < AST_EVENT_TOTAL; i++) { ++ if (!ast_event_cache[i].hash_fn) { ++ /* This event type is not cached. */ ++ continue; ++ } + +- event_dispatcher = ast_taskprocessor_get("core_event_dispatcher", 0); ++ if (!(ast_event_cache[i].container = ao2_container_alloc(NUM_CACHE_BUCKETS, ++ ast_event_hash, ast_event_cmp))) { ++ return -1; ++ } ++ } ++ ++ if (!(event_dispatcher = ast_taskprocessor_get("core_event_dispatcher", 0))) { ++ return -1; ++ } ++ ++ return 0; + } +Index: main/asterisk.c +=================================================================== +--- a/main/asterisk.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/asterisk.c (.../trunk) (revision 186562) +@@ -120,7 +120,6 @@ + #include "asterisk/cdr.h" + #include "asterisk/pbx.h" + #include "asterisk/enum.h" +-#include "asterisk/rtp.h" + #include "asterisk/http.h" + #include "asterisk/udptl.h" + #include "asterisk/app.h" +@@ -182,7 +181,7 @@ + + /*! @} */ + +-struct ast_eid g_eid; ++struct ast_eid ast_eid_default; + + /* XXX tmpdir is a subdir of the spool directory, and no way to remap it */ + char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR; +@@ -427,7 +426,7 @@ + return NULL; + } + +- ast_eid_to_str(eid_str, sizeof(eid_str), &g_eid); ++ ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default); + + ast_cli(a->fd, "\nPBX Core settings\n"); + ast_cli(a->fd, "-----------------\n"); +@@ -2398,11 +2397,12 @@ + int nummatches = 0; + char **matches; + int retval = CC_ERROR; +- char buf[2048]; ++ char buf[2048], savechr; + int res; + + LineInfo *lf = (LineInfo *)el_line(editline); + ++ savechr = *(char *)lf->cursor; + *(char *)lf->cursor = '\0'; + ptr = (char *)lf->cursor; + if (ptr) { +@@ -2428,8 +2428,10 @@ + char *mbuf; + int mlen = 0, maxmbuf = 2048; + /* Start with a 2048 byte buffer */ +- if (!(mbuf = ast_malloc(maxmbuf))) ++ if (!(mbuf = ast_malloc(maxmbuf))) { ++ lf->cursor[0] = savechr; + return (char *)(CC_ERROR); ++ } + snprintf(buf, sizeof(buf), "_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr); + fdsend(ast_consock, buf); + res = 0; +@@ -2438,8 +2440,10 @@ + if (mlen + 1024 > maxmbuf) { + /* Every step increment buffer 1024 bytes */ + maxmbuf += 1024; +- if (!(mbuf = ast_realloc(mbuf, maxmbuf))) ++ if (!(mbuf = ast_realloc(mbuf, maxmbuf))) { ++ lf->cursor[0] = savechr; + return (char *)(CC_ERROR); ++ } + } + /* Only read 1024 bytes at a time */ + res = read(ast_consock, mbuf + mlen, 1024); +@@ -2499,6 +2503,8 @@ + ast_free(matches); + } + ++ lf->cursor[0] = savechr; ++ + return (char *)(long)retval; + } + +@@ -2810,7 +2816,7 @@ + ast_copy_string(cfg_paths.socket_path, DEFAULT_SOCKET, sizeof(cfg_paths.socket_path)); + ast_copy_string(cfg_paths.run_dir, DEFAULT_RUN_DIR, sizeof(cfg_paths.run_dir)); + +- ast_set_default_eid(&g_eid); ++ ast_set_default_eid(&ast_eid_default); + + /* no asterisk.conf? no problem, use buildtime config! */ + if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { +@@ -2981,7 +2987,7 @@ + struct ast_eid tmp_eid; + if (!ast_str_to_eid(&tmp_eid, v->value)) { + ast_verbose("Successfully set global EID to '%s'\n", v->value); +- g_eid = tmp_eid; ++ ast_eid_default = tmp_eid; + } else + ast_verbose("Invalid Entity ID '%s' provided\n", v->value); + } else if (!strcasecmp(v->name, "lightbackground")) { +@@ -3502,7 +3508,10 @@ + } + #endif + +- ast_event_init(); ++ if (ast_event_init()) { ++ printf("%s", term_quit()); ++ exit(1); ++ } + + ast_makesocket(); + sigemptyset(&sigs); +@@ -3569,7 +3578,6 @@ + exit(1); + } + +- ast_rtp_init(); + ast_dsp_init(); + ast_udptl_init(); + +Index: main/timing.c +=================================================================== +--- a/main/timing.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/timing.c (.../trunk) (revision 186562) +@@ -38,6 +38,7 @@ + #include "asterisk/time.h" + #include "asterisk/heap.h" + #include "asterisk/module.h" ++#include "asterisk/poll-compat.h" + + struct timing_holder { + /*! Do _not_ move this from the beginning of the struct. */ +@@ -48,6 +49,11 @@ + + static struct ast_heap *timing_interfaces; + ++struct ast_timer { ++ int fd; ++ struct timing_holder *holder; ++}; ++ + static int timing_holder_cmp(void *_h1, void *_h2) + { + struct timing_holder *h1 = _h1; +@@ -63,16 +69,16 @@ + } + + void *_ast_register_timing_interface(struct ast_timing_interface *funcs, +- struct ast_module *mod) ++ struct ast_module *mod) + { + struct timing_holder *h; + + if (!funcs->timer_open || + !funcs->timer_close || +- !funcs->timer_set_rate || ++ !funcs->timer_set_rate || + !funcs->timer_ack || + !funcs->timer_get_event || +- !funcs->timer_get_max_rate || ++ !funcs->timer_get_max_rate || + !funcs->timer_enable_continuous || + !funcs->timer_disable_continuous) { + return NULL; +@@ -110,10 +116,11 @@ + return res; + } + +-int ast_timer_open(void) ++struct ast_timer *ast_timer_open(void) + { + int fd = -1; + struct timing_holder *h; ++ struct ast_timer *t = NULL; + + ast_heap_rdlock(timing_interfaces); + +@@ -122,124 +129,88 @@ + ast_module_ref(h->mod); + } + ++ if (fd != -1) { ++ if (!(t = ast_calloc(1, sizeof(*t)))) { ++ h->iface->timer_close(fd); ++ } else { ++ t->fd = fd; ++ t->holder = h; ++ } ++ } ++ + ast_heap_unlock(timing_interfaces); + +- return fd; ++ return t; + } + +-void ast_timer_close(int timer) ++void ast_timer_close(struct ast_timer *handle) + { +- struct timing_holder *h; ++ handle->holder->iface->timer_close(handle->fd); ++ ast_module_unref(handle->holder->mod); ++ ast_free(handle); ++} + +- ast_heap_rdlock(timing_interfaces); +- +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- h->iface->timer_close(timer); +- ast_module_unref(h->mod); +- } +- +- ast_heap_unlock(timing_interfaces); ++int ast_timer_fd(const struct ast_timer *handle) ++{ ++ return handle->fd; + } + +-int ast_timer_set_rate(int handle, unsigned int rate) ++int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate) + { +- struct timing_holder *h; + int res = -1; + +- ast_heap_rdlock(timing_interfaces); ++ res = handle->holder->iface->timer_set_rate(handle->fd, rate); + +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- res = h->iface->timer_set_rate(handle, rate); +- } +- +- ast_heap_unlock(timing_interfaces); +- + return res; + } + +-void ast_timer_ack(int handle, unsigned int quantity) ++void ast_timer_ack(const struct ast_timer *handle, unsigned int quantity) + { +- struct timing_holder *h; +- +- ast_heap_rdlock(timing_interfaces); +- +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- h->iface->timer_ack(handle, quantity); +- } +- +- ast_heap_unlock(timing_interfaces); ++ handle->holder->iface->timer_ack(handle->fd, quantity); + } + +-int ast_timer_enable_continuous(int handle) ++int ast_timer_enable_continuous(const struct ast_timer *handle) + { +- struct timing_holder *h; + int res = -1; + +- ast_heap_rdlock(timing_interfaces); ++ res = handle->holder->iface->timer_enable_continuous(handle->fd); + +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- res = h->iface->timer_enable_continuous(handle); +- } +- +- ast_heap_unlock(timing_interfaces); +- + return res; + } + +-int ast_timer_disable_continuous(int handle) ++int ast_timer_disable_continuous(const struct ast_timer *handle) + { +- struct timing_holder *h; + int res = -1; + +- ast_heap_rdlock(timing_interfaces); ++ res = handle->holder->iface->timer_disable_continuous(handle->fd); + +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- res = h->iface->timer_disable_continuous(handle); +- } +- +- ast_heap_unlock(timing_interfaces); +- + return res; + } + +-enum ast_timer_event ast_timer_get_event(int handle) ++enum ast_timer_event ast_timer_get_event(const struct ast_timer *handle) + { +- struct timing_holder *h; + enum ast_timer_event res = -1; + +- ast_heap_rdlock(timing_interfaces); ++ res = handle->holder->iface->timer_get_event(handle->fd); + +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- res = h->iface->timer_get_event(handle); +- } +- +- ast_heap_unlock(timing_interfaces); +- + return res; + } + +-unsigned int ast_timer_get_max_rate(int handle) ++unsigned int ast_timer_get_max_rate(const struct ast_timer *handle) + { +- struct timing_holder *h; + unsigned int res = 0; + +- ast_heap_rdlock(timing_interfaces); ++ res = handle->holder->iface->timer_get_max_rate(handle->fd); + +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- res = h->iface->timer_get_max_rate(handle); +- } +- +- ast_heap_unlock(timing_interfaces); +- + return res; + } + + static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) + { +- int fd, count = 0; ++ struct ast_timer *timer; ++ int count = 0; + struct timeval start, end; + unsigned int test_rate = 50; +- struct timing_holder *h; + + switch (cmd) { + case CLI_INIT: +@@ -267,34 +238,29 @@ + + ast_cli(a->fd, "Attempting to test a timer with %u ticks per second.\n", test_rate); + +- if ((fd = ast_timer_open()) == -1) { ++ if (!(timer = ast_timer_open())) { + ast_cli(a->fd, "Failed to open timing fd\n"); + return CLI_FAILURE; + } + +- ast_heap_rdlock(timing_interfaces); +- if ((h = ast_heap_peek(timing_interfaces, 1))) { +- ast_cli(a->fd, "Using the '%s' timing module for this test.\n", h->iface->name); +- h = NULL; +- } +- ast_heap_unlock(timing_interfaces); ++ ast_cli(a->fd, "Using the '%s' timing module for this test.\n", timer->holder->iface->name); + + start = ast_tvnow(); + +- ast_timer_set_rate(fd, test_rate); ++ ast_timer_set_rate(timer, test_rate); + + while (ast_tvdiff_ms((end = ast_tvnow()), start) < 1000) { + int res; + struct pollfd pfd = { +- .fd = fd, ++ .fd = ast_timer_fd(timer), + .events = POLLIN | POLLPRI, + }; + +- res = poll(&pfd, 1, 100); ++ res = ast_poll(&pfd, 1, 100); + + if (res == 1) { + count++; +- ast_timer_ack(fd, 1); ++ ast_timer_ack(timer, 1); + } else if (!res) { + ast_cli(a->fd, "poll() timed out! This is bad.\n"); + } else if (errno != EAGAIN && errno != EINTR) { +@@ -302,7 +268,7 @@ + } + } + +- ast_timer_close(fd); ++ ast_timer_close(timer); + + ast_cli(a->fd, "It has been %d milliseconds, and we got %d timer ticks\n", + ast_tvdiff_ms(end, start), count); +Index: main/devicestate.c +=================================================================== +--- a/main/devicestate.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/devicestate.c (.../trunk) (revision 186562) +@@ -196,8 +196,10 @@ + ast_cond_t cond; + ast_mutex_t lock; + AST_LIST_HEAD_NOLOCK(, devstate_change) devstate_change_q; ++ unsigned int enabled:1; + } devstate_collector = { + .thread = AST_PTHREADT_NULL, ++ .enabled = 0, + }; + + /* Forward declarations */ +@@ -428,22 +430,26 @@ + static void devstate_event(const char *device, enum ast_device_state state) + { + struct ast_event *event; ++ enum ast_event_type event_type; + ++ if (devstate_collector.enabled) { ++ /* Distributed device state is enabled, so this state change is a change ++ * for a single server, not the real state. */ ++ event_type = AST_EVENT_DEVICE_STATE_CHANGE; ++ } else { ++ event_type = AST_EVENT_DEVICE_STATE; ++ } ++ + ast_debug(3, "device '%s' state '%d'\n", device, state); + +- if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE, ++ if (!(event = ast_event_new(event_type, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device, + AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state, + AST_EVENT_IE_END))) { + return; + } + +- /* Cache this event, replacing an event in the cache with the same +- * device name if it exists. */ +- ast_event_queue_and_cache(event, +- AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, sizeof(struct ast_eid), +- AST_EVENT_IE_END); ++ ast_event_queue_and_cache(event); + } + + /*! Called by the state change thread to find out what the state is, and then +@@ -632,13 +638,12 @@ + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device, + AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state, + AST_EVENT_IE_END); +- +- if (!event) ++ ++ if (!event) { + return; ++ } + +- ast_event_queue_and_cache(event, +- AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_END); ++ ast_event_queue_and_cache(event); + } + + static void handle_devstate_change(struct devstate_change *sc) +@@ -719,21 +724,6 @@ + /*! \brief Initialize the device state engine in separate thread */ + int ast_device_state_engine_init(void) + { +- devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE, +- devstate_change_collector_cb, NULL, AST_EVENT_IE_END); +- +- if (!devstate_collector.event_sub) { +- ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n"); +- return -1; +- } +- +- ast_mutex_init(&devstate_collector.lock); +- ast_cond_init(&devstate_collector.cond, NULL); +- if (ast_pthread_create_background(&devstate_collector.thread, NULL, run_devstate_collector, NULL) < 0) { +- ast_log(LOG_ERROR, "Unable to start device state collector thread.\n"); +- return -1; +- } +- + ast_cond_init(&change_pending, NULL); + if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) { + ast_log(LOG_ERROR, "Unable to start device state change thread.\n"); +@@ -830,3 +820,28 @@ + return AST_DEVICE_NOT_INUSE; + } + ++int ast_enable_distributed_devstate(void) ++{ ++ if (devstate_collector.enabled) { ++ return 0; ++ } ++ ++ devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE, ++ devstate_change_collector_cb, NULL, AST_EVENT_IE_END); ++ ++ if (!devstate_collector.event_sub) { ++ ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n"); ++ return -1; ++ } ++ ++ ast_mutex_init(&devstate_collector.lock); ++ ast_cond_init(&devstate_collector.cond, NULL); ++ if (ast_pthread_create_background(&devstate_collector.thread, NULL, run_devstate_collector, NULL) < 0) { ++ ast_log(LOG_ERROR, "Unable to start device state collector thread.\n"); ++ return -1; ++ } ++ ++ devstate_collector.enabled = 1; ++ ++ return 0; ++} +Index: main/taskprocessor.c +=================================================================== +--- a/main/taskprocessor.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/taskprocessor.c (.../trunk) (revision 186562) +@@ -84,10 +84,10 @@ + /*! \brief tps_singletons is the astobj2 container for taskprocessor singletons */ + static struct ao2_container *tps_singletons; + +-/*! \brief CLI 'taskprocessor ping <blah>' operation requires a ping condition */ ++/*! \brief CLI <example>taskprocessor ping <blah></example> operation requires a ping condition */ + static ast_cond_t cli_ping_cond; + +-/*! \brief CLI 'taskprocessor ping <blah>' operation requires a ping condition lock */ ++/*! \brief CLI <example>taskprocessor ping <blah></example> operation requires a ping condition lock */ + AST_MUTEX_DEFINE_STATIC(cli_ping_cond_lock); + + /*! \brief The astobj2 hash callback for taskprocessors */ +@@ -101,7 +101,7 @@ + /*! \brief Destroy the taskprocessor when its refcount reaches zero */ + static void tps_taskprocessor_destroy(void *tps); + +-/*! \brief CLI 'taskprocessor ping <blah>' handler function */ ++/*! \brief CLI <example>taskprocessor ping <blah></example> handler function */ + static int tps_ping_handler(void *datap); + + /*! \brief Remove the front task off the taskprocessor queue */ +Index: main/astobj2.c +=================================================================== +--- a/main/astobj2.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/astobj2.c (.../trunk) (revision 186562) +@@ -134,20 +134,20 @@ + + /* the underlying functions common to debug and non-debug versions */ + +-static int __ao2_ref(void *user_data, const int delta); +-static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn); +-static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn, +- ao2_callback_fn *cmp_fn); +-static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data, const char *file, int line, const char *func); +-static void *__ao2_callback(struct ao2_container *c, +- const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, +- char *tag, char *file, int line, const char *funcname); +-static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q); ++static int internal_ao2_ref(void *user_data, const int delta); ++static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn); ++static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn, ++ ao2_callback_fn *cmp_fn); ++static struct bucket_list *internal_ao2_link(struct ao2_container *c, void *user_data, const char *file, int line, const char *func); ++static void *internal_ao2_callback(struct ao2_container *c, ++ const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, ++ char *tag, char *file, int line, const char *funcname); ++static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q); + + #ifndef DEBUG_THREADS + int ao2_lock(void *user_data) + #else +-int _ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var) ++int __ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var) + #endif + { + struct astobj2 *p = INTERNAL_OBJ(user_data); +@@ -169,7 +169,7 @@ + #ifndef DEBUG_THREADS + int ao2_unlock(void *user_data) + #else +-int _ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var) ++int __ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var) + #endif + { + struct astobj2 *p = INTERNAL_OBJ(user_data); +@@ -191,7 +191,7 @@ + #ifndef DEBUG_THREADS + int ao2_trylock(void *user_data) + #else +-int _ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var) ++int __ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var) + #endif + { + struct astobj2 *p = INTERNAL_OBJ(user_data); +@@ -227,7 +227,7 @@ + */ + + +-int _ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int line, const char *funcname) ++int __ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int line, const char *funcname) + { + struct astobj2 *obj = INTERNAL_OBJ(user_data); + +@@ -244,20 +244,20 @@ + fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag); + fclose(refo); + } +- return __ao2_ref(user_data, delta); ++ return internal_ao2_ref(user_data, delta); + } + +-int _ao2_ref(void *user_data, const int delta) ++int __ao2_ref(void *user_data, const int delta) + { + struct astobj2 *obj = INTERNAL_OBJ(user_data); + + if (obj == NULL) + return -1; + +- return __ao2_ref(user_data, delta); ++ return internal_ao2_ref(user_data, delta); + } + +-static int __ao2_ref(void *user_data, const int delta) ++static int internal_ao2_ref(void *user_data, const int delta) + { + struct astobj2 *obj = INTERNAL_OBJ(user_data); + int current_value; +@@ -303,7 +303,7 @@ + * We always alloc at least the size of a void *, + * for debugging purposes. + */ +-static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) ++static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) + { + /* allocation */ + struct astobj2 *obj; +@@ -332,13 +332,13 @@ + return EXTERNAL_OBJ(obj); + } + +-void *_ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, char *tag, char *file, int line, const char *funcname) ++void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, char *tag, char *file, int line, const char *funcname) + { + /* allocation */ + void *obj; + FILE *refo = fopen(REF_FILE,"a"); + +- obj = __ao2_alloc(data_size, destructor_fn); ++ obj = internal_ao2_alloc(data_size, destructor_fn); + + if (obj == NULL) + return NULL; +@@ -352,9 +352,9 @@ + return obj; + } + +-void *_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) ++void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) + { +- return __ao2_alloc(data_size, destructor_fn); ++ return internal_ao2_alloc(data_size, destructor_fn); + } + + +@@ -418,8 +418,8 @@ + /* + * A container is just an object, after all! + */ +-static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const unsigned int n_buckets, ao2_hash_fn *hash_fn, +- ao2_callback_fn *cmp_fn) ++static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c, const unsigned int n_buckets, ao2_hash_fn *hash_fn, ++ ao2_callback_fn *cmp_fn) + { + /* XXX maybe consistency check on arguments ? */ + /* compute the container size */ +@@ -439,28 +439,27 @@ + return c; + } + +-struct ao2_container *_ao2_container_alloc_debug(const unsigned int n_buckets, ao2_hash_fn *hash_fn, +- ao2_callback_fn *cmp_fn, char *tag, char *file, int line, const char *funcname) ++struct ao2_container *__ao2_container_alloc_debug(const unsigned int n_buckets, ao2_hash_fn *hash_fn, ++ ao2_callback_fn *cmp_fn, char *tag, char *file, int line, const char *funcname) + { + /* XXX maybe consistency check on arguments ? */ + /* compute the container size */ + size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket); +- struct ao2_container *c = _ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname); ++ struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname); + +- return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn); ++ return internal_ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn); + } + +-struct ao2_container * +-_ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn, +- ao2_callback_fn *cmp_fn) ++struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn, ++ ao2_callback_fn *cmp_fn) + { + /* XXX maybe consistency check on arguments ? */ + /* compute the container size */ + + size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket); +- struct ao2_container *c = _ao2_alloc(container_size, container_destruct); ++ struct ao2_container *c = __ao2_alloc(container_size, container_destruct); + +- return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn); ++ return internal_ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn); + } + + /*! +@@ -486,7 +485,7 @@ + * link an object to a container + */ + +-static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data, const char *file, int line, const char *func) ++static struct bucket_list *internal_ao2_link(struct ao2_container *c, void *user_data, const char *file, int line, const char *func) + { + int i; + /* create a new list entry */ +@@ -516,23 +515,23 @@ + return p; + } + +-void *_ao2_link_debug(struct ao2_container *c, void *user_data, char *tag, char *file, int line, const char *funcname) ++void *__ao2_link_debug(struct ao2_container *c, void *user_data, char *tag, char *file, int line, const char *funcname) + { +- struct bucket_list *p = __ao2_link(c, user_data, file, line, funcname); ++ struct bucket_list *p = internal_ao2_link(c, user_data, file, line, funcname); + + if (p) { +- _ao2_ref_debug(user_data, +1, tag, file, line, funcname); ++ __ao2_ref_debug(user_data, +1, tag, file, line, funcname); + ao2_unlock(c); + } + return p; + } + +-void *_ao2_link(struct ao2_container *c, void *user_data) ++void *__ao2_link(struct ao2_container *c, void *user_data) + { +- struct bucket_list *p = __ao2_link(c, user_data, __FILE__, __LINE__, __PRETTY_FUNCTION__); ++ struct bucket_list *p = internal_ao2_link(c, user_data, __FILE__, __LINE__, __PRETTY_FUNCTION__); + + if (p) { +- _ao2_ref(user_data, +1); ++ __ao2_ref(user_data, +1); + ao2_unlock(c); + } + return p; +@@ -550,23 +549,23 @@ + * Unlink an object from the container + * and destroy the associated * ao2_bucket_list structure. + */ +-void *_ao2_unlink_debug(struct ao2_container *c, void *user_data, char *tag, +- char *file, int line, const char *funcname) ++void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, char *tag, ++ char *file, int line, const char *funcname) + { + if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */ + return NULL; + +- _ao2_callback_debug(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data, tag, file, line, funcname); ++ __ao2_callback_debug(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data, tag, file, line, funcname); + + return NULL; + } + +-void *_ao2_unlink(struct ao2_container *c, void *user_data) ++void *__ao2_unlink(struct ao2_container *c, void *user_data) + { + if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */ + return NULL; + +- _ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data); ++ __ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data); + + return NULL; + } +@@ -595,9 +594,9 @@ + * aren't an excessive load to the system, as the callback should not be + * called as often as, say, the ao2_ref func is called. + */ +-static void *__ao2_callback(struct ao2_container *c, +- const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, +- char *tag, char *file, int line, const char *funcname) ++static void *internal_ao2_callback(struct ao2_container *c, ++ const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, ++ char *tag, char *file, int line, const char *funcname) + { + int i, last; /* search boundaries */ + void *ret = NULL; +@@ -675,9 +674,9 @@ + /* it is important to handle this case before the unlink */ + ret = EXTERNAL_OBJ(cur->astobj); + if (tag) +- _ao2_ref_debug(ret, 1, tag, file, line, funcname); ++ __ao2_ref_debug(ret, 1, tag, file, line, funcname); + else +- _ao2_ref(ret, 1); ++ __ao2_ref(ret, 1); + } + + if (flags & OBJ_UNLINK) { /* must unlink */ +@@ -689,9 +688,9 @@ + /* update number of elements and version */ + ast_atomic_fetchadd_int(&c->elements, -1); + if (tag) +- _ao2_ref_debug(EXTERNAL_OBJ(x->astobj), -1, tag, file, line, funcname); ++ __ao2_ref_debug(EXTERNAL_OBJ(x->astobj), -1, tag, file, line, funcname); + else +- _ao2_ref(EXTERNAL_OBJ(x->astobj), -1); ++ __ao2_ref(EXTERNAL_OBJ(x->astobj), -1); + free(x); /* free the link record */ + } + +@@ -715,45 +714,45 @@ + return ret; + } + +-void *_ao2_callback_debug(struct ao2_container *c, +- const enum search_flags flags, +- ao2_callback_fn *cb_fn, void *arg, +- char *tag, char *file, int line, const char *funcname) ++void *__ao2_callback_debug(struct ao2_container *c, ++ const enum search_flags flags, ++ ao2_callback_fn *cb_fn, void *arg, ++ char *tag, char *file, int line, const char *funcname) + { +- return __ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, tag, file, line, funcname); ++ return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, tag, file, line, funcname); + } + +-void *_ao2_callback(struct ao2_container *c, const enum search_flags flags, +- ao2_callback_fn *cb_fn, void *arg) ++void *__ao2_callback(struct ao2_container *c, const enum search_flags flags, ++ ao2_callback_fn *cb_fn, void *arg) + { +- return __ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, NULL, NULL, 0, NULL); ++ return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, NULL, NULL, 0, NULL); + } + +-void *_ao2_callback_data_debug(struct ao2_container *c, +- const enum search_flags flags, +- ao2_callback_data_fn *cb_fn, void *arg, void *data, +- char *tag, char *file, int line, const char *funcname) ++void *__ao2_callback_data_debug(struct ao2_container *c, ++ const enum search_flags flags, ++ ao2_callback_data_fn *cb_fn, void *arg, void *data, ++ char *tag, char *file, int line, const char *funcname) + { +- return __ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, tag, file, line, funcname); ++ return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, tag, file, line, funcname); + } + +-void *_ao2_callback_data(struct ao2_container *c, const enum search_flags flags, +- ao2_callback_data_fn *cb_fn, void *arg, void *data) ++void *__ao2_callback_data(struct ao2_container *c, const enum search_flags flags, ++ ao2_callback_data_fn *cb_fn, void *arg, void *data) + { +- return __ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, NULL, NULL, 0, NULL); ++ return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, NULL, NULL, 0, NULL); + } + + /*! + * the find function just invokes the default callback with some reasonable flags. + */ +-void *_ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname) ++void *__ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname) + { +- return _ao2_callback_debug(c, flags, c->cmp_fn, arg, tag, file, line, funcname); ++ return __ao2_callback_debug(c, flags, c->cmp_fn, arg, tag, file, line, funcname); + } + +-void *_ao2_find(struct ao2_container *c, void *arg, enum search_flags flags) ++void *__ao2_find(struct ao2_container *c, void *arg, enum search_flags flags) + { +- return _ao2_callback(c, flags, c->cmp_fn, arg); ++ return __ao2_callback(c, flags, c->cmp_fn, arg); + } + + /*! +@@ -772,7 +771,7 @@ + /* + * move to the next element in the container. + */ +-static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q) ++static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q) + { + int lim; + struct bucket_list *p = NULL; +@@ -827,16 +826,16 @@ + return ret; + } + +-void * _ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname) ++void *__ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname) + { + struct bucket_list *p; + void *ret = NULL; + +- ret = __ao2_iterator_next(a, &p); ++ ret = internal_ao2_iterator_next(a, &p); + + if (p) { + /* inc refcount of returned object */ +- _ao2_ref_debug(ret, 1, tag, file, line, funcname); ++ __ao2_ref_debug(ret, 1, tag, file, line, funcname); + } + + if (!(a->flags & F_AO2I_DONTLOCK)) +@@ -845,16 +844,16 @@ + return ret; + } + +-void * _ao2_iterator_next(struct ao2_iterator *a) ++void *__ao2_iterator_next(struct ao2_iterator *a) + { + struct bucket_list *p = NULL; + void *ret = NULL; + +- ret = __ao2_iterator_next(a, &p); ++ ret = internal_ao2_iterator_next(a, &p); + + if (p) { + /* inc refcount of returned object */ +- _ao2_ref(ret, 1); ++ __ao2_ref(ret, 1); + } + + if (!(a->flags & F_AO2I_DONTLOCK)) +@@ -868,13 +867,13 @@ + */ + static int cd_cb(void *obj, void *arg, int flag) + { +- _ao2_ref(obj, -1); ++ __ao2_ref(obj, -1); + return 0; + } + + static int cd_cb_debug(void *obj, void *arg, int flag) + { +- _ao2_ref_debug(obj, -1, "deref object via container destroy", __FILE__, __LINE__, __PRETTY_FUNCTION__); ++ __ao2_ref_debug(obj, -1, "deref object via container destroy", __FILE__, __LINE__, __PRETTY_FUNCTION__); + return 0; + } + +@@ -883,7 +882,7 @@ + struct ao2_container *c = _c; + int i; + +- _ao2_callback(c, OBJ_UNLINK, cd_cb, NULL); ++ __ao2_callback(c, OBJ_UNLINK, cd_cb, NULL); + + for (i = 0; i < c->n_buckets; i++) { + struct bucket_list *current; +@@ -903,7 +902,7 @@ + struct ao2_container *c = _c; + int i; + +- _ao2_callback_debug(c, OBJ_UNLINK, cd_cb_debug, NULL, "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__); ++ __ao2_callback_debug(c, OBJ_UNLINK, cd_cb_debug, NULL, "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__); + + for (i = 0; i < c->n_buckets; i++) { + struct bucket_list *current; +Index: main/asterisk.exports +=================================================================== +--- a/main/asterisk.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/main/asterisk.exports (.../trunk) (revision 186562) +@@ -0,0 +1,33 @@ ++{ ++ global: ++ ast_*; ++ _ast_*; ++ __ast_*; ++ pbx_*; ++ astman_*; ++ ao2_*; ++ __ao2_*; ++ option_debug; ++ option_verbose; ++ dahdi_chan_name; ++ dahdi_chan_name_len; ++ dahdi_chan_mode; ++ callerid_*; ++ cid_di; ++ cid_dr; ++ clidsb; ++ MD5*; ++ sched_*; ++ io_*; ++ jb_*; ++ aes_*; ++ config_*; ++ tdd_*; ++ term_*; ++ channelreloadreason2txt; ++ devstate2str; ++ __manager_event; ++ dialed_interface_info; ++ local: ++ *; ++}; + +Property changes on: main/asterisk.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: main/cli.c +=================================================================== +--- a/main/cli.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/cli.c (.../trunk) (revision 186562) +@@ -1259,7 +1259,7 @@ + { + struct ast_channel *c=NULL; + struct timeval now; +- struct ast_str *out = ast_str_thread_get(&global_app_buf, 16); ++ struct ast_str *out = ast_str_thread_get(&ast_str_thread_global_buf, 16); + char cdrtime[256]; + char nf[256], wf[256], rf[256]; + long elapsed_seconds=0; +Index: main/dial.c +=================================================================== +--- a/main/dial.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/dial.c (.../trunk) (revision 186562) +@@ -274,20 +274,19 @@ + ast_channel_datastore_inherit(chan, channel->owner); + + /* Copy over callerid information */ +- S_REPLACE(channel->owner->cid.cid_num, ast_strdup(chan->cid.cid_num)); +- S_REPLACE(channel->owner->cid.cid_name, ast_strdup(chan->cid.cid_name)); +- S_REPLACE(channel->owner->cid.cid_ani, ast_strdup(chan->cid.cid_ani)); + S_REPLACE(channel->owner->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis)); ++ ast_party_redirecting_copy(&channel->owner->redirecting, &chan->redirecting); + ++ channel->owner->cid.cid_tns = chan->cid.cid_tns; ++ ++ ast_connected_line_copy_from_caller(&channel->owner->connected, &chan->cid); ++ + ast_string_field_set(channel->owner, language, chan->language); + ast_string_field_set(channel->owner, accountcode, chan->accountcode); + channel->owner->cdrflags = chan->cdrflags; + if (ast_strlen_zero(channel->owner->musicclass)) + ast_string_field_set(channel->owner, musicclass, chan->musicclass); + +- channel->owner->cid.cid_pres = chan->cid.cid_pres; +- channel->owner->cid.cid_ton = chan->cid.cid_ton; +- channel->owner->cid.cid_tns = chan->cid.cid_tns; + channel->owner->adsicpe = chan->adsicpe; + channel->owner->transfercapability = chan->transfercapability; + } +@@ -429,6 +428,14 @@ + ast_verbose (VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", channel->owner->name, chan->name); + ast_indicate(chan, AST_CONTROL_SRCUPDATE); + break; ++ case AST_CONTROL_CONNECTED_LINE: ++ ast_verb(3, "%s connected line has changed, passing it to %s\n", channel->owner->name, chan->name); ++ ast_indicate_data(chan, AST_CONTROL_CONNECTED_LINE, fr->data.ptr, fr->datalen); ++ break; ++ case AST_CONTROL_REDIRECTING: ++ ast_verb(3, "%s redirecting info has changed, passing it to %s\n", channel->owner->name, chan->name); ++ ast_indicate_data(chan, AST_CONTROL_REDIRECTING, fr->data.ptr, fr->datalen); ++ break; + case AST_CONTROL_PROCEEDING: + ast_verb(3, "%s is proceeding, passing it to %s\n", channel->owner->name, chan->name); + ast_indicate(chan, AST_CONTROL_PROCEEDING); +Index: main/heap.c +=================================================================== +--- a/main/heap.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/heap.c (.../trunk) (revision 186562) +@@ -302,6 +302,8 @@ + return h->cur_len; + } + ++#ifndef DEBUG_THREADS ++ + int ast_heap_wrlock(struct ast_heap *h) + { + return ast_rwlock_wrlock(&h->lock); +@@ -317,3 +319,21 @@ + return ast_rwlock_unlock(&h->lock); + } + ++#else /* DEBUG_THREADS */ ++ ++int __ast_heap_wrlock(struct ast_heap *h, const char *file, const char *func, int line) ++{ ++ return _ast_rwlock_wrlock(&h->lock, "&h->lock", file, line, func); ++} ++ ++int __ast_heap_rdlock(struct ast_heap *h, const char *file, const char *func, int line) ++{ ++ return _ast_rwlock_rdlock(&h->lock, "&h->lock", file, line, func); ++} ++ ++int __ast_heap_unlock(struct ast_heap *h, const char *file, const char *func, int line) ++{ ++ return _ast_rwlock_unlock(&h->lock, "&h->lock", file, line, func); ++} ++ ++#endif /* DEBUG_THREADS */ +Index: main/pbx.c +=================================================================== +--- a/main/pbx.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/pbx.c (.../trunk) (revision 186562) +@@ -2924,7 +2924,7 @@ + } else if (!strcmp(var, "SYSTEMNAME")) { + s = ast_config_AST_SYSTEM_NAME; + } else if (!strcmp(var, "ENTITYID")) { +- ast_eid_to_str(workspace, workspacelen, &g_eid); ++ ast_eid_to_str(workspace, workspacelen, &ast_eid_default); + s = workspace; + } + } +Index: main/strings.c +=================================================================== +--- a/main/strings.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/strings.c (.../trunk) (revision 186562) +@@ -66,7 +66,7 @@ + } + /* + * Ask vsnprintf how much space we need. Remember that vsnprintf +- * does not count the final '\0' so we must add 1. ++ * does not count the final <code>'\0'</code> so we must add 1. + */ + va_copy(aq, ap); + res = vsnprintf((*buf)->__AST_STR_STR + offset, (*buf)->__AST_STR_LEN - offset, fmt, aq); +@@ -143,7 +143,8 @@ + maxlen--; + (*buf)->__AST_STR_USED++; + +- if (dynamic && (!maxlen || (escapecommas && !(maxlen - 1)))) { ++ if ((ptr >= (*buf)->__AST_STR_STR + (*buf)->__AST_STR_LEN - 3) || ++ (dynamic && (!maxlen || (escapecommas && !(maxlen - 1))))) { + char *oldbase = (*buf)->__AST_STR_STR; + size_t old = (*buf)->__AST_STR_LEN; + if (ast_str_make_space(buf, (*buf)->__AST_STR_LEN * 2)) { +@@ -156,11 +157,10 @@ + ptr += (*buf)->__AST_STR_STR - oldbase; + } + } +- if (__builtin_expect(!(maxsrc && maxlen), 0)) { ++ if (__builtin_expect(!maxlen, 0)) { + ptr--; + } + *ptr = '\0'; +- (*buf)->__AST_STR_USED--; + return (*buf)->__AST_STR_STR; + } + +Index: main/stun.c +=================================================================== +--- a/main/stun.c (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/main/stun.c (.../trunk) (revision 186562) +@@ -0,0 +1,475 @@ ++/* ++ * Asterisk -- An open source telephony toolkit. ++ * ++ * Copyright (C) 1999 - 2008, Digium, Inc. ++ * ++ * Mark Spencer <markster@digium.com> ++ * ++ * See http://www.asterisk.org for more information about ++ * the Asterisk project. Please do not directly contact ++ * any of the maintainers of this project for assistance; ++ * the project provides a web site, mailing lists and IRC ++ * channels for your use. ++ * ++ * 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. ++ */ ++ ++/*! ++ * \file ++ * ++ * \brief STUN Support ++ * ++ * \author Mark Spencer <markster@digium.com> ++ * ++ * \note STUN is defined in RFC 3489. ++ */ ++ ++#include "asterisk.h" ++ ++ASTERISK_FILE_VERSION(__FILE__, "$Revision$") ++ ++#include "asterisk/_private.h" ++#include "asterisk/stun.h" ++#include "asterisk/cli.h" ++#include "asterisk/utils.h" ++#include "asterisk/channel.h" ++ ++static int stundebug; /*!< Are we debugging stun? */ ++ ++/*! ++ * \brief STUN support code ++ * ++ * This code provides some support for doing STUN transactions. ++ * Eventually it should be moved elsewhere as other protocols ++ * than RTP can benefit from it - e.g. SIP. ++ * STUN is described in RFC3489 and it is based on the exchange ++ * of UDP packets between a client and one or more servers to ++ * determine the externally visible address (and port) of the client ++ * once it has gone through the NAT boxes that connect it to the ++ * outside. ++ * The simplest request packet is just the header defined in ++ * struct stun_header, and from the response we may just look at ++ * one attribute, STUN_MAPPED_ADDRESS, that we find in the response. ++ * By doing more transactions with different server addresses we ++ * may determine more about the behaviour of the NAT boxes, of ++ * course - the details are in the RFC. ++ * ++ * All STUN packets start with a simple header made of a type, ++ * length (excluding the header) and a 16-byte random transaction id. ++ * Following the header we may have zero or more attributes, each ++ * structured as a type, length and a value (whose format depends ++ * on the type, but often contains addresses). ++ * Of course all fields are in network format. ++ */ ++ ++typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id; ++ ++struct stun_header { ++ unsigned short msgtype; ++ unsigned short msglen; ++ stun_trans_id id; ++ unsigned char ies[0]; ++} __attribute__((packed)); ++ ++struct stun_attr { ++ unsigned short attr; ++ unsigned short len; ++ unsigned char value[0]; ++} __attribute__((packed)); ++ ++/* ++ * The format normally used for addresses carried by STUN messages. ++ */ ++struct stun_addr { ++ unsigned char unused; ++ unsigned char family; ++ unsigned short port; ++ unsigned int addr; ++} __attribute__((packed)); ++ ++/*! \brief STUN message types ++ * 'BIND' refers to transactions used to determine the externally ++ * visible addresses. 'SEC' refers to transactions used to establish ++ * a session key for subsequent requests. ++ * 'SEC' functionality is not supported here. ++ */ ++ ++#define STUN_BINDREQ 0x0001 ++#define STUN_BINDRESP 0x0101 ++#define STUN_BINDERR 0x0111 ++#define STUN_SECREQ 0x0002 ++#define STUN_SECRESP 0x0102 ++#define STUN_SECERR 0x0112 ++ ++/*! \brief Basic attribute types in stun messages. ++ * Messages can also contain custom attributes (codes above 0x7fff) ++ */ ++#define STUN_MAPPED_ADDRESS 0x0001 ++#define STUN_RESPONSE_ADDRESS 0x0002 ++#define STUN_CHANGE_REQUEST 0x0003 ++#define STUN_SOURCE_ADDRESS 0x0004 ++#define STUN_CHANGED_ADDRESS 0x0005 ++#define STUN_USERNAME 0x0006 ++#define STUN_PASSWORD 0x0007 ++#define STUN_MESSAGE_INTEGRITY 0x0008 ++#define STUN_ERROR_CODE 0x0009 ++#define STUN_UNKNOWN_ATTRIBUTES 0x000a ++#define STUN_REFLECTED_FROM 0x000b ++ ++/*! \brief helper function to print message names */ ++static const char *stun_msg2str(int msg) ++{ ++ switch (msg) { ++ case STUN_BINDREQ: ++ return "Binding Request"; ++ case STUN_BINDRESP: ++ return "Binding Response"; ++ case STUN_BINDERR: ++ return "Binding Error Response"; ++ case STUN_SECREQ: ++ return "Shared Secret Request"; ++ case STUN_SECRESP: ++ return "Shared Secret Response"; ++ case STUN_SECERR: ++ return "Shared Secret Error Response"; ++ } ++ return "Non-RFC3489 Message"; ++} ++ ++/*! \brief helper function to print attribute names */ ++static const char *stun_attr2str(int msg) ++{ ++ switch (msg) { ++ case STUN_MAPPED_ADDRESS: ++ return "Mapped Address"; ++ case STUN_RESPONSE_ADDRESS: ++ return "Response Address"; ++ case STUN_CHANGE_REQUEST: ++ return "Change Request"; ++ case STUN_SOURCE_ADDRESS: ++ return "Source Address"; ++ case STUN_CHANGED_ADDRESS: ++ return "Changed Address"; ++ case STUN_USERNAME: ++ return "Username"; ++ case STUN_PASSWORD: ++ return "Password"; ++ case STUN_MESSAGE_INTEGRITY: ++ return "Message Integrity"; ++ case STUN_ERROR_CODE: ++ return "Error Code"; ++ case STUN_UNKNOWN_ATTRIBUTES: ++ return "Unknown Attributes"; ++ case STUN_REFLECTED_FROM: ++ return "Reflected From"; ++ } ++ return "Non-RFC3489 Attribute"; ++} ++ ++/*! \brief here we store credentials extracted from a message */ ++struct stun_state { ++ const char *username; ++ const char *password; ++}; ++ ++static int stun_process_attr(struct stun_state *state, struct stun_attr *attr) ++{ ++ if (stundebug) ++ ast_verbose("Found STUN Attribute %s (%04x), length %d\n", ++ stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); ++ switch (ntohs(attr->attr)) { ++ case STUN_USERNAME: ++ state->username = (const char *) (attr->value); ++ break; ++ case STUN_PASSWORD: ++ state->password = (const char *) (attr->value); ++ break; ++ default: ++ if (stundebug) ++ ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n", ++ stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); ++ } ++ return 0; ++} ++ ++/*! \brief append a string to an STUN message */ ++static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left) ++{ ++ int size = sizeof(**attr) + strlen(s); ++ if (*left > size) { ++ (*attr)->attr = htons(attrval); ++ (*attr)->len = htons(strlen(s)); ++ memcpy((*attr)->value, s, strlen(s)); ++ (*attr) = (struct stun_attr *)((*attr)->value + strlen(s)); ++ *len += size; ++ *left -= size; ++ } ++} ++ ++/*! \brief append an address to an STUN message */ ++static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left) ++{ ++ int size = sizeof(**attr) + 8; ++ struct stun_addr *addr; ++ if (*left > size) { ++ (*attr)->attr = htons(attrval); ++ (*attr)->len = htons(8); ++ addr = (struct stun_addr *)((*attr)->value); ++ addr->unused = 0; ++ addr->family = 0x01; ++ addr->port = sin->sin_port; ++ addr->addr = sin->sin_addr.s_addr; ++ (*attr) = (struct stun_attr *)((*attr)->value + 8); ++ *len += size; ++ *left -= size; ++ } ++} ++ ++/*! \brief wrapper to send an STUN message */ ++static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp) ++{ ++ return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0, ++ (struct sockaddr *)dst, sizeof(*dst)); ++} ++ ++/*! \brief helper function to generate a random request id */ ++static void stun_req_id(struct stun_header *req) ++{ ++ int x; ++ for (x = 0; x < 4; x++) ++ req->id.id[x] = ast_random(); ++} ++ ++/*! \brief handle an incoming STUN message. ++ * ++ * Do some basic sanity checks on packet size and content, ++ * try to extract a bit of information, and possibly reply. ++ * At the moment this only processes BIND requests, and returns ++ * the externally visible address of the request. ++ * If a callback is specified, invoke it with the attribute. ++ */ ++int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg) ++{ ++ struct stun_header *hdr = (struct stun_header *)data; ++ struct stun_attr *attr; ++ struct stun_state st; ++ int ret = AST_STUN_IGNORE; ++ int x; ++ ++ /* On entry, 'len' is the length of the udp payload. After the ++ * initial checks it becomes the size of unprocessed options, ++ * while 'data' is advanced accordingly. ++ */ ++ if (len < sizeof(struct stun_header)) { ++ ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header)); ++ return -1; ++ } ++ len -= sizeof(struct stun_header); ++ data += sizeof(struct stun_header); ++ x = ntohs(hdr->msglen); /* len as advertised in the message */ ++ if (stundebug) ++ ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x); ++ if (x > len) { ++ ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len); ++ } else ++ len = x; ++ memset(&st, 0, sizeof(st)); ++ while (len) { ++ if (len < sizeof(struct stun_attr)) { ++ ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr)); ++ break; ++ } ++ attr = (struct stun_attr *)data; ++ /* compute total attribute length */ ++ x = ntohs(attr->len) + sizeof(struct stun_attr); ++ if (x > len) { ++ ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len); ++ break; ++ } ++ if (stun_cb) ++ stun_cb(attr, arg); ++ if (stun_process_attr(&st, attr)) { ++ ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr)); ++ break; ++ } ++ /* Clear attribute id: in case previous entry was a string, ++ * this will act as the terminator for the string. ++ */ ++ attr->attr = 0; ++ data += x; ++ len -= x; ++ } ++ /* Null terminate any string. ++ * XXX NOTE, we write past the size of the buffer passed by the ++ * caller, so this is potentially dangerous. The only thing that ++ * saves us is that usually we read the incoming message in a ++ * much larger buffer in the struct ast_rtp ++ */ ++ *data = '\0'; ++ ++ /* Now prepare to generate a reply, which at the moment is done ++ * only for properly formed (len == 0) STUN_BINDREQ messages. ++ */ ++ if (len == 0) { ++ unsigned char respdata[1024]; ++ struct stun_header *resp = (struct stun_header *)respdata; ++ int resplen = 0; /* len excluding header */ ++ int respleft = sizeof(respdata) - sizeof(struct stun_header); ++ ++ resp->id = hdr->id; ++ resp->msgtype = 0; ++ resp->msglen = 0; ++ attr = (struct stun_attr *)resp->ies; ++ switch (ntohs(hdr->msgtype)) { ++ case STUN_BINDREQ: ++ if (stundebug) ++ ast_verbose("STUN Bind Request, username: %s\n", ++ st.username ? st.username : "<none>"); ++ if (st.username) ++ append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft); ++ append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft); ++ resp->msglen = htons(resplen); ++ resp->msgtype = htons(STUN_BINDRESP); ++ stun_send(s, src, resp); ++ ret = AST_STUN_ACCEPT; ++ break; ++ default: ++ if (stundebug) ++ ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype))); ++ } ++ } ++ return ret; ++} ++ ++/*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response. ++ * This is used as a callback for stun_handle_response ++ * when called from ast_stun_request. ++ */ ++static int stun_get_mapped(struct stun_attr *attr, void *arg) ++{ ++ struct stun_addr *addr = (struct stun_addr *)(attr + 1); ++ struct sockaddr_in *sa = (struct sockaddr_in *)arg; ++ ++ if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) ++ return 1; /* not us. */ ++ sa->sin_port = addr->port; ++ sa->sin_addr.s_addr = addr->addr; ++ return 0; ++} ++ ++/*! \brief Generic STUN request ++ * Send a generic stun request to the server specified, ++ * possibly waiting for a reply and filling the 'reply' field with ++ * the externally visible address. Note that in this case the request ++ * will be blocking. ++ * (Note, the interface may change slightly in the future). ++ * ++ * \param s the socket used to send the request ++ * \param dst the address of the STUN server ++ * \param username if non null, add the username in the request ++ * \param answer if non null, the function waits for a response and ++ * puts here the externally visible address. ++ * \return 0 on success, other values on error. ++ */ ++int ast_stun_request(int s, struct sockaddr_in *dst, ++ const char *username, struct sockaddr_in *answer) ++{ ++ struct stun_header *req; ++ unsigned char reqdata[1024]; ++ int reqlen, reqleft; ++ struct stun_attr *attr; ++ int res = 0; ++ int retry; ++ ++ req = (struct stun_header *)reqdata; ++ stun_req_id(req); ++ reqlen = 0; ++ reqleft = sizeof(reqdata) - sizeof(struct stun_header); ++ req->msgtype = 0; ++ req->msglen = 0; ++ attr = (struct stun_attr *)req->ies; ++ if (username) ++ append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft); ++ req->msglen = htons(reqlen); ++ req->msgtype = htons(STUN_BINDREQ); ++ for (retry = 0; retry < 3; retry++) { /* XXX make retries configurable */ ++ /* send request, possibly wait for reply */ ++ unsigned char reply_buf[1024]; ++ fd_set rfds; ++ struct timeval to = { 3, 0 }; /* timeout, make it configurable */ ++ struct sockaddr_in src; ++ socklen_t srclen; ++ ++ res = stun_send(s, dst, req); ++ if (res < 0) { ++ ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n", ++ retry, res); ++ continue; ++ } ++ if (answer == NULL) ++ break; ++ FD_ZERO(&rfds); ++ FD_SET(s, &rfds); ++ res = ast_select(s + 1, &rfds, NULL, NULL, &to); ++ if (res <= 0) /* timeout or error */ ++ continue; ++ memset(&src, 0, sizeof(src)); ++ srclen = sizeof(src); ++ /* XXX pass -1 in the size, because stun_handle_packet might ++ * write past the end of the buffer. ++ */ ++ res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1, ++ 0, (struct sockaddr *)&src, &srclen); ++ if (res < 0) { ++ ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n", ++ retry, res); ++ continue; ++ } ++ memset(answer, 0, sizeof(struct sockaddr_in)); ++ ast_stun_handle_packet(s, &src, reply_buf, res, ++ stun_get_mapped, answer); ++ res = 0; /* signal regular exit */ ++ break; ++ } ++ return res; ++} ++ ++static char *handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) ++{ ++ switch (cmd) { ++ case CLI_INIT: ++ e->command = "stun set debug {on|off}"; ++ e->usage = ++ "Usage: stun set debug {on|off}\n" ++ " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n" ++ " debugging\n"; ++ return NULL; ++ case CLI_GENERATE: ++ return NULL; ++ } ++ ++ if (a->argc != e->args) ++ return CLI_SHOWUSAGE; ++ ++ if (!strncasecmp(a->argv[e->args-1], "on", 2)) ++ stundebug = 1; ++ else if (!strncasecmp(a->argv[e->args-1], "off", 3)) ++ stundebug = 0; ++ else ++ return CLI_SHOWUSAGE; ++ ++ ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled"); ++ return CLI_SUCCESS; ++} ++ ++static struct ast_cli_entry cli_stun[] = { ++ AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"), ++}; ++ ++/*! \brief Initialize the STUN system in Asterisk */ ++void ast_stun_init(void) ++{ ++ ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry)); ++} + +Property changes on: main/stun.c +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: main/file.c +=================================================================== +--- a/main/file.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/file.c (.../trunk) (revision 186562) +@@ -1234,6 +1234,7 @@ + case AST_CONTROL_SRCUPDATE: + case AST_CONTROL_HOLD: + case AST_CONTROL_UNHOLD: ++ case -1: + /* Unimportant */ + break; + default: +Index: main/callerid.c +=================================================================== +--- a/main/callerid.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/callerid.c (.../trunk) (revision 186562) +@@ -791,8 +791,8 @@ + + } + +-int vmwi_generate(unsigned char *buf, int active, int type, int codec, +- const char* name, const char* number, int flags) ++int ast_callerid_vmwi_generate(unsigned char *buf, int active, int type, int codec, ++ const char* name, const char* number, int flags) + { + char msg[256]; + int len = 0; +@@ -922,8 +922,10 @@ + return bytes; + } + +-/*! \brief Clean up phone string +- * remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets. ++/*! ++ * \brief Clean up phone string ++ * \details ++ * Remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets. + * Basically, remove anything that could be invalid in a pattern. + */ + void ast_shrink_phone_number(char *n) +@@ -958,11 +960,13 @@ + n[y] = '\0'; + } + +-/*! \brief Checks if phone number consists of valid characters +- \param exten String that needs to be checked +- \param valid Valid characters in string +- \return 1 if valid string, 0 if string contains invalid characters +-*/ ++/*! ++ * \brief Checks if phone number consists of valid characters ++ * \param exten String that needs to be checked ++ * \param valid Valid characters in string ++ * \retval 1 if valid string ++ * \retval 0 if string contains invalid characters ++ */ + static int ast_is_valid_string(const char *exten, const char *valid) + { + int x; +@@ -975,34 +979,16 @@ + return 1; + } + +-/*! \brief checks if string consists only of digits and * \# and + +- \return 1 if string is valid AST phone number +- \return 0 if not +-*/ + int ast_isphonenumber(const char *n) + { + return ast_is_valid_string(n, "0123456789*#+"); + } + +-/*! \brief checks if string consists only of digits and ( ) - * \# and + +- Pre-qualifies the string for ast_shrink_phone_number() +- \return 1 if string is valid AST shrinkable phone number +- \return 0 if not +-*/ + int ast_is_shrinkable_phonenumber(const char *exten) + { + return ast_is_valid_string(exten, "0123456789*#+()-."); + } + +-/*! +- * \brief Destructively parse instr for caller id information +- * \return always returns 0, as the code always returns something. +- * \note XXX 'name' is not parsed consistently e.g. we have +- * input location name +- * " foo bar " <123> 123 ' foo bar ' (with spaces around) +- * " foo bar " NULL 'foo bar' (without spaces around) +- * The parsing of leading and trailing space/quotes should be more consistent. +- */ + int ast_callerid_parse(char *instr, char **name, char **location) + { + char *ns, *ne, *ls, *le; +@@ -1103,68 +1089,191 @@ + return 0; + } + +-/*! \brief Translation table for Caller ID Presentation settings */ +-static struct { +- int val; ++struct ast_value_translation { ++ int value; + const char *name; + const char *description; +-} pres_types[] = { +- { AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "allowed_not_screened", "Presentation Allowed, Not Screened"}, +- { AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen", "Presentation Allowed, Passed Screen"}, +- { AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen", "Presentation Allowed, Failed Screen"}, +- { AST_PRES_ALLOWED_NETWORK_NUMBER, "allowed", "Presentation Allowed, Network Number"}, +- { AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "prohib_not_screened", "Presentation Prohibited, Not Screened"}, +- { AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen", "Presentation Prohibited, Passed Screen"}, +- { AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen", "Presentation Prohibited, Failed Screen"}, +- { AST_PRES_PROHIB_NETWORK_NUMBER, "prohib", "Presentation Prohibited, Network Number"}, +- { AST_PRES_NUMBER_NOT_AVAILABLE, "unavailable", "Number Unavailable"}, + }; + +-/*! \brief Convert caller ID text code to value +- used in config file parsing +- \param data text string +- \return value AST_PRES_ from callerid.h +-*/ ++/*! \brief Translation table for Caller ID Presentation settings */ ++static const struct ast_value_translation pres_types[] = { ++/* *INDENT-OFF* */ ++ { AST_PRES_ALLOWED | AST_PRES_USER_NUMBER_UNSCREENED, "allowed_not_screened", "Presentation Allowed, Not Screened" }, ++ { AST_PRES_ALLOWED | AST_PRES_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen", "Presentation Allowed, Passed Screen" }, ++ { AST_PRES_ALLOWED | AST_PRES_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen", "Presentation Allowed, Failed Screen" }, ++ { AST_PRES_ALLOWED | AST_PRES_NETWORK_NUMBER, "allowed", "Presentation Allowed, Network Number" }, ++ ++ { AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_UNSCREENED, "prohib_not_screened", "Presentation Prohibited, Not Screened" }, ++ { AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen", "Presentation Prohibited, Passed Screen" }, ++ { AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen", "Presentation Prohibited, Failed Screen" }, ++ { AST_PRES_RESTRICTED | AST_PRES_NETWORK_NUMBER, "prohib", "Presentation Prohibited, Network Number" }, ++ ++ { AST_PRES_UNAVAILABLE | AST_PRES_NETWORK_NUMBER, "unavailable", "Number Unavailable" }, /* Default name to value conversion. */ ++ { AST_PRES_UNAVAILABLE | AST_PRES_USER_NUMBER_UNSCREENED, "unavailable", "Number Unavailable" }, ++ { AST_PRES_UNAVAILABLE | AST_PRES_USER_NUMBER_FAILED_SCREEN, "unavailable", "Number Unavailable" }, ++ { AST_PRES_UNAVAILABLE | AST_PRES_USER_NUMBER_PASSED_SCREEN, "unavailable", "Number Unavailable" }, ++/* *INDENT-ON* */ ++}; ++ ++/*! ++ * \brief Convert caller ID text code to value (used in config file parsing) ++ * \param data text string from config file ++ * \retval value AST_PRES_ from callerid.h ++ * \retval -1 if not in table ++ */ + int ast_parse_caller_presentation(const char *data) + { +- int i; ++ int index; + +- for (i = 0; i < ARRAY_LEN(pres_types); i++) { +- if (!strcasecmp(pres_types[i].name, data)) +- return pres_types[i].val; ++ for (index = 0; index < ARRAY_LEN(pres_types); ++index) { ++ if (!strcasecmp(pres_types[index].name, data)) { ++ return pres_types[index].value; ++ } + } + + return -1; + } + +-/*! \brief Convert caller ID pres value to explanatory string +- \param data value (see callerid.h AST_PRES_ ) +- \return string for human presentation +-*/ ++/*! ++ * \brief Convert caller ID pres value to explanatory string ++ * \param data AST_PRES_ value from callerid.h ++ * \return string for human presentation ++ */ + const char *ast_describe_caller_presentation(int data) + { +- int i; ++ int index; + +- for (i = 0; i < ARRAY_LEN(pres_types); i++) { +- if (pres_types[i].val == data) +- return pres_types[i].description; ++ for (index = 0; index < ARRAY_LEN(pres_types); ++index) { ++ if (pres_types[index].value == data) { ++ return pres_types[index].description; ++ } + } + + return "unknown"; + } + +-/*! \brief Convert caller ID pres value to text code +- \param data text string +- \return string for config file +-*/ ++/*! ++ * \brief Convert caller ID pres value to text code ++ * \param data AST_PRES_ value from callerid.h ++ * \return string for config file ++ */ + const char *ast_named_caller_presentation(int data) + { +- int i; ++ int index; + +- for (i = 0; i < ARRAY_LEN(pres_types); i++) { +- if (pres_types[i].val == data) +- return pres_types[i].name; ++ for (index = 0; index < ARRAY_LEN(pres_types); ++index) { ++ if (pres_types[index].value == data) { ++ return pres_types[index].name; ++ } + } + + return "unknown"; + } ++ ++/*! \brief Translation table for redirecting reason settings */ ++static const struct ast_value_translation redirecting_reason_types[] = { ++/* *INDENT-OFF* */ ++ { AST_REDIRECTING_REASON_UNKNOWN, "unknown", "Unknown" }, ++ { AST_REDIRECTING_REASON_USER_BUSY, "cfb", "Call Forwarding Busy" }, ++ { AST_REDIRECTING_REASON_NO_ANSWER, "cfnr", "Call Forwarding No Reply" }, ++ { AST_REDIRECTING_REASON_UNAVAILABLE, "unavailable", "Callee is Unavailable" }, ++ { AST_REDIRECTING_REASON_UNCONDITIONAL, "cfu", "Call Forwarding Unconditional" }, ++ { AST_REDIRECTING_REASON_TIME_OF_DAY, "time_of_day", "Time of Day" }, ++ { AST_REDIRECTING_REASON_DO_NOT_DISTURB, "dnd", "Do Not Disturb" }, ++ { AST_REDIRECTING_REASON_DEFLECTION, "deflection", "Call Deflection" }, ++ { AST_REDIRECTING_REASON_FOLLOW_ME, "follow_me", "Follow Me" }, ++ { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out_of_order", "Called DTE Out-Of-Order" }, ++ { AST_REDIRECTING_REASON_AWAY, "away", "Callee is Away" }, ++ { AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte", "Call Forwarding By The Called DTE" }, ++/* *INDENT-ON* */ ++}; ++ ++int ast_redirecting_reason_parse(const char *data) ++{ ++ int index; ++ ++ for (index = 0; index < ARRAY_LEN(redirecting_reason_types); ++index) { ++ if (!strcasecmp(redirecting_reason_types[index].name, data)) { ++ return redirecting_reason_types[index].value; ++ } ++ } ++ ++ return -1; ++} ++ ++const char *ast_redirecting_reason_describe(int data) ++{ ++ int index; ++ ++ for (index = 0; index < ARRAY_LEN(redirecting_reason_types); ++index) { ++ if (redirecting_reason_types[index].value == data) { ++ return redirecting_reason_types[index].description; ++ } ++ } ++ ++ return "not-known"; ++} ++ ++const char *ast_redirecting_reason_name(int data) ++{ ++ int index; ++ ++ for (index = 0; index < ARRAY_LEN(redirecting_reason_types); ++index) { ++ if (redirecting_reason_types[index].value == data) { ++ return redirecting_reason_types[index].name; ++ } ++ } ++ ++ return "not-known"; ++} ++ ++/*! \brief Translation table for connected line update source settings */ ++static const struct ast_value_translation connected_line_source_types[] = { ++/* *INDENT-OFF* */ ++ { AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN, "unknown", "Unknown" }, ++ { AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, "answer", "Normal Call Answering" }, ++ { AST_CONNECTED_LINE_UPDATE_SOURCE_DIVERSION, "diversion", "Call Diversion (Deprecated, use REDIRECTING)" }, ++ { AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER, "transfer_active", "Call Transfer(Active)" }, ++ { AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER, "transfer", "Call Transfer(Active)" },/* Old name must come after new name. */ ++ { AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER_ALERTING, "transfer_alerting", "Call Transfer(Alerting)" } ++/* *INDENT-ON* */ ++}; ++ ++int ast_connected_line_source_parse(const char *data) ++{ ++ int index; ++ ++ for (index = 0; index < ARRAY_LEN(connected_line_source_types); ++index) { ++ if (!strcasecmp(connected_line_source_types[index].name, data)) { ++ return connected_line_source_types[index].value; ++ } ++ } ++ ++ return -1; ++} ++ ++const char *ast_connected_line_source_describe(int data) ++{ ++ int index; ++ ++ for (index = 0; index < ARRAY_LEN(connected_line_source_types); ++index) { ++ if (connected_line_source_types[index].value == data) { ++ return connected_line_source_types[index].description; ++ } ++ } ++ ++ return "not-known"; ++} ++ ++const char *ast_connected_line_source_name(int data) ++{ ++ int index; ++ ++ for (index = 0; index < ARRAY_LEN(connected_line_source_types); ++index) { ++ if (connected_line_source_types[index].value == data) { ++ return connected_line_source_types[index].name; ++ } ++ } ++ ++ return "not-known"; ++} +Index: main/audiohook.c +=================================================================== +--- a/main/audiohook.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/main/audiohook.c (.../trunk) (revision 186562) +@@ -441,12 +441,12 @@ + + void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source) + { +- struct ast_audiohook *audiohook = find_audiohook_by_source(old_chan->audiohooks, source); ++ struct ast_audiohook *audiohook; + +- if (!audiohook) { ++ if (!old_chan->audiohooks || !(audiohook = find_audiohook_by_source(old_chan->audiohooks, source))) { + return; + } +- ++ + /* By locking both channels and the audiohook, we can assure that + * another thread will not have a chance to read the audiohook's status + * as done, even though ast_audiohook_remove signals the trigger +@@ -576,6 +576,7 @@ + } + if (!(middle_frame = ast_translate(in_translate->trans_pvt, frame, 0))) + return frame; ++ samples = middle_frame->samples; + } + + /* Queue up signed linear frame to each spy */ +Index: main/rtp_engine.c +=================================================================== +--- a/main/rtp_engine.c (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/main/rtp_engine.c (.../trunk) (revision 186562) +@@ -0,0 +1,1572 @@ ++/* ++ * Asterisk -- An open source telephony toolkit. ++ * ++ * Copyright (C) 1999 - 2008, Digium, Inc. ++ * ++ * Joshua Colp <jcolp@digium.com> ++ * ++ * See http://www.asterisk.org for more information about ++ * the Asterisk project. Please do not directly contact ++ * any of the maintainers of this project for assistance; ++ * the project provides a web site, mailing lists and IRC ++ * channels for your use. ++ * ++ * 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. ++ */ ++ ++/*! \file ++ * ++ * \brief Pluggable RTP Architecture ++ * ++ * \author Joshua Colp <jcolp@digium.com> ++ */ ++ ++#include "asterisk.h" ++ ++ASTERISK_FILE_VERSION(__FILE__, "$Revision$") ++ ++#include <math.h> ++ ++#include "asterisk/channel.h" ++#include "asterisk/frame.h" ++#include "asterisk/module.h" ++#include "asterisk/rtp_engine.h" ++#include "asterisk/manager.h" ++#include "asterisk/options.h" ++#include "asterisk/astobj2.h" ++#include "asterisk/pbx.h" ++ ++/*! Structure that represents an RTP session (instance) */ ++struct ast_rtp_instance { ++ /*! Engine that is handling this RTP instance */ ++ struct ast_rtp_engine *engine; ++ /*! Data unique to the RTP engine */ ++ void *data; ++ /*! RTP properties that have been set and their value */ ++ int properties[AST_RTP_PROPERTY_MAX]; ++ /*! Address that we are expecting RTP to come in to */ ++ struct sockaddr_in local_address; ++ /*! Address that we are sending RTP to */ ++ struct sockaddr_in remote_address; ++ /*! Instance that we are bridged to if doing remote or local bridging */ ++ struct ast_rtp_instance *bridged; ++ /*! Payload and packetization information */ ++ struct ast_rtp_codecs codecs; ++ /*! RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */ ++ int timeout; ++ /*! RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ ++ int holdtimeout; ++ /*! DTMF mode in use */ ++ enum ast_rtp_dtmf_mode dtmf_mode; ++}; ++ ++/*! List of RTP engines that are currently registered */ ++static AST_RWLIST_HEAD_STATIC(engines, ast_rtp_engine); ++ ++/*! List of RTP glues */ ++static AST_RWLIST_HEAD_STATIC(glues, ast_rtp_glue); ++ ++/*! The following array defines the MIME Media type (and subtype) for each ++ of our codecs, or RTP-specific data type. */ ++static const struct ast_rtp_mime_type { ++ struct ast_rtp_payload_type payload_type; ++ char *type; ++ char *subtype; ++ unsigned int sample_rate; ++} ast_rtp_mime_types[] = { ++ {{1, AST_FORMAT_G723_1}, "audio", "G723", 8000}, ++ {{1, AST_FORMAT_GSM}, "audio", "GSM", 8000}, ++ {{1, AST_FORMAT_ULAW}, "audio", "PCMU", 8000}, ++ {{1, AST_FORMAT_ULAW}, "audio", "G711U", 8000}, ++ {{1, AST_FORMAT_ALAW}, "audio", "PCMA", 8000}, ++ {{1, AST_FORMAT_ALAW}, "audio", "G711A", 8000}, ++ {{1, AST_FORMAT_G726}, "audio", "G726-32", 8000}, ++ {{1, AST_FORMAT_ADPCM}, "audio", "DVI4", 8000}, ++ {{1, AST_FORMAT_SLINEAR}, "audio", "L16", 8000}, ++ {{1, AST_FORMAT_LPC10}, "audio", "LPC", 8000}, ++ {{1, AST_FORMAT_G729A}, "audio", "G729", 8000}, ++ {{1, AST_FORMAT_G729A}, "audio", "G729A", 8000}, ++ {{1, AST_FORMAT_G729A}, "audio", "G.729", 8000}, ++ {{1, AST_FORMAT_SPEEX}, "audio", "speex", 8000}, ++ {{1, AST_FORMAT_ILBC}, "audio", "iLBC", 8000}, ++ /* this is the sample rate listed in the RTP profile for the G.722 ++ codec, *NOT* the actual sample rate of the media stream ++ */ ++ {{1, AST_FORMAT_G722}, "audio", "G722", 8000}, ++ {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32", 8000}, ++ {{0, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, ++ {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, ++ {{0, AST_RTP_CN}, "audio", "CN", 8000}, ++ {{1, AST_FORMAT_JPEG}, "video", "JPEG", 90000}, ++ {{1, AST_FORMAT_PNG}, "video", "PNG", 90000}, ++ {{1, AST_FORMAT_H261}, "video", "H261", 90000}, ++ {{1, AST_FORMAT_H263}, "video", "H263", 90000}, ++ {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998", 90000}, ++ {{1, AST_FORMAT_H264}, "video", "H264", 90000}, ++ {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES", 90000}, ++ {{1, AST_FORMAT_T140RED}, "text", "RED", 1000}, ++ {{1, AST_FORMAT_T140}, "text", "T140", 1000}, ++ {{1, AST_FORMAT_SIREN7}, "audio", "G7221", 16000}, ++ {{1, AST_FORMAT_SIREN14}, "audio", "G7221", 32000}, ++}; ++ ++/*! ++ * \brief Mapping between Asterisk codecs and rtp payload types ++ * ++ * Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s: ++ * also, our own choices for dynamic payload types. This is our master ++ * table for transmission ++ * ++ * See http://www.iana.org/assignments/rtp-parameters for a list of ++ * assigned values ++ */ ++static const struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT] = { ++ [0] = {1, AST_FORMAT_ULAW}, ++ #ifdef USE_DEPRECATED_G726 ++ [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */ ++ #endif ++ [3] = {1, AST_FORMAT_GSM}, ++ [4] = {1, AST_FORMAT_G723_1}, ++ [5] = {1, AST_FORMAT_ADPCM}, /* 8 kHz */ ++ [6] = {1, AST_FORMAT_ADPCM}, /* 16 kHz */ ++ [7] = {1, AST_FORMAT_LPC10}, ++ [8] = {1, AST_FORMAT_ALAW}, ++ [9] = {1, AST_FORMAT_G722}, ++ [10] = {1, AST_FORMAT_SLINEAR}, /* 2 channels */ ++ [11] = {1, AST_FORMAT_SLINEAR}, /* 1 channel */ ++ [13] = {0, AST_RTP_CN}, ++ [16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */ ++ [17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */ ++ [18] = {1, AST_FORMAT_G729A}, ++ [19] = {0, AST_RTP_CN}, /* Also used for CN */ ++ [26] = {1, AST_FORMAT_JPEG}, ++ [31] = {1, AST_FORMAT_H261}, ++ [34] = {1, AST_FORMAT_H263}, ++ [97] = {1, AST_FORMAT_ILBC}, ++ [98] = {1, AST_FORMAT_H263_PLUS}, ++ [99] = {1, AST_FORMAT_H264}, ++ [101] = {0, AST_RTP_DTMF}, ++ [102] = {1, AST_FORMAT_SIREN7}, ++ [103] = {1, AST_FORMAT_H263_PLUS}, ++ [104] = {1, AST_FORMAT_MP4_VIDEO}, ++ [105] = {1, AST_FORMAT_T140RED}, /* Real time text chat (with redundancy encoding) */ ++ [106] = {1, AST_FORMAT_T140}, /* Real time text chat */ ++ [110] = {1, AST_FORMAT_SPEEX}, ++ [111] = {1, AST_FORMAT_G726}, ++ [112] = {1, AST_FORMAT_G726_AAL2}, ++ [115] = {1, AST_FORMAT_SIREN14}, ++ [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ ++}; ++ ++int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module) ++{ ++ struct ast_rtp_engine *current_engine; ++ ++ /* Perform a sanity check on the engine structure to make sure it has the basics */ ++ if (ast_strlen_zero(engine->name) || !engine->new || !engine->destroy || !engine->write || !engine->read) { ++ ast_log(LOG_WARNING, "RTP Engine '%s' failed sanity check so it was not registered.\n", !ast_strlen_zero(engine->name) ? engine->name : "Unknown"); ++ return -1; ++ } ++ ++ /* Link owner module to the RTP engine for reference counting purposes */ ++ engine->mod = module; ++ ++ AST_RWLIST_WRLOCK(&engines); ++ ++ /* Ensure that no two modules with the same name are registered at the same time */ ++ AST_RWLIST_TRAVERSE(&engines, current_engine, entry) { ++ if (!strcmp(current_engine->name, engine->name)) { ++ ast_log(LOG_WARNING, "An RTP engine with the name '%s' has already been registered.\n", engine->name); ++ AST_RWLIST_UNLOCK(&engines); ++ return -1; ++ } ++ } ++ ++ /* The engine survived our critique. Off to the list it goes to be used */ ++ AST_RWLIST_INSERT_TAIL(&engines, engine, entry); ++ ++ AST_RWLIST_UNLOCK(&engines); ++ ++ ast_verb(2, "Registered RTP engine '%s'\n", engine->name); ++ ++ return 0; ++} ++ ++int ast_rtp_engine_unregister(struct ast_rtp_engine *engine) ++{ ++ struct ast_rtp_engine *current_engine = NULL; ++ ++ AST_RWLIST_WRLOCK(&engines); ++ ++ if ((current_engine = AST_RWLIST_REMOVE(&engines, engine, entry))) { ++ ast_verb(2, "Unregistered RTP engine '%s'\n", engine->name); ++ } ++ ++ AST_RWLIST_UNLOCK(&engines); ++ ++ return current_engine ? 0 : -1; ++} ++ ++int ast_rtp_glue_register2(struct ast_rtp_glue *glue, struct ast_module *module) ++{ ++ struct ast_rtp_glue *current_glue = NULL; ++ ++ if (ast_strlen_zero(glue->type)) { ++ return -1; ++ } ++ ++ glue->mod = module; ++ ++ AST_RWLIST_WRLOCK(&glues); ++ ++ AST_RWLIST_TRAVERSE(&glues, current_glue, entry) { ++ if (!strcasecmp(current_glue->type, glue->type)) { ++ ast_log(LOG_WARNING, "RTP glue with the name '%s' has already been registered.\n", glue->type); ++ AST_RWLIST_UNLOCK(&glues); ++ return -1; ++ } ++ } ++ ++ AST_RWLIST_INSERT_TAIL(&glues, glue, entry); ++ ++ AST_RWLIST_UNLOCK(&glues); ++ ++ ast_verb(2, "Registered RTP glue '%s'\n", glue->type); ++ ++ return 0; ++} ++ ++int ast_rtp_glue_unregister(struct ast_rtp_glue *glue) ++{ ++ struct ast_rtp_glue *current_glue = NULL; ++ ++ AST_RWLIST_WRLOCK(&glues); ++ ++ if ((current_glue = AST_RWLIST_REMOVE(&glues, glue, entry))) { ++ ast_verb(2, "Unregistered RTP glue '%s'\n", glue->type); ++ } ++ ++ AST_RWLIST_UNLOCK(&glues); ++ ++ return current_glue ? 0 : -1; ++} ++ ++static void instance_destructor(void *obj) ++{ ++ struct ast_rtp_instance *instance = obj; ++ ++ /* Pass us off to the engine to destroy */ ++ if (instance->data && instance->engine->destroy(instance)) { ++ ast_debug(1, "Engine '%s' failed to destroy RTP instance '%p'\n", instance->engine->name, instance); ++ return; ++ } ++ ++ /* Drop our engine reference */ ++ ast_module_unref(instance->engine->mod); ++ ++ ast_debug(1, "Destroyed RTP instance '%p'\n", instance); ++} ++ ++int ast_rtp_instance_destroy(struct ast_rtp_instance *instance) ++{ ++ ao2_ref(instance, -1); ++ ++ return 0; ++} ++ ++struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name, struct sched_context *sched, struct sockaddr_in *sin, void *data) ++{ ++ struct ast_rtp_instance *instance = NULL; ++ struct ast_rtp_engine *engine = NULL; ++ ++ AST_RWLIST_RDLOCK(&engines); ++ ++ /* If an engine name was specified try to use it or otherwise use the first one registered */ ++ if (!ast_strlen_zero(engine_name)) { ++ AST_RWLIST_TRAVERSE(&engines, engine, entry) { ++ if (!strcmp(engine->name, engine_name)) { ++ break; ++ } ++ } ++ } else { ++ engine = AST_RWLIST_FIRST(&engines); ++ } ++ ++ /* If no engine was actually found bail out now */ ++ if (!engine) { ++ ast_log(LOG_ERROR, "No RTP engine was found. Do you have one loaded?\n"); ++ AST_RWLIST_UNLOCK(&engines); ++ return NULL; ++ } ++ ++ /* Bump up the reference count before we return so the module can not be unloaded */ ++ ast_module_ref(engine->mod); ++ ++ AST_RWLIST_UNLOCK(&engines); ++ ++ /* Allocate a new RTP instance */ ++ if (!(instance = ao2_alloc(sizeof(*instance), instance_destructor))) { ++ ast_module_unref(engine->mod); ++ return NULL; ++ } ++ instance->engine = engine; ++ memcpy(&instance->local_address, sin, sizeof(instance->local_address)); ++ ++ ast_debug(1, "Using engine '%s' for RTP instance '%p'\n", engine->name, instance); ++ ++ /* And pass it off to the engine to setup */ ++ if (instance->engine->new(instance, sched, sin, data)) { ++ ast_debug(1, "Engine '%s' failed to setup RTP instance '%p'\n", engine->name, instance); ++ ao2_ref(instance, -1); ++ return NULL; ++ } ++ ++ ast_debug(1, "RTP instance '%p' is setup and ready to go\n", instance); ++ ++ return instance; ++} ++ ++void ast_rtp_instance_set_data(struct ast_rtp_instance *instance, void *data) ++{ ++ instance->data = data; ++} ++ ++void *ast_rtp_instance_get_data(struct ast_rtp_instance *instance) ++{ ++ return instance->data; ++} ++ ++int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *frame) ++{ ++ return instance->engine->write(instance, frame); ++} ++ ++struct ast_frame *ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp) ++{ ++ return instance->engine->read(instance, rtcp); ++} ++ ++int ast_rtp_instance_set_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) ++{ ++ memcpy(&instance->local_address, address, sizeof(instance->local_address)); ++ return 0; ++} ++ ++int ast_rtp_instance_set_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) ++{ ++ if (&instance->remote_address != address) { ++ memcpy(&instance->remote_address, address, sizeof(instance->remote_address)); ++ } ++ ++ /* moo */ ++ ++ if (instance->engine->remote_address_set) { ++ instance->engine->remote_address_set(instance, address); ++ } ++ ++ return 0; ++} ++ ++int ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) ++{ ++ if ((address->sin_family != AF_INET) || ++ (address->sin_port != instance->local_address.sin_port) || ++ (address->sin_addr.s_addr != instance->local_address.sin_addr.s_addr)) { ++ memcpy(address, &instance->local_address, sizeof(address)); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) ++{ ++ if ((address->sin_family != AF_INET) || ++ (address->sin_port != instance->remote_address.sin_port) || ++ (address->sin_addr.s_addr != instance->remote_address.sin_addr.s_addr)) { ++ memcpy(address, &instance->remote_address, sizeof(address)); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++void ast_rtp_instance_set_extended_prop(struct ast_rtp_instance *instance, int property, void *value) ++{ ++ if (instance->engine->extended_prop_set) { ++ instance->engine->extended_prop_set(instance, property, value); ++ } ++} ++ ++void *ast_rtp_instance_get_extended_prop(struct ast_rtp_instance *instance, int property) ++{ ++ if (instance->engine->extended_prop_get) { ++ return instance->engine->extended_prop_get(instance, property); ++ } ++ ++ return NULL; ++} ++ ++void ast_rtp_instance_set_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value) ++{ ++ instance->properties[property] = value; ++ ++ if (instance->engine->prop_set) { ++ instance->engine->prop_set(instance, property, value); ++ } ++} ++ ++int ast_rtp_instance_get_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property) ++{ ++ return instance->properties[property]; ++} ++ ++struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance) ++{ ++ return &instance->codecs; ++} ++ ++void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance) ++{ ++ int i; ++ ++ for (i = 0; i < AST_RTP_MAX_PT; i++) { ++ ast_debug(2, "Clearing payload %d on %p\n", i, codecs); ++ codecs->payloads[i].asterisk_format = 0; ++ codecs->payloads[i].code = 0; ++ if (instance && instance->engine && instance->engine->payload_set) { ++ instance->engine->payload_set(instance, i, 0, 0); ++ } ++ } ++} ++ ++void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance) ++{ ++ int i; ++ ++ for (i = 0; i < AST_RTP_MAX_PT; i++) { ++ if (static_RTP_PT[i].code) { ++ ast_debug(2, "Set default payload %d on %p\n", i, codecs); ++ codecs->payloads[i].asterisk_format = static_RTP_PT[i].asterisk_format; ++ codecs->payloads[i].code = static_RTP_PT[i].code; ++ if (instance && instance->engine && instance->engine->payload_set) { ++ instance->engine->payload_set(instance, i, codecs->payloads[i].asterisk_format, codecs->payloads[i].code); ++ } ++ } ++ } ++} ++ ++void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance) ++{ ++ int i; ++ ++ for (i = 0; i < AST_RTP_MAX_PT; i++) { ++ if (src->payloads[i].code) { ++ ast_debug(2, "Copying payload %d from %p to %p\n", i, src, dest); ++ dest->payloads[i].asterisk_format = src->payloads[i].asterisk_format; ++ dest->payloads[i].code = src->payloads[i].code; ++ if (instance && instance->engine && instance->engine->payload_set) { ++ instance->engine->payload_set(instance, i, dest->payloads[i].asterisk_format, dest->payloads[i].code); ++ } ++ } ++ } ++} ++ ++void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) ++{ ++ if (payload < 0 || payload > AST_RTP_MAX_PT || !static_RTP_PT[payload].code) { ++ return; ++ } ++ ++ codecs->payloads[payload].asterisk_format = static_RTP_PT[payload].asterisk_format; ++ codecs->payloads[payload].code = static_RTP_PT[payload].code; ++ ++ ast_debug(1, "Setting payload %d based on m type on %p\n", payload, codecs); ++ ++ if (instance && instance->engine && instance->engine->payload_set) { ++ instance->engine->payload_set(instance, payload, codecs->payloads[payload].asterisk_format, codecs->payloads[payload].code); ++ } ++} ++ ++int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt, ++ char *mimetype, char *mimesubtype, ++ enum ast_rtp_options options, ++ unsigned int sample_rate) ++{ ++ unsigned int i; ++ int found = 0; ++ ++ if (pt < 0 || pt > AST_RTP_MAX_PT) ++ return -1; /* bogus payload type */ ++ ++ for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { ++ const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i]; ++ ++ if (strcasecmp(mimesubtype, t->subtype)) { ++ continue; ++ } ++ ++ if (strcasecmp(mimetype, t->type)) { ++ continue; ++ } ++ ++ /* if both sample rates have been supplied, and they don't match, ++ then this not a match; if one has not been supplied, then the ++ rates are not compared */ ++ if (sample_rate && t->sample_rate && ++ (sample_rate != t->sample_rate)) { ++ continue; ++ } ++ ++ found = 1; ++ codecs->payloads[pt] = t->payload_type; ++ ++ if ((t->payload_type.code == AST_FORMAT_G726) && ++ t->payload_type.asterisk_format && ++ (options & AST_RTP_OPT_G726_NONSTANDARD)) { ++ codecs->payloads[pt].code = AST_FORMAT_G726_AAL2; ++ } ++ ++ if (instance && instance->engine && instance->engine->payload_set) { ++ instance->engine->payload_set(instance, pt, codecs->payloads[i].asterisk_format, codecs->payloads[i].code); ++ } ++ ++ break; ++ } ++ ++ return (found ? 0 : -2); ++} ++ ++int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload, char *mimetype, char *mimesubtype, enum ast_rtp_options options) ++{ ++ return ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, instance, payload, mimetype, mimesubtype, options, 0); ++} ++ ++void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) ++{ ++ if (payload < 0 || payload > AST_RTP_MAX_PT) { ++ return; ++ } ++ ++ ast_debug(2, "Unsetting payload %d on %p\n", payload, codecs); ++ ++ codecs->payloads[payload].asterisk_format = 0; ++ codecs->payloads[payload].code = 0; ++ ++ if (instance && instance->engine && instance->engine->payload_set) { ++ instance->engine->payload_set(instance, payload, 0, 0); ++ } ++} ++ ++struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs *codecs, int payload) ++{ ++ struct ast_rtp_payload_type result = { .asterisk_format = 0, }; ++ ++ if (payload < 0 || payload > AST_RTP_MAX_PT) { ++ return result; ++ } ++ ++ result.asterisk_format = codecs->payloads[payload].asterisk_format; ++ result.code = codecs->payloads[payload].code; ++ ++ if (!result.code) { ++ result = static_RTP_PT[payload]; ++ } ++ ++ return result; ++} ++ ++void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, int *astformats, int *nonastformats) ++{ ++ int i; ++ ++ *astformats = *nonastformats = 0; ++ ++ for (i = 0; i < AST_RTP_MAX_PT; i++) { ++ if (codecs->payloads[i].code) { ++ ast_debug(1, "Incorporating payload %d on %p\n", i, codecs); ++ } ++ if (codecs->payloads[i].asterisk_format) { ++ *astformats |= codecs->payloads[i].code; ++ } else { ++ *nonastformats |= codecs->payloads[i].code; ++ } ++ } ++} ++ ++int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, const int asterisk_format, const int code) ++{ ++ int i; ++ ++ for (i = 0; i < AST_RTP_MAX_PT; i++) { ++ if (codecs->payloads[i].asterisk_format == asterisk_format && codecs->payloads[i].code == code) { ++ ast_debug(2, "Found code %d at payload %d on %p\n", code, i, codecs); ++ return i; ++ } ++ } ++ ++ for (i = 0; i < AST_RTP_MAX_PT; i++) { ++ if (static_RTP_PT[i].asterisk_format == asterisk_format && static_RTP_PT[i].code == code) { ++ return i; ++ } ++ } ++ ++ return -1; ++} ++ ++const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, const int code, enum ast_rtp_options options) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); i++) { ++ if (ast_rtp_mime_types[i].payload_type.code == code && ast_rtp_mime_types[i].payload_type.asterisk_format == asterisk_format) { ++ if (asterisk_format && (code == AST_FORMAT_G726_AAL2) && (options & AST_RTP_OPT_G726_NONSTANDARD)) { ++ return "G726-32"; ++ } else { ++ return ast_rtp_mime_types[i].subtype; ++ } ++ } ++ } ++ ++ return ""; ++} ++ ++unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, int code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { ++ if ((ast_rtp_mime_types[i].payload_type.code == code) && (ast_rtp_mime_types[i].payload_type.asterisk_format == asterisk_format)) { ++ return ast_rtp_mime_types[i].sample_rate; ++ } ++ } ++ ++ return 0; ++} ++ ++char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, const int capability, const int asterisk_format, enum ast_rtp_options options) ++{ ++ int format, found = 0; ++ ++ if (!buf) { ++ return NULL; ++ } ++ ++ ast_str_append(&buf, 0, "0x%x (", capability); ++ ++ for (format = 1; format < AST_RTP_MAX; format <<= 1) { ++ if (capability & format) { ++ const char *name = ast_rtp_lookup_mime_subtype2(asterisk_format, format, options); ++ ast_str_append(&buf, 0, "%s|", name); ++ found = 1; ++ } ++ } ++ ++ ast_str_append(&buf, 0, "%s", found ? ")" : "nothing)"); ++ ++ return ast_str_buffer(buf); ++} ++ ++void ast_rtp_codecs_packetization_set(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, struct ast_codec_pref *prefs) ++{ ++ codecs->pref = *prefs; ++ ++ if (instance && instance->engine->packetization_set) { ++ instance->engine->packetization_set(instance, &instance->codecs.pref); ++ } ++} ++ ++int ast_rtp_instance_dtmf_begin(struct ast_rtp_instance *instance, char digit) ++{ ++ return instance->engine->dtmf_begin ? instance->engine->dtmf_begin(instance, digit) : -1; ++} ++ ++int ast_rtp_instance_dtmf_end(struct ast_rtp_instance *instance, char digit) ++{ ++ return instance->engine->dtmf_end ? instance->engine->dtmf_end(instance, digit) : -1; ++} ++ ++int ast_rtp_instance_dtmf_mode_set(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode) ++{ ++ if (!instance->engine->dtmf_mode_set || instance->engine->dtmf_mode_set(instance, dtmf_mode)) { ++ return -1; ++ } ++ ++ instance->dtmf_mode = dtmf_mode; ++ ++ return 0; ++} ++ ++enum ast_rtp_dtmf_mode ast_rtp_instance_dtmf_mode_get(struct ast_rtp_instance *instance) ++{ ++ return instance->dtmf_mode; ++} ++ ++void ast_rtp_instance_new_source(struct ast_rtp_instance *instance) ++{ ++ if (instance->engine->new_source) { ++ instance->engine->new_source(instance); ++ } ++} ++ ++int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc) ++{ ++ return instance->engine->qos ? instance->engine->qos(instance, tos, cos, desc) : -1; ++} ++ ++void ast_rtp_instance_stop(struct ast_rtp_instance *instance) ++{ ++ if (instance->engine->stop) { ++ instance->engine->stop(instance); ++ } ++} ++ ++int ast_rtp_instance_fd(struct ast_rtp_instance *instance, int rtcp) ++{ ++ return instance->engine->fd ? instance->engine->fd(instance, rtcp) : -1; ++} ++ ++struct ast_rtp_glue *ast_rtp_instance_get_glue(const char *type) ++{ ++ struct ast_rtp_glue *glue = NULL; ++ ++ AST_RWLIST_RDLOCK(&glues); ++ ++ AST_RWLIST_TRAVERSE(&glues, glue, entry) { ++ if (!strcasecmp(glue->type, type)) { ++ break; ++ } ++ } ++ ++ AST_RWLIST_UNLOCK(&glues); ++ ++ return glue; ++} ++ ++static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) ++{ ++ enum ast_bridge_result res = AST_BRIDGE_FAILED; ++ struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, }; ++ struct ast_frame *fr = NULL; ++ ++ /* Start locally bridging both instances */ ++ if (instance0->engine->local_bridge && instance0->engine->local_bridge(instance0, instance1)) { ++ ast_debug(1, "Failed to locally bridge %s to %s, backing out.\n", c0->name, c1->name); ++ ast_channel_unlock(c0); ++ ast_channel_unlock(c1); ++ return AST_BRIDGE_FAILED_NOWARN; ++ } ++ if (instance1->engine->local_bridge && instance1->engine->local_bridge(instance1, instance0)) { ++ ast_debug(1, "Failed to locally bridge %s to %s, backing out.\n", c1->name, c0->name); ++ if (instance0->engine->local_bridge) { ++ instance0->engine->local_bridge(instance0, NULL); ++ } ++ ast_channel_unlock(c0); ++ ast_channel_unlock(c1); ++ return AST_BRIDGE_FAILED_NOWARN; ++ } ++ ++ ast_channel_unlock(c0); ++ ast_channel_unlock(c1); ++ ++ instance0->bridged = instance1; ++ instance1->bridged = instance0; ++ ++ ast_poll_channel_add(c0, c1); ++ ++ /* Hop into a loop waiting for a frame from either channel */ ++ cs[0] = c0; ++ cs[1] = c1; ++ cs[2] = NULL; ++ for (;;) { ++ /* If the underlying formats have changed force this bridge to break */ ++ if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) { ++ ast_debug(1, "rtp-engine-local-bridge: Oooh, formats changed, backing out\n"); ++ res = AST_BRIDGE_FAILED_NOWARN; ++ break; ++ } ++ /* Check if anything changed */ ++ if ((c0->tech_pvt != pvt0) || ++ (c1->tech_pvt != pvt1) || ++ (c0->masq || c0->masqr || c1->masq || c1->masqr) || ++ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { ++ ast_debug(1, "rtp-engine-local-bridge: Oooh, something is weird, backing out\n"); ++ /* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */ ++ if ((c0->masq || c0->masqr) && (fr = ast_read(c0))) { ++ ast_frfree(fr); ++ } ++ if ((c1->masq || c1->masqr) && (fr = ast_read(c1))) { ++ ast_frfree(fr); ++ } ++ res = AST_BRIDGE_RETRY; ++ break; ++ } ++ /* Wait on a channel to feed us a frame */ ++ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { ++ if (!timeoutms) { ++ res = AST_BRIDGE_RETRY; ++ break; ++ } ++ ast_debug(2, "rtp-engine-local-bridge: Ooh, empty read...\n"); ++ if (ast_check_hangup(c0) || ast_check_hangup(c1)) { ++ break; ++ } ++ continue; ++ } ++ /* Read in frame from channel */ ++ fr = ast_read(who); ++ other = (who == c0) ? c1 : c0; ++ /* Depending on the frame we may need to break out of our bridge */ ++ if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && ++ ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) | ++ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) { ++ /* Record received frame and who */ ++ *fo = fr; ++ *rc = who; ++ ast_debug(1, "rtp-engine-local-bridge: Ooh, got a %s\n", fr ? "digit" : "hangup"); ++ res = AST_BRIDGE_COMPLETE; ++ break; ++ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { ++ if ((fr->subclass == AST_CONTROL_HOLD) || ++ (fr->subclass == AST_CONTROL_UNHOLD) || ++ (fr->subclass == AST_CONTROL_VIDUPDATE) || ++ (fr->subclass == AST_CONTROL_T38) || ++ (fr->subclass == AST_CONTROL_SRCUPDATE)) { ++ /* If we are going on hold, then break callback mode and P2P bridging */ ++ if (fr->subclass == AST_CONTROL_HOLD) { ++ if (instance0->engine->local_bridge) { ++ instance0->engine->local_bridge(instance0, NULL); ++ } ++ if (instance1->engine->local_bridge) { ++ instance1->engine->local_bridge(instance1, NULL); ++ } ++ instance0->bridged = NULL; ++ instance1->bridged = NULL; ++ } else if (fr->subclass == AST_CONTROL_UNHOLD) { ++ if (instance0->engine->local_bridge) { ++ instance0->engine->local_bridge(instance0, instance1); ++ } ++ if (instance1->engine->local_bridge) { ++ instance1->engine->local_bridge(instance1, instance0); ++ } ++ instance0->bridged = instance1; ++ instance1->bridged = instance0; ++ } ++ ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); ++ ast_frfree(fr); ++ } else { ++ *fo = fr; ++ *rc = who; ++ ast_debug(1, "rtp-engine-local-bridge: Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); ++ res = AST_BRIDGE_COMPLETE; ++ break; ++ } ++ } else { ++ if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || ++ (fr->frametype == AST_FRAME_DTMF_END) || ++ (fr->frametype == AST_FRAME_VOICE) || ++ (fr->frametype == AST_FRAME_VIDEO) || ++ (fr->frametype == AST_FRAME_IMAGE) || ++ (fr->frametype == AST_FRAME_HTML) || ++ (fr->frametype == AST_FRAME_MODEM) || ++ (fr->frametype == AST_FRAME_TEXT)) { ++ ast_write(other, fr); ++ } ++ ++ ast_frfree(fr); ++ } ++ /* Swap priority */ ++ cs[2] = cs[0]; ++ cs[0] = cs[1]; ++ cs[1] = cs[2]; ++ } ++ ++ /* Stop locally bridging both instances */ ++ if (instance0->engine->local_bridge) { ++ instance0->engine->local_bridge(instance0, NULL); ++ } ++ if (instance1->engine->local_bridge) { ++ instance1->engine->local_bridge(instance1, NULL); ++ } ++ ++ instance0->bridged = NULL; ++ instance1->bridged = NULL; ++ ++ ast_poll_channel_del(c0, c1); ++ ++ return res; ++} ++ ++static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1, ++ struct ast_rtp_instance *vinstance0, struct ast_rtp_instance *vinstance1, struct ast_rtp_instance *tinstance0, ++ struct ast_rtp_instance *tinstance1, struct ast_rtp_glue *glue0, struct ast_rtp_glue *glue1, int codec0, int codec1, int timeoutms, ++ int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) ++{ ++ enum ast_bridge_result res = AST_BRIDGE_FAILED; ++ struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, }; ++ int oldcodec0 = codec0, oldcodec1 = codec1; ++ struct sockaddr_in ac1 = {0,}, vac1 = {0,}, tac1 = {0,}, ac0 = {0,}, vac0 = {0,}, tac0 = {0,}; ++ struct sockaddr_in t1 = {0,}, vt1 = {0,}, tt1 = {0,}, t0 = {0,}, vt0 = {0,}, tt0 = {0,}; ++ struct ast_frame *fr = NULL; ++ ++ /* Test the first channel */ ++ if (!(glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0))) { ++ ast_rtp_instance_get_remote_address(instance1, &ac1); ++ if (vinstance1) { ++ ast_rtp_instance_get_remote_address(vinstance1, &vac1); ++ } ++ if (tinstance1) { ++ ast_rtp_instance_get_remote_address(tinstance1, &tac1); ++ } ++ } else { ++ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name); ++ } ++ ++ /* Test the second channel */ ++ if (!(glue1->update_peer(c1, instance0, vinstance0, tinstance0, codec0, 0))) { ++ ast_rtp_instance_get_remote_address(instance0, &ac0); ++ if (vinstance0) { ++ ast_rtp_instance_get_remote_address(instance0, &vac0); ++ } ++ if (tinstance0) { ++ ast_rtp_instance_get_remote_address(instance0, &tac0); ++ } ++ } else { ++ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name); ++ } ++ ++ ast_channel_unlock(c0); ++ ast_channel_unlock(c1); ++ ++ instance0->bridged = instance1; ++ instance1->bridged = instance0; ++ ++ ast_poll_channel_add(c0, c1); ++ ++ /* Go into a loop handling any stray frames that may come in */ ++ cs[0] = c0; ++ cs[1] = c1; ++ cs[2] = NULL; ++ for (;;) { ++ /* Check if anything changed */ ++ if ((c0->tech_pvt != pvt0) || ++ (c1->tech_pvt != pvt1) || ++ (c0->masq || c0->masqr || c1->masq || c1->masqr) || ++ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { ++ ast_debug(1, "Oooh, something is weird, backing out\n"); ++ res = AST_BRIDGE_RETRY; ++ break; ++ } ++ ++ /* Check if they have changed their address */ ++ ast_rtp_instance_get_remote_address(instance1, &t1); ++ if (vinstance1) { ++ ast_rtp_instance_get_remote_address(vinstance1, &vt1); ++ } ++ if (tinstance1) { ++ ast_rtp_instance_get_remote_address(tinstance1, &tt1); ++ } ++ if (glue1->get_codec) { ++ codec1 = glue1->get_codec(c1); ++ } ++ ++ ast_rtp_instance_get_remote_address(instance0, &t0); ++ if (vinstance0) { ++ ast_rtp_instance_get_remote_address(vinstance0, &vt0); ++ } ++ if (tinstance0) { ++ ast_rtp_instance_get_remote_address(tinstance0, &tt0); ++ } ++ if (glue0->get_codec) { ++ codec0 = glue0->get_codec(c0); ++ } ++ ++ if ((inaddrcmp(&t1, &ac1)) || ++ (vinstance1 && inaddrcmp(&vt1, &vac1)) || ++ (tinstance1 && inaddrcmp(&tt1, &tac1)) || ++ (codec1 != oldcodec1)) { ++ ast_debug(1, "Oooh, '%s' changed end address to %s:%d (format %d)\n", ++ c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1); ++ ast_debug(1, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n", ++ c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1); ++ ast_debug(1, "Oooh, '%s' changed end taddress to %s:%d (format %d)\n", ++ c1->name, ast_inet_ntoa(tt1.sin_addr), ntohs(tt1.sin_port), codec1); ++ ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", ++ c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1); ++ ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", ++ c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1); ++ ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", ++ c1->name, ast_inet_ntoa(tac1.sin_addr), ntohs(tac1.sin_port), oldcodec1); ++ if (glue0->update_peer(c0, t1.sin_addr.s_addr ? instance1 : NULL, vt1.sin_addr.s_addr ? vinstance1 : NULL, tt1.sin_addr.s_addr ? tinstance1 : NULL, codec1, 0)) { ++ ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name); ++ } ++ memcpy(&ac1, &t1, sizeof(ac1)); ++ memcpy(&vac1, &vt1, sizeof(vac1)); ++ memcpy(&tac1, &tt1, sizeof(tac1)); ++ oldcodec1 = codec1; ++ } ++ if ((inaddrcmp(&t0, &ac0)) || ++ (vinstance0 && inaddrcmp(&vt0, &vac0)) || ++ (tinstance0 && inaddrcmp(&tt0, &tac0))) { ++ ast_debug(1, "Oooh, '%s' changed end address to %s:%d (format %d)\n", ++ c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0); ++ ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", ++ c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0); ++ if (glue1->update_peer(c1, t0.sin_addr.s_addr ? instance0 : NULL, vt0.sin_addr.s_addr ? vinstance0 : NULL, tt0.sin_addr.s_addr ? tinstance0 : NULL, codec0, 0)) { ++ ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name); ++ } ++ memcpy(&ac0, &t0, sizeof(ac0)); ++ memcpy(&vac0, &vt0, sizeof(vac0)); ++ memcpy(&tac0, &tt0, sizeof(tac0)); ++ oldcodec0 = codec0; ++ } ++ ++ /* Wait for frame to come in on the channels */ ++ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { ++ if (!timeoutms) { ++ res = AST_BRIDGE_RETRY; ++ break; ++ } ++ ast_debug(1, "Ooh, empty read...\n"); ++ if (ast_check_hangup(c0) || ast_check_hangup(c1)) { ++ break; ++ } ++ continue; ++ } ++ fr = ast_read(who); ++ other = (who == c0) ? c1 : c0; ++ if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && ++ (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || ++ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) { ++ /* Break out of bridge */ ++ *fo = fr; ++ *rc = who; ++ ast_debug(1, "Oooh, got a %s\n", fr ? "digit" : "hangup"); ++ res = AST_BRIDGE_COMPLETE; ++ break; ++ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { ++ if ((fr->subclass == AST_CONTROL_HOLD) || ++ (fr->subclass == AST_CONTROL_UNHOLD) || ++ (fr->subclass == AST_CONTROL_VIDUPDATE) || ++ (fr->subclass == AST_CONTROL_T38) || ++ (fr->subclass == AST_CONTROL_SRCUPDATE)) { ++ if (fr->subclass == AST_CONTROL_HOLD) { ++ /* If we someone went on hold we want the other side to reinvite back to us */ ++ if (who == c0) { ++ glue1->update_peer(c1, NULL, NULL, NULL, 0, 0); ++ } else { ++ glue0->update_peer(c0, NULL, NULL, NULL, 0, 0); ++ } ++ } else if (fr->subclass == AST_CONTROL_UNHOLD) { ++ /* If they went off hold they should go back to being direct */ ++ if (who == c0) { ++ glue1->update_peer(c1, instance0, vinstance0, tinstance0, codec0, 0); ++ } else { ++ glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0); ++ } ++ } ++ /* Update local address information */ ++ ast_rtp_instance_get_remote_address(instance0, &t0); ++ memcpy(&ac0, &t0, sizeof(ac0)); ++ ast_rtp_instance_get_remote_address(instance1, &t1); ++ memcpy(&ac1, &t1, sizeof(ac1)); ++ /* Update codec information */ ++ if (glue0->get_codec && c0->tech_pvt) { ++ oldcodec0 = codec0 = glue0->get_codec(c0); ++ } ++ if (glue1->get_codec && c1->tech_pvt) { ++ oldcodec1 = codec1 = glue1->get_codec(c1); ++ } ++ ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); ++ ast_frfree(fr); ++ } else { ++ *fo = fr; ++ *rc = who; ++ ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); ++ return AST_BRIDGE_COMPLETE; ++ } ++ } else { ++ if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || ++ (fr->frametype == AST_FRAME_DTMF_END) || ++ (fr->frametype == AST_FRAME_VOICE) || ++ (fr->frametype == AST_FRAME_VIDEO) || ++ (fr->frametype == AST_FRAME_IMAGE) || ++ (fr->frametype == AST_FRAME_HTML) || ++ (fr->frametype == AST_FRAME_MODEM) || ++ (fr->frametype == AST_FRAME_TEXT)) { ++ ast_write(other, fr); ++ } ++ ast_frfree(fr); ++ } ++ /* Swap priority */ ++ cs[2] = cs[0]; ++ cs[0] = cs[1]; ++ cs[1] = cs[2]; ++ } ++ ++ if (glue0->update_peer(c0, NULL, NULL, NULL, 0, 0)) { ++ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); ++ } ++ if (glue1->update_peer(c1, NULL, NULL, NULL, 0, 0)) { ++ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); ++ } ++ ++ instance0->bridged = NULL; ++ instance1->bridged = NULL; ++ ++ ast_poll_channel_del(c0, c1); ++ ++ return res; ++} ++ ++/*! ++ * \brief Conditionally unref an rtp instance ++ */ ++static void unref_instance_cond(struct ast_rtp_instance **instance) ++{ ++ if (*instance) { ++ ao2_ref(*instance, -1); ++ *instance = NULL; ++ } ++} ++ ++enum ast_bridge_result ast_rtp_instance_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) ++{ ++ struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, ++ *vinstance0 = NULL, *vinstance1 = NULL, ++ *tinstance0 = NULL, *tinstance1 = NULL; ++ struct ast_rtp_glue *glue0, *glue1; ++ enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; ++ enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; ++ enum ast_bridge_result res = AST_BRIDGE_FAILED; ++ int codec0 = 0, codec1 = 0; ++ int unlock_chans = 1; ++ ++ /* Lock both channels so we can look for the glue that binds them together */ ++ ast_channel_lock(c0); ++ while (ast_channel_trylock(c1)) { ++ ast_channel_unlock(c0); ++ usleep(1); ++ ast_channel_lock(c0); ++ } ++ ++ /* Ensure neither channel got hungup during lock avoidance */ ++ if (ast_check_hangup(c0) || ast_check_hangup(c1)) { ++ ast_log(LOG_WARNING, "Got hangup while attempting to bridge '%s' and '%s'\n", c0->name, c1->name); ++ goto done; ++ } ++ ++ /* Grab glue that binds each channel to something using the RTP engine */ ++ if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { ++ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); ++ goto done; ++ } ++ ++ audio_glue0_res = glue0->get_rtp_info(c0, &instance0); ++ video_glue0_res = glue0->get_vrtp_info ? glue0->get_vrtp_info(c0, &vinstance0) : AST_RTP_GLUE_RESULT_FORBID; ++ text_glue0_res = glue0->get_trtp_info ? glue0->get_trtp_info(c0, &tinstance0) : AST_RTP_GLUE_RESULT_FORBID; ++ ++ audio_glue1_res = glue1->get_rtp_info(c1, &instance1); ++ video_glue1_res = glue1->get_vrtp_info ? glue1->get_vrtp_info(c1, &vinstance1) : AST_RTP_GLUE_RESULT_FORBID; ++ text_glue1_res = glue1->get_trtp_info ? glue1->get_trtp_info(c1, &tinstance1) : AST_RTP_GLUE_RESULT_FORBID; ++ ++ /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */ ++ if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) { ++ audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID; ++ } ++ if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { ++ audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; ++ } ++ ++ /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ ++ if (audio_glue0_res == AST_RTP_GLUE_RESULT_FORBID || audio_glue1_res == AST_RTP_GLUE_RESULT_FORBID) { ++ res = AST_BRIDGE_FAILED_NOWARN; ++ goto done; ++ } ++ ++ /* If we have gotten to a local bridge make sure that both sides have the same local bridge callback and that they are DTMF compatible */ ++ if ((audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL || audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) && ((instance0->engine->local_bridge != instance1->engine->local_bridge) || (instance0->engine->dtmf_compatible && !instance0->engine->dtmf_compatible(c0, instance0, c1, instance1)))) { ++ res = AST_BRIDGE_FAILED_NOWARN; ++ goto done; ++ } ++ ++ /* Make sure that codecs match */ ++ codec0 = glue0->get_codec ? glue0->get_codec(c0) : 0; ++ codec1 = glue1->get_codec ? glue1->get_codec(c1) : 0; ++ if (codec0 && codec1 && !(codec0 & codec1)) { ++ ast_debug(1, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1); ++ res = AST_BRIDGE_FAILED_NOWARN; ++ goto done; ++ } ++ ++ /* Depending on the end result for bridging either do a local bridge or remote bridge */ ++ if (audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL || audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) { ++ ast_verbose(VERBOSE_PREFIX_3 "Locally bridging %s and %s\n", c0->name, c1->name); ++ res = local_bridge_loop(c0, c1, instance0, instance1, timeoutms, flags, fo, rc, c0->tech_pvt, c1->tech_pvt); ++ } else { ++ ast_verbose(VERBOSE_PREFIX_3 "Remotely bridging %s and %s\n", c0->name, c1->name); ++ res = remote_bridge_loop(c0, c1, instance0, instance1, vinstance0, vinstance1, ++ tinstance0, tinstance1, glue0, glue1, codec0, codec1, timeoutms, flags, ++ fo, rc, c0->tech_pvt, c1->tech_pvt); ++ } ++ ++ unlock_chans = 0; ++ ++done: ++ if (unlock_chans) { ++ ast_channel_unlock(c0); ++ ast_channel_unlock(c1); ++ } ++ ++ unref_instance_cond(&instance0); ++ unref_instance_cond(&instance1); ++ unref_instance_cond(&vinstance0); ++ unref_instance_cond(&vinstance1); ++ unref_instance_cond(&tinstance0); ++ unref_instance_cond(&tinstance1); ++ ++ return res; ++} ++ ++struct ast_rtp_instance *ast_rtp_instance_get_bridged(struct ast_rtp_instance *instance) ++{ ++ return instance->bridged; ++} ++ ++void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struct ast_channel *c1) ++{ ++ struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, ++ *vinstance0 = NULL, *vinstance1 = NULL, ++ *tinstance0 = NULL, *tinstance1 = NULL; ++ struct ast_rtp_glue *glue0, *glue1; ++ enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; ++ enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; ++ int codec0 = 0, codec1 = 0; ++ int res = 0; ++ ++ /* Lock both channels so we can look for the glue that binds them together */ ++ ast_channel_lock(c0); ++ while (ast_channel_trylock(c1)) { ++ ast_channel_unlock(c0); ++ usleep(1); ++ ast_channel_lock(c0); ++ } ++ ++ /* Grab glue that binds each channel to something using the RTP engine */ ++ if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { ++ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); ++ goto done; ++ } ++ ++ audio_glue0_res = glue0->get_rtp_info(c0, &instance0); ++ video_glue0_res = glue0->get_vrtp_info ? glue0->get_vrtp_info(c0, &vinstance0) : AST_RTP_GLUE_RESULT_FORBID; ++ text_glue0_res = glue0->get_trtp_info ? glue0->get_trtp_info(c0, &tinstance0) : AST_RTP_GLUE_RESULT_FORBID; ++ ++ audio_glue1_res = glue1->get_rtp_info(c1, &instance1); ++ video_glue1_res = glue1->get_vrtp_info ? glue1->get_vrtp_info(c1, &vinstance1) : AST_RTP_GLUE_RESULT_FORBID; ++ text_glue1_res = glue1->get_trtp_info ? glue1->get_trtp_info(c1, &tinstance1) : AST_RTP_GLUE_RESULT_FORBID; ++ ++ /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */ ++ if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) { ++ audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID; ++ } ++ if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { ++ audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; ++ } ++ if (audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE) && glue0->get_codec(c0)) { ++ codec0 = glue0->get_codec(c0); ++ } ++ if (audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) && glue1->get_codec(c1)) { ++ codec1 = glue1->get_codec(c1); ++ } ++ ++ /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ ++ if (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE) { ++ goto done; ++ } ++ ++ /* Make sure we have matching codecs */ ++ if (!(codec0 & codec1)) { ++ goto done; ++ } ++ ++ ast_rtp_codecs_payloads_copy(&instance0->codecs, &instance1->codecs, instance1); ++ ++ if (vinstance0 && vinstance1) { ++ ast_rtp_codecs_payloads_copy(&vinstance0->codecs, &vinstance1->codecs, vinstance1); ++ } ++ if (tinstance0 && tinstance1) { ++ ast_rtp_codecs_payloads_copy(&tinstance0->codecs, &tinstance1->codecs, tinstance1); ++ } ++ ++ res = 0; ++ ++done: ++ ast_channel_unlock(c0); ++ ast_channel_unlock(c1); ++ ++ unref_instance_cond(&instance0); ++ unref_instance_cond(&instance1); ++ unref_instance_cond(&vinstance0); ++ unref_instance_cond(&vinstance1); ++ unref_instance_cond(&tinstance0); ++ unref_instance_cond(&tinstance1); ++ ++ if (!res) { ++ ast_debug(1, "Seeded SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); ++ } ++} ++ ++int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1) ++{ ++ struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, ++ *vinstance0 = NULL, *vinstance1 = NULL, ++ *tinstance0 = NULL, *tinstance1 = NULL; ++ struct ast_rtp_glue *glue0, *glue1; ++ enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; ++ enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; ++ int codec0 = 0, codec1 = 0; ++ int res = 0; ++ ++ /* If there is no second channel just immediately bail out, we are of no use in that scenario */ ++ if (!c1) { ++ return -1; ++ } ++ ++ /* Lock both channels so we can look for the glue that binds them together */ ++ ast_channel_lock(c0); ++ while (ast_channel_trylock(c1)) { ++ ast_channel_unlock(c0); ++ usleep(1); ++ ast_channel_lock(c0); ++ } ++ ++ /* Grab glue that binds each channel to something using the RTP engine */ ++ if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { ++ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); ++ goto done; ++ } ++ ++ audio_glue0_res = glue0->get_rtp_info(c0, &instance0); ++ video_glue0_res = glue0->get_vrtp_info ? glue0->get_vrtp_info(c0, &vinstance0) : AST_RTP_GLUE_RESULT_FORBID; ++ text_glue0_res = glue0->get_trtp_info ? glue0->get_trtp_info(c0, &tinstance0) : AST_RTP_GLUE_RESULT_FORBID; ++ ++ audio_glue1_res = glue1->get_rtp_info(c1, &instance1); ++ video_glue1_res = glue1->get_vrtp_info ? glue1->get_vrtp_info(c1, &vinstance1) : AST_RTP_GLUE_RESULT_FORBID; ++ text_glue1_res = glue1->get_trtp_info ? glue1->get_trtp_info(c1, &tinstance1) : AST_RTP_GLUE_RESULT_FORBID; ++ ++ /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */ ++ if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) { ++ audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID; ++ } ++ if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { ++ audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; ++ } ++ if (audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE) && glue0->get_codec(c0)) { ++ codec0 = glue0->get_codec(c0); ++ } ++ if (audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) && glue1->get_codec(c1)) { ++ codec1 = glue1->get_codec(c1); ++ } ++ ++ /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ ++ if (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE) { ++ goto done; ++ } ++ ++ /* Make sure we have matching codecs */ ++ if (!(codec0 & codec1)) { ++ goto done; ++ } ++ ++ /* Bridge media early */ ++ if (glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0)) { ++ ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); ++ } ++ ++ res = 0; ++ ++done: ++ ast_channel_unlock(c0); ++ ast_channel_unlock(c1); ++ ++ unref_instance_cond(&instance0); ++ unref_instance_cond(&instance1); ++ unref_instance_cond(&vinstance0); ++ unref_instance_cond(&vinstance1); ++ unref_instance_cond(&tinstance0); ++ unref_instance_cond(&tinstance1); ++ ++ if (!res) { ++ ast_debug(1, "Setting early bridge SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); ++ } ++ ++ return res; ++} ++ ++int ast_rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations) ++{ ++ return instance->engine->red_init ? instance->engine->red_init(instance, buffer_time, payloads, generations) : -1; ++} ++ ++int ast_rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame) ++{ ++ return instance->engine->red_buffer ? instance->engine->red_buffer(instance, frame) : -1; ++} ++ ++int ast_rtp_instance_get_stats(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat) ++{ ++ return instance->engine->get_stat ? instance->engine->get_stat(instance, stats, stat) : -1; ++} ++ ++char *ast_rtp_instance_get_quality(struct ast_rtp_instance *instance, enum ast_rtp_instance_stat_field field, char *buf, size_t size) ++{ ++ struct ast_rtp_instance_stats stats; ++ enum ast_rtp_instance_stat stat; ++ ++ /* Determine what statistics we will need to retrieve based on field passed in */ ++ if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY) { ++ stat = AST_RTP_INSTANCE_STAT_ALL; ++ } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER) { ++ stat = AST_RTP_INSTANCE_STAT_COMBINED_JITTER; ++ } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS) { ++ stat = AST_RTP_INSTANCE_STAT_COMBINED_LOSS; ++ } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT) { ++ stat = AST_RTP_INSTANCE_STAT_COMBINED_RTT; ++ } else { ++ return NULL; ++ } ++ ++ /* Attempt to actually retrieve the statistics we need to generate the quality string */ ++ if (ast_rtp_instance_get_stats(instance, &stats, stat)) { ++ return NULL; ++ } ++ ++ /* Now actually fill the buffer with the good information */ ++ if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY) { ++ snprintf(buf, size, "ssrc=%i;themssrc=%u;lp=%u;rxjitter=%u;rxcount=%u;txjitter=%u;txcount=%u;rlp=%u;rtt=%u", ++ stats.local_ssrc, stats.remote_ssrc, stats.rxploss, stats.txjitter, stats.rxcount, stats.rxjitter, stats.txcount, stats.txploss, stats.rtt); ++ } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER) { ++ snprintf(buf, size, "minrxjitter=%f;maxrxjitter=%f;avgrxjitter=%f;stdevrxjitter=%f;reported_minjitter=%f;reported_maxjitter=%f;reported_avgjitter=%f;reported_stdevjitter=%f;", ++ stats.local_minjitter, stats.local_maxjitter, stats.local_normdevjitter, sqrt(stats.local_stdevjitter), stats.remote_minjitter, stats.remote_maxjitter, stats.remote_normdevjitter, sqrt(stats.remote_stdevjitter)); ++ } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS) { ++ snprintf(buf, size, "minrxlost=%f;maxrxlost=%f;avgrxlost=%f;stdevrxlost=%f;reported_minlost=%f;reported_maxlost=%f;reported_avglost=%f;reported_stdevlost=%f;", ++ stats.local_minrxploss, stats.local_maxrxploss, stats.local_normdevrxploss, sqrt(stats.local_stdevrxploss), stats.remote_minrxploss, stats.remote_maxrxploss, stats.remote_normdevrxploss, sqrt(stats.remote_stdevrxploss)); ++ } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT) { ++ snprintf(buf, size, "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;", stats.minrtt, stats.maxrtt, stats.normdevrtt, stats.stdevrtt); ++ } ++ ++ return buf; ++} ++ ++void ast_rtp_instance_set_stats_vars(struct ast_channel *chan, struct ast_rtp_instance *instance) ++{ ++ char quality_buf[AST_MAX_USER_FIELD], *quality; ++ struct ast_channel *bridge = ast_bridged_channel(chan); ++ ++ if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { ++ pbx_builtin_setvar_helper(chan, "RTPAUDIOQOS", quality); ++ if (bridge) { ++ pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSBRIDGED", quality); ++ } ++ } ++ ++ if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) { ++ pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSJITTER", quality); ++ if (bridge) { ++ pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSJITTERBRIDGED", quality); ++ } ++ } ++ ++ if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) { ++ pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSLOSS", quality); ++ if (bridge) { ++ pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSLOSSBRIDGED", quality); ++ } ++ } ++ ++ if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) { ++ pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSRTT", quality); ++ if (bridge) { ++ pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSRTTBRIDGED", quality); ++ } ++ } ++} ++ ++int ast_rtp_instance_set_read_format(struct ast_rtp_instance *instance, int format) ++{ ++ return instance->engine->set_read_format ? instance->engine->set_read_format(instance, format) : -1; ++} ++ ++int ast_rtp_instance_set_write_format(struct ast_rtp_instance *instance, int format) ++{ ++ return instance->engine->set_write_format ? instance->engine->set_write_format(instance, format) : -1; ++} ++ ++int ast_rtp_instance_make_compatible(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_channel *peer) ++{ ++ struct ast_rtp_glue *glue; ++ struct ast_rtp_instance *peer_instance = NULL; ++ int res = -1; ++ ++ if (!instance->engine->make_compatible) { ++ return -1; ++ } ++ ++ ast_channel_lock(peer); ++ ++ if (!(glue = ast_rtp_instance_get_glue(peer->tech->type))) { ++ ast_channel_unlock(peer); ++ return -1; ++ } ++ ++ glue->get_rtp_info(peer, &peer_instance); ++ ++ if (!peer_instance || peer_instance->engine != instance->engine) { ++ ast_channel_unlock(peer); ++ peer_instance = (ao2_ref(peer_instance, -1), NULL); ++ return -1; ++ } ++ ++ res = instance->engine->make_compatible(chan, instance, peer, peer_instance); ++ ++ ast_channel_unlock(peer); ++ ++ peer_instance = (ao2_ref(peer_instance, -1), NULL); ++ ++ return res; ++} ++ ++int ast_rtp_instance_activate(struct ast_rtp_instance *instance) ++{ ++ return instance->engine->activate ? instance->engine->activate(instance) : 0; ++} ++ ++void ast_rtp_instance_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username) ++{ ++ if (instance->engine->stun_request) { ++ instance->engine->stun_request(instance, suggestion, username); ++ } ++} ++ ++void ast_rtp_instance_set_timeout(struct ast_rtp_instance *instance, int timeout) ++{ ++ instance->timeout = timeout; ++} ++ ++void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int timeout) ++{ ++ instance->holdtimeout = timeout; ++} ++ ++int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance) ++{ ++ return instance->timeout; ++} ++ ++int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance) ++{ ++ return instance->holdtimeout; ++} + +Property changes on: main/rtp_engine.c +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: configs/sip.conf.sample +=================================================================== +--- a/configs/sip.conf.sample (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/configs/sip.conf.sample (.../trunk) (revision 186562) +@@ -182,6 +182,11 @@ + ;vmexten=voicemail ; dialplan extension to reach mailbox sets the + ; Message-Account in the MWI notify message + ; defaults to "asterisk" ++ ++;preferred_codec_only=yes ; Respond to a SIP invite with the single most preferred codec ++ ; rather than advertising all joint codec capabilities. This ++ ; limits the other side's codec choice to exactly what we prefer. ++ + ;disallow=all ; First disallow all codecs + ;allow=ulaw ; Allow codecs in order of preference + ;allow=ilbc ; see doc/rtp-packetization for framing options +@@ -209,6 +214,14 @@ + ;relaxdtmf=yes ; Relax dtmf handling + ;trustrpid = no ; If Remote-Party-ID should be trusted + ;sendrpid = yes ; If Remote-Party-ID should be sent ++;sendrpid = rpid ; Use the "Remote-Party-ID" header ++ ; to send the identity of the remote party ++ ; This is identical to sendrpid=yes ++;sendrpid = pai ; Use the "P-Asserted-Identity" header ++ ; to send the identity of the remote party ++;rpid_header = rpid ; Which header should be used when sending Remote Party ID ++ ; 'rpid' means to send "Remote-Party-ID" ++ ; 'pai' means to send "P-Asserted-Identity" + ;progressinband=never ; If we should generate in-band ringing always + ; use 'never' to never use in-band signalling, even in cases + ; where some buggy devices might not render it +@@ -256,9 +269,11 @@ + ;authfailureevents=no ; generate manager "peerstatus" events when peer can't + ; authenticate with Asterisk. Peerstatus will be "rejected". + ;alwaysauthreject = yes ; When an incoming INVITE or REGISTER is to be rejected, +- ; for any reason, always reject with '401 Unauthorized' ++ ; for any reason, always reject with an identical response ++ ; equivalent to valid username and invalid password/hash + ; instead of letting the requester know whether there was +- ; a matching user or peer for their request ++ ; a matching user or peer for their request. This reduces ++ ; the ability of an attacker to scan for valid SIP usernames. + + ;g726nonstandard = yes ; If the peer negotiates G726-32 audio, use AAL2 packing + ; order instead of RFC3551 packing order (this is required +@@ -285,6 +300,8 @@ + ;contactpermit=172.16.0.0/255.255.0.0 ; restrict at what IPs your users may + ; register their phones. + ++;engine=asterisk ; RTP engine to use when communicating with the device ++ + ; + ; If regcontext is specified, Asterisk will dynamically create and destroy a + ; NoOp priority 1 extension for a given peer who registers or unregisters with +Index: configs/voicemail.conf.sample +=================================================================== +--- a/configs/voicemail.conf.sample (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/configs/voicemail.conf.sample (.../trunk) (revision 186562) +@@ -115,6 +115,10 @@ + ; Change the from, body and/or subject, variables: + ; VM_NAME, VM_DUR, VM_MSGNUM, VM_MAILBOX, VM_CALLERID, VM_CIDNUM, + ; VM_CIDNAME, VM_DATE ++; Additionally, on forwarded messages, you have the variables: ++; ORIG_VM_CALLERID, ORIG_VM_CIDNUM, ORIG_VM_CIDNAME, ORIG_VM_DATE ++; You can select between two variables by using dialplan functions, e.g. ++; ${IF(${ISNULL(${ORIG_VM_DATE})}?${VM_DATE}:${ORIG_VM_DATE})} + ; + ; Note: The emailbody config row can only be up to 512 characters due to a + ; limitation in the Asterisk configuration subsystem. +@@ -124,6 +128,11 @@ + ; caller", if they are both null. + ;emailbody=Dear ${VM_NAME}:\n\n\tjust wanted to let you know you were just left a ${VM_DUR} long message (number ${VM_MSGNUM})\nin mailbox ${VM_MAILBOX} from ${VM_CALLERID}, on ${VM_DATE}, so you might\nwant to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n + ; ++; Note: ${IF()} strips spacing at the beginning and end of its true and false ++; values, so a newline cannot be placed at either location. The word 'so' is ++; therefore duplicated, in order for the newline to be interpreted correctly. ++;emailbody=Dear ${VM_NAME}:\n\n\tjust wanted to let you know you were just ${IF($["${VM_CIDNUM}" = "${ORIG_VM_CIDNUM}"]?left:forwarded)} a ${VM_DUR} long message (number ${VM_MSGNUM})\nin mailbox ${VM_MAILBOX} from ${VM_CALLERID}, on ${VM_DATE},\n${IF($["${VM_CIDNUM}" = "${ORIG_VM_CIDNUM}"]?so:(originally sent by ${ORIG_VM_CALLERID} on ${ORIG_VM_DATE})\nso)} you might want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n ++; + ; You can also change the Pager From: string, the pager body and/or subject. + ; The above defined variables also can be used here + ;pagerfromstring=The Asterisk PBX +@@ -240,8 +249,10 @@ + ; exitcontext=fromvm ; Context to go to on user exit such as * or 0 + ; The default is the current context. + ; review=yes ; Allow sender to review/rerecord their message before saving it [OFF by default +-; operator=yes ; Allow sender to hit 0 before/after/during leaving a voicemail to +- ; reach an operator [OFF by default] ++; operator=yes ; Allow sender to hit 0 before/after/during leaving a voicemail to ++ ; reach an operator. This option REQUIRES an 'o' extension in the ++ ; same context (or in exitcontext, if set), as that is where the ++ ; 0 key will send you. [OFF by default] + ; envelope=no ; Turn on/off envelope playback before message playback. [ON by default] + ; This does NOT affect option 3,3 from the advanced options menu + ; delete=yes ; After notification, the voicemail is deleted from the server. [per-mailbox only] +Index: configs/misdn.conf.sample +=================================================================== +--- a/configs/misdn.conf.sample (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/configs/misdn.conf.sample (.../trunk) (revision 186562) +@@ -7,13 +7,13 @@ + ; for debugging and general setup, things that are not bound to port groups + ; + +-[general] ++[general] + ; + ; Sets the Path to the misdn-init.conf (for nt_ptp mode checking) + ; + misdn_init=/etc/misdn-init.conf + +-; set debugging flag: ++; set debugging flag: + ; 0 - No Debug + ; 1 - mISDN Messages and * - Messages, and * - State changes + ; 2 - Messages + Message specific Informations (e.g. bearer capability) +@@ -26,8 +26,8 @@ + + + +-; set debugging file and flags for mISDNuser (NT-Stack) +-; ++; set debugging file and flags for mISDNuser (NT-Stack) ++; + ; flags can be or'ed with the following values: + ; + ; DBGM_NET 0x00000001 +@@ -57,7 +57,7 @@ + ntdebugfile=/var/log/misdn-nt.log + + +-; some pbx systems do cut the L1 for some milliseconds, to avoid ++; some pbx systems do cut the L1 for some milliseconds, to avoid + ; dropping running calls, we can set this flag to yes and tell + ; mISDNuser not to drop the calls on L2_RELEASE + ntkeepcalls=no +@@ -76,26 +76,13 @@ + bridging=no + + +-; +-; watches the L1s of every port. If one l1 is down it tries to +-; get it up. The timeout is given in seconds. with 0 as value it +-; does not watch the l1 at all +-; +-; default value: 0 +-; +-; this option is only read at loading time of chan_misdn, +-; which means you need to unload and load chan_misdn to change the +-; value, an asterisk restart should do the trick +-; +-l1watcher_timeout=0 +- + ; stops dialtone after getting first digit on nt Port + ; + ; default value: yes + ; + stop_tone_after_first_digit=yes + +-; whether to append overlapdialed Digits to Extension or not ++; whether to append overlapdialed Digits to Extension or not + ; + ; default value: yes + ; +@@ -122,19 +109,6 @@ + ; + crypt_keys=test,muh + +-; users sections: +-; +-; name your sections as you which but not "general" ! +-; the sections are Groups, you can dial out in extensions.conf +-; with Dial(mISDN/g:extern/101) where extern is a section name, +-; chan_misdn tries every port in this section to find a +-; new free channel +-; +- +-; The default section is not a group section, it just contains config elements +-; which are inherited by group sections. +-; +- + ;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- + ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a + ; SIP channel. Defaults to "no". An enabled jitterbuffer will +@@ -161,6 +135,17 @@ + ; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". + ;----------------------------------------------------------------------------------- + ++; users sections: ++; ++; name your sections as you wish but not "general" or "default" ! ++; the sections are Groups, you can dial out in extensions.conf ++; with Dial(mISDN/g:extern/101) where extern is a section name, ++; chan_misdn tries every port in this section to find a ++; new free channel ++; ++; The default section is not a group section, it just contains config elements ++; which are inherited by group sections. ++; + [default] + + ; define your default context here +@@ -182,7 +167,7 @@ + + ; + ; Either if we should produce DTMF Tones ourselves +-; ++; + senddtmf=yes + + ; +@@ -205,14 +190,26 @@ + ; + allowed_bearers=all + +-; Prefixes for national and international, those are put before the +-; oad if an according dialplan is set by the other end. ++; Prefixes for national and international Type-Of-Number. These are ++; inserted before any number (caller, dialed, connected, redirecting, ++; redirection) received from the ISDN link if that number has the ++; correspondng Type-Of-Number. ++; See the dialplan options. + ; +-; default values: nationalprefix : 0 +-; internationalprefix : 00 ++; default values: ++; unknownprefix= ++; internationalprefix=00 ++; nationalprefix=0 ++; netspecificprefix= ++; subscriberprefix= ++; abbreviatedprefix= + ; ++;unknownprefix= ++internationalprefix=00 + nationalprefix=0 +-internationalprefix=00 ++;netspecificprefix= ++;subscriberprefix= ++;abbreviatedprefix= + + ; set rx/tx gains between -8 and 8 to change the RX/TX Gain + ; +@@ -222,7 +219,7 @@ + rxgain=0 + txgain=0 + +-; some telcos especially in NL seem to need this set to yes, also in ++; some telcos especially in NL seem to need this set to yes, also in + ; switzerland this seems to be important + ; + ; default value: no +@@ -232,7 +229,20 @@ + + + ; +-; This option defines, if chan_misdn should check the L1 on a PMP ++; Monitors L1 of the port. If L1 is down it tries ++; to bring it up. The polling timeout is given in seconds. ++; Setting the value to 0 disables monitoring L1 of the port. ++; ++; default value: 0 ++; ++; This option is only read at chan_misdn loading time. ++; You need to unload and load chan_misdn to change the ++; value. An asterisk restart will also do the trick. ++; ++l1watcher_timeout=0 ++ ++; ++; This option defines, if chan_misdn should check the L1 on a PMP + ; before making a group call on it. The L1 may go down for PMP Ports + ; so we might need this. + ; But be aware! a broken or plugged off cable might be used for a group call +@@ -245,19 +255,19 @@ + + + ; +-; in PMP this option defines which cause should be sent out to ++; in PMP this option defines which cause should be sent out to + ; the 3. caller. chan_misdn does not support callwaiting on TE +-; PMP side. This allows to modify the RELEASE_COMPLETE cause ++; PMP side. This allows to modify the RELEASE_COMPLETE cause + ; at least. + ; + reject_cause=16 + + + ; +-; Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING), +-; this requests additional Infos, so we can waitfordigits ++; Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING), ++; this requests additional Infos, so we can waitfordigits + ; without much issues. This works only for PTP Ports +-; ++; + ; default value: no + ; + need_more_infos=no +@@ -269,40 +279,42 @@ + ; + nttimeout=no + +-; set the method to use for channel selection: +-; standard - always choose the first free channel with the lowest number +-; round_robin - use the round robin algorithm to select a channel. use this +-; if you want to balance your load. ++; Set the method to use for channel selection: ++; standard - Use the first free channel starting from the lowest number. ++; standard_dec - Use the first free channel starting from the highest number. ++; round_robin - Use the round robin algorithm to select a channel. Use this ++; if you want to balance your load. + ; + ; default value: standard + ; + method=standard + + +-; specify if chan_misdn should collect digits before going into the ++; specify if chan_misdn should collect digits before going into the + ; dialplan, you can choose yes=4 Seconds, no, or specify the amount + ; of seconds you need; +-; ++; + overlapdial=yes + + ; +-; dialplan means Type Of Number in ISDN Terms (for outgoing calls) ++; dialplan means Type Of Number in ISDN Terms ++; There are different types of the dialplan: + ; +-; there are different types of the dialplan: ++; dialplan -> for outgoing call's dialed number ++; localdialplan -> for outgoing call's callerid ++; (if -1 is set use the value from the asterisk channel) ++; cpndialplan -> for incoming call's connected party number sent to caller ++; (if -1 is set use the value from the asterisk channel) + ; +-; dialplan -> outgoing Number +-; localdialplan -> callerid +-; cpndialplan -> connected party number ++; dialplan options: + ; +-; dialplan options: +-; + ; 0 - unknown + ; 1 - International + ; 2 - National ++; 3 - Network-Specific + ; 4 - Subscriber ++; 5 - Abbreviated + ; +-; This setting is used for outgoing calls +-; + ; default value: 0 + ; + dialplan=0 +@@ -312,7 +324,7 @@ + + + ; +-; turn this to no if you don't mind correct handling of Progress Indicators ++; turn this to no if you don't mind correct handling of Progress Indicators + ; + early_bconnect=yes + +@@ -320,16 +332,16 @@ + ; + ; turn this on if you like to send Tone Indications to a Incoming + ; isdn channel on a TE Port. Rarely used, only if the Telco allows +-; you to send indications by yourself, normally the Telco sends the ++; you to send indications by yourself, normally the Telco sends the + ; indications to the remote party. +-; ++; + ; default: no + ; + incoming_early_audio=no + + ; uncomment the following to get into s extension at extension conf + ; there you can use DigitTimeout if you can't or don't want to use +-; isdn overlap dial. ++; isdn overlap dial. + ; note: This will jump into the s exten for every exten! + ; + ; default value: no +@@ -337,7 +349,7 @@ + ;always_immediate=no + + ; +-; set this to yes if you want to generate your own dialtone ++; set this to yes if you want to generate your own dialtone + ; with always_immediate=yes, else chan_misdn generates the dialtone + ; + ; default value: no +@@ -345,9 +357,9 @@ + nodialtone=no + + +-; uncomment the following if you want callers which called exactly the ++; uncomment the following if you want callers which called exactly the + ; base number (so no extension is set) jump to the s extension. +-; if the user dials something more it jumps to the correct extension ++; if the user dials something more it jumps to the correct extension + ; instead + ; + ; default value: no +@@ -368,6 +380,8 @@ + ;callgroup=1 + ;pickupgroup=1 + ++; Set the outgoing caller id to the value. ++;callerid="name" <number> + + ; + ; these are the exact isdn screening and presentation indicators +@@ -375,11 +389,31 @@ + ; from asterisks CALLERPRES function. + ; s=0, p=0 -> callerid presented + ; s=1, p=1 -> callerid restricted (the remote end does not see it!) +-; ++; + ; default values s=-1, p=-1 + presentation=-1 + screen=-1 + ++; Put a display ie in the CONNECT message containing the following ++; information if it is available (nt port only): ++; ++; 0 - Do not put the connected line information in the display ie. ++; 1 - Put the available connected line name in the display ie. ++; 2 - Put the available connected line number in the display ie. ++; 3 - Put the available connected line name and number in the display ie. ++; ++display_connected=0 ++ ++; Put a display ie in the SETUP message containing the following ++; information if it is available (nt port only): ++; ++; 0 - Do not put the caller information in the display ie. ++; 1 - Put the available caller name in the display ie. ++; 2 - Put the available caller number in the display ie. ++; 3 - Put the available caller name and number in the display ie. ++; ++display_setup=0 ++ + ; This enables echo cancellation with the given number of taps. + ; Be aware: Move this setting only to outgoing portgroups! + ; A value of zero turns echo cancellation off. +@@ -390,18 +424,9 @@ + ; + ;echocancel=no + +-; Set this to no to disable echotraining. You can enter a number > 10 +-; the value is a multiple of 0.125 ms. + ; +-; default value: no +-; yes = 2000 +-; no = 0 ++; chan_misdns jitterbuffer, default 4000 + ; +-echotraining=no +- +-; +-; chan_misdns jitterbuffer, default 4000 +-; + jitterbuffer=4000 + + ; +@@ -411,7 +436,7 @@ + + + ; +-; change this to yes, if you want to bridge a mISDN data channel to ++; change this to yes, if you want to bridge a mISDN data channel to + ; another channel type or to an application. + ; + hdlc=no +@@ -419,8 +444,8 @@ + + ; + ; defines the maximum amount of incoming calls per port for +-; this group. Calls which exceed the maximum will be marked with +-; the channel variable MAX_OVERFLOW. It will contain the amount of ++; this group. Calls which exceed the maximum will be marked with ++; the channel variable MAX_OVERFLOW. It will contain the amount of + ; overflowed calls + ; + max_incoming=-1 +@@ -432,7 +457,7 @@ + max_outgoing=-1 + + [intern] +-; define your ports, e.g. 1,2 (depends on mISDN-driver loading order) ++; define your ports, e.g. 1,2 (depends on mISDN-driver loading order) + ports=1,2 + ; context where to go to when incoming Call on one of the above ports + context=Intern +@@ -444,21 +469,21 @@ + ; configs. For backwards compatibility you can still set ptp here. + ; + ports=3 +- ++ + [first_extern] + ; again port defs + ports=4 + ; again a context for incoming calls + context=Extern1 +-; msns for te ports, listen on those numbers on the above ports, and ++; msns for te ports, listen on those numbers on the above ports, and + ; indicate the incoming calls to asterisk +-; here you can give a comma separated list or simply an '*' for +-; any msn. ++; here you can give a comma separated list or simply an '*' for ++; any msn. + msns=* + + ; here an example with given msns + [second_extern] + ports=5 + context=Extern2 +-callerid=15 ++callerid="Asterisk" <1234> + msns=102,144,101,104 +Index: configs/features.conf.sample +=================================================================== +--- a/configs/features.conf.sample (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/configs/features.conf.sample (.../trunk) (revision 186562) +@@ -62,7 +62,7 @@ + ;disconnect => *0 ; Disconnect (default is *) -- Make sure to set the H and/or h option in the Dial() or Queue() app call! + ;automon => *1 ; One Touch Record a.k.a. Touch Monitor -- Make sure to set the W and/or w option in the Dial() or Queue() app call! + ;atxfer => *2 ; Attended transfer -- Make sure to set the T and/or t option in the Dial() or Queue() app call! +-;parkcall => #72 ; Park call (one step parking) -- Make sure to set the K and/or K option in the Dial() app call! ++;parkcall => #72 ; Park call (one step parking) -- Make sure to set the K and/or k option in the Dial() app call! + ;automixmon => *3 ; One Touch Record a.k.a. Touch MixMonitor -- Make sure to set the X and/or x option in the Dial() or Queue() app call! + + [applicationmap] +Index: makeopts.in +=================================================================== +--- a/makeopts.in (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/makeopts.in (.../trunk) (revision 186562) +@@ -47,6 +47,8 @@ + PTHREAD_CFLAGS=@PTHREAD_CFLAGS@ + PTHREAD_LIBS=@PTHREAD_LIBS@ + ++GNU_LD=@GNU_LD@ ++ + prefix = @prefix@ + exec_prefix = @exec_prefix@ + +Index: res/res_config_sqlite.c +=================================================================== +--- a/res/res_config_sqlite.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_config_sqlite.c (.../trunk) (revision 186562) +@@ -1862,7 +1862,7 @@ + return 0; + } + +-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime SQLite configuration", ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Realtime SQLite configuration", + .load = load_module, + .unload = unload_module, + ); +Index: res/res_speech.exports +=================================================================== +--- a/res/res_speech.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_speech.exports (.../trunk) (revision 186562) +@@ -0,0 +1,21 @@ ++{ ++ global: ++ ast_speech_change; ++ ast_speech_change_results_type; ++ ast_speech_change_state; ++ ast_speech_destroy; ++ ast_speech_dtmf; ++ ast_speech_grammar_activate; ++ ast_speech_grammar_deactivate; ++ ast_speech_grammar_load; ++ ast_speech_grammar_unload; ++ ast_speech_new; ++ ast_speech_register; ++ ast_speech_results_free; ++ ast_speech_results_get; ++ ast_speech_start; ++ ast_speech_unregister; ++ ast_speech_write; ++ local: ++ *; ++}; + +Property changes on: res/res_speech.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_config_odbc.c +=================================================================== +--- a/res/res_config_odbc.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_config_odbc.c (.../trunk) (revision 186562) +@@ -1067,7 +1067,7 @@ + return 0; + } + +-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime ODBC configuration", ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Realtime ODBC configuration", + .load = load_module, + .unload = unload_module, + .reload = reload_module, +Index: res/res_agi.c +=================================================================== +--- a/res/res_agi.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_agi.c (.../trunk) (revision 186562) +@@ -737,6 +737,10 @@ + ast_frfree(f); + } + } ++ ++ if (async_agi.speech) { ++ ast_speech_destroy(async_agi.speech); ++ } + quit: + /* notify manager users this channel cannot be + controlled anymore by Async AGI */ +@@ -2929,6 +2933,9 @@ + } + } + } ++ if (agi->speech) { ++ ast_speech_destroy(agi->speech); ++ } + /* Notify process */ + if (send_sighup) { + if (pid > -1) { +Index: res/res_adsi.exports +=================================================================== +--- a/res/res_adsi.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_adsi.exports (.../trunk) (revision 186562) +@@ -0,0 +1,33 @@ ++{ ++ global: ++ ast_adsi_available; ++ ast_adsi_begin_download; ++ ast_adsi_channel_restore; ++ ast_adsi_clear_screen; ++ ast_adsi_clear_soft_keys; ++ ast_adsi_connect_session; ++ ast_adsi_data_mode; ++ ast_adsi_disconnect_session; ++ ast_adsi_display; ++ ast_adsi_download_connect; ++ ast_adsi_download_disconnect; ++ ast_adsi_end_download; ++ ast_adsi_get_cpeid; ++ ast_adsi_get_cpeinfo; ++ ast_adsi_input_control; ++ ast_adsi_input_format; ++ ast_adsi_load_session; ++ ast_adsi_load_soft_key; ++ ast_adsi_print; ++ ast_adsi_query_cpeid; ++ ast_adsi_query_cpeinfo; ++ ast_adsi_read_encoded_dtmf; ++ ast_adsi_set_keys; ++ ast_adsi_set_line; ++ ast_adsi_transmit_message; ++ ast_adsi_transmit_message_full; ++ ast_adsi_unload_session; ++ ast_adsi_voice_mode; ++ local: ++ *; ++}; + +Property changes on: res/res_adsi.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_config_ldap.c +=================================================================== +--- a/res/res_config_ldap.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_config_ldap.c (.../trunk) (revision 186562) +@@ -1758,7 +1758,7 @@ + return CLI_SUCCESS; + } + +-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "LDAP realtime interface", ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "LDAP realtime interface", + .load = load_module, + .unload = unload_module, + .reload = reload, +Index: res/res_odbc.c +=================================================================== +--- a/res/res_odbc.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_odbc.c (.../trunk) (revision 186562) +@@ -133,7 +133,7 @@ + struct ao2_container *obj_container; + }; + +-struct ao2_container *class_container; ++static struct ao2_container *class_container; + + static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables); + +@@ -415,11 +415,12 @@ + + /*! + * \brief Find or create an entry describing the table specified. +- * \param obj An active ODBC handle on which to query the table +- * \param table Tablename to describe ++ * \param database Name of an ODBC class on which to query the table ++ * \param tablename Tablename to describe + * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs. + * When a structure is returned, the contained columns list will be + * rdlock'ed, to ensure that it will be retained in memory. ++ * \since 1.6.1 + */ + struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename) + { +Index: res/res_snmp.c +=================================================================== +--- a/res/res_snmp.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_snmp.c (.../trunk) (revision 186562) +@@ -115,7 +115,7 @@ + return ((thread != AST_PTHREADT_NULL) ? pthread_join(thread, NULL) : 0); + } + +-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "SNMP [Sub]Agent for Asterisk", ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SNMP [Sub]Agent for Asterisk", + .load = load_module, + .unload = unload_module, + ); +Index: res/ais/evt.c +=================================================================== +--- a/res/ais/evt.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/ais/evt.c (.../trunk) (revision 186562) +@@ -47,6 +47,7 @@ + #include "asterisk/event.h" + #include "asterisk/config.h" + #include "asterisk/linkedlists.h" ++#include "asterisk/devicestate.h" + + #ifndef AST_MODULE + /* XXX HACK */ +@@ -111,34 +112,7 @@ + + static void queue_event(struct ast_event *ast_event) + { +- enum ast_event_type type; +- +- /*! +- * \todo This hack macks me sad. I need to come up with a better way to +- * figure out whether an event should be cached or not, and what +- * parameters to cache on. +- * +- * As long as the types of events that are supported is limited, +- * this isn't *terrible*, I guess. Perhaps we should just define +- * caching rules in the core, and make them configurable, and not +- * have it be the job of the event publishers. +- */ +- +- type = ast_event_get_type(ast_event); +- +- if (type == AST_EVENT_MWI) { +- ast_event_queue_and_cache(ast_event, +- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_END); +- } else if (type == AST_EVENT_DEVICE_STATE_CHANGE) { +- ast_event_queue_and_cache(ast_event, +- AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, +- AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, sizeof(struct ast_eid), +- AST_EVENT_IE_END); +- } else { +- ast_event_queue(ast_event); +- } ++ ast_event_queue_and_cache(ast_event); + } + + void evt_event_deliver_cb(SaEvtSubscriptionIdT sub_id, +@@ -167,7 +141,7 @@ + return; + } + +- if (!ast_eid_cmp(&g_eid, ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) { ++ if (!ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) { + /* Don't feed events back in that originated locally. */ + return; + } +@@ -209,7 +183,7 @@ + + ast_log(LOG_DEBUG, "Got an event to forward\n"); + +- if (ast_eid_cmp(&g_eid, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) { ++ if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) { + /* If the event didn't originate from this server, don't send it back out. */ + ast_log(LOG_DEBUG, "Returning here\n"); + return; +@@ -341,9 +315,14 @@ + return; + } + +- if (!(publish_event = ast_calloc(1, sizeof(*publish_event)))) ++ if (type == AST_EVENT_DEVICE_STATE_CHANGE && ast_enable_distributed_devstate()) { + return; +- ++ } ++ ++ if (!(publish_event = ast_calloc(1, sizeof(*publish_event)))) { ++ return; ++ } ++ + publish_event->type = type; + ast_log(LOG_DEBUG, "Subscribing to event type %d\n", type); + publish_event->sub = ast_event_subscribe(type, ast_event_cb, event_channel, +@@ -399,9 +378,14 @@ + return; + } + +- if (!(subscribe_event = ast_calloc(1, sizeof(*subscribe_event)))) ++ if (type == AST_EVENT_DEVICE_STATE_CHANGE && ast_enable_distributed_devstate()) { + return; +- ++ } ++ ++ if (!(subscribe_event = ast_calloc(1, sizeof(*subscribe_event)))) { ++ return; ++ } ++ + subscribe_event->type = type; + subscribe_event->id = ast_atomic_fetchadd_int(&unique_id, +1); + +Index: res/res_agi.exports +=================================================================== +--- a/res/res_agi.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_agi.exports (.../trunk) (revision 186562) +@@ -0,0 +1,7 @@ ++{ ++ global: ++ ast_agi_register; ++ ast_agi_unregister; ++ local: ++ *; ++}; + +Property changes on: res/res_agi.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_monitor.c +=================================================================== +--- a/res/res_monitor.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_monitor.c (.../trunk) (revision 186562) +@@ -587,11 +587,7 @@ + + if (ast_strlen_zero(fname)) { + /* No filename base specified, default to channel name as per CLI */ +- if (!(fname = ast_strdup(c->name))) { +- astman_send_error(s, m, "Could not start monitoring channel"); +- ast_channel_unlock(c); +- return 0; +- } ++ fname = ast_strdupa(c->name); + /* Channels have the format technology/channel_name - have to replace that / */ + if ((d = strchr(fname, '/'))) + *d = '-'; +Index: res/res_odbc.exports +=================================================================== +--- a/res/res_odbc.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_odbc.exports (.../trunk) (revision 186562) +@@ -0,0 +1,20 @@ ++{ ++ global: ++ ast_odbc_ast_str_SQLGetData; ++ ast_odbc_backslash_is_escape; ++ ast_odbc_clear_cache; ++ ast_odbc_direct_execute; ++ ast_odbc_find_column; ++ ast_odbc_find_table; ++ ast_odbc_prepare_and_execute; ++ ast_odbc_release_obj; ++ ast_odbc_request_obj; ++ _ast_odbc_request_obj; ++ ast_odbc_request_obj2; ++ _ast_odbc_request_obj2; ++ ast_odbc_retrieve_transaction_obj; ++ ast_odbc_sanity_check; ++ ast_odbc_smart_execute; ++ local: ++ *; ++}; + +Property changes on: res/res_odbc.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_features.exports +=================================================================== +--- a/res/res_features.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_features.exports (.../trunk) (revision 186562) +@@ -0,0 +1,13 @@ ++{ ++ global: ++ ast_bridge_call; ++ ast_masq_park_call; ++ ast_park_call; ++ ast_parking_ext; ++ ast_pickup_call; ++ ast_pickup_ext; ++ ast_register_feature; ++ ast_unregister_feature; ++ local: ++ *; ++}; + +Property changes on: res/res_features.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_curl.c +=================================================================== +--- a/res/res_curl.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_curl.c (.../trunk) (revision 186562) +@@ -71,5 +71,3 @@ + } + + AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "cURL Resource Module"); +- +- +Index: res/res_ael_share.exports +=================================================================== +--- a/res/res_ael_share.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_ael_share.exports (.../trunk) (revision 186562) +@@ -0,0 +1,4 @@ ++{ ++ global: ++ *; ++}; + +Property changes on: res/res_ael_share.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_jabber.exports +=================================================================== +--- a/res/res_jabber.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_jabber.exports (.../trunk) (revision 186562) +@@ -0,0 +1,14 @@ ++{ ++ global: ++ ast_aji_create_chat; ++ ast_aji_disconnect; ++ ast_aji_get_client; ++ ast_aji_get_clients; ++ ast_aji_increment_mid; ++ ast_aji_invite_chat; ++ ast_aji_join_chat; ++ ast_aji_send; ++ ast_aji_send_chat; ++ local: ++ *; ++}; + +Property changes on: res/res_jabber.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_monitor.exports +=================================================================== +--- a/res/res_monitor.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_monitor.exports (.../trunk) (revision 186562) +@@ -0,0 +1,11 @@ ++{ ++ global: ++ ast_monitor_change_fname; ++ ast_monitor_pause; ++ ast_monitor_setjoinfiles; ++ ast_monitor_start; ++ ast_monitor_stop; ++ ast_monitor_unpause; ++ local: ++ *; ++}; + +Property changes on: res/res_monitor.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_rtp_asterisk.c +=================================================================== +--- a/res/res_rtp_asterisk.c (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_rtp_asterisk.c (.../trunk) (revision 186562) +@@ -0,0 +1,2579 @@ ++/* ++ * Asterisk -- An open source telephony toolkit. ++ * ++ * Copyright (C) 1999 - 2008, Digium, Inc. ++ * ++ * Mark Spencer <markster@digium.com> ++ * ++ * See http://www.asterisk.org for more information about ++ * the Asterisk project. Please do not directly contact ++ * any of the maintainers of this project for assistance; ++ * the project provides a web site, mailing lists and IRC ++ * channels for your use. ++ * ++ * 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. ++ */ ++ ++/*! ++ * \file ++ * ++ * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal. ++ * ++ * \author Mark Spencer <markster@digium.com> ++ * ++ * \note RTP is defined in RFC 3550. ++ */ ++ ++#include "asterisk.h" ++ ++ASTERISK_FILE_VERSION(__FILE__, "$Revision$") ++ ++#include <sys/time.h> ++#include <signal.h> ++#include <fcntl.h> ++#include <math.h> ++ ++#include "asterisk/stun.h" ++#include "asterisk/pbx.h" ++#include "asterisk/frame.h" ++#include "asterisk/channel.h" ++#include "asterisk/acl.h" ++#include "asterisk/config.h" ++#include "asterisk/lock.h" ++#include "asterisk/utils.h" ++#include "asterisk/netsock.h" ++#include "asterisk/cli.h" ++#include "asterisk/manager.h" ++#include "asterisk/unaligned.h" ++#include "asterisk/module.h" ++#include "asterisk/rtp_engine.h" ++ ++#define MAX_TIMESTAMP_SKEW 640 ++ ++#define RTP_SEQ_MOD (1<<16) /*!< A sequence number can't be more than 16 bits */ ++#define RTCP_DEFAULT_INTERVALMS 5000 /*!< Default milli-seconds between RTCP reports we send */ ++#define RTCP_MIN_INTERVALMS 500 /*!< Min milli-seconds between RTCP reports we send */ ++#define RTCP_MAX_INTERVALMS 60000 /*!< Max milli-seconds between RTCP reports we send */ ++ ++#define DEFAULT_RTP_START 5000 /*!< Default port number to start allocating RTP ports from */ ++#define DEFAULT_RTP_END 31000 /*!< Default maximum port number to end allocating RTP ports at */ ++ ++#define MINIMUM_RTP_PORT 1024 /*!< Minimum port number to accept */ ++#define MAXIMUM_RTP_PORT 65535 /*!< Maximum port number to accept */ ++ ++#define RTCP_PT_FUR 192 ++#define RTCP_PT_SR 200 ++#define RTCP_PT_RR 201 ++#define RTCP_PT_SDES 202 ++#define RTCP_PT_BYE 203 ++#define RTCP_PT_APP 204 ++ ++#define RTP_MTU 1200 ++ ++#define DEFAULT_DTMF_TIMEOUT 3000 /*!< samples */ ++ ++#define ZFONE_PROFILE_ID 0x505a ++ ++static int dtmftimeout = DEFAULT_DTMF_TIMEOUT; ++ ++static int rtpstart = DEFAULT_RTP_START; /*!< First port for RTP sessions (set in rtp.conf) */ ++static int rtpend = DEFAULT_RTP_END; /*!< Last port for RTP sessions (set in rtp.conf) */ ++static int rtpdebug; /*!< Are we debugging? */ ++static int rtcpdebug; /*!< Are we debugging RTCP? */ ++static int rtcpstats; /*!< Are we debugging RTCP? */ ++static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */ ++static struct sockaddr_in rtpdebugaddr; /*!< Debug packets to/from this host */ ++static struct sockaddr_in rtcpdebugaddr; /*!< Debug RTCP packets to/from this host */ ++#ifdef SO_NO_CHECK ++static int nochecksums; ++#endif ++static int strictrtp; ++ ++enum strict_rtp_state { ++ STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */ ++ STRICT_RTP_LEARN, /*! Accept next packet as source */ ++ STRICT_RTP_CLOSED, /*! Drop all RTP packets not coming from source that was learned */ ++}; ++ ++#define FLAG_3389_WARNING (1 << 0) ++#define FLAG_NAT_ACTIVE (3 << 1) ++#define FLAG_NAT_INACTIVE (0 << 1) ++#define FLAG_NAT_INACTIVE_NOWARN (1 << 1) ++#define FLAG_NEED_MARKER_BIT (1 << 3) ++#define FLAG_DTMF_COMPENSATE (1 << 4) ++ ++/*! \brief RTP session description */ ++struct ast_rtp { ++ int s; ++ struct ast_frame f; ++ unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; ++ unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */ ++ unsigned int themssrc; /*!< Their SSRC */ ++ unsigned int rxssrc; ++ unsigned int lastts; ++ unsigned int lastrxts; ++ unsigned int lastividtimestamp; ++ unsigned int lastovidtimestamp; ++ unsigned int lastitexttimestamp; ++ unsigned int lastotexttimestamp; ++ unsigned int lasteventseqn; ++ int lastrxseqno; /*!< Last received sequence number */ ++ unsigned short seedrxseqno; /*!< What sequence number did they start with?*/ ++ unsigned int seedrxts; /*!< What RTP timestamp did they start with? */ ++ unsigned int rxcount; /*!< How many packets have we received? */ ++ unsigned int rxoctetcount; /*!< How many octets have we received? should be rxcount *160*/ ++ unsigned int txcount; /*!< How many packets have we sent? */ ++ unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/ ++ unsigned int cycles; /*!< Shifted count of sequence number cycles */ ++ double rxjitter; /*!< Interarrival jitter at the moment */ ++ double rxtransit; /*!< Relative transit time for previous packet */ ++ int lasttxformat; ++ int lastrxformat; ++ ++ int rtptimeout; /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */ ++ int rtpholdtimeout; /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ ++ int rtpkeepalive; /*!< Send RTP comfort noice packets for keepalive */ ++ ++ /* DTMF Reception Variables */ ++ char resp; ++ unsigned int lastevent; ++ int dtmfcount; ++ unsigned int dtmfsamples; ++ /* DTMF Transmission Variables */ ++ unsigned int lastdigitts; ++ char sending_digit; /*!< boolean - are we sending digits */ ++ char send_digit; /*!< digit we are sending */ ++ int send_payload; ++ int send_duration; ++ unsigned int flags; ++ struct timeval rxcore; ++ struct timeval txcore; ++ double drxcore; /*!< The double representation of the first received packet */ ++ struct timeval lastrx; /*!< timeval when we last received a packet */ ++ struct timeval dtmfmute; ++ struct ast_smoother *smoother; ++ int *ioid; ++ unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */ ++ unsigned short rxseqno; ++ struct sched_context *sched; ++ struct io_context *io; ++ void *data; ++ struct ast_rtcp *rtcp; ++ struct ast_rtp *bridged; /*!< Who we are Packet bridged to */ ++ ++ enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */ ++ struct sockaddr_in strict_rtp_address; /*!< Remote address information for strict RTP purposes */ ++ ++ struct rtp_red *red; ++}; ++ ++/*! ++ * \brief Structure defining an RTCP session. ++ * ++ * The concept "RTCP session" is not defined in RFC 3550, but since ++ * this structure is analogous to ast_rtp, which tracks a RTP session, ++ * it is logical to think of this as a RTCP session. ++ * ++ * RTCP packet is defined on page 9 of RFC 3550. ++ * ++ */ ++struct ast_rtcp { ++ int rtcp_info; ++ int s; /*!< Socket */ ++ struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ ++ struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ ++ unsigned int soc; /*!< What they told us */ ++ unsigned int spc; /*!< What they told us */ ++ unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/ ++ struct timeval rxlsr; /*!< Time when we got their last SR */ ++ struct timeval txlsr; /*!< Time when we sent or last SR*/ ++ unsigned int expected_prior; /*!< no. packets in previous interval */ ++ unsigned int received_prior; /*!< no. packets received in previous interval */ ++ int schedid; /*!< Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/ ++ unsigned int rr_count; /*!< number of RRs we've sent, not including report blocks in SR's */ ++ unsigned int sr_count; /*!< number of SRs we've sent */ ++ unsigned int lastsrtxcount; /*!< Transmit packet count when last SR sent */ ++ double accumulated_transit; /*!< accumulated a-dlsr-lsr */ ++ double rtt; /*!< Last reported rtt */ ++ unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */ ++ unsigned int reported_lost; /*!< Reported lost packets in their RR */ ++ ++ double reported_maxjitter; ++ double reported_minjitter; ++ double reported_normdev_jitter; ++ double reported_stdev_jitter; ++ unsigned int reported_jitter_count; ++ ++ double reported_maxlost; ++ double reported_minlost; ++ double reported_normdev_lost; ++ double reported_stdev_lost; ++ ++ double rxlost; ++ double maxrxlost; ++ double minrxlost; ++ double normdev_rxlost; ++ double stdev_rxlost; ++ unsigned int rxlost_count; ++ ++ double maxrxjitter; ++ double minrxjitter; ++ double normdev_rxjitter; ++ double stdev_rxjitter; ++ unsigned int rxjitter_count; ++ double maxrtt; ++ double minrtt; ++ double normdevrtt; ++ double stdevrtt; ++ unsigned int rtt_count; ++}; ++ ++struct rtp_red { ++ struct ast_frame t140; /*!< Primary data */ ++ struct ast_frame t140red; /*!< Redundant t140*/ ++ unsigned char pt[AST_RED_MAX_GENERATION]; /*!< Payload types for redundancy data */ ++ unsigned char ts[AST_RED_MAX_GENERATION]; /*!< Time stamps */ ++ unsigned char len[AST_RED_MAX_GENERATION]; /*!< length of each generation */ ++ int num_gen; /*!< Number of generations */ ++ int schedid; /*!< Timer id */ ++ int ti; /*!< How long to buffer data before send */ ++ unsigned char t140red_data[64000]; ++ unsigned char buf_data[64000]; /*!< buffered primary data */ ++ int hdrlen; ++ long int prev_ts; ++}; ++ ++/* Forward Declarations */ ++static int ast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data); ++static int ast_rtp_destroy(struct ast_rtp_instance *instance); ++static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit); ++static int ast_rtp_dtmf_end(struct ast_rtp_instance *instance, char digit); ++static void ast_rtp_new_source(struct ast_rtp_instance *instance); ++static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame); ++static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp); ++static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value); ++static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp); ++static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct sockaddr_in *sin); ++static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations); ++static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame); ++static int ast_rtp_local_bridge(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1); ++static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat); ++static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1); ++static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username); ++static void ast_rtp_stop(struct ast_rtp_instance *instance); ++ ++/* RTP Engine Declaration */ ++static struct ast_rtp_engine asterisk_rtp_engine = { ++ .name = "asterisk", ++ .new = ast_rtp_new, ++ .destroy = ast_rtp_destroy, ++ .dtmf_begin = ast_rtp_dtmf_begin, ++ .dtmf_end = ast_rtp_dtmf_end, ++ .new_source = ast_rtp_new_source, ++ .write = ast_rtp_write, ++ .read = ast_rtp_read, ++ .prop_set = ast_rtp_prop_set, ++ .fd = ast_rtp_fd, ++ .remote_address_set = ast_rtp_remote_address_set, ++ .red_init = rtp_red_init, ++ .red_buffer = rtp_red_buffer, ++ .local_bridge = ast_rtp_local_bridge, ++ .get_stat = ast_rtp_get_stat, ++ .dtmf_compatible = ast_rtp_dtmf_compatible, ++ .stun_request = ast_rtp_stun_request, ++ .stop = ast_rtp_stop, ++}; ++ ++static inline int rtp_debug_test_addr(struct sockaddr_in *addr) ++{ ++ if (!rtpdebug) { ++ return 0; ++ } ++ ++ if (rtpdebugaddr.sin_addr.s_addr) { ++ if (((ntohs(rtpdebugaddr.sin_port) != 0) ++ && (rtpdebugaddr.sin_port != addr->sin_port)) ++ || (rtpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static inline int rtcp_debug_test_addr(struct sockaddr_in *addr) ++{ ++ if (!rtcpdebug) { ++ return 0; ++ } ++ ++ if (rtcpdebugaddr.sin_addr.s_addr) { ++ if (((ntohs(rtcpdebugaddr.sin_port) != 0) ++ && (rtcpdebugaddr.sin_port != addr->sin_port)) ++ || (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp) ++{ ++ unsigned int interval; ++ /*! \todo XXX Do a more reasonable calculation on this one ++ * Look in RFC 3550 Section A.7 for an example*/ ++ interval = rtcpinterval; ++ return interval; ++} ++ ++/*! \brief Calculate normal deviation */ ++static double normdev_compute(double normdev, double sample, unsigned int sample_count) ++{ ++ normdev = normdev * sample_count + sample; ++ sample_count++; ++ ++ return normdev / sample_count; ++} ++ ++static double stddev_compute(double stddev, double sample, double normdev, double normdev_curent, unsigned int sample_count) ++{ ++/* ++ for the formula check http://www.cs.umd.edu/~austinjp/constSD.pdf ++ return sqrt( (sample_count*pow(stddev,2) + sample_count*pow((sample-normdev)/(sample_count+1),2) + pow(sample-normdev_curent,2)) / (sample_count+1)); ++ we can compute the sigma^2 and that way we would have to do the sqrt only 1 time at the end and would save another pow 2 compute ++ optimized formula ++*/ ++#define SQUARE(x) ((x) * (x)) ++ ++ stddev = sample_count * stddev; ++ sample_count++; ++ ++ return stddev + ++ ( sample_count * SQUARE( (sample - normdev) / sample_count ) ) + ++ ( SQUARE(sample - normdev_curent) / sample_count ); ++ ++#undef SQUARE ++} ++ ++static int create_new_socket(const char *type) ++{ ++ int sock = socket(AF_INET, SOCK_DGRAM, 0); ++ ++ if (sock < 0) { ++ if (!type) { ++ type = "RTP/RTCP"; ++ } ++ ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno)); ++ } else { ++ long flags = fcntl(sock, F_GETFL); ++ fcntl(sock, F_SETFL, flags | O_NONBLOCK); ++#ifdef SO_NO_CHECK ++ if (nochecksums) { ++ setsockopt(sock, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums)); ++ } ++#endif ++ } ++ ++ return sock; ++} ++ ++static int ast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data) ++{ ++ struct ast_rtp *rtp = NULL; ++ int x, startplace; ++ ++ /* Create a new RTP structure to hold all of our data */ ++ if (!(rtp = ast_calloc(1, sizeof(*rtp)))) { ++ return -1; ++ } ++ ++ /* Set default parameters on the newly created RTP structure */ ++ rtp->ssrc = ast_random(); ++ rtp->seqno = ast_random() & 0xffff; ++ rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN); ++ ++ /* Create a new socket for us to listen on and use */ ++ if ((rtp->s = create_new_socket("RTP")) < 0) { ++ ast_debug(1, "Failed to create a new socket for RTP instance '%p'\n", instance); ++ ast_free(rtp); ++ return -1; ++ } ++ ++ /* Now actually find a free RTP port to use */ ++ x = (rtpend == rtpstart) ? rtpstart : (ast_random() % (rtpend - rtpstart)) + rtpstart; ++ x = x & ~1; ++ startplace = x; ++ ++ for (;;) { ++ struct sockaddr_in local_address = { 0, }; ++ ++ local_address.sin_port = htons(x); ++ /* Try to bind, this will tell us whether the port is available or not */ ++ if (!bind(rtp->s, (struct sockaddr*)&local_address, sizeof(local_address))) { ++ ast_debug(1, "Allocated port %d for RTP instance '%p'\n", x, instance); ++ ast_rtp_instance_set_local_address(instance, &local_address); ++ break; ++ } ++ ++ x += 2; ++ if (x > rtpend) { ++ x = (rtpstart + 1) & ~1; ++ } ++ ++ /* See if we ran out of ports or if the bind actually failed because of something other than the address being in use */ ++ if (x == startplace || errno != EADDRINUSE) { ++ ast_log(LOG_ERROR, "Oh dear... we couldn't allocate a port for RTP instance '%p'\n", instance); ++ return -1; ++ } ++ } ++ ++ /* Record any information we may need */ ++ rtp->sched = sched; ++ ++ /* Associate the RTP structure with the RTP instance and be done */ ++ ast_rtp_instance_set_data(instance, rtp); ++ ++ return 0; ++} ++ ++static int ast_rtp_destroy(struct ast_rtp_instance *instance) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ /* Destroy the smoother that was smoothing out audio if present */ ++ if (rtp->smoother) { ++ ast_smoother_free(rtp->smoother); ++ } ++ ++ /* Close our own socket so we no longer get packets */ ++ if (rtp->s > -1) { ++ close(rtp->s); ++ } ++ ++ /* Destroy RTCP if it was being used */ ++ if (rtp->rtcp) { ++ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); ++ close(rtp->rtcp->s); ++ ast_free(rtp->rtcp); ++ } ++ ++ /* Destroy RED if it was being used */ ++ if (rtp->red) { ++ AST_SCHED_DEL(rtp->sched, rtp->red->schedid); ++ ast_free(rtp->red); ++ } ++ ++ /* Finally destroy ourselves */ ++ ast_free(rtp); ++ ++ return 0; ++} ++ ++static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in remote_address; ++ int hdrlen = 12, res = 0, i = 0, payload = 101; ++ char data[256]; ++ unsigned int *rtpheader = (unsigned int*)data; ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ /* If we have no remote address information bail out now */ ++ if (!remote_address.sin_addr.s_addr || !remote_address.sin_port) { ++ return -1; ++ } ++ ++ /* Convert given digit into what we want to transmit */ ++ if ((digit <= '9') && (digit >= '0')) { ++ digit -= '0'; ++ } else if (digit == '*') { ++ digit = 10; ++ } else if (digit == '#') { ++ digit = 11; ++ } else if ((digit >= 'A') && (digit <= 'D')) { ++ digit = digit - 'A' + 12; ++ } else if ((digit >= 'a') && (digit <= 'd')) { ++ digit = digit - 'a' + 12; ++ } else { ++ ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); ++ return -1; ++ } ++ ++ /* Grab the payload that they expect the RFC2833 packet to be received in */ ++ payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 0, AST_RTP_DTMF); ++ ++ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); ++ rtp->send_duration = 160; ++ rtp->lastdigitts = rtp->lastts + rtp->send_duration; ++ ++ /* Create the actual packet that we will be sending */ ++ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno)); ++ rtpheader[1] = htonl(rtp->lastdigitts); ++ rtpheader[2] = htonl(rtp->ssrc); ++ ++ /* Actually send the packet */ ++ for (i = 0; i < 2; i++) { ++ rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); ++ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address)); ++ if (res < 0) { ++ ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n", ++ ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno)); ++ } ++ if (rtp_debug_test_addr(&remote_address)) { ++ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", ++ ast_inet_ntoa(remote_address.sin_addr), ++ ntohs(remote_address.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); ++ } ++ rtp->seqno++; ++ rtp->send_duration += 160; ++ rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno)); ++ } ++ ++ /* Record that we are in the process of sending a digit and information needed to continue doing so */ ++ rtp->sending_digit = 1; ++ rtp->send_digit = digit; ++ rtp->send_payload = payload; ++ ++ return 0; ++} ++ ++static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in remote_address; ++ int hdrlen = 12, res = 0; ++ char data[256]; ++ unsigned int *rtpheader = (unsigned int*)data; ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ /* Make sure we know where the other side is so we can send them the packet */ ++ if (!remote_address.sin_addr.s_addr || !remote_address.sin_port) { ++ return -1; ++ } ++ ++ /* Actually create the packet we will be sending */ ++ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); ++ rtpheader[1] = htonl(rtp->lastdigitts); ++ rtpheader[2] = htonl(rtp->ssrc); ++ rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (rtp->send_duration)); ++ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); ++ ++ /* Boom, send it on out */ ++ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address)); ++ if (res < 0) { ++ ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", ++ ast_inet_ntoa(remote_address.sin_addr), ++ ntohs(remote_address.sin_port), strerror(errno)); ++ } ++ ++ if (rtp_debug_test_addr(&remote_address)) { ++ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", ++ ast_inet_ntoa(remote_address.sin_addr), ++ ntohs(remote_address.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); ++ } ++ ++ /* And now we increment some values for the next time we swing by */ ++ rtp->seqno++; ++ rtp->send_duration += 160; ++ ++ return 0; ++} ++ ++static int ast_rtp_dtmf_end(struct ast_rtp_instance *instance, char digit) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in remote_address; ++ int hdrlen = 12, res = 0, i = 0; ++ char data[256]; ++ unsigned int *rtpheader = (unsigned int*)data; ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ /* Make sure we know where the remote side is so we can send them the packet we construct */ ++ if (!remote_address.sin_addr.s_addr || !remote_address.sin_port) { ++ return -1; ++ } ++ ++ /* Convert the given digit to the one we are going to send */ ++ if ((digit <= '9') && (digit >= '0')) { ++ digit -= '0'; ++ } else if (digit == '*') { ++ digit = 10; ++ } else if (digit == '#') { ++ digit = 11; ++ } else if ((digit >= 'A') && (digit <= 'D')) { ++ digit = digit - 'A' + 12; ++ } else if ((digit >= 'a') && (digit <= 'd')) { ++ digit = digit - 'a' + 12; ++ } else { ++ ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); ++ return -1; ++ } ++ ++ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); ++ ++ /* Construct the packet we are going to send */ ++ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); ++ rtpheader[1] = htonl(rtp->lastdigitts); ++ rtpheader[2] = htonl(rtp->ssrc); ++ rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); ++ rtpheader[3] |= htonl((1 << 23)); ++ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); ++ ++ /* Send it 3 times, that's the magical number */ ++ for (i = 0; i < 3; i++) { ++ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address)); ++ if (res < 0) { ++ ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", ++ ast_inet_ntoa(remote_address.sin_addr), ++ ntohs(remote_address.sin_port), strerror(errno)); ++ } ++ if (rtp_debug_test_addr(&remote_address)) { ++ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", ++ ast_inet_ntoa(remote_address.sin_addr), ++ ntohs(remote_address.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); ++ } ++ } ++ ++ /* Oh and we can't forget to turn off the stuff that says we are sending DTMF */ ++ rtp->lastts += rtp->send_duration; ++ rtp->sending_digit = 0; ++ rtp->send_digit = 0; ++ ++ return 0; ++} ++ ++static void ast_rtp_new_source(struct ast_rtp_instance *instance) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ /* We simply set this bit so that the next packet sent will have the marker bit turned on */ ++ ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); ++ ++ return; ++} ++ ++static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery) ++{ ++ struct timeval t; ++ long ms; ++ ++ if (ast_tvzero(rtp->txcore)) { ++ rtp->txcore = ast_tvnow(); ++ rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000; ++ } ++ ++ t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow(); ++ if ((ms = ast_tvdiff_ms(t, rtp->txcore)) < 0) { ++ ms = 0; ++ } ++ rtp->txcore = t; ++ ++ return (unsigned int) ms; ++} ++ ++static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw) ++{ ++ unsigned int sec, usec, frac; ++ sec = tv.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */ ++ usec = tv.tv_usec; ++ frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6); ++ *msw = sec; ++ *lsw = frac; ++} ++ ++/*! \brief Send RTCP recipient's report */ ++static int ast_rtcp_write_rr(const void *data) ++{ ++ struct ast_rtp *rtp = (struct ast_rtp *)data; ++ int res; ++ int len = 32; ++ unsigned int lost; ++ unsigned int extended; ++ unsigned int expected; ++ unsigned int expected_interval; ++ unsigned int received_interval; ++ int lost_interval; ++ struct timeval now; ++ unsigned int *rtcpheader; ++ char bdata[1024]; ++ struct timeval dlsr; ++ int fraction; ++ ++ double rxlost_current; ++ ++ if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0)) ++ return 0; ++ ++ if (!rtp->rtcp->them.sin_addr.s_addr) { ++ ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted\n"); ++ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); ++ return 0; ++ } ++ ++ extended = rtp->cycles + rtp->lastrxseqno; ++ expected = extended - rtp->seedrxseqno + 1; ++ lost = expected - rtp->rxcount; ++ expected_interval = expected - rtp->rtcp->expected_prior; ++ rtp->rtcp->expected_prior = expected; ++ received_interval = rtp->rxcount - rtp->rtcp->received_prior; ++ rtp->rtcp->received_prior = rtp->rxcount; ++ lost_interval = expected_interval - received_interval; ++ ++ if (lost_interval <= 0) ++ rtp->rtcp->rxlost = 0; ++ else rtp->rtcp->rxlost = rtp->rtcp->rxlost; ++ if (rtp->rtcp->rxlost_count == 0) ++ rtp->rtcp->minrxlost = rtp->rtcp->rxlost; ++ if (lost_interval < rtp->rtcp->minrxlost) ++ rtp->rtcp->minrxlost = rtp->rtcp->rxlost; ++ if (lost_interval > rtp->rtcp->maxrxlost) ++ rtp->rtcp->maxrxlost = rtp->rtcp->rxlost; ++ ++ rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->rxlost_count); ++ rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->normdev_rxlost, rxlost_current, rtp->rtcp->rxlost_count); ++ rtp->rtcp->normdev_rxlost = rxlost_current; ++ rtp->rtcp->rxlost_count++; ++ ++ if (expected_interval == 0 || lost_interval <= 0) ++ fraction = 0; ++ else ++ fraction = (lost_interval << 8) / expected_interval; ++ gettimeofday(&now, NULL); ++ timersub(&now, &rtp->rtcp->rxlsr, &dlsr); ++ rtcpheader = (unsigned int *)bdata; ++ rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1)); ++ rtcpheader[1] = htonl(rtp->ssrc); ++ rtcpheader[2] = htonl(rtp->themssrc); ++ rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); ++ rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); ++ rtcpheader[5] = htonl((unsigned int)(rtp->rxjitter * 65536.)); ++ rtcpheader[6] = htonl(rtp->rtcp->themrxlsr); ++ rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); ++ ++ /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos ++ it can change mid call, and SDES can't) */ ++ rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); ++ rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ ++ rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ ++ len += 12; ++ ++ res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); ++ ++ if (res < 0) { ++ ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno)); ++ /* Remove the scheduler */ ++ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); ++ return 0; ++ } ++ ++ rtp->rtcp->rr_count++; ++ if (rtcp_debug_test_addr(&rtp->rtcp->them)) { ++ ast_verbose("\n* Sending RTCP RR to %s:%d\n" ++ " Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n" ++ " IA jitter: %.4f\n" ++ " Their last SR: %u\n" ++ " DLSR: %4.4f (sec)\n\n", ++ ast_inet_ntoa(rtp->rtcp->them.sin_addr), ++ ntohs(rtp->rtcp->them.sin_port), ++ rtp->ssrc, rtp->themssrc, fraction, lost, ++ rtp->rxjitter, ++ rtp->rtcp->themrxlsr, ++ (double)(ntohl(rtcpheader[7])/65536.0)); ++ } ++ ++ return res; ++} ++ ++/*! \brief Send RTCP sender's report */ ++static int ast_rtcp_write_sr(const void *data) ++{ ++ struct ast_rtp *rtp = (struct ast_rtp *)data; ++ int res; ++ int len = 0; ++ struct timeval now; ++ unsigned int now_lsw; ++ unsigned int now_msw; ++ unsigned int *rtcpheader; ++ unsigned int lost; ++ unsigned int extended; ++ unsigned int expected; ++ unsigned int expected_interval; ++ unsigned int received_interval; ++ int lost_interval; ++ int fraction; ++ struct timeval dlsr; ++ char bdata[512]; ++ ++ /* Commented condition is always not NULL if rtp->rtcp is not NULL */ ++ if (!rtp || !rtp->rtcp/* || (&rtp->rtcp->them.sin_addr == 0)*/) ++ return 0; ++ ++ if (!rtp->rtcp->them.sin_addr.s_addr) { /* This'll stop rtcp for this rtp session */ ++ ast_verbose("RTCP SR transmission error, rtcp halted\n"); ++ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); ++ return 0; ++ } ++ ++ gettimeofday(&now, NULL); ++ timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/ ++ rtcpheader = (unsigned int *)bdata; ++ rtcpheader[1] = htonl(rtp->ssrc); /* Our SSRC */ ++ rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/ ++ rtcpheader[3] = htonl(now_lsw); /* now, LSW */ ++ rtcpheader[4] = htonl(rtp->lastts); /* FIXME shouldn't be that, it should be now */ ++ rtcpheader[5] = htonl(rtp->txcount); /* No. packets sent */ ++ rtcpheader[6] = htonl(rtp->txoctetcount); /* No. bytes sent */ ++ len += 28; ++ ++ extended = rtp->cycles + rtp->lastrxseqno; ++ expected = extended - rtp->seedrxseqno + 1; ++ if (rtp->rxcount > expected) ++ expected += rtp->rxcount - expected; ++ lost = expected - rtp->rxcount; ++ expected_interval = expected - rtp->rtcp->expected_prior; ++ rtp->rtcp->expected_prior = expected; ++ received_interval = rtp->rxcount - rtp->rtcp->received_prior; ++ rtp->rtcp->received_prior = rtp->rxcount; ++ lost_interval = expected_interval - received_interval; ++ if (expected_interval == 0 || lost_interval <= 0) ++ fraction = 0; ++ else ++ fraction = (lost_interval << 8) / expected_interval; ++ timersub(&now, &rtp->rtcp->rxlsr, &dlsr); ++ rtcpheader[7] = htonl(rtp->themssrc); ++ rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); ++ rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); ++ rtcpheader[10] = htonl((unsigned int)(rtp->rxjitter * 65536.)); ++ rtcpheader[11] = htonl(rtp->rtcp->themrxlsr); ++ rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); ++ len += 24; ++ ++ rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1)); ++ ++ /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ ++ /* it can change mid call, and SDES can't) */ ++ rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); ++ rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ ++ rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ ++ len += 12; ++ ++ res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); ++ if (res < 0) { ++ ast_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno)); ++ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); ++ return 0; ++ } ++ ++ /* FIXME Don't need to get a new one */ ++ gettimeofday(&rtp->rtcp->txlsr, NULL); ++ rtp->rtcp->sr_count++; ++ ++ rtp->rtcp->lastsrtxcount = rtp->txcount; ++ ++ if (rtcp_debug_test_addr(&rtp->rtcp->them)) { ++ ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); ++ ast_verbose(" Our SSRC: %u\n", rtp->ssrc); ++ ast_verbose(" Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096); ++ ast_verbose(" Sent(RTP): %u\n", rtp->lastts); ++ ast_verbose(" Sent packets: %u\n", rtp->txcount); ++ ast_verbose(" Sent octets: %u\n", rtp->txoctetcount); ++ ast_verbose(" Report block:\n"); ++ ast_verbose(" Fraction lost: %u\n", fraction); ++ ast_verbose(" Cumulative loss: %u\n", lost); ++ ast_verbose(" IA jitter: %.4f\n", rtp->rxjitter); ++ ast_verbose(" Their last SR: %u\n", rtp->rtcp->themrxlsr); ++ ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0)); ++ } ++ manager_event(EVENT_FLAG_REPORTING, "RTCPSent", "To %s:%d\r\n" ++ "OurSSRC: %u\r\n" ++ "SentNTP: %u.%010u\r\n" ++ "SentRTP: %u\r\n" ++ "SentPackets: %u\r\n" ++ "SentOctets: %u\r\n" ++ "ReportBlock:\r\n" ++ "FractionLost: %u\r\n" ++ "CumulativeLoss: %u\r\n" ++ "IAJitter: %.4f\r\n" ++ "TheirLastSR: %u\r\n" ++ "DLSR: %4.4f (sec)\r\n", ++ ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), ++ rtp->ssrc, ++ (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096, ++ rtp->lastts, ++ rtp->txcount, ++ rtp->txoctetcount, ++ fraction, ++ lost, ++ rtp->rxjitter, ++ rtp->rtcp->themrxlsr, ++ (double)(ntohl(rtcpheader[12])/65536.0)); ++ return res; ++} ++ ++/*! \brief Write and RTCP packet to the far end ++ * \note Decide if we are going to send an SR (with Reception Block) or RR ++ * RR is sent if we have not sent any rtp packets in the previous interval */ ++static int ast_rtcp_write(const void *data) ++{ ++ struct ast_rtp *rtp = (struct ast_rtp *)data; ++ int res; ++ ++ if (!rtp || !rtp->rtcp) ++ return 0; ++ ++ if (rtp->txcount > rtp->rtcp->lastsrtxcount) ++ res = ast_rtcp_write_sr(data); ++ else ++ res = ast_rtcp_write_rr(data); ++ ++ return res; ++} ++ ++static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ int pred, mark = 0; ++ unsigned int ms = calc_txstamp(rtp, &frame->delivery); ++ struct sockaddr_in remote_address; ++ ++ if (rtp->sending_digit) { ++ return 0; ++ } ++ ++ if (frame->frametype == AST_FRAME_VOICE) { ++ pred = rtp->lastts + frame->samples; ++ ++ /* Re-calculate last TS */ ++ rtp->lastts = rtp->lastts + ms * 8; ++ if (ast_tvzero(frame->delivery)) { ++ /* If this isn't an absolute delivery time, Check if it is close to our prediction, ++ and if so, go with our prediction */ ++ if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW) { ++ rtp->lastts = pred; ++ } else { ++ ast_debug(3, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms); ++ mark = 1; ++ } ++ } ++ } else if (frame->frametype == AST_FRAME_VIDEO) { ++ mark = frame->subclass & 0x1; ++ pred = rtp->lastovidtimestamp + frame->samples; ++ /* Re-calculate last TS */ ++ rtp->lastts = rtp->lastts + ms * 90; ++ /* If it's close to our prediction, go for it */ ++ if (ast_tvzero(frame->delivery)) { ++ if (abs(rtp->lastts - pred) < 7200) { ++ rtp->lastts = pred; ++ rtp->lastovidtimestamp += frame->samples; ++ } else { ++ ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, frame->samples); ++ rtp->lastovidtimestamp = rtp->lastts; ++ } ++ } ++ } else { ++ pred = rtp->lastotexttimestamp + frame->samples; ++ /* Re-calculate last TS */ ++ rtp->lastts = rtp->lastts + ms * 90; ++ /* If it's close to our prediction, go for it */ ++ if (ast_tvzero(frame->delivery)) { ++ if (abs(rtp->lastts - pred) < 7200) { ++ rtp->lastts = pred; ++ rtp->lastotexttimestamp += frame->samples; ++ } else { ++ ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, frame->samples); ++ rtp->lastotexttimestamp = rtp->lastts; ++ } ++ } ++ } ++ ++ /* If we have been explicitly told to set the marker bit then do so */ ++ if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) { ++ mark = 1; ++ ast_clear_flag(rtp, FLAG_NEED_MARKER_BIT); ++ } ++ ++ /* If the timestamp for non-digt packets has moved beyond the timestamp for digits, update the digit timestamp */ ++ if (rtp->lastts > rtp->lastdigitts) { ++ rtp->lastdigitts = rtp->lastts; ++ } ++ ++ if (ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO)) { ++ rtp->lastts = frame->ts * 8; ++ } ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ /* If we know the remote address construct a packet and send it out */ ++ if (remote_address.sin_port && remote_address.sin_addr.s_addr) { ++ int hdrlen = 12, res; ++ unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen); ++ ++ put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23))); ++ put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts)); ++ put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); ++ ++ if ((res = sendto(rtp->s, (void *)rtpheader, frame->datalen + hdrlen, 0, (struct sockaddr *)&remote_address, sizeof(remote_address))) < 0) { ++ if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { ++ ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno)); ++ } else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) { ++ /* Only give this error message once if we are not RTP debugging */ ++ if (option_debug || rtpdebug) ++ ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port)); ++ ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN); ++ } ++ } else { ++ rtp->txcount++; ++ rtp->txoctetcount += (res - hdrlen); ++ ++ if (rtp->rtcp && rtp->rtcp->schedid < 1) { ++ ast_debug(1, "Starting RTCP transmission on RTP instance '%p'\n", instance); ++ rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); ++ } ++ } ++ ++ if (rtp_debug_test_addr(&remote_address)) { ++ ast_verbose("Sent RTP packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", ++ ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), codec, rtp->seqno, rtp->lastts, res - hdrlen); ++ } ++ } ++ ++ rtp->seqno++; ++ ++ return 0; ++} ++ ++static struct ast_frame *red_t140_to_red(struct rtp_red *red) { ++ unsigned char *data = red->t140red.data.ptr; ++ int len = 0; ++ int i; ++ ++ /* replace most aged generation */ ++ if (red->len[0]) { ++ for (i = 1; i < red->num_gen+1; i++) ++ len += red->len[i]; ++ ++ memmove(&data[red->hdrlen], &data[red->hdrlen+red->len[0]], len); ++ } ++ ++ /* Store length of each generation and primary data length*/ ++ for (i = 0; i < red->num_gen; i++) ++ red->len[i] = red->len[i+1]; ++ red->len[i] = red->t140.datalen; ++ ++ /* write each generation length in red header */ ++ len = red->hdrlen; ++ for (i = 0; i < red->num_gen; i++) ++ len += data[i*4+3] = red->len[i]; ++ ++ /* add primary data to buffer */ ++ memcpy(&data[len], red->t140.data.ptr, red->t140.datalen); ++ red->t140red.datalen = len + red->t140.datalen; ++ ++ /* no primary data and no generations to send */ ++ if (len == red->hdrlen && !red->t140.datalen) ++ return NULL; ++ ++ /* reset t.140 buffer */ ++ red->t140.datalen = 0; ++ ++ return &red->t140red; ++} ++ ++static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in remote_address; ++ int codec, subclass; ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ /* If we don't actually know the remote address don't even bother doing anything */ ++ if (!remote_address.sin_addr.s_addr) { ++ ast_debug(1, "No remote address on RTP instance '%p' so dropping frame\n", instance); ++ return -1; ++ } ++ ++ /* If there is no data length we can't very well send the packet */ ++ if (!frame->datalen) { ++ ast_debug(1, "Received frame with no data for RTP instance '%p' so dropping frame\n", instance); ++ return -1; ++ } ++ ++ /* If the packet is not one our RTP stack supports bail out */ ++ if (frame->frametype != AST_FRAME_VOICE && frame->frametype != AST_FRAME_VIDEO && frame->frametype != AST_FRAME_TEXT) { ++ ast_log(LOG_WARNING, "RTP can only send voice, video, and text\n"); ++ return -1; ++ } ++ ++ if (rtp->red) { ++ /* return 0; */ ++ /* no primary data or generations to send */ ++ if ((frame = red_t140_to_red(rtp->red)) == NULL) ++ return 0; ++ } ++ ++ /* Grab the subclass and look up the payload we are going to use */ ++ subclass = frame->subclass; ++ if (frame->frametype == AST_FRAME_VIDEO) { ++ subclass &= ~0x1; ++ } ++ if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, subclass)) < 0) { ++ ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(frame->subclass)); ++ return -1; ++ } ++ ++ /* Oh dear, if the format changed we will have to set up a new smoother */ ++ if (rtp->lasttxformat != subclass) { ++ ast_debug(1, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass)); ++ rtp->lasttxformat = subclass; ++ if (rtp->smoother) { ++ ast_smoother_free(rtp->smoother); ++ rtp->smoother = NULL; ++ } ++ } ++ ++ /* If no smoother is present see if we have to set one up */ ++ if (!rtp->smoother) { ++ struct ast_format_list fmt = ast_codec_pref_getsize(&ast_rtp_instance_get_codecs(instance)->pref, subclass); ++ ++ switch (subclass) { ++ case AST_FORMAT_SPEEX: ++ case AST_FORMAT_G723_1: ++ case AST_FORMAT_SIREN7: ++ case AST_FORMAT_SIREN14: ++ /* these are all frame-based codecs and cannot be safely run through ++ a smoother */ ++ break; ++ default: ++ if (fmt.inc_ms) { ++ if (!(rtp->smoother = ast_smoother_new((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms))) { ++ ast_log(LOG_WARNING, "Unable to create smoother: format %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); ++ return -1; ++ } ++ if (fmt.flags) { ++ ast_smoother_set_flags(rtp->smoother, fmt.flags); ++ } ++ ast_debug(1, "Created smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); ++ } ++ } ++ } ++ ++ /* Feed audio frames into the actual function that will create a frame and send it */ ++ if (rtp->smoother) { ++ struct ast_frame *f; ++ ++ if (ast_smoother_test_flag(rtp->smoother, AST_SMOOTHER_FLAG_BE)) { ++ ast_smoother_feed_be(rtp->smoother, frame); ++ } else { ++ ast_smoother_feed(rtp->smoother, frame); ++ } ++ ++ while ((f = ast_smoother_read(rtp->smoother)) && (f->data.ptr)) { ++ if (f->subclass == AST_FORMAT_G722) { ++ f->samples /= 2; ++ } ++ ++ ast_rtp_raw_write(instance, f, codec); ++ } ++ } else { ++ int hdrlen = 12; ++ struct ast_frame *f = NULL; ++ ++ if (frame->offset < hdrlen) { ++ f = ast_frdup(frame); ++ } else { ++ f = frame; ++ } ++ if (f->data.ptr) { ++ ast_rtp_raw_write(instance, f, codec); ++ } ++ if (f != frame) { ++ ast_frfree(f); ++ } ++ ++ } ++ ++ return 0; ++} ++ ++static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark) ++{ ++ struct timeval now; ++ double transit; ++ double current_time; ++ double d; ++ double dtv; ++ double prog; ++ ++ double normdev_rxjitter_current; ++ if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) { ++ gettimeofday(&rtp->rxcore, NULL); ++ rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000; ++ /* map timestamp to a real time */ ++ rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */ ++ rtp->rxcore.tv_sec -= timestamp / 8000; ++ rtp->rxcore.tv_usec -= (timestamp % 8000) * 125; ++ /* Round to 0.1ms for nice, pretty timestamps */ ++ rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100; ++ if (rtp->rxcore.tv_usec < 0) { ++ /* Adjust appropriately if necessary */ ++ rtp->rxcore.tv_usec += 1000000; ++ rtp->rxcore.tv_sec -= 1; ++ } ++ } ++ ++ gettimeofday(&now,NULL); ++ /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */ ++ tv->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000; ++ tv->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125; ++ if (tv->tv_usec >= 1000000) { ++ tv->tv_usec -= 1000000; ++ tv->tv_sec += 1; ++ } ++ prog = (double)((timestamp-rtp->seedrxts)/8000.); ++ dtv = (double)rtp->drxcore + (double)(prog); ++ current_time = (double)now.tv_sec + (double)now.tv_usec/1000000; ++ transit = current_time - dtv; ++ d = transit - rtp->rxtransit; ++ rtp->rxtransit = transit; ++ if (d<0) ++ d=-d; ++ rtp->rxjitter += (1./16.) * (d - rtp->rxjitter); ++ ++ if (rtp->rtcp) { ++ if (rtp->rxjitter > rtp->rtcp->maxrxjitter) ++ rtp->rtcp->maxrxjitter = rtp->rxjitter; ++ if (rtp->rtcp->rxjitter_count == 1) ++ rtp->rtcp->minrxjitter = rtp->rxjitter; ++ if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter) ++ rtp->rtcp->minrxjitter = rtp->rxjitter; ++ ++ normdev_rxjitter_current = normdev_compute(rtp->rtcp->normdev_rxjitter,rtp->rxjitter,rtp->rtcp->rxjitter_count); ++ rtp->rtcp->stdev_rxjitter = stddev_compute(rtp->rtcp->stdev_rxjitter,rtp->rxjitter,rtp->rtcp->normdev_rxjitter,normdev_rxjitter_current,rtp->rtcp->rxjitter_count); ++ ++ rtp->rtcp->normdev_rxjitter = normdev_rxjitter_current; ++ rtp->rtcp->rxjitter_count++; ++ } ++} ++ ++static struct ast_frame *send_dtmf(struct ast_rtp_instance *instance, enum ast_frame_type type, int compensate) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in remote_address; ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ if (((compensate && type == AST_FRAME_DTMF_END) || (type == AST_FRAME_DTMF_BEGIN)) && ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) { ++ ast_debug(1, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(remote_address.sin_addr)); ++ rtp->resp = 0; ++ rtp->dtmfsamples = 0; ++ return &ast_null_frame; ++ } ++ ast_debug(1, "Sending dtmf: %d (%c), at %s\n", rtp->resp, rtp->resp, ast_inet_ntoa(remote_address.sin_addr)); ++ if (rtp->resp == 'X') { ++ rtp->f.frametype = AST_FRAME_CONTROL; ++ rtp->f.subclass = AST_CONTROL_FLASH; ++ } else { ++ rtp->f.frametype = type; ++ rtp->f.subclass = rtp->resp; ++ } ++ rtp->f.datalen = 0; ++ rtp->f.samples = 0; ++ rtp->f.mallocd = 0; ++ rtp->f.src = "RTP"; ++ ++ return &rtp->f; ++} ++ ++static struct ast_frame *process_dtmf_rfc2833(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct sockaddr_in *sin, int payloadtype, int mark) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in remote_address; ++ unsigned int event, event_end, samples; ++ char resp = 0; ++ struct ast_frame *f = NULL; ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ /* Figure out event, event end, and samples */ ++ event = ntohl(*((unsigned int *)(data))); ++ event >>= 24; ++ event_end = ntohl(*((unsigned int *)(data))); ++ event_end <<= 8; ++ event_end >>= 24; ++ samples = ntohl(*((unsigned int *)(data))); ++ samples &= 0xFFFF; ++ ++ ast_verbose("Got RTP RFC2833 from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(remote_address.sin_addr), ++ ntohs(remote_address.sin_port), payloadtype, seqno, timestamp, len, (mark?1:0), event, ((event_end & 0x80)?1:0), samples); ++ ++ /* Print out debug if turned on */ ++ if (rtpdebug || option_debug > 2) ++ ast_debug(0, "- RTP 2833 Event: %08x (len = %d)\n", event, len); ++ ++ /* Figure out what digit was pressed */ ++ if (event < 10) { ++ resp = '0' + event; ++ } else if (event < 11) { ++ resp = '*'; ++ } else if (event < 12) { ++ resp = '#'; ++ } else if (event < 16) { ++ resp = 'A' + (event - 12); ++ } else if (event < 17) { /* Event 16: Hook flash */ ++ resp = 'X'; ++ } else { ++ /* Not a supported event */ ++ ast_log(LOG_DEBUG, "Ignoring RTP 2833 Event: %08x. Not a DTMF Digit.\n", event); ++ return &ast_null_frame; ++ } ++ ++ if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)) { ++ if ((rtp->lastevent != timestamp) || (rtp->resp && rtp->resp != resp)) { ++ rtp->resp = resp; ++ rtp->dtmfcount = 0; ++ f = send_dtmf(instance, AST_FRAME_DTMF_END, ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)); ++ f->len = 0; ++ rtp->lastevent = timestamp; ++ } ++ } else { ++ if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) { ++ rtp->resp = resp; ++ f = send_dtmf(instance, AST_FRAME_DTMF_BEGIN, 0); ++ rtp->dtmfcount = dtmftimeout; ++ } else if ((event_end & 0x80) && (rtp->lastevent != seqno) && rtp->resp) { ++ f = send_dtmf(instance, AST_FRAME_DTMF_END, 0); ++ f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */ ++ rtp->resp = 0; ++ rtp->dtmfcount = 0; ++ rtp->lastevent = seqno; ++ } ++ } ++ ++ rtp->dtmfsamples = samples; ++ ++ return f; ++} ++ ++static struct ast_frame *process_dtmf_cisco(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct sockaddr_in *sin, int payloadtype, int mark) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ unsigned int event, flags, power; ++ char resp = 0; ++ unsigned char seq; ++ struct ast_frame *f = NULL; ++ ++ if (len < 4) { ++ return NULL; ++ } ++ ++ /* The format of Cisco RTP DTMF packet looks like next: ++ +0 - sequence number of DTMF RTP packet (begins from 1, ++ wrapped to 0) ++ +1 - set of flags ++ +1 (bit 0) - flaps by different DTMF digits delimited by audio ++ or repeated digit without audio??? ++ +2 (+4,+6,...) - power level? (rises from 0 to 32 at begin of tone ++ then falls to 0 at its end) ++ +3 (+5,+7,...) - detected DTMF digit (0..9,*,#,A-D,...) ++ Repeated DTMF information (bytes 4/5, 6/7) is history shifted right ++ by each new packet and thus provides some redudancy. ++ ++ Sample of Cisco RTP DTMF packet is (all data in hex): ++ 19 07 00 02 12 02 20 02 ++ showing end of DTMF digit '2'. ++ ++ The packets ++ 27 07 00 02 0A 02 20 02 ++ 28 06 20 02 00 02 0A 02 ++ shows begin of new digit '2' with very short pause (20 ms) after ++ previous digit '2'. Bit +1.0 flips at begin of new digit. ++ ++ Cisco RTP DTMF packets comes as replacement of audio RTP packets ++ so its uses the same sequencing and timestamping rules as replaced ++ audio packets. Repeat interval of DTMF packets is 20 ms and not rely ++ on audio framing parameters. Marker bit isn't used within stream of ++ DTMFs nor audio stream coming immediately after DTMF stream. Timestamps ++ are not sequential at borders between DTMF and audio streams, ++ */ ++ ++ seq = data[0]; ++ flags = data[1]; ++ power = data[2]; ++ event = data[3] & 0x1f; ++ ++ if (option_debug > 2 || rtpdebug) ++ ast_debug(0, "Cisco DTMF Digit: %02x (len=%d, seq=%d, flags=%02x, power=%d, history count=%d)\n", event, len, seq, flags, power, (len - 4) / 2); ++ if (event < 10) { ++ resp = '0' + event; ++ } else if (event < 11) { ++ resp = '*'; ++ } else if (event < 12) { ++ resp = '#'; ++ } else if (event < 16) { ++ resp = 'A' + (event - 12); ++ } else if (event < 17) { ++ resp = 'X'; ++ } ++ if ((!rtp->resp && power) || (rtp->resp && (rtp->resp != resp))) { ++ rtp->resp = resp; ++ /* Why we should care on DTMF compensation at reception? */ ++ if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)) { ++ f = send_dtmf(instance, AST_FRAME_DTMF_BEGIN, 0); ++ rtp->dtmfsamples = 0; ++ } ++ } else if ((rtp->resp == resp) && !power) { ++ f = send_dtmf(instance, AST_FRAME_DTMF_END, ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)); ++ f->samples = rtp->dtmfsamples * 8; ++ rtp->resp = 0; ++ } else if (rtp->resp == resp) ++ rtp->dtmfsamples += 20 * 8; ++ rtp->dtmfcount = dtmftimeout; ++ ++ return f; ++} ++ ++static struct ast_frame *process_cn_rfc3389(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct sockaddr_in *sin, int payloadtype, int mark) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ /* Convert comfort noise into audio with various codecs. Unfortunately this doesn't ++ totally help us out becuase we don't have an engine to keep it going and we are not ++ guaranteed to have it every 20ms or anything */ ++ if (rtpdebug) ++ ast_debug(0, "- RTP 3389 Comfort noise event: Level %d (len = %d)\n", rtp->lastrxformat, len); ++ ++ if (ast_test_flag(rtp, FLAG_3389_WARNING)) { ++ struct sockaddr_in remote_address; ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ ast_log(LOG_NOTICE, "Comfort noise support incomplete in Asterisk (RFC 3389). Please turn off on client if possible. Client IP: %s\n", ++ ast_inet_ntoa(remote_address.sin_addr)); ++ ast_set_flag(rtp, FLAG_3389_WARNING); ++ } ++ ++ /* Must have at least one byte */ ++ if (!len) ++ return NULL; ++ if (len < 24) { ++ rtp->f.data.ptr = rtp->rawdata + AST_FRIENDLY_OFFSET; ++ rtp->f.datalen = len - 1; ++ rtp->f.offset = AST_FRIENDLY_OFFSET; ++ memcpy(rtp->f.data.ptr, data + 1, len - 1); ++ } else { ++ rtp->f.data.ptr = NULL; ++ rtp->f.offset = 0; ++ rtp->f.datalen = 0; ++ } ++ rtp->f.frametype = AST_FRAME_CNG; ++ rtp->f.subclass = data[0] & 0x7f; ++ rtp->f.datalen = len - 1; ++ rtp->f.samples = 0; ++ rtp->f.delivery.tv_usec = rtp->f.delivery.tv_sec = 0; ++ ++ return &rtp->f; ++} ++ ++static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in sin; ++ socklen_t len = sizeof(sin); ++ unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET]; ++ unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET); ++ int res, packetwords, position = 0; ++ struct ast_frame *f = &ast_null_frame; ++ ++ /* Read in RTCP data from the socket */ ++ if ((res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0) { ++ ast_assert(errno != EBADF); ++ if (errno != EAGAIN) { ++ ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", strerror(errno)); ++ return NULL; ++ } ++ return &ast_null_frame; ++ } ++ ++ packetwords = res / 4; ++ ++ if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) { ++ /* Send to whoever sent to us */ ++ if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || ++ (rtp->rtcp->them.sin_port != sin.sin_port)) { ++ memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them)); ++ if (option_debug || rtpdebug) ++ ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); ++ } ++ } ++ ++ ast_debug(1, "Got RTCP report of %d bytes\n", res); ++ ++ while (position < packetwords) { ++ int i, pt, rc; ++ unsigned int length, dlsr, lsr, msw, lsw, comp; ++ struct timeval now; ++ double rttsec, reported_jitter, reported_normdev_jitter_current, normdevrtt_current, reported_lost, reported_normdev_lost_current; ++ uint64_t rtt = 0; ++ ++ i = position; ++ length = ntohl(rtcpheader[i]); ++ pt = (length & 0xff0000) >> 16; ++ rc = (length & 0x1f000000) >> 24; ++ length &= 0xffff; ++ ++ if ((i + length) > packetwords) { ++ if (option_debug || rtpdebug) ++ ast_log(LOG_DEBUG, "RTCP Read too short\n"); ++ return &ast_null_frame; ++ } ++ ++ if (rtcp_debug_test_addr(&sin)) { ++ ast_verbose("\n\nGot RTCP from %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); ++ ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown"); ++ ast_verbose("Reception reports: %d\n", rc); ++ ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]); ++ } ++ ++ i += 2; /* Advance past header and ssrc */ ++ ++ switch (pt) { ++ case RTCP_PT_SR: ++ gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */ ++ rtp->rtcp->spc = ntohl(rtcpheader[i+3]); ++ rtp->rtcp->soc = ntohl(rtcpheader[i + 4]); ++ rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/ ++ ++ if (rtcp_debug_test_addr(&sin)) { ++ ast_verbose("NTP timestamp: %lu.%010lu\n", (unsigned long) ntohl(rtcpheader[i]), (unsigned long) ntohl(rtcpheader[i + 1]) * 4096); ++ ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2])); ++ ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4])); ++ } ++ i += 5; ++ if (rc < 1) ++ break; ++ /* Intentional fall through */ ++ case RTCP_PT_RR: ++ /* Don't handle multiple reception reports (rc > 1) yet */ ++ /* Calculate RTT per RFC */ ++ gettimeofday(&now, NULL); ++ timeval2ntp(now, &msw, &lsw); ++ if (ntohl(rtcpheader[i + 4]) && ntohl(rtcpheader[i + 5])) { /* We must have the LSR && DLSR */ ++ comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16); ++ lsr = ntohl(rtcpheader[i + 4]); ++ dlsr = ntohl(rtcpheader[i + 5]); ++ rtt = comp - lsr - dlsr; ++ ++ /* Convert end to end delay to usec (keeping the calculation in 64bit space) ++ sess->ee_delay = (eedelay * 1000) / 65536; */ ++ if (rtt < 4294) { ++ rtt = (rtt * 1000000) >> 16; ++ } else { ++ rtt = (rtt * 1000) >> 16; ++ rtt *= 1000; ++ } ++ rtt = rtt / 1000.; ++ rttsec = rtt / 1000.; ++ rtp->rtcp->rtt = rttsec; ++ ++ if (comp - dlsr >= lsr) { ++ rtp->rtcp->accumulated_transit += rttsec; ++ ++ if (rtp->rtcp->rtt_count == 0) ++ rtp->rtcp->minrtt = rttsec; ++ ++ if (rtp->rtcp->maxrtt<rttsec) ++ rtp->rtcp->maxrtt = rttsec; ++ if (rtp->rtcp->minrtt>rttsec) ++ rtp->rtcp->minrtt = rttsec; ++ ++ normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt, rttsec, rtp->rtcp->rtt_count); ++ ++ rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt, rttsec, rtp->rtcp->normdevrtt, normdevrtt_current, rtp->rtcp->rtt_count); ++ ++ rtp->rtcp->normdevrtt = normdevrtt_current; ++ ++ rtp->rtcp->rtt_count++; ++ } else if (rtcp_debug_test_addr(&sin)) { ++ ast_verbose("Internal RTCP NTP clock skew detected: " ++ "lsr=%u, now=%u, dlsr=%u (%d:%03dms), " ++ "diff=%d\n", ++ lsr, comp, dlsr, dlsr / 65536, ++ (dlsr % 65536) * 1000 / 65536, ++ dlsr - (comp - lsr)); ++ } ++ } ++ ++ rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]); ++ reported_jitter = (double) rtp->rtcp->reported_jitter; ++ ++ if (rtp->rtcp->reported_jitter_count == 0) ++ rtp->rtcp->reported_minjitter = reported_jitter; ++ ++ if (reported_jitter < rtp->rtcp->reported_minjitter) ++ rtp->rtcp->reported_minjitter = reported_jitter; ++ ++ if (reported_jitter > rtp->rtcp->reported_maxjitter) ++ rtp->rtcp->reported_maxjitter = reported_jitter; ++ ++ reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count); ++ ++ rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count); ++ ++ rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current; ++ ++ rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff; ++ ++ reported_lost = (double) rtp->rtcp->reported_lost; ++ ++ /* using same counter as for jitter */ ++ if (rtp->rtcp->reported_jitter_count == 0) ++ rtp->rtcp->reported_minlost = reported_lost; ++ ++ if (reported_lost < rtp->rtcp->reported_minlost) ++ rtp->rtcp->reported_minlost = reported_lost; ++ ++ if (reported_lost > rtp->rtcp->reported_maxlost) ++ rtp->rtcp->reported_maxlost = reported_lost; ++ reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count); ++ ++ rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count); ++ ++ rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current; ++ ++ rtp->rtcp->reported_jitter_count++; ++ ++ if (rtcp_debug_test_addr(&sin)) { ++ ast_verbose(" Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24)); ++ ast_verbose(" Packets lost so far: %d\n", rtp->rtcp->reported_lost); ++ ast_verbose(" Highest sequence number: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff)); ++ ast_verbose(" Sequence number cycles: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16); ++ ast_verbose(" Interarrival jitter: %u\n", rtp->rtcp->reported_jitter); ++ ast_verbose(" Last SR(our NTP): %lu.%010lu\n",(unsigned long) ntohl(rtcpheader[i + 4]) >> 16,((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096); ++ ast_verbose(" DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i + 5])/65536.0); ++ if (rtt) ++ ast_verbose(" RTT: %lu(sec)\n", (unsigned long) rtt); ++ } ++ if (rtt) { ++ manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n" ++ "PT: %d(%s)\r\n" ++ "ReceptionReports: %d\r\n" ++ "SenderSSRC: %u\r\n" ++ "FractionLost: %ld\r\n" ++ "PacketsLost: %d\r\n" ++ "HighestSequence: %ld\r\n" ++ "SequenceNumberCycles: %ld\r\n" ++ "IAJitter: %u\r\n" ++ "LastSR: %lu.%010lu\r\n" ++ "DLSR: %4.4f(sec)\r\n" ++ "RTT: %llu(sec)\r\n", ++ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), ++ pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", ++ rc, ++ rtcpheader[i + 1], ++ (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), ++ rtp->rtcp->reported_lost, ++ (long) (ntohl(rtcpheader[i + 2]) & 0xffff), ++ (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, ++ rtp->rtcp->reported_jitter, ++ (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, ++ ntohl(rtcpheader[i + 5])/65536.0, ++ (unsigned long long)rtt); ++ } else { ++ manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n" ++ "PT: %d(%s)\r\n" ++ "ReceptionReports: %d\r\n" ++ "SenderSSRC: %u\r\n" ++ "FractionLost: %ld\r\n" ++ "PacketsLost: %d\r\n" ++ "HighestSequence: %ld\r\n" ++ "SequenceNumberCycles: %ld\r\n" ++ "IAJitter: %u\r\n" ++ "LastSR: %lu.%010lu\r\n" ++ "DLSR: %4.4f(sec)\r\n", ++ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), ++ pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", ++ rc, ++ rtcpheader[i + 1], ++ (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), ++ rtp->rtcp->reported_lost, ++ (long) (ntohl(rtcpheader[i + 2]) & 0xffff), ++ (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, ++ rtp->rtcp->reported_jitter, ++ (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ++ ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, ++ ntohl(rtcpheader[i + 5])/65536.0); ++ } ++ break; ++ case RTCP_PT_FUR: ++ if (rtcp_debug_test_addr(&sin)) ++ ast_verbose("Received an RTCP Fast Update Request\n"); ++ rtp->f.frametype = AST_FRAME_CONTROL; ++ rtp->f.subclass = AST_CONTROL_VIDUPDATE; ++ rtp->f.datalen = 0; ++ rtp->f.samples = 0; ++ rtp->f.mallocd = 0; ++ rtp->f.src = "RTP"; ++ f = &rtp->f; ++ break; ++ case RTCP_PT_SDES: ++ if (rtcp_debug_test_addr(&sin)) ++ ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); ++ break; ++ case RTCP_PT_BYE: ++ if (rtcp_debug_test_addr(&sin)) ++ ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); ++ break; ++ default: ++ ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); ++ break; ++ } ++ position += (length + 1); ++ } ++ ++ rtp->rtcp->rtcp_info = 1; ++ ++ return f; ++} ++ ++static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int *rtpheader, int len, int hdrlen) ++{ ++ struct ast_rtp_instance *instance1 = ast_rtp_instance_get_bridged(instance); ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance), *bridged = ast_rtp_instance_get_data(instance1); ++ int res = 0, payload = 0, bridged_payload = 0, mark; ++ struct ast_rtp_payload_type payload_type; ++ int reconstruct = ntohl(rtpheader[0]); ++ struct sockaddr_in remote_address; ++ ++ /* Get fields from packet */ ++ payload = (reconstruct & 0x7f0000) >> 16; ++ mark = (((reconstruct & 0x800000) >> 23) != 0); ++ ++ /* Check what the payload value should be */ ++ payload_type = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(instance), payload); ++ ++ /* Otherwise adjust bridged payload to match */ ++ bridged_payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance1), payload_type.asterisk_format, payload_type.code); ++ ++ /* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */ ++ if (!(ast_rtp_instance_get_codecs(instance1)->payloads[bridged_payload].code)) { ++ return -1; ++ } ++ ++ /* If the marker bit has been explicitly set turn it on */ ++ if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) { ++ mark = 1; ++ ast_clear_flag(rtp, FLAG_NEED_MARKER_BIT); ++ } ++ ++ /* Reconstruct part of the packet */ ++ reconstruct &= 0xFF80FFFF; ++ reconstruct |= (bridged_payload << 16); ++ reconstruct |= (mark << 23); ++ rtpheader[0] = htonl(reconstruct); ++ ++ ast_rtp_instance_get_remote_address(instance1, &remote_address); ++ ++ /* Send the packet back out */ ++ res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&remote_address, sizeof(remote_address)); ++ if (res < 0) { ++ if (!ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_NAT) && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { ++ ast_debug(1, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno)); ++ } else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) { ++ if (option_debug || rtpdebug) ++ ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port)); ++ ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN); ++ } ++ return 0; ++ } else if (rtp_debug_test_addr(&remote_address)) { ++ ast_verbose("Sent RTP P2P packet to %s:%u (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), bridged_payload, len - hdrlen); ++ } ++ ++ return 0; ++} ++ ++static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in sin; ++ socklen_t len = sizeof(sin); ++ int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno; ++ unsigned int *rtpheader = (unsigned int*)(rtp->rawdata + AST_FRIENDLY_OFFSET), seqno, ssrc, timestamp; ++ struct ast_rtp_payload_type payload; ++ struct sockaddr_in remote_address; ++ ++ /* If this is actually RTCP let's hop on over and handle it */ ++ if (rtcp) { ++ if (rtp->rtcp) { ++ return ast_rtcp_read(instance); ++ } ++ return &ast_null_frame; ++ } ++ ++ /* If we are currently sending DTMF to the remote party send a continuation packet */ ++ if (rtp->sending_digit) { ++ ast_rtp_dtmf_continuation(instance); ++ } ++ ++ /* Actually read in the data from the socket */ ++ if ((res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr*)&sin, &len)) < 0) { ++ ast_assert(errno != EBADF); ++ if (errno != EAGAIN) { ++ ast_log(LOG_WARNING, "RTP Read error: %s. Hanging up.\n", strerror(errno)); ++ return NULL; ++ } ++ return &ast_null_frame; ++ } ++ ++ /* Make sure the data that was read in is actually enough to make up an RTP packet */ ++ if (res < hdrlen) { ++ ast_log(LOG_WARNING, "RTP Read too short\n"); ++ return &ast_null_frame; ++ } ++ ++ /* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */ ++ if (rtp->strict_rtp_state == STRICT_RTP_LEARN) { ++ memcpy(&rtp->strict_rtp_address, &sin, sizeof(rtp->strict_rtp_address)); ++ rtp->strict_rtp_state = STRICT_RTP_CLOSED; ++ } else if (rtp->strict_rtp_state == STRICT_RTP_CLOSED) { ++ if ((rtp->strict_rtp_address.sin_addr.s_addr != sin.sin_addr.s_addr) || (rtp->strict_rtp_address.sin_port != sin.sin_port)) { ++ ast_debug(1, "Received RTP packet from %s:%d, dropping due to strict RTP protection. Expected it to be from %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), ast_inet_ntoa(rtp->strict_rtp_address.sin_addr), ntohs(rtp->strict_rtp_address.sin_port)); ++ return &ast_null_frame; ++ } ++ } ++ ++ /* Get fields and verify this is an RTP packet */ ++ seqno = ntohl(rtpheader[0]); ++ ++ ast_rtp_instance_get_remote_address(instance, &remote_address); ++ ++ if (!(version = (seqno & 0xC0000000) >> 30)) { ++ if ((ast_stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT) && ++ (!remote_address.sin_port && !remote_address.sin_addr.s_addr)) { ++ ast_rtp_instance_set_remote_address(instance, &sin); ++ } ++ return &ast_null_frame; ++ } ++ ++ /* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */ ++ if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) { ++ if ((remote_address.sin_addr.s_addr != sin.sin_addr.s_addr) || ++ (remote_address.sin_port != sin.sin_port)) { ++ ast_rtp_instance_set_remote_address(instance, &sin); ++ memcpy(&remote_address, &sin, sizeof(remote_address)); ++ if (rtp->rtcp) { ++ memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them)); ++ rtp->rtcp->them.sin_port = htons(ntohs(sin.sin_port)+1); ++ } ++ rtp->rxseqno = 0; ++ ast_set_flag(rtp, FLAG_NAT_ACTIVE); ++ if (option_debug || rtpdebug) ++ ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port)); ++ } ++ } ++ ++ /* If we are directly bridged to another instance send the audio directly out */ ++ if (ast_rtp_instance_get_bridged(instance) && !bridge_p2p_rtp_write(instance, rtpheader, res, hdrlen)) { ++ return &ast_null_frame; ++ } ++ ++ /* If the version is not what we expected by this point then just drop the packet */ ++ if (version != 2) { ++ return &ast_null_frame; ++ } ++ ++ /* Pull out the various other fields we will need */ ++ payloadtype = (seqno & 0x7f0000) >> 16; ++ padding = seqno & (1 << 29); ++ mark = seqno & (1 << 23); ++ ext = seqno & (1 << 28); ++ cc = (seqno & 0xF000000) >> 24; ++ seqno &= 0xffff; ++ timestamp = ntohl(rtpheader[1]); ++ ssrc = ntohl(rtpheader[2]); ++ ++ /* Force a marker bit if the SSRC changes */ ++ if (!mark && rtp->rxssrc && rtp->rxssrc != ssrc) { ++ if (option_debug || rtpdebug) { ++ ast_debug(1, "Forcing Marker bit, because SSRC has changed\n"); ++ } ++ mark = 1; ++ } ++ ++ /* Remove any padding bytes that may be present */ ++ if (padding) { ++ res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1]; ++ } ++ ++ /* Skip over any CSRC fields */ ++ if (cc) { ++ hdrlen += cc * 4; ++ } ++ ++ /* Look for any RTP extensions, currently we do not support any */ ++ if (ext) { ++ hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2; ++ hdrlen += 4; ++ if (option_debug) { ++ int profile; ++ profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16; ++ if (profile == 0x505a) ++ ast_debug(1, "Found Zfone extension in RTP stream - zrtp - not supported.\n"); ++ else ++ ast_debug(1, "Found unknown RTP Extensions %x\n", profile); ++ } ++ } ++ ++ /* Make sure after we potentially mucked with the header length that it is once again valid */ ++ if (res < hdrlen) { ++ ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d\n", res, hdrlen); ++ return &ast_null_frame; ++ } ++ ++ rtp->rxcount++; ++ if (rtp->rxcount == 1) { ++ rtp->seedrxseqno = seqno; ++ } ++ ++ /* Do not schedule RR if RTCP isn't run */ ++ if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) { ++ /* Schedule transmission of Receiver Report */ ++ rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); ++ } ++ if ((int)rtp->lastrxseqno - (int)seqno > 100) /* if so it would indicate that the sender cycled; allow for misordering */ ++ rtp->cycles += RTP_SEQ_MOD; ++ ++ prev_seqno = rtp->lastrxseqno; ++ rtp->lastrxseqno = seqno; ++ ++ if (!rtp->themssrc) { ++ rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */ ++ } ++ ++ if (rtp_debug_test_addr(&sin)) { ++ ast_verbose("Got RTP packet from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", ++ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen); ++ } ++ ++ payload = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(instance), payloadtype); ++ ++ /* If the payload is not actually an Asterisk one but a special one pass it off to the respective handler */ ++ if (!payload.asterisk_format) { ++ struct ast_frame *f = NULL; ++ ++ if (payload.code == AST_RTP_DTMF) { ++ f = process_dtmf_rfc2833(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &sin, payloadtype, mark); ++ } else if (payload.code == AST_RTP_CISCO_DTMF) { ++ f = process_dtmf_cisco(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &sin, payloadtype, mark); ++ } else if (payload.code == AST_RTP_CN) { ++ f = process_cn_rfc3389(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &sin, payloadtype, mark); ++ } else { ++ ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, ast_inet_ntoa(remote_address.sin_addr)); ++ } ++ ++ return f ? f : &ast_null_frame; ++ } ++ ++ rtp->lastrxformat = rtp->f.subclass = payload.code; ++ rtp->f.frametype = (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) ? AST_FRAME_VOICE : (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) ? AST_FRAME_VIDEO : AST_FRAME_TEXT; ++ ++ rtp->rxseqno = seqno; ++ rtp->lastrxts = timestamp; ++ ++ rtp->f.src = "RTP"; ++ rtp->f.mallocd = 0; ++ rtp->f.datalen = res - hdrlen; ++ rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; ++ rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; ++ rtp->f.seqno = seqno; ++ ++ if (rtp->f.subclass == AST_FORMAT_T140 && (int)seqno - (prev_seqno+1) > 0 && (int)seqno - (prev_seqno+1) < 10) { ++ unsigned char *data = rtp->f.data.ptr; ++ ++ memmove(rtp->f.data.ptr+3, rtp->f.data.ptr, rtp->f.datalen); ++ rtp->f.datalen +=3; ++ *data++ = 0xEF; ++ *data++ = 0xBF; ++ *data = 0xBD; ++ } ++ ++ if (rtp->f.subclass == AST_FORMAT_T140RED) { ++ unsigned char *data = rtp->f.data.ptr; ++ unsigned char *header_end; ++ int num_generations; ++ int header_length; ++ int len; ++ int diff =(int)seqno - (prev_seqno+1); /* if diff = 0, no drop*/ ++ int x; ++ ++ rtp->f.subclass = AST_FORMAT_T140; ++ header_end = memchr(data, ((*data) & 0x7f), rtp->f.datalen); ++ header_end++; ++ ++ header_length = header_end - data; ++ num_generations = header_length / 4; ++ len = header_length; ++ ++ if (!diff) { ++ for (x = 0; x < num_generations; x++) ++ len += data[x * 4 + 3]; ++ ++ if (!(rtp->f.datalen - len)) ++ return &ast_null_frame; ++ ++ rtp->f.data.ptr += len; ++ rtp->f.datalen -= len; ++ } else if (diff > num_generations && diff < 10) { ++ len -= 3; ++ rtp->f.data.ptr += len; ++ rtp->f.datalen -= len; ++ ++ data = rtp->f.data.ptr; ++ *data++ = 0xEF; ++ *data++ = 0xBF; ++ *data = 0xBD; ++ } else { ++ for ( x = 0; x < num_generations - diff; x++) ++ len += data[x * 4 + 3]; ++ ++ rtp->f.data.ptr += len; ++ rtp->f.datalen -= len; ++ } ++ } ++ ++ if (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) { ++ rtp->f.samples = ast_codec_get_samples(&rtp->f); ++ if (rtp->f.subclass == AST_FORMAT_SLINEAR) ++ ast_frame_byteswap_be(&rtp->f); ++ calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); ++ /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */ ++ ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO); ++ rtp->f.ts = timestamp / 8; ++ rtp->f.len = rtp->f.samples / ((ast_format_rate(rtp->f.subclass) / 1000)); ++ } else if (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) { ++ /* Video -- samples is # of samples vs. 90000 */ ++ if (!rtp->lastividtimestamp) ++ rtp->lastividtimestamp = timestamp; ++ rtp->f.samples = timestamp - rtp->lastividtimestamp; ++ rtp->lastividtimestamp = timestamp; ++ rtp->f.delivery.tv_sec = 0; ++ rtp->f.delivery.tv_usec = 0; ++ /* Pass the RTP marker bit as bit 0 in the subclass field. ++ * This is ok because subclass is actually a bitmask, and ++ * the low bits represent audio formats, that are not ++ * involved here since we deal with video. ++ */ ++ if (mark) ++ rtp->f.subclass |= 0x1; ++ } else { ++ /* TEXT -- samples is # of samples vs. 1000 */ ++ if (!rtp->lastitexttimestamp) ++ rtp->lastitexttimestamp = timestamp; ++ rtp->f.samples = timestamp - rtp->lastitexttimestamp; ++ rtp->lastitexttimestamp = timestamp; ++ rtp->f.delivery.tv_sec = 0; ++ rtp->f.delivery.tv_usec = 0; ++ } ++ ++ return &rtp->f; ++} ++ ++static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ if (property == AST_RTP_PROPERTY_RTCP) { ++ if (rtp->rtcp) { ++ ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance); ++ return; ++ } ++ if (!(rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)))) { ++ return; ++ } ++ if ((rtp->rtcp->s = create_new_socket("RTCP")) < 0) { ++ ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance); ++ ast_free(rtp->rtcp); ++ rtp->rtcp = NULL; ++ return; ++ } ++ ++ /* Grab the IP address and port we are going to use */ ++ ast_rtp_instance_get_local_address(instance, &rtp->rtcp->us); ++ rtp->rtcp->us.sin_port = htons(ntohs(rtp->rtcp->us.sin_port) + 1); ++ ++ /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */ ++ if (bind(rtp->rtcp->s, (struct sockaddr*)&rtp->rtcp->us, sizeof(rtp->rtcp->us))) { ++ ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance); ++ close(rtp->rtcp->s); ++ ast_free(rtp->rtcp); ++ rtp->rtcp = NULL; ++ return; ++ } ++ ++ ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance); ++ rtp->rtcp->schedid = -1; ++ ++ return; ++ } ++ ++ return; ++} ++ ++static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ return rtcp ? (rtp->rtcp ? rtp->rtcp->s : -1) : rtp->s; ++} ++ ++static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct sockaddr_in *sin) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ if (rtp->rtcp) { ++ ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance); ++ memcpy(&rtp->rtcp->them, sin, sizeof(rtp->rtcp->them)); ++ rtp->rtcp->them.sin_port = htons(ntohs(sin->sin_port) + 1); ++ } ++ ++ rtp->rxseqno = 0; ++ ++ if (strictrtp) { ++ rtp->strict_rtp_state = STRICT_RTP_LEARN; ++ } ++ ++ return; ++} ++ ++/*! \brief Write t140 redundacy frame ++ * \param data primary data to be buffered ++ */ ++static int red_write(const void *data) ++{ ++ struct ast_rtp_instance *instance = (struct ast_rtp_instance*) data; ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ ast_rtp_write(instance, &rtp->red->t140); ++ ++ return 1; ++} ++ ++static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ int x; ++ ++ if (!(rtp->red = ast_calloc(1, sizeof(*rtp->red)))) { ++ return -1; ++ } ++ ++ rtp->red->t140.frametype = AST_FRAME_TEXT; ++ rtp->red->t140.subclass = AST_FORMAT_T140RED; ++ rtp->red->t140.data.ptr = &rtp->red->buf_data; ++ ++ rtp->red->t140.ts = 0; ++ rtp->red->t140red = rtp->red->t140; ++ rtp->red->t140red.data.ptr = &rtp->red->t140red_data; ++ rtp->red->t140red.datalen = 0; ++ rtp->red->ti = buffer_time; ++ rtp->red->num_gen = generations; ++ rtp->red->hdrlen = generations * 4 + 1; ++ rtp->red->prev_ts = 0; ++ ++ for (x = 0; x < generations; x++) { ++ rtp->red->pt[x] = payloads[x]; ++ rtp->red->pt[x] |= 1 << 7; /* mark redundant generations pt */ ++ rtp->red->t140red_data[x*4] = rtp->red->pt[x]; ++ } ++ rtp->red->t140red_data[x*4] = rtp->red->pt[x] = payloads[x]; /* primary pt */ ++ rtp->red->schedid = ast_sched_add(rtp->sched, generations, red_write, instance); ++ ++ rtp->red->t140.datalen = 0; ++ ++ return 0; ++} ++ ++static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ if (frame->datalen > -1) { ++ struct rtp_red *red = rtp->red; ++ memcpy(&red->buf_data[red->t140.datalen], frame->data.ptr, frame->datalen); ++ red->t140.datalen += frame->datalen; ++ red->t140.ts = frame->ts; ++ } ++ ++ return 0; ++} ++ ++static int ast_rtp_local_bridge(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance0); ++ ++ ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); ++ ++ return 0; ++} ++ ++static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ if (!rtp->rtcp) { ++ return -1; ++ } ++ ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXCOUNT, -1, stats->txcount, rtp->txcount); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXCOUNT, -1, stats->rxcount, rtp->rxcount); ++ ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->txploss, rtp->rtcp->reported_lost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->rxploss, rtp->rtcp->expected_prior - rtp->rtcp->received_prior); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MAXRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_maxrxploss, rtp->rtcp->reported_maxlost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MINRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_minrxploss, rtp->rtcp->reported_minlost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_normdevrxploss, rtp->rtcp->reported_normdev_lost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_STDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_stdevrxploss, rtp->rtcp->reported_stdev_lost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MAXRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_maxrxploss, rtp->rtcp->maxrxlost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MINRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_minrxploss, rtp->rtcp->minrxlost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_normdevrxploss, rtp->rtcp->normdev_rxlost); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_STDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_stdevrxploss, rtp->rtcp->stdev_rxlost); ++ AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_LOSS); ++ ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->txjitter, rtp->rxjitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->rxjitter, rtp->rtcp->reported_jitter / (unsigned int) 65536.0); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MAXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_maxjitter, rtp->rtcp->reported_maxjitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MINJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_minjitter, rtp->rtcp->reported_minjitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_normdevjitter, rtp->rtcp->reported_normdev_jitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_STDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_stdevjitter, rtp->rtcp->reported_stdev_jitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MAXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_maxjitter, rtp->rtcp->maxrxjitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MINJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_minjitter, rtp->rtcp->minrxjitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_normdevjitter, rtp->rtcp->normdev_rxjitter); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_STDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_stdevjitter, rtp->rtcp->stdev_rxjitter); ++ AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_JITTER); ++ ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->rtt, rtp->rtcp->rtt); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_MAX_RTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->maxrtt, rtp->rtcp->maxrtt); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_MIN_RTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->minrtt, rtp->rtcp->minrtt); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_NORMDEVRTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->normdevrtt, rtp->rtcp->normdevrtt); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_STDEVRTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->stdevrtt, rtp->rtcp->stdevrtt); ++ AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_RTT); ++ ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_SSRC, -1, stats->local_ssrc, rtp->ssrc); ++ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_SSRC, -1, stats->remote_ssrc, rtp->themssrc); ++ ++ return 0; ++} ++ ++static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1) ++{ ++ /* If both sides are not using the same method of DTMF transmission ++ * (ie: one is RFC2833, other is INFO... then we can not do direct media. ++ * -------------------------------------------------- ++ * | DTMF Mode | HAS_DTMF | Accepts Begin Frames | ++ * |-----------|------------|-----------------------| ++ * | Inband | False | True | ++ * | RFC2833 | True | True | ++ * | SIP INFO | False | False | ++ * -------------------------------------------------- ++ */ ++ return (((ast_rtp_instance_get_prop(instance0, AST_RTP_PROPERTY_DTMF) != ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_DTMF)) || ++ (!chan0->tech->send_digit_begin != !chan1->tech->send_digit_begin)) ? 0 : 1); ++} ++ ++static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ ++ ast_stun_request(rtp->s, suggestion, username, NULL); ++} ++ ++static void ast_rtp_stop(struct ast_rtp_instance *instance) ++{ ++ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ++ struct sockaddr_in sin = { 0, }; ++ ++ if (rtp->rtcp) { ++ AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); ++ } ++ if (rtp->red) { ++ AST_SCHED_DEL(rtp->sched, rtp->red->schedid); ++ free(rtp->red); ++ rtp->red = NULL; ++ } ++ ++ ast_rtp_instance_set_remote_address(instance, &sin); ++ if (rtp->rtcp) { ++ memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr)); ++ memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port)); ++ } ++ ++ ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); ++} ++ ++static char *rtp_do_debug_ip(struct ast_cli_args *a) ++{ ++ struct hostent *hp; ++ struct ast_hostent ahp; ++ int port = 0; ++ char *p, *arg; ++ ++ arg = a->argv[3]; ++ p = strstr(arg, ":"); ++ if (p) { ++ *p = '\0'; ++ p++; ++ port = atoi(p); ++ } ++ hp = ast_gethostbyname(arg, &ahp); ++ if (hp == NULL) { ++ ast_cli(a->fd, "Lookup failed for '%s'\n", arg); ++ return CLI_FAILURE; ++ } ++ rtpdebugaddr.sin_family = AF_INET; ++ memcpy(&rtpdebugaddr.sin_addr, hp->h_addr, sizeof(rtpdebugaddr.sin_addr)); ++ rtpdebugaddr.sin_port = htons(port); ++ if (port == 0) ++ ast_cli(a->fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtpdebugaddr.sin_addr)); ++ else ++ ast_cli(a->fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtpdebugaddr.sin_addr), port); ++ rtpdebug = 1; ++ return CLI_SUCCESS; ++} ++ ++static char *rtcp_do_debug_ip(struct ast_cli_args *a) ++{ ++ struct hostent *hp; ++ struct ast_hostent ahp; ++ int port = 0; ++ char *p, *arg; ++ ++ arg = a->argv[3]; ++ p = strstr(arg, ":"); ++ if (p) { ++ *p = '\0'; ++ p++; ++ port = atoi(p); ++ } ++ hp = ast_gethostbyname(arg, &ahp); ++ if (hp == NULL) { ++ ast_cli(a->fd, "Lookup failed for '%s'\n", arg); ++ return CLI_FAILURE; ++ } ++ rtcpdebugaddr.sin_family = AF_INET; ++ memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr)); ++ rtcpdebugaddr.sin_port = htons(port); ++ if (port == 0) ++ ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr)); ++ else ++ ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port); ++ rtcpdebug = 1; ++ return CLI_SUCCESS; ++} ++ ++static char *handle_cli_rtp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) ++{ ++ switch (cmd) { ++ case CLI_INIT: ++ e->command = "rtp set debug {on|off|ip}"; ++ e->usage = ++ "Usage: rtp set debug {on|off|ip host[:port]}\n" ++ " Enable/Disable dumping of all RTP packets. If 'ip' is\n" ++ " specified, limit the dumped packets to those to and from\n" ++ " the specified 'host' with optional port.\n"; ++ return NULL; ++ case CLI_GENERATE: ++ return NULL; ++ } ++ ++ if (a->argc == e->args) { /* set on or off */ ++ if (!strncasecmp(a->argv[e->args-1], "on", 2)) { ++ rtpdebug = 1; ++ memset(&rtpdebugaddr, 0, sizeof(rtpdebugaddr)); ++ ast_cli(a->fd, "RTP Debugging Enabled\n"); ++ return CLI_SUCCESS; ++ } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { ++ rtpdebug = 0; ++ ast_cli(a->fd, "RTP Debugging Disabled\n"); ++ return CLI_SUCCESS; ++ } ++ } else if (a->argc == e->args +1) { /* ip */ ++ return rtp_do_debug_ip(a); ++ } ++ ++ return CLI_SHOWUSAGE; /* default, failure */ ++} ++ ++static char *handle_cli_rtcp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) ++{ ++ switch (cmd) { ++ case CLI_INIT: ++ e->command = "rtcp set debug {on|off|ip}"; ++ e->usage = ++ "Usage: rtcp set debug {on|off|ip host[:port]}\n" ++ " Enable/Disable dumping of all RTCP packets. If 'ip' is\n" ++ " specified, limit the dumped packets to those to and from\n" ++ " the specified 'host' with optional port.\n"; ++ return NULL; ++ case CLI_GENERATE: ++ return NULL; ++ } ++ ++ if (a->argc == e->args) { /* set on or off */ ++ if (!strncasecmp(a->argv[e->args-1], "on", 2)) { ++ rtcpdebug = 1; ++ memset(&rtcpdebugaddr, 0, sizeof(rtcpdebugaddr)); ++ ast_cli(a->fd, "RTCP Debugging Enabled\n"); ++ return CLI_SUCCESS; ++ } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { ++ rtcpdebug = 0; ++ ast_cli(a->fd, "RTCP Debugging Disabled\n"); ++ return CLI_SUCCESS; ++ } ++ } else if (a->argc == e->args +1) { /* ip */ ++ return rtcp_do_debug_ip(a); ++ } ++ ++ return CLI_SHOWUSAGE; /* default, failure */ ++} ++ ++static char *handle_cli_rtcp_set_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) ++{ ++ switch (cmd) { ++ case CLI_INIT: ++ e->command = "rtcp set stats {on|off}"; ++ e->usage = ++ "Usage: rtcp set stats {on|off}\n" ++ " Enable/Disable dumping of RTCP stats.\n"; ++ return NULL; ++ case CLI_GENERATE: ++ return NULL; ++ } ++ ++ if (a->argc != e->args) ++ return CLI_SHOWUSAGE; ++ ++ if (!strncasecmp(a->argv[e->args-1], "on", 2)) ++ rtcpstats = 1; ++ else if (!strncasecmp(a->argv[e->args-1], "off", 3)) ++ rtcpstats = 0; ++ else ++ return CLI_SHOWUSAGE; ++ ++ ast_cli(a->fd, "RTCP Stats %s\n", rtcpstats ? "Enabled" : "Disabled"); ++ return CLI_SUCCESS; ++} ++ ++static struct ast_cli_entry cli_rtp[] = { ++ AST_CLI_DEFINE(handle_cli_rtp_set_debug, "Enable/Disable RTP debugging"), ++ AST_CLI_DEFINE(handle_cli_rtcp_set_debug, "Enable/Disable RTCP debugging"), ++ AST_CLI_DEFINE(handle_cli_rtcp_set_stats, "Enable/Disable RTCP stats"), ++}; ++ ++static int rtp_reload(int reload) ++{ ++ struct ast_config *cfg; ++ const char *s; ++ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; ++ ++ cfg = ast_config_load2("rtp.conf", "rtp", config_flags); ++ if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { ++ return 0; ++ } ++ ++ rtpstart = DEFAULT_RTP_START; ++ rtpend = DEFAULT_RTP_END; ++ dtmftimeout = DEFAULT_DTMF_TIMEOUT; ++ strictrtp = STRICT_RTP_OPEN; ++ if (cfg) { ++ if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) { ++ rtpstart = atoi(s); ++ if (rtpstart < MINIMUM_RTP_PORT) ++ rtpstart = MINIMUM_RTP_PORT; ++ if (rtpstart > MAXIMUM_RTP_PORT) ++ rtpstart = MAXIMUM_RTP_PORT; ++ } ++ if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) { ++ rtpend = atoi(s); ++ if (rtpend < MINIMUM_RTP_PORT) ++ rtpend = MINIMUM_RTP_PORT; ++ if (rtpend > MAXIMUM_RTP_PORT) ++ rtpend = MAXIMUM_RTP_PORT; ++ } ++ if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) { ++ rtcpinterval = atoi(s); ++ if (rtcpinterval == 0) ++ rtcpinterval = 0; /* Just so we're clear... it's zero */ ++ if (rtcpinterval < RTCP_MIN_INTERVALMS) ++ rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */ ++ if (rtcpinterval > RTCP_MAX_INTERVALMS) ++ rtcpinterval = RTCP_MAX_INTERVALMS; ++ } ++ if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) { ++#ifdef SO_NO_CHECK ++ nochecksums = ast_false(s) ? 1 : 0; ++#else ++ if (ast_false(s)) ++ ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n"); ++#endif ++ } ++ if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) { ++ dtmftimeout = atoi(s); ++ if ((dtmftimeout < 0) || (dtmftimeout > 20000)) { ++ ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n", ++ dtmftimeout, DEFAULT_DTMF_TIMEOUT); ++ dtmftimeout = DEFAULT_DTMF_TIMEOUT; ++ }; ++ } ++ if ((s = ast_variable_retrieve(cfg, "general", "strictrtp"))) { ++ strictrtp = ast_true(s); ++ } ++ ast_config_destroy(cfg); ++ } ++ if (rtpstart >= rtpend) { ++ ast_log(LOG_WARNING, "Unreasonable values for RTP start/end port in rtp.conf\n"); ++ rtpstart = DEFAULT_RTP_START; ++ rtpend = DEFAULT_RTP_END; ++ } ++ ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend); ++ return 0; ++} ++ ++static int reload_module(void) ++{ ++ rtp_reload(1); ++ return 0; ++} ++ ++static int load_module(void) ++{ ++ if (ast_rtp_engine_register(&asterisk_rtp_engine)) { ++ return AST_MODULE_LOAD_DECLINE; ++ } ++ ++ if (ast_cli_register_multiple(cli_rtp, ARRAY_LEN(cli_rtp))) { ++ ast_rtp_engine_unregister(&asterisk_rtp_engine); ++ return AST_MODULE_LOAD_DECLINE; ++ } ++ ++ rtp_reload(0); ++ ++ return AST_MODULE_LOAD_SUCCESS; ++} ++ ++static int unload_module(void) ++{ ++ ast_rtp_engine_unregister(&asterisk_rtp_engine); ++ ast_cli_unregister_multiple(cli_rtp, ARRAY_LEN(cli_rtp)); ++ ++ return 0; ++} ++ ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk RTP Stack", ++ .load = load_module, ++ .unload = unload_module, ++ .reload = reload_module, ++ ); + +Property changes on: res/res_rtp_asterisk.c +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_config_pgsql.c +=================================================================== +--- a/res/res_config_pgsql.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_config_pgsql.c (.../trunk) (revision 186562) +@@ -1530,7 +1530,7 @@ + } + + /* needs usecount semantics defined */ +-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PostgreSQL RealTime Configuration Driver", ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL RealTime Configuration Driver", + .load = load_module, + .unload = unload_module, + .reload = reload +Index: res/res_smdi.exports +=================================================================== +--- a/res/res_smdi.exports (.../tags/1.6.2.0-beta1) (revision 0) ++++ b/res/res_smdi.exports (.../trunk) (revision 186562) +@@ -0,0 +1,18 @@ ++{ ++ global: ++ ast_smdi_interface_find; ++ ast_smdi_interface_unref; ++ ast_smdi_md_message_destroy; ++ ast_smdi_md_message_pop; ++ ast_smdi_md_message_putback; ++ ast_smdi_md_message_wait; ++ ast_smdi_mwi_message_destroy; ++ ast_smdi_mwi_message_pop; ++ ast_smdi_mwi_message_putback; ++ ast_smdi_mwi_message_wait; ++ ast_smdi_mwi_message_wait_station; ++ ast_smdi_mwi_set; ++ ast_smdi_mwi_unset; ++ local: ++ *; ++}; + +Property changes on: res/res_smdi.exports +___________________________________________________________________ +Added: svn:eol-style + + native +Added: svn:mime-type + + text/plain +Added: svn:keywords + + Author Date Id Revision + +Index: res/res_phoneprov.c +=================================================================== +--- a/res/res_phoneprov.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/res/res_phoneprov.c (.../trunk) (revision 186562) +@@ -1305,7 +1305,7 @@ + return 0; + } + +-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "HTTP Phone Provisioning", ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP Phone Provisioning", + .load = load_module, + .unload = unload_module, + .reload = reload, +Index: utils/Makefile +=================================================================== +--- a/utils/Makefile (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/utils/Makefile (.../trunk) (revision 186562) +@@ -163,6 +163,11 @@ + $(CMD_PREFIX) cp "$<" "$@" + utils.o: ASTCFLAGS+=-DSTANDALONE + ++poll.c: $(ASTTOPDIR)/main/poll.c ++ $(ECHO_PREFIX) echo " [CP] $(subst $(ASTTOPDIR)/,,$<) -> $@" ++ $(CMD_PREFIX) cp "$<" "$@" ++poll.o: ASTCFLAGS+=-DSTANDALONE ++ + strings.c: $(ASTTOPDIR)/main/strings.c + $(ECHO_PREFIX) echo " [CP] $(subst $(ASTTOPDIR)/,,$<) -> $@" + $(CMD_PREFIX) cp "$<" "$@" +@@ -179,12 +184,12 @@ + threadstorage.o: ASTCFLAGS+=-DSTANDALONE + + hashtest2.o: ASTCFLAGS+=-O0 -DSTANDALONE +-hashtest2: hashtest2.o md5.o utils.o strings.o astobj2.o sha1.o strcompat.o threadstorage.o clicompat.o ++hashtest2: hashtest2.o md5.o utils.o strings.o astobj2.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o + +-hashtest: hashtest.o md5.o hashtab.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o ++hashtest: hashtest.o md5.o hashtab.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o + hashtest.o: ASTCFLAGS+=-O0 -DSTANDALONE + +-refcounter: refcounter.o md5.o hashtab.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o ++refcounter: refcounter.o md5.o hashtab.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o + refcounter.o: ASTCFLAGS+=-O0 -DSTANDALONE + + extconf.o: extconf.c + +Property changes on: utils +___________________________________________________________________ +Modified: svn:ignore + - *.d +*.i +*.s +aelbison.c +aelparse +aelparse.c +ast_expr2.c +ast_expr2f.c +astman +astobj2.c +check_expr +conf2ael +hashtab.c +hashtest +hashtest2 +md5.c +muted +pbx_ael.c +pval.c +sha1.c +smsq +stereorize +strcompat.c +streamplayer +strings.c +threadstorage.c +utils.c +astcanary +refcounter + + + *.d +*.i +*.s +aelbison.c +aelparse +aelparse.c +ast_expr2.c +ast_expr2f.c +astman +astobj2.c +check_expr +conf2ael +hashtab.c +hashtest +hashtest2 +md5.c +muted +pbx_ael.c +pval.c +poll.c +sha1.c +smsq +stereorize +strcompat.c +streamplayer +strings.c +threadstorage.c +utils.c +astcanary +refcounter + + +Index: Makefile.rules +=================================================================== +--- a/Makefile.rules (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/Makefile.rules (.../trunk) (revision 186562) +@@ -51,8 +51,13 @@ + # per-target settings will be applied + CC_CFLAGS=$(PTHREAD_CFLAGS) $(ASTCFLAGS) + CXX_CFLAGS=$(PTHREAD_CFLAGS) $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(AST_DECLARATION_AFTER_STATEMENT),$(ASTCFLAGS)) +-CC_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) +-CXX_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) ++ ++ifeq ($(GNU_LD),1) ++SO_SUPPRESS_SYMBOLS=-Wl,--version-script,$(if $(wildcard $(subst .so,.exports,$@)),$(subst .so,.exports,$@),$(ASTTOPDIR)/default.exports) ++endif ++ ++CC_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $(SO_SUPPRESS_SYMBOLS) ++CXX_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $(SO_SUPPRESS_SYMBOLS) + CC_LIBS=$(PTHREAD_LIBS) $(LIBS) + CXX_LIBS=$(PTHREAD_LIBS) $(LIBS) + +Index: cdr/cdr_radius.c +=================================================================== +--- a/cdr/cdr_radius.c (.../tags/1.6.2.0-beta1) (revision 186562) ++++ b/cdr/cdr_radius.c (.../trunk) (revision 186562) +@@ -205,13 +205,19 @@ + + if (build_radius_record(&tosend, cdr)) { + ast_debug(1, "Unable to create RADIUS record. CDR not recorded!\n"); +- return result; ++ goto return_cleanup; + } + + result = rc_acct(rh, 0, tosend); +- if (result != OK_RC) ++ if (result != OK_RC) { + ast_log(LOG_ERROR, "Failed to record Radius CDR record!\n"); ++ } + ++return_cleanup: ++ if (tosend) { ++ rc_avpair_free(tosend); ++ } ++ + return result; + } + + +Property changes on: . +___________________________________________________________________ +Deleted: trunk-blocked + - /trunk:182362,182521,182762,182960,183124,183148 +Deleted: trunk-merged + - /trunk:1-182359,182408,182450,182525,182530,182553,182722,182847,183028,183057,183108,183117 +Added: branch-1.4-blocked + + /branches/1.4:43484,43510,43582,43626,43703,43756,44023,44080,44109,44407,44409,44567,44650,44660-44662,44665,44746,44760,44776-44777,44805,44992,45246,45313-45314,45381,45464,46114-46117,46165,46214,46253,46255,46401,46431,46583,46716,46883,47107-47108,47323,47329,47344,47348,47369,47444,47542,47553,47558,47564,47573,47576,47613,47736,47762,47856,47892,48046,48147,48162,48195,48234,48270,48281,48561,48583,48931,49022,49024,49032,49035,49070,49096,49189,49212,49282,49388,49710,50186,50921,51245,51755,51895,52000,52158,52373,52535,52809,52856,53077,53086,53099,53818,53878,54026,56006,56922,57591,58939,58955,59042,59069-59070,59230,60709,61220,62095,62299,62739,62913,63283,63656,64280,64565,64605,65394,65452,66068,66160,66244,66312,66602,66637,67018,67372,72453,72462,72493,72554,72597,72599,72665,73143,74262,74628,74839,75437,75439,75447,75712,75980,76054,76176,76178,76227,76891,77871,78146,78416,78620,78826,78860,80086,80088,80167,80497,80689,80747,81375,81492,82198,82276,82398,83400,83653,84163,84437,84544,85397,85536,85548,85687,85717,85852,86028,86371-86372,86406,87067,87340,87534,88471,89111,89191,89254,89339,89616,89727,89999,90147,90546,90798,91032,91501,92200,92510,93000,93183,93420,93764,94466,94543,94765,94769,94831,96020,96022,96024,97206,98025,98082,98265,98734,98849,98972-98973,98982,99127,99878,100418,100673,100934,101820,102653,103503,103556,103607,103698,103701,103703,103709,103713,103768,103807,104026-104027,104111,104332,104591,105591,107472,107582,109057,109171,109393,110880,112689,112709,113507,114032,114072,114167,114173,114180,114211,114248,114297,114299,114522,114542,114545,114550,114649,115257,115517,117523,117809,118055,119076,120061,120371,120671,120731,121992,122208,122314,122613,122663,124743,124908,125530,125893,126395,126680,127069,127501,127754,128029,128637,129158,129208,129505,130042,130298,130317,130373,131480,132042,132784,132787,132974,133237,133709,134652,134704,134976,136238,136304,136348,136404,136458,136560,137348,137527,137529,137580,137677,137850,138309,138516,138569,138938,138949,139145,139521,139769,140050,140115,141217,141267,141678,143204,143270,143475,143674,143964,144420,144758,145839,146129,146244,147941,148990,149840,150557,150816,151100,151167,151763,154724,158306,158629,159158,159571,160570,160764,162071,162670-162671,163785,164082,164204,164343,165537,165991,166157,166262,166592,168598,169581,169797,171120,171122,171452,172639,173248,173770,173900,174644,174885,175407,175698,177160,177450,177696,178640,178838,180010,181133,182652,182963,182965,184980,185298,185531,186057 +Added: branch-1.4-merged + + /branches/1.4:1-43376,43383,43386,43388,43392,43396,43405,43410,43422,43441,43445,43450,43454,43456,43464,43466,43469,43477,43482,43486,43489,43492,43518,43524,43553,43564,43616,43635-43702,43704-43755,43757-43800,43802-43846,43852,43861-43862,43864,43873,43877,43893,43898-43899,43913,43915,43918-43919,43933,43944,43952,43978,43993,43996-44012,44022,44034-44043,44053,44055,44057,44068,44078,44090,44111,44125,44135,44166-44167,44169,44186,44199,44215,44283-44286,44298,44312,44322,44378,44390,44393,44433,44436,44450,44476,44486,44502,44559,44561-44563,44581,44605,44628,44631,44684,44759,44764,44786,44788,44806,44808-44809,44819,44888,44911,44921,44942,44945,44956,44971,44982,44994,45026-45027,45031,45040,45049,45051,45066,45079,45088,45104,45106,45125,45196,45213,45262,45280,45327,45378,45408,45410,45439,45441,45452,45517,45595,45622,45646,45678,45692,45694,45741,45775,45817-45818,45916,45928,45999,46065,46067,46078,46080,46082-46113,46118-46141,46143-46154,46200,46216,46237,46249,46252,46276,46298,46329,46347,46351-46353,46358,46363,46367,46370,46377,46382,46389,46398,46403,46407,46433,46474,46506,46511,46526,46554,46558,46561,46563,46606,46628,46631,46714,46744,46775,46778,46780,46822,46845,46847,46857,46901,46930,46937,46965,46992,47015,47051,47053,47192,47195-47196,47199,47239,47250,47268,47279,47284,47287,47309,47327,47331,47333,47352,47366,47372,47375,47377,47380,47391,47398,47405,47414,47418,47432-47433,47436,47454,47457,47463,47466,47474,47476,47492,47494,47497,47507,47509,47511,47513,47523,47526-47527,47540,47551,47572,47581,47584,47597,47617,47621,47625,47628,47632,47635,47639,47641,47645,47656,47684,47690,47693,47698,47701,47707,47709,47712,47733,47744,47748,47751,47755,47758,47764,47777,47782,47823,47843,47845,47850,47852,47860,47864-47865,47897,47944,47959,47989,47992,48002,48015,48017,48031,48038,48049,48054,48088,48095,48101,48105,48107,48113,48115,48129,48135,48143,48152,48155,48158,48166,48168,48177,48179,48186,48190,48193,48199,48219,48223,48228,48230,48248,48252,48254,48264,48279,48317,48323,48326,48349,48357,48363,48372,48375,48377,48379,48381-48382,48391,48396,48399,48401,48427,48461,48472,48478,48481,48487,48502,48504,48506,48513,48521,48525,48528,48548,48554,48564,48571,48577,48586,48592,48596,48637,48783,48870,48888,48906,48944,48948,48956,48960,48964,48966,48975,48977,48980,48982,48985,48987-48988,48993,48995,48997,49006,49009,49024,49028,49046,49061,49063,49066,49073,49098-49099,49102,49145,49165,49237,49259,49313,49355,49413,49457-49461,49465,49523,49536,49551,49553,49581,49600,49636,49680,49705,49712,49714-49715,49742,49831,49834,49866,49890,49925,49945,49983,50006,50032,50073,50098,50124,50151,50228,50266,50298,50346,50377,50405,50433,50466,50468,50562,50602,50647,50674,50727,50754,50782,50820,50867,50895,50957,50994,51030,51057,51087,51146,51148,51150,51159,51162,51165,51167,51170,51172,51176,51182,51186,51195,51198,51204-51205,51211,51213,51233,51236,51241,51243,51251,51256,51262,51265,51272,51274,51311,51326,51328,51331,51339,51341,51343,51348,51350,51407,51409,51513,51558,51615,51683,51716,51750,51781,51788,51829,51848,51931,51989,52016,52049,52052,52107,52160,52163,52208,52210,52265,52335,52370,52416,52462,52494-52506,52523,52572,52611,52645,52647,52679,52688,52695,52717,52763,52807-52808,52904,52952,52997,52999,53001,53035,53037,53040,53042,53046,53050,53052,53057,53062,53064,53070,53072,53075,53079,53081,53085,53088,53093,53097,53104,53109,53114,53118,53120,53131,53136,53138,53143,53150,53152,53246,53294,53324,53355,53358,53399,53429,53434,53464,53497,53530,53532,53601,53715,53749,53779-53781,53783,53810,53850,53879-53881,54002,54066,54103,54204,54218,54235,54290,54375,54481,54623,54714,54772,54787,54884,54886,54888,54898,54924,54969,55002,55006,55050,55052,55086,55129,55154,55217,55219,55278,55397,55435,55483,55553,55555,55590,55634,55670,55688,55717,55741,55758,55799,55834,55869,55914,55947,55949,55951,55954,55957,56008,56011,56055,56094,56125,56231,56277,56341,56372,56407,56457,56505,56569,56685,56740,56783,56785,56805,56839,56847,56856,56888,56975,57049,57053,57055,57089,57093,57139,57144,57146,57203,57207,57318,57364,57396,57426,57473,57477,57556,57649,57768,57770,57798,57826,57870,57872,57914,58023,58053,58119,58121,58165,58240,58243,58320,58351-58352,58354,58389,58436,58474,58479,58510,58512,58584,58604,58638,58669,58705,58779,58783,58825-58826,58843,58845,58848,58902,58906,58923,58931,58933,58935,58937,58941,58946-58947,58953,58957,58992,59035,59037,59040,59049,59064,59076,59078,59081,59087,59089,59145,59180,59182,59188,59195,59200,59202,59206,59213,59215,59217,59223,59225,59228,59254,59256,59259,59261-59262,59273,59275,59278,59281,59284,59289,59302,59304,59341,59358,59361,59363,59452,59486,59522,59573,59654,59688,59724,59774,59804,59853,59887,59936,59939,59963,60069,60088,60112,60137,60214,60265,60268,60323,60325,60361,60399,60459,60485,60521,60565,60603,60661,60712-60713,60762,60798,60847,60850,60936,60984,60989,61183,61342-61443,61477,61641,61644-61645,61648,61651,61656,61658,61674,61676,61678,61681,61683,61686,61690,61694,61697,61705,61707,61763,61765,61772,61774,61779,61787,61799,61805,61863,61870,61914,61959,61961,62005,62038,62137,62171,62174,62218,62331,62369,62371,62414,62419,62497,62545,62548,62624,62689,62692,62738,62789,62797-62807,62842,62883,62912,62942,62986,62989,63047,63099,63152,63254,63286,63329,63360,63403,63445,63448,63478,63532,63534-63535,63566,63608,63611-63612,63698,63749,63804,63830,63872,63886,63905,63982,64044,64086,64114,64157,64193,64240,64276,64278,64306,64324,64353,64426,64515-64516,64543,64578,64602,64686,64720,64754,64756,64759,64761,64820,64868,64904,64974,65039,65076,65123,65200-65201,65250,65342,65408,65501,65541,65589,65677,65679-65680,65683,65685,65768,65836,65839,65841-65842,65853,65863,65866,65965-65967,65978,66026,66029-66030,66070,66074,66076,66157,66159,66363,66398,66404,66414,66437,66474,66503,66538,66671,66768,66770,66775,66821,66879,66881,66897,66916,66919,67020-67021,67026,67061,67064,67066,67068,67071,67073,67119,67121,67156,67158,67162,67210,67270,67304,67308,67329,67334,67360,67420,67457,67492,67526,67558,67594,67597,67626,67631,67650,67716,67804,67862,67872,67924,67941,67993,68027-68028,68030,68071,68157,68192,68198,68211,68249,68280,68313,68326,68354,68370,68401,68450,68527,68595,68644,68683,68733,68781,68814,68922,69010,69012,69014,69016,69069,69071,69128,69144,69181,69183-69184,69221,69259,69358,69392,69434,69470,69518,69558,69579,69625,69660-69661,69668,69689,69702,69708,69744,69775,69794,69796,69805,69847,69895,69944,69987,70003,70062,70084,70164,70198,70360,70397,70445,70494,70552,70554,70560,70612,70656,70677,70726-70727,70808,70841,70866,70883,70899,70949,71003,71063,71096,71106,71118,71120-71123,71214,71230,71289,71291,71362,71371,71412,71422,71430,71519,71522,71576,71657,71751,71796,71877,71915,71953,72006,72042,72112,72125,72148,72257,72260,72272,72328,72331,72335,72381,72383,72556,72705,72766,72806,72850-72852,72888,72926,72933,73005,73053,73208,73253,73316,73319,73355,73398,73400,73467,73512,73548,73551,73555,73598,73629,73675,73679,73696,73727,73769,73849,73930,73980,73985,74043,74045,74047,74082,74120,74122,74159,74162,74211,74265,74314,74317,74323,74374,74379,74388,74428,74476,74515,74572,74642,74722,74767,74815,74864,74866,74888,74922,74955,74997,75053,75067,75078,75108,75253,75306,75401,75403,75405,75441,75445,75450,75529,75583,75619,75621,75623,75658,75707,75711,75732,75749,75759,75807,75928,75969,75978,76067,76087,76132,76139,76174,76211,76485,76519,76561,76618,76620,76654,76656,76708,76801,76803,76937,76983,77071,77154,77176,77191,77318,77348,77350,77380,77410,77424-77429,77460,77490,77536,77540,77571,77768,77771,77778,77780,77783,77785,77788,77794-77795,77824,77827,77831,77844,77852,77854,77863,77865,77867,77869,77883,77886-77887,77890,77894,77939,77943,77945,77947,77949,77993,77996,78028,78063,78095,78101,78103,78242,78275,78371,78375,78415,78437,78450,78488,78569,78575,78646,78717,78749,78778,78859,78891,78907,78936,78951,78955,78995,79044,79049,79142,79174,79207,79214,79255,79397,79436,79470,79523,79527,79553,79642,79665,79690,79748,79756,79778,79792,79833,79857,79902,79904,79906,79912,79947,79998,80044,80047,80049,80130,80132,80166,80183,80255,80257,80302,80304,80330,80360,80362,80390,80424,80426,80469,80499,80501,80539,80547,80573,80661,80717,80722,80750,80789,80820,80849,80895,80932,80974,81010,81012,81042,81065,81074,81120,81158,81189,81226,81291,81331,81340,81342,81346,81349,81367,81369,81373,81379,81381,81383,81392,81395,81397,81401,81403,81405-81406,81410,81412,81415-81416,81418,81426,81433,81435,81437,81439,81442,81448,81453,81455,81520,81523,81525,81569,81599,81650,81682,81713,81743,81776,81778,81826,81832,81886,81923,81952,81997,82028,82091,82155,82236,82238,82240,82243,82245,82250,82252,82261,82263,82265,82267,82274,82278,82280,82285-82286,82291,82296,82309,82326,82335,82337,82339,82344,82346,82358,82376,82385,82394,82396,82435,82444,82514,82590-82592,82594,82644,82676,82751,82802,82834,82865,82867,82929,82961,82992,83023-83024,83070,83074,83121,83175,83177,83179,83230,83232,83246,83316,83348,83432,83558,83589,83637,83695,83773,83879,83910,83941,83943,83974,83976,84018,84049,84078,84133,84146,84158,84160,84166,84170,84206,84236,84239,84271,84274,84291,84370,84410,84474,84511,84581,84637,84690,84692,84742,84783,84818,84851,84890,84902,84957,84990,85023,85057,85093,85158,85195,85242,85276,85280,85316,85356,85515,85517,85523,85532-85533,85540,85543,85545,85552,85556,85559,85561,85571,85604,85647,85649,85684,85686,85720,85818,85850,85896,85921,85958,85994,85997,86032,86063,86066,86117,86149,86202,86237,86296,86328,86330,86405,86469,86471,86502,86598,86630,86661,86663,86694,86726,86750,86754,86756,86787,86836,86880-86881,86902,86936,86982,87069,87120,87168,87262,87294,87342,87373,87396,87460,87567,87571,87650,87686,87739,87775,87849,87852,87906,87908,87970,88026,88078,88116,88210,88283,88328,88366,88539,88585,88624,88671,88709,88719,88765,88768,88805,88826,88862,88931,88994,89032,89036-89037,89042,89045-89046,89053,89079,89088,89090,89093,89095,89097,89099,89101,89103,89105,89115,89119,89125,89169-89173,89184,89194,89205,89239,89241,89246,89248,89260,89275,89280-89281,89286,89288,89296,89298,89301-89302,89323,89325,89416,89419,89450,89457,89491,89493,89495,89527,89534,89536,89540,89545,89559,89571,89577,89580,89586-89587,89592,89594,89599,89610,89618,89622,89624,89630-89631,89634,89701,89709,89790,89837,89839,89844,89886,89893,90059,90098,90101,90142,90145,90154-90155,90160,90163,90166-90545,90547-90753,90876,90967,91070,91074,91192,91237,91273,91292,91366,91439,91450,91637,91675,91677,91693,91737,91777,91780,91783,91826,91828,91830,91890,92158,92202,92204,92323,92363,92443,92463,92617,92696,92803,92807,92809,92815,92875,92933-92934,92937,93180,93182,93250,93291,93336,93377,93381,93625,93668,93949,93955,94077,94122,94251,94256,94418,94420,94464,94468,94538,94540,94660,94763,94767,94789-94790,94793,94797,94801,94808,94824,94828-94829,94905,94924,94977,95024,95095,95191,95470,95577,95890,95946,96102,96198-96199,96318,96394,96449,96525,96573,96575,96644,96884,96932,97077,97093,97152,97192,97194-97195,97304,97308,97350,97410,97448,97450,97489,97491,97529,97575,97618,97622,97640,97645,97697,97734,97753,97847,97849,97889,97925,97973,97976,98164,98219,98315,98317,98325,98372,98390,98467,98733,98774,98894,98934,98943,98946,98951,98955,98958,98960,98964,98966,98991,99004,99079,99081,99187,99301,99341,99426,99540,99592,99594,99643,99652,99718,99775,99777,99923,99975,99977-99978,100138,100164,100264,100378,100465,100581,100624,100626,100629,100672,100675,100740,100793,100835,100882,100922,100930,100932,100973,101035,101080,101152,101216,101219,101222,101413-101414,101433,101480,101482,101531,101601,101649,101693,101772,101818,101822,101894,101942,101989,102090,102142,102214,102323,102378,102450,102453,102576,102651,102725,102807,102858,102968,103070,103120,103197,103315,103324,103385,103683,103688,103690,103722,103726,103728,103741,103763,103770,103780,103786,103790,103795,103801,103812,103821,103823,103845,103904,103953,103956,104015,104037,104082,104084,104086,104092,104094-104095,104102,104106,104119,104132,104135,104139,104141,104334,104536,104593,104596,104598,104625,104665,104704,104783,104787,104841,105059,105113,105116,105261,105326,105409,105557,105560,105563,105565,105568,105570,105572,105674,105676,105932,106015,106038,106235,106237,106328,106437,106552,106606,106635,106704,106788,106842,106895,106945,107016,107099,107102,107158,107161,107173,107230,107290,107352,107405,107408,107461,107464,107637,107646,107713-107714,107826,107877,108031,108083,108086,108135,108227,108288,108469,108530,108583,108682,108737,108792,108796,108961,109012,109107,109226,109309,109386,109575,109648,109713,109763,109838,109908,109973,110019,110035,110083,110163,110336,110395,110474,110614,110628,110635,110779,110962,111014,111020,111024,111049,111121,111126,111129,111245,111280,111341,111391,111442,111605,111658,111720,111856,112068,112125,112138,112204,112209,112393,112468,112599,112711,112766,112820,113012,113065,113117-113118,113296,113348,113399,113402,113454,113504,113596,113681,113784,113874,113927,114021,114029,114035,114045,114051,114063,114083,114100,114103,114106,114112,114117,114120,114133,114138,114148,114184,114191,114195,114198,114204,114207,114226,114230,114242,114257,114275,114278,114284,114322,114537,114558,114571,114579,114584,114587,114591,114594,114597,114600,114603,114608,114621,114624,114628,114632,114662,114673,114689,114695,114708,114823,114829,114848,114875,114880,114890-114891,115017,115102,115196,115276,115279,115282,115285,115304,115308,115312,115320,115327,115333,115341,115415,115418,115422,115512,115545,115551,115554,115557,115561,115565,115568,115579,115884,115944,115990,116038,116088,116230,116296,116352,116409,116463,116466,116799,116978,117081,117086,117135,117462,117479,117507,117514,117519,117574,117582,117899,118048,118052,118163,118251,118358,118365,118465,118509,118551,118558,118646,118858,118953-118954,118956,118961,119009,119012,119071,119156,119238,119301,119354,119404,119478,119530,119533,119585,119636,119687,119742,119838,119926,119929,120001,120168,120173,120226,120282,120285,120425,120513,120675,120863-120885,120908,120959,121078,121229,121280,121442,121495,121596,121751,121804,121861,122046,122127,122130,122137,122259,122311,122713,122869,122919,123110,123113,123271,123274,123333,123391,123485,123710,123769,123869,123883,123909,123930,124112,124182,124315,124372,124395,124450,124540,124910,124965,125132,125218,125276,125327,125384,125585,125587,125740,125793,126056,126516,126573,126735,126789,126844,126899,126902,126999,127068,127133,127244,127560,127663,127892-127895,127973,128639,128737,128795,128812,128856,128912,128950,129047,129149,129343,129436,129567,129741,129803,129907,129966-129967,129970,130039,130102,130169,130173,130236,130514,130573,130634,130735,130792,130889,130959,131012,131242,131299,131357,131369,131421,131491,131790,131915,131921,131970,131985,131988,132107,132112,132311,132571,132641-132642,132645,132704,132712-132713,132826,132872,133038,133101,133104,133169,133295,133488,133572,133578,133649,134161,134254,134352,134475,134480,134536,134540,134595,134649,134758,134883,134915,134983,135055,135058,135473,135479,135482,135536,135597,135747,135799,135841-135850,135899,135915,135949,136062,136190,136241,136484,136488,136726,136946,137138,137405,137530,137679,137731,137847,138023,138027,138119-138238,138258,138360,138886,138942,139015,139074,139213,139347,139387,139456,139466,139553,139621,139635,139764,139869,139909,139927,140051,140056,140060,140421,140488,140605,140670,140690,140747,140751,140816,140850,141028,141094,141156,141366,141503,141565,141741,141806,141809,142063,142079,142218,142354,142358,142416,142474,142575,142675,142740,142744,142807,142865,142927,143140,143337,143404,143534,143736,143903,144066,144238,144356,144677,144924-144925,145479,145751,146026,146448,146643,146711,146799,147193,147517,147681,147997,148257,148611,148736,148912,148916,148987,149061,149130,149200,149204,149207,149266,149452,149683,150124,150304,151240-151241,151905,152059,152215,152286,152368,152463,152535,152538-152539,152811,152922,152958,152992,153114,153337,153651,154060,154066,154263,154266,154365,154685,155011,155398,155553,155803,155861,156164,156167,156178,156229,156289,156294,156297,156386,156688,156755,156816,157104,157162-157163,157305,157365,157859,158053,158071,158126,158483,158539,158600,158603,159025,159246,159269,159316,159476,159808,159897,159900,159976,160003,160207,160297,160480,160551,160558,160703,160770,160943,161013,161287,161426,161725,161948,162013-162014,162136,162188,162204,162264-162265,162273,162286,162341,162348,162413,162463,162653,162659,162663,162738,162804,162874,162926,163080,163084,163088,163092,163253,163316,163383,163448,163511,163761,164201,164350,164416,164422,164605,164634,164672,164736,164806,164876,164881,164977,165317,165591,165661,165767,165796,165889,166093,166297,166380,166509,166568,166772,166953,167095,167179,167260,167299,167432,167541,167545,167554,167566,167714,167840,168128,168191,168198,168267,168480,168507,168516,168546,168551,168561,168593,168603,168608,168614,168622,168628,168716,168721,168745,168828,168975,169210,169364,169485,169722,169867,169943,170050,170147,170158,170239,170392,170504,170568,170588,170648,170671,170719,170836,170979,171187,171264,171527,171621,171837,171963,172030,172169,172438,172962,173066,173070,173211,173392,173396,173559,173592,173692,173696,173917,173967-173968,174082,174148,174218,174282,174369,174583,175029,175124,175187,175294,175311,175590,175777,175792,175825,175921,176029,176216,176249-176252,176254,176354,176426,176661,176701,177096,177225,177383,177536,177540,177701,177786,178141,178205,178373,178445,178508,178804,178956,179395,179461,179468,179532,179536,179608,179671,179741,179807,179840,180006,180194,180372,180380,180464,180532,180567,180941,181029-181031,181295,181328,181340,181423,181436,181655,181659-181660,181664,181768,181898,181990,182208,182281,182449,182808,182810,182882,183115,183123,183126,183145,183238,183241,183291,183319,183342,183386,183559,183700,183913,184078,184188,184388,184447,184565,184842,184947,185031,185120-185121,185196,185362,185468,185599,185771,185845,185952,186059,186081,186174,186229,186320,186415,186445,186458 +Modified: svn:externals + - menuselect http://svn.digium.com/svn/menuselect/tags/autotag_for_asterisk/1.6.2.0-beta1 + + + menuselect http://svn.digium.com/svn/menuselect/trunk + + |