Index: channels/h323/caps_h323.cxx
===================================================================
--- channels/h323/caps_h323.cxx	(revision 320715)
+++ channels/h323/caps_h323.cxx	(working copy)
@@ -3,6 +3,7 @@
 #include <h245.h>
 #include "ast_h323.h"
 #include "caps_h323.h"
+#include "chan_h323.h"
 
 #define DEFINE_G711_CAPABILITY(cls, code, capName) \
 class cls : public AST_G711Capability { \
@@ -381,3 +382,199 @@
 {
 	return PString(CISCO_DTMF_RELAY);
 }
+
+/* Asterisk T.38 Capability */
+AST_T38Capability::AST_T38Capability(const struct t38_profile *profile) : H323_T38Capability(H323_T38Capability::e_UDP)
+{
+	SetProfile(profile);
+}
+
+PObject *AST_T38Capability::Clone() const
+{
+	return new AST_T38Capability(*this);
+}
+
+H323Channel *AST_T38Capability::CreateChannel(H323Connection &connection,
+				              H323Channel::Directions direction, unsigned sessionID,
+            				      const H245_H2250LogicalChannelParameters *param) const
+{
+	return new MyT38Channel(connection, *this, direction, sessionID);
+}
+
+void AST_T38Capability::SetProfile(const struct t38_profile* profile)
+{
+	m_profile.IncludeOptionalField(H245_T38FaxProfile::e_t38FaxUdpOptions);
+	m_profile.m_fillBitRemoval = profile->ast.fill_bit_removal;
+	m_profile.m_transcodingJBIG = profile->ast.transcoding_jbig;
+	m_profile.m_transcodingMMR = profile->ast.transcoding_mmr;
+	m_profile.m_version = profile->ast.version;
+
+	switch (profile->ast.rate_management) {
+	case AST_T38_RATE_MANAGEMENT_LOCAL_TCF:
+		m_profile.m_t38FaxRateManagement.SetTag(H245_T38FaxRateManagement::e_localTCF);
+		break;
+	case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF:
+		m_profile.m_t38FaxRateManagement.SetTag(H245_T38FaxRateManagement::e_transferredTCF);
+		break;
+	default:
+		break;	
+	}
+
+	if (profile->max_datagram) {
+	        m_profile.m_t38FaxUdpOptions.IncludeOptionalField(H245_T38FaxUdpOptions::e_t38FaxMaxDatagram);
+		m_profile.m_t38FaxUdpOptions.m_t38FaxMaxDatagram = profile->max_datagram;
+	} else
+		m_profile.m_t38FaxUdpOptions.RemoveOptionalField(H245_T38FaxUdpOptions::e_t38FaxMaxDatagram);
+
+	switch (profile->error_correction_scheme) {
+	case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+		m_profile.m_t38FaxUdpOptions.m_t38FaxUdpEC.SetTag(H245_T38FaxUdpOptions_t38FaxUdpEC::e_t38UDPRedundancy);
+		break;
+	case UDPTL_ERROR_CORRECTION_FEC:
+		m_profile.m_t38FaxUdpOptions.m_t38FaxUdpEC.SetTag(H245_T38FaxUdpOptions_t38FaxUdpEC::e_t38UDPFEC);
+		break;
+	default:
+		break;	
+	}
+
+	switch (profile->ast.rate) {
+	case AST_T38_RATE_2400:
+		m_bit_rate = 24;
+		break;
+	case AST_T38_RATE_4800:
+		m_bit_rate = 48;
+		break;
+        case AST_T38_RATE_7200:
+		m_bit_rate = 72;
+		break;
+	case AST_T38_RATE_9600:
+		m_bit_rate = 96;
+		break;
+        case AST_T38_RATE_12000:
+		m_bit_rate = 120;
+		break;
+        case AST_T38_RATE_14400:
+		m_bit_rate = 144;
+		break;
+	default:
+		break;
+	}
+}
+
+void AST_T38Capability::GetProfile(struct t38_profile* profile) const
+{
+	memset(profile, 0, sizeof(*profile));
+	
+	profile->ast.fill_bit_removal = m_profile.m_fillBitRemoval;
+	profile->ast.transcoding_jbig = m_profile.m_transcodingJBIG;
+	profile->ast.transcoding_mmr = m_profile.m_transcodingMMR;
+	profile->ast.version = m_profile.m_version;
+
+	switch (m_profile.m_t38FaxRateManagement.GetTag()) {
+	case H245_T38FaxRateManagement::e_localTCF:
+		profile->ast.rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF;
+		break;
+	case H245_T38FaxRateManagement::e_transferredTCF:
+		profile->ast.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF;
+		break;
+	default:
+		break;	
+	}
+
+	if (m_profile.m_t38FaxUdpOptions.HasOptionalField(H245_T38FaxUdpOptions::e_t38FaxMaxDatagram))
+		profile->max_datagram = m_profile.m_t38FaxUdpOptions.m_t38FaxMaxDatagram;
+
+	switch (m_profile.m_t38FaxUdpOptions.m_t38FaxUdpEC.GetTag()) {
+	case H245_T38FaxUdpOptions_t38FaxUdpEC::e_t38UDPRedundancy:
+		profile->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
+		break;
+	case H245_T38FaxUdpOptions_t38FaxUdpEC::e_t38UDPFEC:
+		profile->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
+		break;
+	default:
+		break;	
+	}
+
+	switch (m_bit_rate) {
+	case 24:
+		profile->ast.rate = AST_T38_RATE_2400;
+		break;
+	case 48:
+		profile->ast.rate = AST_T38_RATE_4800;
+		break;
+        case 72:
+		profile->ast.rate = AST_T38_RATE_7200;
+		break;
+	case 96:
+		profile->ast.rate = AST_T38_RATE_9600;
+		break;
+        case 120:
+		profile->ast.rate = AST_T38_RATE_12000;
+		break;
+        case 144:
+		profile->ast.rate = AST_T38_RATE_14400;
+		break;
+	default:
+		break;
+	}
+}
+
+BOOL AST_T38Capability::OnSendingPDU(H245_DataApplicationCapability & pdu) const
+{
+	PTRACE(3, "ASTH323T38\tOnSendingPDU for capability");
+
+	pdu.m_maxBitRate = m_bit_rate;
+	pdu.m_application.SetTag(H245_DataApplicationCapability_application::e_t38fax);
+	H245_DataApplicationCapability_application_t38fax & fax = pdu.m_application;
+	fax.m_t38FaxProtocol.SetTag(H245_DataProtocolCapability::e_udp);
+	fax.m_t38FaxProfile = m_profile;
+
+	return TRUE;
+}
+
+BOOL AST_T38Capability::OnSendingPDU(H245_DataMode & pdu) const
+{
+	PTRACE(3, "ASTH323T38\tOnSendingPDU for capability");
+
+	pdu.m_bitRate = m_bit_rate;
+	pdu.m_application.SetTag(H245_DataMode_application::e_t38fax);
+	H245_DataMode_application_t38fax & fax = pdu.m_application;
+	fax.m_t38FaxProtocol.SetTag(H245_DataProtocolCapability::e_udp);
+	fax.m_t38FaxProfile = m_profile;
+
+	return TRUE;
+}
+
+BOOL AST_T38Capability::OnReceivedPDU(const H245_DataApplicationCapability & pdu)
+{
+        if (pdu.m_application.GetTag() != H245_DataApplicationCapability_application::e_t38fax)
+		return FALSE;
+
+	const H245_DataApplicationCapability_application_t38fax & fax = pdu.m_application;
+	const H245_DataProtocolCapability & proto = fax.m_t38FaxProtocol;
+
+	if (proto.GetTag() != H245_DataProtocolCapability::e_udp)
+		return FALSE;
+
+	m_bit_rate = pdu.m_maxBitRate;
+	m_profile = fax.m_t38FaxProfile;
+
+	return TRUE;
+}
+
+BOOL AST_T38Capability::OnReceivedPDU(const H245_DataMode & pdu)
+{
+        if (pdu.m_application.GetTag() != H245_DataApplicationCapability_application::e_t38fax)
+		return FALSE;
+
+	const H245_DataMode_application_t38fax & fax = pdu.m_application;
+	const H245_DataProtocolCapability & proto = fax.m_t38FaxProtocol;
+
+	if (proto.GetTag() != H245_DataProtocolCapability::e_udp)
+		return FALSE;
+
+	m_bit_rate = pdu.m_bitRate;
+	m_profile = fax.m_t38FaxProfile;
+
+	return TRUE;
+}
Index: channels/h323/ast_h323.cxx
===================================================================
--- channels/h323/ast_h323.cxx	(revision 320715)
+++ channels/h323/ast_h323.cxx	(working copy)
@@ -269,13 +269,11 @@
   *					transport = ip.
   *					port = 1720.
   */
-int MyH323EndPoint::MyMakeCall(const PString & dest, PString & token, void *_callReference, void *_opts)
+int MyH323EndPoint::MyMakeCall(const PString & dest, struct call_details *cd, struct call_options *opts)
 {
 	PString fullAddress;
 	MyH323Connection * connection;
 	H323Transport *transport = NULL;
-	unsigned int *callReference = (unsigned int *)_callReference;
-	call_options_t *opts = (call_options_t *)_opts;
 
 	/* Determine whether we are using a gatekeeper or not. */
 	if (GetGatekeeper()) {
@@ -306,18 +304,20 @@
 				cout << "Unable to get address and port" << endl;
 		}
 	}
+	PString token;
 	if (!(connection = (MyH323Connection *)H323EndPoint::MakeCallLocked(fullAddress, token, opts, transport))) {
 		if (h323debug) {
 			cout << "Error making call to \"" << fullAddress << '"' << endl;
 		}
 		return 1;
 	}
-	*callReference = connection->GetCallReference();
+	cd->call_reference = connection->GetCallReference();
+	memcpy(cd->call_token, (const unsigned char *)token, token.GetLength());
 
 	if (h323debug) {
 		cout << "\t-- " << GetLocalUserName() << " is calling host " << fullAddress << endl;
 		cout << "\t-- Call token is " << (const char *)token << endl;
-		cout << "\t-- Call reference is " << *callReference << endl;
+		cout << "\t-- Call reference is " << cd->call_reference << endl;
 #ifdef PTRACING
 		cout << "\t-- DTMF Payload is " << connection->dtmfCodec << endl;
 #endif
@@ -1759,6 +1759,7 @@
 
 	memset(&prefs, 0, sizeof(prefs));
 	int peer_capabilities = 0;
+	const AST_T38Capability* t38Cap = NULL;
 	for (int i = 0; i < remoteCapabilities.GetSize(); ++i) {
 		unsigned int subType = remoteCapabilities[i].GetSubType();
 		if (h323debug) {
@@ -1789,7 +1790,10 @@
 			}
 			break;
 		case H323Capability::e_Data:
-			if (!strcmp((const char *)remoteCapabilities[i].GetFormatName(), CISCO_DTMF_RELAY)) {
+			t38Cap = dynamic_cast<const AST_T38Capability*>(&remoteCapabilities[i]);
+			if (t38Cap)
+				break;
+			else 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);
@@ -1851,10 +1855,12 @@
 #if 0
 	redir_capabilities &= peer_capabilities;
 #endif
-	if (on_setpeercapabilities)
-		on_setpeercapabilities(GetCallReference(), (const char *)callToken, peer_capabilities, &prefs);
 
-	return TRUE;
+	struct t38_profile t38profile;
+	if (t38Cap)
+		t38Cap->GetProfile(&t38profile);
+
+	return set_peer_capabilities(GetCallReference(), (const char *)callToken, peer_capabilities, &prefs, t38Cap ? &t38profile : NULL) == 0;
 }
 
 H323Channel * MyH323Connection::CreateRealTimeLogicalChannel(const H323Capability & capability,
@@ -1887,7 +1893,7 @@
 	return connectionState != ShuttingDownConnection;
 }
 
-void MyH323Connection::SetCapabilities(int caps, int dtmf_mode, void *_prefs, int pref_codec)
+void MyH323Connection::SetCapabilities(int caps, int dtmf_mode, void *_prefs, int pref_codec, struct t38_profile *t38profile)
 {
 	PINDEX lastcap = -1; /* last common capability index */
 	int alreadysent = 0;
@@ -1978,6 +1984,9 @@
 		}
 	}
 
+	if (t38profile)
+		lastcap = localCapabilities.SetCapability(0, 0, new AST_T38Capability(t38profile));
+
 	cap = new H323_UserInputCapability(H323_UserInputCapability::HookFlashH245);
 	if (cap && cap->IsUsable(*this)) {
 		lastcap++;
@@ -2122,7 +2131,345 @@
 #endif
 }
 
+/* T.38 support */
 
+class MyT38Transport : public H323TransportIP
+{
+        BOOL Connect()
+	{
+	        struct sockaddr_in them;
+		them.sin_family = AF_INET;
+		them.sin_addr.s_addr = remoteAddress;
+		them.sin_port = htons(remotePort);
+		return setup_udptl_connection(m_connection.GetCallReference(), 
+			    	    	      (const char *)m_connection.GetCallToken(),
+			    	              &them) == 0;
+	}
+
+	BOOL Close()
+	{
+	        /* actual close is done in Asterisk */
+		return TRUE;
+	}
+    
+	BOOL ReadPDU(PBYTEArray&)
+	{
+		return TRUE;
+	}
+
+	BOOL ExtractPDU(const PBYTEArray&, PINDEX&)
+	{
+		return TRUE;
+	}
+
+	BOOL WritePDU(const PBYTEArray&)
+	{
+		return TRUE;
+	}
+
+	BOOL SetRemoteAddress(const H323TransportAddress& addr)
+	{
+		return addr.GetIpAndPort(remoteAddress, remotePort, "udp");
+	}
+
+	const H323Connection& m_connection;
+
+public:
+	MyT38Transport(const H323Connection& connection) : H323TransportIP(*endPoint, PIPSocket::Address("0.0.0.0"), 0),
+						    	   m_connection(connection)
+	{
+		struct rtp_info info;
+
+		PIPSocket::Address addr;
+		WORD port;
+		m_connection.GetSignallingChannel()->GetRemoteAddress().GetIpAndPort(addr, port);
+	
+		/* Determine the Local (A side) IP Address and port */
+		if(external_udptl_create(m_connection.GetCallReference(),
+		                         (const char *)m_connection.GetCallToken(),
+				         static_cast<in_addr>(addr),
+					 &info) == 0) {
+			localAddress = info.addr;
+			localPort = info.port;
+		}
+	}
+};
+
+struct MyT38Handler : public OpalT38Protocol
+{
+	MyT38Handler(const H323Connection& connection)
+	{
+		transport = new MyT38Transport(connection);
+		autoDeleteTransport = TRUE;
+	}
+};
+
+OpalT38Protocol * MyH323EndPoint::CreateT38ProtocolHandler(const H323Connection & connection) const
+{
+	return new MyT38Handler(connection);
+}
+
+MyT38Channel::MyT38Channel(H323Connection & connection,
+			   const H323Capability & capability,
+			   Directions direction,
+			   unsigned sessionID) : H323_T38Channel(connection,
+						                 capability,
+							         direction,
+							         sessionID,
+								 H323_T38Capability::e_UDP)
+{
+}
+
+BOOL MyT38Channel::CreateListener()
+{
+	/* Don't create any listener - we don't need them.
+	 * Asterisk channel starts UDPTL listener.
+	 */
+	return TRUE;
+}
+
+H323Channel::Directions MyT38Channel::GetDirection() const
+{
+	/* This way we tell OpenH323 that we need openLogicalChannelAck
+	 * to start the channel
+	 */
+	return IsBidirectional;
+}
+
+void MyT38Channel::Receive() 
+{
+	/* do nothing - Asterisk handles all data */
+}
+
+void MyT38Channel::Transmit()
+{
+	/* do nothing - Asterisk handles all data */
+}
+
+BOOL MyT38Channel::Start()
+{
+	if (receiver)
+		/* OpenH323 calls Start() for both the receiving and transmitting channel.
+		 * Both are of MyT38Channel type.
+		 * We start as soon as we can transmit data, because this means we know remote media address.
+		 * We do no actions for receiver to not to send AST_T38_NEGOTIATED twice.
+		 */
+		return TRUE;
+
+	const AST_T38Capability *t38Cap = NULL;
+	/* We need to choose whose capabilities to use to generate T.38 profile to send to bridged party.
+	 * 1) When we receive OLC from peer, T.38 profile is copied to local capability when OpenH323 calls OnReceivedPDU()
+	 * for local capability (in CreateLogicalChannel()). 
+	 * 2) When we send OLC, we put T.38 profile from local capability as well.
+	 * Therefore we should use T.38 profile from local capabilities. 
+	 * Note: T.38 profile at this stage possibly would be ignored by bridged party, because
+	 * it could have already applied profile sent by OnRequestModeChange()).
+	 */
+	for (PINDEX i = 0; i < connection.GetLocalCapabilities().GetSize(); i++) {
+		t38Cap = dynamic_cast<AST_T38Capability*>(&connection.GetLocalCapabilities()[i]);
+		if (t38Cap)
+			break;
+	}
+
+	if (!t38Cap) {
+		PTRACE(1, "T38\tNo capability found!");
+		return FALSE;
+	}
+
+	struct t38_profile profile;
+	t38Cap->GetProfile(&profile);
+	profile.ast.request_response = AST_T38_NEGOTIATED;
+
+	if (request_mode_change(connection.GetCallReference(), 
+    	        		(const char *)connection.GetCallToken(),
+				&profile) != 0) {
+		PTRACE(1, "T38\tfailed to ack mode switch!");
+		return FALSE;
+	}
+
+	/* Don't call parent's method - 
+	 * it starts threads which are not needed in this case
+	 */
+	return TRUE;
+}
+
+/* Remote side refused mode change initiated by us */
+void MyH323Connection::OnRefusedModeChange(const H245_RequestModeReject *pdu)
+{
+	if (t38ModeChangeCapabilities.IsEmpty())
+		/* remote side has refused change to audio mode */
+		return H323Connection::OnRefusedModeChange(pdu);
+
+	/* Remote side has refused change to T.38 mode.
+	 * We need to notify Asterisk in this case
+         * to prevent opposite call leg being stuck in T.38 negotiating state 
+         */
+	struct t38_profile profile;
+	profile.ast.request_response = AST_T38_REFUSED;
+	request_mode_change(GetCallReference(), 
+    		            (const char *)GetCallToken(),
+		            &profile);
+
+        return H323Connection::OnRefusedModeChange(pdu);
+}
+
+/* Remote side accepted mode change initiated by us */
+void MyH323Connection::OnAcceptModeChange(const H245_RequestModeAck & pdu)
+{
+	if (!t38ModeChangeCapabilities.IsEmpty())
+		/* remote side has accepted change to T.38 mode,
+		 * AST_T38_NEGOTIATED will be sent when T.38 channel
+		 * will be started
+		 */
+		return H323Connection::OnAcceptModeChange(pdu);
+
+	/* remote side has accepted change to audio mode */
+	struct t38_profile profile;
+	profile.ast.request_response = AST_T38_TERMINATED;
+
+	request_mode_change(GetCallReference(), 
+    	        	    (const char *)GetCallToken(),
+	  		    &profile);
+
+	CloseAllLogicalChannels(FALSE);
+	const H323Capability* capability = GetPreferredAudioCapability();
+	if (capability != NULL && OpenLogicalChannel(*capability,
+                                                     capability->GetDefaultSessionID(),
+                                                     H323Channel::IsTransmitter)) {
+    		PTRACE(1, "H245\tOpened " << *capability << " after audio mode change");
+    	}
+
+	return H323Connection::OnAcceptModeChange(pdu);
+}
+
+void MyH323Connection::OnModeChanged(BOOL ok)
+{
+	m_modeChangedOk = ok;
+	m_modeChanged.Signal();
+}
+
+/* We accepted mode change request from remote side */
+BOOL MyH323Connection::OnRequestModeChange(const H245_RequestMode & pdu,
+                                           H245_RequestModeAck & ack,
+                                           H245_RequestModeReject & reject,
+                                           PINDEX & selectedMode)
+{
+	bool genericAudio = false;
+	if (H323Connection::OnRequestModeChange(pdu, ack, reject, selectedMode) == FALSE) {
+		/* Hack to support H.323 stack from Objective Systems (and similar) on remote side.
+		 * OpenH323 was unable to find matching capability(es) on our side (in H323Connection::OnRequestModeChange()),
+		 * because remote side has sent us "generic" audio capability. This usually happens
+		 * on switch back to voice mode from T.38. Check if this is the case.
+		 */
+		for (selectedMode = 0; selectedMode < pdu.m_requestedModes.GetSize(); selectedMode++) {
+			for (PINDEX i = 0; i < pdu.m_requestedModes[selectedMode].GetSize(); i++) {
+				const H245_ModeElementType & modeElement = pdu.m_requestedModes[selectedMode][i].m_type;
+				if (modeElement.GetTag() == H245_ModeElementType::e_audioMode) {
+					const H245_AudioMode &audio = modeElement;
+					if (audio.GetTag() == H245_AudioCapability::e_genericAudioCapability)
+						genericAudio = true;
+				}
+			}
+		}
+
+		/* No matching capability and no generic audio, no luck */
+		if (!genericAudio) 
+			return FALSE;
+
+		const H323Capability* cap = GetPreferredAudioCapability();
+		if (!cap)
+			/* "Generic" capability requested but unable to determine preferred capability */
+			return FALSE;
+
+		H245_ModeDescription description;
+		description.SetSize(1);
+		cap->OnSendingPDU(description[0]);
+		H245_ArrayOf_ModeDescription descriptions;
+		descriptions.SetSize(1);
+		descriptions[0] = description;
+		H245_RequestMode& p = const_cast<H245_RequestMode&>(pdu); /* XXX: we alter what is not supposed to be altered by OpenH323,
+									  * but I don't know any better way
+									  */
+		p.m_requestedModes = descriptions;
+		selectedMode = 0;
+	}
+
+	struct t38_profile profile;
+	H323Capability *localCap = NULL;
+	H245_ModeElement *mode = NULL;
+	const H245_ModeDescription & newMode = pdu.m_requestedModes[selectedMode];
+
+	/* Use first matching capability to understand if this is switch to T.38 fax or voice mode */
+	for (PINDEX i = 0; i < newMode.GetSize(); i++) {
+		mode = &newMode[i];
+		localCap = localCapabilities.FindCapability(*mode);
+		if (localCap)
+		        break;
+	}
+
+	if (!localCap)
+		return FALSE;
+
+	if (AST_T38Capability *t38Cap = dynamic_cast<AST_T38Capability *>(localCap)) {
+		/* inherit T.38 profile from peer's RequestMode message */
+		t38Cap->OnReceivedPDU(mode->m_type);
+		t38Cap->GetProfile(&profile);
+		profile.ast.request_response = AST_T38_REQUEST_NEGOTIATE;
+	} else
+		profile.ast.request_response = AST_T38_REQUEST_TERMINATE;
+
+	/* Check if bridged party supports changing mode
+	 * and reject negotiation with peer if not.
+	 * T.38 profile contains peer's requested mode.
+	 */
+	m_modeChangedOk = FALSE;
+	if (request_mode_change(GetCallReference(), 
+				(const char *)GetCallToken(),
+			        &profile))
+		return FALSE;
+	
+	if (profile.ast.request_response == AST_T38_REQUEST_NEGOTIATE) {
+		/* For switch to T.38 - wait until bridged peer rejects or acks
+		 * mode change, respond to our peer respectively. Otherwise (without wait)
+		 * we'll need to enable T.38 mode and then almost immediately switch back
+		 * to voice mode if peer rejects switch to T.38. The latter should be
+		 * better avoided, because it can happen right in the middle of T.38 OLC negotiations
+		 * and peer's equipment will likely go crazy after this.
+		 * Also when bridged peer acks us, we learn final T.38 capabilities and send them to
+		 * our peer.
+		 */
+		if(!m_modeChangeMutex.Wait(0)) 
+			/* likely locked by CleanUpOnCallEnd() */
+			return FALSE;
+		Unlock(); /* to make FindConnectionWithLock() work ok when disposition is received from bridged peer */
+		m_modeChanged.Wait(3000); /* wait 3 sec for disposition from bridged peer */
+		/* connection couldn't be deleted during wait, because it's protected by m_modeChangeMutex,
+		 * so we can lock it again
+		 */
+		Lock();
+		m_modeChangeMutex.Signal();
+	        return m_modeChangedOk;
+	}
+	
+	/* for switch to audio mode - immediately ack,
+	 * because bridged peer/application might not ack AST_T38_REQUEST_TERMINATE request 
+	 * with AST_T38_TERMINATED (just like app_fax acts)
+	 * XXX: review this part when app_fax is fixed
+	 */
+	return TRUE;
+}
+
+void MyH323Connection::CleanUpOnCallEnd()                                                                                                                                                                                                                                                
+{
+	m_modeChanged.Signal();
+	m_modeChangeMutex.Wait(); 
+	/* if we're here - we're not waiting for mode change */
+	H323Connection::CleanUpOnCallEnd();
+	m_modeChangeMutex.Signal(); 
+}
+
+/* end of T.38 support */
+
 /* MyH323_ExternalRTPChannel */
 MyH323_ExternalRTPChannel::MyH323_ExternalRTPChannel(MyH323Connection & connection,
 							const H323Capability & capability,
@@ -2288,7 +2635,6 @@
 							rfc2833_cb				dtmffunc,
 							hangup_cb				hangupfunc,
 							setcapabilities_cb		capabilityfunc,
-							setpeercapabilities_cb	peercapabilityfunc,
 							onhold_cb				holdfunc)
 {
 	on_incoming_call = ifunc;
@@ -2304,14 +2650,18 @@
 	on_set_rfc2833_payload = dtmffunc;
 	on_hangup = hangupfunc;
 	on_setcapabilities = capabilityfunc;
-	on_setpeercapabilities = peercapabilityfunc;
 	on_hold = holdfunc;
 }
 
 /**
  * Add capability to the capability table of the end point.
  */
-int h323_set_capabilities(const char *token, int cap, int dtmf_mode, struct ast_codec_pref *prefs, int pref_codec)
+int h323_set_capabilities(const char *token, 
+			  int cap, 
+			  int dtmf_mode, 
+			  struct ast_codec_pref *prefs, 
+			  int pref_codec,
+			  struct t38_profile *t38profile)
 {
 	MyH323Connection *conn;
 
@@ -2330,12 +2680,115 @@
 		cout << " ERROR: [h323_set_capabilities] Unable to find connection " << token << endl;
 		return 1;
 	}
-	conn->SetCapabilities((/*conn->bridging ? conn->redir_capabilities :*/ cap), dtmf_mode, prefs, pref_codec);
+	conn->SetCapabilities((/*conn->bridging ? conn->redir_capabilities :*/ cap), dtmf_mode, prefs, pref_codec, t38profile);
 	conn->Unlock();
 
 	return 0;
 }
 
