diff options
Diffstat (limited to 'library/process_xml.cpp')
-rw-r--r-- | library/process_xml.cpp | 1137 |
1 files changed, 1137 insertions, 0 deletions
diff --git a/library/process_xml.cpp b/library/process_xml.cpp new file mode 100644 index 00000000..e81fb3f9 --- /dev/null +++ b/library/process_xml.cpp @@ -0,0 +1,1137 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** +// +#include "process_xml.h" +#include "../shared/xml_base.h" +#include <wx/intl.h> +#include <wx/filefn.h> +#include "../shared/global_func.h" +#include "../shared/standard_paths.h" +#include "../shared/string_conv.h" + +using namespace ffs3; +using namespace xmlAccess; //functionally needed!!! + + +class FfsXmlParser : public xmlAccess::XmlParser +{ +public: + FfsXmlParser(const TiXmlElement* rootElement) : xmlAccess::XmlParser(rootElement) {} + + //read gui settings, all values retrieved are optional, so check for initial values! (== -1) + void readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg); + //read batch settings, all values retrieved are optional + void readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg); + //read global settings, valid for both GUI and batch mode, independent from configuration + void readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg); + +private: + //read alternate configuration (optional) => might point to NULL + void readXmlLocalConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair); + + //read basic FreefileSync settings (used by commandline and GUI), return true if ALL values have been retrieved successfully + void readXmlMainConfig(MainConfiguration& mainCfg); +}; + + +//write gui settings +bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& outputCfg, TiXmlDocument& doc); +//write batch settings +bool writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& outputCfg, TiXmlDocument& doc); +//write global settings +bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& outputCfg, TiXmlDocument& doc); +//write alternate configuration (optional) => might point to NULL +void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent); +//write basic FreefileSync settings (used by commandline and GUI), return true if everything was written successfully +bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc); + + +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) +{ + //load XML + if (!wxFileExists(filename)) + throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); + + TiXmlDocument doc; + loadXmlDocument(filename, XML_GUI_CONFIG, doc); //throw (XmlError) + + FfsXmlParser parser(doc.RootElement()); + parser.readXmlGuiConfig(config); //read GUI layout configuration + + if (parser.errorsOccurred()) + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + + parser.getErrorMessageFormatted(), XmlError::WARNING); +} + + +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) +{ + //load XML + if (!wxFileExists(filename)) + throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); + + TiXmlDocument doc; + loadXmlDocument(filename, XML_BATCH_CONFIG, doc); //throw (XmlError) + + FfsXmlParser parser(doc.RootElement()); + parser.readXmlBatchConfig(config); //read GUI layout configuration + + if (parser.errorsOccurred()) + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + + parser.getErrorMessageFormatted(), XmlError::WARNING); +} + + +void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) +{ + //load XML + if (!wxFileExists(getGlobalConfigFile())) + throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + + TiXmlDocument doc; + loadXmlDocument(getGlobalConfigFile(), XML_GLOBAL_SETTINGS, doc); //throw (XmlError) + + FfsXmlParser parser(doc.RootElement()); + parser.readXmlGlobalSettings(config); //read GUI layout configuration + + if (parser.errorsOccurred()) + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"\n\n") + + parser.getErrorMessageFormatted(), XmlError::WARNING); +} + + +void xmlAccess::writeConfig(const XmlGuiConfig& outputCfg, const wxString& filename) +{ + TiXmlDocument doc; + getDefaultXmlDocument(XML_GUI_CONFIG, doc); + + //populate and write XML tree + if (!writeXmlGuiConfig(outputCfg, doc)) //add GUI layout configuration settings + throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); + + saveXmlDocument(filename, doc); //throw (XmlError) +} + + +void xmlAccess::writeConfig(const XmlBatchConfig& outputCfg, const wxString& filename) +{ + TiXmlDocument doc; + getDefaultXmlDocument(XML_BATCH_CONFIG, doc); + + //populate and write XML tree + if (!writeXmlBatchConfig(outputCfg, doc)) //add batch configuration settings + throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); + + saveXmlDocument(filename, doc); //throw (XmlError) +} + + +void xmlAccess::writeConfig(const XmlGlobalSettings& outputCfg) +{ + TiXmlDocument doc; + getDefaultXmlDocument(XML_GLOBAL_SETTINGS, doc); + + //populate and write XML tree + if (!writeXmlGlobalSettings(outputCfg, doc)) //add GUI layout configuration settings + throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + + saveXmlDocument(getGlobalConfigFile(), doc); //throw (XmlError) +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent, CompareVariant& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "ByTimeAndSize") + output = ffs3::CMP_BY_TIME_SIZE; + else if (dummy == "ByContent") + output = ffs3::CMP_BY_CONTENT; + else + return false; + + return true; + } + else + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent, SyncDirection& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "left") + output = SYNC_DIR_LEFT; + else if (dummy == "right") + output = SYNC_DIR_RIGHT; + else //treat all other input as "none" + output = SYNC_DIR_NONE; + + return true; + } + else + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent, xmlAccess::OnError& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "Ignore") + output = xmlAccess::ON_ERROR_IGNORE; + else if (dummy == "Exit") + output = xmlAccess::ON_ERROR_EXIT; + else //treat all other input as popup + output = xmlAccess::ON_ERROR_POPUP; + + return true; + } + else + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3::DeletionPolicy& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "DeletePermanently") + output = ffs3::DELETE_PERMANENTLY; + else if (dummy == "MoveToRecycleBin") + output = ffs3::MOVE_TO_RECYCLE_BIN; + else if (dummy == "MoveToCustomDirectory") + output = ffs3::MOVE_TO_CUSTOM_DIRECTORY; + else + return false; + + return true; + } + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3::SymLinkHandling& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "Ignore") + output = ffs3::SYMLINK_IGNORE; + else if (dummy == "UseDirectly") + output = ffs3::SYMLINK_USE_DIRECTLY; + else if (dummy == "FollowLink") + output = ffs3::SYMLINK_FOLLOW_LINK; + else + return false; + + return true; + } + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent, Zstring& output) +{ + wxString dummy; + if (!xmlAccess::readXmlElement(name, parent, dummy)) + return false; + + output = wxToZ(dummy); + return true; +} + + +bool readXmlAttribute(const std::string& name, const TiXmlElement* node, xmlAccess::ColumnTypes& output) +{ + int dummy = 0; + if (xmlAccess::readXmlAttribute(name, node, dummy)) + { + output = static_cast<xmlAccess::ColumnTypes>(dummy); + return true; + } + else + return false; +} + + +//################################################################################################################ +void FfsXmlParser::readXmlLocalConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair) +{ + //read folder pairs + readXmlElementLogging("Left", &folderPair, enhPair.leftDirectory); + readXmlElementLogging("Right", &folderPair, enhPair.rightDirectory); + + +//########################################################### + //alternate sync configuration + const TiXmlElement* altSyncConfig = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").ToElement(); + if (altSyncConfig) + { + AlternateSyncConfig* altSyncCfg = new AlternateSyncConfig; + enhPair.altSyncConfig.reset(altSyncCfg); + + const TiXmlElement* syncCfg = TiXmlHandleConst(altSyncConfig).FirstChild("Synchronization").ToElement(); + const TiXmlElement* syncDirections = TiXmlHandleConst(syncCfg).FirstChild("Directions").ToElement(); + + //read sync configuration + readXmlElementLogging("Automatic", syncCfg, altSyncCfg->syncConfiguration.automatic); + readXmlElementLogging("LeftOnly", syncDirections, altSyncCfg->syncConfiguration.exLeftSideOnly); + readXmlElementLogging("RightOnly", syncDirections, altSyncCfg->syncConfiguration.exRightSideOnly); + readXmlElementLogging("LeftNewer", syncDirections, altSyncCfg->syncConfiguration.leftNewer); + readXmlElementLogging("RightNewer", syncDirections, altSyncCfg->syncConfiguration.rightNewer); + readXmlElementLogging("Different", syncDirections, altSyncCfg->syncConfiguration.different); + readXmlElementLogging("Conflict", syncDirections, altSyncCfg->syncConfiguration.conflict); + + const TiXmlElement* miscSettings = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").FirstChild("Miscellaneous").ToElement(); + readXmlElementLogging("DeletionPolicy", miscSettings, altSyncCfg->handleDeletion); + readXmlElementLogging("CustomDeletionFolder", miscSettings, altSyncCfg->customDeletionDirectory); + } + +//########################################################### + //alternate filter configuration + const TiXmlElement* filterCfg = TiXmlHandleConst(&folderPair).FirstChild("LocalFilter").ToElement(); + if (filterCfg) + { + //read filter settings + readXmlElementLogging("Include", filterCfg, enhPair.localFilter.includeFilter); + readXmlElementLogging("Exclude", filterCfg, enhPair.localFilter.excludeFilter); + } +} + + +void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) +{ + TiXmlHandleConst hRoot(getRoot()); //custom const handle: TiXml API seems broken in this regard + +//########################################################### + const TiXmlElement* cmpSettings = hRoot.FirstChild("MainConfig").FirstChild("Comparison").ToElement(); + + //read compare variant + readXmlElementLogging("Variant", cmpSettings, mainCfg.compareVar); + + //include symbolic links at all? + readXmlElementLogging("HandleSymlinks", cmpSettings, mainCfg.handleSymlinks); + +//########################################################### + const TiXmlElement* syncCfg = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").ToElement(); + const TiXmlElement* syncDirections = TiXmlHandleConst(syncCfg).FirstChild("Directions").ToElement(); + + //read sync configuration + readXmlElementLogging("Automatic", syncCfg, mainCfg.syncConfiguration.automatic); + readXmlElementLogging("LeftOnly", syncDirections, mainCfg.syncConfiguration.exLeftSideOnly); + readXmlElementLogging("RightOnly", syncDirections, mainCfg.syncConfiguration.exRightSideOnly); + readXmlElementLogging("LeftNewer", syncDirections, mainCfg.syncConfiguration.leftNewer); + readXmlElementLogging("RightNewer", syncDirections, mainCfg.syncConfiguration.rightNewer); + readXmlElementLogging("Different", syncDirections, mainCfg.syncConfiguration.different); + readXmlElementLogging("Conflict", syncDirections, mainCfg.syncConfiguration.conflict); + +//########################################################### + const TiXmlElement* miscSettings = hRoot.FirstChild("MainConfig").FirstChild("Miscellaneous").ToElement(); + + //misc + readXmlElementLogging("DeletionPolicy", miscSettings, mainCfg.handleDeletion); + readXmlElementLogging("CustomDeletionFolder", miscSettings, mainCfg.customDeletionDirectory); +//########################################################### + const TiXmlElement* filter = TiXmlHandleConst(miscSettings).FirstChild("Filter").ToElement(); + + //read filter settings + Zstring includeFilter; + Zstring excludeFilter; + readXmlElementLogging("Include", filter, includeFilter); + readXmlElementLogging("Exclude", filter, excludeFilter); + + mainCfg.globalFilter = FilterConfig(includeFilter, excludeFilter); + +//########################################################### + const TiXmlElement* pairs = hRoot.FirstChild("MainConfig").FirstChild("FolderPairs").FirstChild("Pair").ToElement(); + + //read all folder pairs + mainCfg.additionalPairs.clear(); + bool firstLoop = true; + while (pairs) + { + FolderPairEnh newPair; + readXmlLocalConfig(*pairs, newPair); + + if (firstLoop) //read first folder pair + { + firstLoop = false; + mainCfg.firstPair = newPair; + } + else //read additional folder pairs + mainCfg.additionalPairs.push_back(newPair); + + pairs = pairs->NextSiblingElement(); + } +} + + +void FfsXmlParser::readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg) +{ + //read main config + readXmlMainConfig(outputCfg.mainCfg); + + //read GUI specific config data + const TiXmlElement* guiConfig = TiXmlHandleConst(getRoot()).FirstChild("GuiConfig").ToElement(); + + readXmlElementLogging("HideFiltered", guiConfig, outputCfg.hideFilteredElements); + + xmlAccess::OnError errorHand = ON_ERROR_POPUP; + readXmlElementLogging("HandleError", guiConfig, errorHand); + outputCfg.ignoreErrors = errorHand == xmlAccess::ON_ERROR_IGNORE; + + readXmlElementLogging("SyncPreviewActive", guiConfig, outputCfg.syncPreviewEnabled); +} + + +void FfsXmlParser::readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg) +{ + //read main config + readXmlMainConfig(outputCfg.mainCfg); + + //read batch specific config + const TiXmlElement* batchConfig = TiXmlHandleConst(getRoot()).FirstChild("BatchConfig").ToElement(); + + readXmlElementLogging("Silent", batchConfig, outputCfg.silent); + readXmlElementLogging("LogfileDirectory", batchConfig, outputCfg.logFileDirectory); + readXmlElementLogging("HandleError", batchConfig, outputCfg.handleError); +} + + +void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg) +{ + //read global settings + const TiXmlElement* global = TiXmlHandleConst(getRoot()).FirstChild("Shared").ToElement(); + + //try to read program language setting + readXmlElementLogging("Language", global, outputCfg.programLanguage); + + //ignore +/- 1 hour due to DST change + readXmlElementLogging("IgnoreOneHourDifference", global, outputCfg.ignoreOneHourDiff); + + //copy locked files using VSS + readXmlElementLogging("CopyLockedFiles", global, outputCfg.copyLockedFiles); + + //file permissions + readXmlElementLogging("CopyFilePermissions", global, outputCfg.copyFilePermissions); + + //verify file copying + readXmlElementLogging("VerifyCopiedFiles", global, outputCfg.verifyFileCopy); + + //max. allowed file time deviation + readXmlElementLogging("FileTimeTolerance", global, outputCfg.fileTimeTolerance); + + + const TiXmlElement* optionalDialogs = TiXmlHandleConst(getRoot()).FirstChild("Shared").FirstChild("ShowOptionalDialogs").ToElement(); + + //folder dependency check + readXmlElementLogging("CheckForDependentFolders", optionalDialogs, outputCfg.optDialogs.warningDependentFolders); + //significant difference check + readXmlElementLogging("CheckForSignificantDifference", optionalDialogs, outputCfg.optDialogs.warningSignificantDifference); + //check free disk space + readXmlElementLogging("CheckForFreeDiskSpace", optionalDialogs, outputCfg.optDialogs.warningNotEnoughDiskSpace); + //check for unresolved conflicts + readXmlElementLogging("CheckForUnresolvedConflicts", optionalDialogs, outputCfg.optDialogs.warningUnresolvedConflicts); + + readXmlElementLogging("NotifyDatabaseError", optionalDialogs, outputCfg.optDialogs.warningSyncDatabase); + + readXmlElementLogging("PopupOnConfigChange", optionalDialogs, outputCfg.optDialogs.popupOnConfigChange); + + readXmlElementLogging("SummaryBeforeSync", optionalDialogs, outputCfg.optDialogs.showSummaryBeforeSync); + + + //gui specific global settings (optional) + const TiXmlElement* gui = TiXmlHandleConst(getRoot()).FirstChild("Gui").ToElement(); + const TiXmlElement* mainWindow = TiXmlHandleConst(gui).FirstChild("Windows").FirstChild("Main").ToElement(); + + //read application window size and position + readXmlElementLogging("Width", mainWindow, outputCfg.gui.widthNotMaximized); + readXmlElementLogging("Height", mainWindow, outputCfg.gui.heightNotMaximized); + readXmlElementLogging("PosX", mainWindow, outputCfg.gui.posXNotMaximized); + readXmlElementLogging("PosY", mainWindow, outputCfg.gui.posYNotMaximized); + readXmlElementLogging("Maximized", mainWindow, outputCfg.gui.isMaximized); + + readXmlElementLogging("ManualDeletionOnBothSides", mainWindow, outputCfg.gui.deleteOnBothSides); + readXmlElementLogging("ManualDeletionUseRecycler", mainWindow, outputCfg.gui.useRecyclerForManualDeletion); + + readXmlElementLogging("RespectCaseOnSearch", mainWindow, outputCfg.gui.textSearchRespectCase); + +//########################################################### + //read column attributes + readXmlAttributeLogging("AutoAdjust", TiXmlHandleConst(mainWindow).FirstChild("LeftColumns").ToElement(), outputCfg.gui.autoAdjustColumnsLeft); + readXmlAttributeLogging("ShowFileIcons", TiXmlHandleConst(mainWindow).FirstChild("LeftColumns").ToElement(), outputCfg.gui.showFileIconsLeft); + + const TiXmlElement* leftColumn = TiXmlHandleConst(mainWindow).FirstChild("LeftColumns").FirstChild("Column").ToElement(); + size_t colPos = 0; + while (leftColumn) + { + ColumnAttrib newAttrib; + newAttrib.position = colPos++; + readXmlAttributeLogging("Type", leftColumn, newAttrib.type); + readXmlAttributeLogging("Visible", leftColumn, newAttrib.visible); + readXmlAttributeLogging("Width", leftColumn, newAttrib.width); + outputCfg.gui.columnAttribLeft.push_back(newAttrib); + + leftColumn = leftColumn->NextSiblingElement(); + } + + readXmlAttributeLogging("AutoAdjust", TiXmlHandleConst(mainWindow).FirstChild("RightColumns").ToElement(), outputCfg.gui.autoAdjustColumnsRight); + readXmlAttributeLogging("ShowFileIcons", TiXmlHandleConst(mainWindow).FirstChild("RightColumns").ToElement(), outputCfg.gui.showFileIconsRight); + + const TiXmlElement* rightColumn = TiXmlHandleConst(mainWindow).FirstChild("RightColumns").FirstChild("Column").ToElement(); + colPos = 0; + while (rightColumn) + { + ColumnAttrib newAttrib; + newAttrib.position = colPos++; + readXmlAttributeLogging("Type", rightColumn, newAttrib.type); + readXmlAttributeLogging("Visible", rightColumn, newAttrib.visible); + readXmlAttributeLogging("Width", rightColumn, newAttrib.width); + outputCfg.gui.columnAttribRight.push_back(newAttrib); + + rightColumn = rightColumn->NextSiblingElement(); + } + + //load folder history elements + const TiXmlElement* historyLeft = TiXmlHandleConst(mainWindow).FirstChild("FolderHistoryLeft").ToElement(); + //load max. history size + readXmlAttributeLogging("MaximumSize", historyLeft, outputCfg.gui.folderHistLeftMax); + //load config history elements + readXmlElementLogging("Folder", historyLeft, outputCfg.gui.folderHistoryLeft); + + + const TiXmlElement* historyRight = TiXmlHandleConst(mainWindow).FirstChild("FolderHistoryRight").ToElement(); + //load max. history size + readXmlAttributeLogging("MaximumSize", historyRight, outputCfg.gui.folderHistRightMax); + //load config history elements + readXmlElementLogging("Folder", historyRight, outputCfg.gui.folderHistoryRight); + + + readXmlElementLogging("SelectedTabBottomLeft", mainWindow, outputCfg.gui.selectedTabBottomLeft); + + + //external applications + const TiXmlElement* extApps = TiXmlHandleConst(gui).FirstChild("ExternalApplications").FirstChild("Commandline").ToElement(); + if (extApps) + { + outputCfg.gui.externelApplications.clear(); + while (extApps) + { + wxString description; + wxString cmdLine; + + readXmlAttributeLogging("Description", extApps, description); + const char* text = extApps->GetText(); + if (text) cmdLine = wxString::FromUTF8(text); //read commandline + outputCfg.gui.externelApplications.push_back(std::make_pair(description, cmdLine)); + + extApps = extApps->NextSiblingElement(); + } + } + //load config file history + const TiXmlElement* cfgHistory = TiXmlHandleConst(gui).FirstChild("ConfigHistory").ToElement(); + + //load max. history size + readXmlAttributeLogging("MaximumSize", cfgHistory, outputCfg.gui.cfgHistoryMax); + + //load config history elements + readXmlElementLogging("File", cfgHistory, outputCfg.gui.cfgFileHistory); + + //last update check + readXmlElementLogging("LastUpdateCheck", gui, outputCfg.gui.lastUpdateCheck); + + + //batch specific global settings + //const TiXmlElement* batch = TiXmlHandleConst(root).FirstChild("Batch").ToElement(); +} + + +void addXmlElement(const std::string& name, const CompareVariant variant, TiXmlElement* parent) +{ + switch (variant) + { + case ffs3::CMP_BY_TIME_SIZE: + xmlAccess::addXmlElement(name, std::string("ByTimeAndSize"), parent); + break; + case ffs3::CMP_BY_CONTENT: + xmlAccess::addXmlElement(name, std::string("ByContent"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, const SyncDirection value, TiXmlElement* parent) +{ + switch (value) + { + case SYNC_DIR_LEFT: + xmlAccess::addXmlElement(name, std::string("left"), parent); + break; + case SYNC_DIR_RIGHT: + xmlAccess::addXmlElement(name, std::string("right"), parent); + break; + case SYNC_DIR_NONE: + xmlAccess::addXmlElement(name, std::string("none"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, const xmlAccess::OnError value, TiXmlElement* parent) +{ + switch (value) + { + case xmlAccess::ON_ERROR_IGNORE: + xmlAccess::addXmlElement(name, std::string("Ignore"), parent); + break; + case xmlAccess::ON_ERROR_EXIT: + xmlAccess::addXmlElement(name, std::string("Exit"), parent); + break; + case xmlAccess::ON_ERROR_POPUP: + xmlAccess::addXmlElement(name, std::string("Popup"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, const ffs3::DeletionPolicy value, TiXmlElement* parent) +{ + switch (value) + { + case ffs3::DELETE_PERMANENTLY: + xmlAccess::addXmlElement(name, std::string("DeletePermanently"), parent); + break; + case ffs3::MOVE_TO_RECYCLE_BIN: + xmlAccess::addXmlElement(name, std::string("MoveToRecycleBin"), parent); + break; + case ffs3::MOVE_TO_CUSTOM_DIRECTORY: + xmlAccess::addXmlElement(name, std::string("MoveToCustomDirectory"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, const ffs3::SymLinkHandling value, TiXmlElement* parent) +{ + switch (value) + { + case ffs3::SYMLINK_IGNORE: + xmlAccess::addXmlElement(name, std::string("Ignore"), parent); + break; + case ffs3::SYMLINK_USE_DIRECTLY: + xmlAccess::addXmlElement(name, std::string("UseDirectly"), parent); + break; + case ffs3::SYMLINK_FOLLOW_LINK: + xmlAccess::addXmlElement(name, std::string("FollowLink"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, const Zstring& value, TiXmlElement* parent) +{ + xmlAccess::addXmlElement(name, wxString(zToWx(value)), parent); +} + + +void addXmlAttribute(const std::string& name, const xmlAccess::ColumnTypes value, TiXmlElement* node) +{ + xmlAccess::addXmlAttribute(name, static_cast<int>(value), node); +} + + +void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent) +{ + //write folder pairs + TiXmlElement* newfolderPair = new TiXmlElement("Pair"); + parent.LinkEndChild(newfolderPair); + + addXmlElement("Left", enhPair.leftDirectory, newfolderPair); + addXmlElement("Right", enhPair.rightDirectory, newfolderPair); + + + //alternate sync configuration + const AlternateSyncConfig* altSyncConfig = enhPair.altSyncConfig.get(); + if (altSyncConfig) + { + TiXmlElement* syncCfg = new TiXmlElement("AlternateSyncConfig"); + newfolderPair->LinkEndChild(syncCfg); + + TiXmlElement* syncSettings = new TiXmlElement("Synchronization"); + syncCfg->LinkEndChild(syncSettings); + + //write sync configuration + addXmlElement("Automatic", altSyncConfig->syncConfiguration.automatic, syncSettings); + + TiXmlElement* syncDirections = new TiXmlElement("Directions"); + syncSettings->LinkEndChild(syncDirections); + + addXmlElement("LeftOnly", altSyncConfig->syncConfiguration.exLeftSideOnly, syncDirections); + addXmlElement("RightOnly", altSyncConfig->syncConfiguration.exRightSideOnly, syncDirections); + addXmlElement("LeftNewer", altSyncConfig->syncConfiguration.leftNewer, syncDirections); + addXmlElement("RightNewer", altSyncConfig->syncConfiguration.rightNewer, syncDirections); + addXmlElement("Different", altSyncConfig->syncConfiguration.different, syncDirections); + addXmlElement("Conflict", altSyncConfig->syncConfiguration.conflict, syncDirections); + + + TiXmlElement* miscSettings = new TiXmlElement("Miscellaneous"); + syncCfg->LinkEndChild(miscSettings); + + //misc + addXmlElement("DeletionPolicy", altSyncConfig->handleDeletion, miscSettings); + addXmlElement("CustomDeletionFolder", altSyncConfig->customDeletionDirectory, miscSettings); + } + +//########################################################### + //alternate filter configuration + TiXmlElement* filterCfg = new TiXmlElement("LocalFilter"); + newfolderPair->LinkEndChild(filterCfg); + + //write filter settings + addXmlElement("Include", enhPair.localFilter.includeFilter, filterCfg); + addXmlElement("Exclude", enhPair.localFilter.excludeFilter, filterCfg); +} + + +bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) +{ + TiXmlElement* root = doc.RootElement(); + if (!root) return false; + + TiXmlElement* settings = new TiXmlElement("MainConfig"); + root->LinkEndChild(settings); + +//########################################################### + TiXmlElement* cmpSettings = new TiXmlElement("Comparison"); + settings->LinkEndChild(cmpSettings); + + //write compare algorithm + addXmlElement("Variant", mainCfg.compareVar, cmpSettings); + + //include symbolic links at all? + addXmlElement("HandleSymlinks", mainCfg.handleSymlinks, cmpSettings); + +//########################################################### + TiXmlElement* syncSettings = new TiXmlElement("Synchronization"); + settings->LinkEndChild(syncSettings); + + //write sync configuration + addXmlElement("Automatic", mainCfg.syncConfiguration.automatic, syncSettings); + + TiXmlElement* syncDirections = new TiXmlElement("Directions"); + syncSettings->LinkEndChild(syncDirections); + + addXmlElement("LeftOnly", mainCfg.syncConfiguration.exLeftSideOnly, syncDirections); + addXmlElement("RightOnly", mainCfg.syncConfiguration.exRightSideOnly, syncDirections); + addXmlElement("LeftNewer", mainCfg.syncConfiguration.leftNewer, syncDirections); + addXmlElement("RightNewer", mainCfg.syncConfiguration.rightNewer, syncDirections); + addXmlElement("Different", mainCfg.syncConfiguration.different, syncDirections); + addXmlElement("Conflict", mainCfg.syncConfiguration.conflict, syncDirections); + +//########################################################### + TiXmlElement* miscSettings = new TiXmlElement("Miscellaneous"); + settings->LinkEndChild(miscSettings); + + //write filter settings + TiXmlElement* filter = new TiXmlElement("Filter"); + miscSettings->LinkEndChild(filter); + + addXmlElement("Include", mainCfg.globalFilter.includeFilter, filter); + addXmlElement("Exclude", mainCfg.globalFilter.excludeFilter, filter); + + //other + addXmlElement("DeletionPolicy", mainCfg.handleDeletion, miscSettings); + addXmlElement("CustomDeletionFolder", mainCfg.customDeletionDirectory, miscSettings); + +//########################################################### + TiXmlElement* pairs = new TiXmlElement("FolderPairs"); + settings->LinkEndChild(pairs); + + //write first folder pair + writeXmlLocalConfig(mainCfg.firstPair, *pairs); + + //write additional folder pairs + for (std::vector<FolderPairEnh>::const_iterator i = mainCfg.additionalPairs.begin(); i != mainCfg.additionalPairs.end(); ++i) + writeXmlLocalConfig(*i, *pairs); + + return true; +} + + +bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& inputCfg, TiXmlDocument& doc) +{ + //write main config + if (!writeXmlMainConfig(inputCfg.mainCfg, doc)) + return false; + + //write GUI specific config + TiXmlElement* root = doc.RootElement(); + if (!root) return false; + + TiXmlElement* guiConfig = new TiXmlElement("GuiConfig"); + root->LinkEndChild(guiConfig); + + addXmlElement("HideFiltered", inputCfg.hideFilteredElements, guiConfig); + + addXmlElement("HandleError", inputCfg.ignoreErrors ? xmlAccess::ON_ERROR_IGNORE : xmlAccess::ON_ERROR_POPUP, guiConfig); + + addXmlElement("SyncPreviewActive", inputCfg.syncPreviewEnabled, guiConfig); + + return true; +} + + +bool writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& inputCfg, TiXmlDocument& doc) +{ + //write main config + if (!writeXmlMainConfig(inputCfg.mainCfg, doc)) + return false; + + //write GUI specific config + TiXmlElement* root = doc.RootElement(); + if (!root) return false; + + TiXmlElement* batchConfig = new TiXmlElement("BatchConfig"); + root->LinkEndChild(batchConfig); + + addXmlElement("Silent", inputCfg.silent, batchConfig); + addXmlElement("LogfileDirectory", inputCfg.logFileDirectory, batchConfig); + addXmlElement("HandleError", inputCfg.handleError, batchConfig); + + return true; +} + + +bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlDocument& doc) +{ + TiXmlElement* root = doc.RootElement(); + if (!root) return false; + + //################################################################### + //write global settings + TiXmlElement* global = new TiXmlElement("Shared"); + root->LinkEndChild(global); + + //program language + addXmlElement("Language", inputCfg.programLanguage, global); + + //ignore +/- 1 hour due to DST change + addXmlElement("IgnoreOneHourDifference", inputCfg.ignoreOneHourDiff, global); + + //copy locked files using VSS + addXmlElement("CopyLockedFiles", inputCfg.copyLockedFiles, global); + + //file permissions + addXmlElement("CopyFilePermissions", inputCfg.copyFilePermissions, global); + + //verify file copying + addXmlElement("VerifyCopiedFiles", inputCfg.verifyFileCopy, global); + + //max. allowed file time deviation + addXmlElement("FileTimeTolerance", inputCfg.fileTimeTolerance, global); + + + //optional dialogs + TiXmlElement* optionalDialogs = new TiXmlElement("ShowOptionalDialogs"); + global->LinkEndChild(optionalDialogs); + + //warning: dependent folders + addXmlElement("CheckForDependentFolders", inputCfg.optDialogs.warningDependentFolders, optionalDialogs); + + //significant difference check + addXmlElement("CheckForSignificantDifference", inputCfg.optDialogs.warningSignificantDifference, optionalDialogs); + + //check free disk space + addXmlElement("CheckForFreeDiskSpace", inputCfg.optDialogs.warningNotEnoughDiskSpace, optionalDialogs); + + //check for unresolved conflicts + addXmlElement("CheckForUnresolvedConflicts", inputCfg.optDialogs.warningUnresolvedConflicts, optionalDialogs); + + addXmlElement("NotifyDatabaseError", inputCfg.optDialogs.warningSyncDatabase, optionalDialogs); + + addXmlElement("PopupOnConfigChange", inputCfg.optDialogs.popupOnConfigChange, optionalDialogs); + + addXmlElement("SummaryBeforeSync", inputCfg.optDialogs.showSummaryBeforeSync, optionalDialogs); + + + //################################################################### + //write global gui settings + TiXmlElement* gui = new TiXmlElement("Gui"); + root->LinkEndChild(gui); + + TiXmlElement* windows = new TiXmlElement("Windows"); + gui->LinkEndChild(windows); + + TiXmlElement* mainWindow = new TiXmlElement("Main"); + windows->LinkEndChild(mainWindow); + + //window size + addXmlElement("Width", inputCfg.gui.widthNotMaximized, mainWindow); + addXmlElement("Height", inputCfg.gui.heightNotMaximized, mainWindow); + + //window position + addXmlElement("PosX", inputCfg.gui.posXNotMaximized, mainWindow); + addXmlElement("PosY", inputCfg.gui.posYNotMaximized, mainWindow); + addXmlElement("Maximized", inputCfg.gui.isMaximized, mainWindow); + + addXmlElement("ManualDeletionOnBothSides", inputCfg.gui.deleteOnBothSides, mainWindow); + addXmlElement("ManualDeletionUseRecycler", inputCfg.gui.useRecyclerForManualDeletion, mainWindow); + + addXmlElement("RespectCaseOnSearch", inputCfg.gui.textSearchRespectCase, mainWindow); + + //write column attributes + TiXmlElement* leftColumn = new TiXmlElement("LeftColumns"); + mainWindow->LinkEndChild(leftColumn); + + addXmlAttribute("AutoAdjust", inputCfg.gui.autoAdjustColumnsLeft, leftColumn); + addXmlAttribute("ShowFileIcons", inputCfg.gui.showFileIconsLeft, leftColumn); + + ColumnAttributes columnAtrribLeftCopy = inputCfg.gui.columnAttribLeft; //can't change const vector + sort(columnAtrribLeftCopy.begin(), columnAtrribLeftCopy.end(), xmlAccess::sortByPositionOnly); + for (size_t i = 0; i < columnAtrribLeftCopy.size(); ++i) + { + TiXmlElement* subElement = new TiXmlElement("Column"); + leftColumn->LinkEndChild(subElement); + + const ColumnAttrib& colAttrib = columnAtrribLeftCopy[i]; + addXmlAttribute("Type", colAttrib.type, subElement); + addXmlAttribute("Visible", colAttrib.visible, subElement); + addXmlAttribute("Width", colAttrib.width, subElement); + } + + TiXmlElement* rightColumn = new TiXmlElement("RightColumns"); + mainWindow->LinkEndChild(rightColumn); + + addXmlAttribute("AutoAdjust", inputCfg.gui.autoAdjustColumnsRight, rightColumn); + addXmlAttribute("ShowFileIcons", inputCfg.gui.showFileIconsRight, rightColumn); + + ColumnAttributes columnAtrribRightCopy = inputCfg.gui.columnAttribRight; + sort(columnAtrribRightCopy.begin(), columnAtrribRightCopy.end(), xmlAccess::sortByPositionOnly); + for (size_t i = 0; i < columnAtrribRightCopy.size(); ++i) + { + TiXmlElement* subElement = new TiXmlElement("Column"); + rightColumn->LinkEndChild(subElement); + + const ColumnAttrib& colAttrib = columnAtrribRightCopy[i]; + addXmlAttribute("Type", colAttrib.type, subElement); + addXmlAttribute("Visible", colAttrib.visible, subElement); + addXmlAttribute("Width", colAttrib.width, subElement); + } + + //write folder history elements + TiXmlElement* historyLeft = new TiXmlElement("FolderHistoryLeft"); + mainWindow->LinkEndChild(historyLeft); + TiXmlElement* historyRight = new TiXmlElement("FolderHistoryRight"); + mainWindow->LinkEndChild(historyRight); + + addXmlAttribute("MaximumSize", inputCfg.gui.folderHistLeftMax, historyLeft); + addXmlAttribute("MaximumSize", inputCfg.gui.folderHistRightMax, historyRight); + + addXmlElement("Folder", inputCfg.gui.folderHistoryLeft, historyLeft); + addXmlElement("Folder", inputCfg.gui.folderHistoryRight, historyRight); + + addXmlElement("SelectedTabBottomLeft", inputCfg.gui.selectedTabBottomLeft, mainWindow); + + //external applications + TiXmlElement* extApp = new TiXmlElement("ExternalApplications"); + gui->LinkEndChild(extApp); + + for (ExternalApps::const_iterator i = inputCfg.gui.externelApplications.begin(); i != inputCfg.gui.externelApplications.end(); ++i) + { + TiXmlElement* newEntry = new TiXmlElement("Commandline"); + extApp->LinkEndChild(newEntry); + + addXmlAttribute("Description", i->first, newEntry); + newEntry->LinkEndChild(new TiXmlText(i->second.ToUTF8())); //commandline + } + + //write config file history + TiXmlElement* cfgHistory = new TiXmlElement("ConfigHistory"); + gui->LinkEndChild(cfgHistory); + + addXmlAttribute("MaximumSize", inputCfg.gui.cfgHistoryMax, cfgHistory); + addXmlElement("File", inputCfg.gui.cfgFileHistory, cfgHistory); + + + //last update check + addXmlElement("LastUpdateCheck", inputCfg.gui.lastUpdateCheck, gui); + + //################################################################### + //write global batch settings + + TiXmlElement* batch = new TiXmlElement("Batch"); + root->LinkEndChild(batch); + + return true; +} + + +int xmlAccess::retrieveSystemLanguage() +{ + return wxLocale::GetSystemLanguage(); +} + + +wxString xmlAccess::getGlobalConfigFile() +{ + return ffs3::getConfigDir() + wxT("GlobalSettings.xml"); +} + + +void xmlAccess::OptionalDialogs::resetDialogs() +{ + warningDependentFolders = true; + warningSignificantDifference = true; + warningNotEnoughDiskSpace = true; + warningUnresolvedConflicts = true; + warningSyncDatabase = true; + popupOnConfigChange = true; + showSummaryBeforeSync = true; +} + + +xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) +{ + XmlGuiConfig output; + output.mainCfg = batchCfg.mainCfg; + return output; +} + + +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) +{ + XmlBatchConfig output; + output.mainCfg = guiCfg.mainCfg; + + if (guiCfg.ignoreErrors) + output.handleError = xmlAccess::ON_ERROR_IGNORE; + else + output.handleError = xmlAccess::ON_ERROR_POPUP; + + return output; +} + + +xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filenames) //throw () +{ + bool guiCfgExists = false; + bool batchCfgExists = false; + + for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) + { + switch (xmlAccess::getXmlType(*i)) //throw() + { + case XML_GUI_CONFIG: + guiCfgExists = true; + break; + + case XML_BATCH_CONFIG: + batchCfgExists = true; + break; + + case XML_GLOBAL_SETTINGS: + case XML_REAL_CONFIG: + case XML_OTHER: + return MERGE_OTHER; + } + } + + if (guiCfgExists && batchCfgExists) + return MERGE_GUI_BATCH; + else if (guiCfgExists && !batchCfgExists) + return MERGE_GUI; + else if (!guiCfgExists && batchCfgExists) + return MERGE_BATCH; + else + return MERGE_OTHER; +} + + +namespace +{ +template <class XmlCfg> +XmlCfg loadCfgImpl(const wxString& filename, std::auto_ptr<xmlAccess::XmlError>& exeption) //throw (xmlAccess::XmlError) +{ + XmlCfg cfg; + try + { + xmlAccess::readConfig(filename, cfg); //throw (xmlAccess::XmlError); + } + catch (const xmlAccess::XmlError& e) + { + if (e.getSeverity() == xmlAccess::XmlError::FATAL) + throw; + else + exeption.reset(new xmlAccess::XmlError(e)); + } + return cfg; +} + + +template <class XmlCfg> +void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw (xmlAccess::XmlError) +{ + using namespace xmlAccess; + + assert(!filenames.empty()); + if (filenames.empty()) + return; + + std::vector<ffs3::MainConfiguration> mainCfgs; + std::auto_ptr<XmlError> savedException; + + for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) + { + switch (getXmlType(*i)) + { + case XML_GUI_CONFIG: + mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) + break; + + case XML_BATCH_CONFIG: + mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) + break; + + case XML_GLOBAL_SETTINGS: + case XML_REAL_CONFIG: + case XML_OTHER: + break; + } + } + + if (mainCfgs.empty()) + throw XmlError(_("Invalid FreeFileSync config file!")); + + try //...to init all non-"mainCfg" settings with first config file + { + xmlAccess::readConfig(filenames[0], config); //throw (xmlAccess::XmlError); + } + catch (...) {} + + config.mainCfg = merge(mainCfgs); + + if (savedException.get()) //"re-throw" exception + throw *savedException; +} +} + + +void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config) //throw (xmlAccess::XmlError) +{ + mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::XmlError) +} + + +void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config) //throw (xmlAccess::XmlError); +{ + mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::XmlError) +} |