Handle HOLD/RETRIEVE notifications
authorPaul Cadach <paul@odt.east.telecom.kz>
Thu, 28 Sep 2006 10:41:38 +0000 (10:41 +0000)
committerPaul Cadach <paul@odt.east.telecom.kz>
Thu, 28 Sep 2006 10:41:38 +0000 (10:41 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@43845 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channels/chan_h323.c
channels/h323/ast_h323.cxx
channels/h323/ast_h323.h
channels/h323/chan_h323.h
configs/h323.conf.sample

diff --git a/CHANGES b/CHANGES
index 9427e95..f9c6920 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -15,3 +15,5 @@ Changes since Asterisk 1.4-beta was branched:
   * Ability to use libcap to set high ToS bits when non-root
      on Linux. If configure is unable to find libcap then you
      can use --with-cap to specify the path.
+  * H323 remote hold notification support added (by NOTIFY message
+     and/or H.450 supplementary service)
index 0fad86f..836093f 100644 (file)
@@ -117,6 +117,7 @@ rfc2833_cb on_set_rfc2833_payload;
 hangup_cb on_hangup;
 setcapabilities_cb on_setcapabilities;
 setpeercapabilities_cb on_setpeercapabilities;
+onhold_cb on_hold;
 
 /* global debug flag */
 int h323debug;
@@ -866,7 +867,7 @@ static int oh323_indicate(struct ast_channel *c, int condition, const void *data
        ast_mutex_unlock(&pvt->lock);
 
        if (h323debug)
-               ast_log(LOG_DEBUG, "OH323: Indicating %d on %s\n", condition, token);
+               ast_log(LOG_DEBUG, "OH323: Indicating %d on %s (%s)\n", condition, token, c->name);
 
        switch(condition) {
        case AST_CONTROL_RINGING:
@@ -910,11 +911,14 @@ static int oh323_indicate(struct ast_channel *c, int condition, const void *data
                        free(token);
                return -1;
        case AST_CONTROL_HOLD:
+               h323_hold_call(token, 1);
+               /* We should start MOH only if remote party isn't provide audio for us */
                ast_moh_start(c, data, NULL);
                if (token)
                        free(token);
                return 0;
        case AST_CONTROL_UNHOLD:
+               h323_hold_call(token, 0);
                ast_moh_stop(c);
                if (token)
                        free(token);
@@ -1338,6 +1342,17 @@ static int update_common_options(struct ast_variable *v, struct call_options *op
                        options->tunnelOptions |= H323_TUNNEL_QSIG;
                else
                        ast_log(LOG_WARNING, "Invalid value %s for %s at line %d\n", v->value, v->name, v->lineno);
+       } else if (!strcasecmp(v->name, "hold")) {
+               if (!strcasecmp(v->value, "none"))
+                       options->holdHandling = ~0;
+               else if (!strcasecmp(v->value, "notify"))
+                       options->holdHandling |= H323_HOLD_NOTIFY;
+               else if (!strcasecmp(v->value, "q931only"))
+                       options->holdHandling |= H323_HOLD_NOTIFY | H323_HOLD_Q931ONLY;
+               else if (!strcasecmp(v->value, "h450"))
+                       options->holdHandling |= H323_HOLD_H450;
+               else
+                       ast_log(LOG_WARNING, "Invalid value %s for %s at line %d\n", v->value, v->name, v->lineno);
        } else
                return 1;
 
@@ -1364,6 +1379,7 @@ static struct oh323_user *build_user(char *name, struct ast_variable *v, struct
        user->ha = (struct ast_ha *)NULL;
        memcpy(&user->options, &global_options, sizeof(user->options));
        user->options.dtmfmode = 0;
+       user->options.holdHandling = 0;
        /* Set default context */
        strncpy(user->context, default_context, sizeof(user->context) - 1);
        if (user && !found)
@@ -1410,6 +1426,10 @@ static struct oh323_user *build_user(char *name, struct ast_variable *v, struct
        }
        if (!user->options.dtmfmode)
                user->options.dtmfmode = global_options.dtmfmode;
+       if (user->options.holdHandling == ~0)
+               user->options.holdHandling = 0;
+       else if (!user->options.holdHandling)
+               user->options.holdHandling = global_options.holdHandling;
        ASTOBJ_UNMARK(user);
        ast_free_ha(oldha);
        return user;
@@ -1472,6 +1492,7 @@ static struct oh323_peer *build_peer(const char *name, struct ast_variable *v, s
        peer->ha = NULL;
        memcpy(&peer->options, &global_options, sizeof(peer->options));
        peer->options.dtmfmode = 0;
+       peer->options.holdHandling = 0;
        peer->addr.sin_port = htons(h323_signalling_port);
        peer->addr.sin_family = AF_INET;
        if (!found && name)
@@ -1511,6 +1532,10 @@ static struct oh323_peer *build_peer(const char *name, struct ast_variable *v, s
        }
        if (!peer->options.dtmfmode)
                peer->options.dtmfmode = global_options.dtmfmode;
+       if (peer->options.holdHandling == ~0)
+               peer->options.holdHandling = 0;
+       else if (!peer->options.holdHandling)
+               peer->options.holdHandling = global_options.holdHandling;
        ASTOBJ_UNMARK(peer);
        ast_free_ha(oldha);
        return peer;
@@ -2450,6 +2475,32 @@ static void set_local_capabilities(unsigned call_reference, const char *token)
                ast_log(LOG_DEBUG, "Capabilities for connection %s is set\n", token);
 }
 
+static void remote_hold(unsigned call_reference, const char *token, int is_hold)
+{
+       struct oh323_pvt *pvt;
+
+       if (h323debug)
+               ast_log(LOG_DEBUG, "Setting %shold status for connection %s\n", (is_hold ? "" : "un"), token);
+
+       pvt = find_call_locked(call_reference, token);
+       if (!pvt)
+               return;
+       if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
+               if (is_hold)
+                       ast_queue_control(pvt->owner, AST_CONTROL_HOLD);
+               else
+                       ast_queue_control(pvt->owner, AST_CONTROL_UNHOLD);
+               ast_channel_unlock(pvt->owner);
+       }
+       else {
+               if (is_hold)
+                       pvt->newcontrol = AST_CONTROL_HOLD;
+               else
+                       pvt->newcontrol = AST_CONTROL_UNHOLD;
+       }
+       ast_mutex_unlock(&pvt->lock);
+}
+
 static void *do_monitor(void *data)
 {
        int res;
@@ -2767,6 +2818,7 @@ static int reload_config(int is_reload)
        global_options.dtmfcodec[0] = H323_DTMF_RFC2833_PT;
        global_options.dtmfcodec[1] = H323_DTMF_CISCO_PT;
        global_options.dtmfmode = 0;
+       global_options.holdHandling = 0;
        global_options.capability = GLOBAL_CAPABILITY;
        global_options.bridge = 1;              /* Do native bridging by default */
        strncpy(default_context, "default", sizeof(default_context) - 1);
@@ -2861,6 +2913,10 @@ static int reload_config(int is_reload)
        }
        if (!global_options.dtmfmode)
                global_options.dtmfmode = H323_DTMF_RFC2833;
+       if (global_options.holdHandling == ~0)
+               global_options.holdHandling = 0;
+       else if (!global_options.holdHandling)
+               global_options.holdHandling = H323_HOLD_H450;
 
        for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
                if (strcasecmp(cat, "general")) {
@@ -3175,7 +3231,8 @@ static enum ast_module_load_result load_module(void)
                                                set_dtmf_payload,
                                                hangup_connection,
                                                set_local_capabilities,
-                                               set_peer_capabilities);
+                                               set_peer_capabilities,
+                                               remote_hold);
                /* start the h.323 listener */
                if (h323_start_listener(h323_signalling_port, bindaddr)) {
                        ast_log(LOG_ERROR, "Unable to create H323 listener.\n");
index be22ca6..826be72 100644 (file)
 #include <h323neg.h>
 #include <mediafmt.h>
 #include <lid.h>
+#ifdef H323_H450
+#include "h4501.h"
+#include "h4504.h"
+#include "h45011.h"
+#include "h450pdu.h"
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -528,10 +534,20 @@ MyH323Connection::MyH323Connection(MyH323EndPoint & ep, unsigned callReference,
                                                        unsigned options)
        : H323Connection(ep, callReference, options)
 {
+#ifdef H323_H450
+       /* Dispatcher will free out all registered handlers */
+       if (h450dispatcher)
+               delete h450dispatcher;
+       h450dispatcher = new H450xDispatcher(*this);
+       h4502handler = new H4502Handler(*this, *h450dispatcher);
+       h4504handler = new MyH4504Handler(*this, *h450dispatcher);
+       h4506handler = new H4506Handler(*this, *h450dispatcher);
+       h45011handler = new H45011Handler(*this, *h450dispatcher);
+#endif
        cause = -1;
        sessionId = 0;
        bridging = FALSE;
-       progressSetup = progressAlert = 0;
+       holdHandling = progressSetup = progressAlert = 0;
        dtmfMode = 0;
        dtmfCodec[0] = dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)0;
        redirect_reason = -1;
@@ -664,6 +680,7 @@ void MyH323Connection::SetCallOptions(void *o, BOOL isIncoming)
 
        progressSetup = opts->progress_setup;
        progressAlert = opts->progress_alert;
+       holdHandling = opts->holdHandling;
        dtmfCodec[0] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[0];
        dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[1];
        dtmfMode = opts->dtmfmode;
@@ -1641,6 +1658,48 @@ BOOL MyH323Connection::StartControlChannel(const H225_TransportAddress & h245Add
        return TRUE;
 }
 
+#ifdef H323_H450
+void MyH323Connection::OnReceivedLocalCallHold(int linkedId)
+{
+       if (on_hold)
+               on_hold(GetCallReference(), (const char *)GetCallToken(), 1);
+}
+
+void MyH323Connection::OnReceivedLocalCallRetrieve(int linkedId)
+{
+       if (on_hold)
+               on_hold(GetCallReference(), (const char *)GetCallToken(), 0);
+}
+#endif
+
+void MyH323Connection::MyHoldCall(BOOL isHold)
+{
+       if (((holdHandling & H323_HOLD_NOTIFY) != 0) || ((holdHandling & H323_HOLD_Q931ONLY) != 0)) {
+               PBYTEArray x ((const BYTE *)(isHold ? "\xF9" : "\xFA"), 1);
+               H323SignalPDU signal;
+               signal.BuildNotify(*this);
+               signal.GetQ931().SetIE((Q931::InformationElementCodes)39 /* Q931::NotifyIE */, x);
+               if (h323debug)
+                       cout << "Sending " << (isHold ? "HOLD" : "RETRIEVE") << " notification: " << signal << endl;
+               if ((holdHandling & H323_HOLD_Q931ONLY) != 0) {
+                       PBYTEArray rawData;
+                       signal.GetQ931().RemoveIE(Q931::UserUserIE);
+                       signal.GetQ931().Encode(rawData);
+                       signallingChannel->WritePDU(rawData);
+               } else
+                       WriteSignalPDU(signal);
+       }
+#ifdef H323_H450
+       if ((holdHandling & H323_HOLD_H450) != 0) {
+               if (isHold)
+                       h4504handler->HoldCall(TRUE);
+               else if (IsLocalHold())
+                       h4504handler->RetrieveCall();
+       }
+#endif
+}
+
+
 /* MyH323_ExternalRTPChannel */
 MyH323_ExternalRTPChannel::MyH323_ExternalRTPChannel(MyH323Connection & connection,
                                                        const H323Capability & capability,
@@ -1722,6 +1781,32 @@ BOOL MyH323_ExternalRTPChannel::OnReceivedAckPDU(const H245_H2250LogicalChannelA
        return FALSE;
 }
 
+#ifdef H323_H450
+MyH4504Handler::MyH4504Handler(MyH323Connection &_conn, H450xDispatcher &_disp)
+       :H4504Handler(_conn, _disp)
+{
+       conn = &_conn;
+}
+
+void MyH4504Handler::OnReceivedLocalCallHold(int linkedId)
+{
+       if (conn) {
+               conn->Lock();
+               conn->OnReceivedLocalCallHold(linkedId);
+               conn->Unlock();
+       }
+}
+
+void MyH4504Handler::OnReceivedLocalCallRetrieve(int linkedId)
+{
+       if (conn) {
+               conn->Lock();
+               conn->OnReceivedLocalCallRetrieve(linkedId);
+               conn->Unlock();
+       }
+}
+#endif
+
 
 /** IMPLEMENTATION OF C FUNCTIONS */
 
@@ -1780,7 +1865,8 @@ void h323_callback_register(setup_incoming_cb             ifunc,
                                                        rfc2833_cb                              dtmffunc,
                                                        hangup_cb                               hangupfunc,
                                                        setcapabilities_cb              capabilityfunc,
-                                                       setpeercapabilities_cb  peercapabilityfunc)
+                                                       setpeercapabilities_cb  peercapabilityfunc,
+                                                       onhold_cb                               holdfunc)
 {
        on_incoming_call = ifunc;
        on_outgoing_call = sfunc;
@@ -1796,6 +1882,7 @@ void h323_callback_register(setup_incoming_cb             ifunc,
        on_hangup = hangupfunc;
        on_setcapabilities = capabilityfunc;
        on_setpeercapabilities = peercapabilityfunc;
+       on_hold = holdfunc;
 }
 
 /**
@@ -2097,6 +2184,18 @@ void h323_native_bridge(const char *token, const char *them, char *capability)
 
 }
 
+int h323_hold_call(const char *token, int is_hold)
+{
+       MyH323Connection *conn = (MyH323Connection *)endPoint->FindConnectionWithLock(token);
+       if (!conn) {
+               cout << "ERROR: No connection found, this is bad" << endl;
+               return -1;
+       }
+       conn->MyHoldCall((BOOL)is_hold);
+       conn->Unlock();
+       return 0;
+}
+
 #undef cout
 #undef endl
 void h323_end_process(void)
index 7199b0f..f933217 100644 (file)
@@ -93,6 +93,11 @@ public:
        virtual BOOL HandleSignalPDU(H323SignalPDU &pdu);
        BOOL EmbedTunneledInfo(H323SignalPDU &pdu);
 #endif
+#ifdef H323_H450
+       virtual void OnReceivedLocalCallHold(int linkedId);
+       virtual void OnReceivedLocalCallRetrieve(int linkedId);
+#endif
+       void MyHoldCall(BOOL localHold);
 
        PString sourceAliases;
        PString destAliases;
@@ -108,6 +113,7 @@ public:
        int tunnelOptions;
 #endif
 
+       unsigned holdHandling;
        unsigned progressSetup;
        unsigned progressAlert;
        int cause;
@@ -156,6 +162,23 @@ public:
        void Main();
 };
 
+#ifdef H323_H450
+#include <h450pdu.h>
+
+class MyH4504Handler : public H4504Handler
+{
+       PCLASSINFO(MyH4504Handler, H4504Handler);
+
+public:
+       MyH4504Handler(MyH323Connection &_conn, H450xDispatcher &_disp);
+       virtual void OnReceivedLocalCallHold(int linkedId);
+       virtual void OnReceivedLocalCallRetrieve(int linkedId);
+
+private:
+       MyH323Connection *conn;
+};
+#endif
+
 #include "compat_h323.h"
 
 #endif /* !defined AST_H323_H */
index 13296ed..c8bd6a3 100644 (file)
 #define H323_TUNNEL_CISCO      (1 << 0)
 #define H323_TUNNEL_QSIG       (1 << 1)
 
+#define H323_HOLD_NOTIFY       (1 << 0)
+#define H323_HOLD_Q931ONLY     (1 << 1)
+#define H323_HOLD_H450         (1 << 2)
+
 /** call_option struct holds various bits
  *         of information for each call */
 typedef struct call_options {
@@ -58,6 +62,7 @@ typedef struct call_options {
        int                             bridge;
        int                             nat;
        int                             tunnelOptions;
+       int                             holdHandling;
        struct ast_codec_pref   prefs;
 } call_options_t;
 
@@ -184,6 +189,9 @@ extern setcapabilities_cb on_setcapabilities;
 typedef void (*setpeercapabilities_cb)(unsigned, const char *, int, struct ast_codec_pref *);
 extern setpeercapabilities_cb on_setpeercapabilities;
 
+typedef void (*onhold_cb)(unsigned, const char *, int);
+extern onhold_cb on_hold;
+
 /* debug flag */
 extern int h323debug;
 
@@ -224,7 +232,8 @@ extern "C" {
                                        rfc2833_cb,
                                        hangup_cb,
                                        setcapabilities_cb,
-                                       setpeercapabilities_cb);
+                                       setpeercapabilities_cb,
+                                       onhold_cb);
        int h323_set_capabilities(const char *, int, int, struct ast_codec_pref *, int);
        int h323_set_alias(struct oh323_alias *);
        int h323_set_gk(int, char *, char *);
@@ -249,6 +258,7 @@ extern "C" {
        int h323_answering_call(const char *token, int);
        int h323_soft_hangup(const char *data);
        int h323_show_codec(int fd, int argc, char *argv[]);
+       int h323_hold_call(const char *token, int);
 
 #ifdef __cplusplus
 }
index ef8ff30..e27f1d5 100644 (file)
@@ -104,9 +104,18 @@ port = 1720
 ; example, for Cisco CallManager when Q.SIG tunneling is enabled for a
 ; gateway where Asterisk lives.
 ; The option can be used multiple times, one option per line.
-;tunneling=none              ; Totally disable tunneling (default)
-;tunneling=cisco             ; Enable Cisco-specific tunneling
-;tunneling=qsig              ; Enable tunneling via Q.SIG messages
+;tunneling=none               ; Totally disable tunneling (default)
+;tunneling=cisco              ; Enable Cisco-specific tunneling
+;tunneling=qsig               ; Enable tunneling via Q.SIG messages
+;
+; Specify how to pass hold notification to remote party. Default is to
+; use H.450.4 supplementary service message.
+;hold=none                    ; Do not pass hold/retrieve notifications
+;hold=notify                  ; Use H.225 NOTIFY message
+;hold=q931only                ; Use stripped H.225 NOTIFY message (Q.931 part
+;                             ; only, usable for Cisco CallManager)
+;hold=h450                    ; Pass notification as H.450.4 supplementary
+;                             ; service
 ;
 ;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
 ; jbenable = yes              ; Enables the use of a jitterbuffer on the receiving side of a