+const H323Capability *MyH323Connection::GetPreferredAudioCapability() const
+{
+	const H323Capabilities& localCaps = GetLocalCapabilities();
+	const H323Capabilities& remoteCaps = GetRemoteCapabilities();
+	const H323Capability *remoteCapability = NULL;
+
+	/* Find first matching audio capability */
+	for (PINDEX i = 0; i < localCaps.GetSize(); i++) {
+		const H323Capability & localCapability = localCaps[i];
+		if(localCapability.GetMainType() != H323Capability::e_Audio)
+			continue;
+		remoteCapability = remoteCaps.FindCapability(localCapability);
+		if (remoteCapability)
+			break;
+	}
+
+	return remoteCapability;
+}
+
+int h323_request_mode_change(const char *token, const struct t38_profile *profile)
+{
+	if (!h323_end_point_exist()) {
+		cout << " ERROR: [h323_change_mode] No Endpoint, this is bad" << endl;
+		return -1;
+	}
+	if (!token || !*token) {
+		cout << " ERROR: [h323_change_mode] Invalid call token specified." << endl;
+		return -1;
+	}
+
+	PString myToken(token);
+	MyH323Connection *conn = (MyH323Connection *)endPoint->FindConnectionWithLock(myToken);
+	if (!conn) {
+		cout << " ERROR: [h323_change_mode_t38] Unable to find connection " << token << endl;
+		return -1;
+	}
+
+	BOOL res = FALSE;
+	switch (profile->ast.request_response) {
+	case AST_T38_REQUEST_NEGOTIATE:
+	{
+		/* inherit T.38 capabilities */
+		const H323Capabilities& localCaps = conn->GetLocalCapabilities();
+		AST_T38Capability *t38Cap = NULL;
+		for (PINDEX i = 0; i < localCaps.GetSize(); i++) {
+			t38Cap = dynamic_cast<AST_T38Capability*>(&localCaps[i]);
+			if (t38Cap)
+				break;
+		}
+		/* We should definitely have T.38 capability
+		 * in case if Asterisk calls us this way
+		 */
+		t38Cap->SetProfile(profile);
+
+		res = conn->RequestModeChangeT38();
+		break;
+	}
+	case AST_T38_REQUEST_TERMINATE:
+		/* change mode to audio */
+		if (const H323Capability *remoteCapability = conn->GetPreferredAudioCapability()) {
+			if (h323debug)
+	    			cout << "ModeChange: " << remoteCapability->GetFormatName() << endl;
+			res = conn->RequestModeChange(remoteCapability->GetFormatName());
+		}
+		else if (h323debug)
+	    		cout << "ModeChange: no audio capability" << endl;
+		break;
+	case AST_T38_NEGOTIATED:
+	{
+		/* inherit T.38 capabilities */
+		const H323Capabilities& localCaps = conn->GetLocalCapabilities();
+		AST_T38Capability *t38Cap = NULL;
+		for (PINDEX i = 0; i < localCaps.GetSize(); i++) {
+			t38Cap = dynamic_cast<AST_T38Capability*>(&localCaps[i]);
+			if (t38Cap)
+				break;
+		}
+		/* We should definitely have T.38 capability
+		 * in case if Asterisk calls us this way
+		 */
+		t38Cap->SetProfile(profile);
+
+		conn->OnModeChanged(TRUE);
+		res = TRUE;
+		break;
+	}
+	case AST_T38_REFUSED:
+		conn->OnModeChanged(FALSE);
+		res = TRUE;
+		break;
+	case AST_T38_TERMINATED:
+		conn->OnModeChanged(TRUE);
+		res = TRUE;
+		break;
+	default:
+		break;
+	}
+	    
+	conn->Unlock();
+
+	return res == TRUE ? 0 : -1;
+}
+
 /** Start the H.323 listener */
 int h323_start_listener(int listenPort, struct sockaddr_in bindaddr)
 {
@@ -2507,7 +2960,6 @@
   */
 int h323_make_call(char *dest, call_details_t *cd, call_options_t *call_options)
 {
-	int res;
 	PString	token;
 	PString	host(dest);
 
@@ -2515,9 +2967,7 @@
 		return 1;
 	}
 
-	res = endPoint->MyMakeCall(host, token, &cd->call_reference, call_options);
-	memcpy((char *)(cd->call_token), (const unsigned char *)token, token.GetLength());
-	return res;
+	return endPoint->MyMakeCall(host, cd, call_options);
 };
 
 int h323_clear_call(const char *call_token, int cause)
