//////////////////////////////////////////////////////////////////
//
// GkClient.cxx
//
// Copyright (c) Citron Network Inc. 2001-2003
//
// This work is published under the GNU Public License (GPL)
// see file COPYING for details.
// We also explicitely grant the right to link this code
// with the OpenH323 library.
//
// initial author: Chih-Wei Huang <cwhuang@linux.org.tw>
// initial version: 02/27/2002
//
//////////////////////////////////////////////////////////////////

#if defined(_WIN32) && (_MSC_VER <= 1200)
#pragma warning(disable:4786) // warning about too long debug symbol off
#pragma warning(disable:4284)
#endif

#include <ptlib.h>
#include <h323pdu.h> 
#include <h235auth.h>
#include "stl_supp.h"
#include "RasPDU.h"
#include "RasSrv.h"
#include "ProxyChannel.h"
#include "sigmsg.h"
#include "cisco.h"
#include "GkClient.h"

using std::vector;
using std::multimap;
using std::make_pair;
using std::for_each;
using std::mem_fun;
using std::bind1st;
using Routing::Route;

namespace {
const char* const EndpointSection = "Endpoint";
const char* const RewriteE164Section = "Endpoint::RewriteE164";
}

class AlternateGKs {
public:
	AlternateGKs(const PIPSocket::Address &, WORD);
	void Set(const H225_ArrayOf_AlternateGK &);
	bool Get(PIPSocket::Address &, WORD &);

private:
	typedef multimap<int, H225_TransportAddress> GKList;
	GKList AltGKs;
	GKList::iterator index;
	PIPSocket::Address pgkaddr, pgkport;
};

AlternateGKs::AlternateGKs(const PIPSocket::Address & gkaddr, WORD gkport)
{
	pgkaddr = gkaddr, pgkport = gkport;
}

void AlternateGKs::Set(const H225_ArrayOf_AlternateGK & agk)
{
	AltGKs.clear();
	for (PINDEX i = 0; i < agk.GetSize(); ++i) {
		const H225_AlternateGK & gk = agk[i];
		AltGKs.insert(make_pair(int(gk.m_priority), gk.m_rasAddress));
	}
	index = AltGKs.begin();
}

bool AlternateGKs::Get(PIPSocket::Address & gkaddr, WORD & gkport)
{ 
	if (AltGKs.size() > 0) {
		if (index == AltGKs.end()) {
			index = AltGKs.begin();
			// switch back to original GK
			gkaddr = pgkaddr;
			gkport = (WORD)pgkport;
			return false;
		}

		const H225_TransportAddress & rasAddress = (index++)->second;
		if (GetIPAndPortFromTransportAddr(rasAddress, gkaddr, gkport))
			return true;
		PTRACE(3, "GKC\tInvalid AlternateGK Address!" );
		return Get(gkaddr, gkport); // try next
	}
	return false;
}


class NATClient : public RegularJob {
public:
	NATClient(const H225_TransportAddress &, const H225_EndpointIdentifier &);

	// override from class RegularJob
	virtual void Stop();

private:
	// override from class Task
	virtual void Exec();

	bool DetectIncomingCall();
	void SendInfo(int);

	PIPSocket::Address gkip;
	WORD gkport;
	PString endpointId;
	CallSignalSocket *socket;
};

NATClient::NATClient(const H225_TransportAddress & addr, const H225_EndpointIdentifier & id)
{
	GetIPAndPortFromTransportAddr(addr, gkip, gkport);
	endpointId = id.GetValue();
	socket = 0;
	SetName("NATClient");
	Execute();
}

void NATClient::Stop()
{
	PWaitAndSignal lock(m_deletionPreventer);
	RegularJob::Stop();
	if (socket) {
		SendInfo(Q931::CallState_DisconnectRequest);
		socket->Close();
	}
}

void NATClient::Exec()
{
	ReadLock lockConfig(ConfigReloadMutex);
	
	socket = new CallSignalSocket;
	socket->SetPort(gkport);
	if (socket->Connect(gkip)) {
		PTRACE(2, "GKC\t" << socket->GetName() << " connected, wainting for incoming call");
		if (DetectIncomingCall()) {
			PTRACE(3, "GKC\tIncoming call detected");
			CreateJob(socket, &CallSignalSocket::Dispatch, "NAT call");
			socket = 0;
			return;
		}
	}
	delete socket;
	socket = 0;
	int retryInterval = GkConfig()->GetInteger(EndpointSection, "NATRetryInterval", 60);

	ReadUnlock unlockConfig(ConfigReloadMutex);
	Wait(retryInterval * 1000);
}

bool NATClient::DetectIncomingCall()
{
	while (socket->IsOpen()) {
		long retry = GkConfig()->GetInteger(
			EndpointSection, "NATKeepaliveInterval", 86400
			); // one day
		SendInfo(Q931::CallState_IncomingCallProceeding);

		ReadUnlock unlockConfig(ConfigReloadMutex);
		while (socket->IsOpen() && --retry > 0)
			if (socket->IsReadable(1000)) // one second
				return socket->IsOpen();
	}
	return false;
}

void NATClient::SendInfo(int state)
{
	Q931 information;
	information.BuildInformation(0, false);
	PBYTEArray buf, epid(endpointId, endpointId.GetLength(), false);
	information.SetIE(Q931::FacilityIE, epid);
	information.SetCallState(Q931::CallStates(state));
	information.Encode(buf);
	socket->TransmitData(buf);
}


class GRQRequester : public RasRequester {
public:
	GRQRequester(const PString &);
	~GRQRequester();

	// override from class RasRequester
	virtual bool SendRequest(const Address &, WORD, int = 2);

private:
	// override from class RasHandler
	virtual bool IsExpected(const RasMsg *) const;

	H225_RasMessage grq_ras;
};

