2014-12-17 16:09:57 +03:00
/*
* Copyright ( C ) by Daniel Molkentin < danimo @ owncloud . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
2016-10-25 12:00:07 +03:00
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
2014-12-17 16:09:57 +03:00
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*/
# include "accountstate.h"
2015-04-09 17:19:17 +03:00
# include "accountmanager.h"
2014-12-17 16:09:57 +03:00
# include "account.h"
# include "creds/abstractcredentials.h"
2015-09-05 16:39:22 +03:00
# include "logger.h"
2015-10-20 10:25:42 +03:00
# include "configfile.h"
2014-12-17 16:09:57 +03:00
2015-06-15 16:04:39 +03:00
# include <QSettings>
2015-08-30 16:20:35 +03:00
# include <qfontmetrics.h>
2014-12-17 16:09:57 +03:00
namespace OCC {
2017-05-09 15:24:11 +03:00
Q_LOGGING_CATEGORY ( lcAccountState , " gui.account.state " , QtInfoMsg )
2014-12-18 14:09:48 +03:00
AccountState : : AccountState ( AccountPtr account )
2015-04-23 16:59:32 +03:00
: QObject ( )
2014-12-17 16:09:57 +03:00
, _account ( account )
, _state ( AccountState : : Disconnected )
, _connectionStatus ( ConnectionValidator : : Undefined )
, _waitingForNewCredentials ( false )
{
2017-05-17 11:55:42 +03:00
qRegisterMetaType < AccountState * > ( " AccountState* " ) ;
2014-12-17 16:09:57 +03:00
2014-12-18 14:09:48 +03:00
connect ( account . data ( ) , SIGNAL ( invalidCredentials ( ) ) ,
2017-05-17 11:55:42 +03:00
SLOT ( slotInvalidCredentials ( ) ) ) ;
connect ( account . data ( ) , SIGNAL ( credentialsFetched ( AbstractCredentials * ) ) ,
SLOT ( slotCredentialsFetched ( AbstractCredentials * ) ) ) ;
connect ( account . data ( ) , SIGNAL ( credentialsAsked ( AbstractCredentials * ) ) ,
SLOT ( slotCredentialsAsked ( AbstractCredentials * ) ) ) ;
2015-10-19 12:50:26 +03:00
_timeSinceLastETagCheck . invalidate ( ) ;
2014-12-17 16:09:57 +03:00
}
AccountState : : ~ AccountState ( )
{
}
2017-05-17 11:55:42 +03:00
AccountState * AccountState : : loadFromSettings ( AccountPtr account , QSettings & /*settings*/ )
2016-03-01 18:08:23 +03:00
{
auto accountState = new AccountState ( account ) ;
return accountState ;
}
2017-05-17 11:55:42 +03:00
void AccountState : : writeToSettings ( QSettings & /*settings*/ )
2016-03-01 18:08:23 +03:00
{
}
2014-12-18 14:09:48 +03:00
AccountPtr AccountState : : account ( ) const
2014-12-17 16:09:57 +03:00
{
2015-04-17 18:56:17 +03:00
return _account ;
2014-12-17 16:09:57 +03:00
}
AccountState : : ConnectionStatus AccountState : : connectionStatus ( ) const
{
return _connectionStatus ;
}
QStringList AccountState : : connectionErrors ( ) const
{
return _connectionErrors ;
}
QString AccountState : : connectionStatusString ( ConnectionStatus status )
{
return ConnectionValidator : : statusString ( status ) ;
}
AccountState : : State AccountState : : state ( ) const
{
return _state ;
}
void AccountState : : setState ( State state )
{
if ( _state ! = state ) {
2017-03-30 14:46:20 +03:00
qCInfo ( lcAccountState ) < < " AccountState state change: "
2017-05-17 11:55:42 +03:00
< < stateString ( _state ) < < " -> " < < stateString ( state ) ;
2014-12-17 16:09:57 +03:00
State oldState = _state ;
_state = state ;
if ( _state = = SignedOut ) {
_connectionStatus = ConnectionValidator : : Undefined ;
_connectionErrors . clear ( ) ;
} else if ( oldState = = SignedOut & & _state = = Disconnected ) {
2017-05-08 13:39:08 +03:00
// If we stop being voluntarily signed-out, try to connect and
// auth right now!
checkConnectivity ( ) ;
} else if ( _state = = ServiceUnavailable ) {
// Check if we are actually down for maintenance.
// To do this we must clear the connection validator that just
// produced the 503. It's finished anyway and will delete itself.
_connectionValidator . clear ( ) ;
2016-01-21 15:33:03 +03:00
checkConnectivity ( ) ;
2014-12-17 16:09:57 +03:00
}
2016-04-28 23:43:53 +03:00
if ( oldState = = Connected | | _state = = Connected ) {
emit isConnectedChanged ( ) ;
}
2014-12-17 16:09:57 +03:00
}
2015-05-15 13:26:23 +03:00
// might not have changed but the underlying _connectionErrors might have
emit stateChanged ( _state ) ;
2014-12-17 16:09:57 +03:00
}
QString AccountState : : stateString ( State state )
{
2017-05-17 11:55:42 +03:00
switch ( state ) {
2014-12-17 16:09:57 +03:00
case SignedOut :
2015-07-01 13:30:18 +03:00
return tr ( " Signed out " ) ;
2014-12-17 16:09:57 +03:00
case Disconnected :
2015-07-01 13:30:18 +03:00
return tr ( " Disconnected " ) ;
2014-12-17 16:09:57 +03:00
case Connected :
2015-07-01 13:30:18 +03:00
return tr ( " Connected " ) ;
2015-04-24 12:32:47 +03:00
case ServiceUnavailable :
2015-07-01 13:30:18 +03:00
return tr ( " Service unavailable " ) ;
2017-05-08 13:39:08 +03:00
case MaintenanceMode :
return tr ( " Maintenance mode " ) ;
2014-12-17 16:09:57 +03:00
case NetworkError :
2015-07-01 13:30:18 +03:00
return tr ( " Network error " ) ;
2014-12-17 16:09:57 +03:00
case ConfigurationError :
2015-07-01 13:30:18 +03:00
return tr ( " Configuration error " ) ;
2014-12-17 16:09:57 +03:00
}
2015-07-01 13:30:18 +03:00
return tr ( " Unknown account state " ) ;
2014-12-17 16:09:57 +03:00
}
bool AccountState : : isSignedOut ( ) const
{
return _state = = SignedOut ;
}
2015-12-09 13:06:28 +03:00
void AccountState : : signOutByUi ( )
2014-12-17 16:09:57 +03:00
{
2015-12-09 13:06:28 +03:00
account ( ) - > credentials ( ) - > forgetSensitiveData ( ) ;
setState ( SignedOut ) ;
}
void AccountState : : signIn ( )
{
if ( _state = = SignedOut ) {
2014-12-17 16:09:57 +03:00
setState ( Disconnected ) ;
}
}
bool AccountState : : isConnected ( ) const
{
return _state = = Connected ;
}
2015-10-19 12:50:26 +03:00
void AccountState : : tagLastSuccessfullETagRequest ( )
{
_timeSinceLastETagCheck . restart ( ) ;
}
2016-01-21 15:33:03 +03:00
void AccountState : : checkConnectivity ( )
2014-12-17 16:09:57 +03:00
{
if ( isSignedOut ( ) | | _waitingForNewCredentials ) {
return ;
}
2015-05-15 13:26:23 +03:00
if ( _connectionValidator ) {
2017-03-30 14:46:20 +03:00
qCWarning ( lcAccountState ) < < " ConnectionValidator already running, ignoring " < < account ( ) - > displayName ( ) ;
2015-05-15 13:26:23 +03:00
return ;
}
2015-10-19 12:50:26 +03:00
// IF the account is connected the connection check can be skipped
// if the last successful etag check job is not so long ago.
2015-10-20 10:25:42 +03:00
ConfigFile cfg ;
int polltime = cfg . remotePollInterval ( ) ;
2015-10-19 12:50:26 +03:00
if ( isConnected ( ) & & _timeSinceLastETagCheck . isValid ( )
2017-05-17 11:55:42 +03:00
& & _timeSinceLastETagCheck . elapsed ( ) < polltime ) {
qCDebug ( lcAccountState ) < < account ( ) - > displayName ( ) < < " The last ETag check succeeded within the last " < < polltime / 1000 < < " secs. No connection check needed! " ;
2015-10-19 12:50:26 +03:00
return ;
}
2017-05-17 11:55:42 +03:00
ConnectionValidator * conValidator = new ConnectionValidator ( account ( ) ) ;
2015-05-15 13:26:23 +03:00
_connectionValidator = conValidator ;
2017-05-17 11:55:42 +03:00
connect ( conValidator , SIGNAL ( connectionResult ( ConnectionValidator : : Status , QStringList ) ) ,
SLOT ( slotConnectionValidatorResult ( ConnectionValidator : : Status , QStringList ) ) ) ;
2014-12-17 16:09:57 +03:00
if ( isConnected ( ) ) {
// Use a small authed propfind as a minimal ping when we're
// already connected.
conValidator - > checkAuthentication ( ) ;
} else {
// Check the server and then the auth.
2015-03-19 13:40:47 +03:00
2017-05-17 11:55:42 +03:00
// Let's try this for all OS and see if it fixes the Qt issues we have on Linux #4720 #3888 #4051
//#ifdef Q_OS_WIN
2015-03-19 13:40:47 +03:00
// There seems to be a bug in Qt on Windows where QNAM sometimes stops
// working correctly after the computer woke up from sleep. See #2895 #2899
// and #2973.
// As an attempted workaround, reset the QNAM regularly if the account is
// disconnected.
account ( ) - > resetNetworkAccessManager ( ) ;
2015-07-02 12:28:40 +03:00
// If we don't reset the ssl config a second CheckServerJob can produce a
// ssl config that does not have a sensible certificate chain.
account ( ) - > setSslConfiguration ( QSslConfiguration ( ) ) ;
2017-05-17 11:55:42 +03:00
//#endif
2014-12-17 16:09:57 +03:00
conValidator - > checkServerAndAuth ( ) ;
}
}
2017-05-17 11:55:42 +03:00
void AccountState : : slotConnectionValidatorResult ( ConnectionValidator : : Status status , const QStringList & errors )
2014-12-17 16:09:57 +03:00
{
if ( isSignedOut ( ) ) {
2017-03-30 14:46:20 +03:00
qCWarning ( lcAccountState ) < < " Signed out, ignoring " < < connectionStatusString ( status ) < < _account - > url ( ) . toString ( ) ;
2014-12-17 16:09:57 +03:00
return ;
}
if ( _connectionStatus ! = status ) {
2017-03-30 14:46:20 +03:00
qCInfo ( lcAccountState ) < < " AccountState connection status change: "
2017-05-17 11:55:42 +03:00
< < connectionStatusString ( _connectionStatus ) < < " -> "
< < connectionStatusString ( status ) ;
2014-12-17 16:09:57 +03:00
_connectionStatus = status ;
}
_connectionErrors = errors ;
2017-05-17 11:55:42 +03:00
switch ( status ) {
2014-12-17 16:09:57 +03:00
case ConnectionValidator : : Connected :
2015-07-07 15:44:16 +03:00
if ( _state ! = Connected ) {
2015-06-08 13:14:37 +03:00
setState ( Connected ) ;
}
2014-12-17 16:09:57 +03:00
break ;
case ConnectionValidator : : Undefined :
case ConnectionValidator : : NotConfigured :
setState ( Disconnected ) ;
break ;
case ConnectionValidator : : ServerVersionMismatch :
setState ( ConfigurationError ) ;
break ;
2015-02-11 11:23:04 +03:00
case ConnectionValidator : : StatusNotFound :
// This can happen either because the server does not exist
// or because we are having network issues. The latter one is
// much more likely, so keep trying to connect.
setState ( NetworkError ) ;
break ;
2015-09-05 16:37:20 +03:00
case ConnectionValidator : : CredentialsMissingOrWrong :
slotInvalidCredentials ( ) ;
2014-12-17 16:09:57 +03:00
break ;
2015-01-21 17:30:25 +03:00
case ConnectionValidator : : UserCanceledCredentials :
setState ( SignedOut ) ;
break ;
2015-04-24 12:32:47 +03:00
case ConnectionValidator : : ServiceUnavailable :
setState ( ServiceUnavailable ) ;
2015-02-25 11:49:39 +03:00
break ;
2017-05-08 13:39:08 +03:00
case ConnectionValidator : : MaintenanceMode :
setState ( MaintenanceMode ) ;
break ;
2014-12-17 16:09:57 +03:00
case ConnectionValidator : : Timeout :
setState ( NetworkError ) ;
break ;
}
}
void AccountState : : slotInvalidCredentials ( )
{
2015-09-05 16:37:20 +03:00
if ( isSignedOut ( ) | | _waitingForNewCredentials )
2014-12-17 16:09:57 +03:00
return ;
2015-09-05 16:37:20 +03:00
if ( account ( ) - > credentials ( ) - > ready ( ) )
account ( ) - > credentials ( ) - > invalidateToken ( ) ;
account ( ) - > credentials ( ) - > fetchFromKeychain ( ) ;
2014-12-17 16:09:57 +03:00
setState ( ConfigurationError ) ;
_waitingForNewCredentials = true ;
}
2017-05-17 11:55:42 +03:00
void AccountState : : slotCredentialsFetched ( AbstractCredentials * credentials )
2015-09-05 16:39:22 +03:00
{
if ( ! credentials - > ready ( ) ) {
// No exiting credentials found in the keychain
2016-01-21 15:33:03 +03:00
credentials - > askFromUser ( ) ;
2015-09-05 16:39:22 +03:00
return ;
}
_waitingForNewCredentials = false ;
AccountState: Attempt to fix a crash
Backtrace from the crash reporter:
Crash: EXCEPTION_ACCESS_VIOLATION_READ at 0x21
File "qcoreapplication.cpp", line 1281, in QCoreApplication::postEvent
File "qobject.cpp", line 2125, in QObject::deleteLater
File "connectionvalidator.cpp", line 240, in OCC::ConnectionValidator::reportResult
File "connectionvalidator.cpp", line 206, in OCC::ConnectionValidator::slotAuthFailed
File "moc_connectionvalidator.cpp", line 127, in OCC::ConnectionValidator::qt_static_metacall
File "qobject.cpp", line 3716, in QMetaObject::activate
File "moc_networkjobs.cpp", line 653, in OCC::PropfindJob::finishedWithError
File "networkjobs.cpp", line 570, in OCC::PropfindJob::finished
I believe the problem is caused because 'this' was deleted in ConnectionValidator::reportResult
as the signal connectionResult gets emited. The AccountState::slotConnectionValidatorResult
slot does indeed call slotInvalidCredentials which might call {Shibboleth,Http}Credentials::fetchFromKeychain
which might emit fetched directly, which will call AccountState::slotCredentialsFetched
which deletes the _connectionValidator
So use deleteLater when deleting the _connectionValidator, hoping this helps
2017-03-28 16:11:58 +03:00
if ( _connectionValidator ) {
// When new credentials become available we always want to restart the
// connection validation, even if it's currently running.
_connectionValidator - > deleteLater ( ) ;
_connectionValidator = 0 ;
}
2015-09-05 16:39:22 +03:00
2016-01-21 15:33:03 +03:00
checkConnectivity ( ) ;
2015-09-05 16:39:22 +03:00
}
2017-05-17 11:55:42 +03:00
void AccountState : : slotCredentialsAsked ( AbstractCredentials * credentials )
2014-12-17 16:09:57 +03:00
{
_waitingForNewCredentials = false ;
if ( ! credentials - > ready ( ) ) {
// User canceled the connection or did not give a password
setState ( SignedOut ) ;
return ;
}
AccountState: Attempt to fix a crash
Backtrace from the crash reporter:
Crash: EXCEPTION_ACCESS_VIOLATION_READ at 0x21
File "qcoreapplication.cpp", line 1281, in QCoreApplication::postEvent
File "qobject.cpp", line 2125, in QObject::deleteLater
File "connectionvalidator.cpp", line 240, in OCC::ConnectionValidator::reportResult
File "connectionvalidator.cpp", line 206, in OCC::ConnectionValidator::slotAuthFailed
File "moc_connectionvalidator.cpp", line 127, in OCC::ConnectionValidator::qt_static_metacall
File "qobject.cpp", line 3716, in QMetaObject::activate
File "moc_networkjobs.cpp", line 653, in OCC::PropfindJob::finishedWithError
File "networkjobs.cpp", line 570, in OCC::PropfindJob::finished
I believe the problem is caused because 'this' was deleted in ConnectionValidator::reportResult
as the signal connectionResult gets emited. The AccountState::slotConnectionValidatorResult
slot does indeed call slotInvalidCredentials which might call {Shibboleth,Http}Credentials::fetchFromKeychain
which might emit fetched directly, which will call AccountState::slotCredentialsFetched
which deletes the _connectionValidator
So use deleteLater when deleting the _connectionValidator, hoping this helps
2017-03-28 16:11:58 +03:00
if ( _connectionValidator ) {
// When new credentials become available we always want to restart the
// connection validation, even if it's currently running.
_connectionValidator - > deleteLater ( ) ;
_connectionValidator = 0 ;
}
2015-05-22 10:38:44 +03:00
2016-01-21 15:33:03 +03:00
checkConnectivity ( ) ;
2014-12-17 16:09:57 +03:00
}
2015-07-02 14:31:42 +03:00
std : : unique_ptr < QSettings > AccountState : : settings ( )
2015-06-15 16:04:39 +03:00
{
2016-10-25 13:04:22 +03:00
auto s = Utility : : settingsWithGroup ( QLatin1String ( " Accounts " ) ) ;
2015-06-15 16:04:39 +03:00
s - > beginGroup ( _account - > id ( ) ) ;
return s ;
}
2015-08-30 16:20:35 +03:00
QString AccountState : : shortDisplayNameForSettings ( int width ) const
2015-08-05 15:49:16 +03:00
{
2015-08-30 16:20:35 +03:00
QString user = account ( ) - > credentials ( ) - > user ( ) ;
QString host = account ( ) - > url ( ) . host ( ) ;
2015-09-10 11:17:16 +03:00
int port = account ( ) - > url ( ) . port ( ) ;
if ( port > 0 & & port ! = 80 & & port ! = 443 ) {
host . append ( QLatin1Char ( ' : ' ) ) ;
host . append ( QString : : number ( port ) ) ;
}
2015-08-30 16:20:35 +03:00
if ( width > 0 ) {
QFont f ;
QFontMetrics fm ( f ) ;
2015-09-10 11:17:16 +03:00
host = fm . elidedText ( host , Qt : : ElideMiddle , width ) ;
2015-08-30 16:20:35 +03:00
user = fm . elidedText ( user , Qt : : ElideRight , width ) ;
2015-08-05 15:49:16 +03:00
}
2015-08-30 16:20:35 +03:00
return user + QLatin1String ( " \n " ) + host ;
2015-08-05 15:49:16 +03:00
}
2014-12-17 16:09:57 +03:00
} // namespace OCC