/***************************************************************************
                          cmessagehandler.cpp  -  description
                             -------------------
    begin                : Sun Sep 30 2001
    copyright            : (C) 2001-2005 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cmessagehandler.h"

#include <stdio.h>
#include <stdlib.h>

#include "core/cbytearray.h"
#include "core/cbase64.h"
#include "core/cnetaddr.h"
#include "core/ciconv.h"

// needed for remote encoding setting
#include "cconfig.h"

// parsed messages
#define DC_S_MESSAGE_CHAT 		"<"
#define DC_S_MESSAGE_SEARCH 		"$Search "
#define DC_S_MESSAGE_MYINFO 		"$MyINFO "
#define DC_S_MESSAGE_HELLO 		"$Hello "
#define DC_S_MESSAGE_QUIT 		"$Quit "
#define DC_S_MESSAGE_NICKLIST 		"$NickList "
#define DC_S_MESSAGE_OPLIST 		"$OpList "
#define DC_S_MESSAGE_CONNECTTOME 	"$ConnectToMe "
#define DC_S_MESSAGE_KEY 		"$Key "
#define DC_S_MESSAGE_HUBNAME 		"$HubName "
#define DC_S_MESSAGE_LOCK 		"$Lock "
#define DC_S_MESSAGE_TO 		"$To: "
#define DC_S_MESSAGE_FORCEMOVE 		"$ForceMove "
#define DC_S_MESSAGE_REVCONNECTTOME 	"$RevConnectToMe "
#define DC_S_MESSAGE_SR 		"$SR "
#define DC_S_MESSAGE_MYNICK 		"$MyNick "
#define DC_S_MESSAGE_DIRECTION 		"$Direction "
#define DC_S_MESSAGE_MAXEDOUT 		"$MaxedOut"	// no free slots
#define DC_S_MESSAGE_FILELENGTH 	"$FileLength "	// recv. file
#define DC_S_MESSAGE_GET 		"$Get "
#define DC_S_MESSAGE_ERROR		"$Error "
#define DC_S_MESSAGE_GETLISTLEN		"$GetListLen"
#define DC_S_MESSAGE_VALIDATEDENIDE 	"$ValidateDenide"  //hub login failed ...
#define DC_S_MESSAGE_HUBISFULL 		"$HubIsFull"
#define DC_S_MESSAGE_LISTLEN		"$ListLen "
#define DC_S_MESSAGE_SEND		"$Send"
#define DC_S_MESSAGE_GETINFO 		"$GetINFO "
#define DC_S_MESSAGE_PING 		"$Ping"
#define DC_S_MESSAGE_GETPASS 		"$GetPass"
#define DC_S_MESSAGE_BADPASS 		"$BadPass"
#define DC_S_MESSAGE_LOGEDIN		"$LogedIn"
#define DC_S_MESSAGE_CANCEL		"$Cancel"
#define DC_S_MESSAGE_CANCELED		"$Canceled"
#define DC_S_MESSAGE_SUPPORTS		"$Supports "
#define DC_S_MESSAGE_HUB_TOPIC		"$HubTopic "
#define DC_S_MESSAGE_GET_NET_INFO	"$GetNetInfo"
#define DC_S_MESSAGE_USER_IP		"$UserIP "
// ignored messages
#define DC_S_MESSAGE_MULTISEARCH	"$MultiSearch "
#define DC_S_MESSAGE_USER_COMMAND	"$UserCommand " // ??? ( $UserCommand 0 / $UserCommand 1 Read Help$<%[mynick]> +help&#124;<%[mynick]> -help&#124; )

// todo: parse
#define DC_S_MESSAGE_AGE 		"age="		// age=NEWS ... ???
#define DC_S_EXTRA_DCPLUS		"$DCPlus "

#define DC_S_MESSAGE_UGETBLOCK		"$UGetBlock "
#define DC_S_MESSAGE_UGETZBLOCK		"$UGetZBlock "
// ZLIB compression messages
#define DC_S_MESSAGE_GETZBLOCK		"$GetZBlock "
#define DC_S_MESSAGE_SENDING		"$Sending "
#define DC_S_MESSAGE_FAILED		"$Failed "

//ADCGet command and response
#define DC_S_MESSAGE_ADCGET		"$ADCGET "
#define DC_S_MESSAGE_ADCSND		"$ADCSND "

// ZPipe message - just $ZOn|
// Although CMessageHandler may be able to parse the $ZOn| itself
// it can not handle any compressed data so there's no point
// parsing it, CClient already found it and can deal with it itself
#define DC_S_MESSAGE_ZON		"$ZOn"

CMessageHandler::CMessageHandler( CString remote )
{
	CString to = "UTF-8";
	
	if ( CConfig::Instance() != 0 )
	{
		to = CConfig::Instance()->GetLocalEncoding();
		if ( remote.IsEmpty() )
		{
			remote = CConfig::Instance()->GetRemoteEncoding();
		}
	}
	
	m_pRemoteToLocal = new CIconv( remote, to );
	m_pUTF8ToLocal = new CIconv( "UTF-8", to );
}

CMessageHandler::~CMessageHandler()
{
	delete m_pRemoteToLocal;
	m_pRemoteToLocal = 0;
	delete m_pUTF8ToLocal;
	m_pUTF8ToLocal = 0;
}

bool CMessageHandler::GetContent( const CString sMessage, const CString * sData, CString & sContent )
{
	if ( sData->StartsWith(sMessage) )
	{
		sContent = sData->Mid(sMessage.Length(),sData->Length()-sMessage.Length());
		return true;
	}

	return false;
}

/** */
eDCMessage CMessageHandler::Parse( const CString * sMessage, int & pointer, CDCMessage ** Object  )
{
	CString t;
	CString sContent;
	int old_index,index;

	old_index = index = pointer;

	*Object = 0;

	index = sMessage->Find('|',index);

	if ( index >= 0 )
	{
		pointer = index+1;

		t = sMessage->Mid(old_index,index-old_index);

		if ( t.IsEmpty() )
		{
			if ( old_index != index )
			{
				printf("!!!!!!! PARSER ERROR index: %d %d\n",old_index,index);
				printf("%s\n",sMessage->Data());
			}
			return DC_MESSAGE_UNKNOWN;
		}

		/*
		 * Commands that you get more often should be higher in the if statement.
		 * Main change was moving ADCGET / ADCSND towards the top of the list, which
		 * is more important now DC++ does segmented downloading.
		 */
		// check chat
		/* well '<' != '$' so "check another chat" can catch it */
		/* if ( t.Data()[0] == '<' )
		{
			sContent = t;

			*Object  = ParseChat(sContent);

			return DC_MESSAGE_CHAT;
		} */
		// check another chat
		if ( t.Data()[0] != '$' )
		{
			sContent = t;

			*Object  = ParseChat(sContent);

			return DC_MESSAGE_CHAT;
		}
		else if ( GetContent(DC_S_MESSAGE_SEARCH,&t,sContent) )
		{
			*Object = ParseSearch(sContent);

			return DC_MESSAGE_SEARCH_FILE;
		}
		else if ( GetContent(DC_S_MESSAGE_MYINFO,&t,sContent) )
		{
			*Object  = ParseMyInfo(sContent);

			return DC_MESSAGE_MYINFO;
		}
		else if ( GetContent(DC_S_MESSAGE_SR,&t,sContent) )
		{
			*Object = ParseSearchResult(sContent);

			return DC_MESSAGE_SEARCHRESULT;
		}
		else if ( GetContent(DC_S_MESSAGE_ADCGET,&t,sContent) )
		{
			*Object = ParseADCGet(sContent);
			
			return DC_MESSAGE_ADCGET;
		}
		else if ( GetContent(DC_S_MESSAGE_ADCSND,&t,sContent) )
		{
			*Object = ParseADCSnd(sContent);
			
			return DC_MESSAGE_ADCSND;
		}
		else if ( GetContent(DC_S_MESSAGE_HELLO,&t,sContent) )
		{
			*Object  = ParseHello(sContent);

			return DC_MESSAGE_HELLO;
		}
		else if ( GetContent(DC_S_MESSAGE_QUIT,&t,sContent) )
		{
			*Object  = ParseQuit(sContent);

			return DC_MESSAGE_QUIT;
		}
		else if ( GetContent(DC_S_MESSAGE_NICKLIST,&t,sContent) )
		{
			*Object = ParseNickList(sContent);

			return DC_MESSAGE_NICKLIST;
		}
		else if ( GetContent(DC_S_MESSAGE_OPLIST,&t,sContent) )
		{
			*Object = ParseOpList(sContent);

			return DC_MESSAGE_OPLIST;
		}
		else if ( GetContent(DC_S_MESSAGE_SUPPORTS,&t,sContent) )
		{
			*Object = ParseSupports(sContent);

			return DC_MESSAGE_SUPPORTS;
		}
		else if ( GetContent(DC_S_MESSAGE_CONNECTTOME,&t,sContent) )
		{
			*Object  = ParseConnectToMe(sContent);

			return DC_MESSAGE_CONNECTTOME;
		}
		else if ( GetContent(DC_S_MESSAGE_REVCONNECTTOME,&t,sContent) )
		{
			*Object  = ParseRevConnectToMe(sContent);

			return DC_MESSAGE_REVCONNECTTOME;
		}
		else if ( GetContent(DC_S_MESSAGE_KEY,&t,sContent) )
		{
			*Object = new CMessageKey();

			return DC_MESSAGE_KEY;
		}
		else if ( GetContent(DC_S_MESSAGE_HUBNAME,&t,sContent) )
		{
			*Object  = ParseHubName(sContent);

			return DC_MESSAGE_HUBNAME;
		}
		else if ( GetContent(DC_S_MESSAGE_LOCK,&t,sContent) )
		{
			*Object  = ParseLock(sContent);

			return DC_MESSAGE_LOCK;
		}
		else if ( GetContent(DC_S_MESSAGE_TO,&t,sContent) )
		{
			*Object  = ParsePrivateChat(sContent);

			return DC_MESSAGE_PRIVATECHAT;
		}
		else if ( GetContent(DC_S_MESSAGE_FORCEMOVE,&t,sContent) )
		{
			*Object  = ParseForceMove(sContent);

			return DC_MESSAGE_FORCEMOVE;
		}
		else if ( GetContent(DC_S_MESSAGE_MYNICK,&t,sContent) )
		{
			*Object  = ParseMyNick(sContent);

			return DC_MESSAGE_MYNICK;
		}
		else if ( GetContent(DC_S_MESSAGE_DIRECTION,&t,sContent) )
		{
			*Object = ParseDirection(sContent);

			return DC_MESSAGE_DIRECTION;
		}
		else if ( GetContent(DC_S_MESSAGE_FILELENGTH,&t,sContent) )
		{
			*Object = ParseFileLength(sContent);

			return DC_MESSAGE_FILELENGTH;
		}
		else if ( GetContent(DC_S_MESSAGE_LISTLEN,&t,sContent) )
		{
			*Object = ParseFileLength(sContent);

			return DC_MESSAGE_LISTLEN;
		}
		else if ( GetContent(DC_S_MESSAGE_GET,&t,sContent) )
		{
			*Object = ParseGet(sContent);

			return DC_MESSAGE_GET;
		}
		else if ( GetContent(DC_S_MESSAGE_UGETBLOCK,&t,sContent) )
		{
			*Object = ParseUGetBlock(sContent);

			return DC_MESSAGE_GET;
		}
		else if ( GetContent(DC_S_MESSAGE_UGETZBLOCK,&t,sContent) )
		{
			*Object = ParseUGetZBlock(sContent);
			
			return DC_MESSAGE_GET;
		}
		else if ( GetContent(DC_S_MESSAGE_ERROR,&t,sContent) )
		{
			*Object = ParseError(sContent);

			return DC_MESSAGE_ERROR;
		}
		else if ( GetContent(DC_S_MESSAGE_GETINFO,&t,sContent) )
		{
			*Object = ParseGetInfo(sContent);

			return DC_MESSAGE_GETINFO;
		}
		else if ( GetContent(DC_S_MESSAGE_MAXEDOUT,&t,sContent) )
		{
			*Object = new CMessageMaxedOut();

			return DC_MESSAGE_MAXEDOUT;
		}
		else if ( GetContent(DC_S_MESSAGE_CANCEL,&t,sContent) )
		{
			*Object = new CMessageCancel();

			return DC_MESSAGE_CANCEL;
		}
		else if ( GetContent(DC_S_MESSAGE_CANCELED,&t,sContent) )
		{
			*Object = new CMessageCanceled();

			return DC_MESSAGE_CANCELED;
		}
		else if ( GetContent(DC_S_MESSAGE_SENDING,&t,sContent) )
		{
			*Object = ParseSending(sContent);

			return DC_MESSAGE_SENDING;
		}
		else if ( GetContent(DC_S_MESSAGE_SEND,&t,sContent) )
		{
			*Object = new CMessageSend();

			return DC_MESSAGE_SEND;
		}
		else if ( GetContent(DC_S_MESSAGE_GETLISTLEN,&t,sContent) )
		{
			*Object = new CMessageGetListLen();

			return DC_MESSAGE_GETLISTLEN;
		}
		else if ( GetContent(DC_S_MESSAGE_VALIDATEDENIDE,&t,sContent) )
		{
			*Object = new CMessageValidateDenide();

			return DC_MESSAGE_VALIDATEDENIDE;
		}
		else if ( GetContent(DC_S_MESSAGE_HUBISFULL,&t,sContent) )
		{
			*Object = new CMessageHubIsFull();

			return DC_MESSAGE_HUBISFULL;
		}
		else if ( GetContent(DC_S_MESSAGE_PING,&t,sContent) )
		{
			*Object = new CMessagePing();

			return DC_MESSAGE_PING;
		}
		else if ( GetContent(DC_S_MESSAGE_GETPASS,&t,sContent) )
		{
			*Object = new CMessageGetPass();

			return DC_MESSAGE_GETPASS;
		}
		else if ( GetContent(DC_S_MESSAGE_BADPASS,&t,sContent) )
		{
			*Object = new CMessageBadPass();

			return DC_MESSAGE_BADPASS;
		}
		else if ( GetContent(DC_S_MESSAGE_LOGEDIN,&t,sContent) )
		{
			*Object = ParseLogedIn(sContent);

			return DC_MESSAGE_LOGEDIN;
		}
		else if ( GetContent(DC_S_MESSAGE_HUB_TOPIC,&t,sContent) )
		{
			*Object = ParseHubTopic(sContent);

			return DC_MESSAGE_HUB_TOPIC;
		}
		else if ( GetContent(DC_S_MESSAGE_GET_NET_INFO,&t,sContent) )
		{
			*Object = new CMessageGetNetInfo();

			return DC_MESSAGE_GET_NET_INFO;
		}
		else if ( GetContent(DC_S_MESSAGE_USER_COMMAND,&t,sContent) )
		{
			*Object = ParseUserCommand(sContent);

			return DC_MESSAGE_USER_COMMAND;
		}
		else if ( GetContent(DC_S_MESSAGE_GETZBLOCK,&t,sContent) )
		{
			*Object = ParseGetZBlock(sContent);

			return DC_MESSAGE_GET;
		}
		else if ( GetContent(DC_S_MESSAGE_FAILED,&t,sContent) )
		{
			*Object = ParseError(sContent);

			return DC_MESSAGE_ERROR;
		}
		else if ( GetContent(DC_S_MESSAGE_USER_IP,&t,sContent) )
		{
			*Object = ParseUserIP(sContent);
			
			return DC_MESSAGE_USERIP;
		}
		else
		{
			printf("!!!!!!!!!!!!!! unknown start ");
			printf("index: %d %d!!!!!!!!!!!!!!\n",old_index,index);
			printf("%s\n",t.Data());
			printf("!!!!!!!!!!!!!! unknown end !!!!!!!!!!!!!!\n");
			return DC_MESSAGE_UNKNOWN;
		}
	}
	else
	{
//		printf("%s\n",sMessage.Data());
	}

	return DC_MESSAGE_PARSE_ERROR;
}

