/*
 * capctrl.cxx
 *
 * Module for accoutning per IP/H.323 ID/CLI/prefix inbound call volume
 *
 * $Id: capctrl.cxx,v 1.1 2006/01/27 12:59:49 zvision Exp $
 *
 * Copyright (c) 2006, Michal Zygmuntowicz
 *
 * 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.
 */
#if defined(_WIN32) && (_MSC_VER <= 1200)
#pragma warning(disable:4284)
#endif

#include <ptlib.h>
#include <ptlib/ipsock.h>
#include <h225.h>
#include <h323pdu.h>
#include "h323util.h"
#include "RasTbl.h"
#include "RasPDU.h"
#include "sigmsg.h"
#include "gkacct.h"
#include "gkauth.h"
#include "capctrl.h"

namespace {
// greater operators for sorting route lists

struct IpRule_greater : public std::binary_function<CapacityControl::IpCallVolume, CapacityControl::IpCallVolume, bool> {

	bool operator()(const CapacityControl::IpCallVolume &e1, const CapacityControl::IpCallVolume &e2) const 
	{
		int diff;
		if (e1.first.IsAny()) {
			if (!e2.first.IsAny())
				return false;
		} else {
			if (e2.first.IsAny())
				return true;
			diff = e1.first.Compare(e2.first);
			if (diff != 0)
				return diff > 0;
		}
		return false;
	}
};

struct H323IdRule_greater : public std::binary_function<CapacityControl::H323IdCallVolume, CapacityControl::H323IdCallVolume, bool> {

	bool operator()(const CapacityControl::H323IdCallVolume &e1, const CapacityControl::H323IdCallVolume &e2) const 
	{
		return H323GetAliasAddressString(e1.first) > H323GetAliasAddressString(e2.first);
	}
};

struct CLIRule_greater : public std::binary_function<CapacityControl::CLICallVolume, CapacityControl::CLICallVolume, bool> {

	bool operator()(const CapacityControl::CLICallVolume &e1, const CapacityControl::CLICallVolume &e2) const 
	{
		return e1.first.compare(e2.first) > 0;
	}
};

} // end of anonymous namespace

CapacityControl::InboundCallVolume::InboundCallVolume()
	: m_maxVolume(0), m_currentVolume(0)
{
}

CapacityControl::InboundCallVolume::~InboundCallVolume()
{
}

PString CapacityControl::InboundCallVolume::AsString() const
{
	return PString("pfx: ") + (m_prefix.empty() ? "*" : m_prefix.c_str())
		+ ", vol (cur/max): " + PString(m_currentVolume) + "/" + PString(m_maxVolume);
}

bool CapacityControl::InboundCallVolume::operator==(const InboundCallVolume &obj) const
{
	return m_prefix == obj.m_prefix;
}

bool CapacityControl::InboundIPCallVolume::operator==(const InboundIPCallVolume &obj) const
{
	return m_sourceAddress == obj.m_sourceAddress && ((InboundCallVolume&)*this) == ((InboundCallVolume&)obj);
}

bool CapacityControl::InboundH323IdCallVolume::operator==(const InboundH323IdCallVolume &obj) const
{
	return m_sourceH323Id == obj.m_sourceH323Id && ((InboundCallVolume&)*this) == ((InboundCallVolume&)obj);
}

bool CapacityControl::InboundCLICallVolume::operator==(const InboundCLICallVolume &obj) const
{
	return m_sourceCLI == obj.m_sourceCLI && ((InboundCallVolume&)*this) == ((InboundCallVolume&)obj);
}

CapacityControl::CapacityControl(
	) : Singleton<CapacityControl>("CapacityControl")
{
	LoadConfig();
}

