diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 8c84430e6..c9b356dcf 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 861c2a53b..c491887bb 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 a4159fb68..0c72057c5 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 ae16068ea..e431399f2 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 b3b47d91c..bd906fede 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 d7b0e763e..fd4d30cc3 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 ecb1c20dd..0ce78a91f 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 3d3da6afb..8d356403b 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 cf489bf44..660f561fb 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 1d5e65555..f20133499 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 4af6b27cd..8d75d771a 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();