Handle HOLD/RETRIEVE notifications
[asterisk/asterisk.git] / channels / h323 / ast_h323.cxx
index 07352a6..826be72 100644 (file)
 #include <ptlib.h>
 #include <h323.h>
 #include <h323pdu.h>
+#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" {
@@ -53,6 +60,7 @@ extern "C" {
 #include "chan_h323.h"
 #include "ast_h323.h"
 #include "cisco-h225.h"
+#include "caps_h323.h"
 
 /* PWlib Required Components  */
 #define MAJOR_VERSION 1
@@ -210,240 +218,6 @@ void PAssertFunc(const char *msg)
        /* XXX: Probably we need to crash here */
 }
 
-#define DEFINE_G711_CAPABILITY(cls, code, capName) \
-class cls : public AST_G711Capability { \
-  public: \
-    cls() : AST_G711Capability(240, code) { } \
-}; \
-H323_REGISTER_CAPABILITY(cls, capName) \
-
-DEFINE_G711_CAPABILITY(AST_G711ALaw64Capability, H323_G711Capability::ALaw, OPAL_G711_ALAW_64K);
-DEFINE_G711_CAPABILITY(AST_G711uLaw64Capability, H323_G711Capability::muLaw, OPAL_G711_ULAW_64K);
-H323_REGISTER_CAPABILITY(AST_G7231Capability, OPAL_G7231);
-H323_REGISTER_CAPABILITY(AST_G729Capability,  OPAL_G729);
-H323_REGISTER_CAPABILITY(AST_G729ACapability, OPAL_G729A);
-H323_REGISTER_CAPABILITY(AST_GSM0610Capability, OPAL_GSM0610);
-
-/*
- * Capability: G.711
- */
-AST_G711Capability::AST_G711Capability(int rx_frames, H323_G711Capability::Mode m, H323_G711Capability::Speed s)
-       : H323AudioCapability(rx_frames, 30) // 240ms max, 30ms desired
-{
-       mode = m;
-       speed = s;
-}
-
-
-PObject * AST_G711Capability::Clone() const
-{
-       return new AST_G711Capability(*this);
-}
-
-
-unsigned AST_G711Capability::GetSubType() const
-{
-       static const unsigned G711SubType[2][2] = {
-               { H245_AudioCapability::e_g711Alaw64k, H245_AudioCapability::e_g711Alaw56k },
-               { H245_AudioCapability::e_g711Ulaw64k, H245_AudioCapability::e_g711Ulaw56k }
-       };
-       return G711SubType[mode][speed];
-}
-
-
-PString AST_G711Capability::GetFormatName() const
-{
-       static const char * const G711Name[2][2] = {
-               { OPAL_G711_ALAW_64K, OPAL_G711_ALAW_56K },
-               { OPAL_G711_ULAW_64K, OPAL_G711_ULAW_56K },
-       };
-       return G711Name[mode][speed];
-}
-
-
-H323Codec * AST_G711Capability::CreateCodec(H323Codec::Direction direction) const
-{
-       return NULL;
-}
-
-
-/*
- * Capability: G.723.1
- */
-AST_G7231Capability::AST_G7231Capability(int rx_frames, BOOL annexA_)
-       : H323AudioCapability(rx_frames, 4)
-{
-       annexA = annexA_;
-}
-
-PObject::Comparison AST_G7231Capability::Compare(const PObject & obj) const
-{
-       Comparison result = H323AudioCapability::Compare(obj);
-       if (result != EqualTo) {
-               return result;
-       }
-       PINDEX otherAnnexA = ((const AST_G7231Capability &)obj).annexA;
-       if (annexA < otherAnnexA) {
-               return LessThan;
-       }
-       if (annexA > otherAnnexA) {
-               return GreaterThan;
-       }
-       return EqualTo;
-}
-
-PObject * AST_G7231Capability::Clone() const
-{
-       return new AST_G7231Capability(*this);
-}
-
-PString AST_G7231Capability::GetFormatName() const
-{
-       return (annexA ? OPAL_G7231 "A" : OPAL_G7231);
-}
-
-unsigned AST_G7231Capability::GetSubType() const
-{
-       return H245_AudioCapability::e_g7231;
-}
-
-BOOL AST_G7231Capability::OnSendingPDU(H245_AudioCapability & cap,
-                                                                               unsigned packetSize) const
-{
-       cap.SetTag(H245_AudioCapability::e_g7231);
-       H245_AudioCapability_g7231 & g7231 = cap;
-       g7231.m_maxAl_sduAudioFrames = packetSize;
-       g7231.m_silenceSuppression = annexA;
-       return TRUE;
-}
-
-BOOL AST_G7231Capability::OnReceivedPDU(const H245_AudioCapability & cap,
-                                                                               unsigned & packetSize)
-{
-       if (cap.GetTag() != H245_AudioCapability::e_g7231) {
-               return FALSE;
-       }
-       const H245_AudioCapability_g7231 & g7231 = cap;
-       packetSize = g7231.m_maxAl_sduAudioFrames;
-       annexA = g7231.m_silenceSuppression;
-       return TRUE;
-}
-
-H323Codec * AST_G7231Capability::CreateCodec(H323Codec::Direction direction) const
-{
-       return NULL;
-}
-
-/*
- * Capability: G.729
- */
-AST_G729Capability::AST_G729Capability(int rx_frames)
-       : H323AudioCapability(rx_frames, 2)
-{
-}
-
-PObject * AST_G729Capability::Clone() const
-{
-       return new AST_G729Capability(*this);
-}
-
-unsigned AST_G729Capability::GetSubType() const
-{
-       return H245_AudioCapability::e_g729;
-}
-
-PString AST_G729Capability::GetFormatName() const
-{
-       return OPAL_G729;
-}
-
-H323Codec * AST_G729Capability::CreateCodec(H323Codec::Direction direction) const
-{
-       return NULL;
-}
-
-/*
- * Capability: G.729A
- */
-AST_G729ACapability::AST_G729ACapability(int rx_frames)
-       : H323AudioCapability(rx_frames, 6)
-{
-}
-
-PObject * AST_G729ACapability::Clone() const
-{
-       return new AST_G729ACapability(*this);
-}
-
-unsigned AST_G729ACapability::GetSubType() const
-{
-       return H245_AudioCapability::e_g729AnnexA;
-}
-
-PString AST_G729ACapability::GetFormatName() const
-{
-       return OPAL_G729A;
-}
-
-H323Codec * AST_G729ACapability::CreateCodec(H323Codec::Direction direction) const
-{
-       return NULL;
-}
-
-/*
- * Capability: GSM full rate
- */
-AST_GSM0610Capability::AST_GSM0610Capability(int rx_frames, int comfortNoise_, int scrambled_)
-       : H323AudioCapability(rx_frames, 2)
-{
-       comfortNoise = comfortNoise_;
-       scrambled = scrambled_;
-}
-
-PObject * AST_GSM0610Capability::Clone() const
-{
-       return new AST_GSM0610Capability(*this);
-}
-
-unsigned AST_GSM0610Capability::GetSubType() const
-{
-       return H245_AudioCapability::e_gsmFullRate;
-}
-
-BOOL AST_GSM0610Capability::OnSendingPDU(H245_AudioCapability & cap,
-                                                                               unsigned packetSize) const
-{
-       cap.SetTag(H245_AudioCapability::e_gsmFullRate);
-       H245_GSMAudioCapability & gsm = cap;
-       gsm.m_audioUnitSize = packetSize * 33;
-       gsm.m_comfortNoise = comfortNoise;
-       gsm.m_scrambled = scrambled;
-       return TRUE;
-}
-
-BOOL AST_GSM0610Capability::OnReceivedPDU(const H245_AudioCapability & cap,
-                                                                               unsigned & packetSize)
-{
-       if (cap.GetTag() != H245_AudioCapability::e_gsmFullRate)
-               return FALSE;
-       const H245_GSMAudioCapability & gsm = cap;
-       packetSize = (gsm.m_audioUnitSize + 32) / 33;
-       comfortNoise = gsm.m_comfortNoise;
-       scrambled = gsm.m_scrambled;
-
-       return TRUE;
-}
-
-PString AST_GSM0610Capability::GetFormatName() const
-{
-       return OPAL_GSM0610;
-}
-
-H323Codec * AST_GSM0610Capability::CreateCodec(H323Codec::Direction direction) const
-{
-       return NULL;
-}
-
 
 /** MyH323EndPoint
   */