void CapacityControl::LoadConfig()
{
	IpCallVolumes ipCallVolumes;
	H323IdCallVolumes h323IdCallVolumes;
	CLICallVolumes cliCallVolumes;

	PConfig* cfg = GkConfig();
	const PString cfgSec("CapacityControl");
	
	unsigned ipRules = 0, h323IdRules = 0, cliRules = 0;
	IpCallVolumes::iterator ipRule = ipCallVolumes.end();
	H323IdCallVolumes::iterator h323IdRule  = h323IdCallVolumes.end();
	CLICallVolumes::iterator cliRule  = cliCallVolumes.end();
	
	const PStringToString kv = cfg->GetAllKeyValues(cfgSec);
	for (PINDEX i = 0; i < kv.GetSize(); ++i) {
		PString key = kv.GetKeyAt(i);

		if (key[0] == '%') {
			const PINDEX sepIndex = key.Find('%', 1);
			if (sepIndex != P_MAX_INDEX)
				key = key.Mid(sepIndex + 1).Trim();
		}

		// on Unix multiple entries for the same key are concatenated
		// to one string and separated by a new line
		PStringArray dataLines = kv.GetDataAt(i).Tokenise("\n", FALSE);
		for (PINDEX d = 0; d < dataLines.GetSize(); d++) {
			PString data = dataLines[d];
			InboundCallVolume *rule = NULL;
			bool newIpRule = false, newH323IdRule = false, newCLIRule = false;

			// check the rule type (ip/h323id/cli)
			if (key.Find("ip:") == 0) {
				const PString ip = key.Mid(3).Trim();
			
				NetworkAddress addr;
				if (!(ip == "*" || ip == "any"))
					addr = NetworkAddress(ip);

				ipCallVolumes.resize(ipCallVolumes.size() + 1);
				ipRule = ipCallVolumes.end() - 1;
				ipRule->first = addr;
				ipRule->second.m_sourceAddress = addr;
				newIpRule = true;
	
				rule = &(ipRule->second);
			} else if (key.Find("h323id:") == 0) {
				H225_AliasAddress alias;
				const PString h323id = key.Mid(7).Trim();
				H323SetAliasAddress(h323id, alias, H225_AliasAddress::e_h323_ID);

				h323IdCallVolumes.resize(h323IdCallVolumes.size() + 1);
				h323IdRule = h323IdCallVolumes.end() - 1;
				h323IdRule->first = alias;
				h323IdRule->second.m_sourceH323Id = alias;
				newH323IdRule = true;
				
				rule = &(h323IdRule->second);
			} else if (key.Find("cli:") == 0) {
				const PString cli = key.Mid(4).Trim();

				cliCallVolumes.resize(cliCallVolumes.size() + 1);
				cliRule = cliCallVolumes.end() - 1;
				cliRule->first = cli;
				cliRule->second.m_sourceCLI = cli;
				newCLIRule = true;
				
				rule = &(cliRule->second);
			} else {
				PTRACE(1, "CAPCTRL\tUknown CapacityControl rule: " << key << '=' 
					<< kv.GetDataAt(i)
					);
				continue;
			}

			PStringArray tokens(data.Tokenise(" \t", FALSE));
			if (tokens.GetSize() < 1) {
				PTRACE(1, "CAPCTRL\tInvalid CapacityControl rule syntax: " << key << '=' 
					<< kv.GetDataAt(i)
					);
				if (newIpRule)
					ipCallVolumes.erase(ipRule);
				else if (newH323IdRule)
					h323IdCallVolumes.erase(h323IdRule);
				else if (newCLIRule)
					cliCallVolumes.erase(cliRule);
				continue;
			}
	
			unsigned tno = 0;
			if (tokens.GetSize() >= 2)
				rule->m_prefix = tokens[tno++];
			rule->m_maxVolume = tokens[tno++].AsUnsigned();
			
			if (newIpRule)
				++ipRules;
			else if (newH323IdRule)
				++h323IdRules;
			else if (newCLIRule)
				++cliRules;
		} /* for (d) */
	} /* for (i) */

	// sort rules by IP network mask length	
	std::stable_sort(ipCallVolumes.begin(), ipCallVolumes.end(), IpRule_greater());
	std::stable_sort(h323IdCallVolumes.begin(), h323IdCallVolumes.end(), H323IdRule_greater());
	std::stable_sort(cliCallVolumes.begin(), cliCallVolumes.end(), CLIRule_greater());

	// update route entries that have not changed
	for (unsigned i = 0; i < ipCallVolumes.size(); ++i) {
		IpCallVolumes::iterator rule = m_ipCallVolumes.begin();
		while (rule != m_ipCallVolumes.end()) {
			rule = find(
				rule, m_ipCallVolumes.end(), ipCallVolumes[i]
				);
			if (rule == m_ipCallVolumes.end())
				break;
			if (rule->second == ipCallVolumes[i].second) {
				ipCallVolumes[i].second.m_currentVolume = rule->second.m_currentVolume;
				ipCallVolumes[i].second.m_calls = rule->second.m_calls;
			}
			++rule;
		}
	}

	for (unsigned i = 0; i < h323IdCallVolumes.size(); ++i) {
		H323IdCallVolumes::iterator rule = m_h323IdCallVolumes.begin();
		while (rule != m_h323IdCallVolumes.end()) {
			rule = find(
				rule, m_h323IdCallVolumes.end(), h323IdCallVolumes[i]
				);
			if (rule == m_h323IdCallVolumes.end())
				break;
			if (rule->second == h323IdCallVolumes[i].second) {
				h323IdCallVolumes[i].second.m_currentVolume = rule->second.m_currentVolume;
				h323IdCallVolumes[i].second.m_calls = rule->second.m_calls;
			}
			++rule;
		}
	}

	for (unsigned i = 0; i < cliCallVolumes.size(); ++i) {
		CLICallVolumes::iterator rule = m_cliCallVolumes.begin();
		while (rule != m_cliCallVolumes.end()) {
			rule = find(
				rule, m_cliCallVolumes.end(), cliCallVolumes[i]
				);
			if (rule == m_cliCallVolumes.end())
				break;
			if (rule->second == cliCallVolumes[i].second) {
				cliCallVolumes[i].second.m_currentVolume = rule->second.m_currentVolume;
				cliCallVolumes[i].second.m_calls = rule->second.m_calls;
			}
			++rule;
		}
	}
	
	m_ipCallVolumes.clear();
	m_h323IdCallVolumes.clear();
	m_cliCallVolumes.clear();

	m_ipCallVolumes = ipCallVolumes;
	m_h323IdCallVolumes = h323IdCallVolumes;
	m_cliCallVolumes = cliCallVolumes;

	PTRACE(5, "CAPCTRL\t" << ipRules << " IP rules loaded");
#if PTRACING
	if (PTrace::CanTrace(6)) {
		ostream &strm = PTrace::Begin(6, __FILE__, __LINE__);
		strm << "Per IP call volume rules:" << endl;
		for (unsigned i = 0; i < m_ipCallVolumes.size(); ++i) {
			strm << "\tsrc " << m_ipCallVolumes[i].first.AsString() << ":" << endl;
			strm << "\t\t" << m_ipCallVolumes[i].second.AsString() << endl;
		}
		PTrace::End(strm);
	}
#endif

	PTRACE(5, "CAPCTRL\t" << h323IdRules << " H.323 ID rules loaded");
#if PTRACING
	if (PTrace::CanTrace(6)) {
		ostream &strm = PTrace::Begin(6, __FILE__, __LINE__);
		strm << "Per H.323 ID call volume rules:" << endl;
		for (unsigned i = 0; i < m_h323IdCallVolumes.size(); i++) {
			strm << "\tsrc " << H323GetAliasAddressString(m_h323IdCallVolumes[i].first) << ":" << endl;
			strm << "\t\t" << m_h323IdCallVolumes[i].second.AsString() << endl;
		}
		PTrace::End(strm);
	}
#endif

	PTRACE(5, "CAPCTRL\t" << cliRules << " CLI rules loaded");
#if PTRACING
	if (PTrace::CanTrace(6)) {
		ostream &strm = PTrace::Begin(6, __FILE__, __LINE__);
		strm << "Per CLI call volume rules:" << endl;
		for (unsigned i = 0; i < m_cliCallVolumes.size(); i++) {
			strm << "\tsrc " << m_cliCallVolumes[i].first << ":" << endl;
			strm << "\t\t" << m_cliCallVolumes[i].second.AsString() << endl;
		}
		PTrace::End(strm);
	}
#endif
}

