//////////////////////////////////////////////////////////////////
//
// Toolkit base class for the GnuGK
//
// 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.
//
// History:
// 	991227  initial version (Torsten Will, mediaWays)
//
//////////////////////////////////////////////////////////////////

#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 <ptclib/cypher.h>
#include <h323pdu.h>
#include <map>
#include "stl_supp.h"
#include "gktimer.h"
#include "h323util.h"
#include "gkconfig.h"
#if HAS_MYSQL || HAS_PGSQL
#include "gksql.h"
#endif
#include "clirw.h"
#include "capctrl.h"
#include "Toolkit.h"

using namespace std;

extern const char *ProxySection;
extern const char *RoutedSec;

namespace {

const PString paddingByteConfigKey("KeyFilled");
const BYTE AnyRawAddress[16] = {
	255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255
};

} /* namespace */

NetworkAddress::NetworkAddress() : m_address(0), m_netmask(0)
{
}

NetworkAddress::NetworkAddress(
	const PIPSocket::Address &addr
	) : m_address(addr), m_netmask(addr.GetSize(), AnyRawAddress)
{
}

NetworkAddress::NetworkAddress(
	const PIPSocket::Address &addr,
	const PIPSocket::Address &nm
	) : m_netmask(nm)
{
	// normalize the address
	BYTE rawdata[16];
	const unsigned sz = addr.GetSize();
	for (unsigned i = 0; i < sz; i++)
		rawdata[i] = addr[i] & nm[i];
	m_address = PIPSocket::Address(sz, rawdata);
}
	
NetworkAddress::NetworkAddress(
	const PString &str /// an address in a form A.B.C.D, A.B.C.D/24 or A.B.C.D/255.255.255.0
	)
{
	Toolkit::GetNetworkFromString(str, m_address, m_netmask);
}

unsigned NetworkAddress::GetNetmaskLen() const		
{
	unsigned len = 0;
	const unsigned sz = m_netmask.GetSize() * 8;
	const char *rawdata = m_netmask.GetPointer();
	
	
	for (int b = sz - 1; b >= 0; b--)
		if (rawdata[b >> 3] & (0x80 >> (b & 7)))
			break;
		else
			len++;
			
	return sz - len;
}

bool NetworkAddress::operator==(const NetworkAddress &addr) const
{
	if (m_address.GetSize() != addr.m_address.GetSize())
		return false;

	const unsigned sz = m_address.GetSize();
	for (unsigned i = 0; i < sz; i++)
		if (m_address[i] != addr.m_address[i] || m_netmask[i] != addr.m_netmask[i])
			return false;
			
	return true;
}

bool NetworkAddress::operator==(const PIPSocket::Address &addr) const
{
	if (m_address.GetSize() != addr.GetSize())
		return false;

	const unsigned sz = m_address.GetSize();
	for (unsigned i = 0; i < sz; i++)
		if (m_netmask[i] != 255 || m_address[i] != addr[i])
			return false;
			
	return true;
}

bool NetworkAddress::operator>>(const NetworkAddress &addr) const
{
	if (m_address.GetSize() != addr.m_address.GetSize())
		return false;

	const unsigned sz = m_address.GetSize();
	for (unsigned i = 0; i < sz; i++)
		if (m_netmask[i] != (addr.m_netmask[i] & m_netmask[i])
				|| m_address[i] != (addr.m_address[i] & m_netmask[i]))
			return false;
			
	return true;
}
	
bool NetworkAddress::operator<<(const NetworkAddress &addr) const
{
	return addr >> *this;
}

bool NetworkAddress::operator>>(const PIPSocket::Address &addr) const
{
	if (m_address.GetSize() != addr.GetSize())
		return false;

	const unsigned sz = m_address.GetSize();
	for (unsigned i = 0; i < sz; i++)
		if (m_address[i] != (addr[i] & m_netmask[i]))
			return false;
			
	return true;
}

int NetworkAddress::Compare(const NetworkAddress &addr) const
{
	int diff = m_address.GetSize() - addr.m_address.GetSize();
	if (diff == 0) {
		diff = GetNetmaskLen() - addr.GetNetmaskLen();
		if (diff == 0) {
			const unsigned sz = m_address.GetSize();
			for (unsigned i = 0; i < sz; i++) {
				diff = m_address[i] - addr.m_address[i];
				if (diff != 0)
					break;
			}
		}
	}
	return diff;
}

PString NetworkAddress::AsString() const
{
	return m_address.AsString() + "/" + PString(GetNetmaskLen());
}

bool NetworkAddress::IsAny() const
{
	return const_cast<NetworkAddress*>(this)->m_address.IsAny() ? true : false;
}

bool NetworkAddress::operator<(const NetworkAddress &addr) const
{
	return Compare(addr) < 0;
}

bool NetworkAddress::operator<=(const NetworkAddress &addr) const
{
	return Compare(addr) <= 0;
}

bool NetworkAddress::operator>(const NetworkAddress &addr) const
{
	return Compare(addr) > 0;
}

bool NetworkAddress::operator>=(const NetworkAddress &addr) const
{
	return Compare(addr) >= 0;
}

bool operator==(const PIPSocket::Address &addr, const NetworkAddress &net)
{
	return net == addr;
}

bool operator<<(const PIPSocket::Address &addr, const NetworkAddress &net)
{
	return net >> addr;
}


// class Toolkit::RouteTable::RouteEntry
Toolkit::RouteTable::RouteEntry::RouteEntry(
	const PString & net
) : PIPSocket::RouteEntry(0)
{
	destination = net.Tokenise("/", FALSE)[0];
	GetNetworkFromString(net, network, net_mask);
}

Toolkit::RouteTable::RouteEntry::RouteEntry(
	const PIPSocket::RouteEntry & re,
	const InterfaceTable & it
) : PIPSocket::RouteEntry(re)
{
	PINDEX i;
	for (i = 0; i < it.GetSize(); ++i) {
		const Address & ip = it[i].GetAddress();
		if (Compare(&ip)) {
			destination = ip;
			return;
		}
	}
	for (i = 0; i < it.GetSize(); ++i)
		if (it[i].GetName() == interfaceName) {
			destination = it[i].GetAddress();
			return;
		}
}

inline bool Toolkit::RouteTable::RouteEntry::Compare(const Address *ip) const
{
	return (*ip == destination) || ((*ip & net_mask) == network);
}

