#region Disclaimer / License // Copyright (C) 2010, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Text; using Duplicati.Library.Interface; namespace Duplicati.Library.Main { /// /// A class for keeping all Duplicati options in one place, /// and provide typesafe access to the options /// public class Options { /// /// An enumeration that describes the supported snapshot modes /// public enum SnapShotMode { /// /// A snapshot is created if possible, but silently ignored if it fails to start /// Auto, /// /// A snapshot is created if possible, but an error is logged if it fails to start /// On, /// /// Snapshots are deactivated /// Off, /// /// A snapshot is created, and the backup is aborted if it fails /// Required } /// /// Lock that protects the options collection /// private object m_lock = new object(); private Dictionary m_options; private List> m_loadedModules = new List>(); public Options(Dictionary options) { m_options = options; } public Dictionary RawOptions { get { return m_options; } } /// /// Returns a list of strings that are not supported on the commandline as options, but used internally /// public static string[] InternalOptions { get { return new string[] { "restore", "delete-older-than", "delete-all-but-n-full", "filter" }; } } public IList SupportedCommands { get { return new List(new ICommandLineArgument[] { new CommandLineArgument("full", CommandLineArgument.ArgumentType.Boolean, Strings.Options.FullShort, Strings.Options.FullLong), new CommandLineArgument("volsize", CommandLineArgument.ArgumentType.Size, Strings.Options.VolsizeShort, Strings.Options.VolsizeLong, "5mb"), new CommandLineArgument("totalsize", CommandLineArgument.ArgumentType.Size, Strings.Options.TotalsizeShort, Strings.Options.TotalsizeLong), new CommandLineArgument("auto-cleanup", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AutocleanupShort, Strings.Options.AutocleanupLong), new CommandLineArgument("full-if-older-than", CommandLineArgument.ArgumentType.Timespan, Strings.Options.FullifolderthanShort, Strings.Options.FullifolderthanLong), new CommandLineArgument("allow-full-removal", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AllowfullremoveShort, Strings.Options.AllowfullremoveLong), new CommandLineArgument("signature-control-files", CommandLineArgument.ArgumentType.Path, Strings.Options.SignaturecontrolfilesShort, Strings.Options.SignaturecontrolfilesLong), new CommandLineArgument("signature-cache-path", CommandLineArgument.ArgumentType.Path, Strings.Options.SignaturecachepathShort, Strings.Options.SignaturecachepathLong), new CommandLineArgument("skip-file-hash-checks", CommandLineArgument.ArgumentType.Boolean, Strings.Options.SkipfilehashchecksShort, Strings.Options.SkipfilehashchecksLong), new CommandLineArgument("dont-read-manifests", CommandLineArgument.ArgumentType.Boolean, Strings.Options.DontreadmanifestsShort, Strings.Options.DontreadmanifestsLong), new CommandLineArgument("file-to-restore", CommandLineArgument.ArgumentType.String, Strings.Options.FiletorestoreShort, Strings.Options.FiletorestoreLong), new CommandLineArgument("restore-time", CommandLineArgument.ArgumentType.String, Strings.Options.RestoretimeShort, Strings.Options.RestoretimeLong, "now"), new CommandLineArgument("disable-filetime-check", CommandLineArgument.ArgumentType.String, Strings.Options.DisablefiletimecheckShort, Strings.Options.DisablefiletimecheckLong), new CommandLineArgument("force", CommandLineArgument.ArgumentType.String, Strings.Options.ForceShort, Strings.Options.ForceLong), new CommandLineArgument("tempdir", CommandLineArgument.ArgumentType.Path, Strings.Options.TempdirShort, Strings.Options.TempdirLong), new CommandLineArgument("thread-priority", CommandLineArgument.ArgumentType.Enumeration, Strings.Options.ThreadpriorityShort, Strings.Options.ThreadpriorityLong, "normal", null, new string[] {"high", "abovenormal", "normal", "belownormal", "low", "idle" }), new CommandLineArgument("backup-prefix", CommandLineArgument.ArgumentType.String, Strings.Options.BackupprefixShort, Strings.Options.BackupprefixLong, "duplicati"), new CommandLineArgument("time-separator", CommandLineArgument.ArgumentType.String, Strings.Options.TimeseparatorShort, Strings.Options.TimeseparatorLong, ":", new string[] {"time-seperator"}), new CommandLineArgument("short-filenames", CommandLineArgument.ArgumentType.Boolean, Strings.Options.ShortfilenamesShort, Strings.Options.ShortfilenamesLong), new CommandLineArgument("include", CommandLineArgument.ArgumentType.String, Strings.Options.IncludeShort, Strings.Options.IncludeLong), new CommandLineArgument("exclude", CommandLineArgument.ArgumentType.String, Strings.Options.ExcludeShort, Strings.Options.ExcludeLong), new CommandLineArgument("include-regexp", CommandLineArgument.ArgumentType.String, Strings.Options.IncluderegexpShort, Strings.Options.IncluderegexpLong), new CommandLineArgument("exclude-regexp", CommandLineArgument.ArgumentType.String, Strings.Options.ExcluderegexpShort, Strings.Options.ExcluderegexpLong), new CommandLineArgument("passphrase", CommandLineArgument.ArgumentType.String, Strings.Options.PassphraseShort, Strings.Options.PassphraseLong), new CommandLineArgument("gpg-encryption", CommandLineArgument.ArgumentType.Boolean, Strings.Options.GpgencryptionShort, Strings.Options.GpgencryptionLong, "false", null, null, Strings.Options.GpgencryptionDeprecated), new CommandLineArgument("no-encryption", CommandLineArgument.ArgumentType.Boolean, Strings.Options.NoencryptionShort, Strings.Options.NoencryptionLong, "false"), new CommandLineArgument("number-of-retries", CommandLineArgument.ArgumentType.Integer, Strings.Options.NumberofretriesShort, Strings.Options.NumberofretriesLong, "5"), new CommandLineArgument("retry-delay", CommandLineArgument.ArgumentType.Timespan, Strings.Options.RetrydelayShort, Strings.Options.RetrydelayLong, "10s"), new CommandLineArgument("asynchronous-upload", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AsynchronousuploadShort, Strings.Options.AsynchronousuploadLong, "false"), new CommandLineArgument("asynchronous-upload-limit", CommandLineArgument.ArgumentType.Integer, Strings.Options.AsynchronousuploadlimitShort, Strings.Options.AsynchronousuploadlimitLong, "2"), new CommandLineArgument("asynchronous-upload-folder", CommandLineArgument.ArgumentType.Path, Strings.Options.AsynchronousuploadfolderShort, Strings.Options.AsynchronousuploadfolderLong), new CommandLineArgument("disable-streaming-transfers", CommandLineArgument.ArgumentType.Boolean, Strings.Options.DisableStreamingShort, Strings.Options.DisableStreamingLong, "false"), new CommandLineArgument("max-upload-pr-second", CommandLineArgument.ArgumentType.Size, Strings.Options.MaxuploadprsecondShort, Strings.Options.MaxuploadprsecondLong), new CommandLineArgument("max-download-pr-second", CommandLineArgument.ArgumentType.Size, Strings.Options.MaxdownloadprsecondShort, Strings.Options.MaxdownloadprsecondLong), new CommandLineArgument("skip-files-larger-than", CommandLineArgument.ArgumentType.Size, Strings.Options.SkipfileslargerthanShort, Strings.Options.SkipfileslargerthanLong), new CommandLineArgument("allow-sourcefolder-change", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AllowsourcefolderchangeShort, Strings.Options.AllowsourcefolderchangeLong, "false"), new CommandLineArgument("full-if-sourcefolder-changed", CommandLineArgument.ArgumentType.Boolean, Strings.Options.FullifsourcefolderchangedShort, Strings.Options.FullifsourcefolderchangedLong, "false"), new CommandLineArgument("snapshot-policy", CommandLineArgument.ArgumentType.Enumeration, Strings.Options.SnapshotpolicyShort, Strings.Options.SnapshotpolicyLong, "off", null, new string[] {"auto", "off", "on", "required"}), new CommandLineArgument("encryption-module", CommandLineArgument.ArgumentType.String, Strings.Options.EncryptionmoduleShort, Strings.Options.EncryptionmoduleLong, "aes"), new CommandLineArgument("compression-module", CommandLineArgument.ArgumentType.String, Strings.Options.CompressionmoduleShort, Strings.Options.CompressionmoduleLong, "zip"), new CommandLineArgument("enable-module", CommandLineArgument.ArgumentType.String, Strings.Options.EnablemoduleShort, Strings.Options.EnablemoduleLong), new CommandLineArgument("disable-module", CommandLineArgument.ArgumentType.String, Strings.Options.DisablemoduleShort, Strings.Options.DisablemoduleLong), new CommandLineArgument("debug-output", CommandLineArgument.ArgumentType.Boolean, Strings.Options.DebugoutputShort, Strings.Options.DebugoutputLong), }); } } /// /// A value indicating if the backup is a full backup /// public bool Full { get { return GetBool("full"); } } /// /// Gets the size of each volume in bytes /// public long VolumeSize { get { string volsize = "5mb"; if (m_options.ContainsKey("volsize")) volsize = m_options["volsize"]; #if DEBUG return Math.Max(1024 * 10, Core.Sizeparser.ParseSize(volsize, "mb")); #else return Math.Max(1024 * 1024, Core.Sizeparser.ParseSize(volsize, "mb")); #endif } } /// /// Gets the total size in bytes allowed for a single backup run /// public long MaxSize { get { if (!m_options.ContainsKey("totalsize") || string.IsNullOrEmpty(m_options["totalsize"])) return long.MaxValue; else return Math.Max(VolumeSize, Core.Sizeparser.ParseSize(m_options["totalsize"], "mb")); } } /// /// Gets the maximum size of a single file /// public long SkipFilesLargerThan { get { if (!m_options.ContainsKey("skip-files-larger-than") || string.IsNullOrEmpty(m_options["skip-files-larger-than"])) return long.MaxValue; else return Core.Sizeparser.ParseSize(m_options["skip-files-larger-than"], "mb"); } } /// /// Gets the time at which a full backup should be performed /// /// The time the last full backup was created /// The time at which a full backup should be performed public DateTime FullIfOlderThan(DateTime offsettime) { if (!m_options.ContainsKey("full-if-older-than") || string.IsNullOrEmpty(m_options["full-if-older-than"])) return DateTime.Now.AddYears(1); //We assume that the check will occur in less than one year :) else return Core.Timeparser.ParseTimeInterval(m_options["full-if-older-than"], offsettime); } /// /// A value indicating if orphan files are deleted automatically /// public bool AutoCleanup { get { return GetBool("auto-cleanup"); } } /// /// Gets a list of files to add to the signature volumes /// public string SignatureControlFiles { get { if (!m_options.ContainsKey("signature-control-files") || string.IsNullOrEmpty(m_options["signature-control-files"])) return null; else return m_options["signature-control-files"]; } } /// /// Gets a list of files to add to the signature volumes /// public string SignatureCachePath { get { if (!m_options.ContainsKey("signature-cache-path") || string.IsNullOrEmpty(m_options["signature-cache-path"])) return null; else return m_options["signature-cache-path"]; } } /// /// A value indicating if file hash checks are skipped /// public bool SkipFileHashChecks { get { return GetBool("skip-file-hash-checks"); } } /// /// A value indicating if the manifest files are not read /// public bool DontReadManifests { get { return GetBool("dont-read-manifests"); } } /// /// A value indicating if the source folder is allowed to change /// public bool AllowSourceFolderChange { get { return GetBool("allow-sourcefolder-change"); } } /// /// A value indicating if the backup should be a full backup if the source folder has changed /// public bool FullIfSourceFolderChanged { get { return GetBool("full-if-sourcefolder-changed"); } } /// /// Gets a list of files to restore /// public string FileToRestore { get { if (!m_options.ContainsKey("file-to-restore") || string.IsNullOrEmpty(m_options["file-to-restore"])) return null; else return m_options["file-to-restore"]; } } /// /// Gets the backup that should be restored /// public DateTime RestoreTime { get { if (!m_options.ContainsKey("restore-time") || string.IsNullOrEmpty(m_options["restore-time"])) return DateTime.Now.AddYears(1); //We assume that the check will occur in less than one year :) else return Core.Timeparser.ParseTimeInterval(m_options["restore-time"], DateTime.Now); } } /// /// A value indicating if file time checks are skipped /// public bool DisableFiletimeCheck { get { return GetBool("disable-filetime-check"); } } /// /// A value indicating if file deletes are forced /// public bool Force { get { return GetBool("force"); } } /// /// Gets the folder where temporary files are stored /// public string TempDir { get { if (!m_options.ContainsKey("tempdir") || string.IsNullOrEmpty(m_options["tempdir"])) return null; else return m_options["tempdir"]; } } /// /// Gets the process priority /// public string ThreadPriority { get { if (!m_options.ContainsKey("thread-priority") || string.IsNullOrEmpty(m_options["thread-priority"])) return null; else return m_options["thread-priority"]; } } /// /// A value indicating if file deletes are forced /// public bool UseShortFilenames { get { return GetBool("short-filenames"); } } /// /// Gets the backup prefix /// public string BackupPrefix { get { if (!m_options.ContainsKey("backup-prefix") || string.IsNullOrEmpty(m_options["backup-prefix"])) return this.UseShortFilenames ? "dpl" : "duplicati"; else return m_options["backup-prefix"]; } } /// /// Gets the process priority /// public string TimeSeperatorChar { get { if (!m_options.ContainsKey("time-separator") || string.IsNullOrEmpty(m_options["time-separator"])) if (!m_options.ContainsKey("time-seperator") || string.IsNullOrEmpty(m_options["time-seperator"])) return ":"; else return m_options["time-seperator"]; else return m_options["time-separator"]; } } /// /// Gets the filter used to include or exclude files /// public Core.FilenameFilter Filter { get { if (m_options.ContainsKey("filter") && !string.IsNullOrEmpty(m_options["filter"])) return new Duplicati.Library.Core.FilenameFilter(Core.FilenameFilter.DecodeFilter(m_options["filter"])); else return new Duplicati.Library.Core.FilenameFilter(new List>()); } } /// /// Returns a value indiciating if a filter is specified /// public bool HasFilter { get { return m_options.ContainsKey("filter"); } } /// /// Gets the number of old backups to keep /// public int RemoveAllButNFull { get { if (!m_options.ContainsKey("delete-all-but-n-full") || string.IsNullOrEmpty(m_options["delete-all-but-n-full"])) throw new Exception("No count given for \"Delete All But N Full\""); int x = int.Parse(m_options["delete-all-but-n-full"]); if (x < 0) throw new Exception("Invalid count for delete-all-but-n-full, must be greater than zero"); return x; } } /// /// Gets the timelimit for removal /// public DateTime RemoveOlderThan { get { if (!m_options.ContainsKey("delete-older-than")) throw new Exception("No count given for \"Delete Older Than\""); return Core.Timeparser.ParseTimeInterval(m_options["delete-older-than"], DateTime.Now, true); } } /// /// Gets the encryption passphrase /// public string Passphrase { get { if (!m_options.ContainsKey("passphrase") || string.IsNullOrEmpty(m_options["passphrase"])) return null; else return m_options["passphrase"]; } } /// /// A value indicating if backups are not encrypted /// public bool NoEncryption { get { return GetBool("no-encryption"); } } /// /// Gets the module used for encryption /// public string EncryptionModule { get { //Disabled? if (NoEncryption) return null; //Specified? if (m_options.ContainsKey("encryption-module")) return m_options["encryption-module"]; //Default, support the deprecated --gpg-encryption flag if (GPGEncryption) return "gpg"; else return "aes"; } } /// /// [DEPRECATED] A value indicating if GPG encryption is used /// private bool GPGEncryption { get { return GetBool("gpg-encryption"); } set { m_options["gpg-encryption"] = value.ToString(); } } /// /// Gets the module used for compression /// public string CompressionModule { get { if (m_options.ContainsKey("compression-module")) return m_options["compression-module"]; else return "zip"; } } /// /// Gets the number of time to retry transmission if it fails /// public int NumberOfRetries { get { if (!m_options.ContainsKey("number-of-retries") || string.IsNullOrEmpty(m_options["number-of-retries"])) return 5; else { int x = int.Parse(m_options["number-of-retries"]); if (x < 0) throw new Exception("Invalid count for number-of-retries"); return x; } } } /// /// A value indicating if backups are transmitted on a seperate thread /// public bool AsynchronousUpload { get { return GetBool("asynchronous-upload"); } } /// /// A value indicating if use of the streaming interface is disallowed /// public bool DisableStreamingTransfers { get { return GetBool("disable-streaming-transfers"); } } /// /// Gets the timelimit for removal /// public TimeSpan RetryDelay { get { if (!m_options.ContainsKey("retry-delay") || string.IsNullOrEmpty(m_options["retry-delay"])) return new TimeSpan(TimeSpan.TicksPerSecond * 10); else return Core.Timeparser.ParseTimeSpan(m_options["retry-delay"]); } } /// /// Gets the max upload speed in bytes pr. second /// public long MaxUploadPrSecond { get { lock(m_lock) if (!m_options.ContainsKey("max-upload-pr-second") || string.IsNullOrEmpty(m_options["max-upload-pr-second"])) return 0; else return Core.Sizeparser.ParseSize(m_options["max-upload-pr-second"], "kb"); } set { lock (m_lock) if (value <= 0) m_options["max-upload-pr-second"] = ""; else m_options["max-upload-pr-second"] = value.ToString() + "b"; } } /// /// Gets or sets the max download speed in bytes pr. second /// public long MaxDownloadPrSecond { get { lock (m_lock) if (!m_options.ContainsKey("max-download-pr-second") || string.IsNullOrEmpty(m_options["max-download-pr-second"])) return 0; else return Core.Sizeparser.ParseSize(m_options["max-download-pr-second"], "kb"); } set { lock (m_lock) if (value <= 0) m_options["max-download-pr-second"] = ""; else m_options["max-download-pr-second"] = value.ToString() + "b"; } } /// /// A value indicating if the backup is a full backup /// public bool AllowFullRemoval { get { return GetBool("allow-full-removal"); } } /// /// A value indicating if debug output is enabled /// public bool DebugOutput { get { return GetBool("debug-output"); } } /// /// Gets a list of modules that should be loaded /// public string[] EnableModules { get { if (m_options.ContainsKey("enable-module")) return m_options["enable-module"].Trim().ToLower().Split(','); else return new string[0]; } } /// /// Gets a list of modules that should not be loaded /// public string[] DisableModules { get { if (m_options.ContainsKey("disable-module")) return m_options["disable-module"].Trim().ToLower().Split(','); else return new string[0]; } } public SnapShotMode SnapShotStrategy { get { string strategy; if (!m_options.TryGetValue("snapshot-policy", out strategy)) strategy = ""; if (string.Equals(strategy, "on", StringComparison.InvariantCultureIgnoreCase)) return SnapShotMode.On; else if (string.Equals(strategy, "off", StringComparison.InvariantCultureIgnoreCase)) return SnapShotMode.Off; else if (string.Equals(strategy, "required", StringComparison.InvariantCultureIgnoreCase)) return SnapShotMode.Required; else return SnapShotMode.Off; } } /// /// Gets the number of volumes to create ahead of time when using async transfers, /// a value of zero indicates no limit /// public long AsynchronousUploadLimit { get { string value; if (!m_options.TryGetValue("asynchronous-upload-limit", out value)) value = null; if (string.IsNullOrEmpty(value)) return 2; else return long.Parse(value); } } /// /// Gets the temporary folder to use for asyncronous transfers /// public string AsynchronousUploadFolder { get { string value; if (!m_options.TryGetValue("asynchronous-upload-folder", out value)) value = null; if (string.IsNullOrEmpty(value)) return this.TempDir ?? Core.TempFolder.SystemTempPath; else return value; } } /// /// Gets a list of modules, the key indicates if they are loaded /// public List> LoadedModules { get { return m_loadedModules; } } private bool GetBool(string name) { if (!m_options.ContainsKey(name)) return false; else return Core.Utility.ParseBool(m_options[name], true); } } }