2011-04-06 13:48:02 +04:00
/*
* Copyright ( C ) by Duncan Mac - Vicar P . < duncan @ kde . org >
2013-07-22 22:27:42 +04:00
* Copyright ( C ) by Daniel Molkentin < danimo @ owncloud . com >
* Copyright ( C ) by Klaas Freitag < freitag @ owncloud . com >
2011-04-06 13:48:02 +04:00
*
* 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
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 .
*/
2013-02-25 12:54:19 +04:00
# include "config.h"
2012-07-20 19:13:23 +04:00
2014-07-11 02:31:24 +04:00
# include "account.h"
2014-12-17 16:09:57 +03:00
# include "accountstate.h"
2014-07-11 02:31:24 +04:00
# include "folder.h"
# include "folderman.h"
# include "logger.h"
2014-11-10 01:25:57 +03:00
# include "configfile.h"
2014-07-11 02:31:24 +04:00
# include "networkjobs.h"
2017-09-01 19:11:43 +03:00
# include "common/syncjournalfilerecord.h"
2014-07-11 02:31:24 +04:00
# include "syncresult.h"
# include "clientproxy.h"
# include "syncengine.h"
# include "syncrunfilelog.h"
2015-10-23 18:24:45 +03:00
# include "socketapi.h"
2014-09-18 19:08:53 +04:00
# include "theme.h"
2015-02-25 12:51:05 +03:00
# include "filesystem.h"
2013-10-21 23:42:52 +04:00
2013-07-30 13:19:22 +04:00
# include "creds/abstractcredentials.h"
2013-10-21 23:42:52 +04:00
2012-05-21 18:48:49 +04:00
# include <QTimer>
# include <QUrl>
2012-06-25 17:31:13 +04:00
# include <QDir>
2015-04-24 11:18:33 +03:00
# include <QSettings>
2014-04-06 21:34:56 +04:00
2013-07-22 22:27:42 +04:00
# include <QMessageBox>
# include <QPushButton>
2012-05-21 18:48:49 +04:00
2014-11-10 00:34:07 +03:00
namespace OCC {
2011-02-17 02:21:45 +03:00
2017-12-28 22:33:10 +03:00
Q_LOGGING_CATEGORY ( lcFolder , " nextcloud.gui.folder " , QtInfoMsg )
2017-05-09 15:24:11 +03:00
2017-05-17 11:55:42 +03:00
Folder : : Folder ( const FolderDefinition & definition ,
AccountState * accountState ,
QObject * parent )
2013-07-22 22:27:42 +04:00
: QObject ( parent )
2017-05-17 11:55:42 +03:00
, _accountState ( accountState )
, _definition ( definition )
, _csyncUnavail ( false )
, _lastSyncDuration ( 0 )
, _consecutiveFailingSyncs ( 0 )
, _consecutiveFollowUpSyncs ( 0 )
, _journal ( _definition . absoluteJournalPath ( ) )
, _fileLog ( new SyncRunFileLog )
, _saveBackwardsCompatible ( false )
2011-02-17 02:21:45 +03:00
{
2015-01-16 12:52:26 +03:00
_timeSinceLastSyncStart . start ( ) ;
_timeSinceLastSyncDone . start ( ) ;
2012-03-08 14:39:31 +04:00
2015-06-17 16:55:48 +03:00
SyncResult : : Status status = SyncResult : : NotYetStarted ;
if ( definition . paused ) {
status = SyncResult : : Paused ;
}
_syncResult . setStatus ( status ) ;
2012-02-20 19:45:27 +04:00
2012-10-30 15:42:17 +04:00
// check if the local path exists
checkLocalPath ( ) ;
2013-08-05 19:01:08 +04:00
2015-04-27 18:33:59 +03:00
_syncResult . setFolder ( _definition . alias ) ;
2016-03-09 19:53:18 +03:00
2016-11-15 20:47:04 +03:00
_engine . reset ( new SyncEngine ( _accountState - > account ( ) , path ( ) , remotePath ( ) , & _journal ) ) ;
2016-03-09 19:53:18 +03:00
// pass the setting if hidden files are to be ignored, will be read in csync_update
_engine - > setIgnoreHiddenFiles ( _definition . ignoreHiddenFiles ) ;
2017-11-29 14:04:01 +03:00
ConfigFile : : setupDefaultExcludeFilePaths ( _engine - > excludedFiles ( ) ) ;
if ( ! reloadExcludes ( ) )
2017-05-09 15:24:11 +03:00
qCWarning ( lcFolder , " Could not read system exclude file " ) ;
2016-03-09 19:53:18 +03:00
2017-09-20 11:14:48 +03:00
connect ( _accountState . data ( ) , & AccountState : : isConnectedChanged , this , & Folder : : canSyncChanged ) ;
2020-07-03 00:40:27 +03:00
connect ( _engine . data ( ) , & SyncEngine : : rootEtag , this , & Folder : : etagRetrievedFromSyncEngine ) ;
2016-03-09 19:53:18 +03:00
2017-09-20 11:14:48 +03:00
connect ( _engine . data ( ) , & SyncEngine : : started , this , & Folder : : slotSyncStarted , Qt : : QueuedConnection ) ;
connect ( _engine . data ( ) , & SyncEngine : : finished , this , & Folder : : slotSyncFinished , Qt : : QueuedConnection ) ;
connect ( _engine . data ( ) , & SyncEngine : : csyncUnavailable , this , & Folder : : slotCsyncUnavailable , Qt : : QueuedConnection ) ;
2016-03-09 19:53:18 +03:00
//direct connection so the message box is blocking the sync.
2017-09-20 11:14:48 +03:00
connect ( _engine . data ( ) , & SyncEngine : : aboutToRemoveAllFiles ,
this , & Folder : : slotAboutToRemoveAllFiles ) ;
connect ( _engine . data ( ) , & SyncEngine : : aboutToRestoreBackup ,
this , & Folder : : slotAboutToRestoreBackup ) ;
connect ( _engine . data ( ) , & SyncEngine : : transmissionProgress , this , & Folder : : slotTransmissionProgress ) ;
connect ( _engine . data ( ) , & SyncEngine : : itemCompleted ,
this , & Folder : : slotItemCompleted ) ;
connect ( _engine . data ( ) , & SyncEngine : : newBigFolder ,
this , & Folder : : slotNewBigFolderDiscovered ) ;
connect ( _engine . data ( ) , & SyncEngine : : seenLockedFile , FolderMan : : instance ( ) , & FolderMan : : slotSyncOnceFileUnlocks ) ;
connect ( _engine . data ( ) , & SyncEngine : : aboutToPropagate ,
this , & Folder : : slotLogPropagationStart ) ;
2017-07-11 16:54:01 +03:00
connect ( _engine . data ( ) , & SyncEngine : : syncError , this , & Folder : : slotSyncError ) ;
2016-10-18 17:04:25 +03:00
_scheduleSelfTimer . setSingleShot ( true ) ;
_scheduleSelfTimer . setInterval ( SyncEngine : : minimumFileAgeForUpload ) ;
2017-09-20 11:14:48 +03:00
connect ( & _scheduleSelfTimer , & QTimer : : timeout ,
this , & Folder : : slotScheduleThisFolder ) ;
2018-02-21 15:55:33 +03:00
connect ( ProgressDispatcher : : instance ( ) , & ProgressDispatcher : : folderConflicts ,
this , & Folder : : slotFolderConflicts ) ;
2011-02-17 02:21:45 +03:00
}
Folder : : ~ Folder ( )
{
2016-06-17 20:04:24 +03:00
// Reset then engine first as it will abort and try to access members of the Folder
_engine . reset ( ) ;
2011-02-17 02:21:45 +03:00
}
2012-10-30 15:42:17 +04:00
void Folder : : checkLocalPath ( )
{
2015-04-27 18:33:59 +03:00
const QFileInfo fi ( _definition . localPath ) ;
2016-09-12 16:17:21 +03:00
_canonicalLocalPath = fi . canonicalFilePath ( ) ;
2018-02-20 14:27:10 +03:00
# ifdef Q_OS_MAC
// Workaround QTBUG-55896 (Should be fixed in Qt 5.8)
_canonicalLocalPath = _canonicalLocalPath . normalized ( QString : : NormalizationForm_C ) ;
# endif
2016-09-15 15:56:37 +03:00
if ( _canonicalLocalPath . isEmpty ( ) ) {
2017-03-30 14:46:20 +03:00
qCWarning ( lcFolder ) < < " Broken symlink: " < < _definition . localPath ;
2016-09-15 15:56:37 +03:00
_canonicalLocalPath = _definition . localPath ;
2017-05-17 11:55:42 +03:00
} else if ( ! _canonicalLocalPath . endsWith ( ' / ' ) ) {
2016-09-12 16:17:21 +03:00
_canonicalLocalPath . append ( ' / ' ) ;
}
2017-05-17 11:55:42 +03:00
if ( fi . isDir ( ) & & fi . isReadable ( ) ) {
2017-05-09 15:24:11 +03:00
qCDebug ( lcFolder ) < < " Checked local path ok " ;
2012-10-30 15:42:17 +04:00
} else {
2012-10-30 17:37:15 +04:00
// Check directory again
2017-05-17 11:55:42 +03:00
if ( ! FileSystem : : fileExists ( _definition . localPath , fi ) ) {
2017-01-25 13:28:18 +03:00
_syncResult . appendErrorString ( tr ( " Local folder %1 does not exist. " ) . arg ( _definition . localPath ) ) ;
2017-05-17 11:55:42 +03:00
_syncResult . setStatus ( SyncResult : : SetupError ) ;
} else if ( ! fi . isDir ( ) ) {
2017-01-25 13:28:18 +03:00
_syncResult . appendErrorString ( tr ( " %1 should be a folder but is not. " ) . arg ( _definition . localPath ) ) ;
2017-05-17 11:55:42 +03:00
_syncResult . setStatus ( SyncResult : : SetupError ) ;
} else if ( ! fi . isReadable ( ) ) {
2017-01-25 13:28:18 +03:00
_syncResult . appendErrorString ( tr ( " %1 is not readable. " ) . arg ( _definition . localPath ) ) ;
2017-05-17 11:55:42 +03:00
_syncResult . setStatus ( SyncResult : : SetupError ) ;
2012-10-30 17:37:15 +04:00
}
2012-10-30 15:42:17 +04:00
}
}
2016-04-26 17:38:03 +03:00
QString Folder : : shortGuiRemotePathOrAppName ( ) const
2015-08-11 16:12:43 +03:00
{
if ( remotePath ( ) . length ( ) > 0 & & remotePath ( ) ! = QLatin1String ( " / " ) ) {
QString a = QFile ( remotePath ( ) ) . fileName ( ) ;
if ( a . startsWith ( ' / ' ) ) {
a = a . remove ( 0 , 1 ) ;
}
return a ;
} else {
return Theme : : instance ( ) - > appNameGUI ( ) ;
}
}
2011-04-06 11:52:02 +04:00
QString Folder : : alias ( ) const
{
2015-04-27 18:33:59 +03:00
return _definition . alias ;
2011-04-06 11:52:02 +04:00
}
2011-02-17 17:10:06 +03:00
QString Folder : : path ( ) const
{
2016-09-12 16:17:21 +03:00
return _canonicalLocalPath ;
2011-02-17 17:10:06 +03:00
}
2016-04-26 17:38:03 +03:00
QString Folder : : shortGuiLocalPath ( ) const
2015-08-17 12:39:45 +03:00
{
QString p = _definition . localPath ;
QString home = QDir : : homePath ( ) ;
2017-05-17 11:55:42 +03:00
if ( ! home . endsWith ( ' / ' ) ) {
2015-08-17 12:39:45 +03:00
home . append ( ' / ' ) ;
}
if ( p . startsWith ( home ) ) {
p = p . mid ( home . length ( ) ) ;
}
if ( p . length ( ) > 1 & & p . endsWith ( ' / ' ) ) {
p . chop ( 1 ) ;
}
return QDir : : toNativeSeparators ( p ) ;
}
2015-07-14 17:56:55 +03:00
bool Folder : : ignoreHiddenFiles ( )
{
bool re ( _definition . ignoreHiddenFiles ) ;
return re ;
}
void Folder : : setIgnoreHiddenFiles ( bool ignore )
{
_definition . ignoreHiddenFiles = ignore ;
}
2016-11-22 17:30:12 +03:00
QString Folder : : cleanPath ( ) const
2015-05-03 14:34:23 +03:00
{
2016-09-12 16:17:21 +03:00
QString cleanedPath = QDir : : cleanPath ( _canonicalLocalPath ) ;
2015-05-03 14:34:23 +03:00
2017-05-17 11:55:42 +03:00
if ( cleanedPath . length ( ) = = 3 & & cleanedPath . endsWith ( " :/ " ) )
cleanedPath . remove ( 2 , 1 ) ;
2015-05-03 14:34:23 +03:00
return cleanedPath ;
}
2013-07-22 22:27:42 +04:00
bool Folder : : isBusy ( ) const
{
2016-03-09 19:53:18 +03:00
return _engine - > isSyncRunning ( ) ;
2013-07-22 22:27:42 +04:00
}
2013-10-21 23:42:52 +04:00
QString Folder : : remotePath ( ) const
{
2015-04-27 18:33:59 +03:00
return _definition . targetPath ;
2013-10-21 23:42:52 +04:00
}
QUrl Folder : : remoteUrl ( ) const
2012-03-26 15:20:15 +04:00
{
2016-10-25 13:04:22 +03:00
return Utility : : concatUrlPath ( _accountState - > account ( ) - > davUrl ( ) , remotePath ( ) ) ;
2012-03-26 15:20:15 +04:00
}
2014-08-19 15:58:20 +04:00
bool Folder : : syncPaused ( ) const
2011-10-13 18:41:24 +04:00
{
2017-05-17 11:55:42 +03:00
return _definition . paused ;
2011-10-13 18:41:24 +04:00
}
2015-07-01 12:39:57 +03:00
bool Folder : : canSync ( ) const
{
2016-04-28 23:43:53 +03:00
return ! syncPaused ( ) & & accountState ( ) - > isConnected ( ) ;
2015-07-01 12:39:57 +03:00
}
2017-05-17 11:55:42 +03:00
void Folder : : setSyncPaused ( bool paused )
2011-10-13 18:41:24 +04:00
{
2016-03-02 13:06:03 +03:00
if ( paused = = _definition . paused ) {
return ;
2015-04-24 11:18:33 +03:00
}
2016-03-02 13:06:03 +03:00
_definition . paused = paused ;
saveToSettings ( ) ;
2017-05-17 11:55:42 +03:00
if ( ! paused ) {
2016-03-01 18:07:11 +03:00
setSyncState ( SyncResult : : NotYetStarted ) ;
2015-04-24 11:18:33 +03:00
} else {
setSyncState ( SyncResult : : Paused ) ;
}
2016-03-02 13:06:03 +03:00
emit syncPausedChanged ( this , paused ) ;
2016-03-01 18:07:11 +03:00
emit syncStateChange ( ) ;
2016-04-28 23:43:53 +03:00
emit canSyncChanged ( ) ;
2011-10-13 18:41:24 +04:00
}
2013-02-18 17:56:21 +04:00
void Folder : : setSyncState ( SyncResult : : Status state )
{
_syncResult . setStatus ( state ) ;
}
2012-02-20 19:45:27 +04:00
SyncResult Folder : : syncResult ( ) const
2011-10-13 18:41:24 +04:00
{
2017-05-17 11:55:42 +03:00
return _syncResult ;
2011-10-13 18:41:24 +04:00
}
2014-01-11 23:35:16 +04:00
void Folder : : prepareToSync ( )
2011-04-06 17:57:18 +04:00
{
2017-01-25 13:28:18 +03:00
_syncResult . reset ( ) ;
2017-05-17 11:55:42 +03:00
_syncResult . setStatus ( SyncResult : : NotYetStarted ) ;
2012-03-31 14:44:22 +04:00
}
2014-12-03 00:32:54 +03:00
void Folder : : slotRunEtagJob ( )
2011-04-05 14:16:24 +04:00
{
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " Trying to check " < < remoteUrl ( ) . toString ( ) < < " for changes via ETag check. (time since last sync: " < < ( _timeSinceLastSyncDone . elapsed ( ) / 1000 ) < < " s) " ;
2013-08-05 21:53:58 +04:00
2014-12-18 14:09:48 +03:00
AccountPtr account = _accountState - > account ( ) ;
2014-12-03 00:32:54 +03:00
2016-10-18 17:04:25 +03:00
if ( _requestEtagJob ) {
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < remoteUrl ( ) . toString ( ) < < " has ETag job queued, not trying to sync " ;
2014-09-02 16:51:03 +04:00
return ;
}
2016-03-01 18:07:11 +03:00
if ( ! canSync ( ) ) {
2017-05-17 11:55:42 +03:00
qCInfo ( lcFolder ) < < " Not syncing. : " < < remoteUrl ( ) . toString ( ) < < _definition . paused < < AccountState : : stateString ( _accountState - > state ( ) ) ;
2014-08-26 18:23:19 +04:00
return ;
}
2016-10-18 17:04:25 +03:00
// Do the ordinary etag check for the root folder and schedule a
// sync if it's different.
2014-10-24 14:52:24 +04:00
2016-10-18 17:04:25 +03:00
_requestEtagJob = new RequestEtagJob ( account , remotePath ( ) , this ) ;
2017-05-17 11:55:42 +03:00
_requestEtagJob - > setTimeout ( 60 * 1000 ) ;
2016-10-18 17:04:25 +03:00
// check if the etag is different when retrieved
2020-07-03 00:40:27 +03:00
QObject : : connect ( _requestEtagJob . data ( ) , & RequestEtagJob : : etagRetrieved , this , & Folder : : etagRetrieved ) ;
2016-10-18 17:04:25 +03:00
FolderMan : : instance ( ) - > slotScheduleETagJob ( alias ( ) , _requestEtagJob ) ;
// The _requestEtagJob is auto deleting itself on finish. Our guard pointer _requestEtagJob will then be null.
2013-08-05 21:53:58 +04:00
}
2020-07-03 00:40:27 +03:00
void Folder : : etagRetrieved ( const QString & etag )
2013-08-05 21:53:58 +04:00
{
2013-08-14 14:59:56 +04:00
// re-enable sync if it was disabled because network was down
FolderMan : : instance ( ) - > setSyncEnabled ( true ) ;
2013-08-05 21:53:58 +04:00
if ( _lastEtag ! = etag ) {
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " Compare etag with previous etag: last: " < < _lastEtag < < " , received: " < < etag < < " -> CHANGED " ;
2013-08-05 21:53:58 +04:00
_lastEtag = etag ;
2016-10-18 17:04:25 +03:00
slotScheduleThisFolder ( ) ;
2013-08-05 21:53:58 +04:00
}
2015-10-19 12:50:26 +03:00
2016-03-16 21:07:40 +03:00
_accountState - > tagLastSuccessfullETagRequest ( ) ;
2011-04-06 17:57:18 +04:00
}
2020-07-03 00:40:27 +03:00
void Folder : : etagRetrievedFromSyncEngine ( const QString & etag )
2015-01-23 17:30:00 +03:00
{
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " Root etag from during sync: " < < etag ;
2015-11-18 16:50:43 +03:00
accountState ( ) - > tagLastSuccessfullETagRequest ( ) ;
2015-01-23 17:30:00 +03:00
_lastEtag = etag ;
}
2017-01-25 13:28:18 +03:00
void Folder : : showSyncResultPopup ( )
{
2017-05-17 11:55:42 +03:00
if ( _syncResult . firstItemNew ( ) ) {
createGuiLog ( _syncResult . firstItemNew ( ) - > _file , LogStatusNew , _syncResult . numNewItems ( ) ) ;
2015-01-15 22:48:04 +03:00
}
2017-05-17 11:55:42 +03:00
if ( _syncResult . firstItemDeleted ( ) ) {
createGuiLog ( _syncResult . firstItemDeleted ( ) - > _file , LogStatusRemove , _syncResult . numRemovedItems ( ) ) ;
2015-01-15 22:48:04 +03:00
}
2017-05-17 11:55:42 +03:00
if ( _syncResult . firstItemUpdated ( ) ) {
createGuiLog ( _syncResult . firstItemUpdated ( ) - > _file , LogStatusUpdated , _syncResult . numUpdatedItems ( ) ) ;
2015-01-15 22:48:04 +03:00
}
2013-11-28 13:57:49 +04:00
2017-05-17 11:55:42 +03:00
if ( _syncResult . firstItemRenamed ( ) ) {
2016-03-24 20:20:49 +03:00
LogStatus status ( LogStatusRename ) ;
2013-11-28 13:57:49 +04:00
// if the path changes it's rather a move
2017-01-25 13:28:18 +03:00
QDir renTarget = QFileInfo ( _syncResult . firstItemRenamed ( ) - > _renameTarget ) . dir ( ) ;
QDir renSource = QFileInfo ( _syncResult . firstItemRenamed ( ) - > _file ) . dir ( ) ;
2017-05-17 11:55:42 +03:00
if ( renTarget ! = renSource ) {
2016-03-24 20:20:49 +03:00
status = LogStatusMove ;
2013-11-28 13:57:49 +04:00
}
2017-05-17 11:55:42 +03:00
createGuiLog ( _syncResult . firstItemRenamed ( ) - > _originalFile , status ,
_syncResult . numRenamedItems ( ) , _syncResult . firstItemRenamed ( ) - > _renameTarget ) ;
2013-11-28 13:57:49 +04:00
}
2013-11-08 19:21:59 +04:00
2017-07-12 11:57:41 +03:00
if ( _syncResult . firstNewConflictItem ( ) ) {
createGuiLog ( _syncResult . firstNewConflictItem ( ) - > _file , LogStatusConflict , _syncResult . numNewConflictItems ( ) ) ;
2016-04-05 17:37:54 +03:00
}
2017-03-16 18:30:28 +03:00
if ( int errorCount = _syncResult . numErrorItems ( ) ) {
2017-05-17 11:55:42 +03:00
createGuiLog ( _syncResult . firstItemError ( ) - > _file , LogStatusError , errorCount ) ;
2017-03-16 18:30:28 +03:00
}
2014-09-19 17:58:27 +04:00
2019-06-03 18:50:16 +03:00
if ( int lockedCount = _syncResult . numLockedItems ( ) ) {
createGuiLog ( _syncResult . firstItemLocked ( ) - > _file , LogStatusFileLocked , lockedCount ) ;
}
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " Folder sync result: " < < int ( _syncResult . status ( ) ) ;
2013-11-08 19:21:59 +04:00
}
2017-05-17 11:55:42 +03:00
void Folder : : createGuiLog ( const QString & filename , LogStatus status , int count ,
const QString & renameTarget )
2013-11-08 19:21:59 +04:00
{
2017-05-17 11:55:42 +03:00
if ( count > 0 ) {
2013-11-08 19:21:59 +04:00
Logger * logger = Logger : : instance ( ) ;
QString file = QDir : : toNativeSeparators ( filename ) ;
2014-03-11 13:55:20 +04:00
QString text ;
2016-03-24 20:20:49 +03:00
switch ( status ) {
case LogStatusRemove :
2017-05-17 11:55:42 +03:00
if ( count > 1 ) {
text = tr ( " %1 and %n other file(s) have been removed. " , " " , count - 1 ) . arg ( file ) ;
2014-03-11 13:55:20 +04:00
} else {
text = tr ( " %1 has been removed. " , " %1 names a file. " ) . arg ( file ) ;
}
break ;
2016-03-24 20:20:49 +03:00
case LogStatusNew :
2017-05-17 11:55:42 +03:00
if ( count > 1 ) {
text = tr ( " %1 and %n other file(s) have been downloaded. " , " " , count - 1 ) . arg ( file ) ;
2014-03-11 13:55:20 +04:00
} else {
text = tr ( " %1 has been downloaded. " , " %1 names a file. " ) . arg ( file ) ;
}
break ;
2016-03-24 20:20:49 +03:00
case LogStatusUpdated :
2017-05-17 11:55:42 +03:00
if ( count > 1 ) {
text = tr ( " %1 and %n other file(s) have been updated. " , " " , count - 1 ) . arg ( file ) ;
2014-03-11 13:55:20 +04:00
} else {
text = tr ( " %1 has been updated. " , " %1 names a file. " ) . arg ( file ) ;
}
break ;
2016-03-24 20:20:49 +03:00
case LogStatusRename :
2017-05-17 11:55:42 +03:00
if ( count > 1 ) {
text = tr ( " %1 has been renamed to %2 and %n other file(s) have been renamed. " , " " , count - 1 ) . arg ( file , renameTarget ) ;
2014-03-11 13:55:20 +04:00
} else {
2017-02-23 16:54:17 +03:00
text = tr ( " %1 has been renamed to %2. " , " %1 and %2 name files. " ) . arg ( file , renameTarget ) ;
2014-03-11 13:55:20 +04:00
}
break ;
2016-03-24 20:20:49 +03:00
case LogStatusMove :
2017-05-17 11:55:42 +03:00
if ( count > 1 ) {
text = tr ( " %1 has been moved to %2 and %n other file(s) have been moved. " , " " , count - 1 ) . arg ( file , renameTarget ) ;
2014-03-11 13:55:20 +04:00
} else {
2017-02-23 16:54:17 +03:00
text = tr ( " %1 has been moved to %2. " ) . arg ( file , renameTarget ) ;
2014-03-11 13:55:20 +04:00
}
break ;
2016-04-05 17:37:54 +03:00
case LogStatusConflict :
2017-05-17 11:55:42 +03:00
if ( count > 1 ) {
text = tr ( " %1 has and %n other file(s) have sync conflicts. " , " " , count - 1 ) . arg ( file ) ;
2016-04-05 17:37:54 +03:00
} else {
text = tr ( " %1 has a sync conflict. Please check the conflict file! " ) . arg ( file ) ;
}
break ;
2016-03-24 20:20:49 +03:00
case LogStatusError :
2017-05-17 11:55:42 +03:00
if ( count > 1 ) {
text = tr ( " %1 and %n other file(s) could not be synced due to errors. See the log for details. " , " " , count - 1 ) . arg ( file ) ;
2014-09-19 17:58:27 +04:00
} else {
text = tr ( " %1 could not be synced due to an error. See the log for details. " ) . arg ( file ) ;
}
break ;
2019-09-09 19:22:48 +03:00
case LogStatusFileLocked :
if ( count > 1 ) {
text = tr ( " %1 and %n other file(s) are currently locked. " , " " , count - 1 ) . arg ( file ) ;
} else {
text = tr ( " %1 is currently locked. " ) . arg ( file ) ;
}
break ;
2014-03-11 13:55:20 +04:00
}
2017-05-17 11:55:42 +03:00
if ( ! text . isEmpty ( ) ) {
2018-02-15 22:21:08 +03:00
// Ignores the settings in case of an error or conflict
2018-02-23 14:08:37 +03:00
if ( status = = LogStatusError | | status = = LogStatusConflict )
2018-02-15 22:21:08 +03:00
logger - > postOptionalGuiLog ( tr ( " Sync Activity " ) , text ) ;
2013-11-08 19:21:59 +04:00
}
2013-08-14 18:55:43 +04:00
}
2011-02-17 17:10:06 +03:00
}
2011-02-17 02:21:45 +03:00
2014-11-05 16:52:57 +03:00
int Folder : : slotDiscardDownloadProgress ( )
{
// Delete from journal and from filesystem.
2015-04-27 18:33:59 +03:00
QDir folderpath ( _definition . localPath ) ;
2014-11-05 16:52:57 +03:00
QSet < QString > keep_nothing ;
const QVector < SyncJournalDb : : DownloadInfo > deleted_infos =
2017-05-17 11:55:42 +03:00
_journal . getAndDeleteStaleDownloadInfos ( keep_nothing ) ;
2020-09-27 14:13:21 +03:00
for ( const auto & deleted_info : deleted_infos ) {
2014-11-05 16:52:57 +03:00
const QString tmppath = folderpath . filePath ( deleted_info . _tmpfile ) ;
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " Deleting temporary file: " < < tmppath ;
2016-01-05 13:58:18 +03:00
FileSystem : : remove ( tmppath ) ;
2014-11-05 16:52:57 +03:00
}
return deleted_infos . size ( ) ;
}
int Folder : : downloadInfoCount ( )
{
return _journal . downloadInfoCount ( ) ;
}
2015-01-16 12:17:19 +03:00
int Folder : : errorBlackListEntryCount ( )
2013-12-03 17:47:32 +04:00
{
2015-01-16 12:17:19 +03:00
return _journal . errorBlackListEntryCount ( ) ;
2013-12-03 17:47:32 +04:00
}
2015-01-16 12:17:19 +03:00
int Folder : : slotWipeErrorBlacklist ( )
2013-12-03 17:02:44 +04:00
{
2015-01-16 12:17:19 +03:00
return _journal . wipeErrorBlacklist ( ) ;
2013-12-03 17:02:44 +04:00
}
2017-05-17 11:55:42 +03:00
void Folder : : slotWatchedPathChanged ( const QString & path )
2014-11-07 13:41:21 +03:00
{
2017-09-14 17:43:23 +03:00
if ( ! path . startsWith ( this - > path ( ) ) ) {
qCDebug ( lcFolder ) < < " Changed path is not contained in folder, ignoring: " < < path ;
return ;
}
auto relativePath = path . midRef ( this - > path ( ) . size ( ) ) ;
// Add to list of locally modified paths
//
// We do this before checking for our own sync-related changes to make
// extra sure to not miss relevant changes.
auto relativePathBytes = relativePath . toUtf8 ( ) ;
_localDiscoveryPaths . insert ( relativePathBytes ) ;
qCDebug ( lcFolder ) < < " local discovery: inserted " < < relativePath < < " due to file watcher " ;
2017-05-17 11:55:42 +03:00
// The folder watcher fires a lot of bogus notifications during
// a sync operation, both for actual user files and the database
// and log. Therefore we check notifications against operations
// the sync is doing to filter out our own changes.
2014-11-07 13:41:21 +03:00
# ifdef Q_OS_MAC
2017-05-17 11:55:42 +03:00
// On OSX the folder watcher does not report changes done by our
// own process. Therefore nothing needs to be done here!
2014-11-07 13:41:21 +03:00
# else
// Use the path to figure out whether it was our own change
2017-07-24 09:03:05 +03:00
if ( _engine - > wasFileTouched ( path ) ) {
qCDebug ( lcFolder ) < < " Changed path was touched by SyncEngine, ignoring: " < < path ;
2016-06-03 14:06:11 +03:00
return ;
2014-11-07 13:41:21 +03:00
}
# endif
2016-06-03 14:06:11 +03:00
// Check that the mtime actually changed.
2017-09-14 17:43:23 +03:00
SyncJournalFileRecord record ;
if ( _journal . getFileRecord ( relativePathBytes , & record )
& & record . isValid ( )
& & ! FileSystem : : fileChanged ( path , record . _fileSize , record . _modtime ) ) {
qCInfo ( lcFolder ) < < " Ignoring spurious notification for file " < < relativePath ;
return ; // probably a spurious notification
2014-11-07 13:41:21 +03:00
}
2016-06-03 14:06:11 +03:00
2018-02-23 15:24:46 +03:00
warnOnNewExcludedItem ( record , relativePath ) ;
2016-06-03 14:06:11 +03:00
emit watchedFileChangedExternally ( path ) ;
2016-10-18 17:04:25 +03:00
// Also schedule this folder for a sync, but only after some delay:
// The sync will not upload files that were changed too recently.
scheduleThisFolderSoon ( ) ;
2014-11-07 13:41:21 +03:00
}
2015-04-24 11:18:33 +03:00
void Folder : : saveToSettings ( ) const
{
2016-11-22 17:30:12 +03:00
// Remove first to make sure we don't get duplicates
removeFromSettings ( ) ;
2015-06-15 16:04:39 +03:00
auto settings = _accountState - > settings ( ) ;
2016-11-22 17:30:12 +03:00
// The folder is saved to backwards-compatible "Folders"
// section only if it has the migrate flag set (i.e. was in
// there before) or if the folder is the only one for the
// given target path.
// This ensures that older clients will not read a configuration
// where two folders for different accounts point at the same
// local folders.
2020-09-29 19:32:48 +03:00
const auto folderMap = FolderMan : : instance ( ) - > map ( ) ;
2020-09-29 20:23:06 +03:00
const auto oneAccountOnly = std : : none_of ( folderMap . cbegin ( ) , folderMap . cend ( ) , [ this ] ( const auto * other ) {
2020-09-27 14:13:21 +03:00
return other ! = this & & other - > cleanPath ( ) = = this - > cleanPath ( ) ;
} ) ;
2016-11-22 17:30:12 +03:00
2016-11-23 12:40:17 +03:00
bool compatible = _saveBackwardsCompatible | | oneAccountOnly ;
2016-11-22 17:30:12 +03:00
2016-11-23 12:40:17 +03:00
if ( compatible ) {
2016-11-22 17:30:12 +03:00
settings - > beginGroup ( QLatin1String ( " Folders " ) ) ;
} else {
settings - > beginGroup ( QLatin1String ( " Multifolders " ) ) ;
}
2015-04-24 11:18:33 +03:00
FolderDefinition : : save ( * settings , _definition ) ;
2015-09-17 14:48:05 +03:00
settings - > sync ( ) ;
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " Saved folder " < < _definition . alias < < " to settings, status " < < settings - > status ( ) ;
2015-04-24 11:18:33 +03:00
}
void Folder : : removeFromSettings ( ) const
{
2016-11-22 17:30:12 +03:00
auto settings = _accountState - > settings ( ) ;
2015-04-24 11:18:33 +03:00
settings - > beginGroup ( QLatin1String ( " Folders " ) ) ;
2016-06-03 14:10:01 +03:00
settings - > remove ( FolderMan : : escapeAlias ( _definition . alias ) ) ;
2016-11-22 17:30:12 +03:00
settings - > endGroup ( ) ;
settings - > beginGroup ( QLatin1String ( " Multifolders " ) ) ;
settings - > remove ( FolderMan : : escapeAlias ( _definition . alias ) ) ;
2015-04-24 11:18:33 +03:00
}
2017-05-17 11:55:42 +03:00
bool Folder : : isFileExcludedAbsolute ( const QString & fullPath ) const
2016-09-12 16:02:54 +03:00
{
return _engine - > excludedFiles ( ) . isExcluded ( fullPath , path ( ) , _definition . ignoreHiddenFiles ) ;
}
2017-05-17 11:55:42 +03:00
bool Folder : : isFileExcludedRelative ( const QString & relativePath ) const
2015-10-13 13:53:38 +03:00
{
2016-03-09 19:53:18 +03:00
return _engine - > excludedFiles ( ) . isExcluded ( path ( ) + relativePath , path ( ) , _definition . ignoreHiddenFiles ) ;
2015-10-13 13:53:38 +03:00
}
2014-05-13 15:39:00 +04:00
void Folder : : slotTerminateSync ( )
2011-10-18 12:22:24 +04:00
{
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " folder " < < alias ( ) < < " Terminating! " ;
2013-07-22 22:27:42 +04:00
2017-05-17 11:55:42 +03:00
if ( _engine - > isSyncRunning ( ) ) {
2014-03-17 14:47:23 +04:00
_engine - > abort ( ) ;
2013-11-25 01:26:50 +04:00
2014-05-13 15:39:00 +04:00
setSyncState ( SyncResult : : SyncAbortRequested ) ;
2013-07-22 22:27:42 +04:00
}
2011-10-18 12:22:24 +04:00
}
2014-03-28 13:23:09 +04:00
// This removes the csync File database
// This is needed to provide a clean startup again in case another
2013-07-22 22:27:42 +04:00
// local folder is synced to the same ownCloud.
2012-06-11 12:10:07 +04:00
void Folder : : wipe ( )
{
2016-07-10 13:52:47 +03:00
QString stateDbFile = _engine - > journal ( ) - > databaseFilePath ( ) ;
2013-07-22 22:27:42 +04:00
2014-11-06 14:49:02 +03:00
// Delete files that have been partially downloaded.
slotDiscardDownloadProgress ( ) ;
2016-11-22 15:17:04 +03:00
//Unregister the socket API so it does not keep the ._sync_journal file open
2015-10-23 18:24:45 +03:00
FolderMan : : instance ( ) - > socketApi ( ) - > slotUnregisterPath ( alias ( ) ) ;
2013-12-05 19:06:03 +04:00
_journal . close ( ) ; // close the sync journal
2013-07-22 22:27:42 +04:00
QFile file ( stateDbFile ) ;
2017-05-17 11:55:42 +03:00
if ( file . exists ( ) ) {
if ( ! file . remove ( ) ) {
2017-03-30 14:46:20 +03:00
qCWarning ( lcFolder ) < < " Failed to remove existing csync StateDB " < < stateDbFile ;
2013-07-22 22:27:42 +04:00
} else {
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " wipe: Removed csync StateDB " < < stateDbFile ;
2013-07-22 22:27:42 +04:00
}
} else {
2017-03-30 14:46:20 +03:00
qCWarning ( lcFolder ) < < " statedb is empty, can not remove. " ;
2013-07-22 22:27:42 +04:00
}
2014-11-06 14:49:02 +03:00
// Also remove other db related files
2017-05-17 11:55:42 +03:00
QFile : : remove ( stateDbFile + " .ctmp " ) ;
QFile : : remove ( stateDbFile + " -shm " ) ;
QFile : : remove ( stateDbFile + " -wal " ) ;
QFile : : remove ( stateDbFile + " -journal " ) ;
2015-10-23 18:24:45 +03:00
2016-04-28 23:43:53 +03:00
if ( canSync ( ) )
FolderMan : : instance ( ) - > socketApi ( ) - > slotRegisterPath ( alias ( ) ) ;
2013-07-22 22:27:42 +04:00
}
2017-11-29 14:04:01 +03:00
bool Folder : : reloadExcludes ( )
2013-07-22 22:27:42 +04:00
{
2017-11-29 14:04:01 +03:00
return _engine - > excludedFiles ( ) . reloadExcludeFiles ( ) ;
2013-07-22 22:27:42 +04:00
}
void Folder : : startSync ( const QStringList & pathList )
{
Q_UNUSED ( pathList )
2014-02-05 23:18:03 +04:00
if ( isBusy ( ) ) {
2017-05-09 15:24:11 +03:00
qCCritical ( lcFolder ) < < " ERROR csync is still running and new sync requested. " ;
2013-07-22 22:27:42 +04:00
return ;
}
_csyncUnavail = false ;
2017-05-29 11:48:21 +03:00
_timeSinceLastSyncStart . start ( ) ;
2017-05-17 11:55:42 +03:00
_syncResult . setStatus ( SyncResult : : SyncPrepare ) ;
2013-07-22 22:27:42 +04:00
emit syncStateChange ( ) ;
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " *** Start syncing " < < remoteUrl ( ) . toString ( ) < < " - client version "
2017-05-17 11:55:42 +03:00
< < qPrintable ( Theme : : instance ( ) - > version ( ) ) ;
2014-09-03 16:25:14 +04:00
2017-01-24 19:57:52 +03:00
_fileLog - > start ( path ( ) ) ;
2017-11-29 14:04:01 +03:00
if ( ! reloadExcludes ( ) ) {
2014-09-03 16:25:14 +04:00
slotSyncError ( tr ( " Could not read system exclude file " ) ) ;
2016-01-04 18:46:53 +03:00
QMetaObject : : invokeMethod ( this , " slotSyncFinished " , Qt : : QueuedConnection , Q_ARG ( bool , false ) ) ;
2014-09-03 16:25:14 +04:00
return ;
}
2013-07-22 22:27:42 +04:00
2014-06-07 13:49:41 +04:00
setDirtyNetworkLimits ( ) ;
2017-03-24 17:01:50 +03:00
setSyncOptions ( ) ;
2014-06-07 13:49:41 +04:00
2018-01-24 00:51:25 +03:00
static std : : chrono : : milliseconds fullLocalDiscoveryInterval = [ ] ( ) {
2017-09-14 17:43:23 +03:00
auto interval = ConfigFile ( ) . fullLocalDiscoveryInterval ( ) ;
QByteArray env = qgetenv ( " OWNCLOUD_FULL_LOCAL_DISCOVERY_INTERVAL " ) ;
if ( ! env . isEmpty ( ) ) {
2018-01-24 00:51:25 +03:00
interval = std : : chrono : : milliseconds ( env . toLongLong ( ) ) ;
2017-09-14 17:43:23 +03:00
}
return interval ;
} ( ) ;
2018-02-20 19:28:54 +03:00
bool hasDoneFullLocalDiscovery = _timeSinceLastFullLocalDiscovery . isValid ( ) ;
bool periodicFullLocalDiscoveryNow =
fullLocalDiscoveryInterval . count ( ) > = 0 // negative means we don't require periodic full runs
& & _timeSinceLastFullLocalDiscovery . hasExpired ( fullLocalDiscoveryInterval . count ( ) ) ;
if ( _folderWatcher & & _folderWatcher - > isReliable ( )
& & hasDoneFullLocalDiscovery
& & ! periodicFullLocalDiscoveryNow ) {
2017-09-14 17:43:23 +03:00
qCInfo ( lcFolder ) < < " Allowing local discovery to read from the database " ;
_engine - > setLocalDiscoveryOptions ( LocalDiscoveryStyle : : DatabaseAndFilesystem , _localDiscoveryPaths ) ;
if ( lcFolder ( ) . isDebugEnabled ( ) ) {
QByteArrayList paths ;
for ( auto & path : _localDiscoveryPaths )
paths . append ( path ) ;
qCDebug ( lcFolder ) < < " local discovery paths: " < < paths ;
}
_previousLocalDiscoveryPaths = std : : move ( _localDiscoveryPaths ) ;
} else {
qCInfo ( lcFolder ) < < " Forbidding local discovery to read from the database " ;
_engine - > setLocalDiscoveryOptions ( LocalDiscoveryStyle : : FilesystemOnly ) ;
_previousLocalDiscoveryPaths . clear ( ) ;
}
_localDiscoveryPaths . clear ( ) ;
2017-03-24 17:01:50 +03:00
_engine - > setIgnoreHiddenFiles ( _definition . ignoreHiddenFiles ) ;
QMetaObject : : invokeMethod ( _engine . data ( ) , " startSync " , Qt : : QueuedConnection ) ;
emit syncStarted ( ) ;
}
void Folder : : setSyncOptions ( )
{
2017-01-24 12:16:10 +03:00
SyncOptions opt ;
2015-07-07 17:28:48 +03:00
ConfigFile cfgFile ;
2017-03-24 17:01:50 +03:00
2015-07-27 10:54:20 +03:00
auto newFolderLimit = cfgFile . newBigFolderSizeLimit ( ) ;
2017-01-24 12:16:10 +03:00
opt . _newBigFolderSizeLimit = newFolderLimit . first ? newFolderLimit . second * 1000LL * 1000LL : - 1 ; // convert from MB to B
opt . _confirmExternalStorage = cfgFile . confirmExternalStorage ( ) ;
2017-12-25 13:46:38 +03:00
opt . _moveFilesToTrash = cfgFile . moveToTrash ( ) ;
2015-06-11 16:54:39 +03:00
2017-03-24 17:01:50 +03:00
QByteArray chunkSizeEnv = qgetenv ( " OWNCLOUD_CHUNK_SIZE " ) ;
if ( ! chunkSizeEnv . isEmpty ( ) ) {
opt . _initialChunkSize = chunkSizeEnv . toUInt ( ) ;
} else {
opt . _initialChunkSize = cfgFile . chunkSize ( ) ;
}
QByteArray minChunkSizeEnv = qgetenv ( " OWNCLOUD_MIN_CHUNK_SIZE " ) ;
if ( ! minChunkSizeEnv . isEmpty ( ) ) {
opt . _minChunkSize = minChunkSizeEnv . toUInt ( ) ;
} else {
opt . _minChunkSize = cfgFile . minChunkSize ( ) ;
}
QByteArray maxChunkSizeEnv = qgetenv ( " OWNCLOUD_MAX_CHUNK_SIZE " ) ;
if ( ! maxChunkSizeEnv . isEmpty ( ) ) {
opt . _maxChunkSize = maxChunkSizeEnv . toUInt ( ) ;
} else {
opt . _maxChunkSize = cfgFile . maxChunkSize ( ) ;
}
2016-04-12 12:49:52 +03:00
2017-03-24 17:01:50 +03:00
// Previously min/max chunk size values didn't exist, so users might
// have setups where the chunk size exceeds the new min/max default
// values. To cope with this, adjust min/max to always include the
// initial chunk size value.
opt . _minChunkSize = qMin ( opt . _minChunkSize , opt . _initialChunkSize ) ;
opt . _maxChunkSize = qMax ( opt . _maxChunkSize , opt . _initialChunkSize ) ;
2013-08-05 16:35:01 +04:00
2017-03-24 17:01:50 +03:00
QByteArray targetChunkUploadDurationEnv = qgetenv ( " OWNCLOUD_TARGET_CHUNK_UPLOAD_DURATION " ) ;
if ( ! targetChunkUploadDurationEnv . isEmpty ( ) ) {
2018-01-24 00:51:25 +03:00
opt . _targetChunkUploadDuration = std : : chrono : : milliseconds ( targetChunkUploadDurationEnv . toUInt ( ) ) ;
2017-03-24 17:01:50 +03:00
} else {
opt . _targetChunkUploadDuration = cfgFile . targetChunkUploadDuration ( ) ;
}
_engine - > setSyncOptions ( opt ) ;
2012-06-11 12:10:07 +04:00
}
2014-01-31 20:29:50 +04:00
void Folder : : setDirtyNetworkLimits ( )
{
2016-03-09 19:53:18 +03:00
ConfigFile cfg ;
int downloadLimit = - 75 ; // 75%
int useDownLimit = cfg . useDownloadLimit ( ) ;
if ( useDownLimit > = 1 ) {
downloadLimit = cfg . downloadLimit ( ) * 1000 ;
} else if ( useDownLimit = = 0 ) {
downloadLimit = 0 ;
}
2014-06-07 13:49:41 +04:00
2016-03-09 19:53:18 +03:00
int uploadLimit = - 75 ; // 75%
int useUpLimit = cfg . useUploadLimit ( ) ;
2017-05-17 11:55:42 +03:00
if ( useUpLimit > = 1 ) {
2016-03-09 19:53:18 +03:00
uploadLimit = cfg . uploadLimit ( ) * 1000 ;
} else if ( useUpLimit = = 0 ) {
uploadLimit = 0 ;
2014-01-31 20:29:50 +04:00
}
2016-03-09 19:53:18 +03:00
_engine - > setNetworkLimits ( uploadLimit , downloadLimit ) ;
2014-01-31 20:29:50 +04:00
}
2017-07-11 16:54:01 +03:00
void Folder : : slotSyncError ( const QString & message , ErrorCategory category )
2013-07-22 22:27:42 +04:00
{
2017-06-28 13:45:54 +03:00
_syncResult . appendErrorString ( message ) ;
2017-07-11 16:54:01 +03:00
emit ProgressDispatcher : : instance ( ) - > syncError ( alias ( ) , message , category ) ;
2013-07-22 22:27:42 +04:00
}
2014-03-17 14:47:23 +04:00
void Folder : : slotSyncStarted ( )
2013-07-22 22:27:42 +04:00
{
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " #### Propagation start #################################################### " ;
2013-07-22 22:27:42 +04:00
_syncResult . setStatus ( SyncResult : : SyncRunning ) ;
emit syncStateChange ( ) ;
}
void Folder : : slotCsyncUnavailable ( )
{
_csyncUnavail = true ;
}
2015-10-29 18:43:30 +03:00
void Folder : : slotSyncFinished ( bool success )
2013-07-22 22:27:42 +04:00
{
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " Client version " < < qPrintable ( Theme : : instance ( ) - > version ( ) )
2017-05-17 11:55:42 +03:00
< < " Qt " < < qVersion ( )
< < " SSL " < < QSslSocket : : sslLibraryVersionString ( ) . toUtf8 ( ) . data ( )
;
2015-07-30 15:11:29 +03:00
2017-01-25 13:28:18 +03:00
bool syncError = ! _syncResult . errorStrings ( ) . isEmpty ( ) ;
2017-05-17 11:55:42 +03:00
if ( syncError ) {
2017-03-30 14:46:20 +03:00
qCWarning ( lcFolder ) < < " SyncEngine finished with ERROR " ;
2014-10-10 12:12:30 +04:00
} else {
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " SyncEngine finished without problem. " ;
2014-10-10 12:12:30 +04:00
}
2016-06-09 15:10:47 +03:00
_fileLog - > finish ( ) ;
2017-01-25 13:28:18 +03:00
showSyncResultPopup ( ) ;
2014-03-26 21:10:59 +04:00
2016-12-06 15:18:59 +03:00
auto anotherSyncNeeded = _engine - > isAnotherSyncNeeded ( ) ;
2013-07-22 22:27:42 +04:00
2017-01-25 13:28:18 +03:00
if ( syncError ) {
2013-07-22 22:27:42 +04:00
_syncResult . setStatus ( SyncResult : : Error ) ;
} else if ( _csyncUnavail ) {
2014-08-15 17:01:01 +04:00
_syncResult . setStatus ( SyncResult : : Error ) ;
2017-03-30 14:46:20 +03:00
qCWarning ( lcFolder ) < < " csync not available. " ;
2017-05-17 11:55:42 +03:00
} else if ( _syncResult . foundFilesNotSynced ( ) ) {
2013-08-02 16:22:01 +04:00
_syncResult . setStatus ( SyncResult : : Problem ) ;
2017-05-17 11:55:42 +03:00
} else if ( _definition . paused ) {
2016-11-29 11:56:45 +03:00
// Maybe the sync was terminated because the user paused the folder
_syncResult . setStatus ( SyncResult : : Paused ) ;
2013-07-22 22:27:42 +04:00
} else {
_syncResult . setStatus ( SyncResult : : Success ) ;
}
2014-10-24 12:57:16 +04:00
// Count the number of syncs that have failed in a row.
if ( _syncResult . status ( ) = = SyncResult : : Success
2017-05-17 11:55:42 +03:00
| | _syncResult . status ( ) = = SyncResult : : Problem ) {
2014-10-24 12:57:16 +04:00
_consecutiveFailingSyncs = 0 ;
2017-05-17 11:55:42 +03:00
} else {
2014-10-24 12:57:16 +04:00
_consecutiveFailingSyncs + + ;
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " the last " < < _consecutiveFailingSyncs < < " syncs failed " ;
2014-10-24 12:57:16 +04:00
}
2015-10-29 18:43:30 +03:00
if ( _syncResult . status ( ) = = SyncResult : : Success & & success ) {
2015-10-05 07:21:19 +03:00
// Clear the white list as all the folders that should be on that list are sync-ed
2015-06-11 16:46:01 +03:00
journalDb ( ) - > setSelectiveSyncList ( SyncJournalDb : : SelectiveSyncWhiteList , QStringList ( ) ) ;
}
2017-09-14 17:43:23 +03:00
// bug: This function uses many different criteria for "sync was successful" - investigate!
if ( ( _syncResult . status ( ) = = SyncResult : : Success
| | _syncResult . status ( ) = = SyncResult : : Problem )
& & success ) {
if ( _engine - > lastLocalDiscoveryStyle ( ) = = LocalDiscoveryStyle : : FilesystemOnly ) {
_timeSinceLastFullLocalDiscovery . start ( ) ;
}
qCDebug ( lcFolder ) < < " Sync success, forgetting last sync's local discovery path list " ;
} else {
// On overall-failure we can't forget about last sync's local discovery
// paths yet, reuse them for the next sync again.
// C++17: Could use std::set::merge().
_localDiscoveryPaths . insert (
_previousLocalDiscoveryPaths . begin ( ) , _previousLocalDiscoveryPaths . end ( ) ) ;
qCDebug ( lcFolder ) < < " Sync failed, keeping last sync's local discovery path list " ;
}
_previousLocalDiscoveryPaths . clear ( ) ;
2013-08-14 18:55:43 +04:00
emit syncStateChange ( ) ;
2014-05-27 13:33:08 +04:00
// The syncFinished result that is to be triggered here makes the folderman
2015-10-05 07:21:19 +03:00
// clear the current running sync folder marker.
2014-05-27 13:33:08 +04:00
// Lets wait a bit to do that because, as long as this marker is not cleared,
// file system change notifications are ignored for that folder. And it takes
// some time under certain conditions to make the file system notifications
// all come in.
2017-09-20 11:14:48 +03:00
QTimer : : singleShot ( 200 , this , & Folder : : slotEmitFinishedDelayed ) ;
2014-05-27 13:33:08 +04:00
2018-01-24 00:51:25 +03:00
_lastSyncDuration = std : : chrono : : milliseconds ( _timeSinceLastSyncStart . elapsed ( ) ) ;
2017-05-29 11:48:21 +03:00
_timeSinceLastSyncDone . start ( ) ;
2014-10-24 14:52:24 +04:00
// Increment the follow-up sync counter if necessary.
2016-12-06 15:18:59 +03:00
if ( anotherSyncNeeded = = ImmediateFollowUp ) {
2014-10-24 14:52:24 +04:00
_consecutiveFollowUpSyncs + + ;
2017-03-30 14:46:20 +03:00
qCInfo ( lcFolder ) < < " another sync was requested by the finished sync, this has "
2017-05-17 11:55:42 +03:00
< < " happened " < < _consecutiveFollowUpSyncs < < " times " ;
2014-09-10 19:25:13 +04:00
} else {
2014-10-24 14:52:24 +04:00
_consecutiveFollowUpSyncs = 0 ;
2014-09-10 19:25:13 +04:00
}
2014-10-24 14:52:24 +04:00
// Maybe force a follow-up sync to take place, but only a couple of times.
2017-05-17 11:55:42 +03:00
if ( anotherSyncNeeded = = ImmediateFollowUp & & _consecutiveFollowUpSyncs < = 3 ) {
2016-10-18 17:04:25 +03:00
// Sometimes another sync is requested because a local file is still
// changing, so wait at least a small amount of time before syncing
// the folder again.
scheduleThisFolderSoon ( ) ;
2014-10-24 14:52:24 +04:00
}
2014-05-27 13:33:08 +04:00
}
void Folder : : slotEmitFinishedDelayed ( )
{
2017-05-17 11:55:42 +03:00
emit syncFinished ( _syncResult ) ;
2017-09-12 14:02:00 +03:00
// Immediately check the etag again if there was some sync activity.
if ( ( _syncResult . status ( ) = = SyncResult : : Success
| | _syncResult . status ( ) = = SyncResult : : Problem )
& & ( _syncResult . firstItemDeleted ( )
| | _syncResult . firstItemNew ( )
| | _syncResult . firstItemRenamed ( )
| | _syncResult . firstItemUpdated ( )
| | _syncResult . firstNewConflictItem ( ) ) ) {
slotRunEtagJob ( ) ;
}
2013-07-22 22:27:42 +04:00
}
2013-11-25 19:16:33 +04:00
// the progress comes without a folder and the valid path set. Add that here
// and hand the result over to the progress dispatcher.
2015-01-30 15:36:20 +03:00
void Folder : : slotTransmissionProgress ( const ProgressInfo & pi )
2013-07-22 22:27:42 +04:00
{
2015-06-02 20:45:23 +03:00
emit progressInfo ( pi ) ;
2014-03-14 16:03:16 +04:00
ProgressDispatcher : : instance ( ) - > setProgressInfo ( alias ( ) , pi ) ;
2013-07-22 22:27:42 +04:00
}
2015-08-11 14:45:02 +03:00
// a item is completed: count the errors and forward to the ProgressDispatcher
2017-01-25 13:12:38 +03:00
void Folder : : slotItemCompleted ( const SyncFileItemPtr & item )
2014-06-06 17:52:55 +04:00
{
2017-10-23 17:35:35 +03:00
if ( item - > _instruction = = CSYNC_INSTRUCTION_NONE | | item - > _instruction = = CSYNC_INSTRUCTION_UPDATE_METADATA ) {
// We only care about the updates that deserve to be shown in the UI
return ;
}
2017-01-25 13:28:18 +03:00
_syncResult . processCompletedItem ( item ) ;
2017-01-25 13:12:38 +03:00
_fileLog - > logItem ( * item ) ;
2017-01-25 16:09:44 +03:00
emit ProgressDispatcher : : instance ( ) - > itemCompleted ( alias ( ) , item ) ;
2014-06-06 17:52:55 +04:00
}
2017-01-26 11:03:01 +03:00
void Folder : : slotNewBigFolderDiscovered ( const QString & newF , bool isExternal )
2015-06-11 16:46:01 +03:00
{
auto newFolder = newF ;
if ( ! newFolder . endsWith ( QLatin1Char ( ' / ' ) ) ) {
newFolder + = QLatin1Char ( ' / ' ) ;
}
auto journal = journalDb ( ) ;
// Add the entry to the blacklist if it is neither in the blacklist or whitelist already
2020-05-29 16:07:05 +03:00
bool ok1 = false ;
bool ok2 = false ;
2016-04-06 16:01:28 +03:00
auto blacklist = journal - > getSelectiveSyncList ( SyncJournalDb : : SelectiveSyncBlackList , & ok1 ) ;
auto whitelist = journal - > getSelectiveSyncList ( SyncJournalDb : : SelectiveSyncWhiteList , & ok2 ) ;
if ( ok1 & & ok2 & & ! blacklist . contains ( newFolder ) & & ! whitelist . contains ( newFolder ) ) {
2015-06-11 16:46:01 +03:00
blacklist . append ( newFolder ) ;
journal - > setSelectiveSyncList ( SyncJournalDb : : SelectiveSyncBlackList , blacklist ) ;
}
// And add the entry to the undecided list and signal the UI
2016-04-06 16:01:28 +03:00
auto undecidedList = journal - > getSelectiveSyncList ( SyncJournalDb : : SelectiveSyncUndecidedList , & ok1 ) ;
2017-05-17 11:55:42 +03:00
if ( ok1 ) {
2016-04-06 16:01:28 +03:00
if ( ! undecidedList . contains ( newFolder ) ) {
undecidedList . append ( newFolder ) ;
journal - > setSelectiveSyncList ( SyncJournalDb : : SelectiveSyncUndecidedList , undecidedList ) ;
emit newBigFolderDiscovered ( newFolder ) ;
}
2017-05-17 11:55:42 +03:00
QString message = ! isExternal ? ( tr ( " A new folder larger than %1 MB has been added: %2. \n " )
. arg ( ConfigFile ( ) . newBigFolderSizeLimit ( ) . second )
. arg ( newF ) )
: ( tr ( " A folder from an external storage has been added. \n " ) ) ;
2017-01-26 11:03:01 +03:00
message + = tr ( " Please go in the settings to select it if you wish to download it. " ) ;
2015-07-27 11:33:05 +03:00
2016-04-06 16:01:28 +03:00
auto logger = Logger : : instance ( ) ;
logger - > postOptionalGuiLog ( Theme : : instance ( ) - > appNameGUI ( ) , message ) ;
}
2015-06-11 16:46:01 +03:00
}
2016-06-09 15:28:15 +03:00
void Folder : : slotLogPropagationStart ( )
{
_fileLog - > logLap ( " Propagation starts " ) ;
}
2016-10-18 17:04:25 +03:00
void Folder : : slotScheduleThisFolder ( )
{
2016-10-19 12:03:13 +03:00
FolderMan : : instance ( ) - > scheduleFolder ( this ) ;
2016-10-18 17:04:25 +03:00
}
2015-06-11 16:46:01 +03:00
2017-09-14 17:43:23 +03:00
void Folder : : slotNextSyncFullLocalDiscovery ( )
{
_timeSinceLastFullLocalDiscovery . invalidate ( ) ;
}
2018-02-21 15:55:33 +03:00
void Folder : : slotFolderConflicts ( const QString & folder , const QStringList & conflictPaths )
{
if ( folder ! = _definition . alias )
return ;
auto & r = _syncResult ;
// If the number of conflicts is too low, adjust it upwards
if ( conflictPaths . size ( ) > r . numNewConflictItems ( ) + r . numOldConflictItems ( ) )
r . setNumOldConflictItems ( conflictPaths . size ( ) - r . numNewConflictItems ( ) ) ;
}
2018-02-23 15:24:46 +03:00
void Folder : : warnOnNewExcludedItem ( const SyncJournalFileRecord & record , const QStringRef & path )
{
// Never warn for items in the database
if ( record . isValid ( ) )
return ;
// Don't warn for items that no longer exist.
// Note: This assumes we're getting file watcher notifications
// for folders only on creation and deletion - if we got a notification
// on content change that would create spurious warnings.
QFileInfo fi ( _canonicalLocalPath + path ) ;
if ( ! fi . exists ( ) )
return ;
bool ok = false ;
auto blacklist = _journal . getSelectiveSyncList ( SyncJournalDb : : SelectiveSyncBlackList , & ok ) ;
if ( ! ok )
return ;
if ( ! blacklist . contains ( path + " / " ) )
return ;
const auto message = fi . isDir ( )
? tr ( " The folder %1 was created but was excluded from synchronization previously. "
" Data inside it will not be synchronized. " )
. arg ( fi . filePath ( ) )
: tr ( " The file %1 was created but was excluded from synchronization previously. "
" It will not be synchronized. " )
. arg ( fi . filePath ( ) ) ;
Logger : : instance ( ) - > postOptionalGuiLog ( Theme : : instance ( ) - > appNameGUI ( ) , message ) ;
}
2018-04-24 10:52:15 +03:00
void Folder : : slotWatcherUnreliable ( const QString & message )
{
qCWarning ( lcFolder ) < < " Folder watcher for " < < path ( ) < < " became unreliable: " < < message ;
auto fullMessage =
tr ( " Changes in synchronized folders could not be tracked reliably. \n "
" \n "
" This means that the synchronization client might not upload local changes "
" immediately and will instead only scan for local changes and upload them "
" occasionally (every two hours by default). \n "
" \n "
" %1 " ) . arg ( message ) ;
Logger : : instance ( ) - > postGuiLog ( Theme : : instance ( ) - > appNameGUI ( ) , fullMessage ) ;
}
2016-10-18 17:04:25 +03:00
void Folder : : scheduleThisFolderSoon ( )
{
if ( ! _scheduleSelfTimer . isActive ( ) ) {
_scheduleSelfTimer . start ( ) ;
}
}
2014-06-06 17:52:55 +04:00
2016-11-23 12:40:17 +03:00
void Folder : : setSaveBackwardsCompatible ( bool save )
{
_saveBackwardsCompatible = save ;
}
2017-10-09 13:06:11 +03:00
void Folder : : registerFolderWatcher ( )
{
if ( _folderWatcher )
return ;
if ( ! QDir ( path ( ) ) . exists ( ) )
return ;
2018-04-24 10:52:15 +03:00
_folderWatcher . reset ( new FolderWatcher ( this ) ) ;
2017-10-09 13:06:11 +03:00
connect ( _folderWatcher . data ( ) , & FolderWatcher : : pathChanged ,
this , & Folder : : slotWatchedPathChanged ) ;
2017-09-14 17:43:23 +03:00
connect ( _folderWatcher . data ( ) , & FolderWatcher : : lostChanges ,
this , & Folder : : slotNextSyncFullLocalDiscovery ) ;
2018-04-24 10:52:15 +03:00
connect ( _folderWatcher . data ( ) , & FolderWatcher : : becameUnreliable ,
this , & Folder : : slotWatcherUnreliable ) ;
_folderWatcher - > init ( path ( ) ) ;
2017-10-09 13:06:11 +03:00
}
2017-02-08 14:33:46 +03:00
void Folder : : slotAboutToRemoveAllFiles ( SyncFileItem : : Direction dir , bool * cancel )
2013-07-22 22:27:42 +04:00
{
2016-02-22 18:14:22 +03:00
ConfigFile cfgFile ;
if ( ! cfgFile . promptDeleteFiles ( ) )
return ;
2019-10-04 09:10:55 +03:00
QString msg = dir = = SyncFileItem : : Down ? tr ( " All files in the sync folder '%1' were deleted on the server. \n "
2017-05-17 11:55:42 +03:00
" These deletes will be synchronized to your local sync folder, making such files "
" unavailable unless you have a right to restore. \n "
2019-09-20 17:47:11 +03:00
" If you decide to restore the files, they will be re-synced with the server if you have rights to do so. \n "
2017-05-17 11:55:42 +03:00
" If you decide to delete the files, they will be unavailable to you, unless you are the owner. " )
2019-09-20 17:47:11 +03:00
: tr ( " All files got deleted from your local sync folder '%1'. \n "
" These files will be deleted from the server and will not be available on your other devices if they "
" will not be restored. \n "
" If this action was unintended you can restore the lost data now. " ) ;
QMessageBox msgBox ( QMessageBox : : Warning , tr ( " Delete all files? " ) ,
2017-05-17 11:55:42 +03:00
msg . arg ( shortGuiLocalPath ( ) ) ) ;
2017-02-20 15:58:35 +03:00
msgBox . setWindowFlags ( msgBox . windowFlags ( ) | Qt : : WindowStaysOnTopHint ) ;
2019-09-20 17:47:11 +03:00
msgBox . addButton ( tr ( " Delete all files " ) , QMessageBox : : DestructiveRole ) ;
QPushButton * keepBtn = msgBox . addButton ( tr ( " Restore deleted files " ) , QMessageBox : : AcceptRole ) ;
2013-07-22 22:27:42 +04:00
if ( msgBox . exec ( ) = = - 1 ) {
* cancel = true ;
return ;
}
* cancel = msgBox . clickedButton ( ) = = keepBtn ;
if ( * cancel ) {
2017-04-10 17:15:28 +03:00
FileSystem : : setFolderMinimumPermissions ( path ( ) ) ;
2017-02-15 15:27:41 +03:00
journalDb ( ) - > clearFileTable ( ) ;
2014-10-08 12:41:03 +04:00
_lastEtag . clear ( ) ;
2016-10-18 17:04:25 +03:00
slotScheduleThisFolder ( ) ;
2013-07-22 22:27:42 +04:00
}
}
2015-04-24 11:18:33 +03:00
2016-01-05 13:47:17 +03:00
void Folder : : slotAboutToRestoreBackup ( bool * restore )
{
QString msg =
2016-05-27 18:13:18 +03:00
tr ( " This sync would reset the files to an earlier time in the sync folder '%1'. \n "
2016-01-05 13:47:17 +03:00
" This might be because a backup was restored on the server. \n "
" Continuing the sync as normal will cause all your files to be overwritten by an older "
" file in an earlier state. "
" Do you want to keep your local most recent files as conflict files? " ) ;
QMessageBox msgBox ( QMessageBox : : Warning , tr ( " Backup detected " ) ,
2017-05-17 11:55:42 +03:00
msg . arg ( shortGuiLocalPath ( ) ) ) ;
2017-02-20 15:58:35 +03:00
msgBox . setWindowFlags ( msgBox . windowFlags ( ) | Qt : : WindowStaysOnTopHint ) ;
2016-01-05 13:47:17 +03:00
msgBox . addButton ( tr ( " Normal Synchronisation " ) , QMessageBox : : DestructiveRole ) ;
2017-05-17 11:55:42 +03:00
QPushButton * keepBtn = msgBox . addButton ( tr ( " Keep Local Files as Conflict " ) , QMessageBox : : AcceptRole ) ;
2016-01-05 13:47:17 +03:00
if ( msgBox . exec ( ) = = - 1 ) {
* restore = true ;
return ;
}
* restore = msgBox . clickedButton ( ) = = keepBtn ;
}
2015-04-24 11:18:33 +03:00
2017-05-17 11:55:42 +03:00
void FolderDefinition : : save ( QSettings & settings , const FolderDefinition & folder )
2015-04-24 11:18:33 +03:00
{
2015-09-02 17:05:58 +03:00
settings . beginGroup ( FolderMan : : escapeAlias ( folder . alias ) ) ;
2015-04-24 11:18:33 +03:00
settings . setValue ( QLatin1String ( " localPath " ) , folder . localPath ) ;
2016-11-23 12:40:17 +03:00
settings . setValue ( QLatin1String ( " journalPath " ) , folder . journalPath ) ;
2015-04-24 11:18:33 +03:00
settings . setValue ( QLatin1String ( " targetPath " ) , folder . targetPath ) ;
settings . setValue ( QLatin1String ( " paused " ) , folder . paused ) ;
2015-07-13 23:15:02 +03:00
settings . setValue ( QLatin1String ( " ignoreHiddenFiles " ) , folder . ignoreHiddenFiles ) ;
2017-10-04 14:49:42 +03:00
// Happens only on Windows when the explorer integration is enabled.
if ( ! folder . navigationPaneClsid . isNull ( ) )
settings . setValue ( QLatin1String ( " navigationPaneClsid " ) , folder . navigationPaneClsid ) ;
else
settings . remove ( QLatin1String ( " navigationPaneClsid " ) ) ;
2015-04-24 11:18:33 +03:00
settings . endGroup ( ) ;
}
2017-05-17 11:55:42 +03:00
bool FolderDefinition : : load ( QSettings & settings , const QString & alias ,
FolderDefinition * folder )
2015-04-24 11:18:33 +03:00
{
settings . beginGroup ( alias ) ;
2015-09-02 17:05:58 +03:00
folder - > alias = FolderMan : : unescapeAlias ( alias ) ;
2015-04-24 11:18:33 +03:00
folder - > localPath = settings . value ( QLatin1String ( " localPath " ) ) . toString ( ) ;
2016-11-23 12:40:17 +03:00
folder - > journalPath = settings . value ( QLatin1String ( " journalPath " ) ) . toString ( ) ;
2015-04-24 11:18:33 +03:00
folder - > targetPath = settings . value ( QLatin1String ( " targetPath " ) ) . toString ( ) ;
folder - > paused = settings . value ( QLatin1String ( " paused " ) ) . toBool ( ) ;
2015-07-13 23:15:02 +03:00
folder - > ignoreHiddenFiles = settings . value ( QLatin1String ( " ignoreHiddenFiles " ) , QVariant ( true ) ) . toBool ( ) ;
2017-10-04 14:49:42 +03:00
folder - > navigationPaneClsid = settings . value ( QLatin1String ( " navigationPaneClsid " ) ) . toUuid ( ) ;
2015-04-24 11:18:33 +03:00
settings . endGroup ( ) ;
2015-09-17 13:14:35 +03:00
// Old settings can contain paths with native separators. In the rest of the
// code we assum /, so clean it up now.
folder - > localPath = prepareLocalPath ( folder - > localPath ) ;
2016-11-23 12:40:17 +03:00
// Target paths also have a convention
folder - > targetPath = prepareTargetPath ( folder - > targetPath ) ;
2015-04-24 11:18:33 +03:00
return true ;
}
2017-05-17 11:55:42 +03:00
QString FolderDefinition : : prepareLocalPath ( const QString & path )
2015-09-17 13:14:35 +03:00
{
QString p = QDir : : fromNativeSeparators ( path ) ;
if ( ! p . endsWith ( QLatin1Char ( ' / ' ) ) ) {
p . append ( QLatin1Char ( ' / ' ) ) ;
}
return p ;
}
2016-11-23 12:40:17 +03:00
QString FolderDefinition : : prepareTargetPath ( const QString & path )
{
QString p = path ;
if ( p . endsWith ( QLatin1Char ( ' / ' ) ) ) {
p . chop ( 1 ) ;
}
// Doing this second ensures the empty string or "/" come
// out as "/".
if ( ! p . startsWith ( QLatin1Char ( ' / ' ) ) ) {
p . prepend ( QLatin1Char ( ' / ' ) ) ;
}
return p ;
}
QString FolderDefinition : : absoluteJournalPath ( ) const
{
return QDir ( localPath ) . filePath ( journalPath ) ;
}
QString FolderDefinition : : defaultJournalPath ( AccountPtr account )
{
2020-11-05 17:27:27 +03:00
return SyncJournalDb : : makeDbName ( localPath , account - > url ( ) , targetPath , account - > credentials ( ) - > user ( ) ) ;
2016-11-23 12:40:17 +03:00
}
2014-11-10 00:34:07 +03:00
} // namespace OCC