//////////////////////////////////////////////////////////////////
//
// H.323 utility functions that should migrate into the OpenH323 library
//
// 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:
// 	991129	initial version (Henrik Joerring)
//
//////////////////////////////////////////////////////////////////

#include <ptlib.h>
#include <h323pdu.h>
#include "gk_const.h"
#include "h323util.h"


PString AsString(const PIPSocket::Address & ip, WORD pt)
{
	return PString(PString::Printf, "%d.%d.%d.%d:%u",
		ip[0], ip[1], ip[2], ip[3], pt);
}

PString AsString(const H225_TransportAddress & ta)
{
	PStringStream stream;
	stream << ta;
	return stream;
}

PString AsDotString(const H225_TransportAddress & ip)
{
	return (ip.IsValid() && ip.GetTag() == H225_TransportAddress::e_ipAddress) ?
		AsString((const H225_TransportAddress_ipAddress &)ip) : PString();
}

PString AsString(const H225_TransportAddress_ipAddress & ip)
{
	return PString(PString::Printf, "%d.%d.%d.%d:%u",
		ip.m_ip[0], ip.m_ip[1], ip.m_ip[2], ip.m_ip[3],
		ip.m_port.GetValue());
}

PString AsString(const H225_EndpointType & terminalType)
{
	PString terminalTypeString;
			
	if (terminalType.HasOptionalField(H225_EndpointType::e_terminal))
		terminalTypeString = ",terminal";

	if (terminalType.HasOptionalField(H225_EndpointType::e_gateway))
		terminalTypeString += ",gateway";
	
	if (terminalType.HasOptionalField(H225_EndpointType::e_mcu))
		terminalTypeString += ",mcu";

	if (terminalType.HasOptionalField(H225_EndpointType::e_gatekeeper))
		terminalTypeString += ",gatekeeper";

/* vendor seems always to be set - this clutters up the display
	if (terminalType.HasOptionalField(H225_EndpointType::e_vendor))
		terminalTypeString += ",vendor";
*/

	if (terminalTypeString.IsEmpty())
		terminalTypeString = ",unknown";
	
	return terminalTypeString.Mid(1);
}

PString AsString(const H225_AliasAddress & terminalAlias, BOOL includeAliasName)
{
	PString aliasString;

	if(!terminalAlias.IsValid())
		return includeAliasName ? "invalid:UnknownType" : "invalid";

	switch (terminalAlias.GetTag()) {
		case H225_AliasAddress::e_dialedDigits:
		case H225_AliasAddress::e_url_ID:
		case H225_AliasAddress::e_email_ID:
		case H225_AliasAddress::e_h323_ID:
		case H225_AliasAddress::e_transportID:
		case H225_AliasAddress::e_partyNumber:
			aliasString = H323GetAliasAddressString(terminalAlias);
			// OpenH323 prepends a special prefix to partyNumbers
			// to distinguish a number subtype - we don't need this
			if (terminalAlias.GetTag() == H225_AliasAddress::e_partyNumber) {
				const PINDEX prefixIndex = aliasString.Find(':');
				if (prefixIndex != P_MAX_INDEX)
					aliasString = aliasString.Mid(prefixIndex + 1);
			}
			if (includeAliasName) {
				aliasString += ":" + terminalAlias.GetTagName();
			}
			return aliasString;
			break;
	}

	return includeAliasName ? "none:UnknownType" : "none";
}


PString AsString(const H225_ArrayOf_AliasAddress & terminalAlias, BOOL includeAliasName)
{
	PString aliasListString = "";

	for(PINDEX cnt = 0; cnt < terminalAlias.GetSize(); cnt ++ )
	{

		aliasListString += AsString(terminalAlias[cnt], includeAliasName);

		if (cnt < (terminalAlias.GetSize() - 1)) {
			aliasListString += "=";
		}
	}
	return (aliasListString);
}

PString AsString(const PASN_OctetString & Octets)
{
	PString result;
	if (Octets.GetDataLength() > 0) {
		result = PString(PString::Printf, "%02x", Octets[0]);
		for (PINDEX i = 1; i < Octets.GetDataLength(); ++i)
			result += PString(PString::Printf, " %02x", Octets[i]);
	}
	return result;
}

// convert a socket IP address into an H225 transport address
H225_TransportAddress SocketToH225TransportAddr(const PIPSocket::Address & Addr, WORD Port)
{
	H225_TransportAddress Result;

	Result.SetTag( H225_TransportAddress::e_ipAddress );
	H225_TransportAddress_ipAddress & ResultIP = Result;

	for (int i = 0; i < 4; ++i)
		ResultIP.m_ip[i] = Addr[i];
	ResultIP.m_port  = Port;

	return Result;
}

bool GetTransportAddress(const PString & addr, WORD def_port, PIPSocket::Address & ip, WORD & port)
{
	PString ipAddr = addr.Trim();
	PINDEX p = ipAddr.Find(':');
	port = (p != P_MAX_INDEX) ? WORD(ipAddr.Mid(p+1).AsUnsigned()) : def_port;
	return PIPSocket::GetHostAddress(ipAddr.Left(p), ip) != 0;
}

bool GetTransportAddress(const PString & addr, WORD def_port, H225_TransportAddress & Result)
{
	PIPSocket::Address ip;
	WORD port;
	bool res = GetTransportAddress(addr, def_port, ip, port);
	if (res)
		Result = SocketToH225TransportAddr(ip, port);
	return res;
}

