//////////////////////////////////////////////////////////////////
//
// RasPDU.h
//
// Define RAS PDU for GNU Gatekeeper
// Avoid including large h225.h in RasSrv.h
//
// 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: 05/02/2003
//
//////////////////////////////////////////////////////////////////

#ifndef RASPDU_H
#define RASPDU_H "@(#) $Id: RasPDU.h,v 1.9 2006/04/14 13:56:19 willamowius Exp $"

#include <list>
#include "yasocket.h"
#include "factory.h"
#include "rasinfo.h"


class Toolkit;
class GkStatus;
class RegistrationTable;
class CallTable;
class RasListener;
class MulticastListener;
class CallSignalListener;
class StatusListener;
class RasServer;

const unsigned MaxRasTag = H225_RasMessage::e_serviceControlResponse;

struct GatekeeperMessage {
	PPER_Stream m_rasPDU;
	H225_RasMessage m_recvRAS;
	H225_RasMessage m_replyRAS;
	PIPSocket::Address m_peerAddr;
	WORD m_peerPort;
	PIPSocket::Address m_localAddr;
	RasListener *m_socket;

	unsigned GetTag() const { return m_recvRAS.GetTag(); }
	const char *GetTagName() const;
	bool Read(RasListener *);
	bool Reply() const;
};

class RasListener : public UDPSocket {
public:
	RasListener(const Address &, WORD);
	~RasListener();

	GatekeeperMessage *ReadRas();
	bool SendRas(const H225_RasMessage &, const Address &, WORD);

	WORD GetSignalPort() const { return m_signalPort; }
	void SetSignalPort(WORD pt) { m_signalPort = pt; }

	Address GetLocalAddr(const Address &) const;
	H225_TransportAddress GetRasAddress(const Address &) const;
	H225_TransportAddress GetCallSignalAddress(const Address &) const;

	// new virtual function
	// filter out unwanted message to the listener by returning false
	virtual bool Filter(GatekeeperMessage *) const;

protected:
	Address m_ip;
	PMutex m_wmutex;
	WORD m_signalPort;
	bool m_virtualInterface;
};

class RasMsg : public Task {
public:
	virtual ~RasMsg() { delete m_msg; } //PTRACE(1, "Delete " << m_msg->GetTagName()); }

	// new virtual function
	virtual bool Process() = 0;

	virtual int GetSeqNum() const = 0;
	virtual H225_NonStandardParameter *GetNonStandardParam() = 0;

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

	bool IsFrom(const PIPSocket::Address &, WORD) const;
	void GetRecvAddress(PIPSocket::Address &, WORD &) const;
	unsigned GetTag() const { return m_msg->GetTag(); }
	const char *GetTagName() const { return m_msg->GetTagName(); }

	void GetRasAddress(H225_TransportAddress &) const;
	void GetCallSignalAddress(H225_TransportAddress &) const;

	bool EqualTo(const RasMsg *) const;
	bool operator==(const RasMsg & other) const { return EqualTo(&other); }

	bool Reply() const { return m_msg->Reply(); }

	GatekeeperMessage *operator->() { return m_msg; }
	const GatekeeperMessage *operator->() const { return m_msg; }
	void Release();

	static void Initialize();

protected:
	RasMsg(GatekeeperMessage *m) : m_msg(m) {}
	RasMsg(const RasMsg &);

	static bool PrintStatus(const PString &);
	
	GatekeeperMessage *m_msg;

	// just pointers to global singleton objects
	// cache for faster access
	static Toolkit *Kit;
	static GkStatus *StatusPort;
	static RegistrationTable *EndpointTbl;
	static CallTable *CallTbl; 
	static RasServer *RasSrv;
};

template<class RAS>
class RasPDU : public RasMsg {
public:
	typedef RAS RasClass;
	
	RasPDU(GatekeeperMessage *m) : RasMsg(m), request(m->m_recvRAS) {}

	// override from class RasMsg
	virtual bool Process() { return false; }
	virtual int GetSeqNum() const { return request.m_requestSeqNum; }
	virtual H225_NonStandardParameter *GetNonStandardParam();

	operator RAS & () { return request; }
	operator const RAS & () const { return request; }

	H225_RasMessage & BuildConfirm();
	H225_RasMessage & BuildReject(unsigned);

	typedef Factory<RasMsg, unsigned>::Creator1<GatekeeperMessage *> RasCreator;
	struct Creator : public RasCreator {
		Creator() : RasCreator(RasInfo<RAS>::tag) {}
		virtual RasMsg *operator()(GatekeeperMessage *m) const { return new RasPDU<RAS>(m); }
	};

protected:
	RAS & request;
};

// abstract factory for listeners
class GkInterface {
public:
	typedef PIPSocket::Address Address;

	GkInterface(const Address &);
	virtual ~GkInterface();

	// we can't call virtual functions in constructor
	// so initialize here
	virtual bool CreateListeners(RasServer *);

	bool IsBoundTo(const Address *addr) const { return m_address == *addr; }
	bool IsReachable(const Address *) const;

	RasListener *GetRasListener() const { return m_rasListener; }
	MulticastListener *GetMulticastListener() const { return m_multicastListener; }
	CallSignalListener *GetCallSignalListener() const { return m_callSignalListener; }
	StatusListener *GetStatusListener() const { return m_statusListener; }