Index: channels/h323/chan_h323.h
===================================================================
--- channels/h323/chan_h323.h	(revision 320715)
+++ channels/h323/chan_h323.h	(working copy)
@@ -31,7 +31,18 @@
 
 #include <arpa/inet.h>
 #include "asterisk/format.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <asterisk.h>
+#include <asterisk/frame.h>
+#include <asterisk/udptl.h>
+#include <asterisk/astobj.h>
+#ifdef __cplusplus
+}
+#endif
 
+
 /*
  * Enable support for sending/reception of tunnelled Q.SIG messages and
  * some sort of IEs (especially RedirectingNumber) which Cisco CallManager
@@ -49,6 +60,12 @@
 
 typedef int64_t h323_format;
 
+enum h323_udptl_ec_modes {
+    UDPTL_EC_AUTO = 0,
+    UDPTL_EC_FEC,
+    UDPTL_EC_REDUNDANCY
+};
+
 /** call_option struct holds various bits
  *         of information for each call */
 typedef struct call_options {
@@ -70,6 +87,9 @@
 	h323_format     capability;
 	int				bridge;
 	int				nat;
+	int				t38pt_udptl;
+	enum h323_udptl_ec_modes	t38pt_udptl_ec;
+	unsigned			t38pt_udptl_max_datagram;
 	int				tunnelOptions;
 	int				holdHandling;
 	int				autoframing; /*!< turn on to override local settings with remote framing length */
@@ -144,6 +164,7 @@
    the external RTP port from Asterisk. */
 typedef rtp_info_t *(*on_rtp_cb)(unsigned, const char *);
 extern on_rtp_cb on_external_rtp_create;
+extern on_rtp_cb on_external_udptl_create;
 
 /* This is a callback prototype function, called to send
    the remote IP and RTP port from H.323 to Asterisk */
@@ -218,6 +239,12 @@
 extern "C" {
 #endif
 
+struct t38_profile {
+	struct ast_control_t38_parameters ast;
+	enum ast_t38_ec_modes error_correction_scheme;
+	int max_datagram;
+};
+
 	void h323_gk_urq(void);
 	void h323_end_point_create(void);
 	void h323_end_process(void);
@@ -239,9 +266,8 @@
 					rfc2833_cb,
 					hangup_cb,
 					setcapabilities_cb,
-					setpeercapabilities_cb,
 					onhold_cb);
-	int h323_set_capabilities(const char *, int, int, struct ast_codec_pref *, int);
+	int h323_set_capabilities(const char *, int, int, struct ast_codec_pref *, int, struct t38_profile*);
 	int h323_set_alias(struct oh323_alias *);
 	int h323_set_gk(int, char *, char *);
 	void h323_set_id(char *);
@@ -268,6 +294,11 @@
 	int h323_show_codec(int fd, int argc, char *argv[]);
 	int h323_hold_call(const char *token, int);
 
+int external_udptl_create(unsigned call_reference, const char * token, struct in_addr them, struct rtp_info *info);
+int setup_udptl_connection(unsigned call_reference, const char *token, const struct sockaddr_in *them);
+int request_mode_change(unsigned call_reference, const char *token, struct t38_profile *profile);
+int h323_request_mode_change(const char *token, const struct t38_profile *profile);
+int set_peer_capabilities(unsigned call_reference, const char *token, int capabilities, struct ast_codec_pref *prefs, const struct t38_profile *profile);
 #ifdef __cplusplus
 }
 #endif