CapacityControl::IpCallVolumes::iterator CapacityControl::FindByIp(
	const NetworkAddress &srcIp,
	const PString &calledStationId
	)
{
	unsigned netmaskLen = 0;
	PINDEX matchLen = P_MAX_INDEX;

	const IpCallVolumes::iterator ipEnd = m_ipCallVolumes.end();
	IpCallVolumes::iterator bestIpMatch = ipEnd, i = m_ipCallVolumes.begin();
	while (i != ipEnd) {
		if (bestIpMatch != ipEnd && i->first.GetNetmaskLen() < netmaskLen)
			break;
		if (i->first.IsAny() || srcIp << i->first) {
			PINDEX offset, len;
			if (i->second.m_prefix.empty())
				len = 0;
			else if (!calledStationId.FindRegEx(
					PRegularExpression(i->second.m_prefix.c_str(), PRegularExpression::Extended), offset, len))
				len = P_MAX_INDEX;
			if (len != P_MAX_INDEX && (matchLen == P_MAX_INDEX || len > matchLen)) {
				bestIpMatch = i;
				netmaskLen = i->first.GetNetmaskLen();
				matchLen = len;
			}
		}
		++i;
	}
	
	return bestIpMatch;
}

CapacityControl::H323IdCallVolumes::iterator CapacityControl::FindByH323Id(
	const PString &h323Id,
	const PString &calledStationId
	)
{
	PINDEX matchLen = P_MAX_INDEX;
	const H323IdCallVolumes::iterator h323IdEnd = m_h323IdCallVolumes.end();
	H323IdCallVolumes::iterator bestH323IdMatch = h323IdEnd, i = m_h323IdCallVolumes.begin();
	while (i != h323IdEnd) {
		if (h323Id.IsEmpty())
			break;
		PString alias = H323GetAliasAddressString(i->first);
		if (bestH323IdMatch != h323IdEnd && alias != h323Id)
			break;
		if (alias == h323Id) {
			PINDEX offset, len;
			if (i->second.m_prefix.empty())
				len = 0;
			else if (!calledStationId.FindRegEx(PRegularExpression(i->second.m_prefix.c_str(), PRegularExpression::Extended), offset, len))
				len = P_MAX_INDEX;
			if (len != P_MAX_INDEX && (matchLen == P_MAX_INDEX || len > matchLen)) {
				bestH323IdMatch = i;
				matchLen = len;
			}
		}
		++i;
	}
	
	return bestH323IdMatch;
}