	WORD GetRasPort() const { return m_rasPort; }
	WORD GetMulticastPort() const { return m_multicastPort; }
	WORD GetSignalPort() const { return m_signalPort; }
	WORD GetStatusPort() const { return m_statusPort; }

protected:
	bool ValidateSocket(IPSocket *, WORD &);

	template <class Listener> bool SetListener(WORD nport, WORD & oport, Listener *& listener, Listener *(GkInterface::*creator)())
	{
		if (!listener || !oport || oport != nport) {
			oport = nport;
			if (listener)
				listener->Close();
			listener = (this->*creator)();
			if (ValidateSocket(listener, oport))
				return true;
			else
				listener = 0;
		}
		return false;
	}

	Address m_address;
	RasListener *m_rasListener;
	MulticastListener *m_multicastListener;
	CallSignalListener *m_callSignalListener;
	StatusListener *m_statusListener;
	WORD m_rasPort, m_multicastPort, m_signalPort, m_statusPort;
	RasServer *m_rasSrv;

private:
	virtual RasListener *CreateRasListener();
	virtual MulticastListener *CreateMulticastListener();
	virtual CallSignalListener *CreateCallSignalListener();
	virtual StatusListener *CreateStatusListener();
};

class RasHandler {
public:
	typedef PIPSocket::Address Address;

	RasHandler();
	virtual ~RasHandler() {}

	// new virtual function

	// check if the message is the expected one
	// default behavior: check if the tag is in m_tagArray
	virtual bool IsExpected(const RasMsg *) const;

	// process the RasMsg object
	// the object must be deleted after processed
	virtual void Process(RasMsg *) = 0;

	// give the derived class an opportunity to create customized PDU
	// default behavior: return the original one
	// Note: call RasMsg::Release() if new one is created
	virtual RasMsg *CreatePDU(RasMsg *ras) { return ras; }

	// stop the handler
	virtual void Stop() {}

protected:
	void AddFilter(unsigned);

	RasServer *m_rasSrv;

private:
	// delete the object after running RasMsg::Exec()
	static void ProcessRAS(RasMsg *);

	bool m_tagArray[MaxRasTag + 1];
};

// encapsulate a gatekeeper request and reply
class RasRequester : public RasHandler {
public:
	RasRequester() { Init(); }
	// note the H225_RasMessage object must have
	// longer lifetime than this object
	RasRequester(H225_RasMessage &);
	RasRequester(H225_RasMessage &, const Address &);
	~RasRequester();

	WORD GetSeqNum() const { return m_seqNum; }
	bool WaitForResponse(int);
	RasMsg *GetReply();

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

	// new virtual function
	virtual bool SendRequest(const Address &, WORD, int = 2);
	virtual bool OnTimeout();

protected:
	void AddReply(RasMsg *);

	H225_RasMessage *m_request;
	WORD m_seqNum;
	Address m_txAddr, m_loAddr;
	WORD m_txPort;
	PTime m_sentTime;
	int m_timeout, m_retry;
	PSyncPoint m_sync;

private:
	void Init();

	PMutex m_qmutex;
	std::list<RasMsg *> m_queue;
	std::list<RasMsg *>::iterator m_iterator;
};

template<class RAS>
class Requester : public RasRequester {
public:
	typedef typename RasInfo<RAS>::Tag Tag;
	typedef typename RasInfo<RAS>::ConfirmTag ConfirmTag;
	typedef typename RasInfo<RAS>::RejectTag RejectTag;
	Requester(H225_RasMessage &, const Address &);
	~Requester() { this->m_rasSrv->UnregisterHandler(this); } // fix for GCC 3.4.2
};

template<class RAS>
Requester<RAS>::Requester(H225_RasMessage & obj_ras, const Address & ip) : RasRequester(obj_ras, ip)
{
	obj_ras.SetTag(Tag());
	RAS & ras = obj_ras;
	ras.m_requestSeqNum = GetSeqNum();
	AddFilter(ConfirmTag());
	AddFilter(RejectTag());
	this->m_rasSrv->RegisterHandler(this); // fix for GCC 3.4.2
}

/*****************************************************************

The template class let you to modify the default handler of a
given RAS message. Just explicitly specialize the Process method.
For example,

template<> bool HookedPDU<H225_RegistrationRequest>::Process()
{
	do_something_before_process();
	// call the default handler
	bool result = m_opdu->Process();
	do_something_after_process();
	return result;
}

Then add a creator to hook the interested messages

	HookedPDU<H225_RegistrationRequest>::Creator HookedRRQ;

Note the creator must be executed after RasServer::Run().

*****************************************************************/

template<class RAS>
class HookedPDU : public RasPDU<RAS> {
public:
	HookedPDU(GatekeeperMessage *m, RasMsg *p) : RasPDU<RAS>(m), m_opdu(p) {}
	~HookedPDU() { m_opdu->Release(); }

	virtual bool Process() { return m_opdu->Process(); }

	typedef typename RasPDU<RAS>::RasCreator RasCreator;
	struct Creator : public RasPDU<RAS>::Creator {
		Creator() { PAssert(this->m_old, "Error: Hook failed"); } // fix for GCC 3.4.2
		virtual RasMsg *operator()(GatekeeperMessage *m) const
		{ return new HookedPDU<RAS>(m, dynamic_cast<RasCreator &>(*(this->m_old))(m)); } // fix for GCC 3.4.2
	};

private:
	RasMsg *m_opdu;
};

#endif // RASPDU_H


syntax highlighted by Code2HTML, v. 0.9.1