#include "logger.h"
#include "configfile.h"
#include "ocsnavigationappsjob.h"
+#include "pushnotifications.h"
#include <QSettings>
#include <QTimer>
-#include <qfontmetrics.h>
-
+#include <QFontMetrics>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QNetworkRequest>
#include <QBuffer>
+#include <cmath>
+
namespace OCC {
Q_LOGGING_CATEGORY(lcAccountState, "nextcloud.gui.account.state", QtInfoMsg)
this, &AccountState::slotCredentialsFetched);
connect(account.data(), &Account::credentialsAsked,
this, &AccountState::slotCredentialsAsked);
+ connect(account.data(), &Account::pushNotificationsReady,
+ this, &AccountState::slotPushNotificationsReady);
connect(this, &AccountState::isConnectedChanged, [=]{
// Get the Apps available on the server if we're now connected.
fetchNavigationApps();
}
});
+
+ connect(&_checkConnectionTimer, &QTimer::timeout, this, &AccountState::slotCheckConnection);
+ _checkConnectionTimer.setInterval(ConnectionValidator::DefaultCallingIntervalMsec);
+ _checkConnectionTimer.start();
+
+ QTimer::singleShot(0, this, &AccountState::slotCheckConnection);
}
AccountState::~AccountState() = default;
if (oldState == Connected || _state == Connected) {
emit isConnectedChanged();
}
+ if (_state == Connected) {
+ _checkConnectionTimer.setInterval(ConnectionValidator::DefaultCallingIntervalMsec);
+ setRetryCount(0);
+ }
}
// might not have changed but the underlying _connectionErrors might have
return tr("Unknown account state");
}
+int AccountState::retryCount() const
+{
+ return _retryCount;
+}
+
+void AccountState::increaseRetryCount()
+{
+ ++_retryCount;
+}
+
bool AccountState::isSignedOut() const
{
return _state == SignedOut;
}
}
+void AccountState::systemOnlineConfigurationChanged()
+{
+ QMetaObject::invokeMethod(this, "slotCheckConnection", Qt::QueuedConnection);
+}
+
void AccountState::checkConnectivity()
{
+ qCInfo(lcAccountState()) << "check connectivity";
+
if (isSignedOut() || _waitingForNewCredentials) {
return;
}
void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList &errors)
{
+ const auto updateRetryCount = [this]() {
+ increaseRetryCount();
+ qCInfo(lcAccountState()) << "connection retry count" << retryCount();
+ _lastCheckConnectionTimer.invalidate();
+ _lastCheckConnectionTimer.start();
+ };
+
+ const auto resetRetryCount = [this]() {
+ qCInfo(lcAccountState) << "reset retry count";
+ setRetryCount(0);
+ _lastCheckConnectionTimer.invalidate();
+ _lastCheckConnectionTimer.start();
+ };
+
if (isSignedOut()) {
qCWarning(lcAccountState) << "Signed out, ignoring" << status << _account->url().toString();
return;
case ConnectionValidator::Connected:
if (_state != Connected) {
setState(Connected);
+ resetRetryCount();
// Get the Apps available on the server.
fetchNavigationApps();
case ConnectionValidator::Undefined:
case ConnectionValidator::NotConfigured:
setState(Disconnected);
+ updateRetryCount();
break;
case ConnectionValidator::ServerVersionMismatch:
setState(ConfigurationError);
// or because we are having network issues. The latter one is
// much more likely, so keep trying to connect.
setState(NetworkError);
+ updateRetryCount();
break;
case ConnectionValidator::CredentialsWrong:
case ConnectionValidator::CredentialsNotReady:
break;
case ConnectionValidator::Timeout:
setState(NetworkError);
+ updateRetryCount();
break;
}
}
job->getNavigationApps();
}
+void AccountState::setRetryCount(int count)
+{
+ _retryCount = count;
+}
+
void AccountState::slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode){
if(statusCode == 200){
qCDebug(lcAccountState) << "New navigation apps ETag Response Header received " << value;
qCDebug(lcAccountState) << "Error " << statusCode << " while fetching new navigation apps: " << message;
}
+void AccountState::slotCheckConnection()
+{
+ if (_lastCheckConnectionTimer.isValid()) {
+ const auto currentDelay = (retryCount() <= 1 ? ConnectionValidator::DefaultCallingIntervalMsec :
+ (retryCount() == 2 ? ConnectionValidator::DefaultCallingIntervalMsec * 2 :
+ (retryCount() == 3 ? ConnectionValidator::DefaultCallingIntervalMsec * 4 :
+ ConnectionValidator::DefaultCallingIntervalMsec * 8)));
+
+ if (!_lastCheckConnectionTimer.hasExpired(currentDelay - 1)) {
+ qCInfo(lcAccountState()) << "timer has not expired: do not check now" << _lastCheckConnectionTimer.elapsed() << currentDelay;
+ return;
+ }
+ }
+
+ const auto currentState = state();
+
+ // Don't check if we're manually signed out or
+ // when the error is permanent.
+ const auto pushNotifications = account()->pushNotifications();
+ const auto pushNotificationsAvailable = (pushNotifications && pushNotifications->isReady());
+ if (currentState != AccountState::SignedOut && currentState != AccountState::ConfigurationError
+ && currentState != AccountState::AskingCredentials && !pushNotificationsAvailable) {
+ checkConnectivity();
+ } else if (currentState == AccountState::SignedOut && lastConnectionStatus() == AccountState::ConnectionStatus::SslError) {
+ qCWarning(lcAccountState()) << "Account is signed out due to SSL Handshake error. Going to perform a sign-in attempt...";
+ trySignIn();
+ }
+}
+
+void AccountState::slotPushNotificationsReady()
+{
+ if (state() != AccountState::State::Connected) {
+ setState(AccountState::State::Connected);
+ }
+}
+
void AccountState::slotNavigationAppsFetched(const QJsonDocument &reply, int statusCode)
{
if(_account){
#ifndef ACCOUNTINFO_H
#define ACCOUNTINFO_H
+#include "connectionvalidator.h"
+#include "creds/abstractcredentials.h"
+
#include <QByteArray>
#include <QElapsedTimer>
#include <QPointer>
-#include "connectionvalidator.h"
-#include "creds/abstractcredentials.h"
+#include <QTimer>
#include <memory>
State state() const;
static QString stateString(State state);
+ int retryCount() const;
+ void increaseRetryCount();
+
bool isSignedOut() const;
AccountAppList appList() const;
void trySignIn();
+ void systemOnlineConfigurationChanged();
+
public slots:
/// Triggers a ping to the server to update state and
/// connection status and errors.
void setState(State state);
void fetchNavigationApps();
+ void setRetryCount(int count);
+
signals:
void stateChanged(State state);
void isConnectedChanged();
void slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode);
void slotOcsError(int statusCode, const QString &message);
+private Q_SLOTS:
+
+ void slotCheckConnection();
+ void slotPushNotificationsReady();
+
private:
AccountPtr _account;
State _state;
AccountAppList _apps;
bool _isDesktopNotificationsAllowed;
+
+ int _retryCount = 0;
+
+ QTimer _checkConnectionTimer;
+ QElapsedTimer _lastCheckConnectionTimer;
+
};
class AccountApp : public QObject
void Application::slotSystemOnlineConfigurationChanged(QNetworkConfiguration cnf)
{
if (cnf.state() & QNetworkConfiguration::Active) {
- QMetaObject::invokeMethod(this, "slotCheckConnection", Qt::QueuedConnection);
+ const auto list = AccountManager::instance()->accounts();
+ for (const auto &accountState : list) {
+ accountState->systemOnlineConfigurationChanged();
+ }
}
}
void Application::slotCheckConnection()
{
- const auto list = AccountManager::instance()->accounts();
- for (const auto &accountState : list) {
- AccountState::State state = accountState->state();
-
- // Don't check if we're manually signed out or
- // when the error is permanent.
- const auto pushNotifications = accountState->account()->pushNotifications();
- const auto pushNotificationsAvailable = (pushNotifications && pushNotifications->isReady());
- if (state != AccountState::SignedOut && state != AccountState::ConfigurationError
- && state != AccountState::AskingCredentials && !pushNotificationsAvailable) {
- accountState->checkConnectivity();
- } else if (state == AccountState::SignedOut && accountState->lastConnectionStatus() == AccountState::ConnectionStatus::SslError) {
- qCWarning(lcApplication) << "Account is signed out due to SSL Handshake error. Going to perform a sign-in attempt...";
- accountState->trySignIn();
- }
- }
-
- if (list.isEmpty()) {
+ if (AccountManager::instance()->accounts().isEmpty()) {
// let gui open the setup wizard
_gui->slotOpenSettingsDialog();