1
//=============================================================================
3
// File : KviIrcLink.cpp
4
// Creation date : Mon 03 May 2004 01:45:42 by Szymon Stefanek
6
// This file is part of the KVIrc IRC client distribution
7
// Copyright (C) 2004-2010 Szymon Stefanek <pragma at kvirc dot net>
9
// This program is FREE software. You can redistribute it and/or
10
// modify it under the terms of the GNU General Public License
11
// as published by the Free Software Foundation; either version 2
12
// of the License, or (at your opinion) any later version.
14
// This program is distributed in the HOPE that it will be USEFUL,
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
// See the GNU General Public License for more details.
19
// You should have received a copy of the GNU General Public License
20
// along with this program. If not, write to the Free Software Foundation,
21
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
//=============================================================================
26
#include "KviIrcLink.h"
27
#include "KviDnsResolver.h"
28
#include "KviLocale.h"
29
#include "KviIrcServerDataBase.h"
31
#include "KviProxyDataBase.h"
34
#include "KviOptions.h"
35
#include "KviIrcSocket.h"
36
#include "KviConsoleWindow.h"
37
#include "KviNetUtils.h"
38
#include "KviInternalCommand.h"
39
#include "KviMainWindow.h"
40
#include "KviMexLinkFilter.h"
41
//#include "kvi_garbage.h"
42
#include "KviMemory.h"
43
#include "KviMemory.h"
44
#include "KviIrcConnection.h"
45
#include "KviIrcConnectionTarget.h"
46
#include "KviIrcConnectionTargetResolver.h"
47
#include "KviIrcSocket.h"
48
#include "KviDataBuffer.h"
49
#include "kvi_debug.h"
53
extern KVIRC_API KviIrcServerDataBase * g_pServerDataBase;
54
extern KVIRC_API KviProxyDataBase * g_pProxyDataBase;
55
//extern KVIRC_API KviGarbageCollector * g_pGarbageCollector;
58
KviIrcLink::KviIrcLink(KviIrcConnection * pConnection)
61
m_pConnection = pConnection;
62
m_pTarget = pConnection->target();
63
m_pConsole = m_pConnection->console();
69
m_pReadBuffer = 0; // incoming data buffer
70
m_uReadBufferLen = 0; // incoming data buffer length
71
m_uReadPackets = 0; // total packets read per session
76
KviIrcLink::~KviIrcLink()
84
KviMemory::free(m_pReadBuffer);
88
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
90
// KviIrcSocket management
93
void KviIrcLink::linkFilterDestroyed()
96
m_pConsole->output(KVI_OUT_SYSTEMWARNING,
97
__tr2qs("Ops... for some reason the link filter object has been destroyed"));
100
void KviIrcLink::destroySocket()
104
QObject::disconnect(m_pLinkFilter,0,this,0);
105
// the module extension server links must be destroyed in the module that provided it
106
m_pLinkFilter->die();
112
// We use deleteLater() here, since we could actually be inside an event
113
// related to the QSocketNotifier that the socket is attached to...
114
m_pSocket->deleteLater();
119
void KviIrcLink::createSocket(const QString & szLinkFilterName)
121
destroySocket(); // make sure we do not leak memory
123
m_pSocket = new KviIrcSocket(this);
125
if(szLinkFilterName.isEmpty())
128
if(KviQString::equalCI(szLinkFilterName,"irc"))
131
m_pLinkFilter = (KviMexLinkFilter *)g_pModuleExtensionManager->allocateExtension("linkfilter",
132
szLinkFilterName.toUtf8().data(),m_pConsole,0,this,szLinkFilterName.toUtf8().data());
136
connect(m_pLinkFilter,SIGNAL(destroyed()),this,SLOT(linkFilterDestroyed()));
137
m_pConsole->output(KVI_OUT_SYSTEMMESSAGE,
138
__tr2qs("Using filtered IRC protocol: Link filter is \"%Q\""),&szLinkFilterName);
142
m_pConsole->output(KVI_OUT_SYSTEMWARNING,
143
__tr2qs("Failed to set up the link filter \"%Q\", will try with plain IRC"),&szLinkFilterName);
146
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
148
// Connection related operations
151
void KviIrcLink::abort()
161
m_pResolver->abort();
166
void KviIrcLink::start()
168
m_eState = Connecting;
170
delete m_pResolver; // this should never happen
172
m_pResolver = new KviIrcConnectionTargetResolver(m_pConnection);
173
connect(m_pResolver,SIGNAL(terminated()),this,SLOT(resolverTerminated()));
174
m_pResolver->start(m_pTarget);
177
void KviIrcLink::resolverTerminated()
181
qDebug("Oops... resoverTerminated() triggered without a resolver ?");
185
if(m_pResolver->status() != KviIrcConnectionTargetResolver::Success)
188
m_pConnection->linkAttemptFailed(m_pResolver->lastError());
192
// resolver terminated successfully
196
createSocket(m_pTarget->server()->linkFilter());
198
KviError::Code eError = m_pSocket->startConnection(m_pTarget->server(),m_pTarget->proxy(),
199
m_pTarget->bindAddress().isEmpty() ? 0 : m_pTarget->bindAddress().toUtf8().data());
201
if(eError != KviError::Success)
203
QString szStrDescription(KviError::getDescription(eError));
204
m_pConsole->output(KVI_OUT_SYSTEMERROR,
205
__tr2qs("Failed to start the connection: %Q"),
207
// &(KviError::getDescription(eError)));
210
m_pConnection->linkAttemptFailed(eError);
215
/////////////////////////////////////////////////////////////////////////////////////////////////////////
217
// Incoming data processing
220
void KviIrcLink::processData(char * buffer, int iLen)
224
m_pLinkFilter->processData(buffer,iLen);
228
register char * p = buffer;
229
char * cBeginOfCurData = buffer;
231
char * cMessageBuffer = (char *)KviMemory::allocate(1);
235
if((*p == '\r' )||(*p == '\n'))
237
//found a CR or LF...
238
//prepare a message buffer
239
iBufLen = p - cBeginOfCurData;
240
//check for previous unterminated data
241
if(m_uReadBufferLen > 0)
243
KVI_ASSERT(m_pReadBuffer);
244
cMessageBuffer = (char *)KviMemory::reallocate(cMessageBuffer,iBufLen + m_uReadBufferLen + 1);
245
KviMemory::move(cMessageBuffer,m_pReadBuffer,m_uReadBufferLen);
246
KviMemory::move((void *)(cMessageBuffer + m_uReadBufferLen),cBeginOfCurData,iBufLen);
247
*(cMessageBuffer + iBufLen + m_uReadBufferLen) = '\0';
248
m_uReadBufferLen = 0;
249
KviMemory::free(m_pReadBuffer);
252
KVI_ASSERT(!m_pReadBuffer);
253
cMessageBuffer = (char *)KviMemory::reallocate(cMessageBuffer,iBufLen + 1);
254
KviMemory::move(cMessageBuffer,cBeginOfCurData,iBufLen);
255
*(cMessageBuffer + iBufLen) = '\0';
259
// FIXME: actually it can happen that the socket gets disconnected
260
// in a incomingMessage() call.
261
// The problem might be that some other parts of kvirc assume
262
// that the irc context still exists after a failed write to the socket
263
// (some parts don't even check the return value!)
264
// If the problem presents itself again then the solution is:
265
// disable queue flushing for the "incomingMessage" call
266
// and just call queue_insertMessage()
267
// then after the call terminates flush the queue (eventually detecting
268
// the disconnect and thus destroying the irc context).
269
// For now we try to rely on the remaining parts to handle correctly
270
// such conditions. Let's see...
271
if(*cMessageBuffer != 0)
272
m_pConnection->incomingMessage(cMessageBuffer);
274
if(m_pSocket->state() != KviIrcSocket::Connected)
276
// Disconnected in KviConsoleWindow::incomingMessage() call.
277
// This may happen for several reasons (local event loop
278
// with the user hitting the disconnect button, a scripting
279
// handler event that disconnects explicitly)
281
// We handle it by simply returning control to readData() which
282
// will return immediately (and safely) control to Qt
283
KviMemory::free(cMessageBuffer);
287
while(*p && ((*p=='\r')||(*p=='\n')) )p++;
294
//beginOfCurData points to '\0' if we have
295
//no more stuff to parse, or points to something
296
//different than '\r' or '\n'...
299
//Have remaining data...in the local buffer
300
iBufLen = p - cBeginOfCurData;
301
if(m_uReadBufferLen > 0)
303
//and there was more stuff saved... (really slow connection)
304
KVI_ASSERT(m_pReadBuffer);
305
m_pReadBuffer =(char *)KviMemory::reallocate(m_pReadBuffer,m_uReadBufferLen + iBufLen);
306
KviMemory::move((void *)(m_pReadBuffer+m_uReadBufferLen),cBeginOfCurData,iBufLen);
307
m_uReadBufferLen += iBufLen;
310
KVI_ASSERT(!m_pReadBuffer);
311
m_uReadBufferLen = iBufLen;
312
m_pReadBuffer =(char *)KviMemory::allocate(m_uReadBufferLen);
313
KviMemory::move(m_pReadBuffer,cBeginOfCurData,m_uReadBufferLen);
315
//The m_pReadBuffer contains at max 1 irc message...
316
//that can not be longer than 510 bytes (the message is not CRLF terminated)
317
// FIXME: Is this limit *really* valid on all servers ?
318
if(m_uReadBufferLen > 510) qDebug("WARNING: Receiving an invalid irc message from server.");
320
KviMemory::free(cMessageBuffer);
323
/////////////////////////////////////////////////////////////////////////////////////////////////////////
325
// Outgoing data processing
328
void KviIrcLink::clearOutputQueue(bool bPrivateMessagesOnly)
331
return; // we have no queue at all
335
m_pLinkFilter->clearOutputQueue(bPrivateMessagesOnly);
339
m_pSocket->clearOutputQueue(bPrivateMessagesOnly);
343
bool KviIrcLink::sendPacket(KviDataBuffer * pData)
352
// if we have a filter, let it do its job
354
return m_pLinkFilter->sendPacket(pData);
356
return m_pSocket->sendPacket(pData);
359
void KviIrcLink::socketStateChange()
361
switch(m_pSocket->state())
363
case KviIrcSocket::Connected:
364
m_eState = Connected;
365
m_pConnection->linkEstabilished();
367
case KviIrcSocket::Idle:
369
State old = m_eState;
374
m_pConnection->linkAttemptFailed(m_pSocket->lastError());
377
m_pConnection->linkTerminated();
379
default: // currently can be only Idle
380
qDebug("Ooops... got a KviIrcSocket::Idle state change when KviIrcLink::m_eState was Idle");
385
case KviIrcSocket::Connecting:
386
m_pConsole->output(KVI_OUT_CONNECTION,__tr2qs("Contacting %Q %s (%s) on port %u"),
387
connection()->target()->proxy() ? &(__tr2qs("proxy host")) : &(__tr2qs("IRC server")),
388
connection()->target()->proxy() ? connection()->target()->proxy()->hostName().toUtf8().data() : connection()->target()->server()->hostName().toUtf8().data(),
389
connection()->target()->proxy() ? connection()->target()->proxy()->ip().toUtf8().data() : connection()->target()->server()->ip().toUtf8().data(),
390
connection()->target()->proxy() ? connection()->target()->proxy()->port() : connection()->target()->server()->port());
392
case KviIrcSocket::SSLHandshake:
393
m_pConsole->output(KVI_OUT_CONNECTION,__tr2qs("Low-level transport connection established [%s (%s:%u)]"),
394
connection()->target()->proxy() ? connection()->target()->proxy()->hostName().toUtf8().data() : connection()->target()->server()->hostName().toUtf8().data(),
395
connection()->target()->proxy() ? connection()->target()->proxy()->ip().toUtf8().data() : connection()->target()->server()->ip().toUtf8().data(),
396
connection()->target()->proxy() ? connection()->target()->proxy()->port() : connection()->target()->server()->port());
397
m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Starting Secure Socket Layer handshake"));
399
case KviIrcSocket::ProxyLogin:
400
m_pConsole->output(KVI_OUT_CONNECTION,__tr2qs("%Q established [%s (%s:%u)]"),
401
connection()->link()->socket()->usingSSL() ? &(__tr2qs("Secure proxy connection")) : &(__tr2qs("Proxy connection")),
402
connection()->target()->proxy()->hostName().toUtf8().data(),
403
connection()->target()->proxy()->ip().toUtf8().data(),
404
connection()->target()->proxy()->port());
405
m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Negotiating relay information"));
407
case KviIrcSocket::ProxyFinalV4:
408
m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent connection request, waiting for acknowledgement"));
410
case KviIrcSocket::ProxyFinalV5:
411
m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent target host data, waiting for acknowledgement"));
413
case KviIrcSocket::ProxySelectAuthMethodV5:
414
m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent auth method request, waiting for acknowledgement"));
416
case KviIrcSocket::ProxyUserPassV5:
417
m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent username and password, waiting for acknowledgement"));
419
case KviIrcSocket::ProxyFinalHttp:
420
m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent connection request, waiting for \"HTTP 200\" acknowledgement"));