@@ -760,12 +534,22 @@ 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 = (RTP_DataFrame::PayloadTypes)0;
+       dtmfCodec[0] = dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)0;
        redirect_reason = -1;
 #ifdef TUNNELLING
        tunnelOptions = remoteTunnelOptions = 0;
@@ -896,7 +680,9 @@ void MyH323Connection::SetCallOptions(void *o, BOOL isIncoming)
 
        progressSetup = opts->progress_setup;
        progressAlert = opts->progress_alert;
-       dtmfCodec = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec;
+       holdHandling = opts->holdHandling;
+       dtmfCodec[0] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[0];
+       dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[1];
        dtmfMode = opts->dtmfmode;
 
        if (isIncoming) {
@@ -1338,6 +1124,14 @@ BOOL MyH323Connection::OnReceivedSignalSetup(const H323SignalPDU & setupPDU)
 
        SetCallOptions(res, TRUE);
 
+       /* Disable fastStart if requested by remote side */
+       if (h245Tunneling && !setupPDU.m_h323_uu_pdu.m_h245Tunneling) {
+               masterSlaveDeterminationProcedure->Stop();
+               capabilityExchangeProcedure->Stop();
+               PTRACE(3, "H225\tFast Start DISABLED!");
+               h245Tunneling = FALSE;
+       }
+
        return H323Connection::OnReceivedSignalSetup(setupPDU);
 }
 
