mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
New Discovery Algo: Permsission check
This commit is contained in:
parent
04da4365f9
commit
01f35bdbef
@ -123,16 +123,6 @@ if(OWNCLOUD_5XX_NO_BLACKLIST)
|
||||
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)
|
||||
|
||||
@ -35,7 +35,8 @@ void ProcessDirectoryJob::start()
|
||||
|
||||
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;
|
||||
@ -71,6 +72,8 @@ void ProcessDirectoryJob::start()
|
||||
emit finished();
|
||||
}
|
||||
});
|
||||
connect(serverJob, &DiscoverySingleDirectoryJob::firstDirectoryPermissions, this,
|
||||
[this](const RemotePermissions &perms) { _rootPermissions = perms; });
|
||||
serverJob->start();
|
||||
} else {
|
||||
_hasServerEntries = true;
|
||||
@ -546,7 +549,6 @@ void ProcessDirectoryJob::processFile(PathTuple path,
|
||||
}
|
||||
|
||||
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,
|
||||
@ -733,40 +735,78 @@ void ProcessDirectoryJob::processFile(PathTuple path,
|
||||
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())) {
|
||||
@ -791,10 +831,10 @@ void ProcessDirectoryJob::processFile(PathTuple path,
|
||||
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);
|
||||
@ -816,7 +856,8 @@ void ProcessDirectoryJob::processFile(PathTuple path,
|
||||
}
|
||||
|
||||
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);
|
||||
@ -875,7 +916,14 @@ void ProcessDirectoryJob::processFile(PathTuple path,
|
||||
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;
|
||||
}
|
||||
@ -895,8 +943,11 @@ void ProcessDirectoryJob::processFile(PathTuple path,
|
||||
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,
|
||||
@ -951,6 +1002,77 @@ void ProcessDirectoryJob::processBlacklisted(const PathTuple &path, const OCC::L
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
||||
@ -75,12 +75,15 @@ private:
|
||||
// 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;
|
||||
|
||||
@ -250,13 +250,6 @@ static bool isFileTransferInstruction(csync_instructions_e instruction)
|
||||
|| 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.
|
||||
@ -1057,239 +1050,6 @@ void SyncEngine::slotProgress(const SyncFileItem &item, quint64 current)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 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;
|
||||
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)
|
||||
bool isRename = (*it)->_file.startsWith(parentDir) && (*it)->_file.lastIndexOf('/') == slashPos;
|
||||
|
||||
|
||||
// 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 ((*it)->isDirectory() && !destPerms.hasPermission(RemotePermissions::CanAddSubDirectories)) {
|
||||
destinationOK = false;
|
||||
} else if (!(*it)->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 (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
|
||||
|
||||
@ -210,11 +210,7 @@ private slots:
|
||||
//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
|
||||
@ -226,12 +222,14 @@ private slots:
|
||||
|
||||
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
|
||||
@ -243,10 +241,6 @@ private slots:
|
||||
|
||||
// error: can't upload to readonly!
|
||||
QVERIFY(!fakeFolder.syncOnce());
|
||||
if (fakeFolder.syncEngine().isAnotherSyncNeeded() == ImmediateFollowUp) {
|
||||
QVERIFY(!fakeFolder.syncOnce());
|
||||
}
|
||||
assertCsyncJournalOk(fakeFolder.syncJournal());
|
||||
currentLocalState = fakeFolder.currentLocalState();
|
||||
|
||||
//1.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user