// class Toolkit::RouteTable
void Toolkit::RouteTable::InitTable()
{
	// Workaround for OS doesn't support GetRouteTable
	PIPSocket::GetHostAddress(defAddr);

	ClearTable();
	if (!CreateTable())
		return;

	// Set default IP according to route table
	PIPSocket::Address defGW;
	PIPSocket::GetGatewayAddress(defGW);
	defAddr = GetLocalAddress(defGW);

#if PTRACING
	for (RouteEntry *entry = rtable_begin; entry != rtable_end; ++entry)
		PTRACE(2, "Network=" << entry->GetNetwork() << '/' << entry->GetNetMask() <<
			  ", IP=" << entry->GetDestination());
	PTRACE(2, "Default IP=" << defAddr);
#endif
}

void Toolkit::RouteTable::ClearTable()
{
	if (rtable_begin) {
		for (RouteEntry *r = rtable_begin; r != rtable_end; ++r)
			r->~RouteEntry();
		::free(rtable_begin);
		rtable_begin = 0;
	}
}

// can't pass a reference of Address, or STL complains...
PIPSocket::Address Toolkit::RouteTable::GetLocalAddress(const Address & addr) const
{
	RouteEntry *entry = find_if(rtable_begin, rtable_end,
			bind2nd(mem_fun_ref(&RouteEntry::Compare), &addr));
	return (entry != rtable_end) ? entry->GetDestination() : defAddr;
}

bool Toolkit::RouteTable::CreateTable()
{
	InterfaceTable if_table;
	if (!PIPSocket::GetInterfaceTable(if_table)) {
		PTRACE(1, "Error: Can't get interface table");
		return false;
	}
	PTRACE(4, "InterfaceTable:\n" << setfill('\n') << if_table << setfill(' '));
	PIPSocket::RouteTable r_table;
	if (!PIPSocket::GetRouteTable(r_table)) {
		PTRACE(1, "Error: Can't get route table");
		return false;
	}

	int i = r_table.GetSize();
	rtable_end = rtable_begin = static_cast<RouteEntry *>(::malloc(i * sizeof(RouteEntry)));
	for (PINDEX r = 0; r < i ; ++r) {
		PIPSocket::RouteEntry & r_entry = r_table[r];
		if (r_entry.GetNetMask() != INADDR_ANY)
			// placement operator
			::new (rtable_end++) RouteEntry(r_entry, if_table);
	}
	return true;
}

bool Toolkit::VirtualRouteTable::CreateTable()
{
	PString nets = GkConfig()->GetString("NetworkInterfaces", "");
	if (nets.IsEmpty())
		return false;
	PStringArray networks(nets.Tokenise(" ,;\t", FALSE));
	int i = networks.GetSize();
	if (i > 0) {
		rtable_end = rtable_begin = static_cast<RouteEntry *>(::malloc(i * sizeof(RouteEntry)));
		for (PINDEX r = 0; r < i ; ++r)
			::new (rtable_end++) RouteEntry(networks[r]);
	}
	return true;
}

// class Toolkit::ProxyCriterion
Toolkit::ProxyCriterion::ProxyCriterion() : m_enable(false)
{
}

Toolkit::ProxyCriterion::~ProxyCriterion()
{ 
}

void Toolkit::ProxyCriterion::LoadConfig(PConfig *config)
{
	m_networks.clear();

	m_enable = AsBool(config->GetString(ProxySection, "Enable", "0"));
	if (!m_enable) {
		PTRACE(2, "GK\tH.323 Proxy disabled");
		return;
	}

	PTRACE(2, "GK\tH.323 Proxy enabled");

	PStringArray networks(config->GetString(ProxySection, "InternalNetwork", "").Tokenise(" ,;\t", FALSE));
	for (PINDEX i = 0; i < networks.GetSize(); ++i) {
		m_networks.resize(m_networks.size() + 1);
		m_networks[m_networks.size() - 1] = NetworkAddress(networks[i]);
		PTRACE(2, "GK\tInternal Network " << i << " = " << m_networks.back().AsString());
	}
}

bool Toolkit::ProxyCriterion::Required(const Address & ip1, const Address & ip2) const
{
	return m_enable ? (m_networks.empty() || (IsInternal(ip1) != IsInternal(ip2))) : false;
}

bool Toolkit::ProxyCriterion::IsInternal(const Address & ip) const
{
	std::vector<NetworkAddress>::const_iterator i = m_networks.begin();
	while (i != m_networks.end())
		if (ip << *i++)
			return true;
	return false;
}

// class Toolkit::RewriteTool

static const char *RewriteSection = "RasSrv::RewriteE164";

Toolkit::RewriteData::RewriteData(PConfig *config, const PString & section)
{
	m_RewriteKey = NULL;
	PStringToString cfgs(config->GetAllKeyValues(section));
	m_size = cfgs.GetSize();
	if (m_size > 0) {
		std::map<PString, PString, pstr_prefix_lesser> rules;
		for (PINDEX i = 0; i < m_size; ++i) {
			PString key = cfgs.GetKeyAt(i);
			if (!key && (isdigit(key[0]) || key[0]=='!' || key[0]=='.' || key[0]=='%' || key[0]=='*' || key[0]=='#'))
				rules[key] = cfgs.GetDataAt(i);
		}
		// now the rules are ascendantly sorted by the keys
		if ((m_size = rules.size()) > 0) {
			// replace array constructor with explicit memory allocation
			// and in-place new operators - workaround for VC compiler
//			m_RewriteKey = new PString[m_size * 2];
			m_RewriteKey = (PString*)(new BYTE[sizeof(PString) * m_size * 2]);
			m_RewriteValue = m_RewriteKey + m_size;
			std::map<PString, PString, pstr_prefix_lesser>::iterator iter = rules.begin();
			
			// reverse the order
			for (int i = m_size; i-- > 0 ; ++iter) {
//				m_RewriteKey[i] = iter->first;
				::new(m_RewriteKey + i) PString(iter->first);
//				m_RewriteValue[i] = iter->second;
				::new(m_RewriteValue + i) PString(iter->second);
			}
		}
	}
}

Toolkit::RewriteData::~RewriteData()
{
//	delete [] m_RewriteKey;
	if (m_RewriteKey)
		for (int i = 0; i < m_size * 2; i++)
			(m_RewriteKey+i)->~PString();
	delete[] ((BYTE*)m_RewriteKey);
}

