~ubuntu-branches/ubuntu/trusty/kvirc/trusty-proposed

« back to all changes in this revision

Viewing changes to src/kvirc/kernel/KviIrcLink.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Kai Wasserbäch, Kai Wasserbäch, Raúl Sánchez Siles
  • Date: 2011-02-12 10:40:21 UTC
  • mfrom: (14.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20110212104021-5mh4f75jlku20mnt
The combined "Twisted Experiment" and "Nocturnal Raid" release.

[ Kai Wasserbäch ]
* Synced to upstream's SVN revision 5467.
* debian/rules:
  - Added .PHONY line.
  - Resurrect -DMANUAL_REVISION, got lost somewhere and we build SVN
    revisions again.
  - Replace "-DWITH_NO_EMBEDDED_CODE=YES" with "-DWANT_CRYPTOPP=YES".
  - Change the remaining -DWITH/-DWITHOUT to the new -DWANT syntax.
* debian/control:
  - Removed DMUA, I'm a DD now.
  - Changed my e-mail address.
  - Removed unneeded relationships (no upgrades over two releases are
    supported).
  - Fix Suggests for kvirc-dbg.
  - kvirc-data: Make the "Suggests: kvirc" a Recommends, doesn't make much
    sense to install the -data package without the program.
* debian/source/local-options: Added with "unapply-patches".
* debian/kvirc.lintian-overrides: Updated to work for 4.1.1.
* debian/patches/21_make_shared-mime-info_B-D_superfluous.patch: Updated.
* debian/kvirc-data.install: Added .notifyrc.

[ Raúl Sánchez Siles ]
* Stating the right version where kvirc-data break and replace should happen.
* Fixing link to license file.
* Added French and Portuguese man pages.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//=============================================================================
 
2
//
 
3
//   File : KviIrcLink.cpp
 
4
//   Creation date : Mon 03 May 2004 01:45:42 by Szymon Stefanek
 
5
//
 
6
//   This file is part of the KVIrc IRC client distribution
 
7
//   Copyright (C) 2004-2010 Szymon Stefanek <pragma at kvirc dot net>
 
8
//
 
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.
 
13
//
 
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.
 
18
//
 
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.
 
22
//
 
23
//=============================================================================
 
24
 
 
25
 
 
26
#include "KviIrcLink.h"
 
27
#include "KviDnsResolver.h"
 
28
#include "KviLocale.h"
 
29
#include "KviIrcServerDataBase.h"
 
30
#include "KviProxy.h"
 
31
#include "KviProxyDataBase.h"
 
32
#include "KviError.h"
 
33
#include "kvi_out.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"
 
50
 
 
51
#include <QTimer>
 
52
 
 
53
extern KVIRC_API KviIrcServerDataBase   * g_pServerDataBase;
 
54
extern KVIRC_API KviProxyDataBase    * g_pProxyDataBase;
 
55
//extern KVIRC_API KviGarbageCollector * g_pGarbageCollector;
 
56
 
 
57
 
 
58
KviIrcLink::KviIrcLink(KviIrcConnection * pConnection)
 
59
: QObject()
 
60
{
 
61
        m_pConnection = pConnection;
 
62
        m_pTarget = pConnection->target();
 
63
        m_pConsole = m_pConnection->console();
 
64
 
 
65
        m_pSocket = 0;
 
66
        m_pLinkFilter = 0;
 
67
        m_pResolver = 0;
 
68
 
 
69
        m_pReadBuffer    = 0;            // incoming data buffer
 
70
        m_uReadBufferLen = 0;            // incoming data buffer length
 
71
        m_uReadPackets   = 0;            // total packets read per session
 
72
 
 
73
        m_eState = Idle;
 
74
}
 
75
 
 
76
KviIrcLink::~KviIrcLink()
 
77
{
 
78
        if(m_pResolver)
 
79
                delete m_pResolver;
 
80
 
 
81
        destroySocket();
 
82
 
 
83
        if(m_pReadBuffer)
 
84
                KviMemory::free(m_pReadBuffer);
 
85
}
 
86
 
 
87
 
 
88
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
89
//
 
90
// KviIrcSocket management
 
91
//
 
92
 
 
93
void KviIrcLink::linkFilterDestroyed()
 
94
{
 
95
        m_pLinkFilter = 0;
 
96
        m_pConsole->output(KVI_OUT_SYSTEMWARNING,
 
97
                __tr2qs("Ops... for some reason the link filter object has been destroyed"));
 
98
}
 
99
 
 
100
void KviIrcLink::destroySocket()
 
101
{
 
102
        if(m_pLinkFilter)
 
103
        {
 
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();
 
107
                m_pLinkFilter = 0;
 
108
        }
 
109
 
 
110
        if(m_pSocket)
 
111
        {
 
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();
 
115
                m_pSocket = 0;
 
116
        }
 
117
}
 
118
 
 
119
void KviIrcLink::createSocket(const QString & szLinkFilterName)
 
120
{
 
121
        destroySocket(); // make sure we do not leak memory
 
122
 
 
123
        m_pSocket = new KviIrcSocket(this);
 
124
 
 
125
        if(szLinkFilterName.isEmpty())
 
126
                return;
 
127
 
 
128
        if(KviQString::equalCI(szLinkFilterName,"irc"))
 
129
                return;
 
130
 
 
131
        m_pLinkFilter = (KviMexLinkFilter *)g_pModuleExtensionManager->allocateExtension("linkfilter",
 
132
                szLinkFilterName.toUtf8().data(),m_pConsole,0,this,szLinkFilterName.toUtf8().data());
 
133
 
 
134
        if(m_pLinkFilter)
 
135
        {
 
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);
 
139
                return;
 
140
        }
 
141
 
 
142
        m_pConsole->output(KVI_OUT_SYSTEMWARNING,
 
143
                __tr2qs("Failed to set up the link filter \"%Q\", will try with plain IRC"),&szLinkFilterName);
 
144
}
 
