X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fnetworkconnection.cpp;h=d177d4c1ed866383af222eee2f21da83a754f193;hp=c74dc4ef05ddb0457f31116edfed82b0ca5ed814;hb=f7379184b7c0ae4e53d7470809f84e2ad3239ec1;hpb=e91445dfdb33704e5daafa8631bc9a1419251c1c diff --git a/src/core/networkconnection.cpp b/src/core/networkconnection.cpp index c74dc4ef..d177d4c1 100644 --- a/src/core/networkconnection.cpp +++ b/src/core/networkconnection.cpp @@ -29,13 +29,14 @@ #include "ircchannel.h" #include "ircuser.h" -#include "network.h" #include "identity.h" #include "ircserverhandler.h" #include "userinputhandler.h" #include "ctcphandler.h" +#include "logger.h" + NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : QObject(network), _connectionState(Network::Disconnected), @@ -59,17 +60,15 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) // TokenBucket to avaid sending too much at once _messagesPerSecond(1), _burstSize(5), - _tokenBucket(5), // init with a full bucket - - // TODO: - // should be 510 (2 bytes are added when writing to the socket) - // maxMsgSize is 510 minus the hostmask which will be added by the server - _maxMsgSize(450) + _tokenBucket(5) // init with a full bucket { _autoReconnectTimer.setSingleShot(true); _socketCloseTimer.setSingleShot(true); connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout())); - + + _pingTimer.setInterval(60000); + connect(&_pingTimer, SIGNAL(timeout()), this, SLOT(sendPing())); + _autoWhoTimer.setInterval(_autoWhoDelay * 1000); _autoWhoCycleTimer.setInterval(_autoWhoInterval * 1000); @@ -90,24 +89,7 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) connect(network, SIGNAL(autoReconnectIntervalSet(quint32)), this, SLOT(autoReconnectSettingsChanged())); connect(network, SIGNAL(autoReconnectRetriesSet(quint16)), this, SLOT(autoReconnectSettingsChanged())); -#ifndef QT_NO_OPENSSL - { - QFile certFile(quasselDir().absolutePath() + "/quasselClientCert.pem"); - certFile.open(QIODevice::ReadOnly); - QSslCertificate cert(&certFile); - certFile.close(); - - certFile.open(QIODevice::ReadOnly); - QSslKey key(&certFile, QSsl::Rsa); - certFile.close(); - - if ( !cert.isNull() && cert.isValid() && - !key.isNull() ) { - socket.setLocalCertificate(cert); - socket.setPrivateKey(key); - } - } - +#ifdef HAVE_SSL connect(&socket, SIGNAL(encrypted()), this, SLOT(socketEncrypted())); connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); #endif @@ -237,6 +219,8 @@ void NetworkConnection::networkInitialized(const QString ¤tServer) { network()->setConnected(true); emit connected(networkId()); + _pingTimer.start(); + if(_autoWhoEnabled) { _autoWhoCycleTimer.start(); _autoWhoTimer.start(); @@ -269,21 +253,25 @@ void NetworkConnection::sendPerform() { if(!joinString.isEmpty()) userInputHandler()->handleJoin(statusBuf, joinString); } -void NetworkConnection::disconnectFromIrc(bool requested) { +void NetworkConnection::disconnectFromIrc(bool requested, const QString &reason) { + _quitRequested = requested; // see socketDisconnected(); _autoReconnectTimer.stop(); - _autoReconnectCount = 0; + _autoReconnectCount = 0; // prohibiting auto reconnect displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting.")); - if(socket.state() < QAbstractSocket::ConnectedState) { - setConnectionState(Network::Disconnected); + if(socket.state() == QAbstractSocket::UnconnectedState) { + socketDisconnected(); + } else if(socket.state() < QAbstractSocket::ConnectedState || !requested) { + // we might be in a state waiting for a timeout... + // or (!requested) this is a core shutdown... + // in both cases we don't really care... set a disconnected state + socket.close(); socketDisconnected(); } else { - _socketCloseTimer.start(10000); // the irc server has 10 seconds to close the socket + // quit gracefully if it's user requested quit + userInputHandler()->issueQuit(reason); + // the irc server has 10 seconds to close the socket + _socketCloseTimer.start(10000); } - - // 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() { @@ -295,12 +283,11 @@ void NetworkConnection::socketHasData() { void NetworkConnection::socketError(QAbstractSocket::SocketError) { _previousConnectionAttemptFailed = true; - qDebug() << qPrintable(tr("Could not connect to %1 (%2)").arg(network()->networkName(), socket.errorString())); + qWarning() << qPrintable(tr("Could not connect to %1 (%2)").arg(network()->networkName(), socket.errorString())); emit connectionError(socket.errorString()); emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Connection failure: %1").arg(socket.errorString())); network()->emitConnectionError(socket.errorString()); if(socket.state() < QAbstractSocket::ConnectedState) { - setConnectionState(Network::Disconnected); socketDisconnected(); } // mark last connection attempt as failed @@ -309,7 +296,7 @@ void NetworkConnection::socketError(QAbstractSocket::SocketError) { //exit(1); } -#ifndef QT_NO_OPENSSL +#ifdef HAVE_SSL void NetworkConnection::sslErrors(const QList &sslErrors) { Q_UNUSED(sslErrors) @@ -333,10 +320,10 @@ void NetworkConnection::socketEncrypted() { socketInitialized(); } -#endif // QT_NO_OPENSSL +#endif // HAVE_SSL void NetworkConnection::socketConnected() { -#ifdef QT_NO_OPENSSL +#ifndef HAVE_SSL socketInitialized(); return; #else @@ -353,7 +340,7 @@ void NetworkConnection::socketInitialized() { //emit connected(networkId()); initialize first! Identity *identity = coreSession()->identity(network()->identity()); if(!identity) { - qWarning() << "Identity invalid!"; + qCritical() << "Identity invalid!"; disconnectFromIrc(); return; } @@ -392,21 +379,31 @@ void NetworkConnection::socketCloseTimeout() { } void NetworkConnection::socketDisconnected() { + _pingTimer.stop(); _autoWhoCycleTimer.stop(); _autoWhoTimer.stop(); _autoWhoQueue.clear(); _autoWhoInProgress.clear(); _socketCloseTimer.stop(); - + + IrcUser *me = network()->me(); + if(me) { + foreach(QString channel, me->channels()) + emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, "", me->hostmask()); + } + 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) { + if(_quitRequested) { + setConnectionState(Network::Disconnected); emit quitRequested(networkId()); + } else if(_autoReconnectCount != 0) { + setConnectionState(Network::Reconnecting); + if(_autoReconnectCount == network()->autoReconnectRetries()) + doAutoReconnect(); // first try is immediate + else + _autoReconnectTimer.start(); } } @@ -450,38 +447,80 @@ void NetworkConnection::fillBucketAndProcessQueue() { } } -void NetworkConnection::putCmd(const QString &cmd, const QVariantList ¶ms, const QByteArray &prefix) { - QByteArray msg; - if(!prefix.isEmpty()) - msg += ":" + prefix + " "; - msg += cmd.toUpper().toAscii(); +// 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?) - for(int i = 0; i < params.size() - 1; i++) { - msg += " " + params[i].toByteArray(); + 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; } - if(!params.isEmpty()) - msg += " :" + params.last().toByteArray(); +} +void NetworkConnection::putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix) { + QByteArray msg; if(cmd == "PRIVMSG" && params.count() > 1) { - QByteArray msghead = "PRIVMSG " + params[0].toByteArray() + " :"; + int overrun = lastParamOverrun(cmd, params); + if(overrun) { + QList paramCopy1 = params; + paramCopy1.removeLast(); + QList paramCopy2 = paramCopy1; - while (msg.size() > _maxMsgSize) { + QByteArray lastPart = params.last(); QByteArray splitter(" .,-"); - int splitPosition = 0; + int maxSplitPos = params.last().count() - overrun; + int splitPos = -1; for(int i = 0; i < splitter.size(); i++) { - splitPosition = qMax(splitPosition, msg.lastIndexOf(splitter[i], _maxMsgSize)); + splitPos = qMax(splitPos, lastPart.lastIndexOf(splitter[i], maxSplitPos)); } - if(splitPosition < 300) { - splitPosition = _maxMsgSize; + if(splitPos <= 0) { + splitPos = maxSplitPos; } - putRawLine(msg.left(splitPosition)); - msg = msghead + msg.mid(splitPosition); + + 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]; + } + if(!params.isEmpty()) + msg += " :" + params.last(); putRawLine(msg); } +void NetworkConnection::sendPing() { + userInputHandler()->handlePing(BufferInfo(), QString()); +} + void NetworkConnection::sendAutoWho() { while(!_autoWhoQueue.isEmpty()) { QString chan = _autoWhoQueue.takeFirst();