394 lines
12 KiB
C++
394 lines
12 KiB
C++
// *****************************************************************************
|
|
// * This file is part of the FreeFileSync project. It is distributed under *
|
|
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
|
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
|
|
// *****************************************************************************
|
|
|
|
#ifndef STRUCTURES_H_8210478915019450901745
|
|
#define STRUCTURES_H_8210478915019450901745
|
|
|
|
#include <variant>
|
|
#include <vector>
|
|
#include <chrono>
|
|
#include <zen/zstring.h>
|
|
#include "../afs/abstract.h"
|
|
|
|
|
|
namespace fff
|
|
{
|
|
using AFS = AbstractFileSystem;
|
|
|
|
enum class CompareVariant
|
|
{
|
|
timeSize,
|
|
content,
|
|
size
|
|
};
|
|
|
|
|
|
enum class SymLinkHandling
|
|
{
|
|
exclude,
|
|
asLink,
|
|
follow
|
|
};
|
|
|
|
|
|
enum class SyncDirection : unsigned char //save space for use in FileSystemObject!
|
|
{
|
|
none,
|
|
left,
|
|
right
|
|
};
|
|
|
|
|
|
enum CompareFileResult
|
|
{
|
|
FILE_EQUAL,
|
|
FILE_RENAMED, //both sides equal, except for different file name
|
|
FILE_LEFT_ONLY,
|
|
FILE_RIGHT_ONLY,
|
|
FILE_LEFT_NEWER, //
|
|
FILE_RIGHT_NEWER, //CompareVariant::timeSize only!
|
|
FILE_TIME_INVALID, // -> sync dirction can be determined (if leftNewer/rightNewer agree), unlike with FILE_CONFLICT
|
|
FILE_DIFFERENT_CONTENT, //CompareVariant::content, CompareVariant::size only!
|
|
FILE_CONFLICT
|
|
};
|
|
//attention make sure these /|\ \|/ three enums match!!!
|
|
enum CompareDirResult
|
|
{
|
|
DIR_EQUAL = FILE_EQUAL,
|
|
DIR_RENAMED = FILE_RENAMED,
|
|
DIR_LEFT_ONLY = FILE_LEFT_ONLY,
|
|
DIR_RIGHT_ONLY = FILE_RIGHT_ONLY,
|
|
DIR_CONFLICT = FILE_CONFLICT
|
|
};
|
|
|
|
enum CompareSymlinkResult
|
|
{
|
|
SYMLINK_EQUAL = FILE_EQUAL,
|
|
SYMLINK_RENAMED = FILE_RENAMED,
|
|
SYMLINK_LEFT_ONLY = FILE_LEFT_ONLY,
|
|
SYMLINK_RIGHT_ONLY = FILE_RIGHT_ONLY,
|
|
SYMLINK_LEFT_NEWER = FILE_LEFT_NEWER,
|
|
SYMLINK_RIGHT_NEWER = FILE_RIGHT_NEWER,
|
|
SYMLINK_TIME_INVALID = FILE_TIME_INVALID,
|
|
SYMLINK_DIFFERENT_CONTENT = FILE_DIFFERENT_CONTENT,
|
|
SYMLINK_CONFLICT = FILE_CONFLICT
|
|
};
|
|
|
|
|
|
std::wstring getSymbol(CompareFileResult cmpRes);
|
|
|
|
|
|
enum SyncOperation
|
|
{
|
|
SO_CREATE_LEFT,
|
|
SO_CREATE_RIGHT,
|
|
SO_DELETE_LEFT,
|
|
SO_DELETE_RIGHT,
|
|
|
|
SO_OVERWRITE_LEFT,
|
|
SO_OVERWRITE_RIGHT,
|
|
|
|
SO_MOVE_LEFT_FROM, //SO_DELETE_LEFT - optimization!
|
|
SO_MOVE_LEFT_TO, //SO_CREATE_LEFT
|
|
|
|
SO_MOVE_RIGHT_FROM, //SO_DELETE_RIGHT - optimization!
|
|
SO_MOVE_RIGHT_TO, //SO_CREATE_RIGHT
|
|
|
|
SO_RENAME_LEFT, //items are otherwise equal
|
|
SO_RENAME_RIGHT, //
|
|
|
|
SO_DO_NOTHING, //nothing will be synced: both sides differ
|
|
SO_EQUAL, //nothing will be synced: both sides are equal
|
|
SO_UNRESOLVED_CONFLICT
|
|
};
|
|
|
|
std::wstring getSymbol(SyncOperation op); //method used for exporting .csv file only!
|
|
|
|
|
|
enum class CudAction
|
|
{
|
|
noChange,
|
|
create,
|
|
update,
|
|
delete_, //"delete" is a reserved keyword :(
|
|
};
|
|
|
|
struct DirectionByDiff
|
|
{
|
|
SyncDirection leftOnly = SyncDirection::none;
|
|
SyncDirection rightOnly = SyncDirection::none;
|
|
SyncDirection leftNewer = SyncDirection::none;
|
|
SyncDirection rightNewer = SyncDirection::none;
|
|
|
|
bool operator==(const DirectionByDiff&) const = default;
|
|
};
|
|
|
|
|
|
struct DirectionByChange //=> requires sync.ffs_db
|
|
{
|
|
struct Changes
|
|
{
|
|
SyncDirection create = SyncDirection::none;
|
|
SyncDirection update = SyncDirection::none;
|
|
SyncDirection delete_ = SyncDirection::none; //"delete" is a reserved keyword :(
|
|
|
|
bool operator==(const Changes&) const = default;
|
|
} left, right;
|
|
|
|
bool operator==(const DirectionByChange&) const = default;
|
|
};
|
|
|
|
|
|
struct SyncDirectionConfig
|
|
{
|
|
std::variant<DirectionByDiff, DirectionByChange> dirs;
|
|
|
|
bool operator==(const SyncDirectionConfig&) const = default;
|
|
};
|
|
|
|
|
|
inline
|
|
bool effectivelyEqual(const SyncDirectionConfig& lhs, const SyncDirectionConfig& rhs) { return lhs == rhs; } //no change in behavior
|
|
|
|
|
|
enum class SyncVariant
|
|
{
|
|
twoWay,
|
|
mirror,
|
|
update,
|
|
custom,
|
|
};
|
|
SyncVariant getSyncVariant(const SyncDirectionConfig& cfg);
|
|
|
|
SyncDirectionConfig getDefaultSyncCfg(SyncVariant syncVar);
|
|
|
|
DirectionByDiff getDiffDirDefault(const DirectionByChange& changeDirs); //= when sync.ffs_db not yet available
|
|
DirectionByChange getChangesDirDefault(const DirectionByDiff& diffDirs);
|
|
|
|
std::wstring getVariantName(std::optional<CompareVariant> var);
|
|
std::wstring getVariantName(std::optional<SyncVariant> var);
|
|
|
|
std::wstring getVariantNameWithSymbol(SyncVariant var);
|
|
|
|
|
|
struct CompConfig
|
|
{
|
|
CompareVariant compareVar = CompareVariant::timeSize;
|
|
SymLinkHandling handleSymlinks = SymLinkHandling::exclude;
|
|
std::vector<unsigned int> ignoreTimeShiftMinutes; //treat modification times with these offsets as equal
|
|
|
|
bool operator==(const CompConfig&) const = default;
|
|
};
|
|
|
|
inline
|
|
bool effectivelyEqual(const CompConfig& lhs, const CompConfig& rhs) { return lhs == rhs; } //no change in behavior
|
|
|
|
|
|
enum class DeletionVariant
|
|
{
|
|
permanent,
|
|
recycler,
|
|
versioning
|
|
};
|
|
|
|
enum class VersioningStyle
|
|
{
|
|
replace,
|
|
timestampFolder,
|
|
timestampFile,
|
|
};
|
|
|
|
struct SyncConfig
|
|
{
|
|
//sync direction settings
|
|
SyncDirectionConfig directionCfg = getDefaultSyncCfg(SyncVariant::twoWay);
|
|
|
|
DeletionVariant deletionVariant = DeletionVariant::recycler; //use Recycle Bin, delete permanently or move to user-defined location
|
|
|
|
//versioning options
|
|
Zstring versioningFolderPhrase;
|
|
VersioningStyle versioningStyle = VersioningStyle::replace;
|
|
|
|
//limit number of versions per file: (if versioningStyle != replace)
|
|
int versionMaxAgeDays = 0; //<= 0 := no limit
|
|
int versionCountMin = 0; //only used if versionMaxAgeDays > 0 => < versionCountMax (if versionCountMax > 0)
|
|
int versionCountMax = 0; //<= 0 := no limit
|
|
};
|
|
|
|
|
|
inline
|
|
bool operator==(const SyncConfig& lhs, const SyncConfig& rhs)
|
|
{
|
|
return lhs.directionCfg == rhs.directionCfg &&
|
|
lhs.deletionVariant == rhs.deletionVariant && //!= DeletionVariant::versioning => still consider versioningFolderPhrase: e.g. user temporarily
|
|
lhs.versioningFolderPhrase == rhs.versioningFolderPhrase && //switched to "permanent" deletion and accidentally saved cfg => versioning folder can be restored
|
|
lhs.versioningStyle == rhs.versioningStyle &&
|
|
(lhs.versioningStyle == VersioningStyle::replace ||
|
|
(
|
|
lhs.versionMaxAgeDays == rhs.versionMaxAgeDays &&
|
|
(lhs.versionMaxAgeDays <= 0 ||
|
|
lhs.versionCountMin == rhs.versionCountMin) &&
|
|
lhs.versionCountMax == rhs.versionCountMax
|
|
));
|
|
//adapt effectivelyEqual() on changes, too!
|
|
}
|
|
|
|
|
|
inline
|
|
bool effectivelyEqual(const SyncConfig& lhs, const SyncConfig& rhs)
|
|
{
|
|
return effectivelyEqual(lhs.directionCfg, rhs.directionCfg) &&
|
|
lhs.deletionVariant == rhs.deletionVariant &&
|
|
(lhs.deletionVariant != DeletionVariant::versioning || //only evaluate versioning folder if required!
|
|
(
|
|
lhs.versioningFolderPhrase == rhs.versioningFolderPhrase &&
|
|
lhs.versioningStyle == rhs.versioningStyle &&
|
|
(lhs.versioningStyle == VersioningStyle::replace ||
|
|
(
|
|
lhs.versionMaxAgeDays == rhs.versionMaxAgeDays &&
|
|
(lhs.versionMaxAgeDays <= 0 ||
|
|
lhs.versionCountMin == rhs.versionCountMin) &&
|
|
lhs.versionCountMax == rhs.versionCountMax
|
|
))
|
|
));
|
|
}
|
|
|
|
|
|
enum class UnitSize
|
|
{
|
|
none,
|
|
byte,
|
|
kb,
|
|
mb
|
|
};
|
|
|
|
enum class UnitTime
|
|
{
|
|
none,
|
|
today,
|
|
thisMonth,
|
|
thisYear,
|
|
lastDays
|
|
};
|
|
|
|
struct FilterConfig
|
|
{
|
|
/* Semantics of PathFilter:
|
|
1. using it creates a NEW folder hierarchy! -> must be considered by <Two way> variant! (fortunately it turns out, doing nothing already has perfect semantics :)
|
|
2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! */
|
|
Zstring includeFilter = Zstr("*");
|
|
Zstring excludeFilter;
|
|
|
|
/* Semantics of SoftFilter:
|
|
1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches
|
|
2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed)
|
|
3. => equivalent to a user temporarily (de-)selecting rows -> not relevant for <Two way> variant! ;) */
|
|
unsigned int timeSpan = 0;
|
|
UnitTime unitTimeSpan = UnitTime::none;
|
|
|
|
uint64_t sizeMin = 0;
|
|
UnitSize unitSizeMin = UnitSize::none;
|
|
|
|
uint64_t sizeMax = 0;
|
|
UnitSize unitSizeMax = UnitSize::none;
|
|
|
|
bool operator==(const FilterConfig&) const = default;
|
|
};
|
|
|
|
|
|
void resolveUnits(size_t timeSpan, UnitTime unitTimeSpan,
|
|
uint64_t sizeMin, UnitSize unitSizeMin,
|
|
uint64_t sizeMax, UnitSize unitSizeMax,
|
|
time_t& timeFrom, //unit: UTC time, seconds
|
|
uint64_t& sizeMinBy, //unit: bytes
|
|
uint64_t& sizeMaxBy); //unit: bytes
|
|
|
|
|
|
struct LocalPairConfig //enhanced folder pairs with (optional) alternate configuration
|
|
{
|
|
Zstring folderPathPhraseLeft; //unresolved directory names as entered by user!
|
|
Zstring folderPathPhraseRight; //
|
|
|
|
std::optional<CompConfig> localCmpCfg;
|
|
std::optional<SyncConfig> localSyncCfg;
|
|
FilterConfig localFilter;
|
|
|
|
bool operator==(const LocalPairConfig& rhs) const = default;
|
|
};
|
|
|
|
|
|
enum class ResultsNotification
|
|
{
|
|
always,
|
|
errorWarning,
|
|
errorOnly,
|
|
};
|
|
|
|
|
|
enum class PostSyncCondition
|
|
{
|
|
completion,
|
|
errors,
|
|
success
|
|
};
|
|
|
|
|
|
struct MainConfiguration
|
|
{
|
|
CompConfig cmpCfg; //global compare settings: may be overwritten by folder pair settings
|
|
SyncConfig syncCfg; //global synchronisation settings: may be overwritten by folder pair settings
|
|
FilterConfig globalFilter; //global filter settings: combined with folder pair settings
|
|
|
|
LocalPairConfig firstPair; //there needs to be at least one pair!
|
|
std::vector<LocalPairConfig> additionalPairs;
|
|
|
|
std::map<AfsDevice, size_t /*parallel operations*/> deviceParallelOps; //should only include devices with >= 2 parallel ops
|
|
|
|
bool ignoreErrors = false; //true: errors will still be logged
|
|
size_t autoRetryCount = 0;
|
|
std::chrono::seconds autoRetryDelay{5};
|
|
|
|
Zstring postSyncCommand; //user-defined command line
|
|
PostSyncCondition postSyncCondition = PostSyncCondition::completion;
|
|
|
|
Zstring altLogFolderPathPhrase; //fill to use different log file folder (other than the default %appdata%\FreeFileSync\Logs)
|
|
|
|
std::string emailNotifyAddress; //optional
|
|
ResultsNotification emailNotifyCondition = ResultsNotification::always;
|
|
|
|
bool operator==(const MainConfiguration&) const = default;
|
|
};
|
|
|
|
|
|
size_t getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice);
|
|
void setDeviceParallelOps( std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice, size_t parallelOps);
|
|
size_t getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase);
|
|
void setDeviceParallelOps( std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps);
|
|
|
|
|
|
std::optional<CompareVariant> getCommonCompVariant(const MainConfiguration& mainCfg);
|
|
std::optional<SyncVariant> getCommonSyncVariant(const MainConfiguration& mainCfg);
|
|
|
|
|
|
struct WarningDialogs
|
|
{
|
|
bool warnFolderNotExisting = true;
|
|
bool warnFoldersDifferInCase = true;
|
|
bool warnDependentFolderPair = true;
|
|
bool warnDependentBaseFolders = true;
|
|
bool warnSignificantDifference = true;
|
|
bool warnNotEnoughDiskSpace = true;
|
|
bool warnUnresolvedConflicts = true;
|
|
bool warnRecyclerMissing = true;
|
|
bool warnDirectoryLockFailed = true;
|
|
bool warnVersioningFolderPartOfSync = true;
|
|
|
|
bool operator==(const WarningDialogs&) const = default;
|
|
};
|
|
}
|
|
|
|
#endif //STRUCTURES_H_8210478915019450901745
|