GRQRequester::GRQRequester(const PString & gkid) : RasRequester(grq_ras)
{
	grq_ras.SetTag(H225_RasMessage::e_gatekeeperRequest);
	H225_GatekeeperRequest & grq = grq_ras;
	grq.m_requestSeqNum = GetSeqNum();
	grq.m_protocolIdentifier.SetValue(H225_ProtocolID);
	grq.m_endpointType.IncludeOptionalField(H225_EndpointType::e_gatekeeper);
	grq.IncludeOptionalField(H225_GatekeeperRequest::e_supportsAltGK);
	if (!gkid) {
		grq.IncludeOptionalField(H225_GatekeeperRequest::e_gatekeeperIdentifier);
		grq.m_gatekeeperIdentifier = gkid;
	}
	grq.IncludeOptionalField(H225_GatekeeperRequest::e_authenticationCapability);
	grq.IncludeOptionalField(H225_GatekeeperRequest::e_algorithmOIDs);
	H235AuthSimpleMD5 md5auth;
	md5auth.SetPassword("dummy"); // activate it
	md5auth.SetCapability(grq.m_authenticationCapability, grq.m_algorithmOIDs);
	H235AuthCAT catauth;
	catauth.SetPassword("dummy"); // activate it
	catauth.SetCapability(grq.m_authenticationCapability, grq.m_algorithmOIDs);
	m_rasSrv->RegisterHandler(this);
}

GRQRequester::~GRQRequester()
{
	m_rasSrv->UnregisterHandler(this);
}

bool GRQRequester::SendRequest(const Address & addr, WORD pt, int r)
{
	m_txAddr = addr, m_txPort = pt, m_retry = r;
	H225_GatekeeperRequest & grq = grq_ras;
	vector<Address> GKHome;
	Toolkit::Instance()->GetGKHome(GKHome);
	for (std::vector<Address>::iterator i = GKHome.begin(); i != GKHome.end(); ++i) {
		if ((IsLoopback(addr) || IsLoopback(*i)) && addr != *i)
			continue;
		RasListener *socket = m_rasSrv->SelectInterface(*i)->GetRasListener();
		grq.m_rasAddress = socket->GetRasAddress(addr == INADDR_BROADCAST ? *i : addr);
		if (addr == INADDR_BROADCAST)
			socket->SetOption(SO_BROADCAST, 1);
		socket->SendRas(grq_ras, addr, pt);
		if (addr == INADDR_BROADCAST)
			socket->SetOption(SO_BROADCAST, 0);
	}
	m_sentTime = PTime();
	return true;
}

bool GRQRequester::IsExpected(const RasMsg *ras) const
{
	if (ras->GetSeqNum() == GetSeqNum())
		if (ras->GetTag() == H225_RasMessage::e_gatekeeperRequest)
			return m_txAddr == INADDR_BROADCAST; // catch broadcasted GRQ to avoid loop
		else if (ras->GetTag() == H225_RasMessage::e_gatekeeperConfirm || ras->GetTag() == H225_RasMessage::e_gatekeeperReject)
			return (m_txAddr == INADDR_BROADCAST) ? true : ras->IsFrom(m_txAddr, m_txPort);
	return false;
}


// handler to process requests to GkClient
class GkClientHandler : public RasHandler {
public:
	typedef bool (GkClient::*Handler)(RasMsg *);

	GkClientHandler(GkClient *c, Handler h, unsigned t) : client(c), handlePDU(h), tag(t) {}

private:
	// override from class RasHandler
	virtual bool IsExpected(const RasMsg *ras) const;
	virtual void Process(RasMsg *);

	void OnRequest(RasMsg *ras);

	GkClient *client;
	Handler handlePDU;
	unsigned tag;
};

bool GkClientHandler::IsExpected(const RasMsg *ras) const
{
	return (ras->GetTag() == tag) && client->CheckFrom(ras);
}

void GkClientHandler::Process(RasMsg *ras)
{
	CreateJob(this, &GkClientHandler::OnRequest, ras, ras->GetTagName());
}

void GkClientHandler::OnRequest(RasMsg *ras)
{
	ReadLock lockConfig(ConfigReloadMutex);
	if ((client->*handlePDU)(ras))
		ras->Reply();
	delete ras;
}

namespace {
const long DEFAULT_TTL = 60;
const long DEFAULT_RRQ_RETRY = 3;
}

// class GkClient
GkClient::GkClient() 
	: m_rasSrv(RasServer::Instance()), m_registered(false), m_discoveryComplete(false),
	m_ttl(GkConfig()->GetInteger(EndpointSection, "TimeToLive", DEFAULT_TTL)),
	m_timer(0),
	m_retry(GkConfig()->GetInteger(EndpointSection, "RRQRetryInterval", DEFAULT_RRQ_RETRY)),
	m_rewriteInfo(NULL), m_natClient(NULL),
	m_parentVendor(ParentVendor_GnuGk), m_endpointType(EndpointType_Gateway),
	m_discoverParent(true)
{
	m_resend = m_retry;
	m_gkfailtime = m_retry * 128;
	
	m_gkport = 0;

	m_useAltGKPermanent = false;
	m_gkList = new AlternateGKs(m_gkaddr, m_gkport);

	m_handlers[0] = new GkClientHandler(this, &GkClient::OnURQ, H225_RasMessage::e_unregistrationRequest);
	m_handlers[1] = new GkClientHandler(this, &GkClient::OnBRQ, H225_RasMessage::e_bandwidthRequest);
	m_handlers[2] = new GkClientHandler(this, &GkClient::OnDRQ, H225_RasMessage::e_disengageRequest);
	m_handlers[3] = new GkClientHandler(this, &GkClient::OnIRQ, H225_RasMessage::e_infoRequest);
}

GkClient::~GkClient()
{
	DeleteObjectsInArray(m_handlers, m_handlers + 4);
	delete m_gkList;
	delete m_rewriteInfo;
	PTRACE(1, "GKC\tDelete GkClient");
}