Index: channels/h323/caps_h323.h
===================================================================
--- channels/h323/caps_h323.h	(revision 320715)
+++ channels/h323/caps_h323.h	(working copy)
@@ -1,6 +1,8 @@
 #ifndef __AST_H323CAPS_H
 #define __AST_H323CAPS_H
 
+#include <h323t38.h>
+
 /**This class describes the G.711 codec capability.
  */
 class AST_G711Capability : public H323AudioCapability
@@ -169,4 +171,32 @@
 	}
 };
 
+/* T.38 Caps */
+class AST_T38Capability : public H323_T38Capability
+{
+	PCLASSINFO(AST_T38Capability, H323_T38Capability);
+	/* for TerminalCapabilitySet or OpenLogicalChannel PDU */
+	BOOL OnSendingPDU(H245_DataApplicationCapability & pdu) const;
+	/* for RequestMode PDU */
+	BOOL OnSendingPDU(H245_DataMode & pdu) const;
+	BOOL OnReceivedPDU(const H245_DataApplicationCapability & cap);
+
+	H245_T38FaxProfile m_profile;
+	int m_bit_rate;
+
+public:
+	AST_T38Capability(const struct t38_profile*);
+	PObject *Clone() const;
+   
+	/* Create the channel instance, allocating resources as required. */
+	H323Channel *CreateChannel(H323Connection &connection,
+				   H323Channel::Directions, unsigned sessionID,
+				   const H245_H2250LogicalChannelParameters *param) const;
+
+	void SetProfile(const struct t38_profile*);
+	void GetProfile(struct t38_profile*) const;
+	/* received RequestMode from peer, inherit T.38 profile */
+	BOOL OnReceivedPDU(const H245_DataMode & pdu);
+};
+
 #endif /* __AST_H323CAPS_H */
