36
36
#include "userinputhandler.h"
37
37
#include "ctcphandler.h"
39
NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : QObject(network),
39
NetworkConnection::NetworkConnection(Network *network, CoreSession *session)
40
41
_connectionState(Network::Disconnected),
42
43
_coreSession(session),
43
44
_ircServerHandler(new IrcServerHandler(this)),
44
45
_userInputHandler(new UserInputHandler(this)),
45
46
_ctcpHandler(new CtcpHandler(this)),
46
_autoReconnectCount(0)
47
_autoReconnectCount(0),
48
_quitRequested(false),
50
_previousConnectionAttemptFailed(false),
51
_lastUsedServerlistIndex(0),
53
// TODO make autowho configurable (possibly per-network)
54
_autoWhoEnabled(true),
56
_autoWhoNickLimit(0), // unlimited
59
// TokenBucket to avaid sending too much at once
60
_messagesPerSecond(1),
62
_tokenBucket(5) // init with a full bucket
48
64
_autoReconnectTimer.setSingleShot(true);
50
_previousConnectionAttemptFailed = false;
51
_lastUsedServerlistIndex = 0;
53
// TODO make autowho configurable (possibly per-network)
54
_autoWhoEnabled = true;
55
_autoWhoInterval = 90;
56
_autoWhoNickLimit = 0; // unlimited
65
_socketCloseTimer.setSingleShot(true);
66
connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout()));
59
68
_autoWhoTimer.setInterval(_autoWhoDelay * 1000);
60
_autoWhoTimer.setSingleShot(false);
61
69
_autoWhoCycleTimer.setInterval(_autoWhoInterval * 1000);
62
_autoWhoCycleTimer.setSingleShot(false);
64
// TokenBucket to avaid sending too much at once
65
_messagesPerSecond = 1;
67
_tokenBucket = 5; // init with a full bucket
69
71
_tokenBucketTimer.start(_messagesPerSecond * 1000);
70
_tokenBucketTimer.setSingleShot(false);
72
73
QHash<QString, QString> channels = coreSession()->persistentChannels(networkId());
73
74
foreach(QString chan, channels.keys()) {
104
105
NetworkConnection::~NetworkConnection() {
105
106
if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting)
106
107
disconnectFromIrc(false); // clean up, but this does not count as requested disconnect!
108
disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up
107
109
delete _ircServerHandler;
108
110
delete _userInputHandler;
109
111
delete _ctcpHandler;
252
254
if(socket.state() < QAbstractSocket::ConnectedState) {
253
255
setConnectionState(Network::Disconnected);
254
256
socketDisconnected();
255
} else socket.disconnectFromHost();
258
emit quitRequested(networkId());
258
_socketCloseTimer.start(10000); // the irc server has 10 seconds to close the socket
261
// this flag triggers quitRequested() once the socket is closed
262
// it is needed to determine whether or not the connection needs to be
263
// in the automatic session restore.
264
_quitRequested = requested;
262
267
void NetworkConnection::socketHasData() {
360
365
setConnectionState(state);
368
void NetworkConnection::socketCloseTimeout() {
369
socket.disconnectFromHost();
363
372
void NetworkConnection::socketDisconnected() {
364
373
_autoWhoCycleTimer.stop();
365
374
_autoWhoTimer.stop();
366
375
_autoWhoQueue.clear();
367
376
_autoWhoInProgress.clear();
378
_socketCloseTimer.stop();
369
380
network()->setConnected(false);
370
381
emit disconnected(networkId());
371
382
if(_autoReconnectCount != 0) {
372
383
setConnectionState(Network::Reconnecting);
373
384
if(_autoReconnectCount == network()->autoReconnectRetries()) doAutoReconnect(); // first try is immediate
374
385
else _autoReconnectTimer.start();
386
} else if(_quitRequested) {
387
emit quitRequested(networkId());
416
void NetworkConnection::putCmd(const QString &cmd, const QVariantList ¶ms, const QByteArray &prefix) {
429
// returns 0 if the message will not be chopped by the irc server or number of chopped bytes if message is too long
430
int NetworkConnection::lastParamOverrun(const QString &cmd, const QList<QByteArray> ¶ms) {
431
//the server will pass our message that trunkated to 512 bytes including CRLF with the following format:
432
// ":prefix COMMAND param0 param1 :lastparam"
433
// where prefix = "nickname!user@host"
434
// that means that the last message can be as long as:
435
// 512 - nicklen - userlen - hostlen - commandlen - sum(param[0]..param[n-1])) - 2 (for CRLF) - 4 (":!@" + 1space between prefix and command) - max(paramcount - 1, 0) (space for simple params) - 2 (space and colon for last param)
436
IrcUser *me = network()->me();
437
int maxLen = 480 - cmd.toAscii().count(); // educated guess in case we don't know us (yet?)
440
maxLen = 512 - serverEncode(me->nick()).count() - serverEncode(me->user()).count() - serverEncode(me->host()).count() - cmd.toAscii().count() - 6;
442
if(!params.isEmpty()) {
443
for(int i = 0; i < params.count() - 1; i++) {
444
maxLen -= (params[i].count() + 1);
446
maxLen -= 2; // " :" last param separator;
448
if(params.last().count() > maxLen) {
449
return params.last().count() - maxLen;
458
void NetworkConnection::putCmd(const QString &cmd, const QList<QByteArray> ¶ms, const QByteArray &prefix) {
460
if(cmd == "PRIVMSG" && params.count() > 1) {
461
int overrun = lastParamOverrun(cmd, params);
463
QList<QByteArray> paramCopy1;
464
QList<QByteArray> paramCopy2;
465
for(int i = 0; i < params.count() - 1; i++) {
466
paramCopy1 << params[i];
467
paramCopy2 << params[i];
470
QByteArray lastPart = params.last();
471
QByteArray splitter(" .,-");
472
int maxSplitPos = params.last().count() - overrun;
474
for(int i = 0; i < splitter.size(); i++) {
475
splitPos = qMax(splitPos, lastPart.lastIndexOf(splitter[i], maxSplitPos));
479
splitPos = maxSplitPos;
482
paramCopy1 << lastPart.left(splitPos);
483
paramCopy2 << lastPart.mid(splitPos);
484
putCmd(cmd, paramCopy1, prefix);
485
putCmd(cmd, paramCopy2, prefix);
418
490
if(!prefix.isEmpty())
419
491
msg += ":" + prefix + " ";
420
492
msg += cmd.toUpper().toAscii();
422
494
for(int i = 0; i < params.size() - 1; i++) {
423
msg += " " + params[i].toByteArray();
495
msg += " " + params[i];
425
497
if(!params.isEmpty())
426
msg += " :" + params.last().toByteArray();
498
msg += " :" + params.last();