1
0
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:
Christian Kamm 2018-01-26 13:14:54 +01:00 committed by Kevin Ottens
parent d6078f958b
commit 12d6f680f2
No known key found for this signature in database
GPG Key ID: 074BBBCB8DECC9E2
4 changed files with 181 additions and 0 deletions

View File

@ -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,

View File

@ -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;
};

View File

@ -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);

View File

@ -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);