bool GetIPFromTransportAddr(const H225_TransportAddress & addr, PIPSocket::Address & ip)
{
	WORD port;
	return GetIPAndPortFromTransportAddr(addr, ip, port);
}

bool GetIPAndPortFromTransportAddr(const H225_TransportAddress & addr, PIPSocket::Address & ip, WORD & port)
{
	if (!(addr.IsValid() && addr.GetTag() == H225_TransportAddress::e_ipAddress))
		return false;
	const H225_TransportAddress_ipAddress & ipaddr = addr;
	ip = PIPSocket::Address(ipaddr.m_ip.GetSize(), (const BYTE*)ipaddr.m_ip);
	port = (WORD)ipaddr.m_port;
	return true;
}

bool IsLoopback(const PIPSocket::Address & addr)
{
	return addr.IsLoopback() != 0;
}

unsigned MapH225ReasonToQ931Cause(
	int reason
	)
{
	// from ITU-T Recommendation H.225
	static unsigned H225ReasonToQ931Cause[] =
	{
		34, 47, 3, 16, 88, 111, 38, 42, 28, 41, 17, 31, 16, 31, 20, 31, 47, 127,
		31, 31, 31, 127
	};
	
	if( reason < 0 || reason > H225_ReleaseCompleteReason::e_tunnelledSignallingRejected )
		return 0;
	else
		return H225ReasonToQ931Cause[reason];
}

PString GetGUIDString(
	const H225_GloballyUniqueID& id, /// 128-bit identifier to convert
	bool fixedLength /// skip leading zeros (false) or not (true)
	)
{
	if (id.GetSize() < 16)
		return "Invalid";
		
	PString idstr;
					
	for (int j = 0, i = 0; j < 4; j++) {
		const unsigned hex = ((unsigned)(id[i])<<24) | ((unsigned)(id[i+1])<<16) 
			| ((unsigned)(id[i+2])<<8) | ((unsigned)(id[i+3]));
		i += 4;
		
		idstr += fixedLength ? PString(PString::Printf, "%08x", hex)
			: PString(PString::Unsigned, (long)hex, 16);
		if (j < 3)
			idstr += ' ';
	}

	return idstr;
}

PINDEX GetBestAliasAddressIndex(
	const H225_ArrayOf_AliasAddress& aliases, /// aliases to be searched
	bool exactMatch, /// search only specified tags or find any alias
	unsigned primaryTags, /// ORed tag flags (BestAliasTagMask)
	unsigned secondaryTags /// ORed tag flags (BestAliasTagMask)
	)
{
	if (primaryTags)
		for (PINDEX i = 0; i < aliases.GetSize(); i++)
			if (primaryTags & (1U << aliases[i].GetTag()))
				return i;

	if (secondaryTags)
		for (PINDEX i = 0; i < aliases.GetSize(); i++)
			if (secondaryTags & (1U << aliases[i].GetTag()))
				return i;

	if (!exactMatch && aliases.GetSize() > 0)
		return 0;

	return P_MAX_INDEX;
}

PString GetBestAliasAddressString(
	const H225_ArrayOf_AliasAddress& aliases, /// aliases to be searched
	bool exactMatch, /// search only specified tags or find any alias
	unsigned primaryTags, /// ORed tag flags (BestAliasTagMask)
	unsigned secondaryTags /// ORed tag flags (BestAliasTagMask)
	)
{
	const PINDEX i = GetBestAliasAddressIndex(aliases, exactMatch,
		primaryTags, secondaryTags
		);
	if (i != P_MAX_INDEX)
		return AsString(aliases[i], FALSE);
	else
		return PString();
}

PINDEX FindAlias(
	const H225_ArrayOf_AliasAddress& aliases, /// the list of aliases to check
	const PString& alias /// alias to find on the list
	)
{
	const PINDEX sz = aliases.GetSize();
	for (PINDEX i = 0; i < sz; i++)
		if (alias == AsString(aliases[i], FALSE))
			return i;
			
	return P_MAX_INDEX;
}

int MatchPrefix(
	const char* alias,
	const char* prefix
	)
{
	if (alias == NULL || prefix == NULL)
		return 0;

	const bool negative = (prefix[0] == '!');
	
	int i = 0;
	int j = (negative ? 1 : 0);
	
	while (prefix[j] != 0) {
		const char c = prefix[j];
		if (alias[i] == 0 || (c != '.' && c != '%' && c != alias[i]))
			return 0;
		i++;
		j++;
	}
	
	return negative ? -j + 1 : j;
}

PString RewriteString(
	const PString& s, /// original string to rewrite
	const char *prefix, /// prefix string that matched
	const char *value /// new string that replaces the prefix string
	)
{
	if (prefix == NULL || value == NULL)
		return s;
	
	PString result = value + s.Mid(strlen(prefix));

	const char *lastSrcDot = prefix;
	const char *lastDstDot = strchr(value, '.');
	while (lastDstDot != NULL) {
		lastSrcDot = strchr(lastSrcDot, '.');
		if (lastSrcDot == NULL) {
			PTRACE(0, "GK\tInvalid rewrite rule (dots do not match) - "
				<< prefix << " = " << value
				);
			break;
		}
		int dotDstOffset = (long)lastDstDot - (long)value;
		int dotSrcOffset = (long)lastSrcDot - (long)prefix;
		while (*lastDstDot++ == '.' && *lastSrcDot++ == '.')
			result[dotDstOffset++] = s[dotSrcOffset++];
						
		lastDstDot = strchr(lastDstDot, '.');
	}
	
	return result;
}


syntax highlighted by Code2HTML, v. 0.9.1