summaryrefslogtreecommitdiff
path: root/protocols/IRCG
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/IRCG')
-rw-r--r--protocols/IRCG/src/irclib.cpp2935
1 files changed, 1460 insertions, 1475 deletions
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<CIrcHandler> 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<CIrcHandler> 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);
+}