145
 
 
146
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
147
//
 
148
// Connection related operations
 
149
//
 
150
 
 
151
void KviIrcLink::abort()
 
152
{
 
153
        if(m_pSocket)
 
154
        {
 
155
                m_pSocket->abort();
 
156
                return;
 
157
        }
 
158
 
 
159
        if(m_pResolver)
 
160
        {
 
161
                m_pResolver->abort();
 
162
                return;
 
163
        }
 
164
}
 
165
 
 
166
void KviIrcLink::start()
 
167
{
 
168
        m_eState = Connecting;
 
169
        if(m_pResolver)
 
170
                delete m_pResolver; // this should never happen
 
171
 
 
172
        m_pResolver = new KviIrcConnectionTargetResolver(m_pConnection);
 
173
        connect(m_pResolver,SIGNAL(terminated()),this,SLOT(resolverTerminated()));
 
174
        m_pResolver->start(m_pTarget);
 
175
}
 
176
 
 
177
void KviIrcLink::resolverTerminated()
 
178
{
 
179
        if(!m_pResolver)
 
180
        {
 
181
                qDebug("Oops... resoverTerminated() triggered without a resolver ?");
 
182
                return;
 
183
        }
 
184
 
 
185
        if(m_pResolver->status() != KviIrcConnectionTargetResolver::Success)
 
186
        {
 
187
                m_eState = Idle;
 
188
                m_pConnection->linkAttemptFailed(m_pResolver->lastError());
 
189
                return;
 
190
        }
 
191
 
 
192
        // resolver terminated successfully
 
193
        delete m_pResolver;
 
194
        m_pResolver = 0;
 
195
 
 
196
        createSocket(m_pTarget->server()->linkFilter());
 
197
 
 
198
        KviError::Code eError = m_pSocket->startConnection(m_pTarget->server(),m_pTarget->proxy(),
 
199
                m_pTarget->bindAddress().isEmpty() ? 0 : m_pTarget->bindAddress().toUtf8().data());
 
200
 
 
201
        if(eError != KviError::Success)
 
202
        {
 
203
                QString szStrDescription(KviError::getDescription(eError));
 
204
                m_pConsole->output(KVI_OUT_SYSTEMERROR,
 
205
                        __tr2qs("Failed to start the connection: %Q"),
 
206
                        &szStrDescription);
 
207
//                      &(KviError::getDescription(eError)));
 
208
 
 
209
                m_eState = Idle;
 
210
                m_pConnection->linkAttemptFailed(eError);
 
211
        }
 
212
}
 
213
 
 
214
 
 
215
/////////////////////////////////////////////////////////////////////////////////////////////////////////
 
216
//
 
217
// Incoming data processing
 
218
//
 
219
 
 
220
void KviIrcLink::processData(char * buffer, int iLen)
 
221
{
 
222
        if(m_pLinkFilter)
 
223
        {
 
224
                m_pLinkFilter->processData(buffer,iLen);
 
225
                return;
 
226
        }
 
227
 
 
228
        register char * p = buffer;
 
229
        char * cBeginOfCurData = buffer;
 
230
        int iBufLen = 0;
 
231
        char * cMessageBuffer = (char *)KviMemory::allocate(1);
 
232
 
 
233
        while(*p)
 
234
        {
 
235
                if((*p == '\r' )||(*p == '\n'))
 
236
                {
 
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)
 
242
                        {
 
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);
 
250
                                m_pReadBuffer = 0;
 
251
                        } else {
 
252
                                KVI_ASSERT(!m_pReadBuffer);
 
253
                                cMessageBuffer = (char *)KviMemory::reallocate(cMessageBuffer,iBufLen + 1);
 
254
                                KviMemory::move(cMessageBuffer,cBeginOfCurData,iBufLen);
 
255
                                *(cMessageBuffer + iBufLen) = '\0';
 
256
                        }
 
257
                        m_uReadPackets++;
 
258
 
 
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);
 