Index: channels/h323/ast_h323.h
===================================================================
--- channels/h323/ast_h323.h	(revision 320715)
+++ channels/h323/ast_h323.h	(working copy)
@@ -30,16 +30,19 @@
 #define AST_H323_H
 
 #include "ast_ptlib.h"
+#include <t38proto.h>
+#include <h323t38.h>
 
 #define VERSION(a,b,c) ((a)*10000+(b)*100+(c))
 
 class MyH323EndPoint : public H323EndPoint
 {
 	PCLASSINFO(MyH323EndPoint, H323EndPoint);
+	OpalT38Protocol * CreateT38ProtocolHandler(const H323Connection & connection) const;
 
 public:
 	MyH323EndPoint();
-	int MyMakeCall(const PString &, PString &, void *_callReference, void *_opts);
+	int MyMakeCall(const PString &, struct call_details*, struct call_options*);
 	PBoolean ClearCall(const PString &, H323Connection::CallEndReason reason);
 	PBoolean ClearCall(const PString &);
 
@@ -58,7 +61,19 @@
 class MyH323Connection : public H323Connection
 {
 	PCLASSINFO(MyH323Connection, H323Connection);
-
+	void OnRefusedModeChange(const H245_RequestModeReject *pdu);
+	void OnAcceptModeChange(const H245_RequestModeAck & pdu);
+	BOOL OnRequestModeChange(const H245_RequestMode & pdu,
+                            	 H245_RequestModeAck & ack,
+                            	 H245_RequestModeReject & reject,
+                            	 PINDEX & selectedMode);
+	void OnModeChanged(BOOL ok);
+	void CleanUpOnCallEnd();
+	
+	BOOL m_modeChangedOk;
+	PSyncPoint m_modeChanged;
+	PMutex m_modeChangeMutex;
+	
 public:
 	MyH323Connection(MyH323EndPoint &, unsigned, unsigned);
 	~MyH323Connection();
@@ -85,7 +100,7 @@
 	PBoolean MySendProgress();
 	void OnSendCapabilitySet(H245_TerminalCapabilitySet &);
 	void OnSetLocalCapabilities();
-	void SetCapabilities(int, int, void *, int);
+	void SetCapabilities(int, int, void *, int, struct t38_profile*);
 	PBoolean OnReceivedCapabilitySet(const H323Capabilities &, const H245_MultiplexCapability *,
 			H245_TerminalCapabilitySetReject &);
 	void SetCause(int _cause) { cause = _cause; };
@@ -101,6 +116,7 @@
 	virtual void OnReceivedLocalCallHold(int linkedId);
 	virtual void OnReceivedLocalCallRetrieve(int linkedId);
 #endif
+	const H323Capability *GetPreferredAudioCapability() const;
 	void MyHoldCall(BOOL localHold);
 
 	PString sourceAliases;
@@ -162,6 +178,21 @@
 	WORD remotePort;
 };
 
+struct MyT38Channel : public H323_T38Channel
+{
+	MyT38Channel(H323Connection & connection,        ///< Connection to endpoint for channel
+    		     const H323Capability & capability,  ///< Capability channel is using
+            	     Directions direction,               ///< Direction of channel
+                     unsigned sessionID);                ///< Session ID for channel
+
+private:                
+	BOOL Start();
+	Directions GetDirection() const;
+	BOOL CreateListener();
+	void Receive();
+	void Transmit();
+};
+
 #ifdef H323_H450
 
 #if VERSION(OPENH323_MAJOR, OPENH323_MINOR, OPENH323_BUILD) > VERSION(1,19,4)
Index: channels/chan_h323.c
===================================================================
--- channels/chan_h323.c	(revision 320715)
+++ channels/chan_h323.c	(working copy)
@@ -77,6 +77,7 @@
 #include "asterisk/sched.h"
 #include "asterisk/io.h"
 #include "asterisk/rtp_engine.h"
+#include "asterisk/udptl.h"
 #include "asterisk/acl.h"
 #include "asterisk/callerid.h"
 #include "asterisk/cli.h"
@@ -109,7 +110,6 @@
 rfc2833_cb on_set_rfc2833_payload;
 hangup_cb on_hangup;
 setcapabilities_cb on_setcapabilities;
-setpeercapabilities_cb on_setpeercapabilities;
 onhold_cb on_hold;
 
 int h323debug; /*!< global debug flag */
@@ -174,6 +174,7 @@
 	char rdnis[80];				/*!< Referring DNIS, if available */
 	int amaflags;				/*!< AMA Flags */
 	struct ast_rtp_instance *rtp;		/*!< RTP Session */
+	struct ast_udptl *udptl;
 	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 */
@@ -195,6 +196,7 @@
 	int noInbandDtmf;			/*!< Inband DTMF processing by DSP isn't available */
 	int connection_established;		/*!< Call got CONNECT message */
 	int got_progress;			/*!< Call got PROGRESS message, pass inband audio */
+	enum ast_t38_state t38state;
 	struct oh323_pvt *next;			/*!< Next channel in list */
 } *iflist = NULL;
 
@@ -251,6 +253,43 @@
 static int oh323_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen);
 static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
 
+
+static int oh323_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
+{
+        struct oh323_pvt *p = (struct oh323_pvt *) ast->tech_pvt;
+	int res = -1;
+
+	if (!p)
+		return -1;
+
+	ast_mutex_lock(&p->lock);
+
+	if (h323debug)
+		ast_verbose("----- oh323_queryoption %d on channel %s\n", option, ast->name);
+
+	switch (option) {
+		case AST_OPTION_T38_STATE:
+			if (*datalen != sizeof(enum ast_t38_state)) {
+				ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option."
+					" Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
+				 break;
+			}
+			*((enum ast_t38_state *) data) = p->t38state;
+			res = 0;
+			break;
+
+		default:
+			break;
+	}
+
+	if (h323debug)
+		ast_verbose("+++++ oh323_queryoption %d on channel %s\n", option, ast->name);
+
+	ast_mutex_unlock(&p->lock);
+
+	return res;
+}
+
 static struct ast_channel_tech oh323_tech = {
 	.type = "H323",
 	.description = tdesc,
@@ -266,6 +305,7 @@
 	.indicate = oh323_indicate,
 	.fixup = oh323_fixup,
 	.bridge = ast_rtp_instance_bridge,
+	.queryoption = oh323_queryoption,
 };
 
 static const char* redirectingreason2str(int redirectingreason)
