X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcorenetwork.cpp;h=40b47405e4176e082c0b2253a64189a7b38bba5c;hp=a40b54641f75770e62fb06a67a52efe2a2991543;hb=08a3b942d11b6b6f5939e7c80e26c4a6cde7c785;hpb=710edfe368ed1db79cc274164523d14afc1b2c9d diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index a40b5464..40b47405 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -40,17 +40,21 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) _previousConnectionAttemptFailed(false), _lastUsedServerIndex(0), + _lastPingTime(0), + _maxPingCount(3), + _pingCount(0), + // TODO make autowho configurable (possibly per-network) _autoWhoEnabled(true), _autoWhoInterval(90), _autoWhoNickLimit(0), // unlimited - _autoWhoDelay(3) + _autoWhoDelay(5) { _autoReconnectTimer.setSingleShot(true); _socketCloseTimer.setSingleShot(true); connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout())); - _pingTimer.setInterval(60000); + _pingTimer.setInterval(30000); connect(&_pingTimer, SIGNAL(timeout()), this, SLOT(sendPing())); _autoWhoTimer.setInterval(_autoWhoDelay * 1000); @@ -137,6 +141,10 @@ void CoreNetwork::connectToIrc(bool reconnecting) { qWarning() << "Invalid identity configures, ignoring connect request!"; return; } + + // cleaning up old quit reason + _quitReason.clear(); + // use a random server? if(useRandomServer()) { _lastUsedServerIndex = qrand() % serverList().size(); @@ -177,24 +185,41 @@ void CoreNetwork::connectToIrc(bool reconnecting) { #endif } -void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason) { +void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool withReconnect) { _quitRequested = requested; // see socketDisconnected(); - _autoReconnectTimer.stop(); - _autoReconnectCount = 0; // prohibiting auto reconnect - displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting.")); - 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 + if(!withReconnect) { + _autoReconnectTimer.stop(); + _autoReconnectCount = 0; // prohibiting auto reconnect + } + disablePingTimeout(); + _msgQueue.clear(); + + IrcUser *me_ = me(); + if(me_) { + QString awayMsg; + if(me_->isAway()) + awayMsg = me_->awayMessage(); + Core::setAwayMessage(userId(), networkId(), awayMsg); + Core::setUserModes(userId(), networkId(), me_->userModes()); + } + + if(reason.isEmpty() && identityPtr()) + _quitReason = identityPtr()->quitReason(); + else + _quitReason = reason; + + displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting. (%1)").arg((!requested && !withReconnect) ? tr("Core Shutdown") : _quitReason)); + switch(socket.state()) { + case QAbstractSocket::ConnectedState: + userInputHandler()->issueQuit(_quitReason); + if(requested || withReconnect) { + // the irc server has 10 seconds to close the socket + _socketCloseTimer.start(10000); + break; + } + default: socket.close(); socketDisconnected(); - } else { - // quit gracefully if it's user requested quit - userInputHandler()->issueQuit(reason); - // the irc server has 10 seconds to close the socket - _socketCloseTimer.start(10000); } } @@ -235,7 +260,7 @@ void CoreNetwork::setChannelJoined(const QString &channel) { void CoreNetwork::setChannelParted(const QString &channel) { removeChannelKey(channel); _autoWhoQueue.removeAll(channel.toLower()); - _autoWhoInProgress.remove(channel.toLower()); + _autoWhoPending.remove(channel.toLower()); Core::setChannelPersistent(userId(), networkId(), channel, false); } @@ -253,9 +278,11 @@ void CoreNetwork::removeChannelKey(const QString &channel) { } bool CoreNetwork::setAutoWhoDone(const QString &channel) { - if(_autoWhoInProgress.value(channel.toLower(), 0) <= 0) + QString chan = channel.toLower(); + if(_autoWhoPending.value(chan, 0) <= 0) return false; - _autoWhoInProgress[channel.toLower()]--; + if(--_autoWhoPending[chan] <= 0) + _autoWhoPending.remove(chan); return true; } @@ -301,10 +328,10 @@ void CoreNetwork::socketInitialized() { } // TokenBucket to avoid sending too much at once - _messagesPerSecond = 1; + _messageDelay = 2200; // this seems to be a safe value (2.2 seconds delay) _burstSize = 5; - _tokenBucket = 5; // init with a full bucket - _tokenBucketTimer.start(_messagesPerSecond * 1000); + _tokenBucket = _burstSize; // init with a full bucket + _tokenBucketTimer.start(_messageDelay); if(!server.password.isEmpty()) { putRawLine(serverEncode(QString("PASS %1").arg(server.password))); @@ -314,11 +341,13 @@ void CoreNetwork::socketInitialized() { } void CoreNetwork::socketDisconnected() { - _pingTimer.stop(); + disablePingTimeout(); + _msgQueue.clear(); + _autoWhoCycleTimer.stop(); _autoWhoTimer.stop(); _autoWhoQueue.clear(); - _autoWhoInProgress.clear(); + _autoWhoPending.clear(); _socketCloseTimer.stop(); @@ -327,7 +356,7 @@ void CoreNetwork::socketDisconnected() { IrcUser *me_ = me(); if(me_) { foreach(QString channel, me_->channels()) - emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, "", me_->hostmask()); + emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, _quitReason, me_->hostmask()); } setConnected(false); @@ -337,7 +366,7 @@ void CoreNetwork::socketDisconnected() { Core::setNetworkConnected(userId(), networkId(), false); } else if(_autoReconnectCount != 0) { setConnectionState(Network::Reconnecting); - if(_autoReconnectCount == autoReconnectRetries()) + if(_autoReconnectCount == -1 || _autoReconnectCount == autoReconnectRetries()) doAutoReconnect(); // first try is immediate else _autoReconnectTimer.start(); @@ -373,12 +402,28 @@ void CoreNetwork::networkInitialized() { if(useAutoReconnect()) { // reset counter - _autoReconnectCount = autoReconnectRetries(); + _autoReconnectCount = unlimitedReconnectRetries() ? -1 : autoReconnectRetries(); + } + + // restore away state + QString awayMsg = Core::awayMessage(userId(), networkId()); + if(!awayMsg.isEmpty()) + userInputHandler()->handleAway(BufferInfo(), Core::awayMessage(userId(), networkId())); + + // restore old user modes if server default mode is set. + IrcUser *me_ = me(); + if(me_) { + if(!me_->userModes().isEmpty()) { + restoreUserModes(); + } else { + connect(me_, SIGNAL(userModesSet(QString)), this, SLOT(restoreUserModes())); + connect(me_, SIGNAL(userModesAdded(QString)), this, SLOT(restoreUserModes())); + } } sendPerform(); - _pingTimer.start(); + enablePingTimeout(); if(_autoWhoEnabled) { _autoWhoCycleTimer.start(); @@ -421,6 +466,27 @@ void CoreNetwork::sendPerform() { } } +void CoreNetwork::restoreUserModes() { + IrcUser *me_ = me(); + Q_ASSERT(me_); + + disconnect(me_, SIGNAL(userModesSet(QString)), this, SLOT(restoreUserModes())); + disconnect(me_, SIGNAL(userModesAdded(QString)), this, SLOT(restoreUserModes())); + + QString removeModes; + QString addModes = Core::userModes(userId(), networkId()); + QString currentModes = me_->userModes(); + + removeModes = currentModes; + removeModes.remove(QRegExp(QString("[%1]").arg(addModes))); + addModes.remove(QRegExp(QString("[%1]").arg(currentModes))); + + removeModes = QString("%1 -%2").arg(me_->nick(), removeModes); + addModes = QString("%1 +%2").arg(me_->nick(), addModes); + userInputHandler()->handleMode(BufferInfo(), removeModes); + userInputHandler()->handleMode(BufferInfo(), addModes); +} + void CoreNetwork::setUseAutoReconnect(bool use) { Network::setUseAutoReconnect(use); if(!use) @@ -447,22 +513,46 @@ void CoreNetwork::doAutoReconnect() { qWarning() << "CoreNetwork::doAutoReconnect(): Cannot reconnect while not being disconnected!"; return; } - if(_autoReconnectCount > 0) - _autoReconnectCount--; + if(_autoReconnectCount > 0 || _autoReconnectCount == -1) + _autoReconnectCount--; // -2 means we delay the next reconnect connectToIrc(true); } void CoreNetwork::sendPing() { - userInputHandler()->handlePing(BufferInfo(), QString()); + uint now = QDateTime::currentDateTime().toTime_t(); + if(_pingCount >= _maxPingCount && now - _lastPingTime <= (uint)(_pingTimer.interval() / 1000) + 1) { + // the second check compares the actual elapsed time since the last ping and the pingTimer interval + // if the interval is shorter then the actual elapsed time it means that this thread was somehow blocked + // and unable to even handle a ping answer. So we ignore those misses. + disconnectFromIrc(false, QString("No Ping reply in %1 seconds.").arg(_maxPingCount * _pingTimer.interval() / 1000), true /* withReconnect */); + } else { + _lastPingTime = now; + _pingCount++; + userInputHandler()->handlePing(BufferInfo(), QString()); + } +} + +void CoreNetwork::enablePingTimeout() { + resetPingTimeout(); + _pingTimer.start(); +} + +void CoreNetwork::disablePingTimeout() { + _pingTimer.stop(); + resetPingTimeout(); } void CoreNetwork::sendAutoWho() { + // Don't send autowho if there are still some pending + if(_autoWhoPending.count()) + return; + while(!_autoWhoQueue.isEmpty()) { QString chan = _autoWhoQueue.takeFirst(); IrcChannel *ircchan = ircChannel(chan); if(!ircchan) continue; if(_autoWhoNickLimit > 0 && ircchan->ircUsers().count() > _autoWhoNickLimit) continue; - _autoWhoInProgress[chan]++; + _autoWhoPending[chan]++; putRawLine("WHO " + serverEncode(chan)); if(_autoWhoQueue.isEmpty() && _autoWhoEnabled && !_autoWhoCycleTimer.isActive()) { // Timer was stopped, means a new cycle is due immediately