From a006c6962caa403ef9f07907856cd178b50f77f2 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 11 Dec 2014 15:43:48 +0100 Subject: [PATCH] Move account state related logic into Account. The account state is now managed mostly by the Account itself instead of through Application. The gui can still control whether an account is signed out or not. --- src/gui/application.cpp | 74 ++++---------- src/gui/application.h | 8 +- src/gui/folder.cpp | 10 -- src/gui/folder.h | 1 - src/libsync/account.cpp | 146 ++++++++++++++++++++++++++-- src/libsync/account.h | 57 +++++++++-- src/libsync/connectionvalidator.cpp | 42 +++----- src/libsync/connectionvalidator.h | 5 +- src/libsync/networkjobs.cpp | 19 +--- src/libsync/quotainfo.cpp | 20 +--- src/libsync/quotainfo.h | 1 - 11 files changed, 231 insertions(+), 152 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 8c84430e66..c9b356dcf7 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -165,8 +165,7 @@ void Application::slotLogin() Account *a = AccountManager::instance()->account(); if (a) { FolderMan::instance()->setupFolders(); - _userTriggeredConnect = true; - slotCheckConnection(); + a->setSignedOut(false); } } @@ -180,7 +179,7 @@ void Application::slotLogout() FolderMan *folderMan = FolderMan::instance(); folderMan->setSyncEnabled(false); folderMan->terminateSyncProcess(); - a->setState(Account::SignedOut); + a->setSignedOut(true); // show result _gui->slotComputeOverallSyncStatus(); } @@ -190,12 +189,12 @@ void Application::slotAccountChanged(Account *newAccount, Account *oldAccount) { if (oldAccount) { disconnect(oldAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged())); - disconnect(oldAccount, SIGNAL(stateChanged(int)), this, SLOT(slotToggleFolderman(int))); + disconnect(oldAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int))); connect(oldAccount->quotaInfo(), SIGNAL(quotaUpdated(qint64,qint64)), _gui, SLOT(slotRefreshQuotaDisplay(qint64,qint64))); } connect(newAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged())); - connect(newAccount, SIGNAL(stateChanged(int)), this, SLOT(slotToggleFolderman(int))); + connect(newAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int))); connect(newAccount->quotaInfo(), SIGNAL(quotaUpdated(qint64,qint64)), _gui, SLOT(slotRefreshQuotaDisplay(qint64,qint64))); } @@ -226,20 +225,7 @@ void Application::slotCheckConnection() Account *account = AccountManager::instance()->account(); if( account ) { - if (account->state() == Account::InvalidCredential - || account->state() == Account::SignedOut) { - //Do not try to connect if we are logged out - if (!_userTriggeredConnect) { - return; - } - } - - if (_conValidator) - _conValidator->deleteLater(); - _conValidator = new ConnectionValidator(account); - connect( _conValidator, SIGNAL(connectionResult(ConnectionValidator::Status, QStringList)), - this, SLOT(slotConnectionValidatorResult(ConnectionValidator::Status, QStringList)) ); - _conValidator->checkConnection(); + account->checkConnectivity(); } else { // let gui open the setup wizard @@ -249,28 +235,7 @@ void Application::slotCheckConnection() } } -void Application::slotCredentialsFetched() -{ - Account *account = AccountManager::instance()->account(); - Q_ASSERT(account); - if (!account) { - qDebug() << Q_FUNC_INFO << "No account!"; - return; - } - disconnect(account->credentials(), SIGNAL(fetched()), this, SLOT(slotCredentialsFetched())); - if (!account->credentials()->ready()) { - // User canceled the connection or did not give a password - account->setState(Account::SignedOut); - return; - } - if (account->state() == Account::InvalidCredential) { - // Then we ask again for the credentials if they are wrong again - account->setState(Account::Disconnected); - } - slotCheckConnection(); -} - -void Application::slotToggleFolderman(int state) +void Application::slotAccountStateChanged(int state) { FolderMan* folderMan = FolderMan::instance(); switch (state) { @@ -280,7 +245,8 @@ void Application::slotToggleFolderman(int state) folderMan->slotScheduleAllFolders(); break; case Account::SignedOut: - case Account::InvalidCredential: + case Account::ConfigurationError: + case Account::NetworkError: case Account::Disconnected: qDebug() << "Disabling sync scheduler, terminating sync"; folderMan->setSyncEnabled(false); @@ -289,13 +255,15 @@ void Application::slotToggleFolderman(int state) } // Stop checking the connection if we're manually signed out or - // when the credentials are wrong. + // when the error is permanent. if (state == Account::SignedOut - || state == Account::InvalidCredential) { + || state == Account::ConfigurationError) { _checkConnectionTimer.stop(); } else if (! _checkConnectionTimer.isActive()) { _checkConnectionTimer.start(); } + + slotUpdateConnectionErrors(state); } void Application::slotCrash() @@ -303,19 +271,17 @@ void Application::slotCrash() Utility::crash(); } -void Application::slotConnectionValidatorResult(ConnectionValidator::Status status, - const QStringList& errors) +void Application::slotUpdateConnectionErrors(int accountState) { - qDebug() << "Connection Validator Result: " << ConnectionValidator::statusString(status); - - bool isConnected = status == ConnectionValidator::Connected; + bool isConnected = accountState == Account::Connected; if( !isConnected ) { - _startupNetworkError = ConnectionValidator::isNetworkError(status); - if (_userTriggeredConnect) { - _userTriggeredConnect = false; - } + _startupNetworkError = accountState == Account::NetworkError; + } + + Account *account = AccountManager::instance()->account(); + if (account) { + _gui->setConnectionErrors( isConnected, account->connectionErrors() ); } - _gui->setConnectionErrors( isConnected, errors ); } void Application::slotownCloudWizardDone( int res ) diff --git a/src/gui/application.h b/src/gui/application.h index 861c2a53b6..c491887bb7 100644 --- a/src/gui/application.h +++ b/src/gui/application.h @@ -73,16 +73,14 @@ signals: protected slots: void slotParseOptions( const QString&, QObject* ); void slotCheckConnection(); - void slotConnectionValidatorResult(ConnectionValidator::Status, - const QStringList& errors); + void slotUpdateConnectionErrors(int accountState); void slotStartUpdateDetector(); void slotUseMonoIconsChanged( bool ); void slotLogin(); void slotLogout(); void slotCleanup(); void slotAccountChanged(Account *newAccount, Account *oldAccount = 0); - void slotCredentialsFetched(); - void slotToggleFolderman(int state); + void slotAccountStateChanged(int state); void slotCrash(); private: @@ -90,8 +88,6 @@ private: QPointer _gui; - QPointer _conValidator; - Theme *_theme; bool _helpOnly; diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index a4159fb68a..0c72057c58 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -305,7 +305,6 @@ void Folder::slotRunEtagJob() _requestEtagJob = new RequestEtagJob(account, remotePath(), this); // check if the etag is different QObject::connect(_requestEtagJob, SIGNAL(etagRetreived(QString)), this, SLOT(etagRetreived(QString))); - QObject::connect(_requestEtagJob, SIGNAL(networkError(QNetworkReply*)), this, SLOT(slotNetworkUnavailable())); FolderMan::instance()->slotScheduleETagJob(alias(), _requestEtagJob); // The _requestEtagJob is auto deleting itself on finish. Our guard pointer _requestEtagJob will then be null. } @@ -324,15 +323,6 @@ void Folder::etagRetreived(const QString& etag) } } -void Folder::slotNetworkUnavailable() -{ - Account *account = AccountManager::instance()->account(); - if (account && account->state() == Account::Connected) { - account->setState(Account::Disconnected); - } - emit syncStateChange(); -} - void Folder::bubbleUpSyncResult() { // count new, removed and updated items diff --git a/src/gui/folder.h b/src/gui/folder.h index ae16068ea8..e431399f23 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -178,7 +178,6 @@ private slots: void slotRunEtagJob(); void etagRetreived(const QString &); - void slotNetworkUnavailable(); void slotAboutToPropagate(const SyncFileItemVector& ); void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index b3b47d91c4..bd906fede7 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -74,6 +74,8 @@ Account::Account(AbstractSslErrorHandler *sslErrorHandler, QObject *parent) , _credentials(0) , _treatSslErrorsAsFailure(false) , _state(Account::Disconnected) + , _connectionStatus(ConnectionValidator::Undefined) + , _waitingForNewCredentials(false) , _davPath("remote.php/webdav/") , _wasMigrated(false) { @@ -365,42 +367,166 @@ void Account::setCredentialSetting(const QString &key, const QVariant &value) } } -int Account::state() const +Account::ConnectionStatus Account::connectionStatus() const +{ + return _connectionStatus; +} + +QStringList Account::connectionErrors() const +{ + return _connectionErrors; +} + +QString Account::connectionStatusString(ConnectionStatus status) +{ + return ConnectionValidator::statusString(status); +} + +Account::State Account::state() const { return _state; } -void Account::setState(int state) +void Account::setState(State state) { if (_state != state) { qDebug() << "Account state change: " << stateString(_state) << "->" << stateString(state); + State oldState = _state; _state = state; - emit stateChanged(state); + + if (_state == SignedOut) { + _connectionStatus = ConnectionValidator::Undefined; + _connectionErrors.clear(); + } else if (oldState == SignedOut && _state == Disconnected) { + checkConnectivity(); + } + + emit stateChanged(_state); } } -QString Account::stateString(int state) +QString Account::stateString(State state) { switch (state) { - case Connected: - return QLatin1String("Connected"); - case Disconnected: - return QLatin1String("Disconnected"); case SignedOut: return QLatin1String("SignedOut"); - case InvalidCredential: - return QLatin1String("InvalidCredential"); + case Disconnected: + return QLatin1String("Disconnected"); + case Connected: + return QLatin1String("Connected"); + case NetworkError: + return QLatin1String("NetworkError"); + case ConfigurationError: + return QLatin1String("ConfigurationError"); } return QLatin1String("Unknown"); } +bool Account::isSignedOut() const +{ + return _state == SignedOut; +} + +void Account::setSignedOut(bool signedOut) +{ + if (signedOut) { + setState(SignedOut); + } else { + setState(Disconnected); + } +} + QuotaInfo *Account::quotaInfo() { return _quotaInfo; } +void Account::checkConnectivity() +{ + if (isSignedOut() || _waitingForNewCredentials) { + return; + } + + ConnectionValidator * conValidator = new ConnectionValidator(this); + connect(conValidator, SIGNAL(connectionResult(ConnectionValidator::Status,QStringList)), + SLOT(slotConnectionValidatorResult(ConnectionValidator::Status,QStringList))); + conValidator->checkConnection(); +} + +void Account::slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList& errors) +{ + if (isSignedOut()) { + return; + } + + switch (status) + { + case ConnectionValidator::Connected: + setState(Connected); + break; + case ConnectionValidator::Undefined: + case ConnectionValidator::NotConfigured: + setState(Disconnected); + break; + case ConnectionValidator::ServerVersionMismatch: + case ConnectionValidator::StatusNotFound: + setState(ConfigurationError); + break; + case ConnectionValidator::CredentialsWrong: + handleInvalidCredentials(); + break; + case ConnectionValidator::Timeout: + setState(NetworkError); + break; + } + _connectionErrors = errors; + + if (_connectionStatus != status) { + qDebug() << "Account connection status change: " + << connectionStatusString(_connectionStatus) << "->" + << connectionStatusString(status); + _connectionStatus = status; + } +} + +void Account::handleInvalidCredentials() +{ + if (isSignedOut()) { + return; + } + + setState(ConfigurationError); + _waitingForNewCredentials = true; + + // invalidate & forget token/password + // but try to re-sign in. + connect(_credentials, SIGNAL(fetched()), + SLOT(slotCredentialsFetched()), Qt::UniqueConnection); + if (_credentials->ready()) { + _credentials->invalidateAndFetch(this); + } else { + _credentials->fetch(this); + } +} + +void Account::slotCredentialsFetched() +{ + _waitingForNewCredentials = false; + + disconnect(_credentials, SIGNAL(fetched()), + this, SLOT(slotCredentialsFetched())); + + if (!_credentials->ready()) { + // User canceled the connection or did not give a password + setState(SignedOut); + return; + } + + checkConnectivity(); +} + void Account::slotHandleErrors(QNetworkReply *reply , QList errors) { NetworkJobTimeoutPauser pauser(reply); diff --git a/src/libsync/account.h b/src/libsync/account.h index d7b0e763e1..fd4d30cc37 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -23,6 +23,7 @@ #include #include #include "utility.h" +#include "connectionvalidator.h" class QSettings; class QNetworkReply; @@ -68,11 +69,28 @@ public: class OWNCLOUDSYNC_EXPORT Account : public QObject { Q_OBJECT public: - enum State { Disconnected = 0, /// no network connection - Connected, /// account is online - SignedOut, /// Disconnected + credential token has been discarded - InvalidCredential /// The credentials are invalid and we are asking the user for them - }; + enum State { + /// Not even attempting to connect, most likely because the + /// user explicitly signed out or cancelled a credential dialog. + SignedOut, + + /// Account would like to be connected but hasn't heard back yet. + Disconnected, + + /// The account is successfully talking to the server. + Connected, + + /// Could not communicate with the server for some reason. + /// We assume this may resolve itself over time and will try + /// again automatically. + NetworkError, + + /// An error like invalid credentials where retrying won't help. + ConfigurationError + }; + + /// The actual current connectivity status. + typedef ConnectionValidator::Status ConnectionStatus; QString davPath() const { return _davPath; } void setDavPath(const QString&s) { _davPath = s; } @@ -146,9 +164,15 @@ public: QVariant credentialSetting(const QString& key) const; void setCredentialSetting(const QString& key, const QVariant &value); - int state() const; - void setState(int state); - static QString stateString(int state); + ConnectionStatus connectionStatus() const; + QStringList connectionErrors() const; + static QString connectionStatusString(ConnectionStatus status); + + State state() const; + static QString stateString(State status); + + bool isSignedOut() const; + void setSignedOut(bool signedOut); void clearCookieJar(); @@ -156,12 +180,24 @@ public: QuotaInfo *quotaInfo(); + /// Triggers a ping to the server to update state and + /// connection status and errors. + void checkConnectivity(); + + /// Called when a request fails because of a credential error. + void handleInvalidCredentials(); + +private: + void setState(State state); + signals: void stateChanged(int state); void propagatorNetworkActivity(); protected Q_SLOTS: void slotHandleErrors(QNetworkReply*,QList); + void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList& errors); + void slotCredentialsFetched(); private: QMap _settingsMap; @@ -173,7 +209,10 @@ private: QNetworkAccessManager *_am; AbstractCredentials* _credentials; bool _treatSslErrorsAsFailure; - int _state; + State _state; + ConnectionStatus _connectionStatus; + QStringList _connectionErrors; + bool _waitingForNewCredentials; static QString _configFileName; QString _davPath; // default "remote.php/webdav/"; bool _wasMigrated; diff --git a/src/libsync/connectionvalidator.cpp b/src/libsync/connectionvalidator.cpp index ecb1c20dda..0ce78a91f3 100644 --- a/src/libsync/connectionvalidator.cpp +++ b/src/libsync/connectionvalidator.cpp @@ -30,36 +30,28 @@ ConnectionValidator::ConnectionValidator(Account *account, QObject *parent) QString ConnectionValidator::statusString( Status stat ) { - QString re; - switch( stat ) { case Undefined: - re = QLatin1String("Undefined"); - break; + return QLatin1String("Undefined"); case Connected: - re = QLatin1String("Connected"); - break; + return QLatin1String("Connected"); case NotConfigured: - re = QLatin1String("NotConfigured"); - break; + return QLatin1String("NotConfigured"); case ServerVersionMismatch: - re = QLatin1String("Server Version Mismatch"); - break; + return QLatin1String("Server Version Mismatch"); case CredentialsWrong: - re = QLatin1String("Credentials Wrong"); - break; + return QLatin1String("Credentials Wrong"); case StatusNotFound: - re = QLatin1String("Status not found"); - break; - default: - re = QLatin1String("status undeclared."); + return QLatin1String("Status not found"); + case Timeout: + return QLatin1String("Timeout"); } - return re; + return QLatin1String("status undeclared."); } bool ConnectionValidator::isNetworkError( Status status ) { - return status == StatusNotFound; + return status == Timeout; } void ConnectionValidator::checkConnection() @@ -70,7 +62,7 @@ void ConnectionValidator::checkConnection() return; } - if( _account->state() == Account::Connected ) { + if( _account->connectionStatus() == Connected ) { // When we're already connected, just make sure a minimal request // gets replied to. slotCheckAuthentication(); @@ -113,8 +105,6 @@ void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &inf // status.php could not be loaded. void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply) { - _account->setState(Account::Disconnected); - _errors.append(tr("Unable to connect to %1").arg(_account->url().toString())); _errors.append( reply->errorString() ); reportResult( StatusNotFound ); @@ -122,11 +112,9 @@ void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply) void ConnectionValidator::slotJobTimeout(const QUrl &url) { - _account->setState(Account::Disconnected); - _errors.append(tr("Unable to connect to %1").arg(url.toString())); _errors.append(tr("timeout")); - reportResult( StatusNotFound ); + reportResult( Timeout ); } @@ -148,7 +136,7 @@ void ConnectionValidator::slotCheckAuthentication() void ConnectionValidator::slotAuthFailed(QNetworkReply *reply) { - Status stat = StatusNotFound; + Status stat = Timeout; if( reply->error() == QNetworkReply::AuthenticationRequiredError || reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user/pwd is wrong. @@ -156,9 +144,6 @@ void ConnectionValidator::slotAuthFailed(QNetworkReply *reply) qDebug() << "******** Password is wrong!"; _errors << tr("The provided credentials are not correct"); stat = CredentialsWrong; - if (_account->state() != Account::SignedOut) { - _account->setState(Account::Disconnected); - } } else if( reply->error() != QNetworkReply::NoError ) { _errors << reply->errorString(); @@ -169,7 +154,6 @@ void ConnectionValidator::slotAuthFailed(QNetworkReply *reply) void ConnectionValidator::slotAuthSuccess() { - _account->setState(Account::Connected); _errors.clear(); reportResult(Connected); } diff --git a/src/libsync/connectionvalidator.h b/src/libsync/connectionvalidator.h index 3d3da6afb2..8d356403bf 100644 --- a/src/libsync/connectionvalidator.h +++ b/src/libsync/connectionvalidator.h @@ -36,8 +36,9 @@ public: NotConfigured, ServerVersionMismatch, CredentialsWrong, - // actually also used for timeouts or errors on the authed request - StatusNotFound + StatusNotFound, + // actually also used for other errors on the authed request + Timeout }; void checkConnection(); diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp index cf489bf44d..660f561fbd 100644 --- a/src/libsync/networkjobs.cpp +++ b/src/libsync/networkjobs.cpp @@ -166,7 +166,6 @@ void AbstractNetworkJob::slotFinished() _responseTimestamp = QString::fromAscii(_reply->rawHeader("Date")); _duration = _durationTimer.elapsed(); - if (_followRedirects) { // ### the qWarnings here should be exported via displayErrors() so they // ### can be presented to the user if the job executor has a GUI @@ -188,22 +187,12 @@ void AbstractNetworkJob::slotFinished() } } - bool discard = finished(); AbstractCredentials *creds = _account->credentials(); - if (!creds->stillValid(_reply) &&! _ignoreCredentialFailure - && _account->state() != Account::InvalidCredential) { - _account->setState(Account::InvalidCredential); - - // invalidate & forget token/password - // but try to re-sign in. - connect( creds, SIGNAL(fetched()), - qApp, SLOT(slotCredentialsFetched()), Qt::UniqueConnection); - if (creds->ready()) { - creds->invalidateAndFetch(_account); - } else { - creds->fetch(_account); - } + if (!creds->stillValid(_reply) && ! _ignoreCredentialFailure) { + _account->handleInvalidCredentials(); } + + bool discard = finished(); if (discard) { deleteLater(); } diff --git a/src/libsync/quotainfo.cpp b/src/libsync/quotainfo.cpp index 1d5e655552..f201334990 100644 --- a/src/libsync/quotainfo.cpp +++ b/src/libsync/quotainfo.cpp @@ -34,29 +34,19 @@ QuotaInfo::QuotaInfo(Account *account) , _lastQuotaUsedBytes(0) , _jobRestartTimer(new QTimer(this)) { - connect(_account, SIGNAL(stateChanged(int)), SLOT(slotAccountStateChanged(int))); + connect(_account, SIGNAL(stateChanged(int)), + SLOT(slotAccountStateChanged(int))); connect(_jobRestartTimer, SIGNAL(timeout()), SLOT(slotCheckQuota())); _jobRestartTimer->setSingleShot(true); _jobRestartTimer->start(initialTimeT); } -void QuotaInfo::slotAccountChanged(Account *newAccount, Account *oldAccount) -{ - _account = newAccount; - disconnect(oldAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int))); - connect(newAccount, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int))); -} - void QuotaInfo::slotAccountStateChanged(int state) { - switch (state) { - case Account::SignedOut: // fall through - case Account::InvalidCredential: - case Account::Disconnected: - _jobRestartTimer->stop(); - break; - case Account::Connected: // fall through + if (state == Account::Connected) { slotCheckQuota(); + } else { + _jobRestartTimer->stop(); } } diff --git a/src/libsync/quotainfo.h b/src/libsync/quotainfo.h index 4af6b27cde..8d75d771aa 100644 --- a/src/libsync/quotainfo.h +++ b/src/libsync/quotainfo.h @@ -36,7 +36,6 @@ public Q_SLOTS: void slotCheckQuota(); private Q_SLOTS: - void slotAccountChanged(Account *newAccount, Account *oldAccount); void slotAccountStateChanged(int state); void slotRequestFailed();