void GkClient::OnReload()
{
	PConfig *cfg = GkConfig();
	
	if (IsRegistered())
		if (Toolkit::AsBool(cfg->GetString(EndpointSection, "UnregisterOnReload", "0")))
			SendURQ(); // TODO
		else
			Unregister();

	m_password = Toolkit::Instance()->ReadPassword(EndpointSection, "Password");
	m_retry = m_resend = cfg->GetInteger(EndpointSection, "RRQRetryInterval", DEFAULT_RRQ_RETRY);
	m_gkfailtime = m_retry * 128;
	m_authMode = -1;

	PCaselessString s = GkConfig()->GetString(EndpointSection, "Vendor", "GnuGk");
	if (s == "Generic" ||  s == "Unknown")
		m_parentVendor = ParentVendor_Generic;
	else if (s == "Cisco")
		m_parentVendor = ParentVendor_Cisco;
	else
		m_parentVendor = ParentVendor_GnuGk;

	s = GkConfig()->GetString(EndpointSection, "Type", "GnuGk");
	if (s[0] == 't' || s[0] == 'T') {
		m_endpointType = EndpointType_Terminal;
		m_prefixes.RemoveAll();
	} else {
		m_endpointType = EndpointType_Gateway;
		m_prefixes = cfg->GetString(EndpointSection, "Prefix", "").Tokenise(",;", FALSE);
	}
	
	m_discoverParent = Toolkit::AsBool(cfg->GetString(EndpointSection, "Discovery", "1"));

	m_h323Id = cfg->GetString(
		EndpointSection, "H323ID", (const char *)Toolkit::GKName()
		).Tokenise(" ,;\t", FALSE);
	m_e164 = cfg->GetString(EndpointSection, "E164", "").Tokenise(" ,;\t", FALSE);
	
	PIPSocket::Address gkaddr = m_gkaddr;
	WORD gkport = m_gkport;
	const PCaselessString gk(cfg->GetString(EndpointSection, "Gatekeeper", "no"));
	
	if (!IsRegistered())
		m_ttl = GkConfig()->GetInteger(EndpointSection, "TimeToLive", DEFAULT_TTL);

	
	if (gk == "no") {
		m_timer = 0;
		m_rrjReason = PString();
		return;
	} else if (gk == "auto") {
		m_gkaddr = INADDR_BROADCAST;
		m_gkport = GK_DEF_UNICAST_RAS_PORT;
		m_discoveryComplete = false;
	} else if (GetTransportAddress(gk, GK_DEF_UNICAST_RAS_PORT, m_gkaddr, m_gkport)) {
		m_discoveryComplete = (m_gkaddr == gkaddr) && (m_gkport == gkport);
	} else {
		// unresolvable?
		PTRACE(1, "GKC\tWarning: Can't resolve parent GK " << gk);
		return;
	}

	m_timer = 100;

	delete m_rewriteInfo;
	m_rewriteInfo = new Toolkit::RewriteData(cfg, RewriteE164Section);
}

void GkClient::CheckRegistration()
{
	if (m_timer > 0 && (PTime() - m_registeredTime) > m_timer)
		Register();
}

bool GkClient::CheckFrom(const RasMsg *ras) const
{
	return ras->IsFrom(m_gkaddr, m_gkport);
}

PString GkClient::GetParent() const
{
	return IsRegistered() ?
		AsString(m_gkaddr, m_gkport) + '\t' + m_endpointId.GetValue() :
		"not registered\t" + m_rrjReason;
}

bool GkClient::OnSendingRRQ(H225_RegistrationRequest &rrq)
{
	if (m_parentVendor == ParentVendor_GnuGk) {
		PIPSocket::Address sigip;
		if (rrq.m_callSignalAddress.GetSize() > 0
				&& GetIPFromTransportAddr(rrq.m_callSignalAddress[0], sigip)) {
			rrq.IncludeOptionalField(H225_RegistrationRequest::e_nonStandardData);
			rrq.m_nonStandardData.m_data = "IP=" + sigip.AsString();
		}
	}
	return true;
}

bool GkClient::OnSendingARQ(H225_AdmissionRequest &arq, Routing::AdmissionRequest &req)
{
	if (m_parentVendor == ParentVendor_Cisco) {
		Cisco_ARQnonStandardInfo nonStandardData;

		arq.IncludeOptionalField(H225_AdmissionRequest::e_nonStandardData);
		arq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
		H225_H221NonStandard & h221 = arq.m_nonStandardData.m_nonStandardIdentifier;
		h221.m_manufacturerCode = 18;
		h221.m_t35CountryCode = 181;
		h221.m_t35Extension = 0;
	
		PPER_Stream buff;
		nonStandardData.Encode(buff);
		buff.CompleteEncoding();
		arq.m_nonStandardData.m_data = buff;
	}
	return true;
}

bool GkClient::OnSendingLRQ(H225_LocationRequest &lrq, Routing::LocationRequest &/*req*/)
{
	if (m_parentVendor == ParentVendor_Cisco) {
		Cisco_LRQnonStandardInfo nonStandardData;

		nonStandardData.m_ttl = 6;

		if (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)) {
			nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo);
			nonStandardData.m_gatewaySrcInfo.SetSize(lrq.m_sourceInfo.GetSize());
			for (PINDEX i = 0; i < lrq.m_sourceInfo.GetSize(); i++)
				nonStandardData.m_gatewaySrcInfo[i] = lrq.m_sourceInfo[i];
		} else {
			if (m_h323Id.GetSize() > 0) {
				nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo);
				nonStandardData.m_gatewaySrcInfo.SetSize(m_h323Id.GetSize());
				for (PINDEX i = 0; i < m_h323Id.GetSize(); i++)
					H323SetAliasAddress(m_h323Id[i], nonStandardData.m_gatewaySrcInfo[i], H225_AliasAddress::e_h323_ID);
			}
			if (m_e164.GetSize() > 0) {
				nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo);
				PINDEX sz = nonStandardData.m_gatewaySrcInfo.GetSize();
				nonStandardData.m_gatewaySrcInfo.SetSize(sz + m_e164.GetSize());
				for (PINDEX i = 0; i < m_e164.GetSize(); i++)
					H323SetAliasAddress(m_e164[i], nonStandardData.m_gatewaySrcInfo[sz + i]);
			}
		}
		
		lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
		lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
		H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier;
		h221.m_manufacturerCode = 18;
		h221.m_t35CountryCode = 181;
		h221.m_t35Extension = 0;
	
		PPER_Stream buff;
		nonStandardData.Encode(buff);
		buff.CompleteEncoding();
		lrq.m_nonStandardData.m_data = buff;
	}
	return true;
}