void Toolkit::RewriteTool::LoadConfig(
	PConfig *config
	)
{
	m_RewriteFastmatch = config->GetString(RewriteSection, "Fastmatch", "");
	m_TrailingChar = config->GetString("RasSrv::ARQFeatures", "RemoveTrailingChar", " ")[0];
	delete m_Rewrite;
	m_Rewrite = new RewriteData(config, RewriteSection);
}

bool Toolkit::RewriteTool::RewritePString(PString & s) const
{
	bool changed = false;

	// remove trailing character
	if (s.GetLength() > 1 && s[s.GetLength() - 1] == m_TrailingChar) {
		s = s.Left(s.GetLength() - 1);
		changed = true;
	}
	// startsWith?
	if (strncmp(s, m_RewriteFastmatch, m_RewriteFastmatch.GetLength()) != 0)
		return changed;

	PString t;
	for (PINDEX i = 0; i < m_Rewrite->Size(); ++i) {
		const char *prefix = m_Rewrite->Key(i);
		const int len = MatchPrefix(s, prefix);
		// try a prefix match through all keys
		if (len > 0 || (len == 0 && prefix[0] == '!')) {
			// Rewrite to #t#. Append the suffix, too.
			// old:  01901234999
			//               999 Suffix
			//       0190        Fastmatch
			//       01901234    prefix, Config-Rule: 01901234=0521321
			// new:  0521321999

			const char *newprefix = m_Rewrite->Value(i);

			PString result;
			if (len > 0)
				result = RewriteString(s, prefix, newprefix);
			else
				result = newprefix + s;
				
			PTRACE(2, "\tRewritePString: " << s << " to " << result);
			s = result;
			changed = true;

			break;
		}
	}

	return changed;
}

// class Toolkit::GWRewriteTool

static const char *GWRewriteSection = "RasSrv::GWRewriteE164";

Toolkit::GWRewriteTool::~GWRewriteTool() {
	for (PINDEX i = 0; i < m_GWRewrite.GetSize(); ++i) {
		delete &(m_GWRewrite.GetDataAt(i));
	}
	m_GWRewrite.RemoveAll();
}

bool Toolkit::GWRewriteTool::RewritePString(PString gw, bool direction, PString &data) {

	GWRewriteEntry *gw_entry;
	PString key, value;

	// First lookup the GW in the dictionary
	gw_entry = m_GWRewrite.GetAt(gw);

	if (gw_entry == NULL)
		return false;

	std::vector<pair<PString,PString> >::iterator rule_iterator = direction
		? gw_entry->m_entry_data.first.begin() : gw_entry->m_entry_data.second.begin();
	std::vector<pair<PString,PString> >::iterator end_iterator = direction
		? gw_entry->m_entry_data.first.end() : gw_entry->m_entry_data.second.end();

	for (; rule_iterator != end_iterator; ++rule_iterator) {
	
		key = (*rule_iterator).first;
			
		const int len = MatchPrefix(data, key);
		if (len > 0 || (len == 0 && key[0] == '!')) {
			// Start rewrite
			value = (*rule_iterator).second;
				
			if (len > 0)
				value = RewriteString(data, key, value);
			else
				value = value + data;

			// Log
			PTRACE(2, "\tGWRewriteTool::RewritePString: " << data << " to " << value);

			// Finish rewrite
			data = value;
			return true;
		}
	}

	return false;
}

void Toolkit::GWRewriteTool::PrintData() {

	std::vector<pair<PString,PString> >::iterator rule_iterator;

	PTRACE(2, "GK\tLoaded per GW rewrite data:");

	if (m_GWRewrite.GetSize() == 0) {
		PTRACE(2, "GK\tNo per GW data loaded");
		return;
	}

	for (PINDEX i = 0; i < m_GWRewrite.GetSize(); ++i) {

		// In
		for (rule_iterator = m_GWRewrite.GetDataAt(i).m_entry_data.first.begin(); rule_iterator != m_GWRewrite.GetDataAt(i).m_entry_data.first.end(); ++rule_iterator) {
			PTRACE(3, "GK\t" << m_GWRewrite.GetKeyAt(i) << " (in): " << (*rule_iterator).first << " = " << (*rule_iterator).second);
		}

		// Out
		for (rule_iterator = m_GWRewrite.GetDataAt(i).m_entry_data.second.begin(); rule_iterator != m_GWRewrite.GetDataAt(i).m_entry_data.second.end(); ++rule_iterator) {
			PTRACE(3, "GK\t" << m_GWRewrite.GetKeyAt(i) << " (out): " << (*rule_iterator).first << " = " << (*rule_iterator).second);
		}

	}

	PTRACE(2, "GK\tLoaded " << m_GWRewrite.GetSize() << " GW entries with rewrite info");

}


void Toolkit::GWRewriteTool::LoadConfig(PConfig *config) {

	PINDEX gw_size, i, j, lines_size;
	PString key, cfg_value;
	PStringArray lines, tokenised_line;
	GWRewriteEntry *gw_entry;
	std::map<PString,PString> in_strings, out_strings;
	vector<std::pair<PString,PString> > sorted_in_strings, sorted_out_strings;
	std::map<PString,PString>::reverse_iterator strings_iterator;
	pair<PString,PString> rule;

	PStringToString cfgs(config->GetAllKeyValues(GWRewriteSection));

	// Clear old config
	for (i = 0; i < m_GWRewrite.GetSize(); ++i) {
		delete &(m_GWRewrite.GetDataAt(i));
	}
	m_GWRewrite.RemoveAll();

	gw_size = cfgs.GetSize();
	if (gw_size > 0) {
		for (i = 0; i < gw_size; ++i) {

			// Get the config keys
			key = cfgs.GetKeyAt(i);
			cfg_value = cfgs[key];

			in_strings.clear();
			out_strings.clear();
			sorted_in_strings.clear();
			sorted_out_strings.clear();

			// Split the config data into seperate lines
			lines = cfg_value.Tokenise(PString(";"));

			lines_size = lines.GetSize();

			for (j = 0; j < lines_size; ++j) {

				// Split the config line into three strings, direction, from string, to string
				tokenised_line = lines[j].Tokenise(PString("="));

				if (tokenised_line.GetSize() < 3) {
					PTRACE(0, "GK\tSyntax error in the GWRewriteE164 rule - missing =, rule: " 
						<< key << " => " << lines[j]
						);
					continue;
				}

				// Put into appropriate std::map

				if (tokenised_line[0] == "in")
					in_strings[tokenised_line[1]] = tokenised_line[2];
				else if (tokenised_line[0] == "out")
					out_strings[tokenised_line[1]] = tokenised_line[2];
				else
					PTRACE(0, "GK\tSyntax error in the GWRewriteE164 rule - unknown rule type ("
						<< tokenised_line[0] << ", rule: " << key << " => " << lines[j]
						);
			}

			// Put the map contents into reverse sorted vectors
			for (strings_iterator = in_strings.rbegin(); strings_iterator != in_strings.rend(); ++strings_iterator) {
				rule = *strings_iterator;
				sorted_in_strings.push_back(rule);
			}
			for (strings_iterator = out_strings.rbegin(); strings_iterator != out_strings.rend(); ++strings_iterator) {
				rule = *strings_iterator;
				sorted_out_strings.push_back(rule);
			}


			// Create the entry
			gw_entry = new GWRewriteEntry();
			gw_entry->m_entry_data.first = sorted_in_strings;
			gw_entry->m_entry_data.second = sorted_out_strings;


			// Add to PDictionary hash table
			m_GWRewrite.Insert(key,gw_entry);

		}
	}

	PrintData();
}