CapacityControl::CLICallVolumes::iterator CapacityControl::FindByCli(
	const std::string &cli,
	const PString &calledStationId
	)
{
	PINDEX matchLen = P_MAX_INDEX;
	const CLICallVolumes::iterator cliEnd = m_cliCallVolumes.end();
	CLICallVolumes::iterator bestCliMatch = cliEnd, i = m_cliCallVolumes.begin();
	while (i != cliEnd) {
		if (cli.empty())
			break;
		if (bestCliMatch != cliEnd && i->first != cli)
			break;
		if (i->first == cli) {
			PINDEX offset, len;
			if (i->second.m_prefix.empty())
				len = 0;
			else if (!calledStationId.FindRegEx(PRegularExpression(i->second.m_prefix.c_str(), PRegularExpression::Extended), offset, len))
				len = P_MAX_INDEX;
			if (len != P_MAX_INDEX && (matchLen == P_MAX_INDEX || len > matchLen)) {
				bestCliMatch = i;
				matchLen = len;
			}
		}
		++i;
	}
	
	return bestCliMatch;
}

void CapacityControl::LogCall(
	const NetworkAddress &srcIp,
	const PString &srcAlias,
	const std::string &srcCli,
	const PString &calledStationId,
	PINDEX callNumber,
	bool callStart
	)
{
	IpCallVolumes::iterator bestIpMatch = FindByIp(srcIp, calledStationId);
	if (bestIpMatch != m_ipCallVolumes.end()) {
		PTRACE(5, "CAPCTRL\tCall #" << callNumber
			<< " to " << calledStationId << " matched IP rule " << bestIpMatch->first.AsString()
			<< "\t" << bestIpMatch->second.AsString()
			);
		PWaitAndSignal lock(m_updateMutex);
		if (callStart) {
			++(bestIpMatch->second.m_currentVolume);
			bestIpMatch->second.m_calls.push_back(callNumber);
		} else {
			if (find(bestIpMatch->second.m_calls.begin(), bestIpMatch->second.m_calls.end(), 
					callNumber) != bestIpMatch->second.m_calls.end()) {
				--(bestIpMatch->second.m_currentVolume);
				bestIpMatch->second.m_calls.remove(callNumber);
			}
		}
		return;
	}

	H323IdCallVolumes::iterator bestH323IdMatch = FindByH323Id(srcAlias, calledStationId);
	if (bestH323IdMatch != m_h323IdCallVolumes.end()) {
		PTRACE(5, "CAPCTRL\tCall #" << callNumber
			<< " to " << calledStationId << " matched H323.ID rule " << H323GetAliasAddressString(bestH323IdMatch->first)
			<< "\t" << bestH323IdMatch->second.AsString()
			);
		PWaitAndSignal lock(m_updateMutex);
		if (callStart) {
			++(bestH323IdMatch->second.m_currentVolume);
			bestH323IdMatch->second.m_calls.push_back(callNumber);
		} else {
			if (find(bestH323IdMatch->second.m_calls.begin(), bestH323IdMatch->second.m_calls.end(), 
					callNumber) != bestH323IdMatch->second.m_calls.end()) {
				--(bestH323IdMatch->second.m_currentVolume);
				bestH323IdMatch->second.m_calls.remove(callNumber);
			}
		}
		return;
	}

	CLICallVolumes::iterator bestCliMatch = FindByCli(srcCli, calledStationId);
	if (bestCliMatch != m_cliCallVolumes.end()) {
		PTRACE(5, "CAPCTRL\tCall #" << callNumber
			<< " to " << calledStationId << " matched CLI rule " << bestCliMatch->first
			<< "\t" << bestCliMatch->second.AsString()
			);
		PWaitAndSignal lock(m_updateMutex);
		if (callStart) {
			++(bestCliMatch->second.m_currentVolume);
			bestCliMatch->second.m_calls.push_back(callNumber);
		} else {
			if (find(bestCliMatch->second.m_calls.begin(), bestCliMatch->second.m_calls.end(), 
					callNumber) != bestCliMatch->second.m_calls.end()) {
				--(bestCliMatch->second.m_currentVolume);
				bestCliMatch->second.m_calls.remove(callNumber);
			}
		}
		return;
	}
}