bool GkClient::OnSendingARQ(H225_AdmissionRequest &arq, Routing::SetupRequest &req, bool /*answer*/)
{
	if (m_parentVendor == ParentVendor_Cisco) {
		const Q931 &setup = req.GetWrapper()->GetQ931();
		Cisco_ARQnonStandardInfo nonStandardData;

		if (setup.HasIE(Q931::CallingPartyNumberIE)) {
			PBYTEArray data = setup.GetIE(Q931::CallingPartyNumberIE);
			if (data.GetSize() >= 2 && (data[0] & 0x80) == 0x80) {
				nonStandardData.IncludeOptionalField(Cisco_ARQnonStandardInfo::e_callingOctet3a);
				nonStandardData.m_callingOctet3a = data[1];
			}
		}
		
		arq.IncludeOptionalField(H225_AdmissionRequest::e_nonStandardData);
		arq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
		H225_H221NonStandard & h221 = arq.m_nonStandardData.m_nonStandardIdentifier;
		h221.m_manufacturerCode = 18;
		h221.m_t35CountryCode = 181;
		h221.m_t35Extension = 0;
	
		PPER_Stream buff;
		nonStandardData.Encode(buff);
		buff.CompleteEncoding();
		arq.m_nonStandardData.m_data = buff;
	}
	return true;
}

bool GkClient::OnSendingARQ(H225_AdmissionRequest &arq, Routing::FacilityRequest &/*req*/)
{
	if (m_parentVendor == ParentVendor_Cisco) {
		Cisco_ARQnonStandardInfo nonStandardData;

		arq.IncludeOptionalField(H225_AdmissionRequest::e_nonStandardData);
		arq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
		H225_H221NonStandard & h221 = arq.m_nonStandardData.m_nonStandardIdentifier;
		h221.m_manufacturerCode = 18;
		h221.m_t35CountryCode = 181;
		h221.m_t35Extension = 0;
	
		PPER_Stream buff;
		nonStandardData.Encode(buff);
		buff.CompleteEncoding();
		arq.m_nonStandardData.m_data = buff;
	}
	return true;
}

bool GkClient::OnSendingDRQ(H225_DisengageRequest &/*drq*/, const callptr &/*call*/)
{
	return true;
}

bool GkClient::OnSendingURQ(H225_UnregistrationRequest &/*urq*/)
{
	return true;
}

bool GkClient::SendARQ(Routing::AdmissionRequest & arq_obj)
{
	const H225_AdmissionRequest & oarq = arq_obj.GetRequest();
	H225_RasMessage arq_ras;
	Requester<H225_AdmissionRequest> request(arq_ras, m_loaddr);
	H225_AdmissionRequest & arq = BuildARQ(arq_ras);

	if (oarq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) {
		arq.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo);
		arq.m_destinationInfo = oarq.m_destinationInfo;
	}
	if (oarq.HasOptionalField(H225_AdmissionRequest::e_destExtraCallInfo)) {
		arq.IncludeOptionalField(H225_AdmissionRequest::e_destExtraCallInfo);
		arq.m_destExtraCallInfo = oarq.m_destExtraCallInfo;
	}
	arq.m_srcInfo = oarq.m_srcInfo;
	RewriteE164(arq.m_srcInfo, true);
	arq.m_bandWidth = oarq.m_bandWidth;
	arq.m_callReferenceValue = oarq.m_callReferenceValue;
	arq.m_conferenceID = oarq.m_conferenceID;
	if (oarq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier)) {
		arq.IncludeOptionalField(H225_AdmissionRequest::e_callIdentifier);
		arq.m_callIdentifier = oarq.m_callIdentifier;
	}

	return OnSendingARQ(arq, arq_obj) && WaitForACF(request, &arq_obj);
}

bool GkClient::SendLRQ(Routing::LocationRequest & lrq_obj)
{
	const H225_LocationRequest & olrq = lrq_obj.GetRequest();
	H225_RasMessage lrq_ras;
	Requester<H225_LocationRequest> request(lrq_ras, m_loaddr);
	H225_LocationRequest & lrq = lrq_ras;
	lrq.m_destinationInfo = olrq.m_destinationInfo;
	lrq.m_replyAddress = m_rasSrv->GetRasAddress(m_loaddr);
	lrq.IncludeOptionalField(H225_LocationRequest::e_endpointIdentifier);
	lrq.m_endpointIdentifier = m_endpointId;
	if (olrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)) {
		lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo);
		lrq.m_sourceInfo = olrq.m_sourceInfo;
		RewriteE164(lrq.m_sourceInfo, true);
	}
	if (olrq.HasOptionalField(H225_LocationRequest::e_canMapAlias)) {
		lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias);
		lrq.m_canMapAlias = olrq.m_canMapAlias;
	}
	SetNBPassword(lrq);

	if (!OnSendingLRQ(lrq, lrq_obj))
		return false;
		
	request.SendRequest(m_gkaddr, m_gkport);
	if (request.WaitForResponse(5000)) {
		RasMsg *ras = request.GetReply();
		unsigned tag = ras->GetTag();
		if (tag == H225_RasMessage::e_locationConfirm) {
			H225_LocationConfirm & lcf = (*ras)->m_recvRAS;
			lrq_obj.AddRoute(Route("parent", lcf.m_callSignalAddress));
			RasMsg *oras = lrq_obj.GetWrapper();
			(*oras)->m_replyRAS.SetTag(H225_RasMessage::e_locationConfirm);
			H225_LocationConfirm & nlcf = (*oras)->m_replyRAS;
			if (lcf.HasOptionalField(H225_LocationConfirm::e_cryptoTokens)) {
				nlcf.IncludeOptionalField(H225_LocationConfirm::e_cryptoTokens);
				nlcf.m_cryptoTokens = lcf.m_cryptoTokens;
			}
			return true;
		}
	}
	return false;
}