@@ -1437,7 +1231,8 @@ void MyH323Connection::SendUserInputTone(char tone, unsigned duration, unsigned
 
 void MyH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp)
 {
-       if (dtmfMode == H323_DTMF_RFC2833) {
+       /* Why we should check this? */
+       if ((dtmfMode & (H323_DTMF_CISCO | H323_DTMF_RFC2833 | H323_DTMF_SIGNAL)) != 0) {
                if (h323debug) {
                        cout << "\t-- Received user input tone (" << tone << ") from remote" << endl;
                }
@@ -1467,10 +1262,10 @@ void MyH323Connection::OnSendCapabilitySet(H245_TerminalCapabilitySet & pdu)
                        H245_Capability & cap = entry.m_capability;
                        if (cap.GetTag() == H245_Capability::e_receiveRTPAudioTelephonyEventCapability) {
                                H245_AudioTelephonyEventCapability & atec = cap;
-                               atec.m_dynamicRTPPayloadType = dtmfCodec;
-//                             on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)dtmfCodec);
+                               atec.m_dynamicRTPPayloadType = dtmfCodec[0];
+//                             on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)dtmfCodec[0]);
                                if (h323debug) {
-                                       cout << "\t-- Transmitting RFC2833 on payload " <<
+                                       cout << "\t-- Receiving RFC2833 on payload " <<
                                                atec.m_dynamicRTPPayloadType << endl;
                                }
                        }
@@ -1492,6 +1287,7 @@ BOOL MyH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCa
                unsigned int asterisk_codec;
                unsigned int h245_cap;
                const char *oid;
+               const char *formatName;
        };
        static const struct __codec__ codecs[] = {
                { AST_FORMAT_G723_1, H245_AudioCapability::e_g7231 },
@@ -1500,6 +1296,7 @@ BOOL MyH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCa
                { AST_FORMAT_ALAW, H245_AudioCapability::e_g711Alaw64k },
                { AST_FORMAT_G729A, H245_AudioCapability::e_g729AnnexA },
                { AST_FORMAT_G729A, H245_AudioCapability::e_g729 },
+               { AST_FORMAT_G726_AAL2, H245_AudioCapability::e_nonStandard, NULL, CISCO_G726r32 },
 #ifdef AST_FORMAT_MODEM
                { AST_FORMAT_MODEM, H245_DataApplicationCapability_application::e_t38fax },
 #endif
@@ -1521,21 +1318,12 @@ BOOL MyH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCa
        };
 #endif
        struct ast_codec_pref prefs;
+       RTP_DataFrame::PayloadTypes pt;
 
        if (!H323Connection::OnReceivedCapabilitySet(remoteCaps, muxCap, reject)) {
                return FALSE;
        }
 
-       const H323Capability * cap = remoteCaps.FindCapability(H323_UserInputCapability::SubTypeNames[H323_UserInputCapability::SignalToneRFC2833]);
-       if (cap != NULL) {
-               RTP_DataFrame::PayloadTypes pt = ((H323_UserInputCapability*)cap)->GetPayloadType();
-               on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)pt);
-               if ((dtmfMode == H323_DTMF_RFC2833) && (sendUserInputMode == SendUserInputAsTone))
-                       sendUserInputMode = SendUserInputAsInlineRFC2833;
-               if (h323debug) {
-                       cout << "\t-- Inbound RFC2833 on payload " << pt << endl;
-               }
-       }
        memset(&prefs, 0, sizeof(prefs));
        int peer_capabilities = 0;
        for (int i = 0; i < remoteCapabilities.GetSize(); ++i) {
@@ -1546,7 +1334,7 @@ BOOL MyH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCa
                switch(remoteCapabilities[i].GetMainType()) {
                case H323Capability::e_Audio:
                        for (int x = 0; codecs[x].asterisk_codec > 0; ++x) {
-                               if (subType == codecs[x].h245_cap) {
+                               if ((subType == codecs[x].h245_cap) && (!codecs[x].formatName || (!strcmp(codecs[x].formatName, (const char *)remoteCapabilities[i].GetFormatName())))) {
                                        int ast_codec = codecs[x].asterisk_codec;
                                        int ms = 0;
                                        if (!(peer_capabilities & ast_codec)) {
@@ -1568,6 +1356,32 @@ BOOL MyH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCa
                                }
                        }
                        break;
+               case H323Capability::e_Data:
+                       if (!strcmp((const char *)remoteCapabilities[i].GetFormatName(), CISCO_DTMF_RELAY)) {
+                               pt = remoteCapabilities[i].GetPayloadType();
+                               if ((dtmfMode & H323_DTMF_CISCO) != 0) {
+                                       on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)pt, 1);
+//                                     if (sendUserInputMode == SendUserInputAsTone)
+//                                             sendUserInputMode = SendUserInputAsInlineRFC2833;
+                               }
+                               if (h323debug) {
+                                       cout << "\t-- Outbound Cisco RTP DTMF on payload " << pt << endl;
+                               }
+                       }
+                       break;
+               case H323Capability::e_UserInput:
+                       if (!strcmp((const char *)remoteCapabilities[i].GetFormatName(), H323_UserInputCapability::SubTypeNames[H323_UserInputCapability::SignalToneRFC2833])) {
+                               pt = remoteCapabilities[i].GetPayloadType();
+                               if ((dtmfMode & H323_DTMF_RFC2833) != 0) {
+                                       on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)pt, 0);
+//                                     if (sendUserInputMode == SendUserInputAsTone)
+//                                             sendUserInputMode = SendUserInputAsInlineRFC2833;
+                               }
+                               if (h323debug) {
+                                       cout << "\t-- Outbound RFC2833 on payload " << pt << endl;
+                               }
+                       }
+                       break;
 #if 0
                case H323Capability::e_Video:
                        for (int x = 0; vcodecs[x].asterisk_codec > 0; ++x) {
@@ -1638,7 +1452,7 @@ BOOL MyH323Connection::OnStartLogicalChannel(H323Channel & channel)
        return connectionState != ShuttingDownConnection;
 }
 