273
 
 
274
                        if(m_pSocket->state() != KviIrcSocket::Connected)
 
275
                        {
 
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)
 
280
                                //
 
281
                                // We handle it by simply returning control to readData() which
 
282
                                // will return immediately (and safely) control to Qt
 
283
                                KviMemory::free(cMessageBuffer);
 
284
                                return;
 
285
                        }
 
286
 
 
287
                        while(*p && ((*p=='\r')||(*p=='\n')) )p++;
 
288
                        cBeginOfCurData = p;
 
289
 
 
290
                } else p++;
 
291
        }
 
292
 
 
293
        //now *p == '\0'
 
294
        //beginOfCurData points to '\0' if we have
 
295
        //no more stuff to parse, or points to something
 
296
        //different than '\r' or '\n'...
 
297
        if(*cBeginOfCurData)
 
298
        {
 
299
                //Have remaining data...in the local buffer
 
300
                iBufLen = p - cBeginOfCurData;
 
301
                if(m_uReadBufferLen > 0)
 
302
                {
 
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;
 
308
                } else {
 
309
                        //
 
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);
 
314
                }
 
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.");
 
319
        }
 
320
        KviMemory::free(cMessageBuffer);
 
321
}
 
322
 
 
323
/////////////////////////////////////////////////////////////////////////////////////////////////////////
 
324
//
 
325
// Outgoing data processing
 
326
//
 
327
 
 
328
void KviIrcLink::clearOutputQueue(bool bPrivateMessagesOnly)
 
329
{
 
330
        if(!m_pSocket)
 
331
                return; // we have no queue at all
 
332
        
 
333
        if(m_pLinkFilter)
 
334
        {
 
335
                m_pLinkFilter->clearOutputQueue(bPrivateMessagesOnly);
 
336
                return;
 
337
        }
 
338
        
 
339
        m_pSocket->clearOutputQueue(bPrivateMessagesOnly);
 
340
}
 
341
 
 
342
 
 
343
bool KviIrcLink::sendPacket(KviDataBuffer * pData)
 
344
{
 
345
        if(!m_pSocket)
 
346
        {
 
347
                delete pData;
 
348
                pData = 0;
 
349
                return false;
 
350
        }
 
351
 
 
352
        // if we have a filter, let it do its job
 
353
        if(m_pLinkFilter)
 
354
                return m_pLinkFilter->sendPacket(pData);
 
355
 
 
356
        return m_pSocket->sendPacket(pData);
 
357
}
 
358
 
 
359
void KviIrcLink::socketStateChange()
 
360
{
 
361
        switch(m_pSocket->state())
 
362
        {
 
363
                case KviIrcSocket::Connected:
 
364
                        m_eState = Connected;
 
365
                        m_pConnection->linkEstabilished();
 
366
                break;
 
367
                case KviIrcSocket::Idle:
 
368
                {
 
369
                        State old = m_eState;
 
370
                        m_eState = Idle;
 
371
                        switch(old)
 
372
                        {
 
373
                                case Connecting:
 
374
                                        m_pConnection->linkAttemptFailed(m_pSocket->lastError());
 
375
                                break;
 
376
                                case Connected:
 
377
                                        m_pConnection->linkTerminated();
 
378
                                break;
 
379
                                default: // currently can be only Idle
 
380
                                        qDebug("Ooops... got a KviIrcSocket::Idle state change when KviIrcLink::m_eState was Idle");
 
381
                                break;
 
382
                        }
 
383
                }
 
384
                break;
 
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());
 
391
                break;
 
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"));
 
398
                break;
 
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"));
 
406
                break;
 
407
                case KviIrcSocket::ProxyFinalV4:
 
408
                        m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent connection request, waiting for acknowledgement"));
 
409
                break;
 
410
                case KviIrcSocket::ProxyFinalV5:
 
411
                        m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent target host data, waiting for acknowledgement"));
 
412
                break;
 
413
                case KviIrcSocket::ProxySelectAuthMethodV5:
 
414
                        m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent auth method request, waiting for acknowledgement"));
 
415
                break;
 
416
                case KviIrcSocket::ProxyUserPassV5:
 
417
                        m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent username and password, waiting for acknowledgement"));
 
418
                break;
 
419
                case KviIrcSocket::ProxyFinalHttp:
 
420
                        m_pConsole->outputNoFmt(KVI_OUT_CONNECTION,__tr2qs("Sent connection request, waiting for \"HTTP 200\" acknowledgement"));
 
421
                break;
 
422
                default:
 
423
                break;
 
424
        }
 
425
}