csync_exclude.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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. #ifndef _CSYNC_EXCLUDE_H
  21. #define _CSYNC_EXCLUDE_H
  22. #include "ocsynclib.h"
  23. #include "csync.h"
  24. #include <QObject>
  25. #include <QSet>
  26. #include <QString>
  27. #include <QRegularExpression>
  28. #include <functional>
  29. enum CSYNC_EXCLUDE_TYPE {
  30. CSYNC_NOT_EXCLUDED = 0,
  31. CSYNC_FILE_SILENTLY_EXCLUDED,
  32. CSYNC_FILE_EXCLUDE_AND_REMOVE,
  33. CSYNC_FILE_EXCLUDE_LIST,
  34. CSYNC_FILE_EXCLUDE_INVALID_CHAR,
  35. CSYNC_FILE_EXCLUDE_TRAILING_SPACE,
  36. CSYNC_FILE_EXCLUDE_LONG_FILENAME,
  37. CSYNC_FILE_EXCLUDE_HIDDEN,
  38. CSYNC_FILE_EXCLUDE_STAT_FAILED,
  39. CSYNC_FILE_EXCLUDE_CONFLICT,
  40. CSYNC_FILE_EXCLUDE_CASE_CLASH_CONFLICT,
  41. CSYNC_FILE_EXCLUDE_CANNOT_ENCODE,
  42. CSYNC_FILE_EXCLUDE_SERVER_BLACKLISTED,
  43. CSYNC_FILE_EXCLUDE_LEADING_SPACE,
  44. CSYNC_FILE_EXCLUDE_LEADING_AND_TRAILING_SPACE,
  45. };
  46. class ExcludedFilesTest;
  47. class QFile;
  48. /**
  49. * Manages file/directory exclusion.
  50. *
  51. * Most commonly exclude patterns are loaded from files. See
  52. * addExcludeFilePath() and reloadExcludeFiles().
  53. *
  54. * Excluded files are primarily relevant for sync runs, and for
  55. * file watcher filtering.
  56. *
  57. * Excluded files and ignored files are the same thing. But the
  58. * selective sync blacklist functionality is a different thing
  59. * entirely.
  60. */
  61. class OCSYNC_EXPORT ExcludedFiles : public QObject
  62. {
  63. Q_OBJECT
  64. public:
  65. using Version = std::tuple<int, int, int>;
  66. explicit ExcludedFiles(const QString &localPath = QStringLiteral("/"));
  67. ~ExcludedFiles() override;
  68. /**
  69. * Adds a new path to a file containing exclude patterns.
  70. *
  71. * Does not load the file. Use reloadExcludeFiles() afterwards.
  72. */
  73. void addExcludeFilePath(const QString &path);
  74. /**
  75. * Whether conflict files shall be excluded.
  76. *
  77. * Defaults to true.
  78. */
  79. void setExcludeConflictFiles(bool onoff);
  80. /**
  81. * Checks whether a file or directory should be excluded.
  82. *
  83. * @param filePath the absolute path to the file
  84. * @param basePath folder path from which to apply exclude rules, ends with a /
  85. */
  86. [[nodiscard]] bool isExcluded(
  87. const QString &filePath,
  88. const QString &basePath,
  89. bool excludeHidden) const;
  90. /**
  91. * Adds an exclude pattern anchored to base path
  92. *
  93. * Primarily used in tests. Patterns added this way are preserved when
  94. * reloadExcludeFiles() is called.
  95. */
  96. void addManualExclude(const QString &expr);
  97. void addManualExclude(const QString &expr, const QString &basePath);
  98. /**
  99. * Removes all manually added exclude patterns.
  100. *
  101. * Primarily used in tests.
  102. */
  103. void clearManualExcludes();
  104. /**
  105. * Adjusts behavior of wildcards. Only used for testing.
  106. */
  107. void setWildcardsMatchSlash(bool onoff);
  108. /**
  109. * Sets the client version, only used for testing.
  110. */
  111. void setClientVersion(Version version);
  112. /**
  113. * @brief Check if the given path should be excluded in a traversal situation.
  114. *
  115. * It does only part of the work that full() does because it's assumed
  116. * that all leading directories have been run through traversal()
  117. * before. This can be significantly faster.
  118. *
  119. * That means for 'foo/bar/file' only ('foo/bar/file', 'file') is checked
  120. * against the exclude patterns.
  121. *
  122. * @param Path is folder-relative, should not start with a /.
  123. *
  124. * Note that this only matches patterns. It does not check whether the file
  125. * or directory pointed to is hidden (or whether it even exists).
  126. */
  127. CSYNC_EXCLUDE_TYPE traversalPatternMatch(const QString &path, ItemType filetype);
  128. public slots:
  129. /**
  130. * Reloads the exclude patterns from the registered paths.
  131. */
  132. bool reloadExcludeFiles();
  133. /**
  134. * Loads the exclude patterns from file the registered base paths.
  135. */
  136. void loadExcludeFilePatterns(const QString &basePath, QFile &file);
  137. private:
  138. /**
  139. * Returns true if the version directive indicates the next line
  140. * should be skipped.
  141. *
  142. * A version directive has the form "#!version <op> <version>"
  143. * where <op> can be <, <=, ==, >, >= and <version> can be any version
  144. * like 2.5.0.
  145. *
  146. * Example:
  147. *
  148. * #!version < 2.5.0
  149. * myexclude
  150. *
  151. * Would enable the "myexclude" pattern only for versions before 2.5.0.
  152. */
  153. [[nodiscard]] bool versionDirectiveKeepNextLine(const QByteArray &directive) const;
  154. /**
  155. * @brief Match the exclude pattern against the full path.
  156. *
  157. * @param Path is folder-relative, should not start with a /.
  158. *
  159. * Note that this only matches patterns. It does not check whether the file
  160. * or directory pointed to is hidden (or whether it even exists).
  161. */
  162. [[nodiscard]] CSYNC_EXCLUDE_TYPE fullPatternMatch(const QString &path, ItemType filetype) const;
  163. // Our BasePath need to end with '/'
  164. class BasePathString : public QString
  165. {
  166. public:
  167. BasePathString(QString &&other)
  168. : QString(std::move(other))
  169. {
  170. Q_ASSERT(endsWith(QLatin1Char('/')));
  171. }
  172. BasePathString(const QString &other)
  173. : QString(other)
  174. {
  175. Q_ASSERT(endsWith(QLatin1Char('/')));
  176. }
  177. };
  178. /**
  179. * Generate optimized regular expressions for the exclude patterns anchored to basePath.
  180. *
  181. * The optimization works in two steps: First, all supported patterns are put
  182. * into _fullRegexFile/_fullRegexDir. These regexes can be applied to the full
  183. * path to determine whether it is excluded or not.
  184. *
  185. * The second is a performance optimization. The particularly common use
  186. * case for excludes during a sync run is "traversal": Instead of checking
  187. * the full path every time, we check each parent path with the traversal
  188. * function incrementally.
  189. *
  190. * Example: When the sync run eventually arrives at "a/b/c it can assume
  191. * that the traversal matching has already been run on "a", "a/b"
  192. * and just needs to run the traversal matcher on "a/b/c".
  193. *
  194. * The full matcher is equivalent to or-combining the traversal match results
  195. * of all parent paths:
  196. * full("a/b/c/d") == traversal("a") || traversal("a/b") || traversal("a/b/c")
  197. *
  198. * The traversal matcher can be extremely fast because it has a fast early-out
  199. * case: It checks the bname part of the path against _bnameTraversalRegex
  200. * and only runs a simplified _fullTraversalRegex on the whole path if bname
  201. * activation for it was triggered.
  202. *
  203. * Note: The traversal matcher will return not-excluded on some paths that the
  204. * full matcher would exclude. Example: "b" is excluded. traversal("b/c")
  205. * returns not-excluded because "c" isn't a bname activation pattern.
  206. */
  207. void prepare(const BasePathString &basePath);
  208. void prepare();
  209. static QString extractBnameTrigger(const QString &exclude, bool wildcardsMatchSlash);
  210. static QString convertToRegexpSyntax(QString exclude, bool wildcardsMatchSlash);
  211. QString _localPath;
  212. /// Files to load excludes from
  213. QMap<BasePathString, QStringList> _excludeFiles;
  214. /// Exclude patterns added with addManualExclude()
  215. QMap<BasePathString, QStringList> _manualExcludes;
  216. /// List of all active exclude patterns
  217. QMap<BasePathString, QStringList> _allExcludes;
  218. /// see prepare()
  219. QMap<BasePathString, QRegularExpression> _bnameTraversalRegexFile;
  220. QMap<BasePathString, QRegularExpression> _bnameTraversalRegexDir;
  221. QMap<BasePathString, QRegularExpression> _fullTraversalRegexFile;
  222. QMap<BasePathString, QRegularExpression> _fullTraversalRegexDir;
  223. QMap<BasePathString, QRegularExpression> _fullRegexFile;
  224. QMap<BasePathString, QRegularExpression> _fullRegexDir;
  225. bool _excludeConflictFiles = true;
  226. /**
  227. * Whether * and ? in patterns can match a /
  228. *
  229. * Unfortunately this was how matching was done on Windows so
  230. * it continues to be enabled there.
  231. */
  232. bool _wildcardsMatchSlash = false;
  233. /**
  234. * The client version. Used to evaluate version-dependent excludes,
  235. * see versionDirectiveKeepNextLine().
  236. */
  237. Version _clientVersion;
  238. friend class TestExcludedFiles;
  239. };
  240. #endif /* _CSYNC_EXCLUDE_H */