1/*
2 * libcsync -- a library to sync a directory with another
3 *
4 * Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifndef _CSYNC_EXCLUDE_H
22#define _CSYNC_EXCLUDE_H
23
24#include "ocsynclib.h"
25
26#include "csync.h"
27
28#include <QObject>
29#include <QSet>
30#include <QString>
31#include <QRegularExpression>
32
33#include <functional>
34
35enum csync_exclude_type_e {
36 CSYNC_NOT_EXCLUDED = 0,
37 CSYNC_FILE_SILENTLY_EXCLUDED,
38 CSYNC_FILE_EXCLUDE_AND_REMOVE,
39 CSYNC_FILE_EXCLUDE_LIST,
40 CSYNC_FILE_EXCLUDE_INVALID_CHAR,
41 CSYNC_FILE_EXCLUDE_TRAILING_SPACE,
42 CSYNC_FILE_EXCLUDE_LONG_FILENAME,
43 CSYNC_FILE_EXCLUDE_HIDDEN,
44 CSYNC_FILE_EXCLUDE_STAT_FAILED,
45 CSYNC_FILE_EXCLUDE_CONFLICT,
46 CSYNC_FILE_EXCLUDE_CANNOT_ENCODE
47};
48typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;
49
50class ExcludedFilesTest;
51
52/**
53 * Manages file/directory exclusion.
54 *
55 * Most commonly exclude patterns are loaded from files. See
56 * addExcludeFilePath() and reloadExcludeFiles().
57 *
58 * Excluded files are primarily relevant for sync runs, and for
59 * file watcher filtering.
60 *
61 * Excluded files and ignored files are the same thing. But the
62 * selective sync blacklist functionality is a different thing
63 * entirely.
64 */
65class OCSYNC_EXPORT ExcludedFiles : public QObject
66{
67 Q_OBJECT
68public:
69 typedef std::tuple<int, int, int> Version;
70
71 ExcludedFiles();
72 ~ExcludedFiles();
73
74 /**
75 * Adds a new path to a file containing exclude patterns.
76 *
77 * Does not load the file. Use reloadExcludeFiles() afterwards.
78 */
79 void addExcludeFilePath(const QString &path);
80
81 /**
82 * Whether conflict files shall be excluded.
83 *
84 * Defaults to true.
85 */
86 void setExcludeConflictFiles(bool onoff);
87
88 /**
89 * Checks whether a file or directory should be excluded.
90 *
91 * @param filePath the absolute path to the file
92 * @param basePath folder path from which to apply exclude rules, ends with a /
93 */
94 bool isExcluded(
95 const QString &filePath,
96 const QString &basePath,
97 bool excludeHidden) const;
98
99 /**
100 * Adds an exclude pattern.
101 *
102 * Primarily used in tests. Patterns added this way are preserved when
103 * reloadExcludeFiles() is called.
104 */
105 void addManualExclude(const QByteArray &expr);
106
107 /**
108 * Removes all manually added exclude patterns.
109 *
110 * Primarily used in tests.
111 */
112 void clearManualExcludes();
113
114 /**
115 * Adjusts behavior of wildcards. Only used for testing.
116 */
117 void setWildcardsMatchSlash(bool onoff);
118
119 /**
120 * Sets the client version, only used for testing.
121 */
122 void setClientVersion(Version version);
123
124 /**
125 * Generate a hook for traversal exclude pattern matching
126 * that csync can use.
127 *
128 * Careful: The function will only be valid for as long as this
129 * ExcludedFiles instance stays alive.
130 */
131 auto csyncTraversalMatchFun() const
132 -> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>;
133
134public slots:
135 /**
136 * Reloads the exclude patterns from the registered paths.
137 */
138 bool reloadExcludeFiles();
139
140private:
141 /**
142 * Returns true if the version directive indicates the next line
143 * should be skipped.
144 *
145 * A version directive has the form "#!version <op> <version>"
146 * where <op> can be <, <=, ==, >, >= and <version> can be any version
147 * like 2.5.0.
148 *
149 * Example:
150 *
151 * #!version < 2.5.0
152 * myexclude
153 *
154 * Would enable the "myexclude" pattern only for versions before 2.5.0.
155 */
156 bool versionDirectiveKeepNextLine(const QByteArray &directive) const;
157
158 /**
159 * @brief Match the exclude pattern against the full path.
160 *
161 * @param Path is folder-relative, should not start with a /.
162 *
163 * Note that this only matches patterns. It does not check whether the file
164 * or directory pointed to is hidden (or whether it even exists).
165 */
166 CSYNC_EXCLUDE_TYPE fullPatternMatch(const char *path, ItemType filetype) const;
167
168 /**
169 * @brief Check if the given path should be excluded in a traversal situation.
170 *
171 * It does only part of the work that full() does because it's assumed
172 * that all leading directories have been run through traversal()
173 * before. This can be significantly faster.
174 *
175 * That means for 'foo/bar/file' only ('foo/bar/file', 'file') is checked
176 * against the exclude patterns.
177 *
178 * @param Path is folder-relative, should not start with a /.
179 *
180 * Note that this only matches patterns. It does not check whether the file
181 * or directory pointed to is hidden (or whether it even exists).
182 */
183 CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, ItemType filetype) const;
184
185 /**
186 * Generate optimized regular expressions for the exclude patterns.
187 *
188 * The optimization works in two steps: First, all supported patterns are put
189 * into _fullRegexFile/_fullRegexDir. These regexes can be applied to the full
190 * path to determine whether it is excluded or not.
191 *
192 * The second is a performance optimization. The particularly common use
193 * case for excludes during a sync run is "traversal": Instead of checking
194 * the full path every time, we check each parent path with the traversal
195 * function incrementally.
196 *
197 * Example: When the sync run eventually arrives at "a/b/c it can assume
198 * that the traversal matching has already been run on "a", "a/b"
199 * and just needs to run the traversal matcher on "a/b/c".
200 *
201 * The full matcher is equivalent to or-combining the traversal match results
202 * of all parent paths:
203 * full("a/b/c/d") == traversal("a") || traversal("a/b") || traversal("a/b/c")
204 *
205 * The traversal matcher can be extremely fast because it has a fast early-out
206 * case: It checks the bname part of the path against _bnameTraversalRegex
207 * and only runs a simplified _fullTraversalRegex on the whole path if bname
208 * activation for it was triggered.
209 *
210 * Note: The traversal matcher will return not-excluded on some paths that the
211 * full matcher would exclude. Example: "b" is excluded. traversal("b/c")
212 * returns not-excluded because "c" isn't a bname activation pattern.
213 */
214 void prepare();
215
216 /// Files to load excludes from
217 QSet<QString> _excludeFiles;
218
219 /// Exclude patterns added with addManualExclude()
220 QList<QByteArray> _manualExcludes;
221
222 /// List of all active exclude patterns
223 QList<QByteArray> _allExcludes;
224
225 /// see prepare()
226 QRegularExpression _bnameTraversalRegexFile;
227 QRegularExpression _bnameTraversalRegexDir;
228 QRegularExpression _fullTraversalRegexFile;
229 QRegularExpression _fullTraversalRegexDir;
230 QRegularExpression _fullRegexFile;
231 QRegularExpression _fullRegexDir;
232
233 bool _excludeConflictFiles = true;
234
235 /**
236 * Whether * and ? in patterns can match a /
237 *
238 * Unfortunately this was how matching was done on Windows so
239 * it continues to be enabled there.
240 */
241 bool _wildcardsMatchSlash = false;
242
243 /**
244 * The client version. Used to evaluate version-dependent excludes,
245 * see versionDirectiveKeepNextLine().
246 */
247 Version _clientVersion;
248
249 friend class ExcludedFilesTest;
250};
251
252#endif /* _CSYNC_EXCLUDE_H */
253