bool GkClient::SendARQ(Routing::SetupRequest & setup_obj, bool answer)
{
	H225_RasMessage arq_ras;
	Requester<H225_AdmissionRequest> request(arq_ras, m_loaddr);
	H225_AdmissionRequest & arq = BuildARQ(arq_ras);

	H225_Setup_UUIE & setup = setup_obj.GetRequest();
	arq.m_callReferenceValue = setup_obj.GetWrapper()->GetCallReference();
	arq.m_conferenceID = setup.m_conferenceID;
	if (setup.HasOptionalField(H225_Setup_UUIE::e_callIdentifier)) {
		arq.IncludeOptionalField(H225_AdmissionRequest::e_callIdentifier);
		arq.m_callIdentifier = setup.m_callIdentifier;
	}
	if (setup.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) {
		arq.m_srcInfo = setup.m_sourceAddress;
		if (!answer)
			RewriteE164(arq.m_srcInfo, true);
	} else {
		// no sourceAddress privided in Q.931 Setup?
		// since srcInfo is mandatory, set my aliases as the srcInfo
		if (m_h323Id.GetSize() > 0) {
			arq.m_srcInfo.SetSize(1);
			H323SetAliasAddress(m_h323Id[0], arq.m_srcInfo[0], H225_AliasAddress::e_h323_ID);
		}
		if (m_e164.GetSize() > 0) {
			PINDEX sz = arq.m_srcInfo.GetSize();
			arq.m_srcInfo.SetSize(sz + 1);
			H323SetAliasAddress(m_e164[0], arq.m_srcInfo[sz]);
		}
	}
	if (setup.HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) {
		arq.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo);
		arq.m_destinationInfo = setup.m_destinationAddress;
		if (answer)
			RewriteE164(arq.m_destinationInfo, true);
	}
	arq.m_answerCall = answer;
	// workaround for bandwidth, as OpenH323 library :p
	arq.m_bandWidth = 1280;

	return OnSendingARQ(arq, setup_obj, answer)
		&& WaitForACF(request, answer ? 0 : &setup_obj);
}

bool GkClient::SendARQ(Routing::FacilityRequest & facility_obj)
{
	H225_RasMessage arq_ras;
	Requester<H225_AdmissionRequest> request(arq_ras, m_loaddr);
	H225_AdmissionRequest & arq = BuildARQ(arq_ras);

	H225_Facility_UUIE & facility = facility_obj.GetRequest();
	arq.m_callReferenceValue = facility_obj.GetWrapper()->GetCallReference();
	if (facility.HasOptionalField(H225_Facility_UUIE::e_conferenceID))
		arq.m_conferenceID = facility.m_conferenceID;
	if (facility.HasOptionalField(H225_Facility_UUIE::e_callIdentifier)) {
		arq.IncludeOptionalField(H225_AdmissionRequest::e_callIdentifier);
		arq.m_callIdentifier = facility.m_callIdentifier;
	}
	if (m_h323Id.GetSize() > 0) {
		arq.m_srcInfo.SetSize(1);
		H323SetAliasAddress(m_h323Id[0], arq.m_srcInfo[0], H225_AliasAddress::e_h323_ID);
	}
	if (m_e164.GetSize() > 0) {
		PINDEX sz = arq.m_srcInfo.GetSize();
		arq.m_srcInfo.SetSize(sz + 1);
		H323SetAliasAddress(m_e164[0], arq.m_srcInfo[sz]);
	}
	if (facility.HasOptionalField(H225_Facility_UUIE::e_alternativeAliasAddress)) {
		arq.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo);
		arq.m_destinationInfo = facility.m_alternativeAliasAddress;
	}
	arq.m_answerCall = false;
	// workaround for bandwidth, as OpenH323 library :p
	arq.m_bandWidth = 1280;

	return OnSendingARQ(arq, facility_obj) && WaitForACF(request, &facility_obj);
}

void GkClient::SendDRQ(const callptr & call)
{
	H225_RasMessage drq_ras;
	Requester<H225_DisengageRequest> request(drq_ras, m_loaddr);
	H225_DisengageRequest & drq = drq_ras;
	call->BuildDRQ(drq, H225_DisengageReason::e_normalDrop);
	drq.IncludeOptionalField(H225_DisengageRequest::e_gatekeeperIdentifier);
	drq.m_gatekeeperIdentifier = m_gatekeeperId;
	drq.m_endpointIdentifier = m_endpointId;
	drq.m_answeredCall = !call->GetCallingParty();
	SetPassword(drq);

	if (OnSendingDRQ(drq, call)) {
		request.SendRequest(m_gkaddr, m_gkport);
		request.WaitForResponse(3000);
	}
	// ignore response
}

void GkClient::SendURQ()
{
	// the order is important: build URQ, close NAT socket, then send URQ
	H225_RasMessage urq_ras;
	urq_ras.SetTag(H225_RasMessage::e_unregistrationRequest);
	H225_UnregistrationRequest & urq = urq_ras;
	urq.m_requestSeqNum = m_rasSrv->GetRequestSeqNum();
	urq.IncludeOptionalField(H225_UnregistrationRequest::e_gatekeeperIdentifier);
	urq.m_gatekeeperIdentifier = m_gatekeeperId;
	urq.IncludeOptionalField(H225_UnregistrationRequest::e_endpointIdentifier);
	urq.m_endpointIdentifier = m_endpointId;
	SetCallSignalAddress(urq.m_callSignalAddress);
	SetPassword(urq);

	Unregister();
	
	if (OnSendingURQ(urq))
		m_rasSrv->SendRas(urq_ras, m_gkaddr, m_gkport, m_loaddr);
}

bool GkClient::RewriteE164(H225_AliasAddress & alias, bool fromInternal)
{
	if (alias.GetTag() != H225_AliasAddress::e_dialedDigits) 
		return false;
		        
	PString e164 = AsString(alias, FALSE);

	bool changed = RewriteString(e164, fromInternal);
	if (changed)
		H323SetAliasAddress(e164, alias);

	return changed;
}

bool GkClient::RewriteE164(H225_ArrayOf_AliasAddress & aliases, bool fromInternal)
{
	bool changed = false;
	for (PINDEX i = 0; i < aliases.GetSize(); ++i)
		if (RewriteE164(aliases[i], fromInternal))
			changed = true;
	return changed;
}

bool GkClient::RewriteE164(
	SetupMsg &setup, 
	bool fromInternal
	)
{
	Q931 &q931 = setup.GetQ931();
	H225_Setup_UUIE &setupBody = setup.GetUUIEBody();
	unsigned plan, type;
	PString number;

	bool result = false;
	if (fromInternal) {
		bool r1 = q931.GetCallingPartyNumber(number, &plan, &type);
		if (r1 && (result = RewriteString(number, true))) {
			q931.SetCallingPartyNumber(number, plan, type);
			setup.SetChanged();
		}
		if ((!r1 || result) && setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress))
			if (RewriteE164(setupBody.m_sourceAddress, true)) {
				result = true;
				setup.SetUUIEChanged();
			}
	} else {
		bool r1 = q931.GetCalledPartyNumber(number, &plan, &type);
		if (r1 && (result = RewriteString(number, false))) {
			q931.SetCalledPartyNumber(number, plan, type);
			setup.SetChanged();
		}
		if ((!r1 || result) && setupBody.HasOptionalField(H225_Setup_UUIE::e_destinationAddress))
			if (RewriteE164(setupBody.m_destinationAddress, false)) {
				result = true;
				setup.SetUUIEChanged();
			}
	}
	return result;
}