Toolkit::Toolkit() : Singleton<Toolkit>("Toolkit"), 
	m_Config(NULL), m_ConfigDirty(false),
	m_acctSessionCounter(0), m_acctSessionBase((long)time(NULL)),
	m_timerManager(new GkTimerManager()),
	m_timestampFormatStr("Cisco"),
	m_encKeyPaddingByte(-1), m_encryptAllPasswords(false),
	m_cliRewrite(NULL)
{
	srand(time(0));
}

Toolkit::~Toolkit()
{
	if (m_Config) {
		delete m_Config;
		PFile::Remove(m_tmpconfig);
		PFile::Remove(m_extConfigFilePath);
	}
	delete m_timerManager;
	delete m_cliRewrite;
}

Toolkit::RouteTable *Toolkit::GetRouteTable(bool real)
{
	return real ? &m_RouteTable : m_VirtualRouteTable.IsEmpty() ? &m_RouteTable : &m_VirtualRouteTable;
}

PConfig* Toolkit::Config()
{
	// Make sure the config would not be called before SetConfig
	PAssert(!m_ConfigDefaultSection, "Error: Call Config() before SetConfig()!");
	return (m_Config == NULL) ? ReloadConfig() : m_Config;
}

PConfig* Toolkit::Config(const char *section)
{
	Config()->SetDefaultSection(section);
	return m_Config;
}

PConfig* Toolkit::SetConfig(const PFilePath &fp, const PString &section)
{
	m_ConfigFilePath = fp;
	m_ConfigDefaultSection = section;

	return ReloadConfig();
}

void Toolkit::SetConfig(int act, const PString & sec, const PString & key, const PString & value)
{
	// the original config
	PConfig cfg(m_ConfigFilePath, m_ConfigDefaultSection);
	switch (act)
	{
		case 1:
			cfg.SetString(sec, key, value);
			m_Config->SetString(sec, key, value);
			break;
		case 2:
			cfg.DeleteKey(sec, key);
			m_Config->DeleteKey(sec, key);
			break;
		case 3:
			cfg.DeleteSection(sec);
			m_Config->DeleteSection(sec);
			break;
	}

	m_ConfigDirty = true;
}

PString Toolkit::GetTempDir() const
{
	PString tmpdir;
	
#ifndef _WIN32
	// check if the directory exists and is accessible (access rights)
	if (PFile::Exists("/tmp") && PFile::Access("/tmp", PFile::ReadWrite))
		tmpdir = "/tmp";
	else 
#endif
	{
		PConfig cfg(PConfig::Environment);
		
		if (cfg.HasKey("TMP"))
			tmpdir = cfg.GetString("TMP");
		else if (cfg.HasKey("TEMP"))
			tmpdir = cfg.GetString("TEMP");
		else if (cfg.HasKey("TMPDIR"))
			tmpdir = cfg.GetString("TMPDIR");
	}
	
	if (!tmpdir.IsEmpty()) {
		// strip trailing separator
		if (tmpdir[tmpdir.GetLength()-1] == PDIR_SEPARATOR)
			tmpdir = tmpdir.Left(tmpdir.GetLength()-1);
			
		// check if the directory exists and is accessible (access rights)
		if (!(PFile::Exists(tmpdir) && PFile::Access(tmpdir, PFile::ReadWrite)))
			tmpdir = PString();
	}
	
	return tmpdir;
}

