X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fnetworkconnection.cpp;h=760dcb3c7d2666fd17753f2a8a73527edcd0b294;hp=1adf827a12c30aa2df84f34b9a53ec24915e8304;hb=7942d6e0e0c29f870b471e44df6ba172732ef43a;hpb=d19d557880990d174873926c9ca943a1140bec81 diff --git a/src/core/networkconnection.cpp b/src/core/networkconnection.cpp index 1adf827a..760dcb3c 100644 --- a/src/core/networkconnection.cpp +++ b/src/core/networkconnection.cpp @@ -29,45 +29,48 @@ #include "ircchannel.h" #include "ircuser.h" -#include "network.h" #include "identity.h" #include "ircserverhandler.h" #include "userinputhandler.h" #include "ctcphandler.h" -NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : QObject(network), +NetworkConnection::NetworkConnection(Network *network, CoreSession *session) + : QObject(network), _connectionState(Network::Disconnected), _network(network), _coreSession(session), _ircServerHandler(new IrcServerHandler(this)), _userInputHandler(new UserInputHandler(this)), _ctcpHandler(new CtcpHandler(this)), - _autoReconnectCount(0) + _autoReconnectCount(0), + _quitRequested(false), + + _previousConnectionAttemptFailed(false), + _lastUsedServerlistIndex(0), + + // TODO make autowho configurable (possibly per-network) + _autoWhoEnabled(true), + _autoWhoInterval(90), + _autoWhoNickLimit(0), // unlimited + _autoWhoDelay(3), + + // TokenBucket to avaid sending too much at once + _messagesPerSecond(1), + _burstSize(5), + _tokenBucket(5) // init with a full bucket { _autoReconnectTimer.setSingleShot(true); + _socketCloseTimer.setSingleShot(true); + connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout())); - _previousConnectionAttemptFailed = false; - _lastUsedServerlistIndex = 0; - - // TODO make autowho configurable (possibly per-network) - _autoWhoEnabled = true; - _autoWhoInterval = 90; - _autoWhoNickLimit = 0; // unlimited - _autoWhoDelay = 3; + _pingTimer.setInterval(60000); + connect(&_pingTimer, SIGNAL(timeout()), this, SLOT(sendPing())); _autoWhoTimer.setInterval(_autoWhoDelay * 1000); - _autoWhoTimer.setSingleShot(false); _autoWhoCycleTimer.setInterval(_autoWhoInterval * 1000); - _autoWhoCycleTimer.setSingleShot(false); - - // TokenBucket to avaid sending too much at once - _messagesPerSecond = 1; - _burstSize = 5; - _tokenBucket = 5; // init with a full bucket - + _tokenBucketTimer.start(_messagesPerSecond * 1000); - _tokenBucketTimer.setSingleShot(false); QHash channels = coreSession()->persistentChannels(networkId()); foreach(QString chan, channels.keys()) { @@ -104,6 +107,7 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : Q NetworkConnection::~NetworkConnection() { if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting) disconnectFromIrc(false); // clean up, but this does not count as requested disconnect! + disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up delete _ircServerHandler; delete _userInputHandler; delete _ctcpHandler; @@ -213,6 +217,8 @@ void NetworkConnection::networkInitialized(const QString ¤tServer) { network()->setConnected(true); emit connected(networkId()); + _pingTimer.start(); + if(_autoWhoEnabled) { _autoWhoCycleTimer.start(); _autoWhoTimer.start(); @@ -252,11 +258,14 @@ void NetworkConnection::disconnectFromIrc(bool requested) { if(socket.state() < QAbstractSocket::ConnectedState) { setConnectionState(Network::Disconnected); socketDisconnected(); - } else socket.disconnectFromHost(); - - if(requested) { - emit quitRequested(networkId()); + } else { + _socketCloseTimer.start(10000); // the irc server has 10 seconds to close the socket } + + // this flag triggers quitRequested() once the socket is closed + // it is needed to determine whether or not the connection needs to be + // in the automatic session restore. + _quitRequested = requested; } void NetworkConnection::socketHasData() { @@ -360,18 +369,27 @@ void NetworkConnection::socketStateChanged(QAbstractSocket::SocketState socketSt setConnectionState(state); } +void NetworkConnection::socketCloseTimeout() { + socket.disconnectFromHost(); +} + void NetworkConnection::socketDisconnected() { + _pingTimer.stop(); _autoWhoCycleTimer.stop(); _autoWhoTimer.stop(); _autoWhoQueue.clear(); _autoWhoInProgress.clear(); + _socketCloseTimer.stop(); + network()->setConnected(false); emit disconnected(networkId()); if(_autoReconnectCount != 0) { setConnectionState(Network::Reconnecting); if(_autoReconnectCount == network()->autoReconnectRetries()) doAutoReconnect(); // first try is immediate else _autoReconnectTimer.start(); + } else if(_quitRequested) { + emit quitRequested(networkId()); } } @@ -391,6 +409,7 @@ void NetworkConnection::userInput(BufferInfo buf, QString msg) { void NetworkConnection::putRawLine(QByteArray s) { if(_tokenBucket > 0) { + // qDebug() << "putRawLine: " << s; writeToSocket(s); } else { _msgQueue.append(s); @@ -399,6 +418,7 @@ void NetworkConnection::putRawLine(QByteArray s) { void NetworkConnection::writeToSocket(QByteArray s) { s += "\r\n"; + // qDebug() << "writeToSocket: " << s.size(); socket.write(s); _tokenBucket--; } @@ -413,21 +433,84 @@ void NetworkConnection::fillBucketAndProcessQueue() { } } -void NetworkConnection::putCmd(const QString &cmd, const QVariantList ¶ms, const QByteArray &prefix) { +// returns 0 if the message will not be chopped by the irc server or number of chopped bytes if message is too long +int NetworkConnection::lastParamOverrun(const QString &cmd, const QList ¶ms) { + //the server will pass our message that trunkated to 512 bytes including CRLF with the following format: + // ":prefix COMMAND param0 param1 :lastparam" + // where prefix = "nickname!user@host" + // that means that the last message can be as long as: + // 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) + IrcUser *me = network()->me(); + int maxLen = 480 - cmd.toAscii().count(); // educated guess in case we don't know us (yet?) + + if(me) + maxLen = 512 - serverEncode(me->nick()).count() - serverEncode(me->user()).count() - serverEncode(me->host()).count() - cmd.toAscii().count() - 6; + + if(!params.isEmpty()) { + for(int i = 0; i < params.count() - 1; i++) { + maxLen -= (params[i].count() + 1); + } + maxLen -= 2; // " :" last param separator; + + if(params.last().count() > maxLen) { + return params.last().count() - maxLen; + } else { + return 0; + } + } else { + return 0; + } +} + +void NetworkConnection::putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix) { QByteArray msg; + if(cmd == "PRIVMSG" && params.count() > 1) { + int overrun = lastParamOverrun(cmd, params); + if(overrun) { + QList paramCopy1; + QList paramCopy2; + for(int i = 0; i < params.count() - 1; i++) { + paramCopy1 << params[i]; + paramCopy2 << params[i]; + } + + QByteArray lastPart = params.last(); + QByteArray splitter(" .,-"); + int maxSplitPos = params.last().count() - overrun; + int splitPos = -1; + for(int i = 0; i < splitter.size(); i++) { + splitPos = qMax(splitPos, lastPart.lastIndexOf(splitter[i], maxSplitPos)); + } + + if(splitPos == -1) { + splitPos = maxSplitPos; + } + + paramCopy1 << lastPart.left(splitPos); + paramCopy2 << lastPart.mid(splitPos); + putCmd(cmd, paramCopy1, prefix); + putCmd(cmd, paramCopy2, prefix); + return; + } + } + if(!prefix.isEmpty()) msg += ":" + prefix + " "; msg += cmd.toUpper().toAscii(); for(int i = 0; i < params.size() - 1; i++) { - msg += " " + params[i].toByteArray(); + msg += " " + params[i]; } if(!params.isEmpty()) - msg += " :" + params.last().toByteArray(); + msg += " :" + params.last(); putRawLine(msg); } +void NetworkConnection::sendPing() { + userInputHandler()->handlePing(BufferInfo(), QString()); +} + void NetworkConnection::sendAutoWho() { while(!_autoWhoQueue.isEmpty()) { QString chan = _autoWhoQueue.takeFirst();