X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcorenetwork.cpp;h=e0332096fcacde665aceb9d12fa270eeeb2b9d18;hp=b30ac241e8a04c31099342dfcc0ca42b0454334c;hb=fcacaaf16551524c7ebb6114254d005274cc3d63;hpb=4cf9c160f318a71400b032ea6d3031b9e6628a56 diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index b30ac241..e0332096 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -18,10 +18,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include - #include "corenetwork.h" +#include +#include + #include "core.h" #include "coreidentity.h" #include "corenetworkconfig.h" @@ -32,7 +33,6 @@ // IRCv3 capabilities #include "irccap.h" -INIT_SYNCABLE_OBJECT(CoreNetwork) CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) : Network(networkid, session), _coreSession(session), @@ -46,11 +46,15 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) _requestedUserModes('-') { + // Check if raw IRC logging is enabled + _debugLogRawIrc = (Quassel::isOptionSet("debug-irc") || Quassel::isOptionSet("debug-irc-id")); + _debugLogRawNetId = Quassel::optionValue("debug-irc-id").toInt(); + _autoReconnectTimer.setSingleShot(true); - connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout())); + connect(&_socketCloseTimer, &QTimer::timeout, this, &CoreNetwork::socketCloseTimeout); setPingInterval(networkConfig()->pingInterval()); - connect(&_pingTimer, SIGNAL(timeout()), this, SLOT(sendPing())); + connect(&_pingTimer, &QTimer::timeout, this, &CoreNetwork::sendPing); setAutoWhoDelay(networkConfig()->autoWhoDelay()); setAutoWhoInterval(networkConfig()->autoWhoInterval()); @@ -65,39 +69,39 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) storeChannelCipherKey(buffer.toLower(), bufferCiphers[buffer]); } - connect(networkConfig(), SIGNAL(pingTimeoutEnabledSet(bool)), SLOT(enablePingTimeout(bool))); - connect(networkConfig(), SIGNAL(pingIntervalSet(int)), SLOT(setPingInterval(int))); - connect(networkConfig(), SIGNAL(autoWhoEnabledSet(bool)), SLOT(setAutoWhoEnabled(bool))); - connect(networkConfig(), SIGNAL(autoWhoIntervalSet(int)), SLOT(setAutoWhoInterval(int))); - connect(networkConfig(), SIGNAL(autoWhoDelaySet(int)), SLOT(setAutoWhoDelay(int))); + connect(networkConfig(), &NetworkConfig::pingTimeoutEnabledSet, this, &CoreNetwork::enablePingTimeout); + connect(networkConfig(), &NetworkConfig::pingIntervalSet, this, &CoreNetwork::setPingInterval); + connect(networkConfig(), &NetworkConfig::autoWhoEnabledSet, this, &CoreNetwork::setAutoWhoEnabled); + connect(networkConfig(), &NetworkConfig::autoWhoIntervalSet, this, &CoreNetwork::setAutoWhoInterval); + connect(networkConfig(), &NetworkConfig::autoWhoDelaySet, this, &CoreNetwork::setAutoWhoDelay); - connect(&_autoReconnectTimer, SIGNAL(timeout()), this, SLOT(doAutoReconnect())); - connect(&_autoWhoTimer, SIGNAL(timeout()), this, SLOT(sendAutoWho())); - connect(&_autoWhoCycleTimer, SIGNAL(timeout()), this, SLOT(startAutoWhoCycle())); - connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(checkTokenBucket())); + connect(&_autoReconnectTimer, &QTimer::timeout, this, &CoreNetwork::doAutoReconnect); + connect(&_autoWhoTimer, &QTimer::timeout, this, &CoreNetwork::sendAutoWho); + connect(&_autoWhoCycleTimer, &QTimer::timeout, this, &CoreNetwork::startAutoWhoCycle); + connect(&_tokenBucketTimer, &QTimer::timeout, this, &CoreNetwork::checkTokenBucket); connect(&socket, SIGNAL(connected()), this, SLOT(socketInitialized())); connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); - connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState))); - connect(&socket, SIGNAL(readyRead()), this, SLOT(socketHasData())); + connect(&socket, &QAbstractSocket::stateChanged, this, &CoreNetwork::socketStateChanged); + connect(&socket, &QIODevice::readyRead, this, &CoreNetwork::socketHasData); #ifdef HAVE_SSL connect(&socket, SIGNAL(encrypted()), this, SLOT(socketInitialized())); connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); #endif - connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *))); + connect(this, &CoreNetwork::newEvent, coreSession()->eventManager(), &EventManager::postEvent); // Custom rate limiting // These react to the user changing settings in the client - connect(this, SIGNAL(useCustomMessageRateSet(bool)), SLOT(updateRateLimiting())); - connect(this, SIGNAL(messageRateBurstSizeSet(quint32)), SLOT(updateRateLimiting())); - connect(this, SIGNAL(messageRateDelaySet(quint32)), SLOT(updateRateLimiting())); - connect(this, SIGNAL(unlimitedMessageRateSet(bool)), SLOT(updateRateLimiting())); + connect(this, &Network::useCustomMessageRateSet, this, &CoreNetwork::updateRateLimiting); + connect(this, &Network::messageRateBurstSizeSet, this, &CoreNetwork::updateRateLimiting); + connect(this, &Network::messageRateDelaySet, this, &CoreNetwork::updateRateLimiting); + connect(this, &Network::unlimitedMessageRateSet, this, &CoreNetwork::updateRateLimiting); // IRCv3 capability handling // These react to CAP messages from the server - connect(this, SIGNAL(capAdded(QString)), this, SLOT(serverCapAdded(QString))); - connect(this, SIGNAL(capAcknowledged(QString)), this, SLOT(serverCapAcknowledged(QString))); - connect(this, SIGNAL(capRemoved(QString)), this, SLOT(serverCapRemoved(QString))); + connect(this, &Network::capAdded, this, &CoreNetwork::serverCapAdded); + connect(this, &Network::capAcknowledged, this, &CoreNetwork::serverCapAcknowledged); + connect(this, &Network::capRemoved, this, &CoreNetwork::serverCapRemoved); if (Quassel::isOptionSet("oidentd")) { connect(this, SIGNAL(socketInitialized(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16, qint64)), @@ -117,23 +121,12 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) CoreNetwork::~CoreNetwork() { - // Request a proper disconnect, but don't count as user-requested disconnect - if (socketConnected()) { - // Only try if the socket's fully connected (not initializing or disconnecting). - // Force an immediate disconnect, jumping the command queue. Ensures the proper QUIT is - // shown even if other messages are queued. - disconnectFromIrc(false, QString(), false, true); - // Process the putCmd events that trigger the quit. Without this, shutting down the core - // results in abrubtly closing the socket rather than sending the QUIT as expected. - QCoreApplication::processEvents(); - // Wait briefly for each network to disconnect. Sometimes it takes a little while to send. - if (!forceDisconnect()) { - qWarning() << "Timed out quitting network" << networkName() << - "(user ID " << userId() << ")"; - } + // Ensure we don't get any more signals from the socket while shutting down + disconnect(&socket, nullptr, this, nullptr); + if (!forceDisconnect()) { + qWarning() << QString{"Could not disconnect from network %1 (network ID: %2, user ID: %3)"} + .arg(networkName()).arg(networkId().toInt()).arg(userId().toInt()); } - disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up - delete _userInputHandler; } @@ -145,8 +138,10 @@ bool CoreNetwork::forceDisconnect(int msecs) } // Request a socket-level disconnect if not already happened socket.disconnectFromHost(); - // Return the result of waiting for disconnect; true if successful, otherwise false - return socket.waitForDisconnected(msecs); + if (socket.state() != QAbstractSocket::UnconnectedState) { + return socket.waitForDisconnected(msecs); + } + return true; } @@ -192,6 +187,10 @@ QByteArray CoreNetwork::userEncode(const QString &userNick, const QString &strin void CoreNetwork::connectToIrc(bool reconnecting) { + if (_shuttingDown) { + return; + } + if (Core::instance()->identServer()) { _socketId = Core::instance()->identServer()->addWaitingSocket(); } @@ -282,8 +281,7 @@ void CoreNetwork::connectToIrc(bool reconnecting) } -void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool withReconnect, - bool forceImmediate) +void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool withReconnect) { // Disconnecting from the network, should expect a socket close or error _disconnectExpected = true; @@ -311,20 +309,38 @@ void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting. (%1)").arg((!requested && !withReconnect) ? tr("Core Shutdown") : _quitReason)); if (socket.state() == QAbstractSocket::UnconnectedState) { socketDisconnected(); - } else { + } + else { if (socket.state() == QAbstractSocket::ConnectedState) { - userInputHandler()->issueQuit(_quitReason, forceImmediate); - } else { + // If shutting down, prioritize the QUIT command + userInputHandler()->issueQuit(_quitReason, _shuttingDown); + } + else { socket.close(); } - if (requested || withReconnect) { - // the irc server has 10 seconds to close the socket + if (socket.state() != QAbstractSocket::UnconnectedState) { + // Wait for up to 10 seconds for the socket to close cleanly, then it will be forcefully aborted _socketCloseTimer.start(10000); } } } +void CoreNetwork::socketCloseTimeout() +{ + qWarning() << QString{"Timed out quitting network %1 (network ID: %2, user ID: %3)"} + .arg(networkName()).arg(networkId().toInt()).arg(userId().toInt()); + socket.abort(); +} + + +void CoreNetwork::shutdown() +{ + _shuttingDown = true; + disconnectFromIrc(false, {}, false); +} + + void CoreNetwork::userInput(BufferInfo buf, QString msg) { userInputHandler()->handleUserInput(buf, msg); @@ -422,32 +438,32 @@ void CoreNetwork::removeChannelKey(const QString &channel) Cipher *CoreNetwork::cipher(const QString &target) { if (target.isEmpty()) - return 0; + return nullptr; if (!Cipher::neededFeaturesAvailable()) - return 0; + return nullptr; - CoreIrcChannel *channel = qobject_cast(ircChannel(target)); + auto *channel = qobject_cast(ircChannel(target)); if (channel) { return channel->cipher(); } - CoreIrcUser *user = qobject_cast(ircUser(target)); + auto *user = qobject_cast(ircUser(target)); if (user) { return user->cipher(); } else if (!isChannelName(target)) { return qobject_cast(newIrcUser(target))->cipher(); } - return 0; + return nullptr; } QByteArray CoreNetwork::cipherKey(const QString &target) const { - CoreIrcChannel *c = qobject_cast(ircChannel(target)); + auto *c = qobject_cast(ircChannel(target)); if (c) return c->cipher()->key(); - CoreIrcUser *u = qobject_cast(ircUser(target)); + auto *u = qobject_cast(ircUser(target)); if (u) return u->cipher()->key(); @@ -457,14 +473,14 @@ QByteArray CoreNetwork::cipherKey(const QString &target) const void CoreNetwork::setCipherKey(const QString &target, const QByteArray &key) { - CoreIrcChannel *c = qobject_cast(ircChannel(target)); + auto *c = qobject_cast(ircChannel(target)); if (c) { c->setEncrypted(c->cipher()->setKey(key)); coreSession()->setBufferCipher(networkId(), target, key); return; } - CoreIrcUser *u = qobject_cast(ircUser(target)); + auto *u = qobject_cast(ircUser(target)); if (!u && !isChannelName(target)) u = qobject_cast(newIrcUser(target)); @@ -478,10 +494,10 @@ void CoreNetwork::setCipherKey(const QString &target, const QByteArray &key) bool CoreNetwork::cipherUsesCBC(const QString &target) { - CoreIrcChannel *c = qobject_cast(ircChannel(target)); + auto *c = qobject_cast(ircChannel(target)); if (c) return c->cipher()->usesCBC(); - CoreIrcUser *u = qobject_cast(ircUser(target)); + auto *u = qobject_cast(ircUser(target)); if (u) return u->cipher()->usesCBC(); @@ -489,13 +505,13 @@ bool CoreNetwork::cipherUsesCBC(const QString &target) } #endif /* HAVE_QCA2 */ -bool CoreNetwork::setAutoWhoDone(const QString &channel) +bool CoreNetwork::setAutoWhoDone(const QString &name) { - QString chan = channel.toLower(); - if (_autoWhoPending.value(chan, 0) <= 0) + QString chanOrNick = name.toLower(); + if (_autoWhoPending.value(chanOrNick, 0) <= 0) return false; - if (--_autoWhoPending[chan] <= 0) - _autoWhoPending.remove(chan); + if (--_autoWhoPending[chanOrNick] <= 0) + _autoWhoPending.remove(chanOrNick); return true; } @@ -723,8 +739,8 @@ void CoreNetwork::sendPerform() restoreUserModes(); } else { - connect(me_, SIGNAL(userModesSet(QString)), this, SLOT(restoreUserModes())); - connect(me_, SIGNAL(userModesAdded(QString)), this, SLOT(restoreUserModes())); + connect(me_, &IrcUser::userModesSet, this, &CoreNetwork::restoreUserModes); + connect(me_, &IrcUser::userModesAdded, this, &CoreNetwork::restoreUserModes); } } @@ -758,8 +774,8 @@ 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())); + disconnect(me_, &IrcUser::userModesSet, this, &CoreNetwork::restoreUserModes); + disconnect(me_, &IrcUser::userModesAdded, this, &CoreNetwork::restoreUserModes); QString modesDelta = Core::userModes(userId(), networkId()); QString currentModes = me_->userModes(); @@ -798,20 +814,20 @@ void CoreNetwork::updateIssuedModes(const QString &requestedModes) QString removeModes; bool addMode = true; - for (int i = 0; i < requestedModes.length(); i++) { - if (requestedModes[i] == '+') { + for (auto requestedMode : requestedModes) { + if (requestedMode == '+') { addMode = true; continue; } - if (requestedModes[i] == '-') { + if (requestedMode == '-') { addMode = false; continue; } if (addMode) { - addModes += requestedModes[i]; + addModes += requestedMode; } else { - removeModes += requestedModes[i]; + removeModes += requestedMode; } } @@ -1303,12 +1319,12 @@ void CoreNetwork::startAutoWhoCycle() _autoWhoQueue = channels(); } -void CoreNetwork::queueAutoWhoOneshot(const QString &channelOrNick) +void CoreNetwork::queueAutoWhoOneshot(const QString &name) { // Prepend so these new channels/nicks are the first to be checked // Don't allow duplicates - if (!_autoWhoQueue.contains(channelOrNick.toLower())) { - _autoWhoQueue.prepend(channelOrNick.toLower()); + if (!_autoWhoQueue.contains(name.toLower())) { + _autoWhoQueue.prepend(name.toLower()); } if (capEnabled(IrcCap::AWAY_NOTIFY)) { // When away-notify is active, the timer's stopped. Start a new cycle to who this channel. @@ -1370,11 +1386,23 @@ void CoreNetwork::sendAutoWho() } if (supports("WHOX")) { // Use WHO extended to poll away users and/or user accounts + // Explicitly only match on nickname ("n"), don't rely on server defaults + // + // WHO n%chtsunfra, + // // See http://faerion.sourceforge.net/doc/irc/whox.var - // And https://github.com/hexchat/hexchat/blob/c874a9525c9b66f1d5ddcf6c4107d046eba7e2c5/src/common/proto-irc.c#L750 - putRawLine(serverEncode(QString("WHO %1 %%chtsunfra,%2") - .arg(serverEncode(chanOrNick), QString::number(IrcCap::ACCOUNT_NOTIFY_WHOX_NUM)))); + // And https://github.com/quakenet/snircd/blob/master/doc/readme.who + // And https://github.com/hexchat/hexchat/blob/57478b65758e6b697b1d82ce21075e74aa475efc/src/common/proto-irc.c#L752 + putRawLine(serverEncode(QString("WHO %1 n%chtsunfra,%2") + .arg(serverEncode(chanOrNick), + QString::number(IrcCap::ACCOUNT_NOTIFY_WHOX_NUM)))); } else { + // Fall back to normal WHO + // + // Note: According to RFC 1459, "WHO " can fall back to searching realname, + // hostmask, etc. There's nothing we can do about that :( + // + // See https://tools.ietf.org/html/rfc1459#section-4.5.1 putRawLine(serverEncode(QString("WHO %1").arg(chanOrNick))); } break; @@ -1460,6 +1488,12 @@ void CoreNetwork::fillBucketAndProcessQueue() void CoreNetwork::writeToSocket(const QByteArray &data) { + // Log the message if enabled and network ID matches or allows all + if (_debugLogRawIrc + && (_debugLogRawNetId == -1 || networkId().toInt() == _debugLogRawNetId)) { + // Include network ID + qDebug() << "IRC net" << networkId() << ">>" << data; + } socket.write(data); socket.write("\r\n"); if (!_skipMessageRates) { @@ -1483,6 +1517,9 @@ Network::Server CoreNetwork::usedServer() const void CoreNetwork::requestConnect() const { + if (_shuttingDown) { + return; + } if (connectionState() != Disconnected) { qWarning() << "Requesting connect while already being connected!"; return; @@ -1493,6 +1530,9 @@ void CoreNetwork::requestConnect() const void CoreNetwork::requestDisconnect() const { + if (_shuttingDown) { + return; + } if (connectionState() == Disconnected) { qWarning() << "Requesting disconnect while not being connected!"; return;