bool CapacityControl::CheckCall(
	const NetworkAddress &srcIp,
	const PString &srcAlias,
	const std::string &srcCli,
	const PString &calledStationId
	)
{
	IpCallVolumes::iterator bestIpMatch = FindByIp(srcIp, calledStationId);
	if (bestIpMatch != m_ipCallVolumes.end()) {
		PTRACE(5, "CAPCTRL\tCall from IP " << srcIp.AsString()
			<< " to " << calledStationId << " matched IP rule " << bestIpMatch->first.AsString()
			<< "\t" << bestIpMatch->second.AsString()
			);
		PWaitAndSignal lock(m_updateMutex);
		return bestIpMatch->second.m_currentVolume < bestIpMatch->second.m_maxVolume;
	}

	H323IdCallVolumes::iterator bestH323IdMatch = FindByH323Id(srcAlias, calledStationId);
	if (bestH323IdMatch != m_h323IdCallVolumes.end()) {
		PTRACE(5, "CAPCTRL\tCall to " << calledStationId << " matched H323.ID rule "
			<< H323GetAliasAddressString(bestH323IdMatch->first)
			<< "\t" << bestH323IdMatch->second.AsString()
			);
		PWaitAndSignal lock(m_updateMutex);
		return bestH323IdMatch->second.m_currentVolume < bestH323IdMatch->second.m_maxVolume;
	}

	CLICallVolumes::iterator bestCliMatch = FindByCli(srcCli, calledStationId);
	if (bestCliMatch != m_cliCallVolumes.end()) {
		PTRACE(5, "CAPCTRL\tCall to " << calledStationId << " matched CLI rule "
			<< bestCliMatch->first << "\t" << bestCliMatch->second.AsString()
			);
		PWaitAndSignal lock(m_updateMutex);
		return bestCliMatch->second.m_currentVolume < bestCliMatch->second.m_maxVolume;
	}
	
	return true;
}

namespace {

class CapCtrlAcct : public GkAcctLogger
{
public:
	enum Constants {
		/// events recognized by this module
		CapCtrlAcctEvents = AcctStart | AcctStop
	};
	
	/// Create a logger that updates information about inbound traffic
	CapCtrlAcct( 
		/// name from Gatekeeper::Acct section
		const char* moduleName
		);
		
	/** Log accounting event.
	
		@return
		Status of this logging operation (see #Status enum#)
	*/
	virtual Status Log( 
		AcctEvent evt, /// accounting event to log
		const callptr& call /// additional data for the event
		);

private:
	/* No copy constructor allowed */
	CapCtrlAcct(const CapCtrlAcct&);
	/* No operator= allowed */
	CapCtrlAcct& operator=(const CapCtrlAcct&);
	
private:
	CapacityControl *m_capacityControl;
};

} // end of anonymous namespace

