From f9f7524c41eb5668f3172cfddaa4d50badcd7fff Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Mon, 7 Dec 2009 22:38:05 +0100 Subject: [PATCH] Actually make the client/core connection settings do something This introduces ping timeout settings and automatic reconnect. If you're using KDE, you'll get Solid support to disconnect and reconnect as your network connection status changes. Thanks to sebas for the initial patch for Solid support! Closes #405, closes #702 --- CMakeLists.txt | 2 +- src/client/coreconnection.cpp | 142 ++++++++++++++++++++++++++++++---- src/client/coreconnection.h | 27 ++++++- 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7df90793..f5e36323 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,7 +205,7 @@ if(WITH_KDE) add_definitions(-DHAVE_KDE ${KDE4_DEFINITIONS}) set(HAVE_KDE 1) set(MOC_DEFINES ${MOC_DEFINES} -DHAVE_KDE) - set(QUASSEL_KDE_LIBRARIES ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBRARY} knotifyconfig) + set(QUASSEL_KDE_LIBRARIES ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBRARY} ${KDE4_SOLID_LIBS} knotifyconfig) # We always use external icons for KDE4 support, since we use its iconloader rather than our own set(EMBED_DATA OFF) else(KDE4_FOUND) diff --git a/src/client/coreconnection.cpp b/src/client/coreconnection.cpp index bf801494..d20e434d 100644 --- a/src/client/coreconnection.cpp +++ b/src/client/coreconnection.cpp @@ -39,16 +39,31 @@ CoreConnection::CoreConnection(CoreAccountModel *model, QObject *parent) _model(model), _blockSize(0), _state(Disconnected), + _wantReconnect(false), _progressMinimum(0), _progressMaximum(-1), _progressValue(-1) { qRegisterMetaType("CoreConnection::ConnectionState"); + _reconnectTimer.setSingleShot(true); + connect(&_reconnectTimer, SIGNAL(timeout()), SLOT(reconnectTimeout())); + +#ifdef HAVE_KDE + connect(Solid::Networking::notifier(), SIGNAL(statusChanged(Solid::Networking::Status)), + SLOT(solidNetworkStatusChanged(Solid::Networking::Status))); +#endif } void CoreConnection::init() { + Client::signalProxy()->setHeartBeatInterval(30); connect(Client::signalProxy(), SIGNAL(disconnected()), SLOT(coreSocketDisconnected())); + + CoreConnectionSettings s; + s.initAndNotify("PingTimeoutInterval", this, SLOT(pingTimeoutIntervalChanged(QVariant)), 60); + s.initAndNotify("ReconnectInterval", this, SLOT(reconnectIntervalChanged(QVariant)), 60); + s.notify("NetworkDetectionMode", this, SLOT(networkDetectionModeChanged(QVariant))); + networkDetectionModeChanged(s.networkDetectionMode()); } void CoreConnection::setProgressText(const QString &text) { @@ -87,7 +102,9 @@ void CoreConnection::updateProgress(int value, int max) { setProgressValue(value); } -void CoreConnection::resetConnection() { +void CoreConnection::resetConnection(bool wantReconnect) { + _wantReconnect = wantReconnect; + if(_socket) { disconnect(_socket, 0, this, 0); _socket->deleteLater(); @@ -102,10 +119,87 @@ void CoreConnection::resetConnection() { setProgressMaximum(-1); // disable setState(Disconnected); + emit connectionMsg(tr("Disconnected from core.")); emit encrypted(false); + + // initiate if a reconnect if appropriate + CoreConnectionSettings s; + if(wantReconnect && s.autoReconnect()) { + _reconnectTimer.start(); + //reconnectToCore(); + } +} + +void CoreConnection::reconnectTimeout() { + if(!_socket) { + CoreConnectionSettings s; + if(_wantReconnect && s.autoReconnect()) { + +#ifdef HAVE_KDE + // If using Solid, we don't want to reconnect if we're offline + if(s.networkDetectionMode() == CoreConnectionSettings::UseSolid) { + if(Solid::Networking::status() != Solid::Networking::Connected + && Solid::Networking::status() != Solid::Networking::Unknown) { + return; + } + } +#endif /* HAVE_KDE */ + + reconnectToCore(); + } + } +} + +void CoreConnection::networkDetectionModeChanged(const QVariant &vmode) { + CoreConnectionSettings s; + CoreConnectionSettings::NetworkDetectionMode mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt(); + if(mode == CoreConnectionSettings::UsePingTimeout) + Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30); + else { + Client::signalProxy()->setMaxHeartBeatCount(-1); + } +} + +void CoreConnection::pingTimeoutIntervalChanged(const QVariant &interval) { + CoreConnectionSettings s; + if(s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout) + Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30); // interval is 30 seconds +} + +void CoreConnection::reconnectIntervalChanged(const QVariant &interval) { + _reconnectTimer.setInterval(interval.toInt() * 1000); } +#ifdef HAVE_KDE + +void CoreConnection::solidNetworkStatusChanged(Solid::Networking::Status status) { + CoreConnectionSettings s; + if(s.networkDetectionMode() != CoreConnectionSettings::UseSolid) + return; + + switch(status) { + case Solid::Networking::Unknown: + case Solid::Networking::Connected: + qDebug() << "Solid: Network status changed to connected or unknown"; + if(state() == Disconnected) { + if(_wantReconnect && s.autoReconnect()) { + reconnectToCore(); + } + } + break; + case Solid::Networking::Unconnected: + qDebug() << "Solid: Disconnected"; + if(!isLocalConnection()) + disconnectFromCore(tr("Network is down"), true); + break; + default: + break; + } +} + +#endif + bool CoreConnection::isEncrypted() const { #ifndef HAVE_SSL return false; @@ -115,6 +209,17 @@ bool CoreConnection::isEncrypted() const { #endif } +bool CoreConnection::isLocalConnection() const { + if(!isConnected()) + return false; + if(currentAccount().isInternal()) + return true; + if(_socket->peerAddress().isInSubnet(QHostAddress::LocalHost, 0x00ffffff)) + return true; + + return false; +} + void CoreConnection::socketStateChanged(QAbstractSocket::SocketState socketState) { QString text; @@ -174,13 +279,13 @@ void CoreConnection::setState(ConnectionState state) { void CoreConnection::coreSocketError(QAbstractSocket::SocketError) { qDebug() << "coreSocketError" << _socket << _socket->errorString(); - emit connectionError(_socket->errorString()); - resetConnection(); + disconnectFromCore(_socket->errorString(), true); } void CoreConnection::coreSocketDisconnected() { emit disconnected(); - resetConnection(); + qDebug() << Q_FUNC_INFO; + resetConnection(true); // FIXME handle disconnects gracefully } @@ -191,14 +296,14 @@ void CoreConnection::coreHasData() { if(!msg.contains("MsgType")) { // This core is way too old and does not even speak our init protocol... emit connectionErrorPopup(tr("The Quassel Core you try to connect to is too old! Please consider upgrading.")); - disconnectFromCore(); + disconnectFromCore(QString(), false); return; } if(msg["MsgType"] == "ClientInitAck") { clientInitAck(msg); } else if(msg["MsgType"] == "ClientInitReject") { emit connectionErrorPopup(msg["Error"].toString()); - disconnectFromCore(); + disconnectFromCore(QString(), false); return; } else if(msg["MsgType"] == "CoreSetupAck") { emit coreSetupSuccess(); @@ -218,7 +323,7 @@ void CoreConnection::coreHasData() { sessionStateReceived(msg["SessionState"].toMap()); break; // this is definitively the last message we process here! } else { - disconnectFromCore(tr("Invalid data received from core")); + disconnectFromCore(tr("Invalid data received from core"), false); return; } } @@ -227,14 +332,21 @@ void CoreConnection::coreHasData() { } } -void CoreConnection::disconnectFromCore(const QString &errorString) { +void CoreConnection::disconnectFromCore() { + disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect +} + +void CoreConnection::disconnectFromCore(const QString &errorString, bool wantReconnect) { + if(!wantReconnect) + _reconnectTimer.stop(); + if(errorString.isEmpty()) emit connectionError(tr("Disconnected")); else emit connectionError(errorString); Client::signalProxy()->removeAllPeers(); - resetConnection(); + resetConnection(wantReconnect); } void CoreConnection::reconnectToCore() { @@ -280,7 +392,7 @@ bool CoreConnection::connectToCore(AccountId accId) { } void CoreConnection::connectToCurrentAccount() { - resetConnection(); + resetConnection(false); if(currentAccount().isInternal()) { if(Quassel::runMode() != Quassel::Monolithic) { @@ -358,7 +470,7 @@ void CoreConnection::clientInitAck(const QVariantMap &msg) { if(ver < Quassel::buildInfo().clientNeedsProtocol) { emit connectionErrorPopup(tr("The Quassel Core you are trying to connect to is too old!
" "Need at least core/client protocol v%1 to connect.").arg(Quassel::buildInfo().clientNeedsProtocol)); - disconnectFromCore(); + disconnectFromCore(QString(), false); return; } @@ -387,7 +499,7 @@ void CoreConnection::clientInitAck(const QVariantMap &msg) { bool accepted = false; emit handleNoSslInCore(&accepted); if(!accepted) { - disconnectFromCore(tr("Unencrypted connection canceled")); + disconnectFromCore(tr("Unencrypted connection canceled"), false); return; } s.setAccountValue("ShowNoCoreSslWarning", false); @@ -432,7 +544,7 @@ void CoreConnection::sslErrors() { emit handleSslErrors(socket, &accepted, &permanently); if(!accepted) { - disconnectFromCore(tr("Unencrypted connection canceled")); + disconnectFromCore(tr("Unencrypted connection canceled"), false); return; } @@ -473,7 +585,7 @@ void CoreConnection::loginToCore(const QString &prevError) { bool valid = false; emit userAuthenticationRequired(&_account, &valid, prevError); // *must* be a synchronous call if(!valid || currentAccount().user().isEmpty() || currentAccount().password().isEmpty()) { - disconnectFromCore(tr("Login canceled")); + disconnectFromCore(tr("Login canceled"), false); return; } } @@ -496,6 +608,8 @@ void CoreConnection::loginSuccess() { _model->createOrUpdateAccount(currentAccount()); _model->save(); + _reconnectTimer.stop(); + setProgressText(tr("Receiving session state")); setState(Synchronizing); emit connectionMsg(tr("Synchronizing to %1...").arg(currentAccount().accountName())); diff --git a/src/client/coreconnection.h b/src/client/coreconnection.h index 2f9d7cc4..6a3f37bf 100644 --- a/src/client/coreconnection.h +++ b/src/client/coreconnection.h @@ -24,6 +24,7 @@ // TODO: support system application proxy (new in Qt 4.6) #include "QPointer" +#include "QTimer" #ifdef HAVE_SSL # include @@ -31,6 +32,10 @@ # include #endif +#ifdef HAVE_KDE +# include +#endif + #include "coreaccount.h" #include "types.h" @@ -54,11 +59,12 @@ public: void init(); - inline ConnectionState state() const; inline bool isConnected() const; + inline ConnectionState state() const; inline CoreAccount currentAccount() const; bool isEncrypted() const; + bool isLocalConnection() const; inline int progressMinimum() const; inline int progressMaximum() const; @@ -72,7 +78,7 @@ public: public slots: bool connectToCore(AccountId = 0); void reconnectToCore(); - void disconnectFromCore(const QString &errorString = QString()); + void disconnectFromCore(); signals: void stateChanged(CoreConnection::ConnectionState); @@ -106,6 +112,7 @@ signals: private slots: void connectToCurrentAccount(); + void disconnectFromCore(const QString &errorString, bool wantReconnect = true); void socketStateChanged(QAbstractSocket::SocketState); void coreSocketError(QAbstractSocket::SocketError); @@ -123,7 +130,7 @@ private slots: void internalSessionStateReceived(const QVariant &packedState); void sessionStateReceived(const QVariantMap &state); - void resetConnection(); + void resetConnection(bool wantReconnect = false); void connectionReady(); void loginToCore(const QString &user, const QString &password, bool remember); // for config wizard @@ -147,15 +154,27 @@ private slots: void sslErrors(); #endif + void networkDetectionModeChanged(const QVariant &mode); + void pingTimeoutIntervalChanged(const QVariant &interval); + void reconnectIntervalChanged(const QVariant &interval); + void reconnectTimeout(); + +#ifdef HAVE_KDE + void solidNetworkStatusChanged(Solid::Networking::Status status); +#endif + private: CoreAccountModel *_model; CoreAccount _account; QVariantMap _coreMsgBuffer; - QPointer _socket; + QPointer _socket; quint32 _blockSize; ConnectionState _state; + QTimer _reconnectTimer; + bool _wantReconnect; + QSet _netsToSync; int _numNetsToSync; int _progressMinimum, _progressMaximum, _progressValue; -- 2.20.1