mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2025-04-09 19:15:43 +02:00
Placeholders: Ignore placeholder files in older clients
To do this, we add the placeholder extension to the user exclude file automatically. However, newer clients shouldn't use that exclude pattern: so we also add version directives that allow making exclude patterns dependent on the client version.
This commit is contained in:
parent
d6078f958b
commit
12d6f680f2
src
test/csync/csync_tests
@ -33,9 +33,12 @@
|
||||
#include "csync_misc.h"
|
||||
|
||||
#include "common/utility.h"
|
||||
#include "../version.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
|
||||
/** Expands C-like escape sequences (in place)
|
||||
@ -246,6 +249,7 @@ using namespace OCC;
|
||||
|
||||
ExcludedFiles::ExcludedFiles(QString localPath)
|
||||
: _localPath(std::move(localPath))
|
||||
, _clientVersion(MIRALL_VERSION_MAJOR, MIRALL_VERSION_MINOR, MIRALL_VERSION_PATCH)
|
||||
{
|
||||
Q_ASSERT(_localPath.endsWith("/"));
|
||||
// Windows used to use PathMatchSpec which allows *foo to match abc/deffoo.
|
||||
@ -311,6 +315,34 @@ void ExcludedFiles::setWildcardsMatchSlash(bool onoff)
|
||||
prepare();
|
||||
}
|
||||
|
||||
void ExcludedFiles::setClientVersion(ExcludedFiles::Version version)
|
||||
{
|
||||
_clientVersion = version;
|
||||
}
|
||||
|
||||
void ExcludedFiles::setupPlaceholderExclude(
|
||||
const QString &excludeFile, const QByteArray &placeholderExtension)
|
||||
{
|
||||
if (!QFile::exists(excludeFile)) {
|
||||
// Ensure the parent paths exist
|
||||
QDir().mkpath(QFileInfo(excludeFile).dir().absolutePath());
|
||||
} else {
|
||||
// Does the exclude file contain the exclude already?
|
||||
QFile file(excludeFile);
|
||||
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
auto data = file.readAll();
|
||||
file.close();
|
||||
if (data.contains("\n*" + placeholderExtension + "\n"))
|
||||
return;
|
||||
}
|
||||
|
||||
// Add it to the file
|
||||
QFile file(excludeFile);
|
||||
file.open(QIODevice::ReadWrite | QIODevice::Append);
|
||||
file.write("\n#!version < 2.5.0\n*" + placeholderExtension + "\n");
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool ExcludedFiles::loadExcludeFile(const QByteArray & basePath, const QString & file)
|
||||
{
|
||||
QFile f(file);
|
||||
@ -320,6 +352,10 @@ bool ExcludedFiles::loadExcludeFile(const QByteArray & basePath, const QString &
|
||||
QList<QByteArray> patterns;
|
||||
while (!f.atEnd()) {
|
||||
QByteArray line = f.readLine().trimmed();
|
||||
if (line.startsWith("#!version")) {
|
||||
if (!versionDirectiveKeepNextLine(line))
|
||||
f.readLine();
|
||||
}
|
||||
if (line.isEmpty() || line.startsWith('#'))
|
||||
continue;
|
||||
csync_exclude_expand_escapes(line);
|
||||
@ -363,6 +399,32 @@ bool ExcludedFiles::reloadExcludeFiles()
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ExcludedFiles::versionDirectiveKeepNextLine(const QByteArray &directive) const
|
||||
{
|
||||
if (!directive.startsWith("#!version"))
|
||||
return true;
|
||||
QByteArrayList args = directive.split(' ');
|
||||
if (args.size() != 3)
|
||||
return true;
|
||||
QByteArray op = args[1];
|
||||
QByteArrayList argVersions = args[2].split('.');
|
||||
if (argVersions.size() != 3)
|
||||
return true;
|
||||
|
||||
auto argVersion = std::make_tuple(argVersions[0].toInt(), argVersions[1].toInt(), argVersions[2].toInt());
|
||||
if (op == "<=")
|
||||
return _clientVersion <= argVersion;
|
||||
if (op == "<")
|
||||
return _clientVersion < argVersion;
|
||||
if (op == ">")
|
||||
return _clientVersion > argVersion;
|
||||
if (op == ">=")
|
||||
return _clientVersion >= argVersion;
|
||||
if (op == "==")
|
||||
return _clientVersion == argVersion;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExcludedFiles::isExcluded(
|
||||
const QString &filePath,
|
||||
const QString &basePath,
|
||||
|
@ -65,6 +65,8 @@ class OCSYNC_EXPORT ExcludedFiles : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef std::tuple<int, int, int> Version;
|
||||
|
||||
ExcludedFiles(QString localPath = "/");
|
||||
~ExcludedFiles();
|
||||
|
||||
@ -115,6 +117,11 @@ public:
|
||||
*/
|
||||
void setWildcardsMatchSlash(bool onoff);
|
||||
|
||||
/**
|
||||
* Sets the client version, only used for testing.
|
||||
*/
|
||||
void setClientVersion(Version version);
|
||||
|
||||
/**
|
||||
* Generate a hook for traversal exclude pattern matching
|
||||
* that csync can use.
|
||||
@ -125,6 +132,12 @@ public:
|
||||
auto csyncTraversalMatchFun()
|
||||
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>;
|
||||
|
||||
/**
|
||||
* Adds the exclude that skips placeholder files in older versions
|
||||
* to the user exclude file.
|
||||
*/
|
||||
static void setupPlaceholderExclude(const QString &excludeFile, const QByteArray &placeholderExtension);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Reloads the exclude patterns from the registered paths.
|
||||
@ -136,6 +149,23 @@ public slots:
|
||||
bool loadExcludeFile(const QByteArray & basePath, const QString & file);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns true if the version directive indicates the next line
|
||||
* should be skipped.
|
||||
*
|
||||
* A version directive has the form "#!version <op> <version>"
|
||||
* where <op> can be <, <=, ==, >, >= and <version> can be any version
|
||||
* like 2.5.0.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #!version < 2.5.0
|
||||
* myexclude
|
||||
*
|
||||
* Would enable the "myexclude" pattern only for versions before 2.5.0.
|
||||
*/
|
||||
bool versionDirectiveKeepNextLine(const QByteArray &directive) const;
|
||||
|
||||
/**
|
||||
* @brief Match the exclude pattern against the full path.
|
||||
*
|
||||
@ -247,6 +277,12 @@ private:
|
||||
*/
|
||||
bool _wildcardsMatchSlash = false;
|
||||
|
||||
/**
|
||||
* The client version. Used to evaluate version-dependent excludes,
|
||||
* see versionDirectiveKeepNextLine().
|
||||
*/
|
||||
Version _clientVersion;
|
||||
|
||||
friend class ExcludedFilesTest;
|
||||
};
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
#include "owncloudsetupwizard.h"
|
||||
#include "version.h"
|
||||
#include "csync_exclude.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@ -191,6 +192,9 @@ Application::Application(int &argc, char **argv)
|
||||
if (!AbstractNetworkJob::httpTimeout)
|
||||
AbstractNetworkJob::httpTimeout = cfg.timeout();
|
||||
|
||||
ExcludedFiles::setupPlaceholderExclude(
|
||||
cfg.excludeFile(ConfigFile::UserScope), OWNCLOUD_PLACEHOLDER_SUFFIX);
|
||||
|
||||
_folderManager.reset(new FolderMan);
|
||||
|
||||
connect(this, &SharedTools::QtSingleApplication::messageReceived, this, &Application::slotParseMessage);
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <sys/time.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#define CSYNC_TEST 1
|
||||
#include "csync_exclude.cpp"
|
||||
|
||||
@ -694,6 +696,81 @@ static void check_csync_exclude_expand_escapes(void **state)
|
||||
assert_true(0 == strcmp(line.constData(), "\\"));
|
||||
}
|
||||
|
||||
static void check_placeholder_exclude(void **state)
|
||||
{
|
||||
(void)state;
|
||||
|
||||
auto readFile = [](const QString &file) {
|
||||
QFile f(file);
|
||||
f.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
return f.readAll();
|
||||
};
|
||||
|
||||
QTemporaryDir tempDir;
|
||||
QString path;
|
||||
QByteArray expected = "\n#!version < 2.5.0\n*.owncloud\n";
|
||||
|
||||
// Case 1: No file exists yet, parent dirs are missing too
|
||||
path = tempDir.filePath("foo/bar/exclude.lst");
|
||||
ExcludedFiles::setupPlaceholderExclude(path, ".owncloud");
|
||||
|
||||
assert_true(QFile::exists(path));
|
||||
assert_true(readFile(path) == expected);
|
||||
|
||||
// Case 2: Running it again
|
||||
ExcludedFiles::setupPlaceholderExclude(path, ".owncloud");
|
||||
assert_true(readFile(path) == expected);
|
||||
|
||||
// Case 3: File exists, has some data
|
||||
{
|
||||
QFile f(path);
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
f.write("# bla\nmyexclude\n\nanotherexclude");
|
||||
f.close();
|
||||
}
|
||||
ExcludedFiles::setupPlaceholderExclude(path, ".owncloud");
|
||||
assert_true(readFile(path) == "# bla\nmyexclude\n\nanotherexclude" + expected);
|
||||
|
||||
// Case 4: Running it again still does nothing
|
||||
ExcludedFiles::setupPlaceholderExclude(path, ".owncloud");
|
||||
assert_true(readFile(path) == "# bla\nmyexclude\n\nanotherexclude" + expected);
|
||||
|
||||
// Case 5: Verify that reading this file doesn't actually include the exclude
|
||||
ExcludedFiles excludes;
|
||||
excludes.addExcludeFilePath(path);
|
||||
excludes.reloadExcludeFiles();
|
||||
assert_false(excludes._allExcludes.value("/").contains("*.owncloud"));
|
||||
assert_true(excludes._allExcludes.value("/").contains("myexclude"));
|
||||
}
|
||||
|
||||
static void check_version_directive(void **state)
|
||||
{
|
||||
(void)state;
|
||||
|
||||
ExcludedFiles excludes;
|
||||
excludes.setClientVersion(ExcludedFiles::Version(2, 5, 0));
|
||||
|
||||
std::vector<std::pair<const char *, bool>> tests = {
|
||||
{ "#!version == 2.5.0", true },
|
||||
{ "#!version == 2.6.0", false },
|
||||
{ "#!version < 2.6.0", true },
|
||||
{ "#!version <= 2.6.0", true },
|
||||
{ "#!version > 2.6.0", false },
|
||||
{ "#!version >= 2.6.0", false },
|
||||
{ "#!version < 2.4.0", false },
|
||||
{ "#!version <= 2.4.0", false },
|
||||
{ "#!version > 2.4.0", true },
|
||||
{ "#!version >= 2.4.0", true },
|
||||
{ "#!version < 2.5.0", false },
|
||||
{ "#!version <= 2.5.0", true },
|
||||
{ "#!version > 2.5.0", false },
|
||||
{ "#!version >= 2.5.0", true },
|
||||
};
|
||||
for (auto test : tests) {
|
||||
assert_true(excludes.versionDirectiveKeepNextLine(test.first) == test.second);
|
||||
}
|
||||
}
|
||||
|
||||
}; // class ExcludedFilesTest
|
||||
|
||||
int torture_run_tests(void)
|
||||
@ -715,6 +792,8 @@ int torture_run_tests(void)
|
||||
cmocka_unit_test_setup_teardown(T::check_csync_is_windows_reserved_word, T::setup_init, T::teardown),
|
||||
cmocka_unit_test_setup_teardown(T::check_csync_excluded_performance, T::setup_init, T::teardown),
|
||||
cmocka_unit_test(T::check_csync_exclude_expand_escapes),
|
||||
cmocka_unit_test(T::check_placeholder_exclude),
|
||||
cmocka_unit_test(T::check_version_directive),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, nullptr, nullptr);
|
||||
|
Loading…
Reference in New Issue
Block a user