From bf58c28e54bf5ec1894b47eb71ff897d6cfc2ba0 Mon Sep 17 00:00:00 2001 From: Rozhuk Ivan Date: Tue, 25 Nov 2014 21:38:47 +0000 Subject: IRC code cleanup req for review git-svn-id: http://svn.miranda-ng.org/main/trunk@11069 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/IRCG/src/irclib.cpp | 2935 ++++++++++++++++++++--------------------- 1 file changed, 1460 insertions(+), 1475 deletions(-) (limited to 'protocols/IRCG') diff --git a/protocols/IRCG/src/irclib.cpp b/protocols/IRCG/src/irclib.cpp index d824dbd080..cae41d4668 100644 --- a/protocols/IRCG/src/irclib.cpp +++ b/protocols/IRCG/src/irclib.cpp @@ -1,1475 +1,1460 @@ -/* -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)m_hNetlibUser, (LPARAM)&ncon); - if (con == NULL) { - TCHAR szTemp[300]; - mir_sntprintf(szTemp, SIZEOF(szTemp), _T("\0035%s \002%s\002 (%S: %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 - ForkThread(&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_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 (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)m_hNetlibUser, (LPARAM)&nb); - if (!hBindPort || nb.wPort != m_info.iIdentServerPort) { - debugLogA("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_scriptingEnabled) { - char* pszTemp = mir_strdup(pStart); - - if (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(MCONTACT, CDccSession* dcc) -{ - mir_cslock lck(m_dcc); - - CDccSession* p = m_dcc_chats.find(dcc); - if (p) - m_dcc_chats.remove(p); - - m_dcc_chats.insert(dcc); -} - -void CIrcProto::AddDCCSession(DCCINFO*, CDccSession* dcc) -{ - mir_cslock lck(m_dcc); - m_dcc_xfers.insert(dcc); -} - -void CIrcProto::RemoveDCCSession(MCONTACT hContact) -{ - mir_cslock lck(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; - } -} - -void CIrcProto::RemoveDCCSession(DCCINFO* pdci) -{ - mir_cslock lck(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; - } - } -} - -CDccSession* CIrcProto::FindDCCSession(MCONTACT hContact) -{ - mir_cslock lck(m_dcc); - - for (int i = 0; i < m_dcc_chats.getCount(); i++) - if (m_dcc_chats[i]->di->hContact == hContact) - return m_dcc_chats[i]; - - return 0; -} - -CDccSession* CIrcProto::FindDCCSession(DCCINFO* pdci) -{ - mir_cslock lck(m_dcc); - - for (int i = 0; i < m_dcc_xfers.getCount(); i++) - if (m_dcc_xfers[i]->di == pdci) - return m_dcc_xfers[i]; - - return 0; -} - -CDccSession* CIrcProto::FindDCCSendByPort(int iPort) -{ - mir_cslock lck(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) - return p; - } - - return 0; -} - -CDccSession* CIrcProto::FindDCCRecvByPortAndName(int iPort, const TCHAR* szName) -{ - mir_cslock lck(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) { - db_free(&dbv); - return p; - } - db_free(&dbv); - } - } - - return 0; -} - -CDccSession* CIrcProto::FindPassiveDCCSend(int iToken) -{ - mir_cslock lck(m_dcc); - - for (int i = 0; i < m_dcc_xfers.getCount(); i++) - if (m_dcc_xfers[i]->iToken == iToken) - return m_dcc_xfers[i]; - - return 0; -} - -CDccSession* CIrcProto::FindPassiveDCCRecv(CMString sName, CMString sToken) -{ - mir_cslock lck(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) - return p; - } - - return 0; -} - -void CIrcProto::DisconnectAllDCCSessions(bool Shutdown) -{ - mir_cslock lck(m_dcc); - - for (int i = 0; i < m_dcc_chats.getCount(); i++) - if (m_disconnectDCCChats || Shutdown) - m_dcc_chats[i]->Disconnect(); -} - -void CIrcProto::CheckDCCTimeout(void) -{ - mir_cslock lck(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(); - } -} - -//////////////////////////////////////////////////////////////////// - -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 :) - { - debugLogA("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 file transfer"), 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, 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, retrieved 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 = pStart; - ProtoChainRecvMsg(di->hContact, &pre); - } - - 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; - - while (true) { - 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->debugLogA("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 : %S : %S\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->debugLogA("Sent Ident answer: %s", buf); - else - ppro->debugLogA("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); -} +/* +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)m_hNetlibUser, (LPARAM)&ncon); + if (con == NULL) { + TCHAR szTemp[300]; + mir_sntprintf(szTemp, SIZEOF(szTemp), _T("\0035%s \002%s\002 (%S: %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 + ForkThread(&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 (!con || !buf) + return 0; + if (m_scriptingEnabled && cbBuf == 0) + cbBuf = lstrlenA((const char *)buf); + return Netlib_Send(con, (const char*)buf, cbBuf, MSG_DUMPASTEXT); +} + +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)m_hNetlibUser, (LPARAM)&nb); + if (!hBindPort || nb.wPort != m_info.iIdentServerPort) { + debugLogA("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_scriptingEnabled) { + char* pszTemp = mir_strdup(pStart); + + if (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(MCONTACT, CDccSession* dcc) +{ + mir_cslock lck(m_dcc); + + CDccSession* p = m_dcc_chats.find(dcc); + if (p) + m_dcc_chats.remove(p); + + m_dcc_chats.insert(dcc); +} + +void CIrcProto::AddDCCSession(DCCINFO*, CDccSession* dcc) +{ + mir_cslock lck(m_dcc); + m_dcc_xfers.insert(dcc); +} + +void CIrcProto::RemoveDCCSession(MCONTACT hContact) +{ + mir_cslock lck(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; + } +} + +void CIrcProto::RemoveDCCSession(DCCINFO* pdci) +{ + mir_cslock lck(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; + } + } +} + +CDccSession* CIrcProto::FindDCCSession(MCONTACT hContact) +{ + mir_cslock lck(m_dcc); + + for (int i = 0; i < m_dcc_chats.getCount(); i++) + if (m_dcc_chats[i]->di->hContact == hContact) + return m_dcc_chats[i]; + + return 0; +} + +CDccSession* CIrcProto::FindDCCSession(DCCINFO* pdci) +{ + mir_cslock lck(m_dcc); + + for (int i = 0; i < m_dcc_xfers.getCount(); i++) + if (m_dcc_xfers[i]->di == pdci) + return m_dcc_xfers[i]; + + return 0; +} + +CDccSession* CIrcProto::FindDCCSendByPort(int iPort) +{ + mir_cslock lck(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) + return p; + } + + return 0; +} + +CDccSession* CIrcProto::FindDCCRecvByPortAndName(int iPort, const TCHAR* szName) +{ + mir_cslock lck(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) { + db_free(&dbv); + return p; + } + db_free(&dbv); + } + } + + return 0; +} + +CDccSession* CIrcProto::FindPassiveDCCSend(int iToken) +{ + mir_cslock lck(m_dcc); + + for (int i = 0; i < m_dcc_xfers.getCount(); i++) + if (m_dcc_xfers[i]->iToken == iToken) + return m_dcc_xfers[i]; + + return 0; +} + +CDccSession* CIrcProto::FindPassiveDCCRecv(CMString sName, CMString sToken) +{ + mir_cslock lck(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) + return p; + } + + return 0; +} + +void CIrcProto::DisconnectAllDCCSessions(bool Shutdown) +{ + mir_cslock lck(m_dcc); + + for (int i = 0; i < m_dcc_chats.getCount(); i++) + if (m_disconnectDCCChats || Shutdown) + m_dcc_chats[i]->Disconnect(); +} + +void CIrcProto::CheckDCCTimeout(void) +{ + mir_cslock lck(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(); + } +} + +//////////////////////////////////////////////////////////////////// + +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 :) + { + debugLogA("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 file transfer"), 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, 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, retrieved 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 = pStart; + ProtoChainRecvMsg(di->hContact, &pre); + } + + 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; + + while (true) { + 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->debugLogA("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 : %S : %S\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->debugLogA("Sent Ident answer: %s", buf); + else + ppro->debugLogA("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