CapCtrlAcct::CapCtrlAcct(
	const char* moduleName
	) : GkAcctLogger(moduleName), m_capacityControl(CapacityControl::Instance())
{
	SetSupportedEvents(CapCtrlAcctEvents);
}

GkAcctLogger::Status CapCtrlAcct::Log(
	GkAcctLogger::AcctEvent evt, 
	const callptr &call
	)
{
	if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0)
		return Next;
	if (!call) {
		PTRACE(1, "GKACCT\t" << GetName() << " - missing call info for event " << evt);
		return Fail;
	}
		
	PIPSocket::Address addr;
	WORD port;
	call->GetSrcSignalAddr(addr, port);

	PString h323Id = GetBestAliasAddressString(
		call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID)
		);
	if (h323Id.IsEmpty() && call->GetCallingParty())
		h323Id = GetBestAliasAddressString(
			call->GetCallingParty()->GetAliases(), true,
			AliasAddressTagMask(H225_AliasAddress::e_h323_ID)
			);

	std::string cli((const char*)(GetCallingStationId(call)));

	m_capacityControl->LogCall(addr, h323Id, cli, GetCalledStationId(call), call->GetCallNumber(), evt == AcctStart);

	return Ok;
}

namespace {

/// Authenticator module for controlling inbound traffic
/// To be used together with CapacityControl accounting module
class CapCtrlAuth : public GkAuthenticator
{
public:
	enum SupportedChecks {
		CapCtrlAuthMiscChecks = e_Setup | e_SetupUnreg
	};
	
	/// build authenticator reading settings from the config
	CapCtrlAuth(
		/// name for this authenticator and for the config section to read settings from
		const char* authName,
		/// RAS check events supported by this module
		unsigned supportedRasChecks = 0,
		/// Misc check events supported by this module
		unsigned supportedMiscChecks = CapCtrlAuthMiscChecks
		);
	
	/** Authenticate using data from Q.931 Setup message.
	
		@return:
		#GkAuthenticator::Status enum# with the result of authentication.
	*/
	virtual int Check(
		/// Q.931/H.225 Setup message to be authenticated
		SetupMsg &setup,
		/// authorization data (call duration limit, reject reason, ...)
		SetupAuthData& authData
		);

private:
	CapCtrlAuth();
	CapCtrlAuth(const CapCtrlAuth&);
	CapCtrlAuth& operator=(const CapCtrlAuth&);

private:
	CapacityControl *m_capacityControl;
};

} // end of anonymous namespace

CapCtrlAuth::CapCtrlAuth(
	const char* authName,
	unsigned supportedRasChecks,
	unsigned supportedMiscChecks
	) 
	: 
	GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks), 
	m_capacityControl(CapacityControl::Instance())
{
}

int CapCtrlAuth::Check(
	/// Q.931/H.225 Setup message to be authenticated
	SetupMsg &setup,
	/// authorization data (call duration limit, reject reason, ...)
	SetupAuthData& authData
	)
{
	PString h323Id;
	PIPSocket::Address addr;
	setup.GetPeerAddr(addr);

	if (authData.m_call) {
		h323Id = GetBestAliasAddressString(
			authData.m_call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID)
			);
		if (h323Id.IsEmpty() && authData.m_call->GetCallingParty())
			h323Id = GetBestAliasAddressString(
				authData.m_call->GetCallingParty()->GetAliases(), true,
				AliasAddressTagMask(H225_AliasAddress::e_h323_ID)
				);
	}
	
	if (!m_capacityControl->CheckCall(
			addr, h323Id, (const char*)(authData.m_callingStationId),
			authData.m_calledStationId)) {
		authData.m_rejectCause = Q931::NoCircuitChannelAvailable;
		return e_fail;
	} else
		return e_ok;
}

namespace {
GkAcctLoggerCreator<CapCtrlAcct> CapCtrlAcctLoggerCreator("CapacityControl");
GkAuthCreator<CapCtrlAuth> CapCtrlAuthCreator("CapacityControl");
}


syntax highlighted by Code2HTML, v. 0.9.1