diff --git a/src/csync/csync_exclude.h b/src/csync/csync_exclude.h index 0656371e792fe..d06c5e107d359 100644 --- a/src/csync/csync_exclude.h +++ b/src/csync/csync_exclude.h @@ -43,7 +43,8 @@ enum CSYNC_EXCLUDE_TYPE { CSYNC_FILE_EXCLUDE_HIDDEN, CSYNC_FILE_EXCLUDE_STAT_FAILED, CSYNC_FILE_EXCLUDE_CONFLICT, - CSYNC_FILE_EXCLUDE_CANNOT_ENCODE + CSYNC_FILE_EXCLUDE_CANNOT_ENCODE, + CSYNC_FILE_EXCLUDE_SERVER_BLACKLISTED, }; class ExcludedFilesTest; diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp index b6a689fcdafe7..1ea4a58dca39e 100644 --- a/src/libsync/capabilities.cpp +++ b/src/libsync/capabilities.cpp @@ -208,6 +208,11 @@ bool Capabilities::uploadConflictFiles() const return _capabilities[QStringLiteral("uploadConflictFiles")].toBool(); } +QStringList Capabilities::blacklistedFiles() const +{ + return _capabilities["files"].toMap()["blacklisted_files"].toStringList(); +} + /*-------------------------------------------------------------------------------------*/ // Direct Editing diff --git a/src/libsync/capabilities.h b/src/libsync/capabilities.h index dae0d586643a3..e64f68d0b71f0 100644 --- a/src/libsync/capabilities.h +++ b/src/libsync/capabilities.h @@ -126,6 +126,11 @@ class OWNCLOUDSYNC_EXPORT Capabilities */ QString invalidFilenameRegex() const; + /** + * return the list of filename that should not be uploaded + */ + QStringList blacklistedFiles() const; + /** * Whether conflict files should remain local (default) or should be uploaded. */ diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 64984f7ea1375..13f013efc5f5d 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -142,7 +142,9 @@ void ProcessDirectoryJob::process() // local stat function. // Recall file shall not be ignored (#4420) bool isHidden = e.localEntry.isHidden || (f.first[0] == '.' && f.first != QLatin1String(".sys.admin#recall#")); - if (handleExcluded(path._target, e.localEntry.isDirectory || e.serverEntry.isDirectory, isHidden, e.localEntry.isSymLink)) + if (handleExcluded(path._target, e.localEntry.name, + e.localEntry.isDirectory || e.serverEntry.isDirectory, isHidden, + e.localEntry.isSymLink)) continue; if (_queryServer == InBlackList || _discoveryData->isInSelectiveSyncBlackList(path._original)) { @@ -154,7 +156,7 @@ void ProcessDirectoryJob::process() QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs); } -bool ProcessDirectoryJob::handleExcluded(const QString &path, bool isDirectory, bool isHidden, bool isSymlink) +bool ProcessDirectoryJob::handleExcluded(const QString &path, const QString &localName, bool isDirectory, bool isHidden, bool isSymlink) { auto excluded = _discoveryData->_excludes->traversalPatternMatch(path, isDirectory ? ItemTypeDirectory : ItemTypeFile); @@ -169,6 +171,11 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, bool isDirectory, if (excluded == CSYNC_NOT_EXCLUDED && _discoveryData->_ignoreHiddenFiles && isHidden) { excluded = CSYNC_FILE_EXCLUDE_HIDDEN; } + if (excluded == CSYNC_NOT_EXCLUDED && !localName.isEmpty() + && _discoveryData->_serverBlacklistedFiles.contains(localName)) { + excluded = CSYNC_FILE_EXCLUDE_SERVER_BLACKLISTED; + isInvalidPattern = true; + } auto localCodec = QTextCodec::codecForLocale(); if (localCodec->mibEnum() != 106) { @@ -248,6 +255,9 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, bool isDirectory, case CSYNC_FILE_EXCLUDE_CANNOT_ENCODE: item->_errorString = tr("The filename cannot be encoded on your file system."); break; + case CSYNC_FILE_EXCLUDE_SERVER_BLACKLISTED: + item->_errorString = tr("The filename is blacklisted on the server."); + break; } } diff --git a/src/libsync/discovery.h b/src/libsync/discovery.h index 99d0013b12f5a..0ec44b9408454 100644 --- a/src/libsync/discovery.h +++ b/src/libsync/discovery.h @@ -116,8 +116,10 @@ class ProcessDirectoryJob : public QObject */ void process(); - // return true if the file is excluded - bool handleExcluded(const QString &path, bool isDirectory, bool isHidden, bool isSymlink); + // return true if the file is excluded. + // path is the full relative path of the file. localName is the base name of the local entry. + bool handleExcluded(const QString &path, const QString &localName, bool isDirectory, + bool isHidden, bool isSymlink); /** Reconcile local/remote/db information for a single item. * diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 6f6b4ffd6bb8d..738c2d2122b38 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -170,6 +170,7 @@ class DiscoveryPhase : public QObject QStringList _selectiveSyncWhiteList; ExcludedFiles *_excludes; QRegExp _invalidFilenameRx; // FIXME: maybe move in ExcludedFiles + QStringList _serverBlacklistedFiles; // The blacklist from the capabilities bool _ignoreHiddenFiles = false; std::function _shouldDiscoverLocaly; diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 418d494fcd1c4..ba1d0d6bae0e2 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -608,6 +608,7 @@ void SyncEngine::slotStartDiscovery() } if (!invalidFilenamePattern.isEmpty()) _discoveryPhase->_invalidFilenameRx = QRegExp(invalidFilenamePattern); + _discoveryPhase->_serverBlacklistedFiles = _account->capabilities().blacklistedFiles(); _discoveryPhase->_ignoreHiddenFiles = ignoreHiddenFiles(); connect(_discoveryPhase.data(), &DiscoveryPhase::itemDiscovered, this, &SyncEngine::slotItemDiscovered); diff --git a/test/testlocaldiscovery.cpp b/test/testlocaldiscovery.cpp index babc91bd8a8d3..d1831a56da0eb 100644 --- a/test/testlocaldiscovery.cpp +++ b/test/testlocaldiscovery.cpp @@ -185,7 +185,25 @@ private slots: QCOMPARE(fakeFolder.currentRemoteState(), expectedState); } + // Tests the behavior of invalid filename detection + void testServerBlacklist() + { + FakeFolder fakeFolder { FileInfo::A12_B12_C12_S12() }; + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + fakeFolder.syncEngine().account()->setCapabilities({ { "files", + QVariantMap { { "blacklisted_files", QVariantList { ".foo", "bar" } } } } }); + fakeFolder.localModifier().insert("C/.foo"); + fakeFolder.localModifier().insert("C/bar"); + fakeFolder.localModifier().insert("C/moo"); + fakeFolder.localModifier().insert("C/.moo"); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(fakeFolder.currentRemoteState().find("C/moo")); + QVERIFY(fakeFolder.currentRemoteState().find("C/.moo")); + QVERIFY(!fakeFolder.currentRemoteState().find("C/.foo")); + QVERIFY(!fakeFolder.currentRemoteState().find("C/bar")); + } }; QTEST_GUILESS_MAIN(TestLocalDiscovery)