X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcorenetwork.cpp;h=29f10f06af865b9c834d0b738653d72d25435219;hp=06c769c8b84b17c659883e29858d2f0725085dcb;hb=714b39660fe19e7f092880019429c8da76ee2bd5;hpb=dce2be6d7f9af7dcc0133ee7f0f42e9ef47be568 diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 06c769c8..29f10f06 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -33,7 +33,6 @@ // IRCv3 capabilities #include "irccap.h" -INIT_SYNCABLE_OBJECT(CoreNetwork) CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) : Network(networkid, session), _coreSession(session), @@ -122,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; } @@ -150,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; } @@ -197,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(); } @@ -287,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; @@ -316,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); @@ -427,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(); @@ -462,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)); @@ -483,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(); @@ -494,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; } @@ -803,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; } } @@ -1308,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. @@ -1375,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; @@ -1494,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; @@ -1504,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;