-void MyH323Connection::SetCapabilities(int cap, int dtmf_mode, void *_prefs, int pref_codec)
+void MyH323Connection::SetCapabilities(int caps, int dtmf_mode, void *_prefs, int pref_codec)
 {
        PINDEX lastcap = -1; /* last common capability index */
        int alreadysent = 0;
@@ -1649,11 +1463,12 @@ void MyH323Connection::SetCapabilities(int cap, int dtmf_mode, void *_prefs, int
        struct ast_format_list format;
        int frames_per_packet;
        int max_frames_per_packet;
+       H323Capability *cap;
 
        localCapabilities.RemoveAll();
 
        if (h323debug) {
-               cout << "Setting capabilities to " << ast_getformatname_multiple(caps_str, sizeof(caps_str), cap) << endl;
+               cout << "Setting capabilities to " << ast_getformatname_multiple(caps_str, sizeof(caps_str), caps) << endl;
                ast_codec_pref_string(prefs, caps_str, sizeof(caps_str));
                cout << "Capabilities in preference order is " << caps_str << endl;
        }
@@ -1671,7 +1486,7 @@ void MyH323Connection::SetCapabilities(int cap, int dtmf_mode, void *_prefs, int
                                y <<= 1;
                        codec = y;
                }
-               if (!(cap & codec) || (alreadysent & codec) || !(codec & AST_FORMAT_AUDIO_MASK))
+               if (!(caps & codec) || (alreadysent & codec) || !(codec & AST_FORMAT_AUDIO_MASK))
                        continue;
                alreadysent |= codec;
                format = ast_codec_pref_getsize(prefs, codec);
@@ -1728,29 +1543,76 @@ void MyH323Connection::SetCapabilities(int cap, int dtmf_mode, void *_prefs, int
                        if (format.max_ms)
                                g711aCap->SetTxFramesInPacket(format.max_ms);
                        break;
+               case AST_FORMAT_G726_AAL2:
+                       AST_CiscoG726Capability *g726Cap;
+                       lastcap = localCapabilities.SetCapability(0, 0, g726Cap = new AST_CiscoG726Capability(frames_per_packet));
+                       if (max_frames_per_packet)
+                               g726Cap->SetTxFramesInPacket(max_frames_per_packet);
+                       break;
                default:
                        alreadysent &= ~codec;
                        break;
                }
        }
 
-       lastcap++;
-       lastcap = localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::HookFlashH245));
+       cap = new H323_UserInputCapability(H323_UserInputCapability::HookFlashH245);
+       if (cap && cap->IsUsable(*this)) {
+               lastcap++;
+               lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+       } else if (cap)
+               delete cap;                             /* Capability is not usable */
 
