Fixed issue #41. Fixed issue #11. Refactored the way settings are stored, this means that you cannot go back to a previous version if you have used this one. A backup of the old database will be created, if you need to use a previous version. Fixed a bug in the S3 backend where the filenames would be prefixed with a slash. Moved the file time seperator char out of the file backend, and into common settings. git-svn-id: https://duplicati.googlecode.com/svn/trunk@134 59da171f-624f-0410-aa54-27559c288bec
483 lines
25 KiB
C#
483 lines
25 KiB
C#
#region Disclaimer / License
|
|
// Copyright (C) 2009, 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;
|
|
|
|
namespace Duplicati.Library.Main
|
|
{
|
|
public class Options
|
|
{
|
|
private Dictionary<string, string> m_options;
|
|
|
|
public Options(Dictionary<string, string> options)
|
|
{
|
|
m_options = options;
|
|
}
|
|
|
|
public Dictionary<string, string> RawOptions { get { return m_options; } }
|
|
|
|
public IList<Backend.ICommandLineArgument> SupportedCommands
|
|
{
|
|
get
|
|
{
|
|
return new List<Backend.ICommandLineArgument>(new Backend.ICommandLineArgument[] {
|
|
new Backend.CommandLineArgument("full", Backend.CommandLineArgument.ArgumentType.Boolean, "A flag used to force full backups", "When this flag is specified, Duplicati will make a full backup of all files, and ignore any incremental data."),
|
|
new Backend.CommandLineArgument("volsize", Backend.CommandLineArgument.ArgumentType.Size, "A size string that limits the size of the volumes", "This option can change the default volume size. Changing the size can be usefull if the backend has a limit on the size of each individual file", "5mb"),
|
|
new Backend.CommandLineArgument("totalsize", Backend.CommandLineArgument.ArgumentType.Size, "The number of bytes generated by each backup run", "This option can place an upper limit on the total size of each backup. Note that if this flag is specified the backup may not contain all files, even for a full backup."),
|
|
new Backend.CommandLineArgument("auto-cleanup", Backend.CommandLineArgument.ArgumentType.Boolean, "A flag indiciating that Duplicati should remove unused files", "If a backup is interrupted there will likely be partial files present on the backend. Using this flag, Duplicati will automatically remove such files when encountered."),
|
|
new Backend.CommandLineArgument("full-if-older-than", Backend.CommandLineArgument.ArgumentType.Timespan, "The max duration between full backups", "If the last full backup is older than the duration supplied here, Duplicati will make a full backup, otherwise an incremental"),
|
|
|
|
new Backend.CommandLineArgument("signature-control-files", Backend.CommandLineArgument.ArgumentType.Path, "A list of control files to embed in the backups", "Supply a list of files seperated with semicolons, that will be added to each backup. The Duplicati GUI program uses this to store the setup database with each backup."),
|
|
new Backend.CommandLineArgument("signature-cache-path", Backend.CommandLineArgument.ArgumentType.Path, "A path to temporary storage", "If this path is supplied, Duplicati will store all signature files here, so re-downloads can be avoided."),
|
|
new Backend.CommandLineArgument("skip-file-hash-checks", Backend.CommandLineArgument.ArgumentType.Boolean, "Set this flag to skip hash checks", "If the hash for the volume does not match, Duplicati will refuse to use the backup. Supply this flag to allow Duplicati to proceed anyway."),
|
|
new Backend.CommandLineArgument("file-to-restore", Backend.CommandLineArgument.ArgumentType.String, "A list of files to restore", "By default, duplicati will restore all files in the backup. Use this option to restore only a subset of the files"),
|
|
new Backend.CommandLineArgument("restore-time", Backend.CommandLineArgument.ArgumentType.String, "The time to restore files", "By default, Duplicati will restore files from the most recent backup, use this option to select another item. You may use relative times, like \"-2M\" for a backup from two months ago.", "now"),
|
|
|
|
new Backend.CommandLineArgument("disable-filetime-check", Backend.CommandLineArgument.ArgumentType.String, "Disable checks based on file time", "The operating system keeps track of the last time a file was written. Using this information, Duplicati can quickly determine if the file has been modified. If some application deliberately modifies this information, Duplicati won't work correctly unless this flag is set."),
|
|
new Backend.CommandLineArgument("force", Backend.CommandLineArgument.ArgumentType.String, "Force the removal of files", "When deleting old files, Duplicati will only write out what files are supposed to be deleted. Specify the \"force\" option to actually remove them."),
|
|
new Backend.CommandLineArgument("tempdir", Backend.CommandLineArgument.ArgumentType.Path, "Temporary storage folder", "Duplicati will use the system default temporary folder. This option can be used to supply an alternative folder for temporary storage."),
|
|
new Backend.CommandLineArgument("thread-priority", Backend.CommandLineArgument.ArgumentType.Enumeration, "Thread priority", "Selects another thread priority for the process. Use this to set Duplicati to be more or less CPU intensive.", "normal", null, new string[] {"high", "abovenormal", "normal", "belownormal", "low", "idle" }),
|
|
|
|
new Backend.CommandLineArgument("backup-prefix", Backend.CommandLineArgument.ArgumentType.String, "Backup volume filename prefix", "Any string used to prefix the filenames generated by Duplicati.", "duplicati"),
|
|
new Backend.CommandLineArgument("time-separator", Backend.CommandLineArgument.ArgumentType.String, "Backup volume filename timeseperator", "Per default, Duplicati will use the colon \":\" character to seperate the time fields in the filename. However, on some filesystem, notably windows, this character is not allowed. Use this option to use another character."),
|
|
new Backend.CommandLineArgument("short-filenames", Backend.CommandLineArgument.ArgumentType.Boolean, "Use short filenames", "If the filesystem does not support the long filenames that Duplicati uses, this switch will change the time to be a much more compact, but hard to read format. It will also make the default prefix \"dpl\"."),
|
|
|
|
new Backend.CommandLineArgument("include", Backend.CommandLineArgument.ArgumentType.String, "Include files", "Include files that match this filter. The filter is a \"file-globbing\" filter, much like commandline options. You can use \"*.txt\" to include all text files."),
|
|
new Backend.CommandLineArgument("exclude", Backend.CommandLineArgument.ArgumentType.String, "Exclude files", "Include files that match this filter. The filter is a \"file-globbing\" filter, much like commandline options. You can use \"*.txt\" to exclude all text files."),
|
|
new Backend.CommandLineArgument("include-regexp", Backend.CommandLineArgument.ArgumentType.String, "Include files regular expression", "Include files that match this filter. The filter is a regular expression filter. You can use \"(.*)\\.txt\" to include all text files."),
|
|
new Backend.CommandLineArgument("exclude-regexp", Backend.CommandLineArgument.ArgumentType.String, "Exclude files regular expression", "Include files that match this filter. The filter is a regular expression filter. You can use \"(.*)\\.txt\" to exclude all text files."),
|
|
|
|
new Backend.CommandLineArgument("passphrase", Backend.CommandLineArgument.ArgumentType.String, "Passphrase used to encrypt backups", "Supply a passphrase that Duplicati will use to encrypt the backup volumes, making the unreadable without the passphrase."),
|
|
new Backend.CommandLineArgument("gpg-encryption", Backend.CommandLineArgument.ArgumentType.Boolean, "Use GnuPG for encryption", "By default, Duplicati will use the AES encryption algorithm to encrypt the backup volumes, setting this flag makes Duplicati use the GNU Privacy Guard instead. GnuPG must be installed on the machine for this to work."),
|
|
new Backend.CommandLineArgument("gpg-program-path", Backend.CommandLineArgument.ArgumentType.Path, "The path to GnuPG", "The path to the GNU Privacy Guard program. If not supplied, Duplicati will assume that the program \"gpg\" is avalible in the system path.", "gpg"),
|
|
new Backend.CommandLineArgument("sign-key", Backend.CommandLineArgument.ArgumentType.String, "Sign key for GnuPG", "The GNU Privacy Guard can optionally sign volumes with a special key. This feature is not currently active in Duplicati."),
|
|
new Backend.CommandLineArgument("no-encryption", Backend.CommandLineArgument.ArgumentType.Boolean, "Disable encryption", "If you store the backups on a local disk, and prefer that they are kept unencrypted, you can turn of encryption completely by using this switch."),
|
|
|
|
new Backend.CommandLineArgument("number-of-retries", Backend.CommandLineArgument.ArgumentType.Integer, "Number of times to retry a failed transmission", "If an upload or download fails, Duplicati will retry a number of times before failing. Use this to handle unstable network connections better.", "5"),
|
|
new Backend.CommandLineArgument("retry-delay", Backend.CommandLineArgument.ArgumentType.Timespan, "Time to wait between retries", "After a failed transmission, Duplicati will wait a short period before attempting again. This is usefull if the network drops out occasionally during transmissions.", "10s"),
|
|
new Backend.CommandLineArgument("asynchronous-upload", Backend.CommandLineArgument.ArgumentType.Boolean, "Transmit files on a seperate thread", "By supplying this option, Duplicati will transmit files, while building volumes. This can shorten the time it takes to perform a backup, but requires more diskspace.", "false"),
|
|
|
|
new Backend.CommandLineArgument("max-upload-pr-second", Backend.CommandLineArgument.ArgumentType.Size, "Max number of bytes to upload pr. second", "By setting this value you can limit how much bandwidth Duplicati consumes for uploads. Setting this limit can make the backups take longer, but will make Duplicati less intrusive."),
|
|
new Backend.CommandLineArgument("max-download-pr-second", Backend.CommandLineArgument.ArgumentType.Size, "Max number of bytes to download pr. second", "By setting this value you can limit how much bandwidth Duplicati consumes for downloads. Setting this limit can make the backups take longer, but will make Duplicati less intrusive."),
|
|
new Backend.CommandLineArgument("skip-files-larger-than", Backend.CommandLineArgument.ArgumentType.Size, "A size string that limits the size of files being backed up", "This option allows you to exclude files that are larger than the given value. Use this to prevent backups becoming extremely large."),
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A value indicating if the backup is a full backup
|
|
/// </summary>
|
|
public bool Full { get { return GetBool("full"); } }
|
|
|
|
/// <summary>
|
|
/// Gets the size of each volume in bytes
|
|
/// </summary>
|
|
public long VolumeSize
|
|
{
|
|
get
|
|
{
|
|
string volsize = "5mb";
|
|
if (m_options.ContainsKey("volsize"))
|
|
volsize = m_options["volsize"];
|
|
|
|
return Math.Max(1024 * 1024, Core.Sizeparser.ParseSize(volsize, "mb"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the total size in bytes allowed for a single backup run
|
|
/// </summary>
|
|
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"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the maximum size of a single file
|
|
/// </summary>
|
|
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");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the time at which a full backup should be performed
|
|
/// </summary>
|
|
/// <param name="offsettime">The time the last full backup was created</param>
|
|
/// <returns>The time at which a full backup should be performed</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// A value indicating if orphan files are deleted automatically
|
|
/// </summary>
|
|
public bool AutoCleanup { get { return GetBool("auto-cleanup"); } }
|
|
|
|
/// <summary>
|
|
/// Gets a list of files to add to the signature volumes
|
|
/// </summary>
|
|
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"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of files to add to the signature volumes
|
|
/// </summary>
|
|
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"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A value indicating if file hash checks are skipped
|
|
/// </summary>
|
|
public bool SkipFileHashChecks { get { return GetBool("skip-file-hash-checks"); } }
|
|
|
|
/// <summary>
|
|
/// Gets a list of files to restore
|
|
/// </summary>
|
|
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"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the backup that should be restored
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A value indicating if file time checks are skipped
|
|
/// </summary>
|
|
public bool DisableFiletimeCheck { get { return GetBool("disable-filetime-check"); } }
|
|
|
|
/// <summary>
|
|
/// A value indicating if file deletes are forced
|
|
/// </summary>
|
|
public bool Force { get { return GetBool("force"); } }
|
|
|
|
/// <summary>
|
|
/// Gets the folder where temporary files are stored
|
|
/// </summary>
|
|
public string TempDir
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("tempdir") || string.IsNullOrEmpty(m_options["tempdir"]))
|
|
return null;
|
|
else
|
|
return m_options["tempdir"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the process priority
|
|
/// </summary>
|
|
public string ThreadPriority
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("thread-priority") || string.IsNullOrEmpty(m_options["thread-priority"]))
|
|
return null;
|
|
else
|
|
return m_options["thread-priority"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A value indicating if file deletes are forced
|
|
/// </summary>
|
|
public bool UseShortFilenames { get { return GetBool("short-filenames"); } }
|
|
|
|
/// <summary>
|
|
/// Gets the backup prefix
|
|
/// </summary>
|
|
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"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the process priority
|
|
/// </summary>
|
|
public string TimeSeperatorChar
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("time-separator") || string.IsNullOrEmpty(m_options["time-separator"]))
|
|
return ":";
|
|
else
|
|
return m_options["time-separator"];
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Gets the filter used to include or exclude files
|
|
/// </summary>
|
|
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<KeyValuePair<bool, string>>());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a value indiciating if a filter is specified
|
|
/// </summary>
|
|
public bool HasFilter { get { return m_options.ContainsKey("filter"); } }
|
|
|
|
/// <summary>
|
|
/// Gets the number of old backups to keep
|
|
/// </summary>
|
|
public int RemoveAllButNFull
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("remove-all-but-n-full") || string.IsNullOrEmpty(m_options["remove-all-but-n-full"]))
|
|
throw new Exception("No count given for \"Remove All But N Full\"");
|
|
|
|
int x = int.Parse(m_options["remove-all-but-n-full"]);
|
|
if (x < 0)
|
|
throw new Exception("Invalid count for remove-all-but-n-full");
|
|
|
|
return x;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the timelimit for removal
|
|
/// </summary>
|
|
public DateTime RemoveOlderThan
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("remove-older-than"))
|
|
throw new Exception("No count given for \"Remove Older Than\"");
|
|
|
|
return Core.Timeparser.ParseTimeInterval(m_options["remove-older-than"], DateTime.Now, true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the encryption passphrase
|
|
/// </summary>
|
|
public string Passphrase
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("passphrase") || string.IsNullOrEmpty(m_options["passphrase"]))
|
|
return null;
|
|
else
|
|
return m_options["passphrase"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets GnuPG program path
|
|
/// </summary>
|
|
public string GPGPath
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("gpg-program-path") || string.IsNullOrEmpty(m_options["gpg-program-path"]))
|
|
return "gpg";
|
|
else
|
|
return m_options["gpg-program-path"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the GPG sign key
|
|
/// </summary>
|
|
public string GPGSignKey
|
|
{
|
|
get
|
|
{
|
|
if (!m_options.ContainsKey("sign-key") || string.IsNullOrEmpty(m_options["sign-key"]))
|
|
return null;
|
|
else
|
|
return m_options["sign-key"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A value indicating if backups are not encrypted
|
|
/// </summary>
|
|
public bool NoEncryption { get { return GetBool("no-encryption"); } }
|
|
|
|
/// <summary>
|
|
/// A value indicating if GPG encryption is used
|
|
/// </summary>
|
|
public bool GPGEncryption
|
|
{
|
|
get { return GetBool("gpg-encryption"); }
|
|
set { m_options["gpg-encryption"] = value.ToString(); }
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Gets the number of time to retry transmission if it fails
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A value indicating if backups are transmitted on a seperate thread
|
|
/// </summary>
|
|
public bool AsynchronousUpload { get { return GetBool("asynchronous-upload"); } }
|
|
|
|
|
|
/// <summary>
|
|
/// Gets the timelimit for removal
|
|
/// </summary>
|
|
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"]);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the max upload speed in bytes pr. second
|
|
/// </summary>
|
|
public long MaxUploadPrSecond
|
|
{
|
|
get
|
|
{
|
|
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");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the max download speed in bytes pr. second
|
|
/// </summary>
|
|
public long MaxDownloadPrSecond
|
|
{
|
|
get
|
|
{
|
|
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");
|
|
}
|
|
}
|
|
|
|
private bool GetBool(string name)
|
|
{
|
|
if (!m_options.ContainsKey(name))
|
|
return false;
|
|
else
|
|
{
|
|
string v = m_options[name];
|
|
if (string.IsNullOrEmpty(v))
|
|
return true;
|
|
else
|
|
{
|
|
v = v.ToLower().Trim();
|
|
if (v == "false" || v == "no" || v == "off")
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|