void Toolkit::CreateConfig()
{
	if (m_Config != NULL)
		PFile::Remove(m_tmpconfig);

	PString tmpdir = GetTempDir();
	
#ifdef _WIN32
	if (tmpdir.IsEmpty())
		if (PFile::Access(".", PFile::ReadWrite))
			tmpdir = ".";
		else {
			const PFilePath fpath(m_ConfigFilePath);
			tmpdir = fpath.GetDirectory();
		}
#else
	if (tmpdir.IsEmpty())
		tmpdir = ".";
#endif
	
	// generate a unique name
	do {
		m_tmpconfig = tmpdir + PDIR_SEPARATOR + "gnugk.ini-" + PString(PString::Unsigned, rand()%10000);
		PTRACE(5, "GK\tTrying file name "<< m_tmpconfig << " for temp config");
	} while (PFile::Exists(m_tmpconfig));

#ifdef _WIN32
	// Does WIN32 support symlink?
	if (PFile::Copy(m_ConfigFilePath, m_tmpconfig)) {
#else
	if (symlink(m_ConfigFilePath, m_tmpconfig) == 0) {
#endif
		delete m_Config;
		m_Config = new PConfig(m_tmpconfig, m_ConfigDefaultSection);
	} else { // Oops! Create temporary config file failed, use the original one
		PTRACE(0, "CONFIG\tCould not create/link config to a temporary file " << m_tmpconfig);
		delete m_Config;
		m_Config = new PConfig(m_ConfigFilePath, m_ConfigDefaultSection);
	}

	if (!m_extConfigFilePath) {
		PFile::Remove(m_extConfigFilePath);
	
		// generate a unique name
		do {
			m_extConfigFilePath = tmpdir + PDIR_SEPARATOR + "gnugk.ini-" + PString(PString::Unsigned, rand()%10000);
			PTRACE(5, "GK\tTrying file name "<< m_extConfigFilePath << " for external config");
		} while (PFile::Exists(m_extConfigFilePath));

		m_Config = new GatekeeperConfig(
			m_extConfigFilePath, m_ConfigDefaultSection, m_Config
			);
	}
}

void Toolkit::ReloadSQLConfig()
{
#if HAS_MYSQL || HAS_PGSQL
	if (m_Config->GetSections().GetStringsIndex("SQLConfig") == P_MAX_INDEX)
		return;

	const PString driverName = m_Config->GetString("SQLConfig", "Driver", "");
	if (driverName.IsEmpty()) {
		PTRACE(0, "SQLCONF\tFailed to read config settings from SQL: no driver specified");
		return;
	}
		
	GkSQLConnection *sqlConn = GkSQLConnection::Create(driverName, "SQLCONF");
	if (sqlConn == NULL) {
		PTRACE(0, "SQLCONF\tFailed to create a connection: no driver found for "
				<< driverName << " database"
				);
		return;
	}
	
	if (!sqlConn->Initialize(m_Config, "SQLConfig")) {
		delete sqlConn;
		sqlConn = NULL;
		PTRACE(0, "SQLCONF\tFailed to read config settings from SQL: could not connect to the database");
		return;
	}

	PTRACE(3, "SQLCONF\tSQL config connection established");
	
	PString query;
	GkSQLResult* queryResult;

	query = m_Config->GetString("SQLConfig", "ConfigQuery", "");
	if (!query.IsEmpty()) {
		PTRACE(4, "SQLCONF\tLoading config key=>value pairs from SQL database");
		PStringArray params;
		params += GKName();
		queryResult = sqlConn->ExecuteQuery(query, &params);
		if (queryResult == NULL)
			PTRACE(0, "SQLCONF\tFailed to load config key=>value pairs from SQL "
				"database: timeout or fatal error"
				);
		else if (!queryResult->IsValid())
			PTRACE(0, "SQLCONF\tFailed to load config key=>value pairs from SQL "
				"database (" << queryResult->GetErrorCode() << "): " 
				<< queryResult->GetErrorMessage()
				);
		else if (queryResult->GetNumFields() < 3)
			PTRACE(0, "SQLCONF\tFailed to load config key=>value pairs from SQL "
				"database: at least 3 columns must be present in the result set"
				);
		else {
			while (queryResult->FetchRow(params))
				if (params[0].IsEmpty() || params[1].IsEmpty())
					PTRACE(1, "SQLCONF\tInvalid config key=>value pair entry found "
						"in the SQL database: '[" << params[0] << "] " 
						<< params[1] << '=' << params[1] << '\''
						);
				else {
					m_Config->SetString(params[0], params[1], params[2]);
					PTRACE(6, "SQLCONF\tConfig entry read: '[" << params[0] 
						<< "] " << params[1] << '=' << params[2] << '\''
						);
				}
			PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() 
				<< " config key=>value pairs loaded from SQL database");
		}
		delete queryResult;
		queryResult = NULL;
	}
			

	query = m_Config->GetString("SQLConfig", "RewriteE164Query", "");
	if (!query.IsEmpty()) {
		PTRACE(4, "SQLCONF\tLoading rewrite rules from SQL database");
		PStringArray params;
		params += GKName();
		queryResult = sqlConn->ExecuteQuery(query, &params);
		if (queryResult == NULL)
			PTRACE(0, "SQLCONF\tFailed to load rewrite rules from SQL database: "
				"timeout or fatal error"
				);
		else if (!queryResult->IsValid())
			PTRACE(0, "SQLCONF\tFailed to load rewrite rules from SQL database ("
				<< queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()
				);
		else if (queryResult->GetNumFields() < 2)
			PTRACE(0, "SQLCONF\tFailed to load rewrite rules from SQL database: "
				"at least 2 columns must be present in the result set"
				);
		else {
			while (queryResult->FetchRow(params))
				if (params[0].IsEmpty())
					PTRACE(1, "SQLCONF\tInvalid rewrite rule found in the SQL "
						"database: '" << params[0] << '=' << params[1] << '\''
						);
				else {
					m_Config->SetString("RasSrv::RewriteE164", params[0], params[1]);
					PTRACE(6, "SQLCONF\tRewrite rule read: '" << params[0] 
						<< '=' << params[1] << '\''
						);
				}
			PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " rewrite rules "
				"loaded from SQL database"
				);
		}
		delete queryResult;
		queryResult = NULL;
	}

	query = m_Config->GetString("SQLConfig", "NeighborsQuery", "");
	if (!query.IsEmpty()) {
		PTRACE(4, "SQLCONF\tLoading neighbors from SQL database");
		PStringArray params;
		params += GKName();
		queryResult = sqlConn->ExecuteQuery(query, &params);
		if (queryResult == NULL)
			PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database: "
				"timeout or fatal error"
				);
		else if (!queryResult->IsValid())
			PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database ("
				<< queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()
				);
		else if (queryResult->GetNumFields() < 6)
			PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database: "
				"at least 6 columns must be present in the result set"
				);
		else {
			while (queryResult->FetchRow(params)) {
				PString value;
				if (!params[5])
					value = ";" + params[5];
				if (!(params[4].IsEmpty() && value.IsEmpty()))
					value = ";" + params[4] + value;
				if (!(params[3].IsEmpty() && value.IsEmpty()))
					value = ";" + params[3] + value;
				if (!params[2])
					value = params[1] + ":" + params[2] + value;
				else
					value = params[1] + value;
				if (params[0].IsEmpty() || params[1].IsEmpty())
					PTRACE(1, "SQLCONF\tInvalid neighbor entry found in the SQL "
						"database: '" << params[0] << '=' << value << '\''
						);
				else {
					m_Config->SetString("RasSrv::Neighbors", params[0], value);
					PTRACE(6, "SQLCONF\tNeighbor entry read: '" << params[0] 
						<< '=' << value << '\''
						);
				}
			}
			PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " neighbor entries "
				"loaded from SQL database"
				);
		}
		delete queryResult;
		queryResult = NULL;
	}

	query = m_Config->GetString("SQLConfig", "PermanentEndpointsQuery", "");
	if (!query.IsEmpty()) {
		PTRACE(4, "SQLCONF\tLoading permanent endpoints from SQL database");
		PStringArray params;
		params += GKName();
		queryResult = sqlConn->ExecuteQuery(query, &params);
		if (queryResult == NULL)
			PTRACE(0, "SQLCONF\tFailed to load permanent endpoints from SQL "
				"database: timeout or fatal error"
				);
		else if (!queryResult->IsValid())
			PTRACE(0, "SQLCONF\tFailed to load permanent endpoints from SQL database "
				"("	<< queryResult->GetErrorCode() << "): " 
				<< queryResult->GetErrorMessage()
				);
		else if (queryResult->GetNumFields() < 4)
			PTRACE(0, "SQLCONF\tFailed to load permanent endpoints from SQL database: "
				"at least 4 columns must be present in the result set"
				);
		else {
			PString key;
			PString value;
			while (queryResult->FetchRow(params)) {
				key = params[0];
				if (!params[1])
					key += ":" + params[1];
				value = params[2];
				if (!params[3])
					value += ";" + params[3];
				if (key.IsEmpty() || value.IsEmpty())
					PTRACE(1, "SQLCONF\tInvalid permanent endpoint entry found "
						"in the SQL database: '" << key << '=' << value << '\''
						);
				else {
					m_Config->SetString("RasSrv::PermanentEndpoints", key, value);
					PTRACE(6, "SQLCONF\tPermanent endpoint read: '" << key 
						<< '=' << value << '\''
						);
				}
			}
			PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " permanent "
				"endpoints loaded from SQL database"
				);
		}
		delete queryResult;
		queryResult = NULL;
	}

	query = m_Config->GetString("SQLConfig", "GWPrefixesQuery", "");
	if (!query.IsEmpty()) {
		PTRACE(4, "SQLCONF\tLoading gateway prefixes from SQL database");
		PStringArray params;
		params += GKName();
		queryResult = sqlConn->ExecuteQuery(query, &params);
		if (queryResult == NULL)
			PTRACE(0, "SQLCONF\tFailed to load gateway prefixes from SQL database: "
				"timeout or fatal error"
				);
		else if (!queryResult->IsValid())
			PTRACE(0, "SQLCONF\tFailed to load gateway prefixes from SQL database ("
				<< queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()
				);
		else if (queryResult->GetNumFields() < 2)
			PTRACE(0, "SQLCONF\tFailed to load gateway prefixes from SQL database: "
				"at least 2 columns must be present in the result set"
				);
		else {
			while (queryResult->FetchRow(params))
				if (params[0].IsEmpty() || params[1].IsEmpty())
					PTRACE(1, "SQLCONF\tInvalid gateway prefixes entry found "
						"in the SQL database: '" << params[0] << '=' 
						<< params[1] << '\''
						);
				else {
					m_Config->SetString("RasSrv::GWPrefixes", 
						params[0], params[1]
						);
					PTRACE(6, "SQLCONF\tGateway prefixes read: '" << params[0]
						<< '=' << params[1] << '\''
						);
				}
			PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " gateway prefixes "
				"loaded from SQL database"
				);
		}
		delete queryResult;
		queryResult = NULL;
	}
	
	delete sqlConn;
	sqlConn = NULL;
	PTRACE(3, "SQLCONF\tSQL config connection closed");