@@ -455,24 +495,28 @@
 
 	AST_SCHED_DEL(sched, pvt->DTMFsched);
 
+	/* Unlink us from the owner if we have one */
+	if (pvt->owner) {
+		ast_channel_lock(pvt->owner);
+		if (h323debug)
+			ast_debug(1, "Detaching from %s\n", pvt->owner->name);
+		pvt->owner->tech_pvt = NULL;
+		ast_channel_unlock(pvt->owner);
+	}
+
 	if (pvt->rtp) {
 		ast_rtp_instance_destroy(pvt->rtp);
 	}
 
+	if (pvt->udptl)
+		ast_udptl_destroy(pvt->udptl);
+
 	/* Free dsp used for in-band DTMF detection */
 	if (pvt->vad) {
 		ast_dsp_free(pvt->vad);
 	}
 	cleanup_call_details(&pvt->cd);
 
-	/* Unlink us from the owner if we have one */
-	if (pvt->owner) {
-		ast_channel_lock(pvt->owner);
-		if (h323debug)
-			ast_debug(1, "Detaching from %s\n", pvt->owner->name);
-		pvt->owner->tech_pvt = NULL;
-		ast_channel_unlock(pvt->owner);
-	}
 	cur = iflist;
 	while(cur) {
 		if (cur == pvt) {
@@ -828,6 +872,12 @@
 		else
 			fr = &ast_null_frame;
 		break;
+        case 4:
+		if (pvt->udptl)
+			fr = ast_udptl_read(pvt->udptl);   /* UDPTL for T.38 */
+		else
+			fr = &ast_null_frame;
+		break;
 	default:
 		ast_log(LOG_ERROR, "Unable to handle fd %d on channel %s\n", c->fdno, c->name);
 		fr = &ast_null_frame;
@@ -844,6 +894,15 @@
 	if (frame->frametype != AST_FRAME_VOICE) {
 		if (frame->frametype == AST_FRAME_IMAGE) {
 			return 0;
+		} else if (frame->frametype == AST_FRAME_MODEM) {
+            		if (pvt) {
+                    		ast_mutex_lock(&pvt->lock);
+                    		if (pvt->udptl &&
+                        	    (pvt->t38state == T38_STATE_NEGOTIATED))
+                            		res = ast_udptl_write(pvt->udptl, frame);
+                    		ast_mutex_unlock(&pvt->lock);
+				return res;
+			}
 		} else {
 			ast_log(LOG_WARNING, "Can't send %d type frames with H323 write\n", frame->frametype);
 			return 0;
@@ -866,6 +925,120 @@
 	return res;
 }
 
+static int handle_t38_control(struct oh323_pvt *pvt, const struct ast_control_t38_parameters *parameters, const char *token)
+{
+	int res = -1;
+	ast_mutex_lock(&pvt->lock);
+
+	switch (parameters->request_response) {
+	case AST_T38_REQUEST_NEGOTIATE:
+		if (pvt->t38state == T38_STATE_UNKNOWN) {
+			enum ast_t38_state old_state = pvt->t38state;
+			struct t38_profile profile;
+
+			ast_udptl_set_local_max_ifp(pvt->udptl, parameters->max_ifp);
+			profile.ast = *parameters;
+			profile.max_datagram = ast_udptl_get_local_max_datagram(pvt->udptl);
+			profile.error_correction_scheme = ast_udptl_get_error_correction_scheme(pvt->udptl);
+
+			pvt->t38state = T38_STATE_NEGOTIATING; /* callback can happen very quickly, therefore switch 
+								* to negotiating state right now, while pvt is under lock
+								*/
+			ast_mutex_unlock(&pvt->lock);
+			res = h323_request_mode_change(token, &profile);
+			ast_mutex_lock(&pvt->lock);
+
+			if (res != 0)
+				pvt->t38state = old_state;
+		} else if (pvt->t38state == T38_STATE_NEGOTIATED) {
+			/* already negotiated, send confirmation to bridged peer immediately */
+			struct ast_control_t38_parameters parameters = { .request_response = AST_T38_NEGOTIATED };
+			res = ast_queue_control_data(pvt->owner, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
+		} else if (pvt->t38state == T38_STATE_UNAVAILABLE) {
+			struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REFUSED };
+			res = ast_queue_control_data(pvt->owner, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
+		}
+		break;
+	case AST_T38_REQUEST_TERMINATE:
+		/* bridged peer requests to terminate T38 session */
+		if (pvt->t38state == T38_STATE_NEGOTIATED || pvt->t38state == T38_STATE_NEGOTIATING) {
+			struct t38_profile profile;
+			profile.ast = *parameters;
+
+			pvt->t38state = T38_STATE_NEGOTIATING; /* callback can happen very quickly, therefore switch 
+								* to negotiating state right now, while pvt is under lock
+								*/
+			ast_mutex_unlock(&pvt->lock);
+			res = h323_request_mode_change(token, &profile);
+			ast_mutex_lock(&pvt->lock);
+
+			if (res != 0)
+				/* failed to request change to audio mode */
+				pvt->t38state = pvt->t38state;
+		} else  {
+			/* already not active, send confirmation to bridged peer immediately */
+			struct ast_control_t38_parameters parameters = { .request_response = AST_T38_TERMINATED };
+			res = ast_queue_control_data(pvt->owner, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
+		}
+		break;
+	case AST_T38_REFUSED:
+		/* bridged peer indicates that he has refused to start T38 session */
+		if (pvt->t38state == T38_STATE_NEGOTIATING) {
+			struct t38_profile profile;
+			profile.ast = *parameters;
+			pvt->t38state = T38_STATE_UNKNOWN;
+
+			ast_mutex_unlock(&pvt->lock);
+			res = h323_request_mode_change(token, &profile);
+			ast_mutex_lock(&pvt->lock);
+		}
+		break;
+	case AST_T38_TERMINATED:
+		/* bridged peer indicates that he has terminated T38 session */
+		if (pvt->t38state == T38_STATE_NEGOTIATED || pvt->t38state == T38_STATE_NEGOTIATING) {
+			struct t38_profile profile;
+			profile.ast = *parameters;
+			if (pvt->t38state == T38_STATE_NEGOTIATED)
+				/* AST_T38_TERMINATED sent to us without request from our side.
+				 * This is a bug in bridged channel, but we will handle this anyway
+				 * by re-defining request.
+				 */
+				profile.ast.request_response = AST_T38_REQUEST_TERMINATE;
+			else
+				/* we got ack for our AST_T38_REQUEST_TERMINATE */
+				pvt->t38state = T38_STATE_UNKNOWN;
+
+			ast_mutex_unlock(&pvt->lock);
+			res = h323_request_mode_change(token, &profile);
+			ast_mutex_lock(&pvt->lock);
+		}
+		break;
+	case AST_T38_NEGOTIATED:
+		if (pvt->t38state == T38_STATE_NEGOTIATING) {
+			struct t38_profile profile;
+
+			ast_udptl_set_local_max_ifp(pvt->udptl, parameters->max_ifp);
+			profile.ast = *parameters;
+			profile.max_datagram = ast_udptl_get_local_max_datagram(pvt->udptl);
+			profile.error_correction_scheme = ast_udptl_get_error_correction_scheme(pvt->udptl);
+
+			ast_mutex_unlock(&pvt->lock);
+			res = h323_request_mode_change(token, &profile);
+			ast_mutex_lock(&pvt->lock);
+
+			/* state will be changed when T.38 channel will
+			 * be started in openh323
+			 */
+		}
+		break;
+	default:
+    		break;
+	}
+	
+	ast_mutex_unlock(&pvt->lock);
+	return res;
+}
+
 static int oh323_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen)
 {
 
@@ -874,6 +1047,9 @@
 	int res = -1;
 	int got_progress;
 
+	if (!pvt)
+		return -1;
+	
 	ast_mutex_lock(&pvt->lock);
 	token = (pvt->cd.call_token ? ast_strdup(pvt->cd.call_token) : NULL);
 	got_progress = pvt->got_progress;
@@ -937,6 +1113,14 @@
 	case AST_CONTROL_PROCEEDING:
 	case -1:
 		break;
+	case AST_CONTROL_T38_PARAMETERS:
+		if (datalen != sizeof(struct ast_control_t38_parameters))
+			ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. "
+                                           "Expected %d, got %d\n",
+                            		   (int)sizeof(enum ast_control_t38), (int)datalen);
+		else
+			res = handle_t38_control(pvt, data, token);
+            	break;
 	default:
 		ast_log(LOG_WARNING, "OH323: Don't know how to indicate condition %d on %s\n", condition, token);
 		break;
@@ -1017,6 +1201,42 @@
 	return 0;
 }
 
+static int __oh323_udptl_create(struct oh323_pvt *pvt)
+{
+	struct ast_sockaddr tmp;
+
+	if (pvt->udptl)
+		return 0;
+
+	ast_sockaddr_from_sin(&tmp, &bindaddr);
+	pvt->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, &tmp);
+	if (!pvt->udptl) {
+		/* t38pt_udptl is configured, but we failed to create UDPTL session,
+		 * this is error
+		 */
+		ast_log(LOG_WARNING, "Unable to create UDPTL session: %s\n", strerror(errno));
+		return -1;
+	}
+	pvt->t38state = T38_STATE_UNKNOWN; /* t38pt_udptl is enabled, but no fax negotiations have been done yet */
+
+	switch (pvt->options.t38pt_udptl_ec) {
+	case UDPTL_EC_FEC:
+		ast_udptl_set_error_correction_scheme(pvt->udptl, UDPTL_ERROR_CORRECTION_FEC);
+		break;
+	case UDPTL_EC_REDUNDANCY:
+		ast_udptl_set_error_correction_scheme(pvt->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
+		break;
+	default:
+		break;
+	}
+
+	ast_udptl_setnat(pvt->udptl, pvt->options.nat);
+
+	ast_udptl_setqos(pvt->udptl, tos, cos);
+	
+	return 0;
+}
+
 /*! \brief Private structure should be locked on a call */
 static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const char *host, const char *linkedid)
 {
@@ -1069,11 +1289,15 @@
 			ast_channel_set_fd(ch, 3, ast_rtp_instance_fd(pvt->vrtp, 1));
 		}
 #endif
-#ifdef T38_SUPPORT
+		if (pvt->options.t38pt_udptl && __oh323_udptl_create(pvt)) {
+			ast_hangup(ch);
+			return NULL;
+		}
+
 		if (pvt->udptl) {
 			ast_channel_set_fd(ch, 4, ast_udptl_fd(pvt->udptl));
 		}
-#endif
+
 		if (state == AST_STATE_RING) {
 			ch->rings = 1;
 		}
@@ -1165,6 +1389,7 @@
 	}
 	ast_copy_string(pvt->context, default_context, sizeof(pvt->context));
 	pvt->newstate = pvt->newcontrol = pvt->newdigit = pvt->update_rtp_info = pvt->DTMFsched = -1;
+	pvt->t38state = T38_STATE_UNAVAILABLE; /* we don't know if t38pt_udptl is enabled in peer/user */
 	ast_mutex_init(&pvt->lock);
 	/* Add to interface list */
 	ast_mutex_lock(&iflock);
@@ -1200,6 +1425,47 @@
 	return NULL;
 }
 