bool GkClient::Discovery()
{
	m_loaddr = m_gkaddr;
	m_gatekeeperId = PString();

	if (!m_discoverParent)
		return true;

	GRQRequester request(GkConfig()->GetString(EndpointSection, "GatekeeperIdentifier", ""));
	// the spec: timeout value 5 sec, retry count 2
	request.SendRequest(m_gkaddr, m_gkport);
	while (request.WaitForResponse(5000)) {
		RasMsg *ras = request.GetReply();
		if (ras->GetTag() == H225_RasMessage::e_gatekeeperConfirm) {
			H225_GatekeeperConfirm & gcf = (*ras)->m_recvRAS;
			m_loaddr = (*ras)->m_localAddr;
			if (gcf.HasOptionalField(H225_GatekeeperConfirm::e_gatekeeperIdentifier))
				m_gatekeeperId = gcf.m_gatekeeperIdentifier;
			GetIPAndPortFromTransportAddr(gcf.m_rasAddress, m_gkaddr, m_gkport);
			if (gcf.HasOptionalField(H225_GatekeeperConfirm::e_authenticationMode))
				m_authMode = gcf.m_authenticationMode.GetTag();
			PTRACE(2, "GKC\tDiscover GK " << AsString(m_gkaddr, m_gkport) << " at " << m_loaddr);
			return true;
		} else if (ras->GetTag() == H225_RasMessage::e_gatekeeperReject) {
			H225_GatekeeperReject & grj = (*ras)->m_recvRAS;
			if (grj.HasOptionalField(H225_GatekeeperReject::e_altGKInfo)) {
				m_gkList->Set(grj.m_altGKInfo.m_alternateGatekeeper);
				m_useAltGKPermanent = grj.m_altGKInfo.m_altGKisPermanent;
				break;
			}
		}
	}
	return false;
}

void GkClient::Register()
{
	PWaitAndSignal lock(m_rrqMutex);
	m_rrjReason = "no response";
	if (!IsRegistered() && !m_discoveryComplete)
		while (!(m_discoveryComplete = Discovery()))
			if (!GetAltGK())
				return;

	H225_RasMessage rrq_ras;
	Requester<H225_RegistrationRequest> request(rrq_ras, m_loaddr);
	BuildRRQ(rrq_ras);
	OnSendingRRQ(rrq_ras);
	request.SendRequest(m_gkaddr, m_gkport);
	m_registeredTime = PTime();
	if (request.WaitForResponse(m_retry * 1000)) {
		RasMsg *ras = request.GetReply();
		switch (ras->GetTag())
		{
			case H225_RasMessage::e_registrationConfirm:
				OnRCF(ras);
				return;
			case H225_RasMessage::e_registrationReject:
				OnRRJ(ras);
				break;
		}
	}
	GetAltGK();
}

void GkClient::Unregister()
{
	if (m_natClient) {
		m_natClient->Stop();
		m_natClient = NULL;
	}
	for_each(m_handlers, m_handlers + 4, bind1st(mem_fun(&RasServer::UnregisterHandler), m_rasSrv));
	m_registered = false;
}

bool GkClient::GetAltGK()
{
	Unregister(); // always re-register
	bool result = false;
	if (Toolkit::AsBool(GkConfig()->GetString(EndpointSection, "UseAlternateGK", "1")))
		result = m_gkList->Get(m_gkaddr, m_gkport);
	PString altgk(AsString(m_gkaddr, m_gkport));
	if (m_useAltGKPermanent)
		Toolkit::Instance()->SetConfig(1, EndpointSection, "Gatekeeper", altgk);
	if (result) {
		m_timer = 100, m_resend = m_retry, m_discoveryComplete = false;
		PTRACE(1, "GKC\tUse Alternate GK " << altgk << (m_useAltGKPermanent ? " permanently" : " temporarily"));
	} else {
		// if no alternate gatekeeper found
		// increase our resent time to avoid flooding the parent
		m_timer = (m_resend *= 2) * 1000;
		if (m_resend >= m_gkfailtime) {
			// FIXME: not thread-safed
			Toolkit::Instance()->GetRouteTable()->InitTable();
			m_resend = m_gkfailtime;
		}
	}
	return result;
}

void GkClient::BuildRRQ(H225_RegistrationRequest & rrq)
{
	rrq.m_protocolIdentifier.SetValue(H225_ProtocolID);
	rrq.m_discoveryComplete = m_discoveryComplete;
	SetRasAddress(rrq.m_rasAddress);
	SetCallSignalAddress(rrq.m_callSignalAddress);
	rrq.IncludeOptionalField(H225_RegistrationRequest::e_supportsAltGK);
	IsRegistered() ? BuildLightWeightRRQ(rrq) : BuildFullRRQ(rrq);
	SetPassword(rrq);
}

