add_definitions(-DOWNCLOUD_5XX_NO_BLACKLIST=1)
endif()
-# When this option is enabled, a rename that is not allowed will be renamed back
-# do the original as a restoration step. Withut this option, the restoration will
-# re-download the file instead.
-# The default is off because we don't want to rename the files back behind the user's back
-# Added for IL issue #550
-option(OWNCLOUD_RESTORE_RENAME "OWNCLOUD_RESTORE_RENAME" OFF)
-if(OWNCLOUD_RESTORE_RENAME)
- add_definitions(-DOWNCLOUD_RESTORE_RENAME=1)
-endif()
-
# Disable shibboleth.
# So the client can be built without QtWebKit
option(NO_SHIBBOLETH "Build without Shibboleth support. Allow to build the client without QtWebKit" OFF)
DiscoverySingleDirectoryJob *serverJob = nullptr;
if (_queryServer == NormalQuery) {
- serverJob = new DiscoverySingleDirectoryJob(_discoveryData->_account, _discoveryData->_remoteFolder + _currentFolder._server, this);
+ serverJob = new DiscoverySingleDirectoryJob(_discoveryData->_account,
+ _discoveryData->_remoteFolder + _currentFolder._server, this);
connect(serverJob, &DiscoverySingleDirectoryJob::finished, this, [this](const auto &results) {
if (results) {
_serverEntries = *results;
emit finished();
}
});
+ connect(serverJob, &DiscoverySingleDirectoryJob::firstDirectoryPermissions, this,
+ [this](const RemotePermissions &perms) { _rootPermissions = perms; });
serverJob->start();
} else {
_hasServerEntries = true;
}
qCInfo(lcDisco) << "Discovered" << item->_file << item->_instruction << item->_direction << item->isDirectory();
-
if (item->isDirectory()) {
auto job = new ProcessDirectoryJob(item, recurseQueryServer,
item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist,
if (!_dirItem || _dirItem->_direction == SyncFileItem::Down) {
_childModified = true;
}
- // Check if it is a rename
+ // Check if it is a move
OCC::SyncJournalFileRecord base;
if (!_discoveryData->_statedb->getFileRecordByInode(localEntry.inode, &base)) {
qFatal("TODO: handle DB Errors");
}
- bool isRename = base.isValid() && base._type == item->_type
+ bool isMove = base.isValid() && base._type == item->_type
&& ((base._modtime == localEntry.modtime && base._fileSize == localEntry.size) || item->_type == ItemTypeDirectory);
- if (isRename) {
+ if (isMove) {
// The old file must have been deleted.
- isRename = !QFile::exists(_discoveryData->_localDir + base._path);
+ isMove = !QFile::exists(_discoveryData->_localDir + base._path);
}
// Verify the checksum where possible
- if (isRename && !base._checksumHeader.isEmpty() && item->_type == ItemTypeFile) {
+ if (isMove && !base._checksumHeader.isEmpty() && item->_type == ItemTypeFile) {
if (computeLocalChecksum(parseChecksumHeaderType(base._checksumHeader), path._original)) {
qCInfo(lcDisco) << "checking checksum of potential rename " << path._original << item->_checksumHeader << base._checksumHeader;
- isRename = item->_checksumHeader == base._checksumHeader;
+ isMove = item->_checksumHeader == base._checksumHeader;
}
}
auto originalPath = QString::fromUtf8(base._path);
- if (isRename && _discoveryData->_renamedItems.contains(originalPath))
- isRename = false;
- if (isRename) {
+ if (isMove && _discoveryData->_renamedItems.contains(originalPath))
+ isMove = false;
+
+ //Check local permission if we are allowed to put move the file here
+ if (isMove) {
+ auto destPerms = !_rootPermissions.isNull() ? _rootPermissions
+ : _dirItem ? _dirItem->_remotePerm : _rootPermissions;
+ auto filePerms = base._remotePerm; // Technicly we should use the one from the server, but we'll assume it is the same
+ //true when it is just a rename in the same directory. (not a move)
+ bool isRename = originalPath.startsWith(_currentFolder._original)
+ && originalPath.lastIndexOf('/') == _currentFolder._original.size();
+ // Check if we are allowed to move to the destination.
+ bool destinationOK = true;
+ if (isRename || destPerms.isNull()) {
+ // no need to check for the destination dir permission
+ destinationOK = true;
+ } else if (item->isDirectory() && !destPerms.hasPermission(RemotePermissions::CanAddSubDirectories)) {
+ destinationOK = false;
+ } else if (!item->isDirectory() && !destPerms.hasPermission(RemotePermissions::CanAddFile)) {
+ destinationOK = false;
+ }
+
+ // check if we are allowed to move from the source
+ bool sourceOK = true;
+ if (!filePerms.isNull()
+ && ((isRename && !filePerms.hasPermission(RemotePermissions::CanRename))
+ || (!isRename && !filePerms.hasPermission(RemotePermissions::CanMove)))) {
+ // We are not allowed to move or rename this file
+ sourceOK = false;
+ }
+ if (!sourceOK || !destinationOK) {
+ qCInfo(lcDisco) << "Not a move because permission does not allow it." << sourceOK << destinationOK;
+ if (!sourceOK) {
+ // This is the behavior that we had in the client <= 2.5.
+ _discoveryData->_statedb->avoidRenamesOnNextSync(base._path);
+ }
+ isMove = false;
+ }
+ }
+
+ if (isMove) {
QByteArray oldEtag;
auto it = _discoveryData->_deletedItem.find(originalPath);
if (it != _discoveryData->_deletedItem.end()) {
if ((*it)->_instruction != CSYNC_INSTRUCTION_REMOVE)
- isRename = false;
+ isMove = false;
else
(*it)->_instruction = CSYNC_INSTRUCTION_NONE;
oldEtag = (*it)->_etag;
if (!item->isDirectory() && oldEtag != base._etag) {
- isRename = false;
+ isMove = false;
}
}
if (auto deleteJob = static_cast<ProcessDirectoryJob *>(_discoveryData->_queuedDeletedDirectories.value(originalPath).data())) {
path._server = adjustedOriginalPath;
qCInfo(lcDisco) << "Rename detected (up) " << item->_file << " -> " << item->_renameTarget;
};
- if (isRename && !oldEtag.isEmpty()) {
+ if (isMove && !oldEtag.isEmpty()) {
recurseQueryServer = oldEtag == base._etag ? ParentNotChanged : NormalQuery;
processRename(path);
- } else if (isRename) {
+ } else if (isMove) {
// We must query the server to know if the etag has not changed
_pendingAsyncJobs++;
auto job = new RequestEtagJob(_discoveryData->_account, originalPath, this);
}
qCInfo(lcDisco) << "Discovered" << item->_file << item->_instruction << item->_direction << item->isDirectory();
- if (item->isDirectory()) {
+ bool recurse = checkPremission(item);
+ if (recurse && item->isDirectory()) {
auto job = new ProcessDirectoryJob(item, recurseQueryServer, NormalQuery, _discoveryData, this);
job->_currentFolder = path;
connect(job, &ProcessDirectoryJob::itemDiscovered, this, &ProcessDirectoryJob::itemDiscovered);
item->_type = ItemTypeVirtualFile;
item->_file.append(_discoveryData->_syncOptions._virtualFileSuffix);
} else if (!serverModified) {
- if (!dbEntry._serverHasIgnoredFiles) {
+ // Removed locally: also remove on the server.
+ if (_dirItem && _dirItem->_isRestoration && _dirItem->_instruction == CSYNC_INSTRUCTION_NEW) {
+ // Also restore everything
+ item->_instruction = CSYNC_INSTRUCTION_NEW;
+ item->_direction = SyncFileItem::Down;
+ item->_isRestoration = true;
+ item->_errorString = tr("Not allowed to remove, restoring");
+ } else if (!dbEntry._serverHasIgnoredFiles) {
item->_instruction = CSYNC_INSTRUCTION_REMOVE;
item->_direction = SyncFileItem::Up;
}
if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_SYNC)
item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
+
bool recurse = item->isDirectory() || localEntry.isDirectory || serverEntry.isDirectory;
- if (_queryLocal != NormalQuery && _queryServer != NormalQuery)
+ if (!checkPremission(item))
+ recurse = false;
+ if (_queryLocal != NormalQuery && _queryServer != NormalQuery && !item->_isRestoration)
recurse = false;
if (recurse) {
auto job = new ProcessDirectoryJob(item, recurseQueryServer,
}
}
+bool ProcessDirectoryJob::checkPremission(const OCC::SyncFileItemPtr &item)
+{
+ if (item->_direction != SyncFileItem::Up) {
+ // Currently we only check server-side permissions
+ return true;
+ }
+
+ switch (item->_instruction) {
+ case CSYNC_INSTRUCTION_TYPE_CHANGE:
+ case CSYNC_INSTRUCTION_NEW: {
+ const auto perms = !_rootPermissions.isNull() ? _rootPermissions
+ : _dirItem ? _dirItem->_remotePerm : _rootPermissions;
+ if (perms.isNull()) {
+ // No permissions set
+ return true;
+ } else if (item->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddSubDirectories)) {
+ qCWarning(lcDisco) << "checkForPermission: ERROR" << item->_file;
+ item->_instruction = CSYNC_INSTRUCTION_ERROR;
+ item->_status = SyncFileItem::NormalError;
+ item->_errorString = tr("Not allowed because you don't have permission to add subfolders to that folder");
+ return false;
+ } else if (!item->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddFile)) {
+ qCWarning(lcDisco) << "checkForPermission: ERROR" << item->_file;
+ item->_instruction = CSYNC_INSTRUCTION_ERROR;
+ item->_status = SyncFileItem::NormalError;
+ item->_errorString = tr("Not allowed because you don't have permission to add files in that folder");
+ return false;
+ }
+ break;
+ }
+ case CSYNC_INSTRUCTION_SYNC: {
+ const auto perms = item->_remotePerm;
+ if (perms.isNull()) {
+ // No permissions set
+ return true;
+ }
+ if (!perms.hasPermission(RemotePermissions::CanWrite)) {
+ qCWarning(lcDisco) << "checkForPermission: RESTORING" << item->_file;
+ item->_instruction = CSYNC_INSTRUCTION_CONFLICT;
+ item->_errorString = tr("Not allowed to upload this file because it is read-only on the server, restoring");
+ item->_direction = SyncFileItem::Down;
+ item->_isRestoration = true;
+ // Take the things to write to the db from the "other" node (i.e: info from server).
+ // Do a lookup into the csync remote tree to get the metadata we need to restore.
+ qSwap(item->_size, item->_previousSize);
+ qSwap(item->_modtime, item->_previousModtime);
+ return false;
+ }
+ break;
+ }
+ case CSYNC_INSTRUCTION_REMOVE: {
+ const auto perms = item->_remotePerm;
+ if (perms.isNull()) {
+ // No permissions set
+ return true;
+ }
+ if (!perms.hasPermission(RemotePermissions::CanDelete)) {
+ qCWarning(lcDisco) << "checkForPermission: RESTORING" << item->_file;
+ item->_instruction = CSYNC_INSTRUCTION_NEW;
+ item->_direction = SyncFileItem::Down;
+ item->_isRestoration = true;
+ item->_errorString = tr("Not allowed to remove, restoring");
+ return true; // (we need to recurse to restore sub items)
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+}
void ProcessDirectoryJob::subJobFinished()
{
// return true if the file is excluded
bool handleExcluded(const QString &path, bool isDirectory, bool isHidden);
void processFile(PathTuple, const LocalInfo &, const RemoteInfo &, const SyncJournalFileRecord &dbEntry);
+ // Return false if there is an error and that a directory must not be recursively be taken
+ bool checkPremission(const SyncFileItemPtr &item);
void processBlacklisted(const PathTuple &, const LocalInfo &, const SyncJournalFileRecord &dbEntry);
void subJobFinished();
void progress();
QVector<RemoteInfo> _serverEntries;
QVector<LocalInfo> _localEntries;
+ RemotePermissions _rootPermissions;
bool _hasServerEntries = false;
bool _hasLocalEntries = false;
int _pendingAsyncJobs = 0;
|| instruction == CSYNC_INSTRUCTION_TYPE_CHANGE;
}
-static bool isFileModifyingInstruction(csync_instructions_e instruction)
-{
- return isFileTransferInstruction(instruction)
- || instruction == CSYNC_INSTRUCTION_RENAME
- || instruction == CSYNC_INSTRUCTION_REMOVE;
-}
-
void SyncEngine::deleteStaleDownloadInfos(const SyncFileItemVector &syncItems)
{
// Find all downloadinfo paths that we want to preserve.
}
-/**
- *
- * Make sure that we are allowed to do what we do by checking the permissions and the selective sync list
- *
- */
-void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
-{
- bool selectiveListOk = false;
- auto selectiveSyncBlackList = _journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &selectiveListOk);
- std::sort(selectiveSyncBlackList.begin(), selectiveSyncBlackList.end());
- SyncFileItemPtr needle;
-
- for (SyncFileItemVector::iterator it = syncItems.begin(); it != syncItems.end(); ++it) {
- if ((*it)->_direction != SyncFileItem::Up
- || !isFileModifyingInstruction((*it)->_instruction)) {
- // Currently we only check server-side permissions
- continue;
- }
-
- // Do not propagate anything in the server if it is in the selective sync blacklist
- const QString path = (*it)->destination() + QLatin1Char('/');
-
- switch ((*it)->_instruction) {
- case CSYNC_INSTRUCTION_TYPE_CHANGE:
- case CSYNC_INSTRUCTION_NEW: {
- int slashPos = (*it)->_file.lastIndexOf('/');
- QString parentDir = slashPos <= 0 ? "" : (*it)->_file.mid(0, slashPos);
- const auto perms = getPermissions(parentDir);
- if (perms.isNull()) {
- // No permissions set
- break;
- } else if ((*it)->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddSubDirectories)) {
- qCWarning(lcEngine) << "checkForPermission: ERROR" << (*it)->_file;
- (*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
- (*it)->_status = SyncFileItem::NormalError;
- (*it)->_errorString = tr("Not allowed because you don't have permission to add subfolders to that folder");
-
- for (SyncFileItemVector::iterator it_next = it + 1; it_next != syncItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
- it = it_next;
- if ((*it)->_instruction == CSYNC_INSTRUCTION_RENAME) {
- // The file was most likely moved in this directory.
- // If the file was read only or could not be moved or removed, it should
- // be restored. Do that in the next sync by not considering as a rename
- // but delete and upload. It will then be restored if needed.
- _journal->avoidRenamesOnNextSync((*it)->_file);
- _anotherSyncNeeded = ImmediateFollowUp;
- qCWarning(lcEngine) << "Moving of " << (*it)->_file << " canceled because no permission to add parent folder";
- }
- (*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
- (*it)->_status = SyncFileItem::SoftError;
- (*it)->_errorString = tr("Not allowed because you don't have permission to add parent folder");
- }
-
- } else if (!(*it)->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddFile)) {
- qCWarning(lcEngine) << "checkForPermission: ERROR" << (*it)->_file;
- (*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
- (*it)->_status = SyncFileItem::NormalError;
- (*it)->_errorString = tr("Not allowed because you don't have permission to add files in that folder");
- }
- break;
- }
- case CSYNC_INSTRUCTION_SYNC: {
- const auto perms = getPermissions((*it)->_file);
- if (perms.isNull()) {
- // No permissions set
- break;
- }
- if (!perms.hasPermission(RemotePermissions::CanWrite)) {
- qCWarning(lcEngine) << "checkForPermission: RESTORING" << (*it)->_file;
- (*it)->_instruction = CSYNC_INSTRUCTION_CONFLICT;
- (*it)->_direction = SyncFileItem::Down;
- (*it)->_isRestoration = true;
- /*// Take the things to write to the db from the "other" node (i.e: info from server).
- // Do a lookup into the csync remote tree to get the metadata we need to restore.
- auto csyncIt = _csync_ctx->remote.files.find((*it)->_file.toUtf8());
- if (csyncIt != _csync_ctx->remote.files.end()) {
- (*it)->_modtime = csyncIt->second->modtime;
- (*it)->_size = csyncIt->second->size;
- (*it)->_fileId = csyncIt->second->file_id;
- (*it)->_etag = csyncIt->second->etag;
- }*/
- (*it)->_errorString = tr("Not allowed to upload this file because it is read-only on the server, restoring");
- continue;
- }
- break;
- }
- case CSYNC_INSTRUCTION_REMOVE: {
- const auto perms = getPermissions((*it)->_file);
- if (perms.isNull()) {
- // No permissions set
- break;
- }
- if (!perms.hasPermission(RemotePermissions::CanDelete)) {
- qCWarning(lcEngine) << "checkForPermission: RESTORING" << (*it)->_file;
- (*it)->_instruction = CSYNC_INSTRUCTION_NEW;
- (*it)->_direction = SyncFileItem::Down;
- (*it)->_isRestoration = true;
- (*it)->_errorString = tr("Not allowed to remove, restoring");
-
- if ((*it)->isDirectory()) {
- // restore all sub items
- for (SyncFileItemVector::iterator it_next = it + 1;
- it_next != syncItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
- it = it_next;
-
- if ((*it)->_instruction != CSYNC_INSTRUCTION_REMOVE) {
- qCWarning(lcEngine) << "non-removed job within a removed folder"
- << (*it)->_file << (*it)->_instruction;
- continue;
- }
-
- qCWarning(lcEngine) << "checkForPermission: RESTORING" << (*it)->_file;
-
- (*it)->_instruction = CSYNC_INSTRUCTION_NEW;
- (*it)->_direction = SyncFileItem::Down;
- (*it)->_isRestoration = true;
- (*it)->_errorString = tr("Not allowed to remove, restoring");
- }
- }
- } else if (perms.hasPermission(RemotePermissions::IsShared)
- && perms.hasPermission(RemotePermissions::CanDelete)) {
- // this is a top level shared dir which can be removed to unshare it,
- // regardless if it is a read only share or not.
- // To avoid that we try to restore files underneath this dir which have
- // not delete permission we fast forward the iterator and leave the
- // delete jobs intact. It is not physically tried to remove this files
- // underneath, propagator sees that.
- if ((*it)->isDirectory()) {
- // put a more descriptive message if a top level share dir really is removed.
- if (it == syncItems.begin() || !(path.startsWith((*(it - 1))->_file))) {
- (*it)->_errorString = tr("Local files and share folder removed.");
- }
-
- for (SyncFileItemVector::iterator it_next = it + 1;
- it_next != syncItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
- it = it_next;
- }
- }
- }
- break;
- }
-
- case CSYNC_INSTRUCTION_RENAME: {
- int slashPos = (*it)->_renameTarget.lastIndexOf('/');
- const QString parentDir = slashPos <= 0 ? "" : (*it)->_renameTarget.mid(0, slashPos);
- const auto destPerms = getPermissions(parentDir);
- const auto filePerms = getPermissions((*it)->_file);
-
- //true when it is just a rename in the same directory. (not a move)
- const bool isRename = (*it)->_file.startsWith(parentDir) && (*it)->_file.lastIndexOf('/') == slashPos;
-
- const bool isForbiddenSubDirectoryCreation = (*it)->isDirectory() && !destPerms.hasPermission(RemotePermissions::CanAddSubDirectories);
- const bool isForbiddenFileCreation = !(*it)->isDirectory() && !destPerms.hasPermission(RemotePermissions::CanAddFile);
-
- // Check if we are allowed to move to the destination.
- bool destinationOK = true;
- if (isRename || destPerms.isNull()) {
- // no need to check for the destination dir permission
- destinationOK = true;
- } else if (isForbiddenSubDirectoryCreation || isForbiddenFileCreation) {
- destinationOK = false;
- }
-
- // check if we are allowed to move from the source
- bool sourceOK = true;
- if (!filePerms.isNull()
- && ((isRename && !filePerms.hasPermission(RemotePermissions::CanRename))
- || (!isRename && !filePerms.hasPermission(RemotePermissions::CanMove)))) {
- // We are not allowed to move or rename this file
- sourceOK = false;
-
- if (filePerms.hasPermission(RemotePermissions::CanDelete) && destinationOK) {
- // but we are allowed to delete it
- // TODO! simulate delete & upload
- }
- }
-
-#ifdef OWNCLOUD_RESTORE_RENAME /* We don't like the idea of renaming behind user's back, as the user may be working with the files */
- if (!sourceOK && (!destinationOK || isRename)
- // (not for directory because that's more complicated with the contents that needs to be adjusted)
- && !(*it)->isDirectory()) {
- // Both the source and the destination won't allow move. Move back to the original
- std::swap((*it)->_file, (*it)->_renameTarget);
- (*it)->_direction = SyncFileItem::Down;
- (*it)->_errorString = tr("Move not allowed, item restored");
- (*it)->_isRestoration = true;
- qCWarning(lcEngine) << "checkForPermission: MOVING BACK" << (*it)->_file;
- // in case something does wrong, we will not do it next time
- _journal->avoidRenamesOnNextSync((*it)->_file);
- } else
-#endif
- if (!sourceOK || !destinationOK) {
- // One of them is not possible, just throw an error
- (*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
- (*it)->_status = SyncFileItem::NormalError;
- const QString errorString = tr("Move not allowed because %1 is read-only").arg(sourceOK ? tr("the destination") : tr("the source"));
- (*it)->_errorString = errorString;
-
- qCWarning(lcEngine) << "checkForPermission: ERROR MOVING" << (*it)->_file << errorString;
-
- // Avoid a rename on next sync:
- // TODO: do the resolution now already so we don't need two sync
- // At this point we would need to go back to the propagate phase on both remote to take
- // the decision.
- _journal->avoidRenamesOnNextSync((*it)->_file);
- _anotherSyncNeeded = ImmediateFollowUp;
-
-
- if ((*it)->isDirectory()) {
- for (SyncFileItemVector::iterator it_next = it + 1;
- it_next != syncItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
- it = it_next;
- (*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
- (*it)->_status = SyncFileItem::NormalError;
- (*it)->_errorString = errorString;
- qCWarning(lcEngine) << "checkForPermission: ERROR MOVING" << (*it)->_file;
- }
- }
- }
- break;
- }
- default:
- break;
- }
- }
-}
-
-RemotePermissions SyncEngine::getPermissions(const QString &file) const
-{
- qFatal("FIXME");
- return RemotePermissions();
-}
-
void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems)
{
/* When the server is trying to send us lots of file in the past, this means that a backup
//new directory should be uploaded
fakeFolder.localModifier().rename("readonlyDirectory_PERM_M_/subdir_PERM_CK_", "normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_");
applyPermissionsFromName(fakeFolder.remoteModifier());
- fakeFolder.syncOnce();
- if (fakeFolder.syncEngine().isAnotherSyncNeeded() == ImmediateFollowUp) {
- QVERIFY(fakeFolder.syncOnce());
- }
- assertCsyncJournalOk(fakeFolder.syncJournal());
+ QVERIFY(fakeFolder.syncOnce());
currentLocalState = fakeFolder.currentLocalState();
// old name restored
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
//######################################################################
qInfo( "rename a directory in a read only folder and move a directory to a read-only" );
// do a sync to update the database
applyPermissionsFromName(fakeFolder.remoteModifier());
QVERIFY(fakeFolder.syncOnce());
+ assertCsyncJournalOk(fakeFolder.syncJournal());
//1. rename a directory in a read only folder
//Missing directory should be restored
// error: can't upload to readonly!
QVERIFY(!fakeFolder.syncOnce());
- if (fakeFolder.syncEngine().isAnotherSyncNeeded() == ImmediateFollowUp) {
- QVERIFY(!fakeFolder.syncOnce());
- }
- assertCsyncJournalOk(fakeFolder.syncJournal());
currentLocalState = fakeFolder.currentLocalState();
//1.