+int external_udptl_create(unsigned call_reference, const char * token, struct in_addr them, struct rtp_info *info)
+{
+	struct oh323_pvt *pvt;
+	struct sockaddr_in us;
+	struct ast_sockaddr tmp;
+
+	pvt = find_call_locked(call_reference, token);
+	if (!pvt) {
+		ast_log(LOG_ERROR, "Unable to find call %s(%d)\n", token, call_reference);
+		return -1;
+	}
+	if (!pvt->udptl) {
+		/* Strange, if this function if invoked, UDPTL should - it means we have provided T.38 capability
+		 * and UDPTL should be created
+		 */
+		ast_log(LOG_ERROR, "No UDPTL for call %s(%d)\n", token, call_reference);
+		ast_mutex_unlock(&pvt->lock);
+		return -1;
+	}
+
+	/* figure out our local RTP port and tell the H.323 stack about it */
+	ast_udptl_get_us(pvt->udptl, &tmp);
+	ast_sockaddr_to_sin(&tmp, &us);
+	ast_mutex_unlock(&pvt->lock);
+
+	if (!us.sin_addr.s_addr) { // 0.0.0.0
+		struct ast_sockaddr t;
+		struct sockaddr_in them_s;
+		them_s.sin_addr = them;
+		ast_sockaddr_from_sin(&t, &them_s);
+		ast_ouraddrfor(&t, &tmp);
+		ast_sockaddr_to_sin(&tmp, &us);
+	}
+
+	ast_copy_string(info->addr, ast_inet_ntoa(us.sin_addr), sizeof(info->addr));
+	info->port = ntohs(us.sin_port);
+	if (h323debug)
+		ast_debug(1, "Sending UDPTL 'US' %s:%d\n", info->addr, info->port);
+	return 0;
+}
+
 static int update_state(struct oh323_pvt *pvt, int state, int signal)
 {
 	if (!pvt)
@@ -1389,6 +1655,19 @@
 			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, "t38pt_udptl")) {
+		options->t38pt_udptl = ast_true(v->value);
+	} else if (!strcasecmp(v->name, "t38pt_udptl_ec")) {
+		if (!strcasecmp(v->value, "auto"))
+		    options->t38pt_udptl_ec = UDPTL_EC_AUTO;
+		else if (!strcasecmp(v->value, "fec"))
+		    options->t38pt_udptl_ec = UDPTL_EC_FEC;
+		else if (!strcasecmp(v->value, "redundancy"))
+		    options->t38pt_udptl_ec = UDPTL_EC_REDUNDANCY;
+		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, "t38pt_udptl_max_datagram")) {
+		options->t38pt_udptl_max_datagram = atoi(v->value);
 	} else if (!strcasecmp(v->name, "hold")) {
 		if (!strcasecmp(v->value, "none"))
 			options->holdHandling = ~0;
@@ -2109,6 +2388,129 @@
 	return;
 }
 