#endif // HAS_MYSQL || HAS_PGSQL
}

PConfig* Toolkit::ReloadConfig()
{
	if (!m_ConfigDirty)
		CreateConfig();
	else // the config have been changed via status port, use it directly
		m_ConfigDirty = false;

	m_GKName = Config()->GetString("Name", "OpenH323GK");
	
	m_encryptAllPasswords = Toolkit::AsBool(
		Config()->GetString("EncryptAllPasswords", "0")
		);
	if (Config()->HasKey(paddingByteConfigKey))
		m_encKeyPaddingByte = Config()->GetInteger(paddingByteConfigKey, 0);
	else
		m_encKeyPaddingByte = m_encryptAllPasswords ? 0 : -1;
		
	ReloadSQLConfig();
	
	m_RouteTable.InitTable();
	m_VirtualRouteTable.InitTable();
	m_ProxyCriterion.LoadConfig(m_Config);
	m_Rewrite.LoadConfig(m_Config);
	m_GWRewrite.LoadConfig(m_Config);
	PString GKHome(m_Config->GetString("Home", ""));
	if (m_GKHome.empty() || !GKHome)
		SetGKHome(GKHome.Tokenise(",:;", false));
	
	m_timestampFormatStr = Config()->GetString("TimestampFormat", "Cisco");

	delete m_cliRewrite;
	m_cliRewrite = new CLIRewrite;

	CapacityControl::Instance()->LoadConfig();

	LoadCauseMap(m_Config);
	
	return m_Config;
}

void Toolkit::LoadCauseMap(
	PConfig *cfg
	)
{
	memset(m_causeMap, 0, 16);
	
	if (! Toolkit::AsBool(cfg->GetString(RoutedSec, "ActivateFailover", "0")))
		return;

	PStringArray causes(cfg->GetString(RoutedSec, "FailoverCauses", "1-15,21-127").Tokenise(", \t", FALSE));

	for (PINDEX i = 0; i < causes.GetSize(); ++i)
		if (causes[i].Find('-') == P_MAX_INDEX) {
			unsigned c = causes[i].AsUnsigned() & 0x7f;
			m_causeMap[c >> 3] |= (1UL << (c & 7));
		} else {
			PStringArray causeRange(causes[i].Tokenise("- ", FALSE));
			if (causeRange.GetSize() == 2) {
				unsigned cmin = causeRange[0].AsUnsigned() & 0x7f;
				unsigned cmax = causeRange[1].AsUnsigned() & 0x7f;
				for (; cmin <= cmax; ++cmin)
					m_causeMap[cmin >> 3] |= (1UL << (cmin & 7));
			}
		}
}

BOOL Toolkit::MatchRegex(const PString &str, const PString &regexStr)
{
	PINDEX pos=0;
	PRegularExpression regex(regexStr, PRegularExpression::Extended);
	if(regex.GetErrorCode() != PRegularExpression::NoError) {
		PTRACE(2, "Errornous '"<< regex.GetErrorText() <<"' compiling regex: " << regexStr);
		return FALSE;
	}
	if(!regex.Execute(str, pos)) {
		PTRACE(5, "Gk\tRegex '"<<regexStr<<"' did not match '"<<str<<"'");
		return FALSE;
	}
	return TRUE;
}



