/*
IRC plugin for Miranda IM

Copyright (C) 2003-05 Jurgen Persson
Copyright (C) 2007-09 George Hazan

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.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

// This file holds functions that are called upon receiving
// certain commands from the server.

#include "irc.h"

using namespace irc;

VOID CALLBACK IdentTimerProc( HWND, UINT, UINT_PTR idEvent, DWORD )
{
	CIrcProto *ppro = GetTimerOwner( idEvent );
	if (ppro == NULL)
		return;

	ppro->KillChatTimer( ppro->IdentTimer );
	if ( ppro->m_iStatus == ID_STATUS_OFFLINE || ppro->m_iStatus == ID_STATUS_CONNECTING )
		return;

	if ( ppro->IsConnected() && ppro->m_identTimer )
		ppro->KillIdent();
}

VOID CALLBACK TimerProc( HWND, UINT, UINT_PTR idEvent, DWORD )
{
	CIrcProto *ppro = GetTimerOwner( idEvent );
	if (ppro == NULL)
		return;

	ppro->KillChatTimer( ppro->InitTimer );
	if ( ppro->m_iStatus == ID_STATUS_OFFLINE || ppro->m_iStatus == ID_STATUS_CONNECTING )
		return;

	if ( ppro->m_forceVisible )
		ppro->PostIrcMessage( _T("/MODE %s -i"), ppro->m_info.sNick.c_str());

	if ( lstrlenA( ppro->m_myHost ) == 0 && ppro->IsConnected())
		ppro->DoUserhostWithReason(2, (_T("S") + ppro->m_info.sNick).c_str(), true, _T("%s"), ppro->m_info.sNick.c_str());
}

VOID CALLBACK KeepAliveTimerProc( HWND, UINT, UINT_PTR idEvent, DWORD )
{
	CIrcProto *ppro = GetTimerOwner( idEvent );
	if (ppro == NULL)
		return;

	if ( !ppro->m_sendKeepAlive || ( ppro->m_iStatus == ID_STATUS_OFFLINE || ppro->m_iStatus == ID_STATUS_CONNECTING )) {
		ppro->KillChatTimer( ppro->KeepAliveTimer );
		return;
	}

	TCHAR temp2[270];
	if ( !ppro->m_info.sServerName.IsEmpty())
		mir_sntprintf(temp2, SIZEOF(temp2), _T("PING %s"), ppro->m_info.sServerName.c_str());
	else
		mir_sntprintf(temp2, SIZEOF(temp2), _T("PING %u"), time(0));

	if ( ppro->IsConnected())
		ppro->SendIrcMessage( temp2, false );
}

VOID CALLBACK OnlineNotifTimerProc3( HWND, UINT, UINT_PTR idEvent, DWORD )
{
	CIrcProto *ppro = GetTimerOwner( idEvent );
	if (ppro == NULL)
		return;

	if ( !ppro->m_channelAwayNotification || 
		  ppro->m_iStatus == ID_STATUS_OFFLINE || ppro->m_iStatus == ID_STATUS_CONNECTING || 
		  ( !ppro->m_autoOnlineNotification && !ppro->bTempForceCheck) || ppro->bTempDisableCheck ) {
		ppro->KillChatTimer( ppro->OnlineNotifTimer3 );
		ppro->m_channelsToWho = _T("");
		return;
	}

	CMString name = GetWord( ppro->m_channelsToWho.c_str(), 0 );
	if ( name.IsEmpty()) {
		ppro->m_channelsToWho = _T("");
		int count = (int)CallServiceSync(MS_GC_GETSESSIONCOUNT, 0, (LPARAM)ppro->m_szModuleName);
		for ( int i = 0; i < count; i++ ) {
			GC_INFO gci = {0};
			gci.Flags = BYINDEX | NAME | TYPE | COUNT;
			gci.iItem = i;
			gci.pszModule = ppro->m_szModuleName;
			if ( !CallServiceSync( MS_GC_GETINFO, 0, (LPARAM)&gci ) && gci.iType == GCW_CHATROOM )
				if ( gci.iCount <= ppro->m_onlineNotificationLimit )
					ppro->m_channelsToWho += CMString(gci.pszName) + _T(" ");
	}	}

	if ( ppro->m_channelsToWho.IsEmpty()) {
		ppro->SetChatTimer( ppro->OnlineNotifTimer3, 60*1000, OnlineNotifTimerProc3 );
		return;
	}

	name = GetWord( ppro->m_channelsToWho.c_str(), 0 );
	ppro->DoUserhostWithReason(2, _T("S") + name, true, _T("%s"), name.c_str());
	CMString temp = GetWordAddress( ppro->m_channelsToWho.c_str(), 1 );
	ppro->m_channelsToWho = temp;
	if ( ppro->m_iTempCheckTime )
		ppro->SetChatTimer( ppro->OnlineNotifTimer3, ppro->m_iTempCheckTime*1000, OnlineNotifTimerProc3 );
	else
		ppro->SetChatTimer( ppro->OnlineNotifTimer3, ppro->m_onlineNotificationTime*1000, OnlineNotifTimerProc3 );
}

VOID CALLBACK OnlineNotifTimerProc( HWND, UINT, UINT_PTR idEvent, DWORD )
{
	CIrcProto *ppro = GetTimerOwner( idEvent );
	if (ppro == NULL)
		return;

	if ( ppro->m_iStatus == ID_STATUS_OFFLINE || ppro->m_iStatus == ID_STATUS_CONNECTING || 
		  ( !ppro->m_autoOnlineNotification && !ppro->bTempForceCheck) || ppro->bTempDisableCheck ) {
		ppro->KillChatTimer( ppro->OnlineNotifTimer );
		ppro->m_namesToWho = _T("");
		return;
	}

	CMString name = GetWord( ppro->m_namesToWho.c_str(), 0);
	CMString name2 = GetWord( ppro->m_namesToUserhost.c_str(), 0);

	if ( name.IsEmpty() && name2.IsEmpty()) {
		DBVARIANT dbv;
		for (HANDLE hContact = db_find_first(ppro->m_szModuleName); hContact; hContact = db_find_next(hContact, ppro->m_szModuleName)) {
			if ( ppro->isChatRoom(hContact))
				continue;

			BYTE bDCC = ppro->getByte(hContact, "DCC", 0);
			BYTE bHidden = db_get_b(hContact,"CList", "Hidden", 0);
			if ( bDCC || bHidden)
				continue;
			if ( ppro->getTString( hContact, "Default", &dbv ))
				continue;

			BYTE bAdvanced = ppro->getByte(hContact, "AdvancedMode", 0) ;
			if ( !bAdvanced ) {
				db_free( &dbv );
				if ( !ppro->getTString( hContact, "Nick", &dbv )) {	
					ppro->m_namesToUserhost += CMString(dbv.ptszVal) + _T(" ");
					db_free( &dbv );
				}
			}
			else {
				db_free( &dbv );
				DBVARIANT dbv2;
								
				TCHAR* DBNick = NULL;
				TCHAR* DBWildcard = NULL;
				if ( !ppro->getTString( hContact, "Nick", &dbv ))
					DBNick = dbv.ptszVal;
				if ( !ppro->getTString( hContact, "UWildcard", &dbv2 ))
					DBWildcard = dbv2.ptszVal;

				if ( DBNick && ( !DBWildcard || !WCCmp(CharLower(DBWildcard), CharLower(DBNick)))) 
					ppro->m_namesToWho += CMString(DBNick) + _T(" ");
				else if ( DBWildcard )
					ppro->m_namesToWho += CMString(DBWildcard) + _T(" ");

				if ( DBNick )     db_free(&dbv);
            if ( DBWildcard ) db_free(&dbv2);
			}
		}
	}

	if ( ppro->m_namesToWho.IsEmpty() && ppro->m_namesToUserhost.IsEmpty()) {
		ppro->SetChatTimer( ppro->OnlineNotifTimer, 60*1000, OnlineNotifTimerProc );
		return;
	}

	name = GetWord( ppro->m_namesToWho.c_str(), 0);
	name2 = GetWord( ppro->m_namesToUserhost.c_str(), 0);
	CMString temp;
	if ( !name.IsEmpty()) {
		ppro->DoUserhostWithReason(2, _T("S") + name, true, _T("%s"), name.c_str());
		temp = GetWordAddress( ppro->m_namesToWho.c_str(), 1 );
		ppro->m_namesToWho = temp;
	}

	if ( !name2.IsEmpty()) {
		CMString params;
		for ( int i = 0; i < 3; i++ ) {
			params = _T("");
			for ( int j = 0; j < 5; j++ ) 
				params += GetWord( ppro->m_namesToUserhost, i *5 + j) + _T(" ");

			if ( params[0] != ' ' )
				ppro->DoUserhostWithReason(1, CMString(_T("S")) + params, true, params);
		}
		temp = GetWordAddress( ppro->m_namesToUserhost.c_str(), 15 );
		ppro->m_namesToUserhost = temp;
	}

	if ( ppro->m_iTempCheckTime )
		ppro->SetChatTimer( ppro->OnlineNotifTimer, ppro->m_iTempCheckTime*1000, OnlineNotifTimerProc );
	else
		ppro->SetChatTimer( ppro->OnlineNotifTimer, ppro->m_onlineNotificationTime*1000, OnlineNotifTimerProc );
}

int CIrcProto::AddOutgoingMessageToDB(HANDLE hContact, TCHAR* msg)
{
	if ( m_iStatus == ID_STATUS_OFFLINE || m_iStatus == ID_STATUS_CONNECTING )
		return 0;

	CMString S = DoColorCodes( msg, TRUE, FALSE );

	DBEVENTINFO dbei = { sizeof(dbei) };
	dbei.szModule = m_szModuleName;
	dbei.eventType = EVENTTYPE_MESSAGE;
	dbei.timestamp = (DWORD)time(NULL);
	dbei.flags = DBEF_SENT + DBEF_UTF;
	dbei.pBlob = ( PBYTE )mir_utf8encodeW( S.c_str());
	dbei.cbBlob = (DWORD)strlen(( char* )dbei.pBlob) + 1;
	db_event_add(hContact, &dbei);
	mir_free( dbei.pBlob );
	return 1;
}

void __cdecl CIrcProto::ResolveIPThread(LPVOID di)
{
	IPRESOLVE* ipr = (IPRESOLVE *) di;

	EnterCriticalSection( &m_resolve);

	if ( ipr != NULL && (ipr->iType == IP_AUTO && lstrlenA(m_myHost) == 0 || ipr->iType == IP_MANUAL )) {
		hostent* myhost = gethostbyname( ipr->sAddr.c_str());
		if ( myhost ) {
			IN_ADDR in;
			memcpy( &in, myhost->h_addr, 4 );
			if ( ipr->iType == IP_AUTO )
				mir_snprintf( m_myHost, sizeof( m_myHost ), "%s", inet_ntoa( in ));
			else
				mir_snprintf( m_mySpecifiedHostIP, sizeof( m_mySpecifiedHostIP ), "%s", inet_ntoa( in ));
	}	}
	
	LeaveCriticalSection( &m_resolve );
	delete ipr;
}

bool CIrcProto::OnIrc_PING(const CIrcMessage* pmsg)
{
	TCHAR szResponse[100];
	mir_sntprintf(szResponse, SIZEOF(szResponse), _T("PONG %s"), pmsg->parameters[0].c_str());
	SendIrcMessage( szResponse );
	return false;
}

bool CIrcProto::OnIrc_WELCOME(const CIrcMessage* pmsg)
{
	if ( pmsg->parameters[0] != m_info.sNick )
		m_info.sNick = pmsg->parameters[0];

	if ( pmsg->m_bIncoming && pmsg->parameters.getCount() > 1 ) {
		static TCHAR host[1024];
		int i = 0;
		CMString word = GetWord( pmsg->parameters[1].c_str(), i );
		while ( !word.IsEmpty()) {
			if ( _tcschr( word.c_str(), '!') && _tcschr( word.c_str(), '@' )) {
				lstrcpyn( host, word.c_str(), SIZEOF(host));
				TCHAR* p1 = _tcschr( host, '@' );
				if ( p1 )
					ForkThread( &CIrcProto::ResolveIPThread, new IPRESOLVE( _T2A(p1+1), IP_AUTO ));
			}
			
			word = GetWord(pmsg->parameters[1].c_str(), ++i);
	}	}			
		
	ShowMessage( pmsg ); 
	return true;
}

bool CIrcProto::OnIrc_WHOTOOLONG(const CIrcMessage* pmsg)
{
	CMString command = GetNextUserhostReason(2);
	if ( command[0] == 'U' )
		ShowMessage( pmsg ); 

	return true;
}

bool CIrcProto::OnIrc_BACKFROMAWAY(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming ) {
		int Temp = m_iStatus;
		m_iStatus = m_iDesiredStatus = ID_STATUS_ONLINE;
		ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)Temp, ID_STATUS_ONLINE);

		if ( m_perform )
			DoPerform( "Event: Available" );
	}			
		
	ShowMessage( pmsg );
	return true;
}

bool CIrcProto::OnIrc_SETAWAY(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming ) {
		int Temp = m_iDesiredStatus;
		m_iStatus = m_iDesiredStatus = ID_STATUS_AWAY;
		ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)Temp, ID_STATUS_AWAY);

		if ( m_perform ) {
			switch ( m_iStatus ) {
			case ID_STATUS_AWAY:
				DoPerform( "Event: Away" );
				break;
			case ID_STATUS_NA:
				DoPerform( "Event: N/A" );
				break;
			case ID_STATUS_DND:
				DoPerform( "Event: DND" );
				break;
			case ID_STATUS_OCCUPIED:
				DoPerform( "Event: Occupied" );
				break;
			case ID_STATUS_OUTTOLUNCH:
				DoPerform( "Event: Out for lunch" );
				break;
			case ID_STATUS_ONTHEPHONE:
				DoPerform( "Event: On the phone" );
				break;
			default:
				m_iStatus = ID_STATUS_AWAY;
				DoPerform( "Event: Away" );
				break;
	}	}	}	
		
	ShowMessage( pmsg ); 
	return true;
}

bool CIrcProto::OnIrc_JOIN(const CIrcMessage* pmsg)
{
	if (pmsg->parameters.getCount() > 0 && pmsg->m_bIncoming && pmsg->prefix.sNick != m_info.sNick) {
		CMString host = pmsg->prefix.sUser + _T("@") + pmsg->prefix.sHost;
		DoEvent(GC_EVENT_JOIN, pmsg->parameters[0].c_str(), pmsg->prefix.sNick.c_str(), NULL, _T("Normal"), host.c_str(), NULL, true, false); 
		DoEvent(GC_EVENT_SETCONTACTSTATUS, pmsg->parameters[0].c_str(),pmsg->prefix.sNick.c_str(), NULL, NULL, NULL, ID_STATUS_ONLINE, FALSE, FALSE); 
	}
	else ShowMessage( pmsg ); 

	return true;
}

bool CIrcProto::OnIrc_QUIT(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming) 
	{
		CMString host = pmsg->prefix.sUser + _T("@") + pmsg->prefix.sHost;
		DoEvent(GC_EVENT_QUIT, NULL, pmsg->prefix.sNick.c_str(), pmsg->parameters.getCount()>0?pmsg->parameters[0].c_str():NULL, NULL, host.c_str(), NULL, true, false);
		struct CONTACT user = { (LPTSTR)pmsg->prefix.sNick.c_str(), (LPTSTR)pmsg->prefix.sUser.c_str(), (LPTSTR)pmsg->prefix.sHost.c_str(), false, false, false};
		CList_SetOffline( &user );
		if ( pmsg->prefix.sNick == m_info.sNick ) {
			GCDEST gcd = { m_szModuleName, NULL, GC_EVENT_CONTROL };
			GCEVENT gce = { sizeof(gce), &gcd };
			CallChatEvent(SESSION_OFFLINE, (LPARAM)&gce);
		}
	}
	else ShowMessage( pmsg );

	return true;
}

bool CIrcProto::OnIrc_PART(const CIrcMessage* pmsg)
{
	if ( pmsg->parameters.getCount() > 0 && pmsg->m_bIncoming ) {
		CMString host = pmsg->prefix.sUser + _T("@") + pmsg->prefix.sHost;
		DoEvent(GC_EVENT_PART, pmsg->parameters[0].c_str(), pmsg->prefix.sNick.c_str(), pmsg->parameters.getCount()>1?pmsg->parameters[1].c_str():NULL, NULL, host.c_str(), NULL, true, false); 
		if ( pmsg->prefix.sNick == m_info.sNick ) {
			CMString S = MakeWndID(pmsg->parameters[0].c_str());
			GCDEST gcd = { m_szModuleName, S.c_str(), GC_EVENT_CONTROL };
			GCEVENT gce = { sizeof(gce), &gcd };
			CallChatEvent(SESSION_OFFLINE, (LPARAM)&gce);
		}
	}
	else ShowMessage( pmsg ); 

	return true;
}

bool CIrcProto::OnIrc_KICK(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming && pmsg->parameters.getCount() > 1 )
		DoEvent( GC_EVENT_KICK, pmsg->parameters[0].c_str(), pmsg->parameters[1].c_str(), pmsg->parameters.getCount()>2?pmsg->parameters[2].c_str():NULL, pmsg->prefix.sNick.c_str(), NULL, NULL, true, false); 
	else
		ShowMessage( pmsg ); 

	if ( pmsg->parameters[1] == m_info.sNick ) {
		CMString S = MakeWndID( pmsg->parameters[0].c_str());
		GCDEST gcd = { m_szModuleName, S.c_str(), GC_EVENT_CONTROL };
		GCEVENT gce = { sizeof(gce), &gcd };
		CallChatEvent(SESSION_OFFLINE, (LPARAM)&gce);

		if ( m_rejoinIfKicked ) {
			CHANNELINFO *wi = (CHANNELINFO *)DoEvent(GC_EVENT_GETITEMDATA, pmsg->parameters[0].c_str(), NULL, NULL, NULL, NULL, NULL, FALSE, FALSE, 0);
			if ( wi && wi->pszPassword )
				PostIrcMessage( _T("/JOIN %s %s"), pmsg->parameters[0].c_str(), wi->pszPassword);
			else
				PostIrcMessage( _T("/JOIN %s"), pmsg->parameters[0].c_str());
	}	}

	return true;
}

bool CIrcProto::OnIrc_MODEQUERY(const CIrcMessage* pmsg)
{
	if ( pmsg->parameters.getCount() > 2 && pmsg->m_bIncoming && IsChannel( pmsg->parameters[1] )) {
		CMString sPassword = _T("");
		CMString sLimit = _T("");
		bool bAdd = false;
		int iParametercount = 3;

		LPCTSTR p1 = pmsg->parameters[2].c_str();
		while ( *p1 != '\0' ) {
			if ( *p1 == '+' )
				bAdd = true;
			if ( *p1 == '-' )
				bAdd = false;
			if ( *p1 == 'l' && bAdd ) {
				if (( int )pmsg->parameters.getCount() > iParametercount )
					sLimit = pmsg->parameters[ iParametercount ];
				iParametercount++;
			}
			if ( *p1 == 'k' && bAdd ) {
				if (( int )pmsg->parameters.getCount() > iParametercount )
					sPassword = pmsg->parameters[ iParametercount ];
				iParametercount++;
			}

			p1++;
		}

		AddWindowItemData( pmsg->parameters[1].c_str(), sLimit.IsEmpty() ? 0 : sLimit.c_str(), pmsg->parameters[2].c_str(), sPassword.IsEmpty() ? 0 : sPassword.c_str(), 0 );
	}
	ShowMessage( pmsg );
	return true;
}

bool CIrcProto::OnIrc_MODE(const CIrcMessage* pmsg)
{	
	bool flag = false; 
	bool bContainsValidModes = false;
	CMString sModes = _T("");
	CMString sParams = _T("");

	if ( pmsg->parameters.getCount() > 1 && pmsg->m_bIncoming ) {
		if ( IsChannel( pmsg->parameters[0] )) {
			bool bAdd = false;
			int  iParametercount = 2;
			LPCTSTR p1 = pmsg->parameters[1].c_str();

			while ( *p1 != '\0' ) {
				if ( *p1 == '+' ) {
					bAdd = true;
					sModes += _T("+");
				}
				if ( *p1 == '-' ) {
					bAdd = false;
					sModes += _T("-");
				}
				if ( *p1 == 'l' && bAdd && iParametercount < (int)pmsg->parameters.getCount()) {
					bContainsValidModes = true;
					sModes += _T("l");
					sParams += _T(" ") + pmsg->parameters[iParametercount];
					iParametercount++;
				}
				if ( *p1 == 'b' || *p1 == 'k' && iParametercount < (int)pmsg->parameters.getCount()) {
					bContainsValidModes = true;
					sModes += *p1;
					sParams += _T(" ") + pmsg->parameters[iParametercount];
					iParametercount++;
				}
				if ( strchr( sUserModes.c_str(), (char)*p1 )) {
					CMString sStatus = ModeToStatus( *p1 );
					if (( int )pmsg->parameters.getCount() > iParametercount ) {	
						if ( !_tcscmp(pmsg->parameters[2].c_str(), m_info.sNick.c_str())) {
							char cModeBit = -1;
							CHANNELINFO *wi = (CHANNELINFO *)DoEvent( GC_EVENT_GETITEMDATA, pmsg->parameters[0].c_str(), NULL, NULL, NULL, NULL, NULL, false, false, 0 );
							switch (*p1) {
								case 'v':      cModeBit = 0;       break;
								case 'h':      cModeBit = 1;       break;
								case 'o':      cModeBit = 2;       break;
								case 'a':      cModeBit = 3;       break;
								case 'q':      cModeBit = 4;       break;
							}

							// set bit for own mode on this channel (voice/hop/op/admin/owner)
							if ( bAdd && cModeBit >= 0 )
								wi->OwnMode |= ( 1 << cModeBit );
							else
								wi->OwnMode &= ~( 1 << cModeBit );

							DoEvent( GC_EVENT_SETITEMDATA, pmsg->parameters[0].c_str(), NULL, NULL, NULL, NULL, (DWORD_PTR)wi, false, false, 0 );
						}
						DoEvent( bAdd ? GC_EVENT_ADDSTATUS : GC_EVENT_REMOVESTATUS, pmsg->parameters[0].c_str(), pmsg->parameters[iParametercount].c_str(), pmsg->prefix.sNick.c_str(), sStatus.c_str(), NULL, NULL, m_oldStyleModes?false:true, false); 
						iParametercount++;
					}
				}
				else if (*p1 != 'b' && *p1 != ' ' && *p1 != '+' && *p1 != '-' ) {
					bContainsValidModes = true;
					if (*p1 != 'l' && *p1 != 'k')
						sModes += *p1;
					flag = true;
				}

				p1++;
			}

			if ( m_oldStyleModes ) {
				TCHAR temp[256];
				mir_sntprintf( temp, SIZEOF(temp), TranslateT("%s sets mode %s"), 
					pmsg->prefix.sNick.c_str(), pmsg->parameters[1].c_str());
				
				CMString sMessage = temp;
				for ( int i=2; i < (int)pmsg->parameters.getCount(); i++ )
					sMessage += _T(" ") + pmsg->parameters[i];

				DoEvent( GC_EVENT_INFORMATION, pmsg->parameters[0].c_str(), pmsg->prefix.sNick.c_str(), sMessage.c_str(), NULL, NULL, NULL, true, false ); 
			}
			else if ( bContainsValidModes ) {
				for ( int i = iParametercount; i < (int)pmsg->parameters.getCount(); i++ )
					sParams += _T(" ") + pmsg->parameters[i];

				TCHAR temp[4000];
				mir_sntprintf( temp, 3999, TranslateT(	"%s sets mode %s%s" ), pmsg->prefix.sNick.c_str(), sModes.c_str(), sParams.c_str());
				DoEvent(GC_EVENT_INFORMATION, pmsg->parameters[0].c_str(), pmsg->prefix.sNick.c_str(), temp, NULL, NULL, NULL, true, false); 
			}

			if ( flag )
				PostIrcMessage( _T("/MODE %s"), pmsg->parameters[0].c_str());
		}
		else {
			TCHAR temp[256];
			mir_sntprintf( temp, SIZEOF(temp), TranslateT("%s sets mode %s"), pmsg->prefix.sNick.c_str(), pmsg->parameters[1].c_str());

			CMString sMessage = temp;
			for ( int i=2; i < (int)pmsg->parameters.getCount(); i++ )
				sMessage += _T(" ") + pmsg->parameters[i];

			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, pmsg->prefix.sNick.c_str(), sMessage.c_str(), NULL, NULL, NULL, true, false); 
		}
	}
	else ShowMessage( pmsg ); 

	return true;
}

bool CIrcProto::OnIrc_NICK(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming && pmsg->parameters.getCount() > 0 ) {
		bool bIsMe = pmsg->prefix.sNick.c_str() == m_info.sNick ? true : false;

		if ( m_info.sNick == pmsg->prefix.sNick && pmsg->parameters.getCount() > 0 ) {
			m_info.sNick = pmsg->parameters[0];
			setTString("Nick", m_info.sNick.c_str());
		}

		CMString host = pmsg->prefix.sUser + _T("@") + pmsg->prefix.sHost;
		DoEvent(GC_EVENT_NICK, NULL, pmsg->prefix.sNick.c_str(), pmsg->parameters[0].c_str(), NULL, host.c_str(), NULL, true, bIsMe); 
		DoEvent(GC_EVENT_CHUID, NULL, pmsg->prefix.sNick.c_str(), pmsg->parameters[0].c_str(), NULL, NULL, NULL, true, false); 
		
		struct CONTACT user = { (TCHAR*)pmsg->prefix.sNick.c_str(), (TCHAR*)pmsg->prefix.sUser.c_str(), (TCHAR*)pmsg->prefix.sHost.c_str(), false, false, false};
		HANDLE hContact = CList_FindContact(&user);
		if (hContact) {
			if ( getWord(hContact, "Status", ID_STATUS_OFFLINE) == ID_STATUS_OFFLINE)
				setWord(hContact, "Status", ID_STATUS_ONLINE);
			setTString(hContact, "Nick", pmsg->parameters[0].c_str());
			setTString(hContact, "User", pmsg->prefix.sUser.c_str());
			setTString(hContact, "Host", pmsg->prefix.sHost.c_str());
		}
	}
	else ShowMessage( pmsg );

	return true;
}

bool CIrcProto::OnIrc_NOTICE(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming && pmsg->parameters.getCount() > 1 ) {
		if ( IsCTCP( pmsg ))
			return true;

		if ( !m_ignore || !IsIgnored(pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'n' )) {
			CMString S;
			CMString S2;
			CMString S3;
			if ( pmsg->prefix.sNick.GetLength() > 0 )
				S = pmsg->prefix.sNick;
			else
				S = m_info.sNetwork;
			S3 = m_info.sNetwork;
			if ( IsChannel( pmsg->parameters[0] ))
				S2 = pmsg->parameters[0].c_str();
			else {
				GC_INFO gci = {0};
				gci.Flags = BYID | TYPE;
				gci.pszModule = m_szModuleName;

				CMString S3 = GetWord( pmsg->parameters[1].c_str(), 0);
				if ( S3[0] == '[' && S3[1] == '#' && S3[S3.GetLength()-1] == ']' ) {
					S3.Delete(S3.GetLength()-1, 1);
					S3.Delete(0,1);
					CMString Wnd = MakeWndID( S3.c_str());
					gci.pszID = Wnd.c_str();
					if ( !CallServiceSync( MS_GC_GETINFO, 0, (LPARAM)&gci ) && gci.iType == GCW_CHATROOM )
						S2 = GetWord( gci.pszID, 0 );
					else
						S2 = _T("");
				}
				else S2 = _T("");
			}
			DoEvent(GC_EVENT_NOTICE, S2.IsEmpty() ? 0 : S2.c_str(), S.c_str(), pmsg->parameters[1].c_str(), NULL, S3.c_str(), NULL, true, false); 
		}
	}
	else ShowMessage( pmsg );

	return true;
}

bool CIrcProto::OnIrc_YOURHOST(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming ) {
		static const TCHAR* lpszFmt = _T("Your host is %99[^ \x5b,], running version %99s");
		TCHAR szHostName[100], szVersion[100];
		if ( _stscanf(pmsg->parameters[1].c_str(), lpszFmt, &szHostName, &szVersion) > 0 )
			m_info.sServerName = szHostName;
		if ( pmsg->parameters[0] != m_info.sNick)
			m_info.sNick = pmsg->parameters[0];
	}
	
	ShowMessage( pmsg );
	return true;
}

bool CIrcProto::OnIrc_INVITE(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming && ( m_ignore && IsIgnored( pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'i' )))
		return true;

	if ( pmsg->m_bIncoming && m_joinOnInvite && pmsg->parameters.getCount() >1 && lstrcmpi(pmsg->parameters[0].c_str(), m_info.sNick.c_str()) == 0 ) 
		PostIrcMessage( _T("/JOIN %s"), pmsg->parameters[1].c_str());

	ShowMessage( pmsg );
	return true;
}

bool CIrcProto::OnIrc_PINGPONG(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming && pmsg->sCommand == _T("PING")) {
		TCHAR szResponse[100];
		mir_sntprintf(szResponse, SIZEOF(szResponse), _T("PONG %s"), pmsg->parameters[0].c_str());
		SendIrcMessage( szResponse );
	}

	return true;
}

bool CIrcProto::OnIrc_PRIVMSG(const CIrcMessage* pmsg)
{
	if ( pmsg->parameters.getCount() > 1 ) {
		if ( IsCTCP( pmsg ))
			return true;

		CMString mess = pmsg->parameters[1];
		bool bIsChannel = IsChannel(pmsg->parameters[0]);

		if ( pmsg->m_bIncoming && !bIsChannel ) {
			mess = DoColorCodes( mess.c_str(), TRUE, FALSE );

			struct CONTACT user = { (TCHAR*)pmsg->prefix.sNick.c_str(), (TCHAR*)pmsg->prefix.sUser.c_str(), (TCHAR*)pmsg->prefix.sHost.c_str(), false, false, false};

			if ( CallService(MS_IGNORE_ISIGNORED, NULL, IGNOREEVENT_MESSAGE)) 
				if ( !CList_FindContact(&user))
					return true;

			if (( m_ignore && IsIgnored( pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'q' ))) {
				HANDLE hContact = CList_FindContact(&user);
				if ( !hContact || ( hContact && db_get_b( hContact,"CList", "Hidden", 0) == 1 ))
					return true;
			}

			HANDLE hContact = CList_AddContact(&user, false, true);

			PROTORECVEVENT pre = { 0 };
			pre.timestamp = (DWORD)time(NULL);
			pre.flags = PREF_UTF;
			pre.szMessage = mir_utf8encodeW( mess.c_str());
			setTString(hContact, "User", pmsg->prefix.sUser.c_str());
			setTString(hContact, "Host", pmsg->prefix.sHost.c_str());
			ProtoChainRecvMsg(hContact, &pre);
			mir_free( pre.szMessage );
			return true;
		}
		
		if ( bIsChannel ) {
			if ( !(pmsg->m_bIncoming && m_ignore && IsIgnored(pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'm' ))) {
				if ( !pmsg->m_bIncoming )
					ReplaceString( mess, _T("%%"), _T("%"));
				DoEvent(GC_EVENT_MESSAGE, pmsg->parameters[0].c_str(), pmsg->m_bIncoming?pmsg->prefix.sNick.c_str():m_info.sNick.c_str(), mess.c_str(), NULL, NULL, NULL, true, pmsg->m_bIncoming?false:true); 				
			}
			return true;
	}	}

	ShowMessage( pmsg );
	return true;
}

bool CIrcProto::IsCTCP(const CIrcMessage* pmsg)
{
	// is it a ctcp command, i e is the first and last characer of a PRIVMSG or NOTICE text ASCII 1
	CMString mess = pmsg->parameters[1];
	if ( !( mess.GetLength() > 3 && mess[0] == 1 && mess[ mess.GetLength()-1] == 1 ))
		return false;

	// set mess to contain the ctcp command, excluding the leading and trailing  ASCII 1
	mess.Delete(0,1);
	mess.Delete(mess.GetLength()-1,1);
	
	// exploit???
	if ( mess.Find(1) != -1 || mess.Find( _T("%newl")) != -1 ) {
		TCHAR temp[4096];
		mir_sntprintf(temp, SIZEOF(temp), TranslateT( "CTCP ERROR: Malformed CTCP command received from %s!%s@%s. Possible attempt to take control of your IRC client registered"), pmsg->prefix.sNick.c_str(), pmsg->prefix.sUser.c_str(), pmsg->prefix.sHost.c_str());
		DoEvent( GC_EVENT_INFORMATION, 0, m_info.sNick.c_str(), temp, NULL, NULL, NULL, true, false); 
		return true;
	}

	// extract the type of ctcp command
	CMString ocommand = GetWord(mess.c_str(), 0);
	CMString command = GetWord(mess.c_str(), 0);
	command.MakeLower();

	// should it be ignored?
	if ( m_ignore ) {
		if ( IsChannel( pmsg->parameters[0] )) {
			if ( command == _T("action") && IsIgnored(pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'm'))
				return true;
		}
		else {
			if ( command == _T("action")) {
				if ( IsIgnored( pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'q' ))
					return true;
			}
			else if ( command == _T("dcc")) {
				if ( IsIgnored( pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'd' ))
					return true;
			}
			else if ( IsIgnored( pmsg->prefix.sNick, pmsg->prefix.sUser, pmsg->prefix.sHost, 'c' ))
				return true;
	}	}

	if ( pmsg->sCommand == _T("PRIVMSG")) {
		// incoming ACTION
		if ( command == _T("action")) {
			mess.Delete(0,6);

			if ( IsChannel( pmsg->parameters[0] )) {
				if ( mess.GetLength() > 1 ) {
					mess.Delete(0,1);
					if ( !pmsg->m_bIncoming )
						ReplaceString(mess, _T("%%"), _T("%"));

					DoEvent(GC_EVENT_ACTION, pmsg->parameters[0].c_str(), pmsg->m_bIncoming?pmsg->prefix.sNick.c_str():m_info.sNick.c_str(), mess.c_str(), NULL, NULL, NULL, true, pmsg->m_bIncoming?false:true); 
				}
			}
			else if (pmsg->m_bIncoming)
			{
				mess.Insert(0, pmsg->prefix.sNick.c_str());
				mess.Insert(0, _T("* "));
				mess.Insert(mess.GetLength(), _T(" *"));
				CIrcMessage msg = *pmsg;
				msg.parameters[1] = mess;
				OnIrc_PRIVMSG(&msg);
			}
		}
		// incoming FINGER
		else if (pmsg->m_bIncoming && command == _T("finger")) {
			PostIrcMessage( _T("/NOTICE %s \001FINGER %s (%s)\001"), pmsg->prefix.sNick.c_str(), m_name, m_userID);
			
			TCHAR temp[300];
			mir_sntprintf( temp, SIZEOF(temp), TranslateT("CTCP FINGER requested by %s"), pmsg->prefix.sNick.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, temp, NULL, NULL, NULL, true, false); 
		}

		// incoming VERSION
		else if (pmsg->m_bIncoming && command == _T("version")) {
			PostIrcMessage( _T("/NOTICE %s \001VERSION Miranda NG %%mirver (IRC v.%%version), (c) 2003-2014 J.Persson, G.Hazan\001"), pmsg->prefix.sNick.c_str());
				
			TCHAR temp[300];
			mir_sntprintf( temp, SIZEOF(temp), TranslateT("CTCP VERSION requested by %s"), pmsg->prefix.sNick.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, temp, NULL, NULL, NULL, true, false); 
		}

		// incoming SOURCE
		else if (pmsg->m_bIncoming && command == _T("source")) {
			PostIrcMessage( _T("/NOTICE %s \001SOURCE Get Miranda IRC here: http://miranda-ng.org/ \001"), pmsg->prefix.sNick.c_str());
			
			TCHAR temp[300];
			mir_sntprintf( temp, SIZEOF(temp), TranslateT("CTCP SOURCE requested by %s"), pmsg->prefix.sNick.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, temp, NULL, NULL, NULL, true, false); 
		}

		// incoming USERINFO
		else if (pmsg->m_bIncoming && command == _T("userinfo")) {
			PostIrcMessage( _T("/NOTICE %s \001USERINFO %s\001"), pmsg->prefix.sNick.c_str(), m_userInfo );
			
			TCHAR temp[300];
			mir_sntprintf( temp, SIZEOF(temp), TranslateT("CTCP USERINFO requested by %s") , pmsg->prefix.sNick.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, temp, NULL, NULL, NULL, true, false); 
		}

		// incoming PING
		else if (pmsg->m_bIncoming && command == _T("ping")) {
			PostIrcMessage( _T("/NOTICE %s \001%s\001"), pmsg->prefix.sNick.c_str(), mess.c_str());
			
			TCHAR temp[300];
			mir_sntprintf( temp, SIZEOF(temp), TranslateT("CTCP PING requested by %s"), pmsg->prefix.sNick.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, temp, NULL, NULL, NULL, true, false); 
		}

		// incoming TIME
		else if (pmsg->m_bIncoming && command == _T("time")) {
			TCHAR temp[300];
			time_t tim = time(NULL);
			lstrcpyn( temp, _tctime( &tim ), 25 );
			PostIrcMessage( _T("/NOTICE %s \001TIME %s\001"), pmsg->prefix.sNick.c_str(), temp);
			
			mir_sntprintf(temp, SIZEOF(temp), TranslateT("CTCP TIME requested by %s"), pmsg->prefix.sNick.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, temp, NULL, NULL, NULL, true, false); 
		}

		// incoming DCC request... lots of stuff happening here...
		else if (pmsg->m_bIncoming && command == _T("dcc")) {
			CMString type = GetWord(mess.c_str(), 1);
			type.MakeLower();

			// components of a dcc message
			CMString sFile = _T("");
			DWORD dwAdr = 0;
			int iPort = 0;
			unsigned __int64 dwSize = 0;
			CMString sToken = _T("");
			bool bIsChat = ( type == _T("chat"));

			// 1. separate the dcc command into the correct pieces
			if ( bIsChat || type == _T("send")) {
				// if the filename is surrounded by quotes, do this
				if ( GetWord(mess.c_str(), 2)[0] == '\"' ) {
					int end = 0;
					int begin = mess.Find('\"', 0);
					if ( begin >= 0 ) {
						end = mess.Find('\"', begin + 1); 
						if ( end >= 0 ) {
							sFile = mess.Mid(begin+1, end-begin-1);

							begin = mess.Find(' ', end);
							if ( begin >= 0 ) {
								CMString rest = mess.Mid(begin, mess.GetLength());
								dwAdr = _tcstoul(GetWord(rest.c_str(), 0).c_str(), NULL, 10);
								iPort = _ttoi(GetWord(rest.c_str(), 1).c_str());
								dwSize = _ttoi64(GetWord(rest.c_str(), 2).c_str());
								sToken = GetWord(rest.c_str(), 3);
					}	}	}
				}
				// ... or try another method of separating the dcc command
				else if ( !GetWord(mess.c_str(), (bIsChat) ? 4 : 5 ).IsEmpty()) {
					int index = (bIsChat) ? 4 : 5;
					bool bFlag = false;

					// look for the part of the ctcp command that contains adress, port and size
					while ( !bFlag && !GetWord(mess.c_str(), index).IsEmpty()) {
						CMString sTemp;
						
						if ( type == _T("chat"))
							sTemp = GetWord(mess.c_str(), index-1) + GetWord(mess.c_str(), index);
						else	
							sTemp = GetWord(mess.c_str(), index-2) + GetWord(mess.c_str(), index-1) + GetWord(mess.c_str(), index);
						
						// if all characters are number it indicates we have found the adress, port and size parameters
						int ind = 0;
						while ( sTemp[ind] != '\0' ) {
							if ( !_istdigit( sTemp[ind] ))
								break;
							ind++;
						}
						
						if ( sTemp[ind] == '\0' && GetWord( mess.c_str(), index + ((bIsChat) ? 1 : 2 )).IsEmpty())
							bFlag = true;
						index++;
					}
					
					if ( bFlag ) {
						TCHAR* p1 = _tcsdup( GetWordAddress(mess.c_str(), 2 ));
						TCHAR* p2 = ( TCHAR* )GetWordAddress( p1, index-5 );
						
						if ( type == _T("send")) {
							if ( p2 > p1 ) {
								p2--;
								while( p2 != p1 && *p2 == ' ' ) {
									*p2 = '\0';
									p2--;
								}
								sFile = p1;
							}
						}
						else sFile = _T("chat");

						free( p1 );

						dwAdr = _tcstoul(GetWord(mess.c_str(), index - (bIsChat?2:3)).c_str(), NULL, 10);
						iPort = _ttoi(GetWord(mess.c_str(), index - (bIsChat?1:2)).c_str());
						dwSize = _ttoi64(GetWord(mess.c_str(), index-1).c_str());
						sToken = GetWord(mess.c_str(), index);
				}	} 
			}
			else if (type == _T("accept") || type == _T("resume")) {
				// if the filename is surrounded by quotes, do this
				if ( GetWord(mess.c_str(), 2)[0] == '\"' ) {
					int end = 0;
					int begin = mess.Find('\"', 0);
					if ( begin >= 0 ) {
						end = mess.Find('\"', begin + 1); 
						if ( end >= 0 ) {
							sFile = mess.Mid(begin+1, end);

							begin = mess.Find(' ', end);
							if ( begin >= 0 ) {
								CMString rest = mess.Mid(begin, mess.GetLength());
								iPort = _ttoi(GetWord(rest.c_str(), 0).c_str());
								dwSize = _ttoi(GetWord(rest.c_str(), 1).c_str());
								sToken = GetWord(rest.c_str(), 2);
					}	}	}
				}
				// ... or try another method of separating the dcc command
				else if ( !GetWord(mess.c_str(), 4).IsEmpty()) {
					int index = 4;
					bool bFlag = false;

					// look for the part of the ctcp command that contains adress, port and size
					while ( !bFlag && !GetWord(mess.c_str(), index).IsEmpty()) {
						CMString sTemp = GetWord(mess.c_str(), index-1) + GetWord(mess.c_str(), index);
						
						// if all characters are number it indicates we have found the adress, port and size parameters
						int ind = 0;

						while ( sTemp[ind] != '\0' ) {
							if ( !_istdigit( sTemp[ind] ))
								break;
							ind++;
						}
						
						if ( sTemp[ind] == '\0' && GetWord(mess.c_str(), index + 2).IsEmpty())
							bFlag = true;
						index++;
					}
					if ( bFlag ) {
						TCHAR* p1 = _tcsdup(GetWordAddress(mess.c_str(), 2));
						TCHAR* p2 = ( TCHAR* )GetWordAddress(p1, index-4);
						
						if ( p2 > p1 ) {
							p2--;
							while( p2 != p1 && *p2 == ' ' ) {
								*p2 = '\0';
								p2--;
							}
							sFile = p1;
						}

						free( p1 );

						iPort = _ttoi(GetWord(mess.c_str(), index-2).c_str());
						dwSize = _ttoi64(GetWord(mess.c_str(), index-1).c_str());
						sToken = GetWord(mess.c_str(), index);
			}	}	} 
			// end separating dcc commands

			// 2. Check for malformed dcc commands or other errors
			if ( bIsChat || type == _T("send")) {
				TCHAR szTemp[256];
				szTemp[0] = '\0';

				unsigned long ulAdr = 0;
				if ( m_manualHost )
					ulAdr = ConvertIPToInteger( m_mySpecifiedHostIP );
				else
					ulAdr = ConvertIPToInteger( m_IPFromServer ? m_myHost : m_myLocalHost );

				if ( bIsChat && !m_DCCChatEnabled)
					mir_sntprintf(szTemp, SIZEOF(szTemp), TranslateT("DCC: Chat request from %s denied"),pmsg->prefix.sNick.c_str());

				else if(type == _T("send") && !m_DCCFileEnabled)
					mir_sntprintf(szTemp, SIZEOF(szTemp), TranslateT("DCC: File transfer request from %s denied"),pmsg->prefix.sNick.c_str());

				else if(type == _T("send") && !iPort && ulAdr == 0)
					mir_sntprintf(szTemp, SIZEOF(szTemp), TranslateT("DCC: Reverse file transfer request from %s denied [No local IP]"),pmsg->prefix.sNick.c_str());

				if ( sFile.IsEmpty() || dwAdr == 0 || dwSize == 0 || iPort == 0 && sToken.IsEmpty())
					mir_sntprintf(szTemp, SIZEOF(szTemp), TranslateT("DCC ERROR: Malformed CTCP request from %s [%s]"),pmsg->prefix.sNick.c_str(), mess.c_str());
			
				if ( szTemp[0] ) {
					DoEvent( GC_EVENT_INFORMATION, 0, m_info.sNick.c_str(), szTemp, NULL, NULL, NULL, true, false); 
					return true;
				}

				// remove path from the filename if the remote client (stupidly) sent it
				CMString sFileCorrected = sFile;
				int i = sFile.ReverseFind( '\\' );
				if (i != -1 )
					sFileCorrected = sFile.Mid(i+1, sFile.GetLength());
				sFile = sFileCorrected;
			}
			else if ( type == _T("accept") || type == _T("resume")) {
				TCHAR szTemp[256];
				szTemp[0] = '\0';

				if ( type == _T("resume") && !m_DCCFileEnabled)
					mir_sntprintf(szTemp, SIZEOF(szTemp), TranslateT("DCC: File transfer resume request from %s denied"),pmsg->prefix.sNick.c_str());

				if ( sToken.IsEmpty() && iPort == 0 || sFile.IsEmpty())
					mir_sntprintf(szTemp, SIZEOF(szTemp), TranslateT("DCC ERROR: Malformed CTCP request from %s [%s]"),pmsg->prefix.sNick.c_str(), mess.c_str());
			
				if ( szTemp[0] ) {
					DoEvent( GC_EVENT_INFORMATION, 0, m_info.sNick.c_str(), szTemp, NULL, NULL, NULL, true, false); 
					return true;
				}

				// remove path from the filename if the remote client (stupidly) sent it
				CMString sFileCorrected = sFile;
				int i = sFile.ReverseFind( '\\' );
				if ( i != -1 )
					sFileCorrected = sFile.Mid(i+1, sFile.GetLength());
				sFile = sFileCorrected;
			}

			// 3. Take proper actions considering type of command

			// incoming chat request
			if ( bIsChat ) {
				CONTACT user = { (TCHAR*)pmsg->prefix.sNick.c_str(), 0, 0, false, false, true};
				HANDLE hContact = CList_FindContact( &user );

				// check if it should be ignored
				if ( m_DCCChatIgnore == 1 || 
					m_DCCChatIgnore == 2 && hContact && 
					db_get_b(hContact,"CList", "NotOnList", 0) == 0 && 
					db_get_b(hContact,"CList", "Hidden", 0) == 0)
				{
					CMString host = pmsg->prefix.sUser + _T("@") + pmsg->prefix.sHost;
					CList_AddDCCChat(pmsg->prefix.sNick, host, dwAdr, iPort); // add a CHAT event to the clist
				}
				else {
					TCHAR szTemp[512];
					mir_sntprintf( szTemp, SIZEOF(szTemp), TranslateT("DCC: Chat request from %s denied"),pmsg->prefix.sNick.c_str());
					DoEvent(GC_EVENT_INFORMATION, 0, m_info.sNick.c_str(), szTemp, NULL, NULL, NULL, true, false); 
			}	}

			// remote requested that the file should be resumed
			if ( type == _T("resume")) {
				CDccSession* dcc;
				if ( sToken.IsEmpty())
					dcc = FindDCCSendByPort( iPort );
				else
					dcc = FindPassiveDCCSend( _ttoi( sToken.c_str())); // reverse ft

				if ( dcc ) {
					InterlockedExchange(&dcc->dwWhatNeedsDoing, (long)FILERESUME_RESUME);
					dcc->dwResumePos = dwSize; // dwSize is the resume position
					PostIrcMessage( _T("/PRIVMSG %s \001DCC ACCEPT %s\001"), pmsg->prefix.sNick.c_str(), GetWordAddress(mess.c_str(), 2));
			}	}

			// remote accepted your request for a file resume
			if ( type == _T("accept")) {
				CDccSession* dcc;
				if ( sToken.IsEmpty())
					dcc = FindDCCRecvByPortAndName(iPort, pmsg->prefix.sNick.c_str());
				else
					dcc = FindPassiveDCCRecv(pmsg->prefix.sNick, sToken); // reverse ft

				if ( dcc ) {
					InterlockedExchange( &dcc->dwWhatNeedsDoing, (long)FILERESUME_RESUME );
					dcc->dwResumePos = dwSize;	// dwSize is the resume position					
					SetEvent( dcc->hEvent );
			}	}

			if ( type == _T("send")) {
				CMString sTokenBackup = sToken;
				bool bTurbo = false; // TDCC indicator

				if ( !sToken.IsEmpty() && sToken[sToken.GetLength()-1] == 'T' ) {
					bTurbo = true;
					sToken.Delete(sToken.GetLength()-1,1);
				}

				// if a token exists and the port is non-zero it is the remote
				// computer telling us that is has accepted to act as server for
				// a reverse filetransfer. The plugin should connect to that computer
				// and start sedning the file (if the token is valid). Compare to DCC RECV
				if ( !sToken.IsEmpty() && iPort ) {
					CDccSession* dcc = FindPassiveDCCSend( _ttoi( sToken.c_str()));
					if ( dcc ) {
						dcc->SetupPassive( dwAdr, iPort );
						dcc->Connect();
					}
				}
				else {
					struct CONTACT user = { (TCHAR*)pmsg->prefix.sNick.c_str(), (TCHAR*)pmsg->prefix.sUser.c_str(), (TCHAR*)pmsg->prefix.sHost.c_str(), false, false, false};
					if ( CallService(MS_IGNORE_ISIGNORED, NULL, IGNOREEVENT_FILE))
						if ( !CList_FindContact(&user))
							return true;

					HANDLE hContact = CList_AddContact( &user, false, true );
					if ( hContact ) {
						DCCINFO* di = new DCCINFO;
						di->hContact = hContact;
						di->sFile = sFile;
						di->dwSize = dwSize;
						di->sContactName = pmsg->prefix.sNick;
						di->dwAdr = dwAdr;
						di->iPort = iPort;
						di->iType = DCC_SEND;
						di->bSender = false;
						di->bTurbo = bTurbo;
						di->bSSL = false;
						di->bReverse = (iPort == 0 && !sToken.IsEmpty()) ? true : false;
						if ( di->bReverse )
							di->sToken = sTokenBackup;

						setTString(hContact, "User", pmsg->prefix.sUser.c_str());
						setTString(hContact, "Host", pmsg->prefix.sHost.c_str());

						TCHAR* tszTemp = ( TCHAR* )sFile.c_str();

						PROTORECVFILET pre = {0};
						pre.flags = PREF_TCHAR;
						pre.timestamp = (DWORD)time(NULL);
						pre.fileCount = 1;
						pre.ptszFiles = &tszTemp;						
						pre.lParam = (LPARAM)di;
						ProtoChainRecvFile(hContact, &pre);
			}	}	}
			// end type == "send"
		}
		else if ( pmsg->m_bIncoming ) {
			TCHAR temp[300];
			mir_sntprintf(temp, SIZEOF(temp), TranslateT("CTCP %s requested by %s"), ocommand.c_str(), pmsg->prefix.sNick.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, temp, NULL, NULL, NULL, true, false); 
	}	}

	// handle incoming ctcp in notices. This technique is used for replying to CTCP queries
	else if(pmsg->sCommand == _T("NOTICE")) {
		TCHAR szTemp[300]; 
		szTemp[0] = '\0';

		//if we got incoming CTCP Version for contact in CList - then write its as MirVer for that contact!
		if (pmsg->m_bIncoming && command == _T("version"))
			{
			struct CONTACT user = { (TCHAR*)pmsg->prefix.sNick.c_str(), (TCHAR*)pmsg->prefix.sUser.c_str(), (TCHAR*)pmsg->prefix.sHost.c_str(), false, false, false};
			HANDLE hContact = CList_FindContact(&user);
			if (hContact) 
				setTString( hContact, "MirVer", DoColorCodes(GetWordAddress(mess.c_str(), 1), TRUE, FALSE)); 
			}

		// if the whois window is visible and the ctcp reply belongs to the user in it, then show the reply in the whois window
		if ( m_whoisDlg && IsWindowVisible( m_whoisDlg->GetHwnd())) {
			m_whoisDlg->m_InfoNick.GetText( szTemp, SIZEOF(szTemp));
			if ( lstrcmpi(szTemp, pmsg->prefix.sNick.c_str()) == 0 ) {
				if ( pmsg->m_bIncoming && (command == _T("version") || command == _T("userinfo") || command == _T("time"))) {
					SetActiveWindow( m_whoisDlg->GetHwnd());
					m_whoisDlg->m_Reply.SetText( DoColorCodes(GetWordAddress(mess.c_str(), 1), TRUE, FALSE));
					return true;
				}
				if (pmsg->m_bIncoming && command == _T("ping")) {
					SetActiveWindow( m_whoisDlg->GetHwnd());
					int s = (int)time(0) - (int)_ttol(GetWordAddress(mess.c_str(), 1));
					TCHAR szTemp[30];
					if ( s == 1 )
						mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%u second"), s );
					else
						mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%u seconds"), s );

					m_whoisDlg->m_Reply.SetText( DoColorCodes( szTemp, TRUE, FALSE ));
					return true;
		}	}	}

		//... else show the reply in the current window
		if ( pmsg->m_bIncoming && command == _T("ping")) {
			int s = (int)time(0) - (int)_ttol(GetWordAddress(mess.c_str(), 1));
			mir_sntprintf( szTemp, SIZEOF(szTemp), TranslateT("CTCP PING reply from %s: %u sec(s)"), pmsg->prefix.sNick.c_str(), s); 
			DoEvent( GC_EVENT_INFORMATION, SERVERWINDOW, NULL, szTemp, NULL, NULL, NULL, true, false ); 
		}
		else {
			mir_sntprintf( szTemp, SIZEOF(szTemp), TranslateT("CTCP %s reply from %s: %s"), ocommand.c_str(), pmsg->prefix.sNick.c_str(), GetWordAddress(mess.c_str(), 1));	
			DoEvent( GC_EVENT_INFORMATION, SERVERWINDOW, NULL, szTemp, NULL, NULL, NULL, true, false ); 
	}	}		

	return true;
}

bool CIrcProto::OnIrc_NAMES(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming && pmsg->parameters.getCount() > 3 )
		sNamesList += pmsg->parameters[3] + _T(" ");
	ShowMessage( pmsg );
	return true;
}

bool CIrcProto::OnIrc_ENDNAMES(const CIrcMessage* pmsg)
{
	if ( pmsg->m_bIncoming && pmsg->parameters.getCount() > 1 ) {
		CMString name = _T("a");
		int i = 0;
		BOOL bFlag = false;

		// Is the user on the names list?
		while ( !name.IsEmpty()) {
			name = GetWord( sNamesList.c_str(), i );
			i++;
			if ( !name.IsEmpty()) {
				int index = 0;
				while ( _tcschr( sUserModePrefixes.c_str(), name[index] ))
					index++;

				if ( !lstrcmpi( name.Mid(index, name.GetLength()).c_str(), m_info.sNick.c_str())) {
					bFlag = true;
					break;
		}	}	}

		if ( bFlag ) {
			const TCHAR* sChanName = pmsg->parameters[1].c_str();
			if ( sChanName[0] == '@' || sChanName[0] == '*' || sChanName[0] == '=' )
				sChanName++;

			// Add a new chat window
			CMString sID = MakeWndID(sChanName);
			BYTE btOwnMode = 0;

			GCSESSION gcw = { sizeof(gcw) };
			gcw.iType = GCW_CHATROOM;
			gcw.ptszID = sID.c_str();
			gcw.pszModule = m_szModuleName;
			gcw.ptszName = sChanName;
			if (!CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw)) {
				DBVARIANT dbv;
				GCDEST gcd = { m_szModuleName, sID.c_str(), GC_EVENT_ADDGROUP };
				GCEVENT gce = { sizeof(gce), &gcd };

				PostIrcMessage( _T("/MODE %s"), sChanName );

				//register the statuses
				gce.ptszStatus = _T("Owner");
				CallChatEvent(0, (LPARAM)&gce);
				gce.ptszStatus = _T("Admin");
				CallChatEvent(0, (LPARAM)&gce);
				gce.ptszStatus = _T("Op");
				CallChatEvent(0, (LPARAM)&gce);
				gce.ptszStatus = _T("Halfop");
				CallChatEvent(0, (LPARAM)&gce);
				gce.ptszStatus = _T("Voice");
				CallChatEvent(0, (LPARAM)&gce);
				gce.ptszStatus = _T("Normal");
				CallChatEvent(0, (LPARAM)&gce);

				int i = 0;
				CMString sTemp = GetWord(sNamesList.c_str(), i);

				// Fill the nicklist
				while (!sTemp.IsEmpty()) {
					CMString sStat;
					CMString sTemp2 = sTemp;
					sStat = PrefixToStatus(sTemp[0]);
					
					// fix for networks like freshirc where they allow more than one prefix
					while ( PrefixToStatus(sTemp[0]) != _T("Normal"))
						sTemp.Delete(0,1);
					
					gcd.iType = GC_EVENT_JOIN;
					gce.ptszUID = sTemp.c_str();
					gce.ptszNick = sTemp.c_str();
					gce.ptszStatus = sStat.c_str();
					BOOL bIsMe = ( !lstrcmpi( gce.ptszNick, m_info.sNick.c_str())) ? TRUE : FALSE;
					if ( bIsMe ) {
						char BitNr = -1;
						switch (sTemp2[0]) {
							case '+':   BitNr = 0;   break;
							case '%':   BitNr = 1;   break;
							case '@':   BitNr = 2;   break;
							case '!':   BitNr = 3;   break;
							case '*':   BitNr = 4;   break;
						}
						if (BitNr >=0)
							btOwnMode = ( 1 << BitNr );
						else
							btOwnMode = 0;
					}
					gce.bIsMe = bIsMe;
					gce.time = bIsMe ? time(0) : 0;
					CallChatEvent(0, (LPARAM)&gce);
					DoEvent(GC_EVENT_SETCONTACTSTATUS, sChanName, sTemp.c_str(), NULL, NULL, NULL, ID_STATUS_ONLINE, FALSE, FALSE);
					// fix for networks like freshirc where they allow more than one prefix
					if (PrefixToStatus(sTemp2[0]) != _T("Normal")) {
						sTemp2.Delete(0, 1);
						sStat = PrefixToStatus(sTemp2[0]);
						while (sStat != _T("Normal")) {
							DoEvent(GC_EVENT_ADDSTATUS, sID.c_str(), sTemp.c_str(), _T("system"), sStat.c_str(), NULL, NULL, false, false, 0);
							sTemp2.Delete(0, 1);
							sStat = PrefixToStatus(sTemp2[0]);
						}
					}

					i++;
					sTemp = GetWord(sNamesList.c_str(), i);
				}
				
				//Set the item data for the window
				{
					CHANNELINFO *wi = (CHANNELINFO *)DoEvent(GC_EVENT_GETITEMDATA, sChanName, NULL, NULL, NULL, NULL, NULL, FALSE, FALSE, 0);					
					if (!wi)
						wi = new CHANNELINFO;
					wi->OwnMode = btOwnMode;
					wi->pszLimit = 0;
					wi->pszMode = 0;
					wi->pszPassword = 0;
					wi->pszTopic = 0;
					wi->codepage = getCodepage();
					DoEvent(GC_EVENT_SETITEMDATA, sChanName, NULL, NULL, NULL, NULL, (DWORD_PTR)wi, false, false, 0);

					if ( !sTopic.IsEmpty() && !lstrcmpi(GetWord(sTopic.c_str(), 0).c_str(), sChanName )) {
						DoEvent(GC_EVENT_TOPIC, sChanName, sTopicName.IsEmpty() ? NULL : sTopicName.c_str(), GetWordAddress(sTopic.c_str(), 1), NULL, sTopicTime.IsEmpty() ? NULL : sTopicTime.c_str(), NULL, true, false);
						AddWindowItemData(sChanName, 0, 0, 0, GetWordAddress(sTopic.c_str(), 1));
						sTopic = _T("");
						sTopicName = _T("");
						sTopicTime = _T("");
				}	}
				
				gcd.ptszID = (TCHAR*)sID.c_str();
				gcd.iType = GC_EVENT_CONTROL;
				gce.cbSize = sizeof(GCEVENT);
				gce.dwFlags = 0;
				gce.bIsMe = false;
				gce.dwItemData = false;
				gce.ptszNick = NULL;
				gce.ptszStatus = NULL;
				gce.ptszText = NULL;
				gce.ptszUID = NULL;
				gce.ptszUserInfo = NULL;
				gce.time = time(0);
				gce.pDest = &gcd;
	
				if ( !getTString( "JTemp", &dbv )) {
					CMString command = _T("a");
					CMString save = _T("");
					int i = 0;

					while ( !command.IsEmpty()) {
						command = GetWord( dbv.ptszVal, i );
						i++;
						if ( !command.IsEmpty()) {
							CMString S = command.Mid(1, command.GetLength());
							if ( !lstrcmpi( sChanName, S.c_str()))
								break;

							save += command + _T(" ");
					}	}

					if ( !command.IsEmpty()) {
						save += GetWordAddress( dbv.ptszVal, i );
						switch ( command[0] ) {
						case 'M':
							CallChatEvent( WINDOW_HIDDEN, (LPARAM)&gce);
							break;
						case 'X':
							CallChatEvent( WINDOW_MAXIMIZE, (LPARAM)&gce);
							break;
						default:
							CallChatEvent( SESSION_INITDONE, (LPARAM)&gce);
							break;
						}
					}
					else CallChatEvent( SESSION_INITDONE, (LPARAM)&gce);

					if (save.IsEmpty())
						db_unset(NULL, m_szModuleName, "JTemp");
					else
						setTString("JTemp", save.c_str());
					db_free(&dbv);
				}
				else CallChatEvent( SESSION_INITDONE, (LPARAM)&gce);

				gcd.iType = GC_EVENT_CONTROL;
				gce.pDest = &gcd;
				CallChatEvent( SESSION_ONLINE, (LPARAM)&gce);
	}	}	}

	sNamesList = _T("");
	ShowMessage( pmsg );
	return true;
}

bool CIrcProto::OnIrc_INITIALTOPIC(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 2) {
		AddWindowItemData(pmsg->parameters[1].c_str(), 0, 0, 0, pmsg->parameters[2].c_str());
		sTopic = pmsg->parameters[1] + _T(" ") + pmsg->parameters[2];
		sTopicName = _T("");
		sTopicTime = _T("");
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_INITIALTOPICNAME(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 3) {
		TCHAR tTimeBuf[128], *tStopStr;
		time_t ttTopicTime;
		sTopicName = pmsg->parameters[2];
		ttTopicTime = _tcstol(pmsg->parameters[3].c_str(), &tStopStr, 10);
		_tcsftime(tTimeBuf, 128, _T("%#c"), localtime(&ttTopicTime));
		sTopicTime = tTimeBuf;
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_TOPIC(const CIrcMessage* pmsg)
{
	if (pmsg->parameters.getCount() > 1 && pmsg->m_bIncoming) {
		DoEvent(GC_EVENT_TOPIC, pmsg->parameters[0].c_str(), pmsg->prefix.sNick.c_str(), pmsg->parameters[1].c_str(), NULL, sTopicTime.IsEmpty() ? NULL : sTopicTime.c_str(), NULL, true, false);
		AddWindowItemData(pmsg->parameters[0].c_str(), 0, 0, 0, pmsg->parameters[1].c_str());
	}
	ShowMessage(pmsg);
	return true;
}

static void __stdcall sttShowDlgList(void* param)
{
	CIrcProto *ppro = (CIrcProto*)param;
	if (ppro->m_listDlg == NULL) {
		ppro->m_listDlg = new CListDlg(ppro);
		ppro->m_listDlg->Show();
	}
	SetEvent(ppro->m_evWndCreate);
}

bool CIrcProto::OnIrc_LISTSTART(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming) {
		CallFunctionAsync(sttShowDlgList, this);
		WaitForSingleObject(m_evWndCreate, INFINITE);
		m_channelNumber = 0;
	}

	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_LIST(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming == 1 && m_listDlg && pmsg->parameters.getCount() > 2) {
		m_channelNumber++;
		LVITEM lvItem;
		HWND hListView = GetDlgItem(m_listDlg->GetHwnd(), IDC_INFO_LISTVIEW);
		lvItem.iItem = ListView_GetItemCount(hListView);
		lvItem.mask = LVIF_TEXT | LVIF_PARAM;
		lvItem.iSubItem = 0;
		lvItem.pszText = (TCHAR*)pmsg->parameters[1].c_str();
		lvItem.lParam = lvItem.iItem;
		lvItem.iItem = ListView_InsertItem(hListView, &lvItem);
		lvItem.mask = LVIF_TEXT;
		lvItem.iSubItem = 1;
		lvItem.pszText = (TCHAR*)pmsg->parameters[pmsg->parameters.getCount() - 2].c_str();
		ListView_SetItem(hListView, &lvItem);

		TCHAR* temp = mir_tstrdup(pmsg->parameters[pmsg->parameters.getCount() - 1]);
		TCHAR* find = _tcsstr(temp, _T("[+"));
		TCHAR* find2 = _tcsstr(temp, _T("]"));
		TCHAR* save = temp;
		if (find == temp && find2 != NULL && find + 8 >= find2) {
			temp = _tcsstr(temp, _T("]"));
			if (lstrlen(temp) > 1) {
				temp++;
				temp[0] = '\0';
				lvItem.iSubItem = 2;
				lvItem.pszText = save;
				ListView_SetItem(hListView, &lvItem);
				temp[0] = ' ';
				temp++;
			}
			else temp = save;
		}

		lvItem.iSubItem = 3;
		CMString S = DoColorCodes(temp, TRUE, FALSE);
		lvItem.pszText = (TCHAR*)S.c_str();
		ListView_SetItem(hListView, &lvItem);
		temp = save;
		mir_free(temp);

		int percent = 100;
		if (m_noOfChannels > 0)
			percent = (int)(m_channelNumber * 100) / m_noOfChannels;

		TCHAR text[100];
		if (percent < 100)
			mir_sntprintf(text, SIZEOF(text), TranslateT("Downloading list (%u%%) - %u channels"), percent, m_channelNumber);
		else
			mir_sntprintf(text, SIZEOF(text), TranslateT("Downloading list - %u channels"), m_channelNumber);
		m_listDlg->m_status.SetText(text);
	}

	return true;
}

bool CIrcProto::OnIrc_LISTEND(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && m_listDlg) {
		EnableWindow(GetDlgItem(m_listDlg->GetHwnd(), IDC_JOIN), true);
		ListView_SetSelectionMark(GetDlgItem(m_listDlg->GetHwnd(), IDC_INFO_LISTVIEW), 0);
		ListView_SetColumnWidth(GetDlgItem(m_listDlg->GetHwnd(), IDC_INFO_LISTVIEW), 1, LVSCW_AUTOSIZE);
		ListView_SetColumnWidth(GetDlgItem(m_listDlg->GetHwnd(), IDC_INFO_LISTVIEW), 2, LVSCW_AUTOSIZE);
		ListView_SetColumnWidth(GetDlgItem(m_listDlg->GetHwnd(), IDC_INFO_LISTVIEW), 3, LVSCW_AUTOSIZE);
		m_listDlg->UpdateList();

		TCHAR text[100];
		mir_sntprintf(text, SIZEOF(text), TranslateT("Done: %u channels"), m_channelNumber);
		int percent = 100;
		if (m_noOfChannels > 0)
			percent = (int)(m_channelNumber * 100) / m_noOfChannels;
		if (percent < 70) {
			lstrcat(text, _T(" "));
			lstrcat(text, TranslateT("(probably truncated by server)"));
		}
		SetDlgItemText(m_listDlg->GetHwnd(), IDC_TEXT, text);
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_BANLIST(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 2) {
		if (m_managerDlg->GetHwnd() && (
			m_managerDlg->m_radio1.GetState() && pmsg->sCommand == _T("367") ||
			m_managerDlg->m_radio2.GetState() && pmsg->sCommand == _T("346") ||
			m_managerDlg->m_radio3.GetState() && pmsg->sCommand == _T("348")) &&
			!m_managerDlg->m_radio1.Enabled() && !m_managerDlg->m_radio2.Enabled() && !m_managerDlg->m_radio3.Enabled()) {
			CMString S = pmsg->parameters[2];
			if (pmsg->parameters.getCount() > 3) {
				S += _T("   - ");
				S += pmsg->parameters[3];
				if (pmsg->parameters.getCount() > 4) {
					S += _T(" -  ( ");
					time_t time = StrToInt(pmsg->parameters[4].c_str());
					S += _tctime(&time);
					ReplaceString(S, _T("\n"), _T(" "));
					S += _T(")");
				}
			}

			SendDlgItemMessage(m_managerDlg->GetHwnd(), IDC_LIST, LB_ADDSTRING, 0, (LPARAM)S.c_str());
		}
	}

	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_BANLISTEND(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 1) {
		if (m_managerDlg->GetHwnd() &&
			(m_managerDlg->m_radio1.GetState() && pmsg->sCommand == _T("368")
			|| m_managerDlg->m_radio2.GetState() && pmsg->sCommand == _T("347")
			|| m_managerDlg->m_radio3.GetState() && pmsg->sCommand == _T("349")) &&
			!m_managerDlg->m_radio1.Enabled() && !m_managerDlg->m_radio2.Enabled() && !m_managerDlg->m_radio3.Enabled()) {
			if (strchr(sChannelModes.c_str(), 'b'))
				m_managerDlg->m_radio1.Enable();
			if (strchr(sChannelModes.c_str(), 'I'))
				m_managerDlg->m_radio2.Enable();
			if (strchr(sChannelModes.c_str(), 'e'))
				m_managerDlg->m_radio3.Enable();
			if (!IsDlgButtonChecked(m_managerDlg->GetHwnd(), IDC_NOTOP))
				m_managerDlg->m_add.Enable();
		}
	}

	ShowMessage(pmsg);
	return true;
}

static void __stdcall sttShowWhoisWnd(void* param)
{
	CIrcMessage* pmsg = (CIrcMessage*)param;
	CIrcProto *ppro = (CIrcProto*)pmsg->m_proto;
	if (ppro->m_whoisDlg == NULL) {
		ppro->m_whoisDlg = new CWhoisDlg(ppro);
		ppro->m_whoisDlg->Show();
	}
	SetEvent(ppro->m_evWndCreate);

	ppro->m_whoisDlg->ShowMessage(pmsg);
	delete pmsg;
}

bool CIrcProto::OnIrc_WHOIS_NAME(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 5 && m_manualWhoisCount > 0) {
		CallFunctionAsync(sttShowWhoisWnd, new CIrcMessage(*pmsg));
		WaitForSingleObject(m_evWndCreate, INFINITE);
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_CHANNELS(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && m_whoisDlg && pmsg->parameters.getCount() > 2 && m_manualWhoisCount > 0)
		m_whoisDlg->m_InfoChannels.SetText(pmsg->parameters[2].c_str());
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_AWAY(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && m_whoisDlg && pmsg->parameters.getCount() > 2 && m_manualWhoisCount > 0)
		m_whoisDlg->m_InfoAway2.SetText(pmsg->parameters[2].c_str());
	if (m_manualWhoisCount < 1 && pmsg->m_bIncoming && pmsg->parameters.getCount() > 2)
		WhoisAwayReply = pmsg->parameters[2];
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_OTHER(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && m_whoisDlg && pmsg->parameters.getCount() > 2 && m_manualWhoisCount > 0) {
		TCHAR temp[1024], temp2[1024];
		m_whoisDlg->m_InfoOther.GetText(temp, 1000);
		lstrcat(temp, _T("%s\r\n"));
		mir_sntprintf(temp2, 1020, temp, pmsg->parameters[2].c_str());
		m_whoisDlg->m_InfoOther.SetText(temp2);
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_END(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 1 && m_manualWhoisCount < 1) {
		CONTACT user = { (TCHAR*)pmsg->parameters[1].c_str(), NULL, NULL, false, false, true };
		HANDLE hContact = CList_FindContact(&user);
		if (hContact)
			ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)WhoisAwayReply.c_str());
	}

	m_manualWhoisCount--;
	if (m_manualWhoisCount < 0)
		m_manualWhoisCount = 0;
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_IDLE(const CIrcMessage* pmsg)
{
	int D = 0;
	int H = 0;
	int M = 0;
	int S = 0;
	if (pmsg->m_bIncoming && m_whoisDlg && pmsg->parameters.getCount() > 2 && m_manualWhoisCount > 0) {
		S = StrToInt(pmsg->parameters[2].c_str());
		D = S / (60 * 60 * 24);
		S -= (D * 60 * 60 * 24);
		H = S / (60 * 60);
		S -= (H * 60 * 60);
		M = S / 60;
		S -= (M * 60);

		TCHAR temp[100];
		if (D)
			mir_sntprintf(temp, 99, _T("%ud, %uh, %um, %us"), D, H, M, S);
		else if (H)
			mir_sntprintf(temp, 99, _T("%uh, %um, %us"), H, M, S);
		else if (M)
			mir_sntprintf(temp, 99, _T("%um, %us"), M, S);
		else if (S)
			mir_sntprintf(temp, 99, _T("%us"), S);
		else
			temp[0] = 0;

		TCHAR temp3[256];
		TCHAR tTimeBuf[128], *tStopStr;
		time_t ttTime = _tcstol(pmsg->parameters[3].c_str(), &tStopStr, 10);
		_tcsftime(tTimeBuf, 128, _T("%c"), localtime(&ttTime));
		mir_sntprintf(temp3, SIZEOF(temp3), _T("online since %s, idle %s"), tTimeBuf, temp);
		m_whoisDlg->m_AwayTime.SetText(temp3);
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_SERVER(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && m_whoisDlg && pmsg->parameters.getCount() > 2 && m_manualWhoisCount > 0)
		m_whoisDlg->m_InfoServer.SetText(pmsg->parameters[2].c_str());
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_AUTH(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && m_whoisDlg && pmsg->parameters.getCount() > 2 && m_manualWhoisCount > 0) {
		if (pmsg->sCommand == _T("330"))
			m_whoisDlg->m_InfoAuth.SetText(pmsg->parameters[2].c_str());
		else if (pmsg->parameters[2] == _T("is an identified user") || pmsg->parameters[2] == _T("is a registered nick"))
			m_whoisDlg->m_InfoAuth.SetText(pmsg->parameters[2].c_str());
		else
			OnIrc_WHOIS_OTHER(pmsg);
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHOIS_NO_USER(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 2 && !IsChannel(pmsg->parameters[1])) {
		if (m_whoisDlg)
			m_whoisDlg->ShowMessageNoUser(pmsg);

		CONTACT user = { (TCHAR*)pmsg->parameters[1].c_str(), NULL, NULL, false, false, false };
		HANDLE hContact = CList_FindContact(&user);
		if (hContact) {
			AddOutgoingMessageToDB(hContact, (TCHAR*)((CMString)_T("> ") + pmsg->parameters[2] + (CMString)_T(": ") + pmsg->parameters[1]).c_str());

			DBVARIANT dbv;
			if (!getTString(hContact, "Default", &dbv)) {
				setTString(hContact, "Nick", dbv.ptszVal);

				DBVARIANT dbv2;
				if (getByte(hContact, "AdvancedMode", 0) == 0)
					DoUserhostWithReason(1, ((CMString)_T("S") + dbv.ptszVal).c_str(), true, dbv.ptszVal);
				else {
					if (!getTString(hContact, "UWildcard", &dbv2)) {
						DoUserhostWithReason(2, ((CMString)_T("S") + dbv2.ptszVal).c_str(), true, dbv2.ptszVal);
						db_free(&dbv2);
					}
					else DoUserhostWithReason(2, ((CMString)_T("S") + dbv.ptszVal).c_str(), true, dbv.ptszVal);
				}
				setString(hContact, "User", "");
				setString(hContact, "Host", "");
				db_free(&dbv);
			}
		}
	}

	ShowMessage(pmsg);
	return true;
}

static void __stdcall sttShowNickWnd(void* param)
{
	CIrcMessage* pmsg = (CIrcMessage*)param;
	CIrcProto *ppro = pmsg->m_proto;
	if (ppro->m_nickDlg == NULL) {
		ppro->m_nickDlg = new CNickDlg(ppro);
		ppro->m_nickDlg->Show();
	}
	SetEvent(ppro->m_evWndCreate);
	SetWindowText(GetDlgItem(ppro->m_nickDlg->GetHwnd(), IDC_CAPTION), TranslateT("Change nickname"));
	SetWindowText(GetDlgItem(ppro->m_nickDlg->GetHwnd(), IDC_TEXT), pmsg->parameters.getCount() > 2 ? pmsg->parameters[2].c_str() : _T(""));
	ppro->m_nickDlg->m_Enick.SetText(pmsg->parameters[1].c_str());
	ppro->m_nickDlg->m_Enick.SendMsg(CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
	delete pmsg;
}

bool CIrcProto::OnIrc_NICK_ERR(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming) {
		if (nickflag && ((m_alternativeNick[0] != 0)) && (pmsg->parameters.getCount() > 2 && _tcscmp(pmsg->parameters[1].c_str(), m_alternativeNick))) {
			TCHAR m[200];
			mir_sntprintf(m, SIZEOF(m), _T("NICK %s"), m_alternativeNick);
			if (IsConnected())
				SendIrcMessage(m);
		}
		else {
			CallFunctionAsync(sttShowNickWnd, new CIrcMessage(*pmsg));
			WaitForSingleObject(m_evWndCreate, INFINITE);
		}
	}

	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_JOINERROR(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming) {
		DBVARIANT dbv;
		if (!getTString("JTemp", &dbv)) {
			CMString command = _T("a");
			CMString save = _T("");
			int i = 0;

			while (!command.IsEmpty()) {
				command = GetWord(dbv.ptszVal, i);
				i++;

				if (!command.IsEmpty() && pmsg->parameters[0] == command.Mid(1, command.GetLength()))
					save += command + _T(" ");
			}

			db_free(&dbv);

			if (save.IsEmpty())
				db_unset(NULL, m_szModuleName, "JTemp");
			else
				setTString("JTemp", save.c_str());
		}
	}

	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_UNKNOWN(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 0) {
		if (pmsg->parameters[0] == _T("WHO") && GetNextUserhostReason(2) != _T("U"))
			return true;
		if (pmsg->parameters[0] == _T("USERHOST") && GetNextUserhostReason(1) != _T("U"))
			return true;
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_ENDMOTD(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && !bPerformDone)
		DoOnConnect(pmsg);
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_NOOFCHANNELS(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 1)
		m_noOfChannels = StrToInt(pmsg->parameters[1].c_str());

	if (pmsg->m_bIncoming && !bPerformDone)
		DoOnConnect(pmsg);

	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_ERROR(const CIrcMessage* pmsg)
{
	if (pmsg->m_bIncoming && !m_disableErrorPopups && m_iDesiredStatus != ID_STATUS_OFFLINE) {
		MIRANDASYSTRAYNOTIFY msn;
		msn.cbSize = sizeof(MIRANDASYSTRAYNOTIFY);
		msn.szProto = m_szModuleName;
		msn.tszInfoTitle = TranslateT("IRC error");

		CMString S;
		if (pmsg->parameters.getCount() > 0)
			S = DoColorCodes(pmsg->parameters[0].c_str(), TRUE, FALSE);
		else
			S = TranslateT("Unknown");

		msn.tszInfo = (TCHAR*)S.c_str();
		msn.dwInfoFlags = NIIF_ERROR | NIIF_INTERN_UNICODE;
		msn.uTimeout = 15000;
		CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&msn);
	}
	ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHO_END(const CIrcMessage* pmsg)
{
	CMString command = GetNextUserhostReason(2);
	if (command[0] == 'S') {
		if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 1) {
			// is it a channel?
			if (IsChannel(pmsg->parameters[1])) {
				CMString S;
				CMString User = GetWord(m_whoReply.c_str(), 0);
				while (!User.IsEmpty()) {
					if (GetWord(m_whoReply.c_str(), 3)[0] == 'G') {
						S += User;
						S += _T("\t");
						DoEvent(GC_EVENT_SETCONTACTSTATUS, pmsg->parameters[1].c_str(), User.c_str(), NULL, NULL, NULL, ID_STATUS_AWAY, FALSE, FALSE);
					}
					else DoEvent(GC_EVENT_SETCONTACTSTATUS, pmsg->parameters[1].c_str(), User.c_str(), NULL, NULL, NULL, ID_STATUS_ONLINE, FALSE, FALSE);

					CMString SS = GetWordAddress(m_whoReply.c_str(), 4);
					if (SS.IsEmpty())
						break;
					m_whoReply = SS;
					User = GetWord(m_whoReply.c_str(), 0);
				}

				DoEvent(GC_EVENT_SETSTATUSEX, pmsg->parameters[1].c_str(), NULL, S.IsEmpty() ? NULL : S.c_str(), NULL, NULL, GC_SSE_TABDELIMITED, FALSE, FALSE);
				return true;
			}

			/// if it is not a channel
			TCHAR* UserList = mir_tstrdup(m_whoReply.c_str());
			const TCHAR* p1 = UserList;
			m_whoReply = _T("");
			CONTACT user = { (TCHAR*)pmsg->parameters[1].c_str(), NULL, NULL, false, true, false };
			HANDLE hContact = CList_FindContact(&user);

			if (hContact && getByte(hContact, "AdvancedMode", 0) == 1) {
				DBVARIANT dbv1, dbv2, dbv3, dbv4, dbv5, dbv6, dbv7;
				TCHAR *DBDefault = NULL, *DBNick = NULL, *DBWildcard = NULL;
				TCHAR *DBUser = NULL, *DBHost = NULL, *DBManUser = NULL, *DBManHost = NULL;
				if (!getTString(hContact, "Default", &dbv1)) DBDefault = dbv1.ptszVal;
				if (!getTString(hContact, "Nick", &dbv2)) DBNick = dbv2.ptszVal;
				if (!getTString(hContact, "UWildcard", &dbv3)) DBWildcard = dbv3.ptszVal;
				if (!getTString(hContact, "UUser", &dbv4)) DBUser = dbv4.ptszVal;
				if (!getTString(hContact, "UHost", &dbv5)) DBHost = dbv5.ptszVal;
				if (!getTString(hContact, "User", &dbv6)) DBManUser = dbv6.ptszVal;
				if (!getTString(hContact, "Host", &dbv7)) DBManHost = dbv7.ptszVal;
				if (DBWildcard)
					CharLower(DBWildcard);

				CMString nick;
				CMString user;
				CMString host;
				CMString away = GetWord(p1, 3);

				while (!away.IsEmpty()) {
					nick = GetWord(p1, 0);
					user = GetWord(p1, 1);
					host = GetWord(p1, 2);
					if ((DBWildcard && WCCmp(DBWildcard, nick.c_str()) || DBNick && !lstrcmpi(DBNick, nick.c_str()) || DBDefault && !lstrcmpi(DBDefault, nick.c_str()))
						&& (WCCmp(DBUser, user.c_str()) && WCCmp(DBHost, host.c_str()))) {
						if (away[0] == 'G' && getWord(hContact, "Status", ID_STATUS_OFFLINE) != ID_STATUS_AWAY)
							setWord(hContact, "Status", ID_STATUS_AWAY);
						else if (away[0] == 'H' && getWord(hContact, "Status", ID_STATUS_OFFLINE) != ID_STATUS_ONLINE)
							setWord(hContact, "Status", ID_STATUS_ONLINE);

						if ((DBNick && lstrcmpi(nick.c_str(), DBNick)) || !DBNick)
							setTString(hContact, "Nick", nick.c_str());
						if ((DBManUser && lstrcmpi(user.c_str(), DBManUser)) || !DBManUser)
							setTString(hContact, "User", user.c_str());
						if ((DBManHost && lstrcmpi(host.c_str(), DBManHost)) || !DBManHost)
							setTString(hContact, "Host", host.c_str());

						goto LBL_Exit;
					}
					p1 = GetWordAddress(p1, 4);
					away = GetWord(p1, 3);
				}

				if (DBWildcard && DBNick && !WCCmp(CharLower(DBWildcard), CharLower(DBNick))) {
					setTString(hContact, "Nick", DBDefault);

					DoUserhostWithReason(2, ((CMString)_T("S") + DBWildcard).c_str(), true, DBWildcard);

					setString(hContact, "User", "");
					setString(hContact, "Host", "");
					goto LBL_Exit;
				}

				if (getWord(hContact, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) {
					setWord(hContact, "Status", ID_STATUS_OFFLINE);
					setTString(hContact, "Nick", DBDefault);
					setString(hContact, "User", "");
					setString(hContact, "Host", "");
				}
LBL_Exit:
				if (DBDefault)  db_free(&dbv1);
				if (DBNick)     db_free(&dbv2);
				if (DBWildcard) db_free(&dbv3);
				if (DBUser)     db_free(&dbv4);
				if (DBHost)     db_free(&dbv5);
				if (DBManUser)  db_free(&dbv6);
				if (DBManHost)  db_free(&dbv7);
			}
			mir_free(UserList);
		}
	}
	else ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_WHO_REPLY(const CIrcMessage* pmsg)
{
	CMString command = PeekAtReasons(2);
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 6 && command[0] == 'S') {
		m_whoReply.AppendFormat(_T("%s %s %s %s "), pmsg->parameters[5].c_str(), pmsg->parameters[2].c_str(), pmsg->parameters[3].c_str(), pmsg->parameters[6].c_str());
		if (lstrcmpi(pmsg->parameters[5].c_str(), m_info.sNick.c_str()) == 0) {
			TCHAR host[1024];
			lstrcpyn(host, pmsg->parameters[3].c_str(), 1024);
			ForkThread(&CIrcProto::ResolveIPThread, new IPRESOLVE(_T2A(host), IP_AUTO));
		}
	}

	if (command[0] == 'U')
		ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_TRYAGAIN(const CIrcMessage* pmsg)
{
	CMString command = _T("");
	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 1) {
		if (pmsg->parameters[1] == _T("WHO"))
			command = GetNextUserhostReason(2);

		if (pmsg->parameters[1] == _T("USERHOST"))
			command = GetNextUserhostReason(1);
	}
	if (command[0] == 'U')
		ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_USERHOST_REPLY(const CIrcMessage* pmsg)
{
	CMString command = _T("");
	if (pmsg->m_bIncoming) {
		command = GetNextUserhostReason(1);
		if (!command.IsEmpty() && command != _T("U") && pmsg->parameters.getCount() > 1) {
			CONTACT finduser = { NULL, NULL, NULL, false, false, false };
			TCHAR* p1 = NULL;
			TCHAR* p2 = NULL;
			int awaystatus = 0;
			CMString sTemp;
			CMString host;
			CMString user;
			CMString nick;
			CMString mask;
			CMString mess;
			CMString channel;
			int i;
			int j;

			// Status-check pre-processing: Setup check-list
			OBJLIST<CMString> checklist(10);
			if (command[0] == 'S') {
				j = 0;
				sTemp = GetWord(command.c_str(), 0);
				sTemp.Delete(0, 1);
				while (!sTemp.IsEmpty()) {
					checklist.insert(new CMString(sTemp));
					j++;
					sTemp = GetWord(command.c_str(), j);
				}
			}

			// Cycle through results
			j = 0;
			sTemp = GetWord(pmsg->parameters[1].c_str(), j);
			while (!sTemp.IsEmpty()) {
				p1 = mir_tstrdup(sTemp.c_str());
				p2 = p1;

				// Pull out host, user and nick
				p2 = _tcschr(p1, '@');
				if (p2) {
					*p2 = '\0';
					p2++;
					host = p2;
				}
				p2 = _tcschr(p1, '=');
				if (p2) {
					if (*(p2 - 1) == '*')
						*(p2 - 1) = '\0';  //  remove special char for IRCOps
					*p2 = '\0';
					p2++;
					awaystatus = *p2;
					p2++;
					user = p2;
					nick = p1;
				}
				mess = _T("");
				mask = nick + _T("!") + user + _T("@") + host;
				if (host.IsEmpty() || user.IsEmpty() || nick.IsEmpty()) {
					mir_free(p1);
					continue;
				}

				// Do command
				switch (command[0]) {
				case 'S':	// Status check
				{
									finduser.name = (TCHAR*)nick.c_str();
									finduser.host = (TCHAR*)host.c_str();
									finduser.user = (TCHAR*)user.c_str();

									HANDLE hContact = CList_FindContact(&finduser);
									if (hContact && getByte(hContact, "AdvancedMode", 0) == 0) {
										setWord(hContact, "Status", awaystatus == '-' ? ID_STATUS_AWAY : ID_STATUS_ONLINE);
										setTString(hContact, "User", user.c_str());
										setTString(hContact, "Host", host.c_str());
										setTString(hContact, "Nick", nick.c_str());

										// If user found, remove from checklist
										for (i = 0; i < checklist.getCount(); i++)
										if (!lstrcmpi(checklist[i].c_str(), nick.c_str()))
											checklist.remove(i);
									}
									break;
				}
				case 'I':	// m_ignore
					mess = _T("/IGNORE %question=\"");
					mess += TranslateT("Please enter the hostmask (nick!user@host)\nNOTE! Contacts on your contact list are never ignored");
					mess += (CMString)_T("\",\"") + TranslateT("Ignore") + _T("\",\"*!*@") + host + _T("\"");
					if (m_ignoreChannelDefault)
						mess += _T(" +qnidcm");
					else
						mess += _T(" +qnidc");
					break;

				case 'J':	// Unignore
					mess = _T("/UNIGNORE *!*@") + host;
					break;

				case 'B':	// Ban
					channel = (command.c_str() + 1);
					mess = _T("/MODE ") + channel + _T(" +b *!*@") + host;
					break;

				case 'K':	// Ban & Kick
					channel = (command.c_str() + 1);
					mess.Format(_T("/MODE %s +b *!*@%s%%newl/KICK %s %s"), channel.c_str(), host.c_str(), channel.c_str(), nick.c_str());
					break;

				case 'L':	// Ban & Kick with reason
					channel = (command.c_str() + 1);
					mess.Format(_T("/MODE %s +b *!*@%s%%newl/KICK %s %s %%question=\"%s\",\"%s\",\"%s\""),
						channel.c_str(), host.c_str(), channel.c_str(), nick.c_str(),
						TranslateT("Please enter the reason"), TranslateT("Ban'n Kick"), TranslateT("Jerk"));
					break;
				}

				mir_free(p1);

				// Post message
				if (!mess.IsEmpty())
					PostIrcMessageWnd(NULL, NULL, mess.c_str());
				j++;
				sTemp = GetWord(pmsg->parameters[1].c_str(), j);
			}

			// Status-check post-processing: make buddies in ckeck-list offline
			if (command[0] == 'S') {
				for (i = 0; i < checklist.getCount(); i++) {
					finduser.name = (TCHAR*)checklist[i].c_str();
					finduser.ExactNick = true;
					CList_SetOffline(&finduser);
				}
			}

			return true;
		}
	}

	if (!pmsg->m_bIncoming || command == _T("U"))
		ShowMessage(pmsg);
	return true;
}

bool CIrcProto::OnIrc_SUPPORT(const CIrcMessage* pmsg)
{
	static const TCHAR* lpszFmt = _T("Try server %99[^ ,], port %19s");
	TCHAR szAltServer[100];
	TCHAR szAltPort[20];
	if (pmsg->parameters.getCount() > 1 && _stscanf(pmsg->parameters[1].c_str(), lpszFmt, &szAltServer, &szAltPort) == 2) {
		ShowMessage(pmsg);
		lstrcpynA(m_serverName, _T2A(szAltServer), 99);
		lstrcpynA(m_portStart, _T2A(szAltPort), 9);

		m_noOfChannels = 0;
		ConnectToServer();
		return true;
	}

	if (pmsg->m_bIncoming && !bPerformDone)
		DoOnConnect(pmsg);

	if (pmsg->m_bIncoming && pmsg->parameters.getCount() > 0) {
		CMString S;
		for (int i = 0; i < pmsg->parameters.getCount(); i++) {
			TCHAR* temp = mir_tstrdup(pmsg->parameters[i].c_str());
			if (_tcsstr(temp, _T("CHANTYPES="))) {
				TCHAR* p1 = _tcschr(temp, '=');
				p1++;
				if (lstrlen(p1) > 0)
					sChannelPrefixes = p1;
			}
			if (_tcsstr(temp, _T("CHANMODES="))) {
				TCHAR* p1 = _tcschr(temp, '=');
				p1++;
				if (lstrlen(p1) > 0)
					sChannelModes = (char*)_T2A(p1);
			}
			if (_tcsstr(temp, _T("PREFIX="))) {
				TCHAR* p1 = _tcschr(temp, '(');
				TCHAR* p2 = _tcschr(temp, ')');
				if (p1 && p2) {
					p1++;
					if (p1 != p2)
						sUserModes = (char*)_T2A(p1);
					sUserModes = sUserModes.Mid(0, p2 - p1);
					p2++;
					if (*p2 != '\0')
						sUserModePrefixes = p2;
				}
				else {
					p1 = _tcschr(temp, '=');
					p1++;
					sUserModePrefixes = p1;
					for (int i = 0; i < sUserModePrefixes.GetLength() + 1; i++) {
						if (sUserModePrefixes[i] == '@')
							sUserModes.SetAt(i, 'o');
						else if (sUserModePrefixes[i] == '+')
							sUserModes.SetAt(i, 'v');
						else if (sUserModePrefixes[i] == '-')
							sUserModes.SetAt(i, 'u');
						else if (sUserModePrefixes[i] == '%')
							sUserModes.SetAt(i, 'h');
						else if (sUserModePrefixes[i] == '!')
							sUserModes.SetAt(i, 'a');
						else if (sUserModePrefixes[i] == '*')
							sUserModes.SetAt(i, 'q');
						else if (sUserModePrefixes[i] == '\0')
							sUserModes.SetAt(i, '\0');
						else
							sUserModes.SetAt(i, '_');
					}
				}
			}

			mir_free(temp);
		}
	}

	ShowMessage(pmsg);
	return true;
}

void CIrcProto::OnIrcDefault(const CIrcMessage* pmsg)
{
	ShowMessage(pmsg);
}

void CIrcProto::OnIrcDisconnected()
{
	m_statusMessage = _T("");
	db_unset(NULL, m_szModuleName, "JTemp");
	bTempDisableCheck = false;
	bTempForceCheck = false;
	m_iTempCheckTime = 0;

	m_myHost[0] = '\0';

	int Temp = m_iStatus;
	KillIdent();
	KillChatTimer(OnlineNotifTimer);
	KillChatTimer(OnlineNotifTimer3);
	KillChatTimer(KeepAliveTimer);
	KillChatTimer(InitTimer);
	KillChatTimer(IdentTimer);
	m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
	ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)Temp, ID_STATUS_OFFLINE);

	CMString sDisconn = _T("\0035\002");
	sDisconn += TranslateT("*Disconnected*");
	DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, sDisconn.c_str(), NULL, NULL, NULL, true, false);

	GCDEST gcd = { m_szModuleName, 0, GC_EVENT_CONTROL };
	GCEVENT gce = { sizeof(gce), &gcd };
	CallChatEvent(SESSION_OFFLINE, (LPARAM)&gce);

	if (!Miranda_Terminated())
		CList_SetAllOffline(m_disconnectDCCChats);

	// restore the original nick, cause it might be changed
	memcpy(m_nick, m_pNick, sizeof(m_nick));
	setTString("Nick", m_pNick);

	CLISTMENUITEM mi = { sizeof(mi) };
	mi.flags = CMIM_FLAGS | CMIF_GRAYED;
	Menu_ModifyItem(hMenuJoin, &mi);
	Menu_ModifyItem(hMenuList, &mi);
	Menu_ModifyItem(hMenuNick, &mi);
}

/////////////////////////////////////////////////////////////////////////////////////////
// OnConnect

static void __stdcall sttMainThrdOnConnect(void* param)
{
	CIrcProto *ppro = (CIrcProto*)param;

	ppro->SetChatTimer(ppro->InitTimer, 1 * 1000, TimerProc);
	if (ppro->m_identTimer)
		ppro->SetChatTimer(ppro->IdentTimer, 60 * 1000, IdentTimerProc);
	if (ppro->m_sendKeepAlive)
		ppro->SetChatTimer(ppro->KeepAliveTimer, 60 * 1000, KeepAliveTimerProc);
	if (ppro->m_autoOnlineNotification && !ppro->bTempDisableCheck || ppro->bTempForceCheck) {
		ppro->SetChatTimer(ppro->OnlineNotifTimer, 1000, OnlineNotifTimerProc);
		if (ppro->m_channelAwayNotification)
			ppro->SetChatTimer(ppro->OnlineNotifTimer3, 3000, OnlineNotifTimerProc3);
	}
}

bool CIrcProto::DoOnConnect(const CIrcMessage*)
{
	bPerformDone = true;
	nickflag = true;

	CLISTMENUITEM mi = { sizeof(mi) };
	mi.flags = CMIM_FLAGS;
	Menu_ModifyItem(hMenuJoin, &mi);
	Menu_ModifyItem(hMenuList, &mi);
	Menu_ModifyItem(hMenuNick, &mi);

	int Temp = m_iStatus;
	m_iStatus = ID_STATUS_ONLINE;
	ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)Temp, m_iStatus);

	if (m_iDesiredStatus == ID_STATUS_AWAY)
		PostIrcMessage(_T("/AWAY %s"), m_statusMessage.Mid(0, 450).c_str());

	if (m_perform) {
		DoPerform("ALL NETWORKS");
		if (IsConnected()) {
			DoPerform(_T2A(m_info.sNetwork.c_str()));
			switch (m_iStatus) {
				case ID_STATUS_FREECHAT:   DoPerform("Event: Free for chat");   break;
				case ID_STATUS_ONLINE:     DoPerform("Event: Available");       break;
			}
		}
	}

	if (m_rejoinChannels) {
		int count = CallServiceSync(MS_GC_GETSESSIONCOUNT, 0, (LPARAM)m_szModuleName);
		for (int i = 0; i < count; i++) {
			GC_INFO gci = { 0 };
			gci.Flags = BYINDEX | DATA | NAME | TYPE;
			gci.iItem = i;
			gci.pszModule = m_szModuleName;
			if (!CallServiceSync(MS_GC_GETINFO, 0, (LPARAM)&gci) && gci.iType == GCW_CHATROOM) {
				CHANNELINFO *wi = (CHANNELINFO*)gci.dwItemData;
				if (wi && wi->pszPassword)
					PostIrcMessage(_T("/JOIN %s %s"), gci.pszName, wi->pszPassword);
				else
					PostIrcMessage(_T("/JOIN %s"), gci.pszName);
			}
		}
	}

	DoEvent(GC_EVENT_ADDGROUP, SERVERWINDOW, NULL, NULL, _T("Normal"), NULL, NULL, FALSE, TRUE);
	{
		GCDEST gcd = { m_szModuleName, SERVERWINDOW, GC_EVENT_CONTROL };
		GCEVENT gce = { sizeof(gce), &gcd };
		CallChatEvent(SESSION_ONLINE, (LPARAM)&gce);
	}

	CallFunctionAsync(sttMainThrdOnConnect, this);
	nickflag = false;
	return 0;
}

static void __cdecl AwayWarningThread(LPVOID)
{
	MessageBox(NULL, TranslateT("The usage of /AWAY in your perform buffer is restricted\n as IRC sends this command automatically."), TranslateT("IRC Error"), MB_OK);
}

int CIrcProto::DoPerform(const char* event)
{
	String sSetting = String("PERFORM:") + event;
	sSetting.MakeUpper();

	DBVARIANT dbv;
	if (!getTString(sSetting.c_str(), &dbv)) {
		if (!my_strstri(dbv.ptszVal, _T("/away")))
			PostIrcMessageWnd(NULL, NULL, dbv.ptszVal);
		else
			mir_forkthread(AwayWarningThread, NULL);
		db_free(&dbv);
		return 1;
	}
	return 0;
}

int CIrcProto::IsIgnored(const CMString& nick, const CMString& address, const CMString& host, char type)
{
	return IsIgnored(nick + _T("!") + address + _T("@") + host, type);
}

int CIrcProto::IsIgnored(CMString user, char type)
{
	for (int i = 0; i < m_ignoreItems.getCount(); i++) {
		const CIrcIgnoreItem& C = m_ignoreItems[i];

		if (type == '\0')
		if (!lstrcmpi(user.c_str(), C.mask.c_str()))
			return i + 1;

		bool bUserContainsWild = (_tcschr(user.c_str(), '*') != NULL || _tcschr(user.c_str(), '?') != NULL);
		if (!bUserContainsWild && WCCmp(C.mask.c_str(), user.c_str()) ||
			bUserContainsWild && !lstrcmpi(user.c_str(), C.mask.c_str())) {
			if (C.flags.IsEmpty() || C.flags[0] != '+')
				continue;

			if (!_tcschr(C.flags.c_str(), type))
				continue;

			if (C.network.IsEmpty())
				return i + 1;

			if (IsConnected() && !lstrcmpi(C.network.c_str(), m_info.sNetwork.c_str()))
				return i + 1;
		}
	}

	return 0;
}

bool CIrcProto::AddIgnore(const TCHAR* mask, const TCHAR* flags, const TCHAR* network)
{
	RemoveIgnore(mask);
	m_ignoreItems.insert(new CIrcIgnoreItem(mask, (_T("+") + CMString(flags)).c_str(), network));
	RewriteIgnoreSettings();

	if (m_ignoreDlg)
		m_ignoreDlg->RebuildList();
	return true;
}

bool CIrcProto::RemoveIgnore(const TCHAR* mask)
{
	int idx;
	while ((idx = IsIgnored(mask, '\0')) != 0)
		m_ignoreItems.remove(idx - 1);

	RewriteIgnoreSettings();

	if (m_ignoreDlg)
		m_ignoreDlg->RebuildList();
	return true;
}