X-Git-Url: https://git.quassel-irc.org/?a=blobdiff_plain;f=src%2Fcore%2Fcorenetwork.cpp;h=ac69044dcc34da7a1e1e45513361c3b31d35a7b3;hb=4b22388af4ceb1c6c24c8c4436283c1e3c33dd1f;hp=932af6fc472f882625a3d431dc324480a33a7bd4;hpb=d0e9b7a1d5e73041ade519189eea012500440ba9;p=quassel.git diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 932af6fc..ac69044d 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -36,6 +36,7 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) _userInputHandler(new CoreUserInputHandler(this)), _autoReconnectCount(0), _quitRequested(false), + _disconnectExpected(false), _previousConnectionAttemptFailed(false), _lastUsedServerIndex(0), @@ -71,7 +72,6 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(fillBucketAndProcessQueue())); connect(&socket, SIGNAL(connected()), this, SLOT(socketInitialized())); - connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); 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())); @@ -82,7 +82,7 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *))); if (Quassel::isOptionSet("oidentd")) { - connect(this, SIGNAL(socketOpen(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(socketInitialized(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection); connect(this, SIGNAL(socketDisconnected(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(removeSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16))); } } @@ -90,13 +90,39 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) CoreNetwork::~CoreNetwork() { - if (connectionState() != Disconnected && connectionState() != Network::Reconnecting) - disconnectFromIrc(false); // clean up, but this does not count as requested disconnect! + // 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() << ")"; + } + } disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up delete _userInputHandler; } +bool CoreNetwork::forceDisconnect(int msecs) +{ + if (socket.state() == QAbstractSocket::UnconnectedState) { + // Socket already disconnected. + return true; + } + // 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); +} + + QString CoreNetwork::channelDecode(const QString &bufferName, const QByteArray &string) const { if (!bufferName.isEmpty()) { @@ -208,8 +234,11 @@ void CoreNetwork::connectToIrc(bool reconnecting) } -void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool withReconnect) +void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool withReconnect, + bool forceImmediate) { + // Disconnecting from the network, should expect a socket close or error + _disconnectExpected = true; _quitRequested = requested; // see socketDisconnected(); if (!withReconnect) { _autoReconnectTimer.stop(); @@ -236,7 +265,7 @@ void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool socketDisconnected(); } else { if (socket.state() == QAbstractSocket::ConnectedState) { - userInputHandler()->issueQuit(_quitReason); + userInputHandler()->issueQuit(_quitReason, forceImmediate); } else { socket.close(); } @@ -254,16 +283,21 @@ void CoreNetwork::userInput(BufferInfo buf, QString msg) } -void CoreNetwork::putRawLine(QByteArray s) +void CoreNetwork::putRawLine(const QByteArray s, const bool prepend) { - if (_tokenBucket > 0) + if (_tokenBucket > 0) { writeToSocket(s); - else - _msgQueue.append(s); + } else { + if (prepend) { + _msgQueue.prepend(s); + } else { + _msgQueue.append(s); + } + } } -void CoreNetwork::putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix) +void CoreNetwork::putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix, const bool prepend) { QByteArray msg; @@ -280,16 +314,16 @@ void CoreNetwork::putCmd(const QString &cmd, const QList ¶ms, co msg += params[i]; } - putRawLine(msg); + putRawLine(msg, prepend); } -void CoreNetwork::putCmd(const QString &cmd, const QList> ¶ms, const QByteArray &prefix) +void CoreNetwork::putCmd(const QString &cmd, const QList> ¶ms, const QByteArray &prefix, const bool prependAll) { QListIterator> i(params); while (i.hasNext()) { QList msg = i.next(); - putCmd(cmd, msg, prefix); + putCmd(cmd, msg, prefix, prependAll); } } @@ -435,8 +469,10 @@ void CoreNetwork::socketHasData() void CoreNetwork::socketError(QAbstractSocket::SocketError error) { - if (_quitRequested && error == QAbstractSocket::RemoteHostClosedError) + // Ignore socket closed errors if expected + if (_disconnectExpected && error == QAbstractSocket::RemoteHostClosedError) { return; + } _previousConnectionAttemptFailed = true; qWarning() << qPrintable(tr("Could not connect to %1 (%2)").arg(networkName(), socket.errorString())); @@ -458,16 +494,24 @@ void CoreNetwork::socketInitialized() return; } - emit socketOpen(identity, localAddress(), localPort(), peerAddress(), peerPort()); - Server server = usedServer(); + #ifdef HAVE_SSL - if (server.useSsl && !socket.isEncrypted()) + // Non-SSL connections enter here only once, always emit socketInitialized(...) in these cases + // SSL connections call socketInitialized() twice, only emit socketInitialized(...) on the first (not yet encrypted) run + if (!server.useSsl || !socket.isEncrypted()) { + emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort()); + } + + if (server.useSsl && !socket.isEncrypted()) { + // We'll finish setup once we're encrypted, and called again return; + } +#else + emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort()); #endif - socket.setSocketOption(QAbstractSocket::KeepAliveOption, true); - emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort()); + socket.setSocketOption(QAbstractSocket::KeepAliveOption, true); // TokenBucket to avoid sending too much at once _messageDelay = 2200; // this seems to be a safe value (2.2 seconds delay) @@ -489,7 +533,7 @@ void CoreNetwork::socketInitialized() else { nick = identity->nicks()[0]; } - putRawLine(serverEncode(QString("NICK :%1").arg(nick))); + putRawLine(serverEncode(QString("NICK %1").arg(nick))); putRawLine(serverEncode(QString("USER %1 8 * :%2").arg(identity->ident(), identity->realName()))); } @@ -517,6 +561,8 @@ void CoreNetwork::socketDisconnected() setConnected(false); emit disconnected(networkId()); emit socketDisconnected(identityPtr(), localAddress(), localPort(), peerAddress(), peerPort()); + // Reset disconnect expectations + _disconnectExpected = false; if (_quitRequested) { _quitRequested = false; setConnectionState(Network::Disconnected); @@ -538,6 +584,7 @@ void CoreNetwork::socketStateChanged(QAbstractSocket::SocketState socketState) switch (socketState) { case QAbstractSocket::UnconnectedState: state = Network::Disconnected; + socketDisconnected(); break; case QAbstractSocket::HostLookupState: case QAbstractSocket::ConnectingState: @@ -560,6 +607,7 @@ void CoreNetwork::networkInitialized() { setConnectionState(Network::Initialized); setConnected(true); + _disconnectExpected = false; _quitRequested = false; if (useAutoReconnect()) {