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 | |
35 | enum 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 | }; |
48 | typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE; |
49 | |
50 | class 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 | */ |
65 | class OCSYNC_EXPORT ExcludedFiles : public QObject |
66 | { |
67 | Q_OBJECT |
68 | public: |
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 | |
134 | public slots: |
135 | /** |
136 | * Reloads the exclude patterns from the registered paths. |
137 | */ |
138 | bool reloadExcludeFiles(); |
139 | |
140 | private: |
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 | |