bool Toolkit::RewriteE164(H225_AliasAddress &alias)
{
	if (alias.GetTag() != H225_AliasAddress::e_dialedDigits)
		return FALSE;

	PString E164 = ::AsString(alias, FALSE);

	bool changed = RewritePString(E164);
	if (changed)
		H323SetAliasAddress(E164, alias);

	return changed;
}

bool Toolkit::RewriteE164(H225_ArrayOf_AliasAddress & aliases)
{
	bool changed = false;
	for (PINDEX n = 0; n < aliases.GetSize(); ++n)
		changed |= RewriteE164(aliases[n]);
	return changed;
}

bool Toolkit::GWRewriteE164(PString gw, bool direction, H225_AliasAddress &alias) {

	PString E164;
	bool changed;

	if (alias.GetTag() != H225_AliasAddress::e_dialedDigits) {
		return false;
	}

	E164 = ::AsString(alias, FALSE);
	changed = GWRewritePString(gw,direction,E164);

	if (changed) {
		H323SetAliasAddress(E164, alias);
	}

	return changed;
}

bool Toolkit::GWRewriteE164(PString gw, bool direction, H225_ArrayOf_AliasAddress &aliases) {

	bool changed;
	PINDEX n;

	changed = false;
	for (n = 0; n < aliases.GetSize(); ++n) {
		changed |= GWRewriteE164(gw,direction,aliases[n]);
	}

	return changed;
}


PString Toolkit::GetGKHome(vector<PIPSocket::Address> & GKHome) const
{
	GKHome = m_GKHome;
	PString result;
	int hsize = GKHome.size();
	for (int i = 0; i < hsize; ++i) {
		result += GKHome[i].AsString();
		if (i < hsize - 1)
			result += ",";
	}
	return result;
}

void Toolkit::SetGKHome(const PStringArray & home)
{
	std::vector<PIPSocket::Address>::iterator begin;
	m_GKHome.clear();
	int n, i, size = home.GetSize();
	if (size > 0)
		for (n = 0; n < size; ++n)
			m_GKHome.push_back(home[n]);

	PIPSocket::InterfaceTable it;
	if (PIPSocket::GetInterfaceTable(it)) {
		int is = it.GetSize();
		if (size > 0) {
			// check if the interface is valid
			for (n = 0; n < size; ++n) {
				for (i = 0; i < is; ++i)
					if (m_GKHome[n] == it[i].GetAddress())
						break;
				if (i == is) {
					PTRACE(1, "GK\tAddress " << m_GKHome[n] << " not found"
						" in the PWLib interface table"
						);
					//begin = m_GKHome.begin();
					//copy(begin + n + 1, begin + size, begin + n);
					//--size, --n;
				}
			}
		}
		if (size == 0) {
			m_GKHome.clear();
			size = is;
			for (n = 0; n < size; ++n)
				m_GKHome.push_back(it[n].GetAddress());
		}
		// remove INADDR_ANY
		for (n = 0; n < size; ++n)
			if (m_GKHome[n] == INADDR_ANY) {
				begin = m_GKHome.begin();
				copy(begin + n + 1, begin + size, begin + n);
				--size, --n;
			}
	}

	// remove duplicate interfaces
	for (n = 0; n < size; ++n)
		for (i = 0; i < n; ++i)
			if (m_GKHome[n] == m_GKHome[i]) {
				begin = m_GKHome.begin();
				copy(begin + n + 1, begin + size, begin + n);
				--size, --n;
				break;
			}

	m_GKHome.resize(size);
	// put the default IP to the first
	begin = find(m_GKHome.begin(), m_GKHome.end(), m_RouteTable.GetLocalAddress());
	if (begin != m_GKHome.end())
		swap(m_GKHome[0], *begin);
}

int
Toolkit::GetInternalExtensionCode( const unsigned &country,
				   const unsigned &extension,
				   const unsigned &manufacturer) const
{
	switch(country) {
	case t35cOpenOrg:
		switch(manufacturer) {
		case t35mOpenOrg:
			switch(extension) {
				case t35eFailoverRAS: return iecFailoverRAS;
			}
		}
	}

	// default for all other cases
	return iecUnknown;
}

int Toolkit::GetInternalExtensionCode(const H225_H221NonStandard& data) const
{
	return GetInternalExtensionCode(data.m_t35CountryCode,
			data.m_t35Extension,
			data.m_manufacturerCode);
}

bool Toolkit::AsBool(const PString & str)
{
	if (str.IsEmpty())
		return false;
	const unsigned char c = (unsigned char)tolower(str[0]);
	return ( c=='t' || c=='1' || c=='y' || c=='a' );
}

void Toolkit::GetNetworkFromString(
	const PString &s,
	PIPSocket::Address &network,
	PIPSocket::Address &netmask
	)
{
	if (s *= "ALL") {
		network = netmask = INADDR_ANY;
		return;
	}

	PINDEX slashPos = s.Find('/');
	if (slashPos == P_MAX_INDEX) {
		// a single IP
		static BYTE fullNetMask[16] = { 
			255, 255, 255, 255, 255, 255, 255, 255,
			255, 255, 255, 255, 255, 255, 255, 255
			};
		network = PIPSocket::Address(s);
		netmask = PIPSocket::Address(network.GetSize(), fullNetMask);
	} else {
		network = PIPSocket::Address(s.Left(slashPos));
		
		const PString netmaskString = s.Mid(slashPos + 1);
		BYTE rawData[16];
		
		if (netmaskString.FindOneOf(".:") != P_MAX_INDEX) {
			// netmask as a network address
			netmask = PIPSocket::Address(netmaskString);
		} else {
			// netmask as an integer
			const DWORD netmaskLen = netmaskString.AsUnsigned();
			for (unsigned b = 0; b < (unsigned)(network.GetSize() * 8); b++)
				if (b < netmaskLen)
					rawData[b >> 3] |= 0x80U >> (b & 7);
				else
					rawData[b >> 3] &= ~(0x80U >> (b & 7));
			netmask = PIPSocket::Address(network.GetSize(), rawData);
		}
		
		// normalize the address
		for (unsigned b = 0; b < (unsigned)(network.GetSize()); b++)
			rawData[b] = network[b] & netmask[b];
				
		network = PIPSocket::Address(network.GetSize(), rawData);
	}
}