/** */
CDCMessage * CMessageHandler::ParseLock( const CString & sContent )
{
	CMessageLock * msg = new CMessageLock();

	int i,i1;

	i = sContent.Find(" Pk=");

	// lock without pk
	if ( i < 0 )
	{
		msg->m_sData = sContent;
	}
	else
	{
		msg->m_sData = sContent.Left(i);
		msg->m_sPK   = sContent.Mid(i+4,sContent.Length()-i-4);
	}

//	printf("TR: '%s' '%s'\n",msg->m_sData.Data(),msg->m_sPK.Data());

	if ( msg->m_sData.StartsWith("EXTENDEDPROTOCOL",16) )
	{
		msg->m_bExtProtocol = true;
	}

	// DC++ client
	if ( msg->m_sPK.StartsWith("DCPLUSPLUS",10) )
	{
		msg->m_eClientVersion = eucvDCPP;

		if ( (i = msg->m_sPK.Find("ABC")) != -1 )
		{
			msg->m_sVersionString = msg->m_sPK.Mid(10,i-10);
		}
	}
	// DC++H Hub
	else if ( msg->m_sPK.StartsWith("DCHUBPLUSPLUS",13) )
	{
		msg->m_eClientVersion = eucvDCHPP;

		if ( (i = msg->m_sPK.Find("ABC")) != -1 )
		{
			msg->m_sVersionString = msg->m_sPK.Mid(13,i-13);
		}
	}
	// PtokaX Hub
	else if ( msg->m_sPK.Left(6).ToUpper() == "PTOKAX" )
	{
		msg->m_eClientVersion = eucvPTOKAX;
	}
	// ZPoc Hub - removed typoed CHRISITAN,
	// apparently it could also be ZDCROOMSERVERAABC but i doubt anyone cares
	else if ( msg->m_sPK.StartsWith("ZPOC",4) )
	{
		msg->m_eClientVersion = eucvZPOC;
	}
	// opendcd Hub
	else if ( msg->m_sPK.StartsWith("opendcd",7) )
	{
		msg->m_eClientVersion = eucvOPENDCD;
	}
	// dclib based valknut
	else if ( msg->m_sPK.StartsWith("DCGUI",5) )
	{
		msg->m_eClientVersion = eucvDCGUI;
		
		if ( (i = msg->m_sPK.Find("ABC")) != -1 )
		{
			msg->m_sVersionString = msg->m_sPK.Mid(5,i-5);
		}
	}
	// microdc client
	else if ( msg->m_sPK.StartsWith("MICRODC",7) )
	{
		msg->m_eClientVersion = eucvMICRODC;
	}
	// shakespeer client
	else if ( msg->m_sPK.StartsWith("ShakesPeer",10) )
	{
		msg->m_eClientVersion = eucvSHAKESPEER;
		
		if ( (i = msg->m_sPK.Find("ABC")) != -1 )
		{
			msg->m_sVersionString = msg->m_sPK.Mid(10,i-10);
		}
	}

	if ( msg->m_sVersionString.NotEmpty() )
	{
		if ( (i=msg->m_sVersionString.Find('.')) != -1 )
		{
			msg->m_nVersionMajor = msg->m_sVersionString.Mid(0,i).asINT();
			i++;
			if ( (i1=msg->m_sVersionString.Find('.',i)) != -1 )
			{
				msg->m_nVersionMinor = msg->m_sVersionString.Mid(i,msg->m_sVersionString.Length()-i1).asINT();
				i=i1+1;
				msg->m_nVersionPatch = msg->m_sVersionString.Mid(i,msg->m_sVersionString.Length()-i).asINT();
			}
			else
			{
				msg->m_nVersionMinor = msg->m_sVersionString.Mid(i,msg->m_sVersionString.Length()-i).asINT();
			}
		}
	}

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseHello( const CString & sContent )
{
	CMessageHello * msg = new CMessageHello();

	msg->m_sNick = m_pRemoteToLocal->encode(sContent);

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseMyNick( const CString & sContent )
{
	CMessageMyNick * msg = new CMessageMyNick();

	msg->m_sNick = m_pRemoteToLocal->encode(sContent);

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseChat( const CString & sContent )
{
	CMessageChat * msg = new CMessageChat();

	int i=-1,i1=-1;

	if ( (i=sContent.Find('<')) == 0 )
		i1 = sContent.Find('>',i+1);

	if ( (i != -1) && (i1 != -1) )
	{
		msg->m_sNick    = m_pRemoteToLocal->encode(sContent.Mid(i+1,i1-1));
		// check if after the '>' a space
		if ( sContent.Data()[i1+1] == ' ' )
			i1++;

		msg->m_sMessage = m_pRemoteToLocal->encode(sContent.Mid(i1+1,sContent.Length()-i1-1));
	}
	else
	{
		// no < nick > found ... use complete message
		msg->m_sMessage = m_pRemoteToLocal->encode(sContent);
	}

	// reconvert dcproto chars
	msg->m_sMessage = msg->m_sMessage.Replace( "&#36;", "$" );
	msg->m_sMessage = msg->m_sMessage.Replace( "&#124;", "|" );

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseMyInfo( const CString & sContent )
{
	/* $MyINFO $ALL xyzzy desc<DCGUI V:0.3.19svn,M:A,H:1/0/0,S:2>$ $DSL$ejs1920@yahoo.co.uk$35036485563$| */
	/* $MyINFO $ALL strongdc_test desc<StrgDC++ V:2.21,M:A,H:1/0/0,S:2>$ $0.005$email$1123919289$| */
	CMessageMyInfo * msg=0;
	CString s;
	int i,i1,i2,i3,i4,i5,i6;

	// dest e.g. $ALL
	if ( (i=sContent.Find(' ')) < 0 )
		return 0;

	if ( (i1=sContent.Find(' ',i+1)) < 0 )
		return 0;

	if ( (i2=sContent.Find('$',i1+1)) < 0 )
		return 0;

	if ( (i3=sContent.Find('$',i2+1)) < 0 )
		return 0;

	if ( (i4=sContent.Find('$',i3+1)) < 0 )
		return 0;

	if ( (i5=sContent.Find('$',i4+1)) < 0 )
		return 0;

	if ( (i6=sContent.Find('$',i5+1)) < 0 )
		return 0;

	msg = new CMessageMyInfo();

	msg->m_sNick    = m_pRemoteToLocal->encode(sContent.Mid(i +1,i1-i -1));
	msg->m_sUnknown = m_pRemoteToLocal->encode(sContent.Mid(i2+1,i3-i2-1));

	// check user mode
	if ( (i4-i3-1) > 0 )
	{
		const char c = sContent.Data()[i4-1];
		
		if ( c & emsfNormal )
		{
			msg->m_eAwayMode = euamNORMAL;
		}
		else if ( c & emsfAway )
		{
			msg->m_eAwayMode = euamAWAY;
		}
		
		if ( c & emsfServer )
		{
			msg->m_bServerFlag = true;
		}
		
		if ( c & emsfFireball )
		{
			msg->m_bFireballFlag = true;
		}
		
		if ( c & emsfTLS )
		{
			msg->m_bTLSFlag = true;
		}
	}

	s = sContent.Mid(i3+1,i4-i3-2);

	if ( s == "28.8Kbps" )
		msg->m_eUserSpeed = eus288KBPS;
	else if ( s == "33.6Kbps" )
		msg->m_eUserSpeed = eus288KBPS;
	else if ( s == "56Kbps" )
		msg->m_eUserSpeed = eus56KBPS;
	else if ( s == "Modem" )
		msg->m_eUserSpeed = eusMODEM;
	else if ( s == "ISDN" )
		msg->m_eUserSpeed = eusISDN;
	else if ( s == "DSL" )
		msg->m_eUserSpeed = eusDSL;
	else if ( s == "Satellite" )
		msg->m_eUserSpeed = eusSATELLITE;
	else if ( s == "Cable" )
		msg->m_eUserSpeed = eusCABLE;
	else if ( s == "LAN(T1)" )
		msg->m_eUserSpeed = eusLANT1;
	else if ( s == "LAN(T3)" )
		msg->m_eUserSpeed = eusLANT3;
	else if ( s == "Wireless" )
		msg->m_eUserSpeed = eusWIRELESS;
	else if ( s == "Microwave" )
		msg->m_eUserSpeed = eusMICROWAVE;
	else if ( s == "0.005" )
		msg->m_eUserSpeed = eus0005;
	else if ( s == "0.01" )
		msg->m_eUserSpeed = eus001;
	else if ( s == "0.02" )
		msg->m_eUserSpeed = eus002;
	else if ( s == "0.05" )
		msg->m_eUserSpeed = eus005;
	else if ( s == "0.1" )
		msg->m_eUserSpeed = eus01;
	else if ( s == "0.2" )
		msg->m_eUserSpeed = eus02;
	else if ( s == "0.5" )
		msg->m_eUserSpeed = eus05;
	else if ( s == "1" )
		msg->m_eUserSpeed = eus1;
	else if ( s == "2" )
		msg->m_eUserSpeed = eus2;
	else if ( s == "5" )
		msg->m_eUserSpeed = eus5;
	else if ( s == "10" )
		msg->m_eUserSpeed = eus10;
	else if ( s == "20" )
		msg->m_eUserSpeed = eus20;
	else if ( s == "50" )
		msg->m_eUserSpeed = eus50;
	else if ( s == "100" )
		msg->m_eUserSpeed = eus100;
	else if ( s == "1000" )
		msg->m_eUserSpeed = eus1000;
	else
		msg->m_eUserSpeed = eusUNKNOWN;

	msg->m_sUserSpeed = s;
	msg->m_sEMail = m_pRemoteToLocal->encode(sContent.Mid(i4+1,i5-i4-1));

	s = sContent.Mid(i5+1,i6-i5-1);
	msg->m_nShared = s.asULL();

	// parse comment
	s = sContent.Mid(i1+1,i2-i1-1);

	// find version start tag
	if ( (i=s.FindRev("<++ ")) != -1 )
		msg->m_eClientVersion = eucvDCPP;
	else if ( (i=s.FindRev("<DCGUI ")) != -1 )
		msg->m_eClientVersion = eucvDCGUI;
	else if ( (i=s.FindRev("<DCTC ")) != -1 )
		msg->m_eClientVersion = eucvDCTC;
	else if ( (i=s.FindRev("<DC ")) != -1 )
		msg->m_eClientVersion = eucvNMDC;
	else if ( (i=s.FindRev("<QuickDC ")) != -1 )
		msg->m_eClientVersion = eucvQUICKDC;
	else if ( (i=s.FindRev("<oDC ")) != -1 )
		msg->m_eClientVersion = eucvOPERADC;
	else if ( (i=s.FindRev("<SdDC++ ")) != -1 )
		msg->m_eClientVersion = eucvSDDC;
	else if ( (i=s.FindRev("<StrgDC++ ")) != -1 )
		msg->m_eClientVersion = eucvSTRGDCPP;
	else if ( (i=s.FindRev("<RMDC++ ")) != -1 )
		msg->m_eClientVersion = eucvRMDC;
	else if ( (i=s.FindRev("<DC:PRO ")) != -1 )
		msg->m_eClientVersion = eucvDCPRO;
	else if ( (i=s.FindRev("<iDC")) != -1 )
		msg->m_eClientVersion = eucvIDC;
	else if ( (i=s.FindRev("<PerlDC")) != -1 )
		msg->m_eClientVersion = eucvPERLDC;
	else if ( (i=s.FindRev("<microdc")) != -1 )
		msg->m_eClientVersion = eucvMICRODC;
	else if ( (i=s.FindRev("<SP")) != -1 )
		msg->m_eClientVersion = eucvSHAKESPEER;
	else if ( (i=s.FindRev("<")) != -1 )
		msg->m_eClientVersion = eucvUNKNOWN;
	else
		msg->m_eClientVersion = eucvNONE;

	msg->m_eClientMode = ecmPASSIVE;

	// find version end tag
	if ( (i!=-1) && ((i1=s.Find('>',i)) != -1) )
	{
		msg->m_sComment    = m_pRemoteToLocal->encode(s.Left(i));
		msg->m_sVerComment = s.Mid(i,i1-i+1);

		// try to find some default tags for unknown clients
		if ( msg->m_eClientVersion == eucvUNKNOWN )
		{
			if ( (msg->m_sVerComment.Find("V:") == -1) ||
			     (msg->m_sVerComment.Find("M:") == -1) )
			{
				msg->m_eClientVersion = eucvNONE;
			}
		}
		
		if ( msg->m_eClientVersion != eucvNONE )
		{
			// search for the active tag
			// A: active, P: passive 5: socks5
			if ( msg->m_sVerComment.Find("M:A") != -1 )
			{
				msg->m_eClientMode = ecmACTIVE;
			}
		}
	}
	
	// reset values
	if ( msg->m_eClientVersion == eucvNONE )
	{
		msg->m_sComment    = m_pRemoteToLocal->encode(s);
		msg->m_sVerComment.Empty();
	}
	
	// set myinfo valid flag
	msg->m_bValid = true;

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseConnectToMe( const CString & sContent )
{
	CMessageConnectToMe * msg=0;
	CString s;

	int i,i1;

	if ( (i=sContent.Find(' ')) < 0 )
	{
		return 0;
	}

	if ( (i1=sContent.Find(':',i+1)) < 0 )
	{
		return 0;
	}

	msg = new CMessageConnectToMe();

	msg->m_sNick = m_pRemoteToLocal->encode(sContent.Mid(0,i));
	msg->m_sHost = sContent.Mid(i+1,i1-i-1);

	/* connect using TLS specified by 'S' appended to port */
	if ( sContent.Data()[sContent.Length()-1] == 'S' )
	{
		msg->m_bCrypto = true;
		s = sContent.Mid(i1+1,sContent.Length()-i1-2);
	}
	else
	{
		/* m_bCrypto set to false in CMessageConnectToMe constructor */
		s = sContent.Mid(i1+1,sContent.Length()-i1-1);
	}

	if ( s.IsEmpty() )
	{
		msg->m_nPort = 411;
	}
	else
	{
		msg->m_nPort = s.asINT();
		
		// fix negative ports
		if ( msg->m_nPort < 0 )
			msg->m_nPort += 65536;
	}

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseRevConnectToMe( const CString & sContent )
{
	CMessageRevConnectToMe * msg=0;

	int i;

	if ( (i=sContent.Find(' ')) < 0 )
	{
		return 0;
	}

	msg = new CMessageRevConnectToMe();

	msg->m_sDstNick = m_pRemoteToLocal->encode(sContent.Mid(0,i));
	msg->m_sNick    = m_pRemoteToLocal->encode(sContent.Mid(i+1,sContent.Length()-i-1));

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseNickList( const CString & sContent )
{
	CMessageNickList * msg = new CMessageNickList();
	int i = 0, i1 = 0;

	while ( (i=sContent.Find('$',i)) >= 0 )
	{
		msg->m_NickList.Add(new CString(m_pRemoteToLocal->encode(sContent.Mid(i1,i-i1))));

		i+=2;
		i1=i;
	}

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseOpList( const CString & sContent )
{
	CMessageOpList * msg = new CMessageOpList();
	int i = 0, i1 = 0;

	while ( (i=sContent.Find('$',i)) >= 0 )
	{
		msg->m_NickList.Add(new CString(m_pRemoteToLocal->encode(sContent.Mid(i1,i-i1))));

		i+=2;
		i1=i;
	}

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseQuit( const CString & sContent )
{
	CMessageQuit * msg = new CMessageQuit();

	msg->m_sNick = m_pRemoteToLocal->encode(sContent);

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseSearch( const CString & sContent )
{
	CMessageSearchFile * msg=0;

	CString s,s1;
	int t,i,i1,i2,i3,i4,i5,i6;

//	printf("Entering PARSESEARCH\n");
	if ( (i=sContent.Find(' ')) < 0 )
		return 0;

	if ( (i1=sContent.Find('?',i+1)) < 0 )
		return 0;

	if ( (i2=sContent.Find('?',i1+1)) < 0 )
		return 0;

	if ( (i3=sContent.Find('?',i2+1)) < 0 )
		return 0;

	if ( (i4=sContent.Find('?',i3+1)) < 0 )
		return 0;

	s = sContent.Left(i);

	if ( (i5 = s.Find(':')) < 0 )
	{
		return 0;
	}

	msg = new CMessageSearchFile();

	s1 = s.Left(i5+1);

	if ( s1 == "Hub:" )
	{
		msg->m_bLocal  = true;
		msg->m_sSource = m_pRemoteToLocal->encode(s.Mid(i5+1,s.Length()-i5-1));
	}
	else
	{
		// parse the host string
		msg->m_bLocal  = false;

		i6 = s.Find(':');

		if ( i6 > 0 )
		{
			s1 = s.Mid(i6+1,s.Length()-i6-1);
		}
		else
		{
			s1.Empty();
		}

		if ( (i6 < 0) || (s1.IsEmpty()) )
		{
			msg->m_sSource = s;
			msg->m_nPort   = 411;
		}
		else
		{
			msg->m_sSource = s.Mid(0,i6);
			msg->m_nPort   = s1.asINT();
			
			// fix negative ports
			if ( msg->m_nPort < 0 )
				msg->m_nPort += 65536;
		}
	}

	// sizelimit
	if ( sContent.Mid(i+1,i1-i-1) == "F" )
		msg->m_bSizeLimit = false;
	else
		msg->m_bSizeLimit = true;

	// at most or at least
	if ( sContent.Mid(i1+1,i2-i1-1) == "F" )
		msg->m_eSizeType = esstATLEAST;
	else
		msg->m_eSizeType = esstATMOST;

	// size
	s1 = sContent.Mid(i2+1,i3-i2-1);
	msg->m_nSize = s1.asULL();

	// type
	s1 = sContent.Mid(i3+1,i4-i3-1);
	t = s1.asINT();

	switch(t)
	{
		case 1:
			msg->m_eFileType = eftALL;
			break;
		case 2:
			msg->m_eFileType = eftMP3;
			break;
		case 3:
			msg->m_eFileType = eftARCHIVE;
			break;
		case 4:
			msg->m_eFileType = eftDOCUMENT;
			break;
		case 5:
			msg->m_eFileType = eftAPPLICATION;
			break;
		case 6:
			msg->m_eFileType = eftPICTURE;
			break;
		case 7:
			msg->m_eFileType = eftVIDEO;
			break;
		case 8:
			msg->m_eFileType = eftFOLDER;
			break;
		case 9:
			msg->m_eFileType = eftHASH;
			break;
		default:
			msg->m_eFileType = eftUNKNOWN;
			break;
	}

	// the search string
	s = m_pRemoteToLocal->encode(sContent.Mid(i4+1,sContent.Length()-i4-1));
	
	if ( msg->m_eFileType == eftHASH )
	{
		s = s.Mid(4);
	}

	// check if ext proto
	/*
		STRING: EXT<BASE64>
		BASE64: XYEXT:<PROTO>
		X     : PADDING TO EVEN LENGTH
		Y     : UNSIGNED CHAR <> 0
	*/
	msg->m_bExtended = false;

	if ( (i=s.Find("EXT")) == 0 )
	{
		CByteArray in,out;

		s1 = s.Mid(i+3,s.Length()-i-3);

		in.SetSize(0);
		in.Append( (unsigned char*)s1.Data(), s1.Length()+1 );
		i = CBase64::Decode(&out,&in);
		if ( i > 5 )
		{
			s1.Set( (char*)out.Data(), out.Size() );
			i=s1.Find("EXT:");
			if ( (i == 1) || (i==2) )
			{
				printf("Found ext decode: '%s'\n",s1.Data());
				s = s1.Mid(i+4,s1.Length()-i-4);
				msg->m_bExtended = true;
			}
		}
	}

	s = s.Replace('$'," ");
	s = s.Replace("&#36;","$");
	msg->m_sString = s.Replace("&#124;","|");

//	printf("Quitting PARSESEARCH\n");

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseSearchResult( const CString & sContent )
{
	int i;
	
	if ( (i=sContent.Find(0x05)) < 0 )
		return 0;
	
	if ( sContent.Find(0x05,i+1) < 0 )
	{
		return ParseSearchResultFolder(sContent);
	}
	else
	{
		return ParseSearchResultFile(sContent);
	}
}

/** 
 * $SR <source_nick> <file_name><0x05><file_size> <free_slots>/<total slots><0x05><hub_name> (<hub_ip:port>)|
 *                  i           i1               i2                         i3
 */
CDCMessage * CMessageHandler::ParseSearchResultFile( const CString & sContent )
{
	CMessageSearchResult * msg=0;
	CString s;
	int i,i1,i2,i3;

/*	for(i=0;i<sContent.Length();i++)
		printf("%02X ",sContent.Data()[i]);
	printf("\n");*/

	if ( (i=sContent.Find(' ')) < 0 )
		return 0;

	if ( (i1=sContent.Find(0x05,i+1)) < 0 )
		return 0;

	if ( (i2=sContent.Find(' ',i1+1)) < 0 )
		return 0;

	if ( (i3=sContent.Find(0x05,i2+1)) < 0 )
		return 0;

	msg = new CMessageSearchResult();

	msg->m_sNick = m_pRemoteToLocal->encode(sContent.Mid(0,i));
	msg->m_sFile = m_pRemoteToLocal->encode(sContent.Mid(i+1,i1-i-1));
	s = sContent.Mid(i1+1,i2-i1-1);
	msg->m_nSize = s.asULL();

	// parse the user slots
	s = sContent.Mid(i2+1,i3-i2-1);
	msg->m_nFreeSlot = 0;
	msg->m_nMaxSlot  = 0;

	if ( (i1 = s.Find('/')) != -1 )
	{
		msg->m_nFreeSlot = s.Mid(0,i1).asUINT();
		msg->m_nMaxSlot  = s.Mid(i1+1,s.Length()-i1-1).asUINT();
	}

	s = sContent.Mid(i3+1,sContent.Length()-i3-1);

	if ( s.NotEmpty() )
	{
		if ( (i1 = s.FindRev(')')) != -1 )
		{
			if ( (i2 = s.FindRev('('),i1-1) != -1 )
			{
				if ( i2 > 0 ) i2--; // remove space " ("
				msg->m_sHubName = m_pRemoteToLocal->encode(s.Mid(0,i2));
				msg->m_sHubHost = s.Mid(i2+2,i1-i2-2);
			}
		}
	}

	// check negative ports
	CString ip;
	unsigned int port;
	
	CNetAddr::ParseHost( msg->m_sHubHost, ip, port );
	
	msg->m_sHubHost = ip;
	
	if ( port != 0 )
	{
		 msg->m_sHubHost += ':';
		 msg->m_sHubHost += CString::number(port);
	}

	// parse special content in hubname
	if ( msg->m_sHubName.StartsWith("TTH:",4) )
	{
		// tiger tree hash
		msg->m_sHash = msg->m_sHubName.Mid(4);
		// set dummy hubname
		msg->m_sHubName = msg->m_sHubHost;
	}

/*	printf("%s\n%s | %s | %s | %s | %s | %s\n",\
		sContent.Data(),
		msg->sNick.Data(),
		msg->sFile.Data(),
		msg->sSize.Data(),
		msg->sSlot.Data(),
		msg->sHubName.Data(),
		msg->sHubHost.Data() );*/
	
	msg->m_bFolder = false;

	return msg;
}

/**
 * $SR <source_nick> <directory_name> <free_slots>/<total_slots><0x05><hub_name> (<hub_ip:port>)|
 *                  s1               s2                         i1              
 */
CDCMessage * CMessageHandler::ParseSearchResultFolder( const CString & sContent )
{
	CMessageSearchResult * msg=0;
	CString s;
	int s1,s2,i1,slash,openBracket,closeBracket;

/*	for(i=0;i<sContent.Length();i++)
		printf("%02X ",sContent.Data()[i]);
	printf("\n");*/

	if ( (s1=sContent.Find(' ')) < 0 )
		return 0;

	if ( (i1=sContent.Find(0x05,s1+1)) < 0 )
		return 0;

	if ( (s2=sContent.FindRev(' ',i1)) < 0 )
		return 0;

	msg = new CMessageSearchResult();

	msg->m_sNick = m_pRemoteToLocal->encode(sContent.Mid(0,s1));
	msg->m_sFile = m_pRemoteToLocal->encode(sContent.Mid(s1+1,s2-s1-1));
	msg->m_nSize = 0;

	// parse the user slots
	s = sContent.Mid(s2+1,i1-s2-1);
	msg->m_nFreeSlot = 0;
	msg->m_nMaxSlot  = 0;

	if ( (slash = s.Find('/')) != -1 )
	{
		msg->m_nFreeSlot = s.Mid(0,slash).asUINT();
		msg->m_nMaxSlot  = s.Mid(slash+1,s.Length()-slash-1).asUINT();
	}

	s = sContent.Mid(i1+1,sContent.Length()-i1-1);

	if ( s.NotEmpty() )
	{
		if ( (closeBracket = s.FindRev(')')) != -1 )
		{
			if ( (openBracket = s.FindRev('('),closeBracket-1) != -1 )
			{
				if ( openBracket > 0 ) openBracket--; // remove space " ("
				msg->m_sHubName = m_pRemoteToLocal->encode(s.Mid(0,openBracket));
				msg->m_sHubHost = s.Mid(openBracket+2,closeBracket-openBracket-2);
			}
		}
	}

	// check negative ports
	CString ip;
	unsigned int port;
	
	CNetAddr::ParseHost( msg->m_sHubHost, ip, port );
	
	msg->m_sHubHost = ip;
	
	if ( port != 0 )
	{
		 msg->m_sHubHost += ':';
		 msg->m_sHubHost += CString::number(port);
	}

	// parse special content in hubname
	if ( msg->m_sHubName.StartsWith("TTH:",4) )
	{
		// tiger tree hash
		msg->m_sHash = msg->m_sHubName.Mid(4);
		// set dummy hubname
		msg->m_sHubName = msg->m_sHubHost;
	}

	/* printf("%s\n%s : %s : %l : %s : %s : %s : %l : %l\n",\
		sContent.Data(),
		msg->m_sNick.Data(),
		msg->m_sFile.Data(),
		msg->m_nSize,
		msg->m_sHubName.Data(),
		msg->m_sHubHost.Data(),
		msg->m_sHash.Data(),
		msg->m_nFreeSlot,
		msg->m_nMaxSlot ); */
	
	msg->m_bFolder = true;

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParsePrivateChat( const CString & sContent )
{
	/* $To: target nick From: source nick $ <multi src nick> message text| */
	/* "$To: " and "|" are already removed leaving */
	/* target nick From: source nick $ <multi src nick> message text */
	
	int p1 = sContent.Find( " From:", 0 );
	
	if ( p1 == -1 )
	{
		return 0;
	}
	
	int p2 = sContent.Find( '$', p1+6 );
	
	if ( p2 == -1 )
	{
		return 0;
	}

	CMessagePrivateChat * msg = new CMessagePrivateChat();

	msg->m_sDstNick = m_pRemoteToLocal->encode( sContent.Mid(0,p1) );
	msg->m_sSrcNick = m_pRemoteToLocal->encode( sContent.Mid(p1+7,p2-p1-8) );

	CString s = sContent.Mid(p2+1,sContent.Length()-p2-1);

	p1 = s.Find('<');
	p2 = s.Find('>',p1+1);

	if ( (p1==-1) || (p2==-1) )
	{
		msg->m_sMessage = m_pRemoteToLocal->encode(s);
	}
	else
	{
		msg->m_sMultiSrcNick = m_pRemoteToLocal->encode(s.Mid(p1+1,p2-p1-1));
		
		if ( (s.Length() > p2+1) && (s.Data()[p2+1] == ' ') )
		{
			++p2;
		}
		
		msg->m_sMessage      = m_pRemoteToLocal->encode(s.Mid(p2+1,s.Length()-p2-1));
	}

	// reconvert dcproto chars
	msg->m_sMessage = msg->m_sMessage.Replace( "&#36;", "$" );
	msg->m_sMessage = msg->m_sMessage.Replace( "&#124;", "|" );

/*
	printf("%s\n%s\n%s\n%s\n",\
		sContent.Data(),
		msg->sNick.Data(),
		msg->sFrom.Data(),
		msg->sString.Data() );
*/

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseForceMove( const CString & sContent )
{
	if ( sContent.IsEmpty() )
	{
		return 0;
	}

	int i = sContent.Find(':');
	CString s;

	CMessageForceMove * msg = new CMessageForceMove();

	if ( i > 0 )
	{
		s = sContent.Mid(i+1,sContent.Length()-i-1);
	}

	if ( (i < 0) || (s.IsEmpty()) )
	{
		msg->m_sHost = sContent;
		msg->m_nPort   = 411;
	}
	else
	{
		msg->m_sHost = sContent.Mid(0,i);
		msg->m_nPort = s.asINT();
		
		// fix negative ports
		if ( msg->m_nPort < 0 )
			msg->m_nPort += 65536;
	}

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseDirection( const CString & sContent )
{
	int i;

	if ( (i=sContent.Find(' ')) < 0 )
		return 0;

	CMessageDirection * msg = new CMessageDirection();

	CString s = sContent.Mid(0,i);

	if ( s == "Upload" )
		msg->m_eDirection = edUPLOAD;
	else if ( s == "Download" )
		msg->m_eDirection = edDOWNLOAD;
	else
		msg->m_eDirection = edNONE;

	s = sContent.Mid(i+1,sContent.Length()-i-1);
	if ( s.IsEmpty() )
		msg->m_nLevel = 0;
	else
		msg->m_nLevel = s.asINT();

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseFileLength( const CString & sContent )
{
	CMessageFileLength * msg = new CMessageFileLength();

	if ( sContent.IsEmpty() )
		msg->m_nFileLength = 0;
	else
		msg->m_nFileLength = sContent.asULL();

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseGet( const CString & sContent )
{
	int last,secondlast;
	CString pos, size;
	CMessageGet * msg = 0;
	
	// last $ will usually be 1-starting based file position
	// but will be size if using valknut CHUNK extension
	last = sContent.FindRev('$');
	
	if ( last < 0 )
	{
		return 0;
	}
	
	msg = new CMessageGet();
	
	// second last $ may be in the filename
	// but will be file position if using valknut CHUNK extension
	secondlast = sContent.FindRev('$',last-1);
	
	if ( secondlast < 0 )
	{
		pos = sContent.Mid(last+1);
		secondlast = last;
	}
	else
	{
		size = sContent.Mid(last+1);
		pos = sContent.Mid(secondlast+1,last-(secondlast+1));
		
		if ( pos.asULL() == 0 ) // $Get starts at 1
		{
			pos = size;
			size.Empty();
			secondlast = last;
		}
	}
	
	msg->m_sFilename = m_pRemoteToLocal->encode(sContent.Mid(0,secondlast));
	
	msg->m_nPos = pos.asULL();
	
	if ( size.NotEmpty() )
	{
		msg->m_nSize = size.asULL();
	}
	
	//printf( "name=%s pos=%llu size=%llu\n", msg->m_sFilename.Data(), msg->m_nPos, msg->m_nSize );
	
	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseUGetBlock( const CString & sContent )
{
	/* This parser is copied and pasted into ParseGetZBlock */
	int i,i1;
	CString s;
	CMessageGet * msg = new CMessageGet();

	i = sContent.Find(' ');
	i1= sContent.Find(' ',i+1);
	msg->m_nPos  = sContent.Mid(0,i).asULL();
	msg->m_nSize = sContent.Mid(i+1,i1-i).asULL();
	s = sContent.Mid(i1+1,sContent.Length()-i1-1);
	
	/* encode the filename from UTF-8 to local encoding */
	msg->m_sFilename = m_pUTF8ToLocal->encode(s);
	msg->m_bUGet = true;
	
	// fix start position
	msg->m_nPos++;
/*
	printf("UGET '%s'\n%llu %llu\n'%s'\n",
		sContent.Data(),
		msg->m_nPos,
		msg->m_nSize,
		msg->m_sFilename.Data());
*/

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseUGetZBlock( const CString & sContent )
{
	CMessageGet * msg = (CMessageGet*) ParseUGetBlock(sContent);
	
	msg->m_bZLib = true;
	
	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseError( const CString & sContent )
{
	CMessageError * msg = new CMessageError();

	msg->m_sError = m_pRemoteToLocal->encode(sContent);

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseHubName( const CString & sContent )
{
	CMessageHubName * msg = new CMessageHubName();

	msg->m_sHubName = m_pRemoteToLocal->encode(sContent);

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseGetInfo( const CString & sContent )
{
	int i;

	if ( (i=sContent.Find(' ')) < 0 )
	{
		return 0;
	}

	CMessageGetInfo * msg = new CMessageGetInfo();

	msg->m_sDstNick = m_pRemoteToLocal->encode(sContent.Mid(0,i));
	msg->m_sSrcNick = m_pRemoteToLocal->encode(sContent.Mid(i+1,sContent.Length()-i-1));

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseLogedIn( CString sContent )
{
	CMessageLogedIn * msg = new CMessageLogedIn();

	// remove the space
	if ( sContent.NotEmpty() && (sContent.Data()[0] == ' ') )
	{
		sContent = sContent.Mid(1,sContent.Length()-1);
	}

	msg->m_sNick = m_pRemoteToLocal->encode(sContent);

	return msg;
}

#define DC_SUPPORT_TAG_MINISLOTS	"MiniSlots "
#define DC_SUPPORT_TAG_XMLBZLIST	"XmlBZList "
#define DC_SUPPORT_TAG_ADCGET		"ADCGet "
#define DC_SUPPORT_TAG_TTHL		"TTHL "
#define DC_SUPPORT_TAG_TTHF		"TTHF "
#define DC_SUPPORT_TAG_ZLIG		"ZLIG "

/* These are obsolete and should not be used. */
#define DC_SUPPORT_TAG_GETZBLOCK	"GetZBlock "
#define DC_SUPPORT_TAG_BZLIST		"BZList "

/* These are obsolete dclib extensions and should not be used. */
#define DC_SUPPORT_TAG_SSL		"SSL "
#define DC_SUPPORT_TAG_CHUNK		"CHUNK "

/* These are all ignored - either unused client extensions or hub extensions. */
#define DC_SUPPORT_TAG_GETTESTZBLOCK	"GetTestZBlock "
#define DC_SUPPORT_TAG_USER_CMD		"UserCommand "
#define DC_SUPPORT_TAG_NOGETINFO	"NoGetINFO "
#define DC_SUPPORT_TAG_USERIP		"UserIP "
#define DC_SUPPORT_TAG_USERIP2		"UserIP2 "
#define DC_SUPPORT_TAG_XRULES		"XRules "
#define DC_SUPPORT_TAG_BOTINFO		"BotINFO "
#define DC_SUPPORT_TAG_BOTINFO_2	"BotInfo "
#define DC_SUPPORT_TAG_NOHELLO		"NoHello "
#define DC_SUPPORT_TAG_OPPLUS		"OpPlus "
#define DC_SUPPORT_TAG_MCTO		"MCTo "
#define DC_SUPPORT_TAG_BOTLIST		"BotList "
#define DC_SUPPORT_TAG_CLIENTID		"ClientID "
#define DC_SUPPORT_TAG_SECUREDEXECUTOR	"SecuredExecutor "
#define DC_SUPPORT_TAG_FEED		"Feed "
#define DC_SUPPORT_TAG_ZLINE		"ZLine "
#define DC_SUPPORT_TAG_ZPIPE0		"ZPipe0 "
#define DC_SUPPORT_TAG_CDM		"CDM "
#define DC_SUPPORT_TAG_GETCID		"GetCID "
#define DC_SUPPORT_TAG_BANMSG		"BanMsg "

/** */
CDCMessage * CMessageHandler::ParseSupports( CString sContent )
{
	int i = 0, i1 = 0;
	CString s;

	if ( sContent.Right(1) != " " )
	{
		sContent += ' ';
	}

	CMessageSupports * msg = new CMessageSupports();

	msg->m_sContent = sContent;

	while( (i1=sContent.Find(' ',i)) != -1 )
	{
		++i1;

		s = sContent.Mid(i,i1-i);

		if ( s == DC_SUPPORT_TAG_MINISLOTS )
		{
			msg->m_bMiniSlots = true;
		}
		else if ( s == DC_SUPPORT_TAG_XMLBZLIST )
		{
			msg->m_bXMLBZList = true;
		}
		else if ( s == DC_SUPPORT_TAG_ADCGET )
		{
			msg->m_bADCGet = true;
		}
		else if ( s == DC_SUPPORT_TAG_TTHL )
		{
			msg->m_bTTHL = true;
		}
		else if ( s == DC_SUPPORT_TAG_TTHF )
		{
			msg->m_bTTHF = true;
		}
		else if ( s == DC_SUPPORT_TAG_ZLIG )
		{
			msg->m_bZLIG = true;
		}
		else if ( s == DC_SUPPORT_TAG_GETZBLOCK ) /* obsolete */
		{
			msg->m_bZBlock = true;
		}
		else if ( s == DC_SUPPORT_TAG_BZLIST ) /* obsolete */
		{
			msg->m_bBZList = true;
		}
		else if ( s == DC_SUPPORT_TAG_SSL ) /* obsolete dclib specific */
		{
			msg->m_bSSL = true;
		}
		else if ( s == DC_SUPPORT_TAG_CHUNK ) /* obsolete dclib specific */
		{
			msg->m_bChunk = true;
		}
#if 0
		else if ( s == DC_SUPPORT_TAG_GETTESTZBLOCK )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_USER_CMD )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_NOGETINFO )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_USERIP )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_USERIP2 )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_XRULES )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_BOTINFO )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_BOTINFO_2 )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_NOHELLO )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_OPPLUS )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_MCTO )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_BOTLIST )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_CLIENTID )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_SECUREDEXECUTOR )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_FEED )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_ZLINE )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_ZPIPE0 )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_CDM )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_GETCID )
		{
			// ignore
		}
		else if ( s == DC_SUPPORT_TAG_BANMSG )
		{
			// ignore
		}
		else
		{
			printf("Unknown support tag: '%s'\n",s.Data());
		}

#endif
		i = i1;
	}

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseHubTopic( const CString & sContent )
{
	CMessageHubTopic * msg = new CMessageHubTopic();

	msg->m_sTopic = m_pRemoteToLocal->encode(sContent);

	return msg;
}

/** $UserCommand 1 2 Menu\SubMenu\SayHello $<%[mynick]> hello&#124;| */
CDCMessage * CMessageHandler::ParseUserCommand( const CString & sContent )
{
	int type = -1;
	int context = -1;
	CString name;
	CString command;
	
	int i = sContent.Find(' ');
	if ( i > 0 )
	{
		type = sContent.Mid(0,i).asINT();
		int i2 = sContent.Find(' ',i+1);
		if ( i2 > 0 )
		{
			context = sContent.Mid(i+1,i2-i-1).asINT();
			int i3 = sContent.Find('$',i2+1);
			if ( i3 > 0 )
			{
				name = sContent.Mid(i2+1,i3-i2-1);
				command = sContent.Mid(i3+1);
			}
			else
			{
				name = sContent.Mid(i2+1);
			}
		}
		else
		{
			context = sContent.Mid(i+1).asINT();
		}
	}
	else
	{
		type = sContent.asINT();
	}
	
	if ( name.Right(1) == " " )
	{
		name = name.Left(name.Length()-1);
	}
	
	name = m_pRemoteToLocal->encode(name);
	command = m_pRemoteToLocal->encode(command);
	
	command = command.Replace( "&#36;", "$" );
	command = command.Replace( "&#124;", "|" );
	
	// printf("UserCommand type=%d context=%d name='%s' command='%s'\n",type,context,name.Data(),command.Data());
	
	CMessageUserCommand * msg = new CMessageUserCommand();
	msg->type = type;
	msg->context = context;
	msg->name = name;
	msg->command = command;
	
	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseGetZBlock( const CString & sContent )
{
	/* This parser is copied and pasted from ParseUGetBlock */
	int i,i1;
	CString s;
	CMessageGet * msg = new CMessageGet();

	i = sContent.Find(' ');
	i1= sContent.Find(' ',i+1);
	msg->m_nPos  = sContent.Mid(0,i).asULL();
	msg->m_nSize = sContent.Mid(i+1,i1-i).asULL();
	s = sContent.Mid(i1+1,sContent.Length()-i1-1);
	
	/* encode the filename from remote to local encoding */
	msg->m_sFilename = m_pRemoteToLocal->encode(s);
	msg->m_bZLib = true;
	
	// fix start position
	msg->m_nPos++;
/*
	printf("GETZBLOCK '%s'\n%llu %llu\n'%s'\n",
		sContent.Data(),
		msg->m_nPos,
		msg->m_nSize,
		msg->m_sFilename.Data());
*/

	return msg;
}

/** */
CDCMessage * CMessageHandler::ParseSending( const CString & sContent )
{
	CMessageSending * msg = new CMessageSending();

	if ( sContent.IsEmpty() )
		msg->m_nLength = 0;
	else
		msg->m_nLength = sContent.asULL();

	return msg;
}

/** $ADCGET file TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA 0 1154 ZL1| */
CDCMessage * CMessageHandler::ParseADCGet( const CString & sContent )
{
	//printf("sContent=\"%s\"\n", sContent.Data());
	
	CMessageADCGet * msg = new CMessageADCGet();

	int i = sContent.Find(' ');
	CString s = sContent.Mid(0,i);
	if ( s == "file" )
	{
		msg->m_eADCType = eAdcFile;
	}
	else if ( s == "tthl" )
	{
		msg->m_eADCType = eAdcTTHL;
	}
	else if ( s == "list" )
	{
		msg->m_eADCType = eAdcList;
	}
	else
	{
		delete msg;
		printf("CMessageHandler::ParseADCGet: Unknown ADCGET type '%s'\n",s.Data());
		return 0;
	}
	
	s = sContent.Mid(i+1,sContent.Length()-i-1);
	
	if (s.Right(4).ToUpper() == " ZL1")
	{
		msg->m_bZlib = true;
		s = s.Mid(0, s.Length() - 4);
	}
	else
	{
		msg->m_bZlib = false;
	}
	
	i = s.FindRev(' ');
	msg->m_nSize = s.Mid(i, s.Length() - i).asLONGLONG();
	s = s.Mid(0, i);
	
	i = s.FindRev(' ');
	msg->m_nPos = s.Mid(i, s.Length() - i).asULL();
	s = s.Mid(0, i);
	
	if ( s.StartsWith("TTH/",4) )
	{
		msg->m_sTTH = s.Mid(4);
	}
	else
	{
		// unescape spaces
		s = s.Replace( "\\ ", " " );
		msg->m_sFile = m_pUTF8ToLocal->encode(s);
	}
	
	//printf("ADCGET Type=%s TTH=%s Pos=%lld Size=%lld Zlib=%d\n File=%s\n", msg->m_sType.Data(),
	//	msg->m_sTTH.Data(), msg->m_nPos, msg->m_nSize, msg->m_bZlib, msg->m_sFile.Data() );
	
	return msg;
}

/** $ADCSND file TTH/PPUROLR2WSYTGPLCM3KV4V6LJC36SCTFQJFDJKA 0 1154 ZL1| */
CDCMessage * CMessageHandler::ParseADCSnd( const CString & sContent )
{
	//printf("sContent=\"%s\"\n", sContent.Data());
	
	CMessageADCSnd * msg = new CMessageADCSnd();
	
	int i = sContent.Find(' ');
	CString s = sContent.Mid(0,i);
	if ( s == "file" )
	{
		msg->m_eADCType = eAdcFile;
	}
	else if ( s == "tthl" )
	{
		msg->m_eADCType = eAdcTTHL;
	}
	else if ( s == "list" )
	{
		msg->m_eADCType = eAdcList;
	}
	else
	{
		delete msg;
		printf("CMessageHandler::ParseADCGet: Unknown ADCSND type '%s'\n",s.Data());
		return 0;
	}
	
	s = sContent.Mid(i+1,sContent.Length()-i-1);
	
	if (s.Right(4).ToUpper() == " ZL1")
	{
		msg->m_bZlib = true;
		s = s.Mid(0, s.Length() - 4);
	}
	else
	{
		msg->m_bZlib = false;
	}
	
	i = s.FindRev(' ');
	msg->m_nSize = s.Mid(i, s.Length() - i).asLONGLONG();
	s = s.Mid(0, i);
	
	i = s.FindRev(' ');
	msg->m_nPos = s.Mid(i, s.Length() - i).asULL();
	s = s.Mid(0, i);
	
	if ( s.StartsWith("TTH/",4) )
	{
		msg->m_sTTH = s.Mid(4);
	}
	else
	{
		// unescape spaces
		s = s.Replace( "\\ ", " " );
		msg->m_sFile = m_pUTF8ToLocal->encode(s);
	}
	
	//printf("ADCSND Type=%s TTH=%s Pos=%lld Size=%lld Zlib=%d File=%d\n", msg->m_sType.Data(),
	//	msg->m_sTTH.Data(), msg->m_nPos, msg->m_nSize, msg->m_bZlib, msg->m_sFile.Data() );
	
	return msg;
}

/**
 * $UserIP nick ip|
 * $UserIP nick1 ip1$$nick2 ip2$$|
 */
CDCMessage * CMessageHandler::ParseUserIP( CString sContent )
{
	int middle = sContent.Find(' ');
	if ( middle < 0 )
	{
		return 0;
	}
	
	CMessageUserIP * msg = new CMessageUserIP();
	
	if ( sContent.Right(2) != "$$" )
	{
		sContent += "$$";
	}
	
	int list_sep = sContent.Find("$$");
	int start = 0;
	while ( (list_sep != -1) && (middle != -1) )
	{
		msg->m_lNicks.push_back( m_pRemoteToLocal->encode(sContent.Mid(start,middle-start)) );
		++middle;
		msg->m_lIPs.push_back( sContent.Mid( middle, list_sep - middle ) );
		
		start = list_sep + 2;
		list_sep = sContent.Find("$$",start);
		middle = sContent.Find(' ',start);
	}
	
	return msg;
}