void GkClient::BuildFullRRQ(H225_RegistrationRequest & rrq)
{
	rrq.m_terminalType.IncludeOptionalField(H225_EndpointType::e_gatekeeper);

	PINDEX as, p;
	if (m_endpointType == EndpointType_Terminal) {
		rrq.m_terminalType.IncludeOptionalField(H225_EndpointType::e_terminal);
	} else {
		rrq.m_terminalType.IncludeOptionalField(H225_EndpointType::e_gateway);
		as = m_prefixes.GetSize();
		if (as > 0) {
			rrq.m_terminalType.m_gateway.IncludeOptionalField(H225_GatewayInfo::e_protocol);
			rrq.m_terminalType.m_gateway.m_protocol.SetSize(1);
			H225_SupportedProtocols & protocol = rrq.m_terminalType.m_gateway.m_protocol[0];
			protocol.SetTag(H225_SupportedProtocols::e_voice);
			H225_VoiceCaps & voicecap = (H225_VoiceCaps &)protocol;
			voicecap.m_supportedPrefixes.SetSize(as);
			for (PINDEX p = 0; p < as; ++p)
				H323SetAliasAddress(m_prefixes[p].Trim(), voicecap.m_supportedPrefixes[p].m_prefix);
		}
//		rrq.IncludeOptionalField(H225_RegistrationRequest::e_multipleCalls);
//		rrq.m_multipleCalls = FALSE;
	}

	rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias);
	as = m_h323Id.GetSize();
	rrq.m_terminalAlias.SetSize(as);
	for (p = 0; p < as; ++p)
		H323SetAliasAddress(m_h323Id[p], rrq.m_terminalAlias[p], H225_AliasAddress::e_h323_ID);

	PINDEX s = m_e164.GetSize() + as;
	rrq.m_terminalAlias.SetSize(s);
	for (p = as; p < s; ++p)
		H323SetAliasAddress(m_e164[p-as], rrq.m_terminalAlias[p]);

	int ttl = GkConfig()->GetInteger(EndpointSection, "TimeToLive", DEFAULT_TTL);
	if (ttl > 0) {
		rrq.IncludeOptionalField(H225_RegistrationRequest::e_timeToLive);
		rrq.m_timeToLive = ttl;
	}

	H225_VendorIdentifier & vendor = rrq.m_endpointVendor;
	vendor.IncludeOptionalField(H225_VendorIdentifier::e_productId);
	vendor.m_productId = PString(PString::Printf, "GNU Gatekeeper on %s %s %s, %s %s", (const unsigned char*)(PProcess::GetOSName()), (const unsigned char*)(PProcess::GetOSHardware()), (const unsigned char*)(PProcess::GetOSVersion()) ,__DATE__, __TIME__);
	vendor.IncludeOptionalField(H225_VendorIdentifier::e_versionId);
	vendor.m_versionId = "Version " + PProcess::Current().GetVersion();

	// set user provided endpointIdentifier, if any
	PString endpointId(GkConfig()->GetString(EndpointSection, "EndpointIdentifier", ""));
	if (!endpointId) {
		rrq.IncludeOptionalField(H225_RegistrationRequest::e_endpointIdentifier);
		rrq.m_endpointIdentifier = endpointId;
	}
	// set gatekeeperIdentifier found in discovery procedure
	if (!m_gatekeeperId.GetValue()) {
		rrq.IncludeOptionalField(H225_RegistrationRequest::e_gatekeeperIdentifier);
		rrq.m_gatekeeperIdentifier = m_gatekeeperId;
	}
	rrq.m_keepAlive = FALSE;
}

void GkClient::BuildLightWeightRRQ(H225_RegistrationRequest & rrq)
{
	rrq.IncludeOptionalField(H225_RegistrationRequest::e_endpointIdentifier);
	rrq.m_endpointIdentifier = m_endpointId;
	rrq.IncludeOptionalField(H225_RegistrationRequest::e_gatekeeperIdentifier);
	rrq.m_gatekeeperIdentifier = m_gatekeeperId;
	rrq.m_keepAlive = TRUE;
}

bool GkClient::WaitForACF(RasRequester & request, Routing::RoutingRequest *robj)
{
	request.SendRequest(m_gkaddr, m_gkport);
	if (request.WaitForResponse(5000)) {
		RasMsg *ras = request.GetReply();
		if (ras->GetTag() == H225_RasMessage::e_admissionConfirm) {
			if (robj) {
				H225_AdmissionConfirm & acf = (*ras)->m_recvRAS;
				Route route("parent", acf.m_destCallSignalAddress);
				route.m_flags |= Route::e_toParent;
				robj->AddRoute(route);
			}
			return true;
		}
		if (ras->GetTag() == H225_RasMessage::e_admissionReject)
			OnARJ(ras);
	}
	return false;
}

H225_AdmissionRequest & GkClient::BuildARQ(H225_AdmissionRequest & arq)
{
	arq.m_callType.SetTag(H225_CallType::e_pointToPoint);

	arq.m_endpointIdentifier = m_endpointId;

	arq.IncludeOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress);
	arq.m_srcCallSignalAddress = m_rasSrv->GetCallSignalAddress(m_loaddr);

	arq.IncludeOptionalField(H225_AdmissionRequest::e_canMapAlias);
	arq.m_canMapAlias = TRUE;

	arq.IncludeOptionalField(H225_AdmissionRequest::e_gatekeeperIdentifier);
	arq.m_gatekeeperIdentifier = m_gatekeeperId;

	SetPassword(arq);

	return arq;
}

void GkClient::OnRCF(RasMsg *ras)
{
	H225_RegistrationConfirm & rcf = (*ras)->m_recvRAS;
	if (!IsRegistered()) {
		PTRACE(2, "GKC\tRegister with " << AsString(m_gkaddr, m_gkport) << " successfully");
		m_registered = true;
		m_endpointId = rcf.m_endpointIdentifier;
		m_gatekeeperId = rcf.m_gatekeeperIdentifier;
		if (rcf.HasOptionalField(H225_RegistrationConfirm::e_alternateGatekeeper))
			m_gkList->Set(rcf.m_alternateGatekeeper);
		for_each(m_handlers, m_handlers + 4, bind1st(mem_fun(&RasServer::RegisterHandler), m_rasSrv));
	}
	
	// Not all RCF contain TTL, in that case keep old value
	if (rcf.HasOptionalField(H225_RegistrationConfirm::e_timeToLive)) {
		m_ttl = PMAX(rcf.m_timeToLive - m_retry, 30);
		// Have it reregister at 3/4 of TimeToLive, otherwise the parent
		// might go out of sync and ends up sending an URQ
		m_ttl = (m_ttl / 4) * 3;
	}
	
	m_timer = m_ttl * 1000;
	m_resend = m_retry;

	// NAT handling
	if (rcf.HasOptionalField(H225_RegistrationConfirm::e_nonStandardData))
		if (rcf.m_nonStandardData.m_data.AsString().Find("NAT=") == 0)
			if (!m_natClient && rcf.m_callSignalAddress.GetSize() > 0)
				m_natClient = new NATClient(rcf.m_callSignalAddress[0], m_endpointId);
}

void GkClient::OnRRJ(RasMsg *ras)
{
	H225_RegistrationReject & rrj = (*ras)->m_recvRAS;
	m_rrjReason =  "Reason: " + rrj.m_rejectReason.GetTagName();
	PTRACE(1, "GKC\tRegistration Rejected: " << rrj.m_rejectReason.GetTagName());

	if (rrj.HasOptionalField(H225_RegistrationReject::e_altGKInfo)) {
		m_gkList->Set(rrj.m_altGKInfo.m_alternateGatekeeper);
		m_useAltGKPermanent = rrj.m_altGKInfo.m_altGKisPermanent;
		GetAltGK();
	} else if (rrj.m_rejectReason.GetTag() == H225_RegistrationRejectReason::e_fullRegistrationRequired) {
		SendURQ();
		m_timer = 100;
		Toolkit::Instance()->GetRouteTable()->InitTable();
	}
}