PString Toolkit::CypherDecode(const PString & key, const PString & crypto, int s)
{
	size_t sz = key.GetLength();
	if (sz > sizeof(PTEACypher::Key))
		sz = sizeof(PTEACypher::Key);
	PTEACypher::Key thekey;
	memset(&thekey, s, sizeof(PTEACypher::Key));
	memcpy(&thekey, (const char *)key, sz);
	PTEACypher cypher(thekey);

	return cypher.Decode(crypto);
}

PString Toolkit::GenerateAcctSessionId()
{
	PWaitAndSignal lock( m_acctSessionMutex );
	return psprintf(PString("%08x%08x"),m_acctSessionBase,++m_acctSessionCounter);
}

bool Toolkit::AsTimeInterval(
	/// formatted time interval string
	const char* inputString,
	/// variable to store calculated time interval on success
	PTimeInterval& interval
	)
{
	if (inputString == NULL)
		return false;

	PTimeInterval result;
	bool process_next = true;
	bool valid = false;
		
	while (process_next) {
		char* strend = const_cast<char*>(inputString);
		long val = strtol(inputString, &strend, 10);
		
		// no tokens found?
		if (strend == inputString)
			break;
		
		// integer range overflow
		if ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE) {
			valid = false;
			break;
		}
		
		valid = true;
		inputString = strend;
		
		// the last token
		if (*inputString == 0) {
			result += PTimeInterval(val);
			break;
		}

		// unit specifier		
		switch (*inputString)
		{
		case 's':
			result += PTimeInterval(0,val);
			break;
		case 'm':
			result += PTimeInterval(0,0,val);
			break;
		case 'h':
			result += PTimeInterval(0,0,0,val);
			break;
		case 'd':
			result += PTimeInterval(0,0,0,0,val);
			break;
		case 'w':
			result += PTimeInterval(0,0,0,0,val*7);
			break;
		case 'M':
			result += PTimeInterval(0,0,0,0,val*30);
			break;
		case 'y':
			result += PTimeInterval(0,0,0,0,val*365);
			break;
		default:
			result += PTimeInterval(val);
			process_next = false;
		}
		
		if (process_next)
			inputString++;
	}
	
	if (valid)
		interval = result;
	return valid;
}

PString Toolkit::AsString(
	const PTime& tm, /// timestamp to convert into a string
	const PString& formatStr /// format string to use
	)
{
	PString fmtStr = !formatStr ? formatStr : m_timestampFormatStr;
	if (fmtStr.IsEmpty())
		return PString();

	if (fmtStr *= "Cisco")
		fmtStr = "%H:%M:%S.%u %Z %a %b %d %Y";
	else if (fmtStr *= "ISO8601")
		return tm.AsString(PTime::LongISO8601);
	else if (fmtStr *= "RFC822")
		return tm.AsString(PTime::RFC1123);
	else if (fmtStr *= "MySQL" )
		fmtStr = "%Y-%m-%d %H:%M:%S";
	
	struct tm _tm;
	struct tm* tmptr = &_tm;
	time_t t = tm.GetTimeInSeconds();

#ifndef _WIN32
	if (localtime_r(&t, tmptr) != tmptr) {
#else
	tmptr = localtime(&t);
	if (tmptr  == NULL) {
#endif
		PTRACE(0, "TOOLKIT\tCould not apply timestamp formatting - using default");
		return tm.AsString( "hh:mm:ss.uuu z www MMM d yyyy" );
	}

	// replace %u with microseconds - this is our extension
	PINDEX i = 0;
	PINDEX length = fmtStr.GetLength();
	do {
		i = fmtStr.Find("%u", i);
		if (i != P_MAX_INDEX) {
			if (i > 0 && fmtStr[i-1] == '%') {
				i += 2;
				continue;
			}
			const PString us(PString::Printf, "%03d", (unsigned)tm.GetMicrosecond());
			fmtStr.Splice(us, i, 2);
			length += us.GetLength();
			i += us.GetLength();
			length -= 2;
		}
	} while (i != P_MAX_INDEX && i < length);
	
	PString buf;
	
	buf.SetSize(128);
	if (strftime(buf.GetPointer(), 128, (const char*)fmtStr, tmptr) == 0) {
		PTRACE(0, "TOOLKIT\tCould not apply timestamp formatting - using default");
		return tm.AsString( "hh:mm:ss.uuu z www MMM d yyyy" );
	}
	
	buf.MakeMinimumSize();
	return buf;
}

PString Toolkit::ReadPassword(
	const PString &cfgSection, /// config section to read
	const PString &cfgKey, /// config key to read an encrypted password from
	bool forceEncrypted
	)
{
	if (cfgSection.IsEmpty() || cfgKey.IsEmpty())
		return PString();
		
	PConfig* const cfg = Config();
	if (!cfg->HasKey(cfgSection, cfgKey))
		return PString();

	int paddingByte = m_encKeyPaddingByte;
	if (cfg->HasKey(cfgSection, paddingByteConfigKey))
		paddingByte = cfg->GetInteger(cfgSection, paddingByteConfigKey, 0);

	if (paddingByte == -1)
		if (forceEncrypted || m_encryptAllPasswords)
			paddingByte = 0;
		else
			return cfg->GetString(cfgSection, cfgKey, "");

	PTEACypher::Key encKey;
	memset(&encKey, paddingByte, sizeof(encKey));

	const size_t keyLen = cfgKey.GetLength();
	if (keyLen > 0)
		memcpy(&encKey, (const char*)cfgKey, min(keyLen, sizeof(encKey)));

	PTEACypher cypher(encKey);
	PString s;
	if (!cypher.Decode(cfg->GetString(cfgSection, cfgKey, ""), s))
		PTRACE(1, "GK\tFailed to decode config password for [" << cfgSection
			<< "] => " << cfgKey
			);
	return s;
}

void Toolkit::RewriteCLI(
	SetupMsg &msg
	) const
{
	m_cliRewrite->InRewrite(msg);
}

void Toolkit::RewriteCLI(
	SetupMsg &msg,
	SetupAuthData &authData,
	const PIPSocket::Address &addr
	) const
{
	m_cliRewrite->OutRewrite(msg, authData, addr);
}

void Toolkit::SetRerouteCauses(
	unsigned char *causeMap
	)
{
	memcpy(causeMap, m_causeMap, 128/8);
}


syntax highlighted by Code2HTML, v. 0.9.1