From 3b55a62fdcb1f8222de3c2c8fbed530792c419a0 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Fri, 12 Oct 2012 14:53:57 +0000 Subject: GTalkExt, ICQ, IRC, Jabber: folders restructurization git-svn-id: http://svn.miranda-ng.org/main/trunk@1890 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/IRCG/src/irclib.cpp | 1505 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1505 insertions(+) create mode 100644 protocols/IRCG/src/irclib.cpp (limited to 'protocols/IRCG/src/irclib.cpp') diff --git a/protocols/IRCG/src/irclib.cpp b/protocols/IRCG/src/irclib.cpp new file mode 100644 index 0000000000..c018b57a02 --- /dev/null +++ b/protocols/IRCG/src/irclib.cpp @@ -0,0 +1,1505 @@ +/* +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. +*/ +#include "irc.h" + +#define DCCCHATTIMEOUT 300 +#define DCCSENDTIMEOUT 120 + +using namespace irc; + +int CDccSession::nDcc = 0; + +static int CompareHandlers( const CIrcHandler* p1, const CIrcHandler* p2 ) +{ + return lstrcmp( p1->m_name, p2->m_name ); +} + +OBJLIST CIrcProto::m_handlers( 30, CompareHandlers ); + +//////////////////////////////////////////////////////////////////// + +CIrcMessage::CIrcMessage( CIrcProto* _pro, const TCHAR* lpszCmdLine, int codepage, bool bIncoming, bool bNotify ) : + m_proto( _pro ), + m_bIncoming( bIncoming ), + m_bNotify( bNotify ), + m_codePage( codepage ), + parameters( 10 ) +{ + ParseIrcCommand(lpszCmdLine); +} + +CIrcMessage::CIrcMessage(const CIrcMessage& m) : + sCommand( m.sCommand ), + m_bIncoming( m.m_bIncoming ), + m_bNotify( m.m_bNotify ), + m_codePage( m.m_codePage ), + m_proto( m.m_proto ), + parameters( m.parameters.getCount()) +{ + prefix.sNick = m.prefix.sNick; + prefix.sUser = m.prefix.sUser; + prefix.sHost = m.prefix.sHost; + + for ( int i=0; i < m.parameters.getCount(); i++ ) + parameters.insert( new CMString( m.parameters[i] )); +} + +CIrcMessage::~CIrcMessage() +{ +} + +void CIrcMessage::Reset() +{ + prefix.sNick = prefix.sUser = prefix.sHost = sCommand = _T(""); + m_bIncoming = false; + m_bNotify = true; + + parameters.destroy(); +} + +CIrcMessage& CIrcMessage::operator = (const CIrcMessage& m) +{ + if ( &m != this ) { + sCommand = m.sCommand; + parameters = m.parameters; + prefix.sNick = m.prefix.sNick; + prefix.sUser = m.prefix.sUser; + prefix.sHost = m.prefix.sHost; + m_bIncoming = m.m_bIncoming; + m_bNotify = m.m_bNotify; + } + return *this; +} + +CIrcMessage& CIrcMessage::operator = (const TCHAR* lpszCmdLine) +{ + Reset(); + ParseIrcCommand(lpszCmdLine); + return *this; +} + +void CIrcMessage::ParseIrcCommand(const TCHAR* lpszCmdLine) +{ + const TCHAR* p1 = lpszCmdLine; + const TCHAR* p2 = lpszCmdLine; + + // prefix exists ? + if ( *p1 == ':' ) { + // break prefix into its components (nick!user@host) + p2 = ++p1; + while( *p2 && !_tcschr( _T(" !"), *p2 )) + ++p2; + prefix.sNick.SetString(p1, p2 - p1); + if ( *p2 != '!' ) + goto end_of_prefix; + p1 = ++p2; + while( *p2 && !_tcschr( _T(" @"), *p2 )) + ++p2; + prefix.sUser.SetString(p1, p2 - p1); + if ( *p2 != '@' ) + goto end_of_prefix; + p1 = ++p2; + while( *p2 && *p2 != ' ' ) + ++p2; + prefix.sHost.SetString(p1, p2 - p1); +end_of_prefix : + while( *p2 && *p2 == ' ' ) + ++p2; + p1 = p2; + } + + // get command + p2 = p1; + while( *p2 && *p2 != ' ' ) + ++p2; + + sCommand.SetString(p1, p2 - p1); + sCommand.MakeUpper(); + while( *p2 && *p2 == ' ' ) + ++p2; + p1 = p2; + + // get parameters + while( *p1 ) { + if ( *p1 == ':' ) { + ++p1; + + // seek end-of-message + while( *p2 ) + ++p2; + parameters.insert( new CMString(p1, p2 - p1)); + break; + } + else { + // seek end of parameter + while( *p2 && *p2 != ' ' ) + ++p2; + parameters.insert( new CMString(p1, p2 - p1)); + // see next parameter + while( *p2 && *p2 == ' ' ) + ++p2; + p1 = p2; +} } } + +//////////////////////////////////////////////////////////////////// + +int CIrcProto::getCodepage() const +{ + return ( con != NULL ) ? codepage : CP_ACP; +} + +void CIrcProto::SendIrcMessage( const TCHAR* msg, bool bNotify, int codepage ) +{ + if ( codepage == -1 ) + codepage = getCodepage(); + + if ( this ) { + char* str = mir_t2a_cp( msg, codepage ); + rtrim( str ); + int cbLen = (int)strlen( str ); + str = ( char* )mir_realloc( str, cbLen+3 ); + strcat( str, "\r\n" ); + NLSend(( const BYTE* )str, cbLen+2 ); + mir_free( str ); + + if ( bNotify ) { + CIrcMessage ircMsg( this, msg, codepage ); + if ( !ircMsg.sCommand.IsEmpty() && ircMsg.sCommand != _T("QUIT")) + Notify( &ircMsg ); +} } } + +bool CIrcProto::Connect(const CIrcSessionInfo& info) +{ + codepage = m_codepage; + + NETLIBOPENCONNECTION ncon = { 0 }; + ncon.cbSize = sizeof(ncon); + ncon.szHost = info.sServer.c_str(); + ncon.wPort = info.iPort; + con = (HANDLE) CallService( MS_NETLIB_OPENCONNECTION, (WPARAM) hNetlib, (LPARAM) & ncon); + if (con == NULL) { + TCHAR szTemp[300]; + mir_sntprintf(szTemp, SIZEOF(szTemp), _T("\0035%s \002%s\002 (") _T(TCHAR_STR_PARAM) _T(": %u)."), + TranslateT("Failed to connect to"), si.sNetwork.c_str(), si.sServer.c_str(), si.iPort); + DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, NULL, szTemp, NULL, NULL, NULL, true, false); + return false; + } + + FindLocalIP(con); // get the local ip used for filetransfers etc + + if ( info.m_iSSL > 0 ) { + if ( !CallService( MS_NETLIB_STARTSSL, ( WPARAM ) con, 0 ) && info.m_iSSL == 2 ) { + Netlib_CloseHandle( con ); + con = NULL; + m_info.Reset(); + return false; + } } + + if ( Miranda_Terminated()) { + Disconnect(); + return false; + } + + m_info = info; + + // start receiving messages from host + ircFork( &CIrcProto::ThreadProc, NULL ); + Sleep( 100 ); + if ( info.sPassword.GetLength()) + NLSend( "PASS %s\r\n", info.sPassword.c_str()); + NLSend( _T("NICK %s\r\n"), info.sNick.c_str()); + + CMString m_userID = GetWord(info.sUserID.c_str(), 0); + TCHAR szHostName[MAX_PATH]; + DWORD cbHostName = SIZEOF( szHostName ); + GetComputerName(szHostName, &cbHostName); + CMString HostName = GetWord(szHostName, 0); + if ( m_userID.IsEmpty()) + m_userID = _T("Miranda"); + if ( HostName.IsEmpty()) + HostName= _T("host"); + NLSend( _T("USER %s %s %s :%s\r\n"), m_userID.c_str(), HostName.c_str(), _T("server"), info.sFullName.c_str()); + + return con != NULL; +} + +void CIrcProto::Disconnect(void) +{ + static const DWORD dwServerTimeout = 5 * 1000; + + if ( con == NULL ) + return; + + KillIdent(); + + if ( m_quitMessage && m_quitMessage[0] ) + NLSend( _T("QUIT :%s\r\n"), m_quitMessage); + else + NLSend( "QUIT \r\n" ); + + Sleep(50); + + if ( con ) + Netlib_Shutdown(con); + + m_info.Reset(); + return; +} + +void CIrcProto::Notify(const CIrcMessage* pmsg) +{ + OnIrcMessage(pmsg); +} + +int CIrcProto::NLSend( const unsigned char* buf, int cbBuf) +{ + if ( m_bMbotInstalled && m_scriptingEnabled ) { + int iVal = NULL; + char * pszTemp = 0; + pszTemp = ( char* ) mir_alloc( lstrlenA((const char *) buf ) + 1); + lstrcpynA(pszTemp, (const char *)buf, lstrlenA ((const char *)buf) + 1); + + if ( Scripting_TriggerMSPRawOut(&pszTemp) && pszTemp ) { + if (con) + iVal = Netlib_Send(con, (const char*)pszTemp, lstrlenA(pszTemp), MSG_DUMPASTEXT); + } + if ( pszTemp ) + mir_free( pszTemp ); + + return iVal; + } + + if (con) + return Netlib_Send(con, (const char*)buf, cbBuf, MSG_DUMPASTEXT); + + return 0; +} + +int CIrcProto::NLSend( const TCHAR* fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + + TCHAR szBuf[1024*4]; + mir_vsntprintf(szBuf, SIZEOF(szBuf), fmt, marker); + va_end(marker); + + char* buf = mir_t2a_cp( szBuf, getCodepage()); + int result = NLSend((unsigned char*)buf, (int)strlen(buf)); + mir_free( buf ); + return result; +} + +int CIrcProto::NLSend( const char* fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + + char szBuf[1024*4]; + int cbLen = mir_vsnprintf(szBuf, SIZEOF(szBuf), fmt, marker); + va_end(marker); + + return NLSend((unsigned char*)szBuf, cbLen ); +} + +int CIrcProto::NLSendNoScript( const unsigned char* buf, int cbBuf) +{ + if ( con ) + return Netlib_Send(con, (const char*)buf, cbBuf, MSG_DUMPASTEXT); + + return 0; +} + +int CIrcProto::NLReceive(unsigned char* buf, int cbBuf) +{ + return Netlib_Recv( con, (char*)buf, cbBuf, MSG_DUMPASTEXT ); +} + +void CIrcProto::KillIdent() +{ + if ( hBindPort ) { + HANDLE hPort = hBindPort; + hBindPort = NULL; + Netlib_CloseHandle( hPort ); + } +} + +void CIrcProto::InsertIncomingEvent(TCHAR* pszRaw) +{ + CIrcMessage msg( this, pszRaw, true); + Notify( &msg ); + return; +} + +void CIrcProto::createMessageFromPchar( const char* p ) +{ + TCHAR* ptszMsg; + if ( codepage != CP_UTF8 && m_utfAutodetect ) { + if ( mir_utf8decodecp( NEWSTR_ALLOCA(p), codepage, &ptszMsg ) == NULL ) + ptszMsg = mir_a2t_cp( p, codepage ); + } + else ptszMsg = mir_a2t_cp( p, codepage ); + CIrcMessage msg( this, ptszMsg, codepage, true ); + Notify( &msg ); + mir_free( ptszMsg ); +} + +void CIrcProto::DoReceive() +{ + char chBuf[1024*4+1]; + int cbInBuf = 0; + + if ( m_info.bIdentServer && m_info.iIdentServerPort != NULL ) { + NETLIBBIND nb = {0}; + nb.cbSize = sizeof(NETLIBBIND); + nb.pfnNewConnectionV2 = DoIdent; + nb.pExtra = this; + nb.wPort = m_info.iIdentServerPort; + hBindPort = (HANDLE)CallService( MS_NETLIB_BINDPORT, (WPARAM)hNetlib,(LPARAM) &nb); + if ( !hBindPort || nb.wPort != m_info.iIdentServerPort ) { + DoNetlibLog("Error: unable to bind local port %u", m_info.iIdentServerPort); + KillIdent(); + } } + + while( con ) { + int nLinesProcessed = 0; + + int cbRead = NLReceive((unsigned char*)chBuf+cbInBuf, sizeof(chBuf)-cbInBuf-1); + if ( cbRead <= 0 ) + break; + + cbInBuf += cbRead; + chBuf[cbInBuf] = '\0'; + + char* pStart = chBuf; + while( *pStart ) { + char* pEnd; + + // seek end-of-line + for(pEnd=pStart; *pEnd && *pEnd != '\r' && *pEnd != '\n'; ++pEnd) + ; + if ( *pEnd == '\0' ) + break; // uncomplete message. stop parsing. + + ++nLinesProcessed; + + // replace end-of-line with NULLs and skip + while( *pEnd == '\r' || *pEnd == '\n' ) + *pEnd++ = '\0'; + + // process single message by monitor objects + if ( *pStart ) { + if ( m_bMbotInstalled && m_scriptingEnabled ) { + char* pszTemp = mir_strdup( pStart ); + + if ( Scripting_TriggerMSPRawIn( &pszTemp ) && pszTemp ) { + char* p1 = pszTemp; + // replace end-of-line with NULLs + while( *p1 != '\0' ) { + if ( *p1 == '\r' || *p1 == '\n') + *p1 = '\0'; + p1++; + } + + createMessageFromPchar( pszTemp ); + } + + mir_free( pszTemp ); + } + else createMessageFromPchar( pStart ); + } + + cbInBuf -= pEnd - pStart; + pStart = pEnd; + } + + // discard processed messages + if ( nLinesProcessed != 0 ) + memmove(chBuf, pStart, cbInBuf+1); + } + + if ( con ) { + Netlib_CloseHandle(con); + con = NULL; + } + + // notify monitor objects that the connection has been closed + Notify(NULL); +} + +void __cdecl CIrcProto::ThreadProc( void* ) +{ + DoReceive(); + m_info.Reset(); +} + +void CIrcProto::AddDCCSession(HANDLE, CDccSession* dcc) +{ + EnterCriticalSection(&m_dcc); + + CDccSession* p = m_dcc_chats.find(dcc); + if ( p ) + m_dcc_chats.remove( p ); + + m_dcc_chats.insert( dcc ); + + LeaveCriticalSection(&m_dcc); +} + +void CIrcProto::AddDCCSession(DCCINFO*, CDccSession* dcc) +{ + EnterCriticalSection(&m_dcc); + + m_dcc_xfers.insert( dcc ); + + LeaveCriticalSection(&m_dcc); +} + +void CIrcProto::RemoveDCCSession(HANDLE hContact) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_chats.getCount(); i++ ) + if ( m_dcc_chats[i]->di->hContact == hContact ) { + m_dcc_chats.remove( i ); + break; + } + + LeaveCriticalSection(&m_dcc); +} + +void CIrcProto::RemoveDCCSession(DCCINFO* pdci) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_xfers.getCount(); i++ ) + if ( m_dcc_xfers[i]->di == pdci ) { + m_dcc_xfers.remove( i ); + break; + } + + LeaveCriticalSection(&m_dcc); +} + +CDccSession* CIrcProto::FindDCCSession(HANDLE hContact) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_chats.getCount(); i++ ) + if ( m_dcc_chats[i]->di->hContact == hContact ) { + LeaveCriticalSection(&m_dcc); + return m_dcc_chats[ i ]; + } + + LeaveCriticalSection(&m_dcc); + return 0; +} + +CDccSession* CIrcProto::FindDCCSession(DCCINFO* pdci) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_xfers.getCount(); i++ ) + if ( m_dcc_xfers[i]->di == pdci ) { + LeaveCriticalSection(&m_dcc); + return m_dcc_xfers[ i ]; + } + + LeaveCriticalSection(&m_dcc); + return 0; +} + +CDccSession* CIrcProto::FindDCCSendByPort(int iPort) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_xfers.getCount(); i++ ) { + CDccSession* p = m_dcc_xfers[i]; + if ( p->di->iType == DCC_SEND && p->di->bSender && iPort == p->di->iPort ) { + LeaveCriticalSection(&m_dcc); + return p; + } + } + + LeaveCriticalSection(&m_dcc); + return 0; +} + +CDccSession* CIrcProto::FindDCCRecvByPortAndName(int iPort, const TCHAR* szName) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_xfers.getCount(); i++ ) { + CDccSession* p = m_dcc_xfers[i]; + DBVARIANT dbv; + if ( !getTString(p->di->hContact, "Nick", &dbv)) { + if ( p->di->iType == DCC_SEND && !p->di->bSender && !lstrcmpi( szName, dbv.ptszVal) && iPort == p->di->iPort ) { + DBFreeVariant(&dbv); + LeaveCriticalSection(&m_dcc); + return p; + } + DBFreeVariant(&dbv); + } + } + + LeaveCriticalSection(&m_dcc); + return 0; +} + +CDccSession* CIrcProto::FindPassiveDCCSend(int iToken) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_xfers.getCount(); i++ ) { + if ( m_dcc_xfers[ i ]->iToken == iToken ) { + LeaveCriticalSection(&m_dcc); + return m_dcc_xfers[ i ]; + } + } + + LeaveCriticalSection(&m_dcc); + return 0; +} + +CDccSession* CIrcProto::FindPassiveDCCRecv(CMString sName, CMString sToken) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_xfers.getCount(); i++ ) { + CDccSession* p = m_dcc_xfers[i]; + if ( sToken == p->di->sToken && sName == p->di->sContactName ) { + LeaveCriticalSection(&m_dcc); + return p; + } + } + LeaveCriticalSection(&m_dcc); + return 0; +} + +void CIrcProto::DisconnectAllDCCSessions(bool Shutdown) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_chats.getCount(); i++ ) + if ( m_disconnectDCCChats || Shutdown ) + m_dcc_chats[i]->Disconnect(); + + LeaveCriticalSection(&m_dcc); +} + +void CIrcProto::CheckDCCTimeout(void) +{ + EnterCriticalSection(&m_dcc); + + for ( int i=0; i < m_dcc_chats.getCount(); i++ ) { + CDccSession* p = m_dcc_chats[i]; + if ( time(0) > p->tLastActivity + DCCCHATTIMEOUT ) + p->Disconnect(); + } + + for ( int j=0; j < m_dcc_xfers.getCount(); j++ ) { + CDccSession* p = m_dcc_xfers[j]; + if ( time(0) > p->tLastActivity + DCCSENDTIMEOUT ) + p->Disconnect(); + } + + LeaveCriticalSection(&m_dcc); +} + +//////////////////////////////////////////////////////////////////// + +CIrcIgnoreItem::CIrcIgnoreItem( const TCHAR* _mask, const TCHAR* _flags, const TCHAR* _network ) : + mask( _mask ), + flags( _flags ), + network( _network ) +{ +} + +CIrcIgnoreItem::CIrcIgnoreItem( int codepage, const char* _mask, const char* _flags, const char* _network ) : + mask( (TCHAR *)_A2T( _mask, codepage )), + flags( (TCHAR *)_A2T( _flags, codepage )), + network( (TCHAR *)_A2T( _network, codepage )) +{ +} + +CIrcIgnoreItem::~CIrcIgnoreItem() +{ +} + +//////////////////////////////////////////////////////////////////// + +CIrcSessionInfo::CIrcSessionInfo() : + iPort(0), + bIdentServer(false), + iIdentServerPort(0) +{ +} + +CIrcSessionInfo::CIrcSessionInfo(const CIrcSessionInfo& si) : + sServer(si.sServer), + sServerName(si.sServerName), + iPort(si.iPort), + sNick(si.sNick), + sUserID(si.sUserID), + sFullName(si.sFullName), + sPassword(si.sPassword), + bIdentServer(si.bIdentServer), + m_iSSL(si.m_iSSL), + sIdentServerType(si.sIdentServerType), + sNetwork(si.sNetwork), + iIdentServerPort(si.iIdentServerPort) +{ +} + +void CIrcSessionInfo::Reset() +{ + sServer = ""; + sServerName = _T(""); + iPort = 0; + sNick = _T(""); + sUserID = _T(""); + sFullName = _T(""); + sPassword = ""; + bIdentServer = false; + bNickFlag = false; + m_iSSL = 0; + sIdentServerType = _T(""); + iIdentServerPort = 0; + sNetwork = _T(""); +} + +//////////////////////////////////////////////////////////////////// + +void CIrcProto::OnIrcMessage(const CIrcMessage* pmsg) +{ + if ( pmsg != NULL ) { + PfnIrcMessageHandler pfn = FindMethod( pmsg->sCommand.c_str()); + if ( pfn ) { + // call member function. if it returns 'false', + // call the default handling + __try + { + if ( !(this->*pfn)( pmsg )) + OnIrcDefault( pmsg ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) // dedicated to Sava :) + { + DoNetlibLog( "IRC handler feels sick: %S", pmsg->sCommand.c_str()); + } + } + else // handler not found. call default handler + OnIrcDefault( pmsg ); + } + else OnIrcDisconnected(); +} + +PfnIrcMessageHandler CIrcProto::FindMethod(const TCHAR* lpszName) +{ + CIrcHandler temp( lpszName, NULL ); + CIrcHandler* p = m_handlers.find( &temp ); + return ( p == NULL ) ? NULL : p->m_handler; +} + +//////////////////////////////////////////////////////////////////// + +#define IPC_ADDR_SIZE 4 /* Size of IP address, change for IPv6. */ + +char* ConvertIntegerToIP(unsigned long int_ip_addr) +{ + IN_ADDR intemp; + IN_ADDR in; + intemp.S_un.S_addr = int_ip_addr; + + in.S_un.S_un_b.s_b1 = intemp.S_un.S_un_b.s_b4; + in.S_un.S_un_b.s_b2 = intemp.S_un.S_un_b.s_b3; + in.S_un.S_un_b.s_b3 = intemp.S_un.S_un_b.s_b2; + in.S_un.S_un_b.s_b4 = intemp.S_un.S_un_b.s_b1; + + return inet_ntoa( in ); +} + +unsigned long ConvertIPToInteger( char* IP ) +{ + IN_ADDR in; + IN_ADDR intemp; + + if ( IP == 0 || lstrlenA(IP) == 0) + return 0; + + intemp.S_un.S_addr = inet_addr(IP); + + in.S_un.S_un_b.s_b1 = intemp.S_un.S_un_b.s_b4; + in.S_un.S_un_b.s_b2 = intemp.S_un.S_un_b.s_b3; + in.S_un.S_un_b.s_b3 = intemp.S_un.S_un_b.s_b2; + in.S_un.S_un_b.s_b4 = intemp.S_un.S_un_b.s_b1; + return in.S_un.S_addr; +} + +//////////////////////////////////////////////////////////////////// + +// initialize basic stuff needed for the dcc objects, also start a timer for checking the status of connections (timeouts) +CDccSession::CDccSession( CIrcProto* _pro, DCCINFO* pdci ) : + m_proto( _pro ), + NewFileName(0), + dwWhatNeedsDoing(0), + tLastPercentageUpdate(0), + dwTotal(0), + iGlobalToken(), + dwResumePos(0), + hEvent(0), + con(0), + hBindPort(0) +{ + tLastActivity = time(0); + + di = pdci; // Setup values passed to the constructor + + ZeroMemory(&pfts, sizeof(PROTOFILETRANSFERSTATUS)); + pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); + + if(di->iType == DCC_SEND && di->bSender == false) + hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + if(nDcc == 0) + m_proto->SetChatTimer(m_proto->DCCTimer, 20*1000, DCCTimerProc); + + nDcc++; // increase the count of existing objects + + iGlobalToken++; + if(iGlobalToken == 1000) + iGlobalToken = 1; + iToken = iGlobalToken; + + iPacketSize = m_proto->getWord( "PacketSize", 4096 ); + + if ( di->dwAdr ) + m_proto->setDword(di->hContact, "IP", di->dwAdr); // mtooltip stuff +} + +CDccSession::~CDccSession() // destroy all that needs destroying +{ + if ( di->iType == DCC_SEND ) { + // ack SUCCESS or FAILURE + if (dwTotal == di->dwSize ) + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, (void *)di, 0); + else + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (void *)di, 0); + } + + if ( di->iType == DCC_CHAT ) { + CDccSession* dcc = m_proto->FindDCCSession(di->hContact); + if ( dcc && this == dcc ) { + m_proto->RemoveDCCSession(di->hContact); // objects automatically remove themselves from the list of objects + m_proto->setWord(di->hContact, "Status", ID_STATUS_OFFLINE); + } } + + if ( di->iType == DCC_SEND ) + m_proto->RemoveDCCSession( di ); + + if ( hEvent != NULL ) { + SetEvent( hEvent ); + CloseHandle( hEvent ); + hEvent = NULL; + } + + delete di; + nDcc--; + + if ( nDcc < 0 ) + nDcc = 0; + + if ( nDcc == 0 ) + m_proto->KillChatTimer( m_proto->DCCTimer ); // destroy the timer when no dcc objects remain +} + +int CDccSession::NLSend(const unsigned char* buf, int cbBuf) +{ + tLastActivity = time(0); + + if (con) + return Netlib_Send(con, (const char*)buf, cbBuf, di->iType == DCC_CHAT?MSG_DUMPASTEXT:MSG_NODUMP); + + return 0; +} + +int CDccSession::NLReceive(const unsigned char* buf, int cbBuf) +{ + int n = 0; + + if(con) + n = Netlib_Recv(con, (char*)buf, cbBuf, di->iType == DCC_CHAT?MSG_DUMPASTEXT:MSG_NODUMP); + + tLastActivity = time(0); + return n; +} + +int CDccSession::SendStuff(const TCHAR* fmt) +{ + String buf = _T2A( fmt, m_proto->getCodepage()); + return NLSend(( const unsigned char* )buf.c_str(), buf.GetLength()); +} + +// called when the user wants to connect/create a new connection given the parameters in the constructor. +int CDccSession::Connect() +{ + if ( !di->bSender || di->bReverse ) { + if ( !con ) + mir_forkthread( ConnectProc, this ); // spawn a new thread for time consuming activities, ie when connecting to a remote computer + return true; + } + + if ( !con ) + return SetupConnection(); // no need to spawn thread for setting up a listening port locally + + return false; +} + +void __cdecl CDccSession::ConnectProc( void *pparam ) +{ + CDccSession* pThis = (CDccSession*)pparam; + if ( !pThis->con ) + pThis->SetupConnection(); +} + +// small function to setup the address and port of the remote computer fror passive filetransfers +void CDccSession::SetupPassive(DWORD adress, DWORD port) +{ + di->dwAdr = adress; + di->iPort = (int)port; + + m_proto->setDword(di->hContact, "IP", di->dwAdr); // mtooltip stuff +} + +int CDccSession::SetupConnection() +{ + // if it is a dcc chat connection make sure it is "offline" to begin with, since no connection exists still + if ( di->iType == DCC_CHAT ) + m_proto->setWord(di->hContact, "Status", ID_STATUS_OFFLINE); + + // Set up stuff needed for the filetransfer dialog (if it is a filetransfer) + if ( di->iType == DCC_SEND ) { + file[0] = ( TCHAR* )di->sFileAndPath.c_str(); + file[1] = 0; + + pfts.tszCurrentFile = ( TCHAR* )di->sFileAndPath.c_str(); + pfts.tszWorkingDir = ( TCHAR* )di->sPath.c_str(); + pfts.hContact = di->hContact; + pfts.flags = PFTS_TCHAR + ((di->bSender) ? PFTS_SENDING : PFTS_RECEIVING); + pfts.totalFiles = 1; + pfts.currentFileNumber = 0; + pfts.totalBytes = di->dwSize; + pfts.currentFileSize = pfts.totalBytes; + pfts.ptszFiles = file; + pfts.totalProgress = 0; + pfts.currentFileProgress = 0; + pfts.currentFileTime = (unsigned long)time(0); + } + + // create a listening socket for outgoing chat/send requests. The remote computer connects to this computer. Used for both chat and filetransfer. + if ( di->bSender && !di->bReverse ) { + NETLIBBIND nb = {0}; + nb.cbSize = sizeof(NETLIBBIND); + nb.pfnNewConnectionV2 = DoIncomingDcc; // this is the (helper) function to be called once an incoming connection is made. The 'real' function that is called is IncomingConnection() + nb.pExtra = (void *)this; + nb.wPort = 0; + hBindPort = (HANDLE)CallService( MS_NETLIB_BINDPORT, (WPARAM)m_proto->hNetlibDCC,(LPARAM) &nb); + + if ( hBindPort == NULL ) { + delete this; // dcc objects destroy themselves when the connection has been closed or failed for some reasson. + return 0; + } + + di->iPort = nb.wPort; // store the port internally so it is possible to search for it (for resuming of filetransfers purposes) + return nb.wPort; // return the created port so it can be sent to the remote computer in a ctcp/dcc command + } + + // If a remote computer initiates a chat session this is used to connect to the remote computer (user already accepted at this point). + // also used for connecting to a remote computer for remote file transfers + if ( di->iType == DCC_CHAT && !di->bSender || di->iType == DCC_SEND && di->bSender && di->bReverse ) { + NETLIBOPENCONNECTION ncon = { 0 }; + ncon.cbSize = sizeof(ncon); + ncon.szHost = ConvertIntegerToIP(di->dwAdr); + ncon.wPort = (WORD) di->iPort; + con = (HANDLE) CallService( MS_NETLIB_OPENCONNECTION, (WPARAM)m_proto->hNetlibDCC, (LPARAM) & ncon); + } + + + // If a remote computer initiates a filetransfer this is used to connect to that computer (the user has chosen to accept but it is possible the file exists/needs to be resumed etc still) + if ( di->iType == DCC_SEND && !di->bSender ) { + + // this following code is for miranda to be able to show the resume/overwrite etc dialog if the file that we are receiving already exists. + // It must be done before we create the connection or else the other party will begin sending packets while the user is still deciding if + // s/he wants to resume/cancel or whatever. Just the way dcc is... + + // if the file exists (dialog is shown) WaitForSingleObject() till the dialog is closed and PS_FILERESUME has been processed. + // dwWhatNeedsDoing will be set using InterlockedExchange() (from other parts of the code depending on action) before SetEvent() is called. + // If the user has chosen rename then InterlockedExchange() will be used for setting NewFileName to a string containing the new name. + // Furthermore dwResumePos will be set using InterlockedExchange() to indicate what the file position to start from is. + if ( ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, (void *)di, (LPARAM)&pfts)) { + WaitForSingleObject( hEvent, INFINITE ); + switch( dwWhatNeedsDoing ) { + case FILERESUME_RENAME: + // If the user has chosen to rename the file we need to change variables accordingly. NewFileName has been set using + // InterlockedExchange() + if ( NewFileName) { // the user has chosen to rename the new incoming file. + di->sFileAndPath = NewFileName; + + int i = di->sFileAndPath.ReverseFind( '\\' ); + if ( i != -1 ) { + di->sPath = di->sFileAndPath.Mid(0, i+1); + di->sFile = di->sFileAndPath.Mid(i+1, di->sFileAndPath.GetLength()); + } + + pfts.tszCurrentFile = ( TCHAR* )di->sFileAndPath.c_str(); + pfts.tszWorkingDir = ( TCHAR* )di->sPath.c_str(); + pfts.totalBytes = di->dwSize; + pfts.currentFileSize = pfts.totalBytes; + + delete[] NewFileName; + NewFileName = NULL; + } + break; + + case FILERESUME_OVERWRITE: + case FILERESUME_RESUME : + // no action needed at this point, just break out of the switch statement + break; + + case FILERESUME_CANCEL : + return FALSE; + + case FILERESUME_SKIP : + default: + delete this; // per usual dcc objects destroy themselves when they fail or when connection is closed + return FALSE; + } } + + // hack for passive filetransfers + if ( di->iType == DCC_SEND && !di->bSender && di->bReverse ) { + NETLIBBIND nb = {0}; + nb.cbSize = sizeof(NETLIBBIND); + nb.pfnNewConnectionV2 = DoIncomingDcc; // this is the (helper) function to be called once an incoming connection is made. The 'real' function that is called is IncomingConnection() + nb.pExtra = (void *)this; + nb.wPort = 0; + hBindPort = (HANDLE)CallService( MS_NETLIB_BINDPORT, (WPARAM)m_proto->hNetlibDCC,(LPARAM) &nb); + + if ( hBindPort == NULL ) { + m_proto->DoEvent(GC_EVENT_INFORMATION, 0, m_proto->m_info.sNick.c_str(), LPGENT("DCC ERROR: Unable to bind local port for passive filetransfer"), NULL, NULL, NULL, true, false); + delete this; // dcc objects destroy themselves when the connection has been closed or failed for some reasson. + return 0; + } + + di->iPort = nb.wPort; // store the port internally so it is possible to search for it (for resuming of filetransfers purposes) + + CMString sFileWithQuotes = di->sFile; + + // if spaces in the filename surround with quotes + if ( sFileWithQuotes.Find( ' ', 0 ) != -1 ) { + sFileWithQuotes.Insert( 0, _T("\"")); + sFileWithQuotes.Insert( sFileWithQuotes.GetLength(), _T("\"")); + } + + // send out DCC RECV command for passive filetransfers + unsigned long ulAdr = 0; + if ( m_proto->m_manualHost ) + ulAdr = ConvertIPToInteger( m_proto->m_mySpecifiedHostIP ); + else + ulAdr = m_proto->m_IPFromServer ? ConvertIPToInteger( m_proto->m_myHost ) : nb.dwExternalIP; + + if ( di->iPort && ulAdr ) + m_proto->PostIrcMessage( _T("/CTCP %s DCC SEND %s %u %u %I64u %s"), di->sContactName.c_str(), sFileWithQuotes.c_str(), ulAdr, di->iPort, di->dwSize, di->sToken.c_str()); + + return TRUE; + } + + // connect to the remote computer from which you are receiving the file (now all actions to take (resume/overwrite etc) have been decided + NETLIBOPENCONNECTION ncon = { 0 }; + ncon.cbSize = sizeof(ncon); + ncon.szHost = ConvertIntegerToIP(di->dwAdr); + ncon.wPort = (WORD) di->iPort; + + con = (HANDLE) CallService( MS_NETLIB_OPENCONNECTION, (WPARAM)m_proto->hNetlibDCC, (LPARAM) & ncon); + } + + // if for some reason the plugin has failed to connect to the remote computer the object is destroyed. + if ( con == NULL ) { + delete this; + return FALSE; // failed to connect + } + + // if it is a chat connection set the user to online now since we now know there is a connection + if ( di->iType == DCC_CHAT ) + m_proto->setWord(di->hContact, "Status", ID_STATUS_ONLINE); + + // spawn a new thread to handle receiving/sending of data for the new chat/filetransfer connection to the remote computer + mir_forkthread( ThreadProc, this ); + + return con != NULL; +} + +// called by netlib for incoming connections on a listening socket (chat/filetransfer) +int CDccSession::IncomingConnection(HANDLE hConnection, DWORD dwIP) +{ + con = hConnection; + if ( con == NULL ) { + delete this; + return false; // failed to connect + } + + m_proto->setDword(di->hContact, "IP", dwIP); // mToolTip stuff + + if ( di->iType == DCC_CHAT ) + m_proto->setWord(di->hContact, "Status", ID_STATUS_ONLINE); // set chat to online + + // same as above, spawn a new thread to handle receiving/sending of data for the new incoming chat/filetransfer connection + mir_forkthread(ThreadProc, this ); + return true; +} + +// here we decide which function to use for communicating with the remote computer, depending on connection type +void __cdecl CDccSession::ThreadProc(void *pparam) +{ + CDccSession* pThis = (CDccSession*)pparam; + + // if it is an incoming connection on a listening port, then we should close the listenting port so only one can connect (the one you offered + // the connection to) can connect and not evil IRCopers with haxxored IRCDs + if ( pThis->hBindPort ) { + Netlib_CloseHandle(pThis->hBindPort); + pThis->hBindPort = NULL; + } + + if ( pThis->di->iType == DCC_CHAT ) + pThis->DoChatReceive(); // dcc chat + + else if ( !pThis->di->bSender ) + pThis->DoReceiveFile(); // receive a file + + else if ( pThis->di->bSender ) + pThis->DoSendFile(); // send a file +} + +// this is done when the user is initiating a filetransfer to a remote computer +void CDccSession::DoSendFile() +{ + // initialize the filetransfer dialog + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, (void *)di, 0); + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, (void *)di, 0); + + WORD wPacketSize = m_proto->getWord( "DCCPacketSize", 1024*4); + + if ( wPacketSize < 256 ) + wPacketSize = 256; + + if ( wPacketSize > 32 * 1024 ) + wPacketSize = 32 * 1024; + + BYTE* chBuf = new BYTE[wPacketSize+1]; + + // is there a connection? + if ( con ) { + // open the file for reading + int hFile = _topen( di->sFileAndPath.c_str(), _O_RDONLY | _O_BINARY, _S_IREAD); + if (hFile >= 0) { + unsigned __int64 dwLastAck = 0; + + // if the user has chosen to resume a file, dwResumePos will contain a value (set using InterlockedExchange()) + // and then the variables and the file pointer are changed accordingly. + if ( dwResumePos && dwWhatNeedsDoing == FILERESUME_RESUME ) { + _lseeki64(hFile, dwResumePos, SEEK_SET); + dwTotal = dwResumePos; + dwLastAck = dwResumePos; + pfts.totalProgress = dwResumePos; + pfts.currentFileProgress = dwResumePos; + } + + // initial ack to set the 'percentage-ready meter' to the correct value + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_DATA, (void *)di, (LPARAM) &pfts); + + tLastActivity = time(0); + + // create a packet receiver to handle receiving ack's from the remote computer. + HANDLE hPackrcver = (HANDLE)CallService( MS_NETLIB_CREATEPACKETRECVER, (WPARAM)con, (LPARAM)sizeof(DWORD)); + NETLIBPACKETRECVER npr; + npr.cbSize = sizeof(NETLIBPACKETRECVER); + npr.dwTimeout = 60*1000; + npr.bufferSize = sizeof(DWORD); + npr.bytesUsed = 0; + + // until the connection is dropped it will spin around in this while() loop + while ( con ) { + // read a packet + int iRead = _read(hFile, chBuf, wPacketSize); + if ( iRead <= 0 ) + break; // break out if everything has already been read + + // send the package + int cbSent = NLSend((unsigned char*)chBuf, iRead); + if ( cbSent <= 0 ) + break; // break out if connection is lost or a transmission error has occured + + if ( !con ) + break; + + dwTotal += cbSent; + + // block connection and receive ack's from remote computer (if applicable) + if ( m_proto->m_DCCMode == 0 ) { + DWORD dwRead = 0; + DWORD dwPacket = NULL; + + do { + dwRead = CallService( MS_NETLIB_GETMOREPACKETS, (WPARAM)hPackrcver, (LPARAM)&npr); + npr.bytesUsed = sizeof(DWORD); + + if ( dwRead <= 0) + break; // connection closed, or a timeout occurred. + + dwPacket = *(DWORD*)npr.buffer; + dwLastAck = ntohl(dwPacket); + + } + while(con && dwTotal != dwLastAck); + + if ( !con || dwRead <= 0 ) + goto DCC_STOP; + } + + if ( m_proto->m_DCCMode == 1 ) { + DWORD dwRead = 0; + DWORD dwPacket = 0; + + do { + dwRead = CallService( MS_NETLIB_GETMOREPACKETS, (WPARAM)hPackrcver, (LPARAM)&npr); + npr.bytesUsed = sizeof(DWORD); + if ( dwRead <= 0) + break; // connection closed, or a timeout occurred. + + dwPacket = *(DWORD*)npr.buffer; + dwLastAck = ntohl(dwPacket); + } + while(con && (di->dwSize != dwTotal + && dwTotal - dwLastAck >= 100*1024 + || di->dwSize == dwTotal // get the last packets when the whole file has been sent + && dwTotal != dwLastAck)); + + if ( !con || dwRead <= 0 ) + goto DCC_STOP; + } + + // update the filetransfer dialog's 'percentage-ready meter' once per second only to save cpu + if ( tLastPercentageUpdate < time(0)) { + tLastPercentageUpdate = time(0); + pfts.totalProgress = dwTotal; + pfts.currentFileProgress = dwTotal; + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_DATA, (void *)di, (LPARAM) &pfts); + } + + // close the connection once the whole file has been sent an completely ack'ed + if ( dwLastAck >= di->dwSize ) { + Netlib_CloseHandle(con); + con = NULL; + } } + +DCC_STOP: + // need to close the connection if it isn't allready + if ( con ) { + Netlib_CloseHandle(con); + con = NULL; + } + + // ack the progress one final time + tLastActivity = time(0); + pfts.totalProgress = dwTotal; + pfts.currentFileProgress = dwTotal; + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_DATA, (void *)di, (LPARAM) &pfts); + + _close(hFile); + } + else // file was not possible to open for reading + { + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (void *)di, 0); + if ( con ) { + Netlib_CloseHandle(con); + con = NULL; + } } } + + delete []chBuf; + delete this; // ... and hopefully all went right, cuz here the object is deleted in any case +} + +// This is called when receiving a file from a remote computer. +void CDccSession::DoReceiveFile() +{ + // initialize the filetransfer dialog + ProtoBroadcastAck( m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, (void *)di, 0); + + BYTE chBuf[1024*32+1]; + + // do some stupid thing so the filetransfer dialog shows the right thing + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, (void *)di, 0); + + // open the file for writing (and reading in case it is a resume) + int hFile = _topen( di->sFileAndPath.c_str(), + (dwWhatNeedsDoing == FILERESUME_RESUME ? _O_APPEND : _O_TRUNC | _O_CREAT) | _O_RDWR | _O_BINARY, + _S_IREAD | _S_IWRITE); + if ( hFile >= 0 ) { + unsigned __int64 dwLastAck = 0; + + // dwResumePos and dwWhatNeedsDoing has possibly been set using InterlockedExchange() + // if set it is a resume and we adjust variables and the file pointer accordingly. + if ( dwResumePos && dwWhatNeedsDoing == FILERESUME_RESUME ) { + _lseeki64(hFile, dwResumePos, SEEK_SET); + dwTotal = dwResumePos; + dwLastAck = dwResumePos; + pfts.totalProgress = dwResumePos; + pfts.currentFileProgress = dwResumePos; + } + + // send an initial ack for the percentage-ready meter + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_DATA, (void *)di, (LPARAM) &pfts); + + // the while loop will spin around till the connection is dropped, locally or by the remote computer. + while ( con ) { + // read + int cbRead = NLReceive((unsigned char*)chBuf, sizeof(chBuf)); + if ( cbRead <= 0 ) + break; + + // write it to the file + _write(hFile, chBuf, cbRead); + + dwTotal += cbRead; + + // this snippet sends out an ack for every 4 kb received in send ahead + // or every packet for normal mode + if ( !di->bTurbo ) { + DWORD no = dwTotal; + no = htonl(no); + NLSend((unsigned char *)&no, sizeof(DWORD)); + dwLastAck = dwTotal; + } + else dwLastAck = dwTotal; + + // sets the 'last update time' to check for timed out connections, and also make sure we only + // ack the 'percentage-ready meter' only once a second to save CPU. + if ( tLastPercentageUpdate < time( 0 )) { + tLastPercentageUpdate = time (0); + pfts.totalProgress = dwTotal; + pfts.currentFileProgress = dwTotal; + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_DATA, (void *)di, (LPARAM) &pfts); + } + + // if file size is known and everything is received then disconnect + if ( di->dwSize && di->dwSize == dwTotal ) { + Netlib_CloseHandle(con); + con = NULL; + } } + // receiving loop broken locally or by remote computer, just some cleaning up left.... + + pfts.totalProgress = dwTotal; + pfts.currentFileProgress = dwTotal; + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_DATA, (void *)di, (LPARAM) &pfts); + _close(hFile); + } + else { + ProtoBroadcastAck(m_proto->m_szModuleName, di->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (void *)di, 0); + if ( con ) { // file not possible to open for writing so we ack FAILURE and close the handle + Netlib_CloseHandle(con); + con = NULL; + } } + + delete this; // and finally the object is deleted +} + +// this function handles receiving text in dcc chats and then send it in the protochain. very uncomplicated... +// For sending text the SendStuff() function is called (with the help of some function in CIrcProto to find the right +// Dcc object). See CIrcProto for info on how the dcc objects are stored, retreived and deleted. + +void CDccSession::DoChatReceive() +{ + char chBuf[1024*4+1]; + int cbInBuf = 0; + + // loop to spin around while there is a connection + while( con ) { + int cbRead; + int nLinesProcessed = 0; + + cbRead = NLReceive((unsigned char*)chBuf+cbInBuf, sizeof(chBuf)-cbInBuf-1); + if ( cbRead <= 0 ) + break; + + cbInBuf += cbRead; + chBuf[cbInBuf] = '\0'; + + char* pStart = chBuf; + while( *pStart ) { + char* pEnd; + + // seek end-of-line + for(pEnd=pStart; *pEnd && *pEnd != '\r' && *pEnd != '\n'; ++pEnd) + ; + if ( *pEnd == '\0' ) + break; // uncomplete message. stop parsing. + + ++nLinesProcessed; + + // replace end-of-line with NULLs and skip + while( *pEnd == '\r' || *pEnd == '\n' ) + *pEnd++ = '\0'; + + if ( *pStart ) { + // send it off to some messaging module + + PROTORECVEVENT pre = {0}; + pre.timestamp = (DWORD)time(NULL); +// pre.szMessage = (char*)DoColorCodes((TCHAR*)pStart, true, false); //!!!! // remove color codes + pre.szMessage = pStart; + + CCSDATA ccs = {0}; + ccs.szProtoService = PSR_MESSAGE; + ccs.hContact = di->hContact; + ccs.lParam = (LPARAM) ⪯ + + CallService( MS_PROTO_CHAINRECV, 0, (LPARAM) & ccs); + } + + cbInBuf -= pEnd - pStart; + pStart = pEnd; + } + + // discard processed messages + if ( nLinesProcessed != 0 ) + memmove(chBuf, pStart, cbInBuf+1); + } + + delete this; // delete the object when the connection is dropped +} + +// disconnect the stuff +int CDccSession::Disconnect() +{ + if ( hBindPort ) { + Netlib_CloseHandle(hBindPort); + hBindPort = NULL; + } + + // if 'con' exists it is cuz a connection exists. + // Terminating 'con' will cause any spawned threads to die and then the object will destroy itself. + if ( con ) { + Netlib_CloseHandle(con); + con = NULL; + } + else delete this; // if 'con' do not exist (no connection made so far from the object) the object is destroyed + + return TRUE; +} + +//////////////////////////////////////////////////////////////////// +// check if the dcc chats should disconnect ( default 5 minute timeout ) + +VOID CALLBACK DCCTimerProc( HWND, UINT, UINT_PTR idEvent, DWORD ) +{ + CIrcProto* ppro = GetTimerOwner( idEvent ); + if ( ppro ) + ppro->CheckDCCTimeout(); +} + +// helper function for incoming dcc connections. +void DoIncomingDcc(HANDLE hConnection, DWORD dwRemoteIP, void * p1) +{ + CDccSession* dcc = (CDccSession*)p1; + dcc->IncomingConnection(hConnection, dwRemoteIP); +} + +// ident server + +void strdel( char* parBuffer, int len ) +{ + char* p; + for ( p = parBuffer+len; *p != 0; p++ ) + p[ -len ] = *p; + + p[ -len ] = '\0'; +} + +void DoIdent(HANDLE hConnection, DWORD, void* extra ) +{ + CIrcProto* ppro = ( CIrcProto* )extra; + + char szBuf[1024]; + int cbTotal = 0; + + for (;;) { + int cbRead = Netlib_Recv(hConnection, szBuf+cbTotal, sizeof(szBuf)-1-cbTotal, 0); + if ( cbRead == SOCKET_ERROR || cbRead == 0) + break; + + cbTotal += cbRead; + szBuf[cbTotal] = '\0'; + +LBL_Parse: + char* EOLPos = strstr(szBuf, "\r\n"); + if (EOLPos == NULL) + continue; + + EOLPos[0] = EOLPos[1] = '\0'; + rtrim( szBuf ); + ppro->DoNetlibLog("Got Ident request: %s", szBuf); + + unsigned int PeerPortNrRcvd = 0, LocalPortNrRcvd = 0; + int iParamCnt = sscanf( szBuf, "%d , %d", &LocalPortNrRcvd, &PeerPortNrRcvd ); + + int cbLen = 0; + char buf[1024*4]; + + if (iParamCnt != 2) + cbLen = mir_snprintf(buf, SIZEOF(buf), "%s : ERROR : UNKNOWN-ERROR\r\n", szBuf); + else + { + for (int i = 0; i < g_Instances.getCount(); i++) + { + if (PeerPortNrRcvd == g_Instances[i]->m_info.iPort && LocalPortNrRcvd == g_Instances[i]->m_myLocalPort) + { + cbLen = mir_snprintf(buf, SIZEOF(buf), "%s : USERID : " TCHAR_STR_PARAM " : " TCHAR_STR_PARAM "\r\n", + szBuf, g_Instances[i]->m_info.sIdentServerType.c_str() , g_Instances[i]->m_info.sUserID.c_str()); + break; + } + } + + if (cbLen == 0) + cbLen = mir_snprintf(buf, SIZEOF(buf), "%s : ERROR : INVALID-PORT\r\n", szBuf); + } + + if ( Netlib_Send(hConnection, (const char*)buf, cbLen, 0) > 0) + ppro->DoNetlibLog("Sent Ident answer: %s", buf); + else + ppro->DoNetlibLog("Sending Ident answer failed."); + + if ( ppro->m_identTimer ) + break; + + cbTotal -= EOLPos + 2 - szBuf; + strdel(szBuf, int(EOLPos + 2 - szBuf)); + goto LBL_Parse; + } + Netlib_CloseHandle(hConnection); +} -- cgit v1.2.3