void GkClient::OnARJ(RasMsg *ras)
{
	H225_AdmissionReject & arj = (*ras)->m_recvRAS;
	if (arj.HasOptionalField(H225_AdmissionReject::e_altGKInfo)) {
		m_gkList->Set(arj.m_altGKInfo.m_alternateGatekeeper);
		m_useAltGKPermanent = arj.m_altGKInfo.m_altGKisPermanent;
		GetAltGK();
	} else if (arj.m_rejectReason.GetTag() == H225_AdmissionRejectReason::e_callerNotRegistered) {
		Unregister();
		m_timer = 100; // re-register again
	}
}

bool GkClient::OnURQ(RasMsg *ras)
{
	Unregister();

	m_registeredTime = PTime();
	H225_UnregistrationRequest & urq = (*ras)->m_recvRAS;
	switch (urq.m_reason.GetTag())
	{
		case H225_UnregRequestReason::e_reregistrationRequired:
		case H225_UnregRequestReason::e_ttlExpired:
			m_timer = 500;
			break;

		default:
			m_timer = m_retry * 1000;
			if (urq.HasOptionalField(H225_UnregistrationRequest::e_alternateGatekeeper)) {
				m_gkList->Set(urq.m_alternateGatekeeper);
				GetAltGK();
			}
			break;
	}

	(*ras)->m_replyRAS.SetTag(H225_RasMessage::e_unregistrationConfirm);
	H225_UnregistrationConfirm & ucf = (*ras)->m_replyRAS;
	ucf.m_requestSeqNum = urq.m_requestSeqNum;
	return true;
}

bool GkClient::OnDRQ(RasMsg *ras)
{
	H225_DisengageRequest & drq = (*ras)->m_recvRAS;
	if (callptr call = drq.HasOptionalField(H225_DisengageRequest::e_callIdentifier) ? CallTable::Instance()->FindCallRec(drq.m_callIdentifier) : CallTable::Instance()->FindCallRec(drq.m_callReferenceValue))
		call->Disconnect(true);

	(*ras)->m_replyRAS.SetTag(H225_RasMessage::e_disengageConfirm); 
	H225_DisengageConfirm & dcf = (*ras)->m_replyRAS;
	dcf.m_requestSeqNum = drq.m_requestSeqNum;
	return true;
}

bool GkClient::OnBRQ(RasMsg *ras)
{
	// lazy implementation, just reply confirm
	H225_BandwidthRequest & brq = (*ras)->m_recvRAS;
	(*ras)->m_replyRAS.SetTag(H225_RasMessage::e_bandwidthConfirm);
	H225_BandwidthConfirm & bcf = (*ras)->m_replyRAS;
	bcf.m_requestSeqNum = brq.m_requestSeqNum;
	bcf.m_bandWidth = brq.m_bandWidth;
	return true;
}

bool GkClient::OnIRQ(RasMsg *ras)
{
	// TODO
	H225_InfoRequest & irq = (*ras)->m_recvRAS;
	(*ras)->m_replyRAS.SetTag(H225_RasMessage::e_infoRequestResponse);
	H225_InfoRequestResponse & irr = (*ras)->m_replyRAS;
	irr.m_requestSeqNum = irq.m_requestSeqNum;
	return true;
}

bool GkClient::RewriteString(PString & alias, bool fromInternal) const
{
	if (!m_rewriteInfo)
		return false;
	for (PINDEX i = 0; i < m_rewriteInfo->Size(); ++i) {
		PString prefix, insert;
		if (fromInternal) {
			insert = m_rewriteInfo->Key(i);
			prefix = m_rewriteInfo->Value(i);
		} else {
			prefix = m_rewriteInfo->Key(i);
			insert = m_rewriteInfo->Value(i);
		}
		int len = prefix.GetLength();
		if (len == 0 || strncmp(prefix, alias, len) == 0){
			PString result = insert + alias.Mid(len);
			PTRACE(2, "GKC\tRewritePString: " << alias << " to " << result);
			alias = result;
			return true;
		}
	}
	return false;
}

void GkClient::SetClearTokens(H225_ArrayOf_ClearToken & clearTokens, const PString & id)
{
	clearTokens.RemoveAll();
	H235AuthCAT auth;
	// avoid copying for thread-safely
	auth.SetLocalId((const char *)id);
	auth.SetPassword((const char *)m_password);
	H225_ArrayOf_CryptoH323Token dumbTokens;
	auth.PrepareTokens(clearTokens, dumbTokens);
}

void GkClient::SetCryptoTokens(H225_ArrayOf_CryptoH323Token & cryptoTokens, const PString & id)
{
	cryptoTokens.RemoveAll();
	H235AuthSimpleMD5 auth;
	// avoid copying for thread-safely
	auth.SetLocalId((const char *)id);
	auth.SetPassword((const char *)m_password);
	H225_ArrayOf_ClearToken dumbTokens;
	auth.PrepareTokens(dumbTokens, cryptoTokens);
}

void GkClient::SetRasAddress(H225_ArrayOf_TransportAddress & addr)
{
	addr.SetSize(1);
	addr[0] = m_rasSrv->GetRasAddress(m_loaddr);
}

void GkClient::SetCallSignalAddress(H225_ArrayOf_TransportAddress & addr)
{
	addr.SetSize(1);
	addr[0] = m_rasSrv->GetCallSignalAddress(m_loaddr);
}

void GkClient::SetNBPassword(
	H225_LocationRequest& lrq, /// LRQ message to be filled with tokens
	const PString& id // login name
	)
{
	if (!m_password) {
		lrq.IncludeOptionalField(H225_LocationRequest::e_cryptoTokens), SetCryptoTokens(lrq.m_cryptoTokens, id);
		lrq.IncludeOptionalField(H225_LocationRequest::e_tokens), SetClearTokens(lrq.m_tokens, id);
	}
}


syntax highlighted by Code2HTML, v. 0.9.1