+int setup_udptl_connection(unsigned call_reference, const char *token, const struct sockaddr_in *them)
+{
+	struct oh323_pvt *p;
+	struct ast_sockaddr tmp;
+
+	if (h323debug)
+		ast_debug(1, "Setting up UDPTL connection for %s\n", token);
+
+	/* Find the call or allocate a private structure if call not found */
+	p = find_call_locked(call_reference, token);
+	if (!p) {
+		ast_log(LOG_ERROR, "Something is wrong: udptl\n");
+		return -1;
+	}
+	if (!p->udptl) {
+		ast_log(LOG_ERROR, "No UDPTL for call %s(%d)\n", token, call_reference);
+		ast_mutex_unlock(&p->lock);
+		return -1;
+	}
+
+	ast_sockaddr_from_sin(&tmp, them);
+	ast_udptl_set_peer(p->udptl, &tmp);
+
+	ast_mutex_unlock(&p->lock);
+
+	if (h323debug)
+		ast_debug(1, "UDPTL connection prepared for %s\n", token);
+
+	return 0;
+}
+
+int request_mode_change(unsigned call_reference, 
+			const char *token, 
+			struct t38_profile *profile)
+{
+	struct oh323_pvt *p = NULL;
+	int res = -1;
+
+	if(h323debug)
+		ast_debug(1, "Requesting mode change (%d) for %s\n", profile->ast.request_response, token);
+        
+	p = find_call_locked(call_reference, token);
+	if (!p) {
+		ast_log(LOG_ERROR, "Unable to find call %s(%d)\n", token, call_reference);
+		return -1;
+	}
+
+	if ((profile->ast.request_response == AST_T38_REQUEST_NEGOTIATE && p->t38state != T38_STATE_UNKNOWN) ||
+	    (profile->ast.request_response == AST_T38_REQUEST_TERMINATE && p->t38state != T38_STATE_NEGOTIATED && p->t38state != T38_STATE_NEGOTIATING) ||
+	    (profile->ast.request_response == AST_T38_NEGOTIATED && p->t38state != T38_STATE_NEGOTIATING) || 
+	    (profile->ast.request_response == AST_T38_TERMINATED && p->t38state != T38_STATE_NEGOTIATING) ||
+	    (profile->ast.request_response == AST_T38_REFUSED && p->t38state != T38_STATE_NEGOTIATING)) {
+		/* no need to pass this to bridged peer */
+		ast_log(LOG_ERROR, "Wrong request/response %d received in state %d for call %s(%d)\n", profile->ast.request_response, p->t38state, token, call_reference);
+		ast_mutex_unlock(&p->lock);
+		return -1;
+	}
+	    
+	if (profile->ast.request_response == AST_T38_REQUEST_NEGOTIATE || 
+	    profile->ast.request_response == AST_T38_NEGOTIATED) {
+		unsigned max_datagram = profile->max_datagram;
+		if (max_datagram < p->options.t38pt_udptl_max_datagram)
+			max_datagram = p->options.t38pt_udptl_max_datagram;
+
+		if (!p->udptl) {
+			/* Strange, if we accept T.38 mode change - it means we have provided T.38 capability
+			 * and UDPTL should be created
+			 */ 
+			ast_log(LOG_ERROR, "No UDPTL for call %s(%d)\n", token, call_reference);
+    			ast_mutex_unlock(&p->lock);
+			return -1;
+		}
+
+		ast_udptl_set_far_max_datagram(p->udptl, max_datagram);
+		ast_udptl_set_error_correction_scheme(p->udptl, profile->error_correction_scheme);
+		profile->ast.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
+        }
+
+        if (p->owner) {
+                while (p->owner && ast_channel_trylock(p->owner)) {
+                        ast_debug(1,"Failed to grab lock, trying again\n");
+                        DEADLOCK_AVOIDANCE(&p->lock);
+                }
+                if (!p->owner) {
+			ast_log(LOG_ERROR, "No owner for call %s(%d)\n", token, call_reference);
+    			ast_mutex_unlock(&p->lock);
+			return -1;
+		}
+        } else {
+		ast_log(LOG_ERROR, "No owner for call %s(%d)\n", token, call_reference);
+		ast_mutex_unlock(&p->lock);
+		return -1;
+	}
+
+	res = ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &profile->ast, sizeof(profile->ast));
+	if (res == 0) {
+		switch (profile->ast.request_response) {
+		case AST_T38_REQUEST_NEGOTIATE:
+		case AST_T38_REQUEST_TERMINATE:
+			p->t38state = T38_STATE_NEGOTIATING;
+			break;
+		case AST_T38_TERMINATED:
+		case AST_T38_REFUSED:
+			p->t38state = T38_STATE_UNKNOWN;
+			break;
+		case AST_T38_NEGOTIATED:
+			/* T.38 channel has been started in openh323 */
+			p->t38state = T38_STATE_NEGOTIATED;
+			break;
+		default:
+			break;
+		}
+	}
+
+	ast_channel_unlock(p->owner);
+	ast_mutex_unlock(&p->lock);
+
+	if(h323debug)
+		ast_debug(1, "Request mode change %s for %s\n", res ? "failed" : "done", token);
+
+	return res;
+}
+
 /*! \brief
   *	Call-back function to signal asterisk that the channel has been answered
   * Returns nothing
@@ -2506,16 +2908,17 @@
 		ast_debug(1, "DTMF payload on %s set to %d\n", token, payload);
 }
 
-static void set_peer_capabilities(unsigned call_reference, const char *token, int capabilities, struct ast_codec_pref *prefs)
+int set_peer_capabilities(unsigned call_reference, const char *token, int capabilities, struct ast_codec_pref *prefs, const struct t38_profile *t38profile)
 {
 	struct oh323_pvt *pvt;
-
+	int res = -1;
+	
 	if (h323debug)
 		ast_debug(1, "Got remote capabilities from connection %s\n", token);
 
 	pvt = find_call_locked(call_reference, token);
 	if (!pvt)
-		return;
+		return -1;
 	pvt->peercapability = capabilities;
 	pvt->jointcapability = pvt->options.capability & capabilities;
 	if (prefs) {
@@ -2538,7 +2941,32 @@
 			}
 		}
 	}
+
+	/* UDPTL can be not created yet (when servicing incoming call) */
+	if (pvt->options.t38pt_udptl && __oh323_udptl_create(pvt)) {
+		ast_log(LOG_ERROR, "Unable to create UDPTL for call %s(%d)\n", token, call_reference);
+		goto ERROR;
+	}
+
+	if (t38profile && pvt->udptl) {
+		unsigned max_datagram = t38profile->max_datagram;
+		if (max_datagram < pvt->options.t38pt_udptl_max_datagram)
+			max_datagram = pvt->options.t38pt_udptl_max_datagram;
+
+		if (pvt->options.t38pt_udptl_ec == UDPTL_EC_AUTO)
+			/* Use peer's options. We support all EC schemes, so in "auto" mode we take
+		         * what peer suggests us. Then we have more chances that he won't reject
+		         * RequestMode message from us when/if we will trigger T.38 mode switch.
+		         */
+			ast_udptl_set_error_correction_scheme(pvt->udptl, t38profile->error_correction_scheme);
+		ast_udptl_set_far_max_datagram(pvt->udptl, max_datagram);
+	}
+
+	res = 0;
+ERROR:
 	ast_mutex_unlock(&pvt->lock);
+
+	return res;
 }
 
 static void set_local_capabilities(unsigned call_reference, const char *token)
@@ -2546,19 +2974,39 @@
 	struct oh323_pvt *pvt;
 	int capability, dtmfmode, pref_codec;
 	struct ast_codec_pref prefs;
+	struct t38_profile t38profile;
+	int t38 = 0;
 
 	if (h323debug)
 		ast_debug(1, "Setting capabilities for connection %s\n", token);
 
 	pvt = find_call_locked(call_reference, token);
-	if (!pvt)
+	if (!pvt) {
+		ast_log(LOG_ERROR, "Unable to find call %s(%d)\n", token, call_reference);
 		return;
+	}
 	capability = (pvt->jointcapability) ? pvt->jointcapability : pvt->options.capability;
 	dtmfmode = pvt->options.dtmfmode;
 	prefs = pvt->options.prefs;
 	pref_codec = pvt->pref_codec;
+
+	/* UDPTL can be not created yet (when servicing incoming call) */
+	if (pvt->options.t38pt_udptl && __oh323_udptl_create(pvt)) {
+		ast_log(LOG_ERROR, "Unable to create UDPTL for call %s(%d)\n", token, call_reference);
+		ast_mutex_unlock(&pvt->lock);
+		return;
+	}
+	if (pvt->udptl) {
+		/* indicate that we support T.38 */
+		memset(&t38profile, 0, sizeof(t38profile));
+		t38profile.ast.rate = AST_T38_RATE_14400;
+		t38profile.ast.rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF;
+		t38profile.error_correction_scheme = ast_udptl_get_error_correction_scheme(pvt->udptl);
+		t38 = 1;
+	}
+
 	ast_mutex_unlock(&pvt->lock);
-	h323_set_capabilities(token, capability, dtmfmode, &prefs, pref_codec);
+	h323_set_capabilities(token, capability, dtmfmode, &prefs, pref_codec, t38 ? &t38profile : NULL);
 
 	if (h323debug) {
 		int i;
@@ -3290,6 +3738,22 @@
 	.update_peer = oh323_set_rtp_peer,
 };
 
+static struct ast_udptl *oh323_get_udptl_peer(struct ast_channel *chan)
+{
+	struct oh323_pvt *p;
+
+	p = chan->tech_pvt;
+	if (!p)
+		return NULL;
+	
+	return p->udptl;
+}
+
+static struct ast_udptl_protocol oh323_udptl = {
+	.type = "H323",
+	.get_udptl_info = oh323_get_udptl_peer,
+};
+
 static enum ast_module_load_result load_module(void)
 {
 	int res;
@@ -3349,6 +3813,8 @@
 
 		ast_rtp_glue_register(&oh323_rtp_glue);
 
+		ast_udptl_proto_register(&oh323_udptl);
+
 		/* Register our callback functions */
 		h323_callback_register(setup_incoming_call,
 						setup_outgoing_call,
@@ -3363,7 +3829,6 @@
 						set_dtmf_payload,
 						hangup_connection,
 						set_local_capabilities,
-						set_peer_capabilities,
 						remote_hold);
 		/* start the h.323 listener */
 		if (h323_start_listener(h323_signalling_port, bindaddr)) {
Index: include/asterisk/strings.h
===================================================================
--- include/asterisk/strings.h	(revision 320715)
+++ include/asterisk/strings.h	(working copy)
@@ -505,7 +505,7 @@
 char *ast_str_truncate(struct ast_str *buf, ssize_t len),
 {
 	if (len < 0) {
-		buf->__AST_STR_USED += ((ssize_t) abs(len)) > (ssize_t) buf->__AST_STR_USED ? -buf->__AST_STR_USED : len;
+		buf->__AST_STR_USED += ((ssize_t) abs((int)len)) > (ssize_t) buf->__AST_STR_USED ? -buf->__AST_STR_USED : len;
 	} else {
 		buf->__AST_STR_USED = len;
 	}