-       lastcap++;
        dtmfMode = dtmf_mode;
-       if (dtmf_mode == H323_DTMF_INBAND) {
-               localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::BasicString));
-               sendUserInputMode = SendUserInputAsString;
-       } else {
-               lastcap = localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::SignalToneRFC2833));
-               /* Cisco sends DTMF only through h245-alphanumeric or h245-signal, no support for RFC2833 */
-               lastcap = localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::SignalToneH245));
-               sendUserInputMode = SendUserInputAsTone;        /* RFC2833 transmission handled at Asterisk level */
+       if (h323debug) {
+               cout << "DTMF mode is " << (int)dtmfMode << endl;
+       }
+       if (dtmfMode) {
+               lastcap++;
+               if (dtmfMode == H323_DTMF_INBAND) {
+                       cap = new H323_UserInputCapability(H323_UserInputCapability::BasicString);
+                       if (cap && cap->IsUsable(*this)) {
+                               lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+                       } else if (cap)
+                               delete cap;             /* Capability is not usable */
+                       sendUserInputMode = SendUserInputAsString;
+               } else {
+                       if ((dtmfMode & H323_DTMF_RFC2833) != 0) {
+                               cap = new H323_UserInputCapability(H323_UserInputCapability::SignalToneRFC2833);
+                               if (cap && cap->IsUsable(*this))
+                                       lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+                               else {
+                                       dtmfMode |= H323_DTMF_SIGNAL;
+                                       if (cap)
+                                               delete cap;     /* Capability is not usable */
+                               }
+                       }
+                       if ((dtmfMode & H323_DTMF_CISCO) != 0) {
+                               /* Try Cisco's RTP DTMF relay too, but prefer RFC2833 or h245-signal */
+                               cap = new AST_CiscoDtmfCapability();
+                               if (cap && cap->IsUsable(*this)) {
+                                       lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+                                       /* We cannot send Cisco RTP DTMFs, use h245-signal instead */
+                                       dtmfMode |= H323_DTMF_SIGNAL;
+                               } else {
+                                       dtmfMode |= H323_DTMF_SIGNAL;
+                                       if (cap)
+                                               delete cap;     /* Capability is not usable */
+                               }
+                       }
+                       if ((dtmfMode & H323_DTMF_SIGNAL) != 0) {
+                               /* Cisco usually sends DTMF correctly only through h245-alphanumeric or h245-signal */
+                               cap = new H323_UserInputCapability(H323_UserInputCapability::SignalToneH245);
+                               if (cap && cap->IsUsable(*this))
+                                       lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+                               else if (cap)
+                                       delete cap;     /* Capability is not usable */
+                       }
+                       sendUserInputMode = SendUserInputAsTone;        /* RFC2833 transmission handled at Asterisk level */
+               }
        }
 
        if (h323debug) {
-               cout << "Allowed Codecs:\n\t" << setprecision(2) << localCapabilities << endl;
+               cout << "Allowed Codecs for " << GetCallToken() << " (" << GetSignallingChannel()->GetLocalAddress() << "):\n\t" << setprecision(2) << localCapabilities << endl;
        }
 }
 
@@ -1796,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,
@@ -1877,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 */
 
@@ -1935,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;
@@ -1951,6 +1882,7 @@ void h323_callback_register(setup_incoming_cb             ifunc,
        on_hangup = hangupfunc;
        on_setcapabilities = capabilityfunc;
        on_setpeercapabilities = peercapabilityfunc;
+       on_hold = holdfunc;
 }
 
 /**
@@ -2252,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)