diff --git a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
index f1960f082..c3f4f7e19 100644
--- a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
+++ b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
@@ -18,6 +18,7 @@
v4.5false
+ truetrue
diff --git a/Duplicati/CommandLine/BackendTester/Program.cs b/Duplicati/CommandLine/BackendTester/Program.cs
index 47a578d3e..fad9b76df 100644
--- a/Duplicati/CommandLine/BackendTester/Program.cs
+++ b/Duplicati/CommandLine/BackendTester/Program.cs
@@ -68,7 +68,7 @@ namespace Duplicati.CommandLine.BackendTester
var p = Library.Utility.Utility.ExpandEnvironmentVariables(_args[0]);
if (System.IO.File.Exists(p))
_args = (from x in System.IO.File.ReadLines(p)
- where !string.IsNullOrWhiteSpace(x) && !x.Trim().StartsWith("#")
+ where !string.IsNullOrWhiteSpace(x) && !x.Trim().StartsWith("#", StringComparison.Ordinal)
select x.Trim()
).ToArray();
}
@@ -121,7 +121,7 @@ namespace Duplicati.CommandLine.BackendTester
}
catch (Exception ex)
{
- Console.WriteLine("Unittest failed: " + ex.ToString());
+ Console.WriteLine("Unittest failed: " + ex);
}
}
@@ -325,7 +325,7 @@ namespace Duplicati.CommandLine.BackendTester
}
if (e != null)
- Console.WriteLine("failed\n*** Error: {0}", e.ToString());
+ Console.WriteLine("failed\n*** Error: {0}", e);
else
Console.WriteLine("done");
@@ -350,7 +350,7 @@ namespace Duplicati.CommandLine.BackendTester
try { backend.Delete(tx.remotefilename); }
catch (Exception ex)
{
- Console.WriteLine("*** Failed to delete file {0}, message: {1}", tx.remotefilename, ex.ToString());
+ Console.WriteLine("*** Failed to delete file {0}, message: {1}", tx.remotefilename, ex);
}
curlist = backend.List();
@@ -397,11 +397,11 @@ namespace Duplicati.CommandLine.BackendTester
if (e != null)
{
- Console.WriteLine("Failed to upload file {0}, error message: {1}, remote name: {2}", i, e.ToString(), remotefilename);
+ Console.WriteLine("Failed to upload file {0}, error message: {1}, remote name: {2}", i, e, remotefilename);
while (e.InnerException != null)
{
e = e.InnerException;
- Console.WriteLine(string.Format(" Inner exception: {0}", e.ToString()));
+ Console.WriteLine(string.Format(" Inner exception: {0}", e));
}
}
else
diff --git a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
index 089ff1686..e92114664 100644
--- a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
+++ b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
@@ -12,6 +12,7 @@
v4.5false
+ truetrue
diff --git a/Duplicati/CommandLine/BackendTool/Program.cs b/Duplicati/CommandLine/BackendTool/Program.cs
index 2f8054575..ca25f403b 100644
--- a/Duplicati/CommandLine/BackendTool/Program.cs
+++ b/Duplicati/CommandLine/BackendTool/Program.cs
@@ -158,7 +158,7 @@ namespace Duplicati.CommandLine.BackendTool
{
Console.WriteLine("Command failed: " + ex.Message);
if (debugoutput || !(ex is UserInformationException))
- Console.WriteLine(ex.ToString());
+ Console.WriteLine(ex);
return 100;
}
}
diff --git a/Duplicati/CommandLine/Commands.cs b/Duplicati/CommandLine/Commands.cs
index ed248d367..30c3fe662 100644
--- a/Duplicati/CommandLine/Commands.cs
+++ b/Duplicati/CommandLine/Commands.cs
@@ -260,7 +260,7 @@ namespace Duplicati.CommandLine
options["version"] = v.ToString();
}
}
- else if (args[0].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[0].StartsWith("["))
+ else if (args[0].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[0].StartsWith("[", StringComparison.Ordinal))
{
try
{
@@ -277,7 +277,7 @@ namespace Duplicati.CommandLine
// Prefix all filenames with "*/" so we search all folders
for(var ix = 0; ix < args.Count; ix++)
- if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("["))
+ if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("[", StringComparison.Ordinal))
args[ix] = "*" + System.IO.Path.DirectorySeparatorChar.ToString() + args[ix];
// Support for not adding the --auth-username if possible
@@ -360,7 +360,7 @@ namespace Duplicati.CommandLine
var f = res.Filesets.First();
outwriter.WriteLine("Listing contents {0} ({1}):", f.Version, f.Time);
foreach(var e in res.Files)
- outwriter.WriteLine("{0} {1}", e.Path, e.Path.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()) ? "" : "(" + Library.Utility.Utility.FormatSizeString(e.Sizes.First()) + ")");
+ outwriter.WriteLine("{0} {1}", e.Path, e.Path.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) ? "" : "(" + Library.Utility.Utility.FormatSizeString(e.Sizes.First()) + ")");
}
else
{
@@ -444,12 +444,12 @@ namespace Duplicati.CommandLine
// Prefix all filenames with "*/" so we search all folders
for (var ix = 0; ix < args.Count; ix++)
- if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("["))
+ if (args[ix].IndexOfAny(new char[] { '*', '?', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }) < 0 && !args[ix].StartsWith("[", StringComparison.Ordinal))
args[ix] = "*" + System.IO.Path.DirectorySeparatorChar.ToString() + args[ix];
// suffix all folders with "*" so we restore all contents in the folder
for (var ix = 0; ix < args.Count; ix++)
- if (args[ix].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[ix].StartsWith("[") && args[ix].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
+ if (args[ix].IndexOfAny(new char[] { '*', '?' }) < 0 && !args[ix].StartsWith("[", StringComparison.Ordinal) && args[ix].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
args[ix] += "*";
var output = new ConsoleOutput(outwriter, options);
diff --git a/Duplicati/CommandLine/Duplicati.CommandLine.csproj b/Duplicati/CommandLine/Duplicati.CommandLine.csproj
index a873c0467..b14f49867 100644
--- a/Duplicati/CommandLine/Duplicati.CommandLine.csproj
+++ b/Duplicati/CommandLine/Duplicati.CommandLine.csproj
@@ -19,6 +19,7 @@
v4.5false
+ truetrue
diff --git a/Duplicati/CommandLine/Help.cs b/Duplicati/CommandLine/Help.cs
index c088eb0c7..88350d6cb 100644
--- a/Duplicati/CommandLine/Help.cs
+++ b/Duplicati/CommandLine/Help.cs
@@ -22,10 +22,10 @@ namespace Duplicati.CommandLine
StringBuilder sb = new StringBuilder();
foreach(var line in sr.ReadToEnd().Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.None))
{
- if (line.Trim().StartsWith("#"))
+ if (line.Trim().StartsWith("#", StringComparison.Ordinal))
continue;
- if (line.Trim().StartsWith(">"))
+ if (line.Trim().StartsWith(">", StringComparison.Ordinal))
{
if (sb.Length > 0)
{
@@ -410,13 +410,13 @@ namespace Duplicati.CommandLine
string c = s;
string leadingSpaces = "";
- while (c.Length > 0 && c.StartsWith(" "))
+ while (c.Length > 0 && c.StartsWith(" ", StringComparison.Ordinal))
{
leadingSpaces += " ";
c = c.Remove(0, 1);
}
- bool extraIndent = c.StartsWith("--");
+ bool extraIndent = c.StartsWith("--", StringComparison.Ordinal);
while (c.Length > 0)
{
@@ -424,7 +424,7 @@ namespace Duplicati.CommandLine
len -= leadingSpaces.Length;
if (len < c.Length)
{
- int ix = c.LastIndexOf(" ", len);
+ int ix = c.LastIndexOf(" ", len, StringComparison.Ordinal);
if (ix > 0)
len = ix;
}
diff --git a/Duplicati/CommandLine/Program.cs b/Duplicati/CommandLine/Program.cs
index 3d6239d9e..a9ce8e319 100644
--- a/Duplicati/CommandLine/Program.cs
+++ b/Duplicati/CommandLine/Program.cs
@@ -270,7 +270,7 @@ namespace Duplicati.CommandLine
else if (!(ex is Library.Interface.CancelException))
{
errwriter.WriteLine();
- errwriter.WriteLine(ex.ToString());
+ errwriter.WriteLine(ex);
}
else
{
diff --git a/Duplicati/CommandLine/RecoveryTool/Download.cs b/Duplicati/CommandLine/RecoveryTool/Download.cs
index 2a22a36ac..28998f7c1 100644
--- a/Duplicati/CommandLine/RecoveryTool/Download.cs
+++ b/Duplicati/CommandLine/RecoveryTool/Download.cs
@@ -122,7 +122,7 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch(Exception ex)
{
- Console.WriteLine(" error: {0}", ex.ToString());
+ Console.WriteLine(" error: {0}", ex);
errors++;
}
diff --git a/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj b/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
index 892ed39f2..5b3bcc943 100644
--- a/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
+++ b/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
@@ -11,6 +11,7 @@
Duplicati.snkv4.5false
+ truetrue
diff --git a/Duplicati/CommandLine/RecoveryTool/Index.cs b/Duplicati/CommandLine/RecoveryTool/Index.cs
index 6b3eb1540..b09413e60 100644
--- a/Duplicati/CommandLine/RecoveryTool/Index.cs
+++ b/Duplicati/CommandLine/RecoveryTool/Index.cs
@@ -49,7 +49,7 @@ namespace Duplicati.CommandLine.RecoveryTool
ixfile = Path.GetFullPath(ixfile);
if (!File.Exists(ixfile))
{
- using(File.Create(ixfile))
+ using (File.Create(ixfile))
{
}
}
@@ -67,7 +67,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var errors = 0;
var totalblocks = 0L;
var files = 0;
- foreach(var file in Directory.EnumerateFiles(folder))
+ foreach (var file in Directory.EnumerateFiles(folder))
{
Console.Write("{0}: {1}", i, file);
@@ -95,11 +95,12 @@ namespace Duplicati.CommandLine.RecoveryTool
var filekey = Path.GetFileName(file);
var blocks = 0;
- using(var cp = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, file, options))
- using(var tf = new Library.Utility.TempFile())
+ using (var stream = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (var cp = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, stream, Library.Interface.ArchiveMode.Read, options))
+ using (var tf = new Library.Utility.TempFile())
{
- using(var sw = new StreamWriter(tf))
- foreach(var f in cp.ListFiles(null))
+ using (var sw = new StreamWriter(tf))
+ foreach (var f in cp.ListFiles(null))
{
sw.WriteLine("{0}, {1}", Library.Utility.Utility.Base64UrlToBase64Plain(f), filekey);
blocks++;
@@ -121,9 +122,9 @@ namespace Duplicati.CommandLine.RecoveryTool
Console.WriteLine(" done!");
}
}
- catch(Exception ex)
+ catch (Exception ex)
{
- Console.WriteLine(" error: {0}", ex.ToString());
+ Console.WriteLine(" error: {0}", ex);
errors++;
}
@@ -142,7 +143,7 @@ namespace Duplicati.CommandLine.RecoveryTool
try
{
// If the file can fit into memory, this is MUCH faster
- using(var tfout = new Library.Utility.TempFile())
+ using (var tfout = new Library.Utility.TempFile())
{
var data = File.ReadAllLines(filein);
Array.Sort(data, StringComparer.Ordinal);
@@ -156,8 +157,8 @@ namespace Duplicati.CommandLine.RecoveryTool
{
}
- using(var tfin = new Library.Utility.TempFile())
- using(var tfout = new Library.Utility.TempFile())
+ using (var tfin = new Library.Utility.TempFile())
+ using (var tfout = new Library.Utility.TempFile())
{
long swaps;
@@ -167,15 +168,15 @@ namespace Duplicati.CommandLine.RecoveryTool
{
swaps = 0L;
- using(var sw = new System.IO.StreamWriter(tfout))
- using(var sr = new System.IO.StreamReader(tfin))
+ using (var sw = new System.IO.StreamWriter(tfout))
+ using (var sr = new System.IO.StreamReader(tfin))
{
var c1 = sr.ReadLine();
var c2 = sr.ReadLine();
while (c1 != null || c2 != null)
{
- if (c1 != null && c1.StartsWith("a"))
+ if (c1 != null && c1.StartsWith("a", StringComparison.Ordinal))
Console.Write("");
var cmp = StringComparer.Ordinal.Compare(c1, c2);
@@ -198,7 +199,7 @@ namespace Duplicati.CommandLine.RecoveryTool
File.Copy(tfout, tfin, true);
- } while(swaps > 0);
+ } while (swaps > 0);
File.Copy(tfout, fileout, true);
}
@@ -206,16 +207,16 @@ namespace Duplicati.CommandLine.RecoveryTool
private static void MergeFiles(string file1, string file2, string fileout)
{
- using(var tf = new Library.Utility.TempFile())
+ using (var tf = new Library.Utility.TempFile())
{
- using(var sw = new System.IO.StreamWriter(tf))
- using(var sr1 = new System.IO.StreamReader(file1))
- using(var sr2 = new System.IO.StreamReader(file2))
+ using (var sw = new System.IO.StreamWriter(tf))
+ using (var sr1 = new System.IO.StreamReader(file1))
+ using (var sr2 = new System.IO.StreamReader(file2))
{
var c1 = sr1.ReadLine();
var c2 = sr2.ReadLine();
- while(c1 != null || c2 != null)
+ while (c1 != null || c2 != null)
{
var cmp = StringComparer.Ordinal.Compare(c1, c2);
diff --git a/Duplicati/CommandLine/RecoveryTool/List.cs b/Duplicati/CommandLine/RecoveryTool/List.cs
index d14640f14..fb3cc7408 100644
--- a/Duplicati/CommandLine/RecoveryTool/List.cs
+++ b/Duplicati/CommandLine/RecoveryTool/List.cs
@@ -44,7 +44,7 @@ namespace Duplicati.CommandLine.RecoveryTool
if (args.Count == 2)
{
var times = ParseListFiles(folder);
- foreach(var v in times.Zip(Enumerable.Range(0, times.Length), (a,b) => new KeyValuePair(b, a.Key)))
+ foreach (var v in times.Zip(Enumerable.Range(0, times.Length), (a, b) => new KeyValuePair(b, a.Key)))
Console.WriteLine("{0}: {1}", v.Key, v.Value.ToLocalTime());
}
else
@@ -55,7 +55,7 @@ namespace Duplicati.CommandLine.RecoveryTool
Library.Main.Volumes.VolumeReaderBase.UpdateOptionsFromManifest(p.CompressionModule, file, new Duplicati.Library.Main.Options(options));
- foreach(var f in EnumerateFilesInDList(file, filter, options))
+ foreach (var f in EnumerateFilesInDList(file, filter, options))
Console.WriteLine("{0} ({1})", f.Path, Library.Utility.Utility.FormatSizeString(f.Size));
}
@@ -65,9 +65,10 @@ namespace Duplicati.CommandLine.RecoveryTool
public static IEnumerable EnumerateFilesInDList(string file, Duplicati.Library.Utility.IFilter filter, Dictionary options)
{
var p = Library.Main.Volumes.VolumeBase.ParseFilename(file);
- using(var cm = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, file, options))
- using(var filesetreader = new Library.Main.Volumes.FilesetVolumeReader(cm, new Duplicati.Library.Main.Options(options)))
- foreach(var f in filesetreader.Files)
+ using (var fs = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (var cm = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, fs, Library.Interface.ArchiveMode.Read, options))
+ using (var filesetreader = new Library.Main.Volumes.FilesetVolumeReader(cm, new Duplicati.Library.Main.Options(options)))
+ foreach (var f in filesetreader.Files)
{
if (f.Type != Duplicati.Library.Main.FilelistEntryType.File)
continue;
@@ -98,9 +99,9 @@ namespace Duplicati.CommandLine.RecoveryTool
var parsedtime = Library.Utility.Timeparser.ParseTimeInterval(time, DateTime.Now, true);
var el = (
from n in ParseListFiles(folder)
- let diff = Math.Abs((n.Key - parsedtime).TotalSeconds)
- orderby diff
- select n).First();
+ let diff = Math.Abs((n.Key - parsedtime).TotalSeconds)
+ orderby diff
+ select n).First();
Console.WriteLine("Selected time {0}", el.Key);
diff --git a/Duplicati/CommandLine/RecoveryTool/Program.cs b/Duplicati/CommandLine/RecoveryTool/Program.cs
index e37eda13e..265f94659 100644
--- a/Duplicati/CommandLine/RecoveryTool/Program.cs
+++ b/Duplicati/CommandLine/RecoveryTool/Program.cs
@@ -97,7 +97,7 @@ namespace Duplicati.CommandLine.RecoveryTool
if (ex is Duplicati.Library.Interface.UserInformationException)
Console.WriteLine(ex.Message);
else
- Console.WriteLine("Program crashed: {0}{1}", Environment.NewLine, ex.ToString());
+ Console.WriteLine("Program crashed: {0}{1}", Environment.NewLine, ex);
return 200;
}
}
@@ -125,7 +125,7 @@ namespace Duplicati.CommandLine.RecoveryTool
cargs.AddRange(
from c in fargs
- where !string.IsNullOrWhiteSpace(c) && !c.StartsWith("#") && !c.StartsWith("!") && !c.StartsWith("REM ", StringComparison.OrdinalIgnoreCase)
+ where !string.IsNullOrWhiteSpace(c) && !c.StartsWith("#", StringComparison.Ordinal) && !c.StartsWith("!", StringComparison.Ordinal) && !c.StartsWith("REM ", StringComparison.OrdinalIgnoreCase)
select c
);
diff --git a/Duplicati/CommandLine/RecoveryTool/Recompress.cs b/Duplicati/CommandLine/RecoveryTool/Recompress.cs
index 21dddd731..af7b51508 100644
--- a/Duplicati/CommandLine/RecoveryTool/Recompress.cs
+++ b/Duplicati/CommandLine/RecoveryTool/Recompress.cs
@@ -6,6 +6,7 @@ using Duplicati.Library.Main;
using Duplicati.Library.Main.Volumes;
using Duplicati.Library.Utility;
using Newtonsoft.Json.Linq;
+using Duplicati.Library.Interface;
namespace Duplicati.CommandLine.RecoveryTool
{
@@ -130,7 +131,7 @@ namespace Duplicati.CommandLine.RecoveryTool
{
Console.WriteLine(" - cannot detect compression type");
continue;
- }
+ }
if ((!reencrypt && File.Exists(localFileTarget)) || (reencrypt && File.Exists(localFileTarget + "." + localFileSourceEncryption)))
{
@@ -181,14 +182,15 @@ namespace Duplicati.CommandLine.RecoveryTool
File.Move(localFileSource, localFileSource + ".same");
localFileSource = localFileSource + ".same";
}
-
- using (var cmOld = Library.DynamicLoader.CompressionLoader.GetModule(remoteFile.CompressionModule, localFileSource, options))
- using (var cmNew = Library.DynamicLoader.CompressionLoader.GetModule(target_compr_module, localFileTarget, options))
+ using (var localFileSourceStream = new System.IO.FileStream(localFileSource, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (var cmOld = Library.DynamicLoader.CompressionLoader.GetModule(remoteFile.CompressionModule, localFileSourceStream, ArchiveMode.Read, options))
+ using (var localFileTargetStream = new FileStream(localFileTarget, FileMode.Create, FileAccess.Write, FileShare.Delete))
+ using (var cmNew = Library.DynamicLoader.CompressionLoader.GetModule(target_compr_module, localFileTargetStream, ArchiveMode.Write, options))
foreach (var cmfile in cmOld.ListFiles(""))
{
string cmfileNew = cmfile;
var cmFileVolume = VolumeBase.ParseFilename(cmfileNew);
-
+
if (remoteFile.FileType == RemoteVolumeType.Index && cmFileVolume != null && cmFileVolume.FileType == RemoteVolumeType.Blocks)
{
// Correct inner filename extension to target compression type
@@ -205,7 +207,7 @@ namespace Duplicati.CommandLine.RecoveryTool
JToken token = JObject.Parse(textJSON);
var fileInfoBlocks = new FileInfo(Path.Combine(targetfolder, cmfileNew.Replace("vol/", "")));
var filehasher = HashAlgorithmHelper.Create(m_Options.FileHashAlgorithm);
-
+
using (var fileStream = fileInfoBlocks.Open(FileMode.Open))
{
fileStream.Position = 0;
@@ -216,7 +218,7 @@ namespace Duplicati.CommandLine.RecoveryTool
token["volumesize"] = fileInfoBlocks.Length;
textJSON = token.ToString();
}
-
+
using (var sourceStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(textJSON)))
using (var cs = cmNew.CreateFile(cmfileNew, Library.Interface.CompressionHint.Compressible, cmOld.GetLastWriteTime(cmfile)))
Library.Utility.Utility.CopyStream(sourceStream, cs);
@@ -228,12 +230,12 @@ namespace Duplicati.CommandLine.RecoveryTool
Library.Utility.Utility.CopyStream(sourceStream, cs);
}
}
-
+
File.Delete(localFileSource);
destinationFileInfo = new FileInfo(localFileTarget);
destinationFileInfo.LastWriteTime = originLastWriteTime;
}
-
+
if (reencrypt && remoteFile.EncryptionModule != null)
{
Console.Write(" reencrypting ...");
@@ -247,7 +249,7 @@ namespace Duplicati.CommandLine.RecoveryTool
destinationFileInfo = new FileInfo(localFileTarget);
destinationFileInfo.LastWriteTime = originLastWriteTime;
}
-
+
if (reupload)
{
Console.Write(" reuploading ...");
@@ -261,7 +263,7 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch (Exception ex)
{
- Console.WriteLine(" error: {0}", ex.ToString());
+ Console.WriteLine(" error: {0}", ex);
errors++;
}
}
diff --git a/Duplicati/CommandLine/RecoveryTool/Restore.cs b/Duplicati/CommandLine/RecoveryTool/Restore.cs
index 8a357c941..686de93d1 100644
--- a/Duplicati/CommandLine/RecoveryTool/Restore.cs
+++ b/Duplicati/CommandLine/RecoveryTool/Restore.cs
@@ -100,7 +100,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var hashesprblock = blocksize / (blockhasher.HashSize / 8);
- using(var mru = new CompressedFileMRUCache(options))
+ using (var mru = new CompressedFileMRUCache(options))
{
Console.WriteLine("Building lookup table for file hashes");
var lookup = new HashLookupHelper(ixfile, mru, (int)blocksize, blockhasher.HashSize / 8);
@@ -111,8 +111,8 @@ namespace Duplicati.CommandLine.RecoveryTool
if (!string.IsNullOrWhiteSpace(targetpath))
Console.WriteLine("Computing restore path");
-
- foreach(var f in List.EnumerateFilesInDList(filelist, filter, options))
+
+ foreach (var f in List.EnumerateFilesInDList(filelist, filter, options))
{
if (largestprefix == null)
{
@@ -124,7 +124,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var parts = f.Path.Split(new char[] { Path.DirectorySeparatorChar });
var ni = 0;
- for(; ni < Math.Min(parts.Length, largestprefixparts.Length); ni++)
+ for (; ni < Math.Min(parts.Length, largestprefixparts.Length); ni++)
if (!Library.Utility.Utility.ClientFilenameStringComparer.Equals(parts[ni], largestprefixparts[ni]))
break;
@@ -155,19 +155,19 @@ namespace Duplicati.CommandLine.RecoveryTool
var i = 0L;
var errors = 0L;
- foreach(var f in List.EnumerateFilesInDList(filelist, filter, options))
+ foreach (var f in List.EnumerateFilesInDList(filelist, filter, options))
{
try
{
var targetfile = MapToRestorePath(f.Path, largestprefix, targetpath);
if (!Directory.Exists(Path.GetDirectoryName(targetfile)))
Directory.CreateDirectory(Path.GetDirectoryName(targetfile));
-
+
Console.Write("{0}: {1} ({2})", i, targetfile, Library.Utility.Utility.FormatSizeString(f.Size));
- using(var tf = new Library.Utility.TempFile())
+ using (var tf = new Library.Utility.TempFile())
{
- using(var sw = File.OpenWrite(tf))
+ using (var sw = File.OpenWrite(tf))
{
if (f.BlocklistHashes == null)
{
@@ -176,7 +176,7 @@ namespace Duplicati.CommandLine.RecoveryTool
else
{
var blhi = 0L;
- foreach(var blh in f.BlocklistHashes)
+ foreach (var blh in f.BlocklistHashes)
{
Console.Write(" {0}", blhi);
var blockhashoffset = blhi * hashesprblock * blocksize;
@@ -184,16 +184,16 @@ namespace Duplicati.CommandLine.RecoveryTool
try
{
var bi = 0;
- foreach(var h in lookup.ReadBlocklistHashes(blh))
+ foreach (var h in lookup.ReadBlocklistHashes(blh))
{
try
{
sw.Position = blockhashoffset + (bi * blocksize);
lookup.WriteHash(sw, h);
}
- catch(Exception ex)
+ catch (Exception ex)
{
- Console.WriteLine("Failed to read hash: {0}{1}{2}", h, Environment.NewLine, ex.ToString());
+ Console.WriteLine("Failed to read hash: {0}{1}{2}", h, Environment.NewLine, ex);
}
bi++;
@@ -201,16 +201,16 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch (Exception ex)
{
- Console.WriteLine("Failed to read Blocklist hash: {0}{1}{2}", blh, Environment.NewLine, ex.ToString());
+ Console.WriteLine("Failed to read Blocklist hash: {0}{1}{2}", blh, Environment.NewLine, ex);
}
blhi++;
}
}
}
-
+
string fh;
- using(var fs = File.OpenRead(tf))
+ using (var fs = File.OpenRead(tf))
fh = Convert.ToBase64String(filehasher.ComputeHash(fs));
if (fh == f.Hash)
@@ -233,7 +233,7 @@ namespace Duplicati.CommandLine.RecoveryTool
}
catch (Exception ex)
{
- Console.WriteLine(" error: {0}", ex.ToString());
+ Console.WriteLine(" error: {0}", ex);
errors++;
}
i++;
@@ -288,7 +288,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var hashes = 0L;
string prev = null;
m_indexfile.Position = 0;
- foreach(var n in AllFileLines())
+ foreach (var n in AllFileLines())
{
if (n.Key != prev)
{
@@ -308,7 +308,7 @@ namespace Duplicati.CommandLine.RecoveryTool
var prevoff = 0L;
var hc = 0L;
m_indexfile.Position = 0;
- foreach(var n in AllFileLines())
+ foreach (var n in AllFileLines())
{
if (n.Key != prev)
{
@@ -331,9 +331,9 @@ namespace Duplicati.CommandLine.RecoveryTool
var max = m_indexfile.Read(m_linebuf, 0, m_linebuf.Length);
if (max == 0)
return null;
-
+
var lfi = 0;
- for(int i = 0; i < max; i++)
+ for (int i = 0; i < max; i++)
if (m_linebuf[i] == m_newline[lfi])
{
if (lfi == m_newline.Length - 1)
@@ -352,12 +352,12 @@ namespace Duplicati.CommandLine.RecoveryTool
lfi = 0;
}
- throw new Exception(string.Format("Unexpected long line starting at offset {0}, read {1} bytes without a newline", p, m_linebuf.Length));
+ throw new Exception(string.Format("Unexpected long line starting at offset {0}, read {1} bytes without a newline", p, m_linebuf.Length));
}
private IEnumerable> AllFileLines()
{
- while(true)
+ while (true)
{
var str = ReadNextLine();
if (str == null)
@@ -366,7 +366,7 @@ namespace Duplicati.CommandLine.RecoveryTool
if (str.Length == 0)
continue;
- var ix = str.IndexOf(", ");
+ var ix = str.IndexOf(", ", StringComparison.Ordinal);
if (ix < 0)
Console.WriteLine("Failed to parse line starting at offset {0} in index file, string: {1}", m_indexfile.Position - str.Length - m_newline.Length, str);
yield return new KeyValuePair(str.Substring(0, ix), str.Substring(ix + 2));
@@ -376,7 +376,7 @@ namespace Duplicati.CommandLine.RecoveryTool
public IEnumerable ReadBlocklistHashes(string hash)
{
var bytes = ReadHash(hash);
- for(var i = 0; i < bytes.Length; i += m_hashsize)
+ for (var i = 0; i < bytes.Length; i += m_hashsize)
yield return Convert.ToBase64String(bytes, i, m_hashsize);
}
@@ -392,10 +392,10 @@ namespace Duplicati.CommandLine.RecoveryTool
m_indexfile.Position = m_offsets[ix];
- foreach(var v in AllFileLines())
+ foreach (var v in AllFileLines())
{
if (v.Key == hash)
- using(var fs = m_cache.ReadBlock(v.Value, hash))
+ using (var fs = m_cache.ReadBlock(v.Value, hash))
{
var buf = new byte[m_blocksize];
var l = fs.Read(buf, 0, buf.Length);
@@ -403,8 +403,8 @@ namespace Duplicati.CommandLine.RecoveryTool
return buf;
}
-
-
+
+
if (StringComparer.Ordinal.Compare(hash, v.Key) < 0)
break;
}
@@ -430,6 +430,7 @@ namespace Duplicati.CommandLine.RecoveryTool
private class CompressedFileMRUCache : IDisposable
{
private Dictionary m_lookup = new Dictionary();
+ private Dictionary m_streams = new Dictionary();
private List m_mru = new List();
private Dictionary m_options;
@@ -443,12 +444,18 @@ namespace Duplicati.CommandLine.RecoveryTool
public Stream ReadBlock(string filename, string hash)
{
Library.Interface.ICompression cf;
- if (!m_lookup.TryGetValue(filename, out cf))
+ Stream stream;
+ if (!m_lookup.TryGetValue(filename, out cf) || !m_streams.TryGetValue(filename, out stream))
{
- cf = Library.DynamicLoader.CompressionLoader.GetModule(Path.GetExtension(filename).Trim('.'), filename, m_options);
+ stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
+ cf = Library.DynamicLoader.CompressionLoader.GetModule(Path.GetExtension(filename).Trim('.'), stream, Library.Interface.ArchiveMode.Read, m_options);
if (cf == null)
+ {
+ stream.Dispose();
throw new Exception(string.Format("Unable to decompress {0}, no such compression module {1}", filename, Path.GetExtension(filename).Trim('.')));
+ }
m_lookup[filename] = cf;
+ m_streams[filename] = stream;
}
if (m_mru.Count > 0 && m_mru[0] != filename)
@@ -462,6 +469,8 @@ namespace Duplicati.CommandLine.RecoveryTool
var f = m_mru.Last();
m_lookup[f].Dispose();
m_lookup.Remove(f);
+ m_streams[f].Dispose();
+ m_streams.Remove(f);
m_mru.Remove(f);
}
@@ -472,11 +481,16 @@ namespace Duplicati.CommandLine.RecoveryTool
public void Dispose()
{
- foreach(var v in m_lookup.Values)
+ foreach (var v in m_lookup.Values)
+ try { v.Dispose(); }
+ catch { }
+
+ foreach (var v in m_streams.Values)
try { v.Dispose(); }
catch { }
m_lookup = null;
+ m_streams = null;
m_mru = null;
}
diff --git a/Duplicati/CommandLine/help.txt b/Duplicati/CommandLine/help.txt
index cab82daf6..fb489aadc 100644
--- a/Duplicati/CommandLine/help.txt
+++ b/Duplicati/CommandLine/help.txt
@@ -138,6 +138,8 @@ Usage: delete []
Marks old data deleted and removes outdated dlist files. A backup is deleted when it is older than or when there are more newer versions than . Data is considered old, when it is not required from any existing backup anymore.
+ --dry-run
+ Performs the operation, but does not write changes to the local database or the remote storage
--keep-time=bin\Debug
diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs
index 97c9ff33a..11e8d4098 100644
--- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs
+++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs
@@ -73,7 +73,7 @@ namespace Duplicati.GUI.TrayIcon
public HttpServerConnection(Uri server, string password, bool saltedpassword, bool dbPasswordSourceDatabase, Dictionary options)
{
m_baseUri = server.ToString();
- if (!m_baseUri.EndsWith("/"))
+ if (!m_baseUri.EndsWith("/", StringComparison.Ordinal))
m_baseUri += "/";
m_apiUri = m_baseUri + "api/v1";
@@ -225,7 +225,7 @@ namespace Duplicati.GUI.TrayIcon
var req = (System.Net.HttpWebRequest) System.Net.WebRequest.Create(m_baseUri + LOGIN_SCRIPT);
req.Method = "POST";
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
- System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
req.ContentType = "application/x-www-form-urlencoded";
@@ -252,7 +252,7 @@ namespace Duplicati.GUI.TrayIcon
(System.Net.HttpWebRequest) System.Net.WebRequest.Create(m_baseUri + LOGIN_SCRIPT);
req.Method = "POST";
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
- System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
req.ContentType = "application/x-www-form-urlencoded";
if (req.CookieContainer == null)
@@ -313,7 +313,7 @@ namespace Duplicati.GUI.TrayIcon
(System.Net.HttpWebRequest) System.Net.WebRequest.Create(m_baseUri + STATUS_WINDOW);
req.Method = "GET";
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
- System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
if (req.CookieContainer == null)
req.CookieContainer = new System.Net.CookieContainer();
@@ -404,7 +404,7 @@ namespace Duplicati.GUI.TrayIcon
if (m_xsrftoken != null)
req.Headers.Add(XSRF_HEADER, m_xsrftoken);
req.UserAgent = "Duplicati TrayIcon Monitor, v" +
- System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add(TRAYICONPASSWORDSOURCE_HEADER, m_TrayIconHeaderValue);
if (req.CookieContainer == null)
req.CookieContainer = new System.Net.CookieContainer();
diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs
index 79324e237..6566bb4cb 100644
--- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs
+++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs
@@ -250,15 +250,15 @@ namespace Duplicati.GUI.TrayIcon
}
catch (WebException ex)
{
- System.Diagnostics.Trace.WriteLine("Request error: " + ex.ToString());
- Console.WriteLine("Request error: " + ex.ToString());
+ System.Diagnostics.Trace.WriteLine("Request error: " + ex);
+ Console.WriteLine("Request error: " + ex);
reSpawn++;
}
catch (Exception ex)
{
- System.Diagnostics.Trace.WriteLine("Unexpected error: " + ex.ToString());
- Console.WriteLine("Unexpected error: " + ex.ToString());
+ System.Diagnostics.Trace.WriteLine("Unexpected error: " + ex);
+ Console.WriteLine("Unexpected error: " + ex);
return;
}
} while (reSpawn < 3);
diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/RumpsRunner.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/RumpsRunner.cs
index 68a9b7be5..958606ba3 100644
--- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/RumpsRunner.cs
+++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/RumpsRunner.cs
@@ -177,8 +177,8 @@ namespace Duplicati.GUI.TrayIcon
m_toRumps = ch.AsWriteOnly();
WriteChannel(m_rumpsProcess.StandardInput, ch.AsReadOnly());
- ReadChannel(m_rumpsProcess.StandardOutput);
- ReadChannel(m_rumpsProcess.StandardError);
+ var standardOutputTask = ReadChannel(m_rumpsProcess.StandardOutput);
+ var standardErrorTask = ReadChannel(m_rumpsProcess.StandardError);
m_toRumps.WriteNoWait(JsonConvert.SerializeObject(new {Action = "background"}));
//m_toRumps.WriteNoWait(JsonConvert.SerializeObject(new {Action = "setappicon", Image = GetIcon(m_lastIcon)}));
@@ -243,7 +243,7 @@ namespace Duplicati.GUI.TrayIcon
menu.Callback();
}
}
- else if (!line.StartsWith("info") && !string.IsNullOrWhiteSpace(line))
+ else if (!line.StartsWith("info", StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(line))
{
Console.WriteLine("Unexpected message: {0}", line);
}
diff --git a/Duplicati/Library/AutoUpdater/UpdaterManager.cs b/Duplicati/Library/AutoUpdater/UpdaterManager.cs
index db19e68aa..5a18f77e4 100644
--- a/Duplicati/Library/AutoUpdater/UpdaterManager.cs
+++ b/Duplicati/Library/AutoUpdater/UpdaterManager.cs
@@ -20,6 +20,7 @@ using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
+using Duplicati.Library.Interface;
namespace Duplicati.Library.AutoUpdater
{
@@ -47,8 +48,8 @@ namespace Duplicati.Library.AutoUpdater
public static readonly string INSTALLDIR;
- private static readonly string INSTALLED_BASE_DIR =
- string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable(string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)))
+ private static readonly string INSTALLED_BASE_DIR =
+ string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable(string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)))
? System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location)
: Library.Utility.Utility.ExpandEnvironmentVariables(System.Environment.GetEnvironmentVariable(string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)));
@@ -87,7 +88,7 @@ namespace Duplicati.Library.AutoUpdater
///
/// Gets the last version found from an update
///
- public static UpdateInfo LastUpdateCheckVersion { get; private set; }
+ public static UpdateInfo LastUpdateCheckVersion { get; private set; }
static UpdaterManager()
{
@@ -118,7 +119,7 @@ namespace Duplicati.Library.AutoUpdater
var programfiles = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
// The user can override updates by having a local updates folder
- var overrides = new List(new string [] {
+ var overrides = new List(new string[] {
System.IO.Path.Combine(InstalledBaseDir, "updates"),
});
@@ -131,7 +132,7 @@ namespace Duplicati.Library.AutoUpdater
{
if (Library.Utility.Utility.IsClientOSX)
overrides.Add(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support", APPNAME, "updates"));
-
+
overrides.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates"));
}
@@ -148,7 +149,7 @@ namespace Duplicati.Library.AutoUpdater
var attempts = new List();
// We do not want to install anything in the basedir, if the application is installed in "ProgramFiles"
- if (!string.IsNullOrWhiteSpace(programfiles) && !InstalledBaseDir.StartsWith(Library.Utility.Utility.AppendDirSeparator(programfiles)))
+ if (!string.IsNullOrWhiteSpace(programfiles) && !InstalledBaseDir.StartsWith(Library.Utility.Utility.AppendDirSeparator(programfiles), StringComparison.Ordinal))
attempts.Add(System.IO.Path.Combine(InstalledBaseDir, "updates"));
if (Library.Utility.Utility.IsClientOSX)
@@ -173,7 +174,7 @@ namespace Duplicati.Library.AutoUpdater
installdir = p;
break;
}
-
+
if (string.IsNullOrWhiteSpace(installdir))
foreach (var p in attempts)
if (!string.IsNullOrWhiteSpace(p) && TestDirectoryIsWriteable(p))
@@ -181,7 +182,7 @@ namespace Duplicati.Library.AutoUpdater
installdir = p;
break;
}
-
+
INSTALLDIR = installdir;
}
else
@@ -222,7 +223,7 @@ namespace Duplicati.Library.AutoUpdater
Displayname = string.IsNullOrWhiteSpace(Duplicati.License.VersionNumbers.TAG) ? "Current" : Duplicati.License.VersionNumbers.TAG,
Version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(),
ReleaseTime = new DateTime(0),
- ReleaseType =
+ ReleaseType =
#if DEBUG
"Debug"
#else
@@ -257,13 +258,13 @@ namespace Duplicati.Library.AutoUpdater
{
var selfversion = TryParseVersion(SelfVersion.Version);
- m_hasUpdateInstalled =
+ m_hasUpdateInstalled =
(from n in FindInstalledVersions()
- let nversion = TryParseVersion(n.Value.Version)
- let newerVersion = selfversion < nversion
- where newerVersion && VerifyUnpackedFolder(n.Key, n.Value)
- orderby nversion descending
- select n)
+ let nversion = TryParseVersion(n.Value.Version)
+ let newerVersion = selfversion < nversion
+ where newerVersion && VerifyUnpackedFolder(n.Key, n.Value)
+ orderby nversion descending
+ select n)
.FirstOrDefault();
}
@@ -278,11 +279,11 @@ namespace Duplicati.Library.AutoUpdater
if (!System.IO.Directory.Exists(probe))
{
- try
+ try
{
System.IO.Directory.CreateDirectory(probe);
if (probe != path)
- System.IO.Directory.Delete(probe);
+ System.IO.Directory.Delete(probe);
return true;
}
catch
@@ -296,20 +297,20 @@ namespace Duplicati.Library.AutoUpdater
public static string InstallID
{
get
- {
- try { return System.IO.File.ReadAllText(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE)).Replace('\r', '\n').Split(new char[] { '\n' }).FirstOrDefault().Trim() ?? ""; }
+ {
+ try { return System.IO.File.ReadAllText(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE)).Replace('\r', '\n').Split(new char[] { '\n' }).FirstOrDefault().Trim() ?? ""; }
catch { }
return "";
}
}
-
+
public static UpdateInfo CheckForUpdate(ReleaseType channel = ReleaseType.Unknown)
{
if (channel == ReleaseType.Unknown)
channel = AutoUpdateSettings.DefaultUpdateChannel;
- foreach(var rawurl in MANIFEST_URLS)
+ foreach (var rawurl in MANIFEST_URLS)
{
var url = rawurl;
@@ -332,17 +333,17 @@ namespace Duplicati.Library.AutoUpdater
try
{
- using(var tmpfile = new Library.Utility.TempFile())
+ using (var tmpfile = new Library.Utility.TempFile())
{
System.Net.WebClient wc = new System.Net.WebClient();
wc.Headers.Add(System.Net.HttpRequestHeader.UserAgent, string.Format("{0} v{1}{2}", APPNAME, SelfVersion.Version, string.IsNullOrWhiteSpace(InstallID) ? "" : " -" + InstallID));
wc.Headers.Add("X-Install-ID", InstallID);
wc.DownloadFile(url, tmpfile);
- using(var fs = System.IO.File.OpenRead(tmpfile))
- using(var ss = new SignatureReadingStream(fs, SIGN_KEY))
- using(var tr = new System.IO.StreamReader(ss))
- using(var jr = new Newtonsoft.Json.JsonTextReader(tr))
+ using (var fs = System.IO.File.OpenRead(tmpfile))
+ using (var ss = new SignatureReadingStream(fs, SIGN_KEY))
+ using (var tr = new System.IO.StreamReader(ss))
+ using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
{
var update = new Newtonsoft.Json.JsonSerializer().Deserialize(jr);
@@ -384,10 +385,10 @@ namespace Duplicati.Library.AutoUpdater
{
try
{
- using(var fs = System.IO.File.OpenRead(manifest))
- using(var ss = new SignatureReadingStream(fs, SIGN_KEY))
- using(var tr = new System.IO.StreamReader(ss))
- using(var jr = new Newtonsoft.Json.JsonTextReader(tr))
+ using (var fs = System.IO.File.OpenRead(manifest))
+ using (var ss = new SignatureReadingStream(fs, SIGN_KEY))
+ using (var tr = new System.IO.StreamReader(ss))
+ using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
return new Newtonsoft.Json.JsonSerializer().Deserialize(jr);
}
catch (Exception ex)
@@ -404,7 +405,7 @@ namespace Duplicati.Library.AutoUpdater
{
var res = new List>();
if (INSTALLDIR != null)
- foreach(var folder in System.IO.Directory.GetDirectories(INSTALLDIR))
+ foreach (var folder in System.IO.Directory.GetDirectories(INSTALLDIR))
{
var r = ReadInstalledManifest(folder);
if (r != null)
@@ -429,20 +430,20 @@ namespace Duplicati.Library.AutoUpdater
var packagepath = new Library.Utility.Uri(updates[0]).Path;
var packagename = packagepath.Split('/').Last();
- foreach(var alt_url in AutoUpdateSettings.URLs.Reverse())
+ foreach (var alt_url in AutoUpdateSettings.URLs.Reverse())
{
var alt_uri = new Library.Utility.Uri(alt_url);
var path_components = alt_uri.Path.Split('/');
- var path = string.Join("/", path_components.Take(path_components.Count() - 1).Union(new string[] { packagename}));
+ var path = string.Join("/", path_components.Take(path_components.Count() - 1).Union(new string[] { packagename }));
var new_path = alt_uri.SetPath(path);
updates.Insert(0, new_path.ToString());
}
}
- using(var tempfile = new Library.Utility.TempFile())
+ using (var tempfile = new Library.Utility.TempFileStream())
{
- foreach(var url in updates)
+ foreach (var url in updates)
{
try
{
@@ -455,36 +456,35 @@ namespace Duplicati.Library.AutoUpdater
wreq.Headers.Add("X-Install-ID", InstallID);
var areq = new Duplicati.Library.Utility.AsyncHttpRequest(wreq);
- using(var resp = areq.GetResponse())
- using(var rss = areq.GetResponseStream())
- using(var pgs = new Duplicati.Library.Utility.ProgressReportingStream(rss, version.CompressedSize, cb))
- using(var fs = System.IO.File.Open(tempfile, System.IO.FileMode.Create))
- Duplicati.Library.Utility.Utility.CopyStream(pgs, fs);
+ using (var resp = areq.GetResponse())
+ using (var rss = areq.GetResponseStream())
+ using (var pgs = new Duplicati.Library.Utility.ProgressReportingStream(rss, version.CompressedSize, cb))
+ {
+ Duplicati.Library.Utility.Utility.CopyStream(pgs, tempfile);
+ }
var sha256 = System.Security.Cryptography.SHA256.Create();
- var md5 = System.Security.Cryptography.MD5.Create();
+ var md5 = System.Security.Cryptography.MD5.Create();
- using(var s = System.IO.File.OpenRead(tempfile))
- {
- if (s.Length != version.CompressedSize)
- throw new Exception(string.Format("Invalid file size {0}, expected {1} for {2}", s.Length, version.CompressedSize, url));
-
- var sha256hash = Convert.ToBase64String(sha256.ComputeHash(s));
- if (sha256hash != version.SHA256)
- throw new Exception(string.Format("Damaged or corrupted file, sha256 mismatch for {0}", url));
- }
+ if (tempfile.Length != version.CompressedSize)
+ throw new Exception(string.Format("Invalid file size {0}, expected {1} for {2}", tempfile.Length, version.CompressedSize, url));
- using(var s = System.IO.File.OpenRead(tempfile))
+ tempfile.Position = 0;
+ var sha256hash = Convert.ToBase64String(sha256.ComputeHash(tempfile));
+ if (sha256hash != version.SHA256)
+ throw new Exception(string.Format("Damaged or corrupted file, sha256 mismatch for {0}", url));
+
+
+ tempfile.Position = 0;
+ var md5hash = Convert.ToBase64String(md5.ComputeHash(tempfile));
+ if (md5hash != version.MD5)
+ throw new Exception(string.Format("Damaged or corrupted file, md5 mismatch for {0}", url));
+
+ tempfile.Position = 0;
+ using (var tempfolder = new Duplicati.Library.Utility.TempFolder())
+ using (ICompression zip = new Duplicati.Library.Compression.FileArchiveZip(tempfile, ArchiveMode.Read, new Dictionary()))
{
- var md5hash = Convert.ToBase64String(md5.ComputeHash(s));
- if (md5hash != version.MD5)
- throw new Exception(string.Format("Damaged or corrupted file, md5 mismatch for {0}", url));
- }
-
- using(var tempfolder = new Duplicati.Library.Utility.TempFolder())
- using(var zip = new Duplicati.Library.Compression.FileArchiveZip(tempfile, new Dictionary()))
- {
- foreach(var file in zip.ListFilesWithSize(""))
+ foreach (var file in zip.ListFilesWithSize(""))
{
if (System.IO.Path.IsPathRooted(file.Key) || file.Key.Trim().StartsWith("..", StringComparison.OrdinalIgnoreCase))
throw new Exception(string.Format("Out-of-place file path detected: {0}", file.Key));
@@ -494,8 +494,8 @@ namespace Duplicati.Library.AutoUpdater
if (!System.IO.Directory.Exists(targetfolder))
System.IO.Directory.CreateDirectory(targetfolder);
- using(var zs = zip.OpenRead(file.Key))
- using(var fs = System.IO.File.Create(targetpath))
+ using (var zs = zip.OpenRead(file.Key))
+ using (var fs = System.IO.File.Create(targetpath))
zs.CopyTo(fs);
}
@@ -505,7 +505,7 @@ namespace Duplicati.Library.AutoUpdater
var targetfolder = System.IO.Path.Combine(INSTALLDIR, versionstring);
if (System.IO.Directory.Exists(targetfolder))
System.IO.Directory.Delete(targetfolder, true);
-
+
System.IO.Directory.CreateDirectory(targetfolder);
var tempfolderpath = Duplicati.Library.Utility.Utility.AppendDirSeparator(tempfolder);
@@ -514,14 +514,14 @@ namespace Duplicati.Library.AutoUpdater
// Would be nice, but does not work :(
//System.IO.Directory.Move(tempfolder, targetfolder);
- foreach(var e in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(tempfolder))
+ foreach (var e in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(tempfolder))
{
var relpath = e.Substring(tempfolderlength);
if (string.IsNullOrWhiteSpace(relpath))
continue;
var fullpath = System.IO.Path.Combine(targetfolder, relpath);
- if (relpath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
+ if (relpath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
System.IO.Directory.CreateDirectory(fullpath);
else
System.IO.File.Copy(e, fullpath);
@@ -530,16 +530,16 @@ namespace Duplicati.Library.AutoUpdater
// Verification will kick in when we list the installed updates
//VerifyUnpackedFolder(targetfolder, version);
System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, CURRENT_FILE), versionstring);
-
+
m_hasUpdateInstalled = null;
var obsolete = (from n in FindInstalledVersions()
- where n.Value.Version != version.Version && n.Value.Version != SelfVersion.Version
- let x = TryParseVersion(n.Value.Version)
- orderby x descending
- select n).Skip(1).ToArray();
+ where n.Value.Version != version.Version && n.Value.Version != SelfVersion.Version
+ let x = TryParseVersion(n.Value.Version)
+ orderby x descending
+ select n).Skip(1).ToArray();
- foreach(var f in obsolete)
+ foreach (var f in obsolete)
try { System.IO.Directory.Delete(f.Key, true); }
catch { }
@@ -551,7 +551,7 @@ namespace Duplicati.Library.AutoUpdater
}
}
}
- catch(Exception ex)
+ catch (Exception ex)
{
if (OnError != null)
OnError(ex);
@@ -572,11 +572,11 @@ namespace Duplicati.Library.AutoUpdater
var sha256 = System.Security.Cryptography.SHA256.Create();
var md5 = System.Security.Cryptography.MD5.Create();
- using(var fs = System.IO.File.OpenRead(System.IO.Path.Combine(folder, UPDATE_MANIFEST_FILENAME)))
+ using (var fs = System.IO.File.OpenRead(System.IO.Path.Combine(folder, UPDATE_MANIFEST_FILENAME)))
{
- using(var ss = new SignatureReadingStream(fs, SIGN_KEY))
- using(var tr = new System.IO.StreamReader(ss))
- using(var jr = new Newtonsoft.Json.JsonTextReader(tr))
+ using (var ss = new SignatureReadingStream(fs, SIGN_KEY))
+ using (var tr = new System.IO.StreamReader(ss))
+ using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
update = new Newtonsoft.Json.JsonSerializer().Deserialize(jr);
sha256.Initialize();
@@ -607,7 +607,7 @@ namespace Duplicati.Library.AutoUpdater
folder = Library.Utility.Utility.AppendDirSeparator(folder);
var baselen = folder.Length;
- foreach(var file in Library.Utility.Utility.EnumerateFileSystemEntries(folder))
+ foreach (var file in Library.Utility.Utility.EnumerateFileSystemEntries(folder))
{
var relpath = file.Substring(baselen);
if (string.IsNullOrWhiteSpace(relpath))
@@ -620,7 +620,7 @@ namespace Duplicati.Library.AutoUpdater
if (!paths.TryGetValue(relpath, out fe))
{
var ignore = false;
- foreach(var c in ignores)
+ foreach (var c in ignores)
if (ignore = relpath.StartsWith(c, Library.Utility.Utility.ClientFilenameStringComparision))
break;
@@ -638,7 +638,7 @@ namespace Duplicati.Library.AutoUpdater
sha256.Initialize();
md5.Initialize();
- using(var fs = System.IO.File.OpenRead(file))
+ using (var fs = System.IO.File.OpenRead(file))
{
if (Convert.ToBase64String(sha256.ComputeHash(fs)) != fe.SHA256)
throw new Exception(string.Format("Invalid sha256 hash for file: {0}", file));
@@ -690,11 +690,11 @@ namespace Duplicati.Library.AutoUpdater
var manifestpath = manifest ?? System.IO.Path.Combine(inputfolder, UPDATE_MANIFEST_FILENAME);
- using(var s = System.IO.File.OpenRead(manifestpath))
- using(var sr = new System.IO.StreamReader(s))
- using(var jr = new Newtonsoft.Json.JsonTextReader(sr))
+ using (var s = System.IO.File.OpenRead(manifestpath))
+ using (var sr = new System.IO.StreamReader(s))
+ using (var jr = new Newtonsoft.Json.JsonTextReader(sr))
remoteManifest = new Newtonsoft.Json.JsonSerializer().Deserialize(jr);
-
+
if (remoteManifest.Files == null)
remoteManifest.Files = new FileEntry[0];
@@ -702,8 +702,8 @@ namespace Duplicati.Library.AutoUpdater
remoteManifest.ReleaseTime = DateTime.UtcNow;
var ignoreFiles = (from n in remoteManifest.Files
- where n.Ignore
- select n).ToArray();
+ where n.Ignore
+ select n).ToArray();
var ignoreMap = ignoreFiles.ToDictionary(k => k.Path, k => "", Duplicati.Library.Utility.Utility.ClientFilenameStringComparer);
@@ -724,60 +724,73 @@ namespace Duplicati.Library.AutoUpdater
var md5 = System.Security.Cryptography.MD5.Create();
var sha256 = System.Security.Cryptography.SHA256.Create();
- Func computeMD5 = (path) =>
+ Func computeStreamMD5 = (stream) =>
{
md5.Initialize();
- using(var fs = System.IO.File.OpenRead(path))
- return Convert.ToBase64String(md5.ComputeHash(fs));
+ return Convert.ToBase64String(md5.ComputeHash(stream));
+ };
+
+ Func computeStreamSHA256 = (stream) =>
+ {
+ sha256.Initialize();
+ return Convert.ToBase64String(sha256.ComputeHash(stream));
+ };
+
+ Func computeMD5 = (path) =>
+ {
+ using (Stream fs = System.IO.File.OpenRead(path))
+ return computeStreamMD5(fs);
};
Func computeSHA256 = (path) =>
{
- sha256.Initialize();
- using(var fs = System.IO.File.OpenRead(path))
- return Convert.ToBase64String(sha256.ComputeHash(fs));
+ using (Stream fs = System.IO.File.OpenRead(path))
+ return computeStreamSHA256(fs);
};
// Build a zip
- using (var archive_temp = new Duplicati.Library.Utility.TempFile())
+ using (var archive_temp_file = new Duplicati.Library.Utility.TempFile())
{
- using (var zipfile = new Duplicati.Library.Compression.FileArchiveZip(archive_temp, new Dictionary()))
+ using (var archive_temp = new Duplicati.Library.Utility.TempFileStream(archive_temp_file))
{
- Func addToArchive = (path, relpath) =>
+ using (ICompression zipfile = new Duplicati.Library.Compression.FileArchiveZip(archive_temp, ArchiveMode.Write, new Dictionary()))
{
- if (ignoreMap.ContainsKey(relpath))
- return false;
-
- if (path.EndsWith(dirsep))
- return true;
-
- using (var source = System.IO.File.OpenRead(path))
- using (var target = zipfile.CreateFile(relpath,
- Duplicati.Library.Interface.CompressionHint.Compressible,
- System.IO.File.GetLastAccessTimeUtc(path)))
+ Func addToArchive = (path, relpath) =>
{
- source.CopyTo(target);
- remoteManifest.UncompressedSize += source.Length;
- }
+ if (ignoreMap.ContainsKey(relpath))
+ return false;
- return true;
- };
-
- // Build the update manifest
- localManifest.Files =
- (from fse in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(inputfolder)
- let relpath = fse.Substring(baselen)
- where addToArchive(fse, relpath)
- select new FileEntry() {
- Path = relpath,
- LastWriteTime = System.IO.File.GetLastAccessTimeUtc(fse),
- MD5 = fse.EndsWith(dirsep) ? null : computeMD5(fse),
- SHA256 = fse.EndsWith(dirsep) ? null : computeSHA256(fse)
- })
- .Union(ignoreFiles).ToArray();
+ if (path.EndsWith(dirsep, StringComparison.Ordinal))
+ return true;
+
+ using (var source = System.IO.File.OpenRead(path))
+ using (var target = zipfile.CreateFile(relpath,
+ Duplicati.Library.Interface.CompressionHint.Compressible,
+ System.IO.File.GetLastAccessTimeUtc(path)))
+ {
+ source.CopyTo(target);
+ remoteManifest.UncompressedSize += source.Length;
+ }
+
+ return true;
+ };
+
+ // Build the update manifest
+ localManifest.Files =
+ (from fse in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(inputfolder)
+ let relpath = fse.Substring(baselen)
+ where addToArchive(fse, relpath)
+ select new FileEntry()
+ {
+ Path = relpath,
+ LastWriteTime = System.IO.File.GetLastAccessTimeUtc(fse),
+ MD5 = fse.EndsWith(dirsep, StringComparison.Ordinal) ? null : computeMD5(fse),
+ SHA256 = fse.EndsWith(dirsep, StringComparison.Ordinal) ? null : computeSHA256(fse)
+ })
+ .Union(ignoreFiles).ToArray();
+
+ // Write a signed manifest with the files
- // Write a signed manifest with the files
-
using (var ms = new System.IO.MemoryStream())
using (var sw = new System.IO.StreamWriter(ms))
{
@@ -788,26 +801,26 @@ namespace Duplicati.Library.AutoUpdater
{
SignatureReadingStream.CreateSignedStream(ms, ms2, key);
ms2.Position = 0;
- using (var sigfile = zipfile.CreateFile(UPDATE_MANIFEST_FILENAME,
+ using (var sigfile = zipfile.CreateFile(UPDATE_MANIFEST_FILENAME,
Duplicati.Library.Interface.CompressionHint.Compressible,
DateTime.UtcNow))
ms2.CopyTo(sigfile);
}
}
+ }
+
+ archive_temp.Position = 0;
+ remoteManifest.CompressedSize = archive_temp.Length;
+ remoteManifest.MD5 = computeStreamMD5(archive_temp);
+ remoteManifest.SHA256 = computeStreamSHA256(archive_temp);
}
-
- remoteManifest.CompressedSize = new System.IO.FileInfo(archive_temp).Length;
- remoteManifest.MD5 = computeMD5(archive_temp);
- remoteManifest.SHA256 = computeSHA256(archive_temp);
-
- System.IO.File.Move(archive_temp, System.IO.Path.Combine(outputfolder, "package.zip"));
-
+ System.IO.File.Move(archive_temp_file, System.IO.Path.Combine(outputfolder, "package.zip"));
}
// Write a signed manifest for upload
- using(var tf = new Duplicati.Library.Utility.TempFile())
+ using (var tf = new Duplicati.Library.Utility.TempFile())
{
using (var ms = new System.IO.MemoryStream())
using (var sw = new System.IO.StreamWriter(ms))
@@ -984,7 +997,7 @@ namespace Duplicati.Library.AutoUpdater
{
try
{
- Console.WriteLine("Crash! {0}{1}", Environment.NewLine, tex.ToString());
+ Console.WriteLine("Crash! {0}{1}", Environment.NewLine, tex);
}
catch
{
@@ -997,7 +1010,7 @@ namespace Duplicati.Library.AutoUpdater
string.Format("{0}-crashlog.txt", AutoUpdateSettings.AppName)
);
- System.IO.File.WriteAllText(report_file, tex.ToString());
+ System.IO.File.WriteAllText(report_file, tex.ToString());
}
catch
{
@@ -1070,9 +1083,8 @@ namespace Duplicati.Library.AutoUpdater
return r;
}
- var args = Environment.CommandLine;
var app = Environment.GetCommandLineArgs().First();
- args = args.Substring(app.Length);
+ var args = Library.Utility.Utility.WrapAsCommandLine(Environment.GetCommandLineArgs().Skip(1), false);
if (!Path.IsPathRooted(app))
app = Path.Combine(InstalledBaseDir, app);
@@ -1091,7 +1103,7 @@ namespace Duplicati.Library.AutoUpdater
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
- ErrorDialog = false,
+ ErrorDialog = false,
};
pi.EnvironmentVariables.Clear();
@@ -1201,7 +1213,7 @@ namespace Duplicati.Library.AutoUpdater
try { AppDomain.Unload(domain); }
catch (Exception ex)
- {
+ {
Console.WriteLine("Appdomain unload error: {0}", ex);
}
@@ -1216,13 +1228,12 @@ namespace Duplicati.Library.AutoUpdater
try
{
- var args = Environment.CommandLine;
var app = Environment.GetCommandLineArgs().First();
- args = args.Substring(app.Length);
+ var args = Library.Utility.Utility.WrapAsCommandLine(Environment.GetCommandLineArgs().Skip(1), false);
if (!System.IO.Path.IsPathRooted(app))
app = System.IO.Path.Combine(InstalledBaseDir, app);
-
+
// Re-launch but give the OS a little time to fully unload all open handles, etc.
var si = new System.Diagnostics.ProcessStartInfo(app, args);
diff --git a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
index 032854043..b6543a6e1 100644
--- a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
+++ b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
@@ -147,7 +147,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
_userInfo.Domain = "";
_url = u.SetScheme("ftp").SetQuery(null).SetCredentials(null, null).ToString();
- if (!_url.EndsWith("/"))
+ if (!_url.EndsWith("/", StringComparison.Ordinal))
{
_url += "/";
}
@@ -215,7 +215,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
// Get the remote path
var url = new Uri(this._url);
- remotePath = "/" + (url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath);
+ remotePath = "/" + (url.AbsolutePath.EndsWith("/", StringComparison.Ordinal) ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath);
if (!string.IsNullOrEmpty(filename))
{
@@ -354,7 +354,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
foreach (var fileEntry in fileEntries)
{
- if (fileEntry.Name.Equals(remotename) || fileEntry.Name.EndsWith("/" + remotename) || fileEntry.Name.EndsWith("\\" + remotename))
+ if (fileEntry.Name.Equals(remotename) || fileEntry.Name.EndsWith("/" + remotename, StringComparison.Ordinal) || fileEntry.Name.EndsWith("\\" + remotename, StringComparison.Ordinal))
{
if (fileEntry.Size < 0 || streamLen < 0 || fileEntry.Size == streamLen)
{
@@ -522,7 +522,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
var url = new Uri(_url);
// Get the remote path
- var remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
+ var remotePath = url.AbsolutePath.EndsWith("/", StringComparison.Ordinal) ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
// Try to create the directory
client.CreateDirectory(remotePath, true);
@@ -561,7 +561,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
ftpClient.ValidateCertificate += HandleValidateCertificate;
// Get the remote path
- var remotePath = uri.AbsolutePath.EndsWith("/") ? uri.AbsolutePath.Substring(0, uri.AbsolutePath.Length - 1) : uri.AbsolutePath;
+ var remotePath = uri.AbsolutePath.EndsWith("/", StringComparison.Ordinal) ? uri.AbsolutePath.Substring(0, uri.AbsolutePath.Length - 1) : uri.AbsolutePath;
ftpClient.SetWorkingDirectory(remotePath);
this.Client = ftpClient;
diff --git a/Duplicati/Library/Backend/AmazonCloudDrive/AmzCD.cs b/Duplicati/Library/Backend/AmazonCloudDrive/AmzCD.cs
index b68319333..520d9a06c 100644
--- a/Duplicati/Library/Backend/AmazonCloudDrive/AmzCD.cs
+++ b/Duplicati/Library/Backend/AmazonCloudDrive/AmzCD.cs
@@ -75,7 +75,7 @@ namespace Duplicati.Library.Backend.AmazonCloudDrive
var uri = new Utility.Uri(url);
m_path = uri.HostAndPath;
- if (!m_path.EndsWith("/"))
+ if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
string authid = null;
@@ -152,7 +152,7 @@ namespace Duplicati.Library.Backend.AmazonCloudDrive
{
if (m_endPointInfo == null)
RefreshMetadataAndContentUrl();
- while (m_endPointInfo.ContentUrl.EndsWith("/"))
+ while (m_endPointInfo.ContentUrl.EndsWith("/", StringComparison.Ordinal))
m_endPointInfo.ContentUrl = m_endPointInfo.ContentUrl.Substring(0, m_endPointInfo.ContentUrl.Length - 1);
return m_endPointInfo.ContentUrl;
}
@@ -164,7 +164,7 @@ namespace Duplicati.Library.Backend.AmazonCloudDrive
{
if (m_endPointInfo == null)
RefreshMetadataAndContentUrl();
- while (m_endPointInfo.MetadataUrl.EndsWith("/"))
+ while (m_endPointInfo.MetadataUrl.EndsWith("/", StringComparison.Ordinal))
m_endPointInfo.MetadataUrl = m_endPointInfo.MetadataUrl.Substring(0, m_endPointInfo.MetadataUrl.Length - 1);
return m_endPointInfo.MetadataUrl;
}
diff --git a/Duplicati/Library/Backend/Backblaze/B2.cs b/Duplicati/Library/Backend/Backblaze/B2.cs
index 5b8838eab..35ec87393 100644
--- a/Duplicati/Library/Backend/Backblaze/B2.cs
+++ b/Duplicati/Library/Backend/Backblaze/B2.cs
@@ -57,11 +57,11 @@ namespace Duplicati.Library.Backend.Backblaze
m_bucketname = uri.Host;
m_prefix = "/" + uri.Path;
- if (!m_prefix.EndsWith("/"))
+ if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// For B2 we do not use a leading slash
- while(m_prefix.StartsWith("/"))
+ while(m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
m_urlencodedprefix = string.Join("/", m_prefix.Split(new [] { '/' }).Select(x => Library.Utility.Uri.UrlPathEncode(x)));
@@ -323,7 +323,7 @@ namespace Duplicati.Library.Backend.Backblaze
foreach(var f in resp.Files)
{
- if (!f.FileName.StartsWith(m_prefix))
+ if (!f.FileName.StartsWith(m_prefix, StringComparison.Ordinal))
continue;
var name = f.FileName.Substring(m_prefix.Length);
diff --git a/Duplicati/Library/Backend/Backblaze/B2AuthHelper.cs b/Duplicati/Library/Backend/Backblaze/B2AuthHelper.cs
index 8adef2e89..55197519c 100644
--- a/Duplicati/Library/Backend/Backblaze/B2AuthHelper.cs
+++ b/Duplicati/Library/Backend/Backblaze/B2AuthHelper.cs
@@ -49,7 +49,7 @@ namespace Duplicati.Library.Backend.Backblaze
private string DropTrailingSlashes(string url)
{
- while(url.EndsWith("/"))
+ while(url.EndsWith("/", StringComparison.Ordinal))
url = url.Substring(0, url.Length - 1);
return url;
}
diff --git a/Duplicati/Library/Backend/Box/BoxBackend.cs b/Duplicati/Library/Backend/Box/BoxBackend.cs
index 124a51fe7..02c6d2cca 100644
--- a/Duplicati/Library/Backend/Box/BoxBackend.cs
+++ b/Duplicati/Library/Backend/Box/BoxBackend.cs
@@ -93,7 +93,7 @@ namespace Duplicati.Library.Backend.Box
var uri = new Utility.Uri(url);
m_path = uri.HostAndPath;
- if (!m_path.EndsWith("/"))
+ if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
string authid = null;
diff --git a/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs b/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs
index 29cbc2ba1..82793def0 100644
--- a/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs
+++ b/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs
@@ -84,10 +84,10 @@ namespace Duplicati.Library.Backend
if (!string.IsNullOrEmpty(u.UserInfo))
{
- if (u.UserInfo.IndexOf(":") >= 0)
+ if (u.UserInfo.IndexOf(":", StringComparison.Ordinal) >= 0)
{
- m_username = u.UserInfo.Substring(0, u.UserInfo.IndexOf(":"));
- m_password = u.UserInfo.Substring(u.UserInfo.IndexOf(":") + 1);
+ m_username = u.UserInfo.Substring(0, u.UserInfo.IndexOf(":", StringComparison.Ordinal));
+ m_password = u.UserInfo.Substring(u.UserInfo.IndexOf(":", StringComparison.Ordinal) + 1);
}
else
{
@@ -108,9 +108,9 @@ namespace Duplicati.Library.Backend
m_path = uri.HostAndPath;
}
- if (m_path.EndsWith("/"))
+ if (m_path.EndsWith("/", StringComparison.Ordinal))
m_path = m_path.Substring(0, m_path.Length - 1);
- if (!m_path.StartsWith("/"))
+ if (!m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
if (!options.TryGetValue("cloudfiles-authentication-url", out m_authUrl))
@@ -416,7 +416,7 @@ namespace Duplicati.Library.Backend
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(m_storageUrl + UrlEncode(m_path + remotename) + query);
req.Headers.Add("X-Auth-Token", UrlEncode(m_authToken));
- req.UserAgent = "Duplicati CloudFiles Backend v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ req.UserAgent = "Duplicati CloudFiles Backend v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.KeepAlive = false;
req.PreAuthenticate = true;
req.AllowWriteStreamBuffering = false;
diff --git a/Duplicati/Library/Backend/Dropbox/Dropbox.cs b/Duplicati/Library/Backend/Dropbox/Dropbox.cs
index 289ec4f34..281395868 100644
--- a/Duplicati/Library/Backend/Dropbox/Dropbox.cs
+++ b/Duplicati/Library/Backend/Dropbox/Dropbox.cs
@@ -22,10 +22,10 @@ namespace Duplicati.Library.Backend
var uri = new Utility.Uri(url);
m_path = Library.Utility.Uri.UrlDecode(uri.HostAndPath);
- if (m_path.Length != 0 && !m_path.StartsWith("/"))
+ if (m_path.Length != 0 && !m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
- if (m_path.EndsWith("/"))
+ if (m_path.EndsWith("/", StringComparison.Ordinal))
m_path = m_path.Substring(0, m_path.Length - 1);
if (options.ContainsKey(AUTHID_OPTION))
@@ -117,7 +117,7 @@ namespace Duplicati.Library.Backend
string path = String.Format("{0}/{1}", m_path, remotename);
dbx.Delete(path);
}
- catch (DropboxException de)
+ catch (DropboxException)
{
// we can catch some events here and convert them to Duplicati exceptions
throw;
@@ -163,7 +163,7 @@ namespace Duplicati.Library.Backend
string path = string.Format("{0}/{1}", m_path, remotename);
dbx.UploadFile(path, stream);
}
- catch (DropboxException de)
+ catch (DropboxException)
{
// we can catch some events here and convert them to Duplicati exceptions
throw;
@@ -177,7 +177,7 @@ namespace Duplicati.Library.Backend
string path = string.Format("{0}/{1}", m_path, remotename);
dbx.DownloadFile(path, stream);
}
- catch (DropboxException de)
+ catch (DropboxException)
{
// we can catch some events here and convert them to Duplicati exceptions
throw;
diff --git a/Duplicati/Library/Backend/FTP/FTPBackend.cs b/Duplicati/Library/Backend/FTP/FTPBackend.cs
index 76bda999b..e09bd165e 100644
--- a/Duplicati/Library/Backend/FTP/FTPBackend.cs
+++ b/Duplicati/Library/Backend/FTP/FTPBackend.cs
@@ -83,7 +83,7 @@ namespace Duplicati.Library.Backend
m_userInfo.Domain = "";
m_url = u.SetScheme("ftp").SetQuery(null).SetCredentials(null, null).ToString();
- if (!m_url.EndsWith("/"))
+ if (!m_url.EndsWith("/", StringComparison.Ordinal))
m_url += "/";
m_useSSL = Utility.Utility.ParseBoolOption(options, "use-ssl");
@@ -278,7 +278,7 @@ namespace Duplicati.Library.Backend
{
IEnumerable files = List(remotename);
foreach(IFileEntry fe in files)
- if (fe.Name.Equals(remotename) || fe.Name.EndsWith("/" + remotename) || fe.Name.EndsWith("\\" + remotename))
+ if (fe.Name.Equals(remotename) || fe.Name.EndsWith("/" + remotename, StringComparison.Ordinal) || fe.Name.EndsWith("\\" + remotename, StringComparison.Ordinal))
{
if (fe.Size < 0 || streamLen < 0 || fe.Size == streamLen)
return;
@@ -390,7 +390,7 @@ namespace Duplicati.Library.Backend
private System.Net.FtpWebRequest CreateRequest(string remotename, bool createFolder)
{
string url = m_url;
- if (createFolder && url.EndsWith("/"))
+ if (createFolder && url.EndsWith("/", StringComparison.Ordinal))
url = url.Substring(0, url.Length - 1);
System.Net.FtpWebRequest req = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(url + remotename);
diff --git a/Duplicati/Library/Backend/File/FileBackend.cs b/Duplicati/Library/Backend/File/FileBackend.cs
index 18774c176..343234e13 100644
--- a/Duplicati/Library/Backend/File/FileBackend.cs
+++ b/Duplicati/Library/Backend/File/FileBackend.cs
@@ -76,7 +76,7 @@ namespace Duplicati.Library.Backend
for (int i = 0; i < paths.Count; i++)
{
- if (paths[i].StartsWith("*:"))
+ if (paths[i].StartsWith("*:", StringComparison.Ordinal))
{
string rpl_path = paths[i].Substring(1);
paths.RemoveAt(i);
@@ -304,7 +304,7 @@ namespace Duplicati.Library.Backend
//TODO: Can trick this with symlinks, where the symlink is on one mounted volume,
// and the actual storage is on another
foreach (System.IO.DriveInfo di in System.IO.DriveInfo.GetDrives())
- if (path.StartsWith(Utility.Utility.AppendDirSeparator(di.Name)) && di.Name.Length > root.Length)
+ if (path.StartsWith(Utility.Utility.AppendDirSeparator(di.Name), StringComparison.Ordinal) && di.Name.Length > root.Length)
root = di.Name;
}
else
diff --git a/Duplicati/Library/Backend/File/Win32.cs b/Duplicati/Library/Backend/File/Win32.cs
index 2bc99b6d8..a0c69b672 100644
--- a/Duplicati/Library/Backend/File/Win32.cs
+++ b/Duplicati/Library/Backend/File/Win32.cs
@@ -95,13 +95,13 @@ namespace Duplicati.Library.Backend
//Strip it down from \\server\share\folder1\folder2\filename.extension to
// \\server\share
string minpath = path;
- if (!minpath.StartsWith("\\\\"))
+ if (!minpath.StartsWith("\\\\", StringComparison.Ordinal))
return false;
- int first = minpath.IndexOf("\\", 2);
+ int first = minpath.IndexOf("\\", 2, StringComparison.Ordinal);
if (first <= 0)
return false;
- int next = minpath.IndexOf("\\", first + 1);
+ int next = minpath.IndexOf("\\", first + 1, StringComparison.Ordinal);
if (next >= 0)
minpath = minpath.Substring(0, next);
diff --git a/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs b/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs
index 1ac463469..1d8bc335c 100644
--- a/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs
+++ b/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs
@@ -79,11 +79,11 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
m_bucket = uri.Host;
m_prefix = "/" + uri.Path;
- if (!m_prefix.EndsWith("/"))
+ if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// For GCS we do not use a leading slash
- if (m_prefix.StartsWith("/"))
+ if (m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
string authid;
diff --git a/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs b/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs
index feec7f4e8..d71db7813 100644
--- a/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs
+++ b/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs
@@ -48,7 +48,7 @@ namespace Duplicati.Library.Backend.GoogleDrive
var uri = new Utility.Uri(url);
m_path = uri.HostAndPath;
- if (!m_path.EndsWith("/"))
+ if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
string authid = null;
diff --git a/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs b/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs
index ba1bbd0d5..46f797351 100644
--- a/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs
+++ b/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs
@@ -115,7 +115,7 @@ namespace Duplicati.Library.Backend
m_path = u.HostAndPath; // Host and path of "jottacloud://folder/subfolder" is "folder/subfolder", so the actual folder path within the mount point.
if (string.IsNullOrEmpty(m_path)) // Require a folder. Actually it is possible to store files directly on the root level of the mount point, but that does not seem to be a good option.
throw new UserInformationException(Strings.Jottacloud.NoPathError);
- if (!m_path.EndsWith("/"))
+ if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
if (!string.IsNullOrEmpty(u.Username))
{
@@ -300,7 +300,7 @@ namespace Duplicati.Library.Backend
req.Credentials = m_userInfo;
req.PreAuthenticate = true; // We need this under Mono for some reason, and it appears some servers require this as well
req.KeepAlive = false;
- req.UserAgent = "Duplicati Jottacloud Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ req.UserAgent = "Duplicati Jottacloud Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
req.Headers.Add("x-jftp-version", API_VERSION);
return req;
}
diff --git a/Duplicati/Library/Backend/OAuthHelper/JSONWebHelper.cs b/Duplicati/Library/Backend/OAuthHelper/JSONWebHelper.cs
index 426ede3dc..3547a8a05 100644
--- a/Duplicati/Library/Backend/OAuthHelper/JSONWebHelper.cs
+++ b/Duplicati/Library/Backend/OAuthHelper/JSONWebHelper.cs
@@ -27,7 +27,7 @@ namespace Duplicati.Library
{
public class JSONWebHelper
{
- public static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
+ public static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
private readonly string m_user_agent;
public string OAuthLoginUrl { get; protected set; }
public string UserAgent { get { return m_user_agent; } }
diff --git a/Duplicati/Library/Backend/OneDrive/OneDrive.cs b/Duplicati/Library/Backend/OneDrive/OneDrive.cs
index f93f6102d..390eff0fe 100644
--- a/Duplicati/Library/Backend/OneDrive/OneDrive.cs
+++ b/Duplicati/Library/Backend/OneDrive/OneDrive.cs
@@ -21,7 +21,7 @@ namespace Duplicati.Library.Backend
private const long BITS_FILE_SIZE_LIMIT = 1024 * 1024 * 15;
private const long BITS_CHUNK_SIZE = 1024 * 1024 * 10;
- private static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
+ private static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
private string m_rootfolder;
private string m_prefix;
@@ -42,7 +42,7 @@ namespace Duplicati.Library.Backend
m_rootfolder = uri.Host;
m_prefix = "/" + uri.Path;
- if (!m_prefix.EndsWith("/"))
+ if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
string authid = null;
diff --git a/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs b/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs
index fa801bd77..ed4f88cba 100644
--- a/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs
+++ b/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs
@@ -188,11 +188,11 @@ namespace Duplicati.Library.Backend.OpenStack
m_container = uri.Host;
m_prefix = "/" + uri.Path;
- if (!m_prefix.EndsWith("/"))
+ if (!m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// For OpenStack we do not use a leading slash
- if (m_prefix.StartsWith("/"))
+ if (m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
options.TryGetValue(USERNAME_OPTION, out m_username);
@@ -232,7 +232,7 @@ namespace Duplicati.Library.Backend.OpenStack
private string JoinUrls(string uri, string fragment)
{
fragment = fragment ?? "";
- return uri + (uri.EndsWith("/") ? "" : "/") + (fragment.StartsWith("/") ? fragment.Substring(1) : fragment);
+ return uri + (uri.EndsWith("/", StringComparison.Ordinal) ? "" : "/") + (fragment.StartsWith("/", StringComparison.Ordinal) ? fragment.Substring(1) : fragment);
}
private string JoinUrls(string uri, string fragment1, string fragment2)
{
@@ -338,7 +338,7 @@ namespace Duplicati.Library.Backend.OpenStack
foreach (var n in items)
{
var name = n.name;
- if (name.StartsWith(m_prefix))
+ if (name.StartsWith(m_prefix, StringComparison.Ordinal))
name = name.Substring(m_prefix.Length);
if (n.bytes == null)
diff --git a/Duplicati/Library/Backend/S3/S3Backend.cs b/Duplicati/Library/Backend/S3/S3Backend.cs
index c287fe491..4d3e9ddf6 100644
--- a/Duplicati/Library/Backend/S3/S3Backend.cs
+++ b/Duplicati/Library/Backend/S3/S3Backend.cs
@@ -218,25 +218,25 @@ namespace Duplicati.Library.Backend
{
m_bucket = Library.Utility.Uri.UrlDecode(u.PathAndQuery);
- if (m_bucket.StartsWith("/"))
+ if (m_bucket.StartsWith("/", StringComparison.Ordinal))
m_bucket = m_bucket.Substring(1);
if (m_bucket.Contains("/"))
{
- m_prefix = m_bucket.Substring(m_bucket.IndexOf("/") + 1);
- m_bucket = m_bucket.Substring(0, m_bucket.IndexOf("/"));
+ m_prefix = m_bucket.Substring(m_bucket.IndexOf("/", StringComparison.Ordinal) + 1);
+ m_bucket = m_bucket.Substring(0, m_bucket.IndexOf("/", StringComparison.Ordinal));
}
}
else
{
//Subdomain type lookup
- if (host.ToLower().EndsWith("." + s3host))
+ if (host.ToLower().EndsWith("." + s3host, StringComparison.Ordinal))
{
m_bucket = host.Substring(0, host.Length - ("." + s3host).Length);
host = s3host;
m_prefix = Library.Utility.Uri.UrlDecode(u.PathAndQuery);
- if (m_prefix.StartsWith("/"))
+ if (m_prefix.StartsWith("/", StringComparison.Ordinal))
m_prefix = m_prefix.Substring(1);
}
else
@@ -255,7 +255,7 @@ namespace Duplicati.Library.Backend
m_options = options;
m_prefix = m_prefix.Trim();
- if (m_prefix.Length != 0 && !m_prefix.EndsWith("/"))
+ if (m_prefix.Length != 0 && !m_prefix.EndsWith("/", StringComparison.Ordinal))
m_prefix += "/";
// Auto-disable dns lookup for non AWS configurations
@@ -315,7 +315,7 @@ namespace Duplicati.Library.Backend
((FileEntry)file).Name = file.Name.Substring(m_prefix.Length);
//Fix for a bug in Duplicati 1.0 beta 3 and earlier, where filenames are incorrectly prefixed with a slash
- if (file.Name.StartsWith("/") && !m_prefix.StartsWith("/"))
+ if (file.Name.StartsWith("/", StringComparison.Ordinal) && !m_prefix.StartsWith("/", StringComparison.Ordinal))
((FileEntry)file).Name = file.Name.Substring(1);
yield return file;
@@ -362,7 +362,7 @@ namespace Duplicati.Library.Backend
//This is a fix for the S3 backend prior to beta 3, where the filenames had a slash prefixed
try
{
- if (!remotename.StartsWith("/"))
+ if (!remotename.StartsWith("/", StringComparison.Ordinal))
Connection.GetFileStream(m_bucket, GetFullKey("/" + remotename), output);
return;
}
diff --git a/Duplicati/Library/Backend/SSHv2/KeyUploader.cs b/Duplicati/Library/Backend/SSHv2/KeyUploader.cs
index fd4f6464c..7f72ce4dd 100644
--- a/Duplicati/Library/Backend/SSHv2/KeyUploader.cs
+++ b/Duplicati/Library/Backend/SSHv2/KeyUploader.cs
@@ -99,7 +99,7 @@ namespace Duplicati.Library.Backend
}
var keys = authorized_keys == null ? new string[0] : authorized_keys.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
- var cleaned_keys = keys.Select(x => x.Trim()).Where(x => x.Length > 0 && !x.StartsWith("#"));
+ var cleaned_keys = keys.Select(x => x.Trim()).Where(x => x.Length > 0 && !x.StartsWith("#", StringComparison.Ordinal));
// Does the key already exist?
if (cleaned_keys.Where(x =>
diff --git a/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
index 1131c521f..c91b0d6a5 100644
--- a/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
+++ b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
@@ -78,10 +78,10 @@ namespace Duplicati.Library.Backend
m_path = uri.Path;
- if (!string.IsNullOrWhiteSpace(m_path) && !m_path.EndsWith("/"))
+ if (!string.IsNullOrWhiteSpace(m_path) && !m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
- if (!m_path.StartsWith("/"))
+ if (!m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
m_server = uri.Host;
@@ -114,7 +114,7 @@ namespace Duplicati.Library.Backend
CreateConnection();
//Bugfix, some SSH servers do not like a trailing slash
string p = m_path;
- if (p.EndsWith("/"))
+ if (p.EndsWith("/", StringComparison.Ordinal))
p.Substring(0, p.Length - 1);
m_con.CreateDirectory(p);
}
@@ -297,7 +297,7 @@ namespace Duplicati.Library.Backend
string working_dir = m_con.WorkingDirectory;
- if (!working_dir.EndsWith("/"))
+ if (!working_dir.EndsWith("/", StringComparison.Ordinal))
working_dir += "/";
if (working_dir == path)
@@ -322,33 +322,40 @@ namespace Duplicati.Library.Backend
foreach (Renci.SshNet.Sftp.SftpFile ls in m_con.ListDirectory(path))
if (ls.Name.ToString() != "." && ls.Name.ToString() != "..")
- yield return new FileEntry(ls.Name.ToString(), ls.Length, ls.LastAccessTime, ls.LastWriteTime) { IsFolder = ls.Attributes.IsDirectory };
+ yield return new FileEntry(ls.Name, ls.Length, ls.LastAccessTime, ls.LastWriteTime) { IsFolder = ls.Attributes.IsDirectory };
}
public static Renci.SshNet.PrivateKeyFile ValidateKeyFile(string filename, string password)
{
- if (filename.StartsWith(KEYFILE_URI, StringComparison.OrdinalIgnoreCase))
+ try
{
- using (var ms = new System.IO.MemoryStream())
- using (var sr = new System.IO.StreamWriter(ms))
+ if (filename.StartsWith(KEYFILE_URI, StringComparison.OrdinalIgnoreCase))
{
- sr.Write(Duplicati.Library.Utility.Uri.UrlDecode(filename.Substring(KEYFILE_URI.Length)));
- sr.Flush();
+ using (var ms = new System.IO.MemoryStream())
+ using (var sr = new System.IO.StreamWriter(ms))
+ {
+ sr.Write(Duplicati.Library.Utility.Uri.UrlDecode(filename.Substring(KEYFILE_URI.Length)));
+ sr.Flush();
- ms.Position = 0;
+ ms.Position = 0;
+ if (String.IsNullOrEmpty(password))
+ return new Renci.SshNet.PrivateKeyFile(ms);
+ else
+ return new Renci.SshNet.PrivateKeyFile(ms, password);
+ }
+ }
+ else
+ {
if (String.IsNullOrEmpty(password))
- return new Renci.SshNet.PrivateKeyFile(ms);
+ return new Renci.SshNet.PrivateKeyFile(filename);
else
- return new Renci.SshNet.PrivateKeyFile(ms, password);
+ return new Renci.SshNet.PrivateKeyFile(filename, password);
}
}
- else
+ catch (Exception ex)
{
- if (String.IsNullOrEmpty(password))
- return new Renci.SshNet.PrivateKeyFile(filename);
- else
- return new Renci.SshNet.PrivateKeyFile(filename, password);
+ throw new UserInformationException(string.Format("Failed to parse the keyfile, check the key format and passphrase. Error message was {0}", ex.Message), ex);
}
}
diff --git a/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs b/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs
index 9d2bd0e77..221fe2488 100644
--- a/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs
+++ b/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs
@@ -162,9 +162,9 @@ namespace Duplicati.Library.Backend
m_spWebUrl = null;
m_serverRelPath = u.Path;
- if (!m_serverRelPath.StartsWith("/"))
+ if (!m_serverRelPath.StartsWith("/", StringComparison.Ordinal))
m_serverRelPath = "/" + m_serverRelPath;
- if (!m_serverRelPath.EndsWith("/"))
+ if (!m_serverRelPath.EndsWith("/", StringComparison.Ordinal))
m_serverRelPath += "/";
// remove marker for SP-Web
m_serverRelPath = m_serverRelPath.Replace("//", "/");
@@ -290,7 +290,7 @@ namespace Duplicati.Library.Backend
retCtx = null;
string path = orgUrl.Path;
- int webIndicatorPos = path.IndexOf("//");
+ int webIndicatorPos = path.IndexOf("//", StringComparison.Ordinal);
// if a hint is supplied, we will of course use this first.
if (webIndicatorPos >= 0)
@@ -364,7 +364,6 @@ namespace Duplicati.Library.Backend
/// We have to check for the exceptions thrown to know about file /folder existence.
/// Why the funny guys at MS provided an .Exists field stays a mystery...
///
- /// the filename
private void wrappedExecuteQueryOnConext(SP.ClientContext ctx, string serverRelPathInfo, bool isFolder)
{
try { ctx.ExecuteQuery(); }
diff --git a/Duplicati/Library/Backend/Sia/Sia.cs b/Duplicati/Library/Backend/Sia/Sia.cs
index 636c2dc63..2839682d4 100644
--- a/Duplicati/Library/Backend/Sia/Sia.cs
+++ b/Duplicati/Library/Backend/Sia/Sia.cs
@@ -46,9 +46,9 @@ namespace Duplicati.Library.Backend.Sia
}
while(m_targetpath.Contains("//"))
m_targetpath = m_targetpath.Replace("//","/");
- while (m_targetpath.StartsWith("/"))
+ while (m_targetpath.StartsWith("/", StringComparison.Ordinal))
m_targetpath = m_targetpath.Substring(1);
- while (m_targetpath.EndsWith("/"))
+ while (m_targetpath.EndsWith("/", StringComparison.Ordinal))
m_targetpath = m_targetpath.Remove(m_targetpath.Length - 1);
if (m_targetpath.Length == 0)
@@ -73,7 +73,7 @@ namespace Duplicati.Library.Backend.Sia
}
req.KeepAlive = false;
- req.UserAgent = string.Format("Sia-Agent (Duplicati SIA client {0})", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
+ req.UserAgent = string.Format("Sia-Agent (Duplicati SIA client {0})", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
return req;
}
@@ -294,7 +294,7 @@ namespace Duplicati.Library.Backend.Sia
{
// Sia returns a complete file list, but we're only interested in files that are
// in our target path
- if (f.Siapath.StartsWith(m_targetpath))
+ if (f.Siapath.StartsWith(m_targetpath, StringComparison.Ordinal))
{
FileEntry fe = new FileEntry(f.Siapath.Substring(m_targetpath.Length + 1));
fe.Size = f.Filesize;
diff --git a/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs b/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs
index 2c78d77a9..274f56ffd 100644
--- a/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs
+++ b/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs
@@ -99,13 +99,13 @@ namespace Duplicati.Library.Backend
var u = new Utility.Uri(url);
u.RequireHost();
- if (!u.Path.StartsWith("uri/URI:DIR2:") && !u.Path.StartsWith("uri/URI%3ADIR2%3A"))
+ if (!u.Path.StartsWith("uri/URI:DIR2:", StringComparison.Ordinal) && !u.Path.StartsWith("uri/URI%3ADIR2%3A", StringComparison.Ordinal))
throw new UserInformationException(Strings.TahoeBackend.UnrecognizedUriError);
m_useSSL = Utility.Utility.ParseBoolOption(options, "use-ssl");
m_url = u.SetScheme(m_useSSL ? "https" : "http").SetQuery(null).SetCredentials(null, null).ToString();
- if (!m_url.EndsWith("/"))
+ if (!m_url.EndsWith("/", StringComparison.Ordinal))
m_url += "/";
}
@@ -114,7 +114,7 @@ namespace Duplicati.Library.Backend
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(m_url + (Library.Utility.Uri.UrlEncode(remotename).Replace("+", "%20")) + (string.IsNullOrEmpty(queryparams) || queryparams.Trim().Length == 0 ? "" : "?" + queryparams));
req.KeepAlive = false;
- req.UserAgent = "Duplicati Tahoe-LAFS Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ req.UserAgent = "Duplicati Tahoe-LAFS Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
return req;
}
diff --git a/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs b/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs
index d35815f16..8272e43ce 100644
--- a/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs
+++ b/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs
@@ -91,13 +91,13 @@ namespace Duplicati.Library.Backend
m_useSSL = Utility.Utility.ParseBoolOption(options, "use-ssl");
m_url = u.SetScheme(m_useSSL ? "https" : "http").SetCredentials(null, null).SetQuery(null).ToString();
- if (!m_url.EndsWith("/"))
+ if (!m_url.EndsWith("/", StringComparison.Ordinal))
m_url += "/";
m_path = u.Path;
- if (!m_path.StartsWith("/"))
+ if (!m_path.StartsWith("/", StringComparison.Ordinal))
m_path = "/" + m_path;
- if (!m_path.EndsWith("/"))
+ if (!m_path.EndsWith("/", StringComparison.Ordinal))
m_path += "/";
m_path = Library.Utility.Uri.UrlDecode(m_path);
@@ -195,19 +195,19 @@ namespace Duplicati.Library.Backend
//TODO: This list is getting ridiculous, should change to regexps
- if (name.StartsWith(m_url))
+ if (name.StartsWith(m_url, StringComparison.Ordinal))
cmp_path = m_url;
- else if (name.StartsWith(m_rawurl))
+ else if (name.StartsWith(m_rawurl, StringComparison.Ordinal))
cmp_path = m_rawurl;
- else if (name.StartsWith(m_rawurlPort))
+ else if (name.StartsWith(m_rawurlPort, StringComparison.Ordinal))
cmp_path = m_rawurlPort;
- else if (name.StartsWith(m_path))
+ else if (name.StartsWith(m_path, StringComparison.Ordinal))
cmp_path = m_path;
- else if (name.StartsWith("/" + m_path))
+ else if (name.StartsWith("/" + m_path, StringComparison.Ordinal))
cmp_path = "/" + m_path;
- else if (name.StartsWith(m_sanitizedUrl))
+ else if (name.StartsWith(m_sanitizedUrl, StringComparison.Ordinal))
cmp_path = m_sanitizedUrl;
- else if (name.StartsWith(m_reverseProtocolUrl))
+ else if (name.StartsWith(m_reverseProtocolUrl, StringComparison.Ordinal))
cmp_path = m_reverseProtocolUrl;
else
continue;
@@ -360,7 +360,7 @@ namespace Duplicati.Library.Backend
}
req.KeepAlive = false;
- req.UserAgent = "Duplicati WEBDAV Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ req.UserAgent = "Duplicati WEBDAV Client v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
return req;
}
diff --git a/Duplicati/Library/Compression/FileArchiveZip.cs b/Duplicati/Library/Compression/FileArchiveZip.cs
index 4f1dc9c07..4ce923bf4 100644
--- a/Duplicati/Library/Compression/FileArchiveZip.cs
+++ b/Duplicati/Library/Compression/FileArchiveZip.cs
@@ -24,7 +24,6 @@ using Duplicati.Library.Interface;
using SharpCompress.Common;
using SharpCompress.Archives;
-using SharpCompress.Archives.Zip;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;
using SharpCompress.Readers;
@@ -34,10 +33,13 @@ namespace Duplicati.Library.Compression
{
///
/// An abstraction of a zip archive as a FileArchive, based on SharpCompress.
- /// Please note, duplicati does not require both Read & Write access at the same time so this has not been implemented
+ /// Please note, duplicati does not require both Read & Write access at the same time so this has not been implemented.
///
public class FileArchiveZip : ICompression
{
+ private const string CannotReadWhileWriting = "Cannot read while writing";
+ private const string CannotWriteWhileReading = "Cannot write while reading";
+
///
/// The commandline option for toggling the compression level
///
@@ -51,16 +53,16 @@ namespace Duplicati.Library.Compression
///
/// The commandline option for toggling the compression method
///
- private const string COMPRESSION_METHOD_OPTION = "zip-compression-method";
- ///
- /// The commandline option for toggling the zip64 support
- ///
- private const string COMPRESSION_ZIP64_OPTION = "zip-compression-zip64";
-
- ///
- /// The default compression level
- ///
- private const SharpCompress.Compressors.Deflate.CompressionLevel DEFAULT_COMPRESSION_LEVEL = SharpCompress.Compressors.Deflate.CompressionLevel.Level9;
+ private const string COMPRESSION_METHOD_OPTION = "zip-compression-method";
+ ///
+ /// The commandline option for toggling the zip64 support
+ ///
+ private const string COMPRESSION_ZIP64_OPTION = "zip-compression-zip64";
+
+ ///
+ /// The default compression level
+ ///
+ private const SharpCompress.Compressors.Deflate.CompressionLevel DEFAULT_COMPRESSION_LEVEL = SharpCompress.Compressors.Deflate.CompressionLevel.Level9;
///
/// The default compression method
@@ -83,9 +85,9 @@ namespace Duplicati.Library.Compression
private const int CENTRAL_HEADER_ENTRY_SIZE_ZIP64_EXTRA = 2 + 2 + 8 + 8 + 8 + 4;
///
- /// This property indicates that this current instance should write to a file
+ /// This property indicates reading or writing access mode of the file archive.
///
- private bool m_isWriting;
+ ArchiveMode m_mode;
///
/// Gets the number of bytes expected to be written after the stream is disposed
@@ -100,12 +102,12 @@ namespace Duplicati.Library.Compression
/// The stream used to either read or write
///
private Stream m_stream;
-
+
///
/// Lookup table for faster access to entries based on their name.
///
private Dictionary m_entryDict;
-
+
///
/// The writer instance used when creating archives
///
@@ -119,18 +121,13 @@ namespace Duplicati.Library.Compression
///
/// The compression level applied when the hint does not indicate incompressible
///
- private SharpCompress.Compressors.Deflate.CompressionLevel m_defaultCompressionLevel;
+ private SharpCompress.Compressors.Deflate.CompressionLevel m_defaultCompressionLevel;
- ///
+ ///
/// The compression level applied when the hint does not indicate incompressible
///
private CompressionType m_compressionType;
- ///
- /// The name of the file being read
- ///
- private string m_filename;
-
///
/// A flag indicating if zip64 is in use
///
@@ -141,14 +138,15 @@ namespace Duplicati.Library.Compression
///
public FileArchiveZip() { }
- public IArchive Archive
+ private IArchive Archive
{
get
{
- if (m_stream == null)
- m_stream = new System.IO.FileStream(m_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
if (m_archive == null)
+ {
+ m_stream.Position = 0;
m_archive = ArchiveFactory.Open(m_stream);
+ }
return m_archive;
}
}
@@ -168,19 +166,16 @@ namespace Duplicati.Library.Compression
public Stream GetStreamFromReader(IEntry entry)
{
- Stream fs = null;
SharpCompress.Readers.Zip.ZipReader rd = null;
try
{
- fs = new System.IO.FileStream(m_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
- rd = SharpCompress.Readers.Zip.ZipReader.Open(fs);
+ rd = SharpCompress.Readers.Zip.ZipReader.Open(m_stream);
while (rd.MoveToNextEntry())
if (entry.Key == rd.Entry.Key)
return new StreamWrapper(rd.OpenEntryStream(), stream => {
rd.Dispose();
- fs.Dispose();
});
throw new Exception(string.Format("Stream not found: {0}", entry.Key));
@@ -189,9 +184,7 @@ namespace Duplicati.Library.Compression
{
if (rd != null)
rd.Dispose();
- if (fs != null)
- fs.Dispose();
-
+
throw;
}
@@ -199,22 +192,18 @@ namespace Duplicati.Library.Compression
///
/// Constructs a new zip instance.
- /// If the file exists and has a non-zero length we read it,
- /// otherwise we create a new archive.
+ /// Access mode is specified by mode parameter.
+ /// Note that stream would not be disposed by FileArchiveZip instance so
+ /// you may reuse it and have to dispose it yourself.
///
- /// The name of the file to read or write
+ /// The stream to read or write depending access mode
+ /// The archive acces mode
/// The options passed on the commandline
- public FileArchiveZip(string filename, Dictionary options)
+ public FileArchiveZip(Stream stream, ArchiveMode mode, IDictionary options)
{
- if (string.IsNullOrEmpty(filename) && filename.Trim().Length == 0)
- throw new ArgumentException("Invalid filename.", nameof(filename));
-
- if (File.Exists(filename) && new FileInfo(filename).Length > 0)
- {
- m_isWriting = false;
- m_filename = filename;
- }
- else
+ m_stream = stream;
+ m_mode = mode;
+ if (mode == ArchiveMode.Write)
{
var compression = new ZipWriterOptions(CompressionType.Deflate);
@@ -241,8 +230,6 @@ namespace Duplicati.Library.Compression
m_defaultCompressionLevel = compression.DeflateCompressionLevel;
m_compressionType = compression.CompressionType;
- m_isWriting = true;
- m_stream = new System.IO.FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read);
m_writer = WriterFactory.Open(m_stream, ArchiveType.Zip, compression);
//Size of endheader, taken from SharpCompress ZipWriter
@@ -270,26 +257,26 @@ namespace Duplicati.Library.Compression
public IList SupportedCommands
{
get
- {
- // This is the cross between these two:
- // https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs
- // https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/CompressionType.cs
-
- var methods = new[]
+ {
+ // This is the cross between these two:
+ // https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs
+ // https://github.com/adamhathcock/sharpcompress/blob/master/src/SharpCompress/Common/CompressionType.cs
+
+ var methods = new[]
{
- CompressionType.None.ToString(),
- CompressionType.Deflate.ToString(),
- CompressionType.BZip2.ToString(),
- CompressionType.LZMA.ToString(),
- CompressionType.PPMd.ToString(),
+ CompressionType.None.ToString(),
+ CompressionType.Deflate.ToString(),
+ CompressionType.BZip2.ToString(),
+ CompressionType.LZMA.ToString(),
+ CompressionType.PPMd.ToString(),
};
return new List(new ICommandLineArgument[] {
new CommandLineArgument(COMPRESSION_LEVEL_OPTION, CommandLineArgument.ArgumentType.Enumeration, Strings.FileArchiveZip.CompressionlevelShort, Strings.FileArchiveZip.CompressionlevelLong, DEFAULT_COMPRESSION_LEVEL.ToString(), null, new string[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}),
new CommandLineArgument(COMPRESSION_LEVEL_OPTION_ALIAS, CommandLineArgument.ArgumentType.Enumeration, Strings.FileArchiveZip.CompressionlevelShort, Strings.FileArchiveZip.CompressionlevelLong, DEFAULT_COMPRESSION_LEVEL.ToString(), null, new string[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, Strings.FileArchiveZip.CompressionlevelDeprecated(COMPRESSION_LEVEL_OPTION)),
new CommandLineArgument(COMPRESSION_METHOD_OPTION, CommandLineArgument.ArgumentType.Enumeration, Strings.FileArchiveZip.CompressionmethodShort, Strings.FileArchiveZip.CompressionmethodLong(COMPRESSION_LEVEL_OPTION), DEFAULT_COMPRESSION_METHOD.ToString(), null, methods),
- new CommandLineArgument(COMPRESSION_ZIP64_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.FileArchiveZip.Compressionzip64Short, Strings.FileArchiveZip.Compressionzip64Long, DEFAULT_ZIP64.ToString())
- });
+ new CommandLineArgument(COMPRESSION_ZIP64_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.FileArchiveZip.Compressionzip64Short, Strings.FileArchiveZip.Compressionzip64Long, DEFAULT_ZIP64.ToString())
+ });
}
}
@@ -329,8 +316,8 @@ namespace Duplicati.Library.Compression
/// A stream with the file contents
public Stream OpenRead(string file)
{
- if (m_isWriting)
- throw new InvalidOperationException("Cannot read while writing");
+ if (m_mode != ArchiveMode.Read)
+ throw new InvalidOperationException(CannotReadWhileWriting);
var ze = GetEntry(file);
if (ze == null)
@@ -373,8 +360,7 @@ namespace Duplicati.Library.Compression
try
{
- using (var fs = new System.IO.FileStream(m_filename, FileMode.Open, FileAccess.Read, FileShare.Read))
- using (var rd = SharpCompress.Readers.Zip.ZipReader.Open(fs, new ReaderOptions() { LookForHeader = false }))
+ using (var rd = SharpCompress.Readers.Zip.ZipReader.Open(m_stream, new ReaderOptions() { LookForHeader = false }))
while (rd.MoveToNextEntry())
{
d[rd.Entry.Key] = rd.Entry;
@@ -390,10 +376,10 @@ namespace Duplicati.Library.Compression
// If we have zero files, or just a manifest, don't bother
if (d.Count < 2)
throw;
-
+
Logging.Log.WriteMessage(string.Format("Zip archive appears to have broken records, returning the {0} records that could be recovered", d.Count), Logging.LogMessageType.Warning, ex2);
}
-
+
m_entryDict = d;
}
}
@@ -406,8 +392,8 @@ namespace Duplicati.Library.Compression
/// The ZipEntry for the file or null if no such file was found
private IEntry GetEntry(string file)
{
- if (m_isWriting)
- throw new InvalidOperationException("Cannot read while writing");
+ if (m_mode != ArchiveMode.Read)
+ throw new InvalidOperationException(CannotReadWhileWriting);
LoadEntryTable();
@@ -429,13 +415,13 @@ namespace Duplicati.Library.Compression
/// A writeable stream for the file contents
public virtual Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite)
{
- if (!m_isWriting)
- throw new InvalidOperationException("Cannot write while reading");
+ if (m_mode != ArchiveMode.Write)
+ throw new InvalidOperationException(CannotWriteWhileReading);
m_flushBufferSize += CENTRAL_HEADER_ENTRY_SIZE + System.Text.Encoding.UTF8.GetByteCount(file);
if (m_usingZip64)
m_flushBufferSize += CENTRAL_HEADER_ENTRY_SIZE_ZIP64_EXTRA;
-
+
return ((ZipWriter)m_writer).WriteToStream(file, new ZipWriterEntryOptions()
{
DeflateCompressionLevel = hint == CompressionHint.Noncompressible ? SharpCompress.Compressors.Deflate.CompressionLevel.None : m_defaultCompressionLevel,
@@ -452,8 +438,8 @@ namespace Duplicati.Library.Compression
/// True if the file exists, false otherwise
public bool FileExists(string file)
{
- if (m_isWriting)
- throw new InvalidOperationException("Cannot read while writing");
+ if (m_mode != ArchiveMode.Read)
+ throw new InvalidOperationException(CannotReadWhileWriting);
return GetEntry(file) != null;
}
@@ -465,7 +451,7 @@ namespace Duplicati.Library.Compression
{
get
{
- return m_isWriting ? m_stream.Length : Archive.TotalSize;
+ return m_mode == ArchiveMode.Write ? m_stream.Length : Archive.TotalSize;
}
}
@@ -473,11 +459,11 @@ namespace Duplicati.Library.Compression
/// The size of the current unflushed buffer
///
public long FlushBufferSize
- {
+ {
get
{
return m_flushBufferSize;
- }
+ }
}
@@ -514,8 +500,6 @@ namespace Duplicati.Library.Compression
m_writer.Dispose();
m_writer = null;
- if (m_stream != null)
- m_stream.Dispose();
m_stream = null;
}
diff --git a/Duplicati/Library/Compression/SevenZipCompression.cs b/Duplicati/Library/Compression/SevenZipCompression.cs
index 1622c9701..6d35b2311 100644
--- a/Duplicati/Library/Compression/SevenZipCompression.cs
+++ b/Duplicati/Library/Compression/SevenZipCompression.cs
@@ -2,17 +2,17 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using Duplicati.Library.Interface;
+using Duplicati.Library.Utility;
namespace Duplicati.Library.Compression
{
- public class SevenZipCompression: ICompressionHinting
+ public class SevenZipCompression : ICompressionHinting
{
// next file starts a new stream if the previous stream is larger than this
private const int kStreamThreshold = 1 << 20; // 1 MB
- private FileStream m_file;
+ private Stream m_stream;
private ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter m_writer;
private ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.PlainEncoder m_copyEncoder;
private ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.ThreadedEncoder m_lzma2Encoder;
@@ -33,9 +33,9 @@ namespace Duplicati.Library.Compression
get { return m_lowOverheadMode; }
set
{
- if(m_lowOverheadMode != value)
+ if (m_lowOverheadMode != value)
{
- if(m_lzma2Encoder != null)
+ if (m_lzma2Encoder != null)
throw new NotSupportedException("Cannot change LowOverheadMode after writing has started.");
m_lowOverheadMode = value;
@@ -50,29 +50,46 @@ namespace Duplicati.Library.Compression
///
/// Constructs a new zip instance.
- /// If the file exists and has a non-zero length we read it,
- /// otherwise we create a new archive.
+ /// Access mode is specified by mode parameter.
+ /// Note that stream would not be disposed by FileArchiveZip instance so
+ /// you may reuse it and have to dispose it yourself.
///
- /// The name of the file to read or write
+ /// The stream to read or write depending access mode
+ /// The archive acces mode
/// The options passed on the commandline
- public SevenZipCompression(string filename, Dictionary options)
+ public SevenZipCompression(Stream stream, ArchiveMode mode, IDictionary options)
+ {
+ InitializeCompression(options);
+ // Preventing the stream being closed by ArchiveWriter so it could be reused.
+ // Required for using with MemoryStream.
+ m_stream = new ShaderStream(stream, true);
+ if (mode == ArchiveMode.Write)
+ InitializeArchiveWriter();
+ else
+ {
+ stream.Position = 0;
+ InitializeArchiveReader();
+ }
+ }
+
+ private void InitializeCompression(IDictionary options)
{
m_threadCount = DEFAULT_THREAD_COUNT;
string threadCountSetting;
int threadCountValue;
- if(options != null
+ if (options != null
&& options.TryGetValue(THREAD_COUNT_OPTION, out threadCountSetting)
&& Int32.TryParse(threadCountSetting, out threadCountValue)
&& threadCountValue > 0)
{
// arbitrary limit to avoid stupid mistakes
- if(threadCountValue > MAX_THREAD_COUNT)
+ if (threadCountValue > MAX_THREAD_COUNT)
threadCountValue = MAX_THREAD_COUNT;
m_threadCount = threadCountValue;
}
-
+
string cplvl;
int tmplvl;
if (options.TryGetValue(COMPRESSION_LEVEL_OPTION, out cplvl) && int.TryParse(cplvl, out tmplvl))
@@ -85,67 +102,59 @@ namespace Duplicati.Library.Compression
m_encoderProps.mLzmaProps.mLevel = tmplvl;
m_encoderProps.mLzmaProps.mAlgo = Library.Utility.Utility.ParseBoolOption(options, COMPRESSION_FASTALGO_OPTION) ? 0 : 1;
-
- var file = new FileInfo(filename);
- if(file.Exists && file.Length > 0)
- InitializeArchiveReader(filename);
- else
- InitializeArchiveWriter(filename);
}
- private void InitializeArchiveReader(string file)
+ private void InitializeArchiveReader()
{
- m_file = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
m_archive = new master._7zip.Legacy.CArchiveDatabaseEx();
m_reader = new master._7zip.Legacy.ArchiveReader();
- m_reader.Open(m_file);
+ m_reader.Open(m_stream);
m_reader.ReadDatabase(m_archive, null);
m_archive.Fill();
}
- private void InitializeArchiveWriter(string file)
+ private void InitializeArchiveWriter()
{
- m_file = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Delete);
- m_writer = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter(m_file);
+ m_writer = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter(m_stream);
}
public void Dispose()
{
- if(m_archive != null)
+ if (m_archive != null)
m_archive = null;
- if(m_reader != null)
+ if (m_reader != null)
try { m_reader.Close(); }
finally { m_reader = null; }
- if(m_writer != null)
+ if (m_writer != null)
{
try
{
- if(m_copyEncoder != null)
+ if (m_copyEncoder != null)
try
{
m_writer.ConnectEncoder(m_copyEncoder);
m_copyEncoder.Dispose();
}
finally { m_copyEncoder = null; }
-
- if(m_lzma2Encoder != null)
+
+ if (m_lzma2Encoder != null)
try
{
m_writer.ConnectEncoder(m_lzma2Encoder);
m_lzma2Encoder.Dispose();
}
finally { m_lzma2Encoder = null; }
-
+
m_writer.WriteFinalHeader();
}
finally { m_writer = null; }
}
- if(m_file != null)
- try { m_file.Dispose(); }
- finally { m_file = null; }
+ if (m_stream != null)
+ try { m_stream.Dispose(); }
+ finally { m_stream = null; }
}
#region ICompression - Reader Methods
@@ -162,26 +171,26 @@ namespace Duplicati.Library.Compression
public IEnumerable> ListFilesWithSize(string prefix)
{
- if(m_reader == null)
+ if (m_reader == null)
throw new InvalidOperationException(Strings.SevenZipCompression.NoReaderError);
StringComparison sc = Library.Utility.Utility.ClientFilenameStringComparision;
- return
+ return
from n in m_reader.GetFiles(m_archive)
- where
- !n.IsDir &&
- (prefix == null || n.Name.StartsWith(prefix, sc))
- select new KeyValuePair(n.Name, n.Size);
+ where
+ !n.IsDir &&
+ (prefix == null || n.Name.StartsWith(prefix, sc))
+ select new KeyValuePair(n.Name, n.Size);
}
-
+
public DateTime GetLastWriteTime(string file)
{
var item = m_reader.GetFiles(m_archive).FirstOrDefault(x => x.Name == file);
- if(item == null)
+ if (item == null)
throw new FileNotFoundException(Strings.SevenZipCompression.FileNotFoundError, file);
- if(!item.MTime.HasValue)
+ if (!item.MTime.HasValue)
return new DateTime(0);
return item.MTime.Value;
@@ -189,14 +198,14 @@ namespace Duplicati.Library.Compression
public Stream OpenRead(string file)
{
- if(string.IsNullOrEmpty(file))
- throw new ArgumentNullException("file");
+ if (string.IsNullOrEmpty(file))
+ throw new ArgumentNullException(nameof(file));
- if(m_reader == null)
+ if (m_reader == null)
throw new InvalidOperationException(Strings.SevenZipCompression.NoReaderError);
var item = m_reader.GetFiles(m_archive).FirstOrDefault(x => x.Name == file);
- if(item == null)
+ if (item == null)
throw new FileNotFoundException(Strings.SevenZipCompression.FileNotFoundError, file);
return m_reader.OpenStream(m_archive, m_reader.GetFileIndex(m_archive, item), null);
@@ -211,7 +220,7 @@ namespace Duplicati.Library.Compression
m_writer.ConnectEncoder(m_lzma2Encoder);
}
- private sealed class WriterEntry: ManagedLzma.LZMA.Master.SevenZip.IArchiveWriterEntry
+ private sealed class WriterEntry : ManagedLzma.LZMA.Master.SevenZip.IArchiveWriterEntry
{
private string mName;
private DateTime? mTimestamp;
@@ -254,19 +263,19 @@ namespace Duplicati.Library.Compression
public Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite)
{
- if(string.IsNullOrEmpty(file))
- throw new ArgumentNullException("file");
+ if (string.IsNullOrEmpty(file))
+ throw new ArgumentNullException(nameof(file));
- if(m_writer == null)
+ if (m_writer == null)
throw new InvalidOperationException(Strings.SevenZipCompression.NoWriterError);
var entry = new WriterEntry(file, lastWrite);
- if(hint != CompressionHint.Noncompressible)
+ if (hint != CompressionHint.Noncompressible)
{
- if(m_lzma2Encoder == null)
+ if (m_lzma2Encoder == null)
{
- if(m_lowOverheadMode)
+ if (m_lowOverheadMode)
m_lzma2Encoder = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.LzmaEncoder();
else
m_lzma2Encoder = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.Lzma2Encoder(m_threadCount, m_encoderProps);
@@ -279,13 +288,13 @@ namespace Duplicati.Library.Compression
}
else
{
- if(m_copyEncoder == null)
+ if (m_copyEncoder == null)
m_copyEncoder = new ManagedLzma.LZMA.Master.SevenZip.ArchiveWriter.PlainEncoder();
- if(m_lzma2Encoder != null && m_lzma2Encoder == m_writer.CurrentEncoder)
+ if (m_lzma2Encoder != null && m_lzma2Encoder == m_writer.CurrentEncoder)
m_lzma2Encoder.SetOutputThreshold(kStreamThreshold); // rearm threshold so we can switch back
- if(m_writer.CurrentEncoder != m_copyEncoder)
+ if (m_writer.CurrentEncoder != m_copyEncoder)
m_writer.ConnectEncoder(m_copyEncoder);
return m_copyEncoder.BeginWriteFile(entry);
@@ -296,10 +305,10 @@ namespace Duplicati.Library.Compression
{
get
{
- if(m_writer != null)
+ if (m_writer != null)
return m_writer.WrittenSize;
- else if(m_reader != null)
- return m_file.Length;
+ else if (m_reader != null)
+ return m_stream.Length;
else
throw new InvalidOperationException();
}
@@ -309,17 +318,17 @@ namespace Duplicati.Library.Compression
{
get
{
- if(m_writer != null)
+ if (m_writer != null)
{
long remaining = m_writer.CurrentSizeLimit - m_writer.WrittenSize;
- if(m_copyEncoder != null && m_copyEncoder != m_writer.CurrentEncoder)
+ if (m_copyEncoder != null && m_copyEncoder != m_writer.CurrentEncoder)
remaining += m_copyEncoder.UpperBound;
- if(m_lzma2Encoder != null && m_lzma2Encoder != m_writer.CurrentEncoder)
+ if (m_lzma2Encoder != null && m_lzma2Encoder != m_writer.CurrentEncoder)
remaining += m_lzma2Encoder.UpperBound;
return remaining;
}
- else if(m_reader != null)
- return m_file.Length;
+ else if (m_reader != null)
+ return m_stream.Length;
else
throw new InvalidOperationException();
}
@@ -392,12 +401,12 @@ namespace Duplicati.Library.Compression
DEFAULT_THREAD_COUNT.ToString()),
new CommandLineArgument(
- COMPRESSION_LEVEL_OPTION,
- CommandLineArgument.ArgumentType.Enumeration,
- Strings.SevenZipCompression.CompressionlevelShort,
+ COMPRESSION_LEVEL_OPTION,
+ CommandLineArgument.ArgumentType.Enumeration,
+ Strings.SevenZipCompression.CompressionlevelShort,
Strings.SevenZipCompression.CompressionlevelLong,
- DEFAULT_COMPRESSION_LEVEL.ToString(),
- null,
+ DEFAULT_COMPRESSION_LEVEL.ToString(),
+ null,
new string[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}),
new CommandLineArgument(
diff --git a/Duplicati/Library/DynamicLoader/BackendLoader.cs b/Duplicati/Library/DynamicLoader/BackendLoader.cs
index fab99a87d..78632b3a2 100644
--- a/Duplicati/Library/DynamicLoader/BackendLoader.cs
+++ b/Duplicati/Library/DynamicLoader/BackendLoader.cs
@@ -77,7 +77,7 @@ namespace Duplicati.Library.DynamicLoader
{
if (m_interfaces.ContainsKey(uri.Scheme))
return (IBackend)Activator.CreateInstance(m_interfaces[uri.Scheme].GetType(), url, newOpts);
- else if (uri.Scheme.EndsWith("s"))
+ else if (uri.Scheme.EndsWith("s", StringComparison.Ordinal))
{
var tmpscheme = uri.Scheme.Substring(0, uri.Scheme.Length - 1);
if (m_interfaces.ContainsKey(tmpscheme))
@@ -123,7 +123,7 @@ namespace Duplicati.Library.DynamicLoader
IBackend b;
if (m_interfaces.TryGetValue(uri.Scheme, out b) && b != null)
return b.SupportedCommands;
- else if (uri.Scheme.EndsWith("s"))
+ else if (uri.Scheme.EndsWith("s", StringComparison.Ordinal))
{
var tmpscheme = uri.Scheme.Substring(0, uri.Scheme.Length - 1);
if (m_interfaces.ContainsKey(tmpscheme))
diff --git a/Duplicati/Library/DynamicLoader/CompressionLoader.cs b/Duplicati/Library/DynamicLoader/CompressionLoader.cs
index 70db8bf1b..293132f65 100644
--- a/Duplicati/Library/DynamicLoader/CompressionLoader.cs
+++ b/Duplicati/Library/DynamicLoader/CompressionLoader.cs
@@ -19,6 +19,7 @@
#endregion
using System;
using System.Collections.Generic;
+using System.IO;
using System.Text;
using Duplicati.Library.Interface;
@@ -59,17 +60,17 @@ namespace Duplicati.Library.DynamicLoader
/// The filename used to compress/decompress contents
/// The options to pass to the instance constructor
/// The instanciated encryption module or null if the file extension is not supported
- public ICompression GetModule(string fileExtension, string filename, Dictionary options)
+ public ICompression GetModule(string fileExtension, Stream stream, ArchiveMode mode, Dictionary options)
{
if (string.IsNullOrEmpty(fileExtension))
throw new ArgumentNullException("fileExtension");
LoadInterfaces();
-
+
lock (m_lock)
{
if (m_interfaces.ContainsKey(fileExtension))
- return (ICompression)Activator.CreateInstance(m_interfaces[fileExtension].GetType(), filename, options);
+ return (ICompression)Activator.CreateInstance(m_interfaces[fileExtension].GetType(), stream, mode, options);
else
return null;
}
@@ -125,17 +126,17 @@ namespace Duplicati.Library.DynamicLoader
return _compressionLoader.GetSupportedCommands(key);
}
-
///
/// Instanciates a specific compression module, given the file extension and options
///
- /// The file extension to create the instance for
- /// The filename of the file used to compress/decompress contents
+ /// The file extension to create the instance for
+ /// The stream of the file used to compress/decompress contents
+ /// True is the file opened for writing
/// The options to pass to the instance constructor
/// The instanciated compression module or null if the file extension is not supported
- public static ICompression GetModule(string fileextension, string filename, Dictionary options)
+ public static ICompression GetModule(string fileextension, Stream stream, ArchiveMode mode, Dictionary options)
{
- return _compressionLoader.GetModule(fileextension, filename, options);
+ return _compressionLoader.GetModule(fileextension, stream, mode, options);
}
#endregion
diff --git a/Duplicati/Library/DynamicLoader/EncryptionLoader.cs b/Duplicati/Library/DynamicLoader/EncryptionLoader.cs
index cd6e387c9..dacf9087d 100644
--- a/Duplicati/Library/DynamicLoader/EncryptionLoader.cs
+++ b/Duplicati/Library/DynamicLoader/EncryptionLoader.cs
@@ -132,7 +132,7 @@ namespace Duplicati.Library.DynamicLoader
///
/// Instanciates a specific encryption module, given the file extension and options
///
- /// The file extension to create the instance for
+ /// The file extension to create the instance for
/// The passphrase used to encrypt contents
/// The options to pass to the instance constructor
/// The instanciated encryption module or null if the file extension is not supported
diff --git a/Duplicati/Library/Encryption/AESEncryption.cs b/Duplicati/Library/Encryption/AESEncryption.cs
index d342e35c1..ba0bd9fcf 100644
--- a/Duplicati/Library/Encryption/AESEncryption.cs
+++ b/Duplicati/Library/Encryption/AESEncryption.cs
@@ -67,7 +67,6 @@ namespace Duplicati.Library.Encryption
///
/// Constructs a new AES encryption/decyption instance
///
- /// The key used for encryption. The key gets stretched through SHA hashing to fit the key size requirements
public AESEncryption(string passphrase, Dictionary options)
{
if(string.IsNullOrEmpty(passphrase))
diff --git a/Duplicati/Library/Encryption/GPGEncryption.cs b/Duplicati/Library/Encryption/GPGEncryption.cs
index ac1bfa597..03d1ca5db 100644
--- a/Duplicati/Library/Encryption/GPGEncryption.cs
+++ b/Duplicati/Library/Encryption/GPGEncryption.cs
@@ -222,7 +222,6 @@ namespace Duplicati.Library.Encryption
///
/// The commandline arguments
/// The input stream
- /// The output stream
private System.IO.Stream Execute(string args, System.IO.Stream input, bool encrypt)
{
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
diff --git a/Duplicati/Library/Interface/CommandLineArgument.cs b/Duplicati/Library/Interface/CommandLineArgument.cs
index 7931019ba..fad82bf6b 100644
--- a/Duplicati/Library/Interface/CommandLineArgument.cs
+++ b/Duplicati/Library/Interface/CommandLineArgument.cs
@@ -221,7 +221,6 @@ namespace Duplicati.Library.Interface
/// The arguments long description
/// The default value of the argumen
/// A list of aliases for the command
- /// A list of valid values for the command
public CommandLineArgument(string name, ArgumentType type, string shortDescription, string longDescription, string defaultValue, string[] aliases)
: this(name, type, shortDescription, longDescription, defaultValue)
{
diff --git a/Duplicati/Library/Interface/ICompression.cs b/Duplicati/Library/Interface/ICompression.cs
index a0ee13ee6..6b6cec997 100644
--- a/Duplicati/Library/Interface/ICompression.cs
+++ b/Duplicati/Library/Interface/ICompression.cs
@@ -19,10 +19,26 @@
#endregion
using System;
using System.Collections.Generic;
+using System.IO;
using System.Text;
namespace Duplicati.Library.Interface
{
+ ///
+ /// Enumerates the possible modes for compression and decompression
+ ///
+ public enum ArchiveMode
+ {
+ ///
+ /// Indicates compression i.e. write archive mode, which means that the stream must be writeable
+ ///
+ Write,
+ ///
+ /// Indicates decompression i.e. read archive mode, which means that the stream must be readable
+ ///
+ Read
+ }
+
///
/// A value that is given to the compressor as a hint
/// to how compressible the file is
@@ -43,9 +59,8 @@ namespace Duplicati.Library.Interface
/// Indicates that the files is incompressible
///
Noncompressible
-
}
-
+
///
/// An interface for passing additional hints to the compressor
/// about the expected contents of the volume
@@ -60,18 +75,7 @@ namespace Duplicati.Library.Interface
bool LowOverheadMode { get; set; }
}
- ///
- /// An interface for accessing files in an archive, such as a folder or compressed file.
- /// All modules that implements compression must implement this interface.
- /// The classes that implements this interface MUST also
- /// implement a default constructor and a construtor that
- /// has the signature new(string file, Dictionary<string, string> options).
- /// The default constructor is used to construct an instance
- /// so the DisplayName and other values can be read.
- /// The other constructor is used to do the actual work.
- /// The input file may not exist or have zero length, in which case it should be created.
- ///
- public interface ICompression : IDisposable
+ public interface IArchiveReader
{
///
/// Returns all files in the archive, matching the prefix, if any.
@@ -86,35 +90,14 @@ namespace Duplicati.Library.Interface
/// An optional prefix for limiting the files returned
/// All files in the archive, matching the prefix, if any
IEnumerable> ListFilesWithSize(string prefix);
-
+
///
- /// Returns a stream with data from the given file
+ /// Returns a stream with data from the given file in archive.
///
/// The file to read the data from
/// A stream with data from the given file
System.IO.Stream OpenRead(string file);
- ///
- /// Creates a file in the archive
- ///
- /// The file to create
- /// A hint to the compressor as to how compressible the file data is
- /// The time the file was last written
- /// A stream with the data to write into the file
- System.IO.Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite);
-
- ///
- /// Returns a value indicating if the specified file exists
- ///
- /// The name of the file to examine
- /// True if the file exists, false otherwise
- bool FileExists(string file);
-
- ///
- /// The total size of the archive
- ///
- long Size { get; }
-
///
/// Returns the last modification time for the file
///
@@ -122,10 +105,48 @@ namespace Duplicati.Library.Interface
/// The timestamp on the file
DateTime GetLastWriteTime(string file);
+ ///
+ /// Returns a value indicating if the specified file exists
+ ///
+ /// The name of the file to examine
+ /// True if the file exists, false otherwise
+ bool FileExists(string file);
+ }
+
+ public interface IArchiveWriter
+ {
+ ///
+ /// Creates a file in the archive.
+ ///
+ /// The file to create in the archive
+ /// A hint to the compressor as to how compressible the file data is
+ /// The time the file was last written
+ /// A stream with the data to write into the file
+ System.IO.Stream CreateFile(string file, CompressionHint hint, DateTime lastWrite);
+
///
/// The size in bytes of the buffer that will be written when flushed
///
long FlushBufferSize { get; }
+ }
+
+ ///
+ /// An interface for accessing files in an archive, such as a folder or compressed file.
+ /// All modules that implements compression must implement this interface.
+ /// The classes that implements this interface MUST also
+ /// implement a default constructor and a construtor that
+ /// has the signature new(string file, Dictionary<string, string> options).
+ /// The default constructor is used to construct an instance
+ /// so the DisplayName and other values can be read.
+ /// The other constructor is used to do the actual work.
+ /// The input file may not exist or have zero length, in which case it should be created.
+ ///
+ public interface ICompression : IDisposable, IArchiveReader, IArchiveWriter
+ {
+ ///
+ /// The total size of the archive.
+ ///
+ long Size { get; }
///
/// The extension that the compression implementation adds to the filename
diff --git a/Duplicati/Library/Interface/IGenericCallbackModule.cs b/Duplicati/Library/Interface/IGenericCallbackModule.cs
index dcdf6690d..aba4010f8 100644
--- a/Duplicati/Library/Interface/IGenericCallbackModule.cs
+++ b/Duplicati/Library/Interface/IGenericCallbackModule.cs
@@ -32,7 +32,6 @@ namespace Duplicati.Library.Interface
///
/// Called when the operation finishes
///
- /// The full name of the operation
/// The result object, if this derives from an exception, the operation failed
void OnFinish(object result);
}
diff --git a/Duplicati/Library/Logging/StreamLog.cs b/Duplicati/Library/Logging/StreamLog.cs
index 314fa0a68..eb3137406 100644
--- a/Duplicati/Library/Logging/StreamLog.cs
+++ b/Duplicati/Library/Logging/StreamLog.cs
@@ -62,7 +62,7 @@ namespace Duplicati.Library.Logging
m_stream.WriteLine("{0:u} - {1}: {2}", DateTime.UtcNow.ToString("u"), type, message);
if (exception != null)
{
- m_stream.WriteLine(exception.ToString());
+ m_stream.WriteLine(exception);
m_stream.WriteLine();
}
}
diff --git a/Duplicati/Library/Main/BackendManager.cs b/Duplicati/Library/Main/BackendManager.cs
index 82edd14e2..15969fbcd 100644
--- a/Duplicati/Library/Main/BackendManager.cs
+++ b/Duplicati/Library/Main/BackendManager.cs
@@ -13,7 +13,7 @@ namespace Duplicati.Library.Main
internal class BackendManager : IDisposable
{
public const string VOLUME_HASH = "SHA256";
-
+
///
/// Class to represent hash failures
///
@@ -38,7 +38,7 @@ namespace Duplicati.Library.Main
/// The exception that caused this exception
public HashMismatchException(string message, Exception innerException) : base(message, innerException) { }
}
-
+
private enum OperationType
{
Get,
@@ -162,20 +162,20 @@ namespace Duplicati.Library.Main
this.WaitForComplete();
if (Exception != null)
throw Exception;
-
+
return (TempFile)this.Result;
}
TempFile IDownloadWaitHandle.Wait(out string hash, out long size)
{
this.WaitForComplete();
-
+
if (Exception != null)
throw Exception;
-
+
hash = this.Hash;
size = this.Size;
-
+
return (TempFile)this.Result;
}
@@ -192,7 +192,7 @@ namespace Duplicati.Library.Main
this.Encrypted = true;
}
}
-
+
public bool UpdateHashAndSize(Options options)
{
if (Hash == null || Size < 0)
@@ -204,7 +204,7 @@ namespace Duplicati.Library.Main
return false;
}
-
+
public void DeleteLocalFile(IBackendWriter stat)
{
if (this.LocalTempfile != null)
@@ -212,9 +212,9 @@ namespace Duplicati.Library.Main
catch (Exception ex) { stat.AddWarning(string.Format("Failed to dispose temporary file: {0}", this.LocalTempfile), ex); }
finally { this.LocalTempfile = null; }
}
-
+
public BackendActionType BackendActionType
- {
+ {
get
{
switch (this.Operation)
@@ -234,56 +234,7 @@ namespace Duplicati.Library.Main
}
}
}
-
- }
- /// A small utility stream that allows to keep streams open and counts the bytes sent through.
- private class ShaderStream : System.IO.Stream
- {
- private readonly System.IO.Stream m_baseStream;
- private readonly bool m_keepBaseOpen;
- private long m_read = 0;
- private long m_written = 0;
-
- public ShaderStream(System.IO.Stream baseStream, bool keepBaseOpen)
- {
- if (baseStream == null) throw new ArgumentNullException("baseStream");
- this.m_baseStream = baseStream;
- this.m_keepBaseOpen = keepBaseOpen;
- }
-
- public long TotalBytesRead { get { return m_read; } }
- public long TotalBytesWritten { get { return m_written; } }
-
- public override bool CanRead { get { return m_baseStream.CanRead; } }
- public override bool CanSeek { get { return m_baseStream.CanSeek; } }
- public override bool CanWrite { get { return m_baseStream.CanWrite; } }
- public override long Length { get { return m_baseStream.Length; } }
- public override long Position
- {
- get { return m_baseStream.Position; }
- set { m_baseStream.Position = value; }
- }
- public override void Flush() { m_baseStream.Flush(); }
- public override long Seek(long offset, System.IO.SeekOrigin origin) { return m_baseStream.Seek(offset, origin); }
- public override void SetLength(long value) { m_baseStream.SetLength(value); }
- public override int Read(byte[] buffer, int offset, int count)
- {
- int r = m_baseStream.Read(buffer, offset, count);
- m_read += r;
- return r;
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- m_baseStream.Write(buffer, offset, count);
- m_written += count;
- }
- protected override void Dispose(bool disposing)
- {
- if (disposing && !m_keepBaseOpen)
- m_baseStream.Close();
- base.Dispose(disposing);
- }
}
private class DatabaseCollector
@@ -293,16 +244,16 @@ namespace Duplicati.Library.Main
private System.Threading.Thread m_callerThread;
private List m_dbqueue;
private IBackendWriter m_stats;
-
+
private interface IDbEntry { }
-
+
private class DbOperation : IDbEntry
{
public string Action;
public string File;
public string Result;
}
-
+
private class DbUpdate : IDbEntry
{
public string Remotename;
@@ -310,53 +261,53 @@ namespace Duplicati.Library.Main
public long Size;
public string Hash;
}
-
+
private class DbRename : IDbEntry
{
public string Oldname;
public string Newname;
}
-
+
public DatabaseCollector(LocalDatabase database, IBackendWriter stats)
{
m_database = database;
m_stats = stats;
- m_dbqueue = new List();
+ m_dbqueue = new List();
if (m_database != null)
m_callerThread = System.Threading.Thread.CurrentThread;
}
-
+
public void LogDbOperation(string action, string file, string result)
{
- lock(m_dbqueuelock)
+ lock (m_dbqueuelock)
m_dbqueue.Add(new DbOperation() { Action = action, File = file, Result = result });
}
-
+
public void LogDbUpdate(string remotename, RemoteVolumeState state, long size, string hash)
{
- lock(m_dbqueuelock)
+ lock (m_dbqueuelock)
m_dbqueue.Add(new DbUpdate() { Remotename = remotename, State = state, Size = size, Hash = hash });
}
public void LogDbRename(string oldname, string newname)
{
- lock(m_dbqueuelock)
+ lock (m_dbqueuelock)
m_dbqueue.Add(new DbRename() { Oldname = oldname, Newname = newname });
}
-
+
public bool FlushDbMessages(bool checkThread = false)
{
if (m_database != null && (checkThread == false || m_callerThread == System.Threading.Thread.CurrentThread))
return FlushDbMessages(m_database, null);
-
+
return false;
}
-
+
public bool FlushDbMessages(LocalDatabase db, System.Data.IDbTransaction transaction)
{
List entries;
- lock(m_dbqueuelock)
+ lock (m_dbqueuelock)
if (m_dbqueue.Count == 0)
return false;
else
@@ -369,7 +320,7 @@ namespace Duplicati.Library.Main
HashSet volsRemoved = new HashSet();
//As we replace the list, we can now freely access the elements without locking
- foreach(var e in entries)
+ foreach (var e in entries)
if (e is DbOperation)
db.LogRemoteOperation(((DbOperation)e).Action, ((DbOperation)e).File, ((DbOperation)e).Result, transaction);
else if (e is DbUpdate && ((DbUpdate)e).State == RemoteVolumeState.Deleted)
@@ -382,7 +333,7 @@ namespace Duplicati.Library.Main
else if (e is DbRename)
db.RenameRemoteFile(((DbRename)e).Oldname, ((DbRename)e).Newname, transaction);
else if (e != null)
- m_stats.AddError(string.Format("Queue had element of type: {0}, {1}", e.GetType(), e.ToString()), null);
+ m_stats.AddError(string.Format("Queue had element of type: {0}, {1}", e.GetType(), e), null);
// Finally remove volumes from DB.
if (volsRemoved.Count > 0)
@@ -407,9 +358,9 @@ namespace Duplicati.Library.Main
// Cache these
private readonly int m_numberofretries;
private readonly TimeSpan m_retrydelay;
-
+
public string BackendUrl { get { return m_backendurl; } }
-
+
public bool HasDied { get { return m_lastException != null; } }
public Exception LastException { get { return m_lastException; } }
@@ -444,7 +395,7 @@ namespace Duplicati.Library.Main
}
if (m_taskControl != null)
- m_taskControl.StateChangedEvent += (state) => {
+ m_taskControl.StateChangedEvent += (state) => {
if (state == TaskControlState.Abort)
m_thread.Abort();
};
@@ -509,7 +460,7 @@ namespace Duplicati.Library.Main
{
if (m_taskControl != null)
m_taskControl.TaskControlRendevouz();
-
+
if (m_options.NoConnectionReuse && m_backend != null)
{
m_backend.Dispose();
@@ -521,7 +472,7 @@ namespace Duplicati.Library.Main
if (m_backend == null)
throw new Exception("Backend failed to re-load");
- using(new Logging.Timer(string.Format("RemoteOperation{0}", item.Operation)))
+ using (new Logging.Timer(string.Format("RemoteOperation{0}", item.Operation)))
switch (item.Operation)
{
case OperationType.Put:
@@ -558,7 +509,7 @@ namespace Duplicati.Library.Main
retries++;
lastException = ex;
m_statwriter.AddRetryAttempt(string.Format("Operation {0} with file {1} attempt {2} of {3} failed with message: {4}", item.Operation, item.RemoteFilename, retries, m_numberofretries, ex.Message), ex);
-
+
// If the thread is aborted, we exit here
if (ex is System.Threading.ThreadAbortException)
{
@@ -567,33 +518,33 @@ namespace Duplicati.Library.Main
item.SignalComplete();
throw;
}
-
+
m_statwriter.SendEvent(item.BackendActionType, retries < m_numberofretries ? BackendEventType.Retrying : BackendEventType.Failed, item.RemoteFilename, item.Size);
bool recovered = false;
if (!uploadSuccess && ex is Duplicati.Library.Interface.FolderMissingException && m_options.AutocreateFolders)
{
- try
- {
+ try
+ {
// If we successfully create the folder, we can re-use the connection
- m_backend.CreateFolder();
+ m_backend.CreateFolder();
recovered = true;
}
- catch(Exception dex)
- {
- m_statwriter.AddWarning(string.Format("Failed to create folder: {0}", ex.Message), dex);
+ catch (Exception dex)
+ {
+ m_statwriter.AddWarning(string.Format("Failed to create folder: {0}", ex.Message), dex);
}
}
-
+
// To work around the Apache WEBDAV issue, we rename the file here
if (item.Operation == OperationType.Put && retries < m_numberofretries && !item.NotTrackedInDb)
RenameFileAfterError(item);
-
+
if (!recovered)
{
try { m_backend.Dispose(); }
- catch(Exception dex) { m_statwriter.AddWarning(LC.L("Failed to dispose backend instance: {0}", ex.Message), dex); }
-
+ catch (Exception dex) { m_statwriter.AddWarning(LC.L("Failed to dispose backend instance: {0}", ex.Message), dex); }
+
m_backend = null;
if (retries < m_numberofretries && m_retrydelay.Ticks != 0)
@@ -603,13 +554,13 @@ namespace Duplicati.Library.Main
{
if (m_taskControl != null && m_taskControl.IsAbortRequested())
break;
-
+
System.Threading.Thread.Sleep(500);
}
}
}
}
-
+
} while (retries < m_numberofretries);
@@ -624,7 +575,7 @@ namespace Duplicati.Library.Main
m_statwriter.AddMessage(LC.L("Recovered from problem with attempting to delete non-existing file {0}", item.RemoteFilename));
}
}
- catch(Exception ex)
+ catch (Exception ex)
{
m_statwriter.AddWarning(LC.L("Failed to recover from error deleting file {0}", item.RemoteFilename), ex);
}
@@ -635,17 +586,17 @@ namespace Duplicati.Library.Main
item.Exception = lastException;
if (item.Operation == OperationType.Put)
item.DeleteLocalFile(m_statwriter);
-
+
if (item.ExceptionKillsHandler)
{
m_lastException = lastException;
//TODO: If there are temp files in the queue, we must delete them
m_queue.SetCompleted();
- }
-
+ }
+
}
-
+
item.SignalComplete();
}
}
@@ -663,13 +614,13 @@ namespace Duplicati.Library.Main
var time = p.Time.Ticks == 0 ? p.Time : p.Time.AddSeconds(1);
var newname = VolumeBase.GenerateFilename(p.FileType, p.Prefix, guid, time, p.CompressionModule, p.EncryptionModule);
var oldname = item.RemoteFilename;
-
+
m_statwriter.SendEvent(item.BackendActionType, BackendEventType.Rename, oldname, item.Size);
m_statwriter.SendEvent(item.BackendActionType, BackendEventType.Rename, newname, item.Size);
m_statwriter.AddMessage(string.Format("Renaming \"{0}\" to \"{1}\"", oldname, newname));
m_db.LogDbRename(oldname, newname);
item.RemoteFilename = newname;
-
+
// If there is an index file attached to the block file,
// it references the block filename, so we create a new index file
// which is a copy of the current, but with the new name
@@ -681,13 +632,13 @@ namespace Duplicati.Library.Main
item.Indexfile.Item1.Close();
item.IndexfileUpdated = true;
}
-
+
IndexVolumeWriter wr = null;
try
{
var hashsize = HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm).HashSize / 8;
wr = new IndexVolumeWriter(m_options);
- using(var rd = new IndexVolumeReader(p.CompressionModule, item.Indexfile.Item2.LocalFilename, m_options, hashsize))
+ using (var rd = new IndexVolumeReader(p.CompressionModule, item.Indexfile.Item2.LocalFilename, m_options, hashsize))
wr.CopyFrom(rd, x => x == oldname ? newname : x);
item.Indexfile.Item1.Dispose();
item.Indexfile = new Tuple(wr, item.Indexfile.Item2);
@@ -701,7 +652,7 @@ namespace Duplicati.Library.Main
try { wr.Dispose(); }
catch { }
finally { wr = null; }
-
+
throw;
}
}
@@ -739,9 +690,9 @@ namespace Duplicati.Library.Main
private void DoPut(FileEntryItem item)
{
if (m_encryption != null)
- lock(m_encryptionLock)
+ lock (m_encryptionLock)
item.Encrypt(m_encryption, m_statwriter);
-
+
if (item.UpdateHashAndSize(m_options) && !item.NotTrackedInDb)
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Uploading, item.Size, item.Hash);
@@ -750,7 +701,7 @@ namespace Duplicati.Library.Main
item.Indexfile.Item1.FinishVolume(item.Hash, item.Size);
item.Indexfile.Item1.Close();
item.IndexfileUpdated = true;
- }
+ }
m_db.LogDbOperation("put", item.RemoteFilename, JsonConvert.SerializeObject(new { Size = item.Size, Hash = item.Hash }));
m_statwriter.SendEvent(BackendActionType.Put, BackendEventType.Started, item.RemoteFilename, item.Size);
@@ -772,7 +723,7 @@ namespace Duplicati.Library.Main
if (!item.NotTrackedInDb)
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Uploaded, item.Size, item.Hash);
-
+
m_statwriter.SendEvent(BackendActionType.Put, BackendEventType.Completed, item.RemoteFilename, item.Size);
if (m_options.ListVerifyUploads)
@@ -1079,7 +1030,7 @@ namespace Duplicati.Library.Main
item.Result = tmpfile;
tmpfile = null;
}
-
+
}
catch
{
@@ -1147,7 +1098,7 @@ namespace Duplicati.Library.Main
m_statwriter.AddMessage(LC.L("Listing indicates file {0} is deleted correctly", item.RemoteFilename));
return;
}
-
+
}
result = ex.ToString();
@@ -1157,11 +1108,11 @@ namespace Duplicati.Library.Main
{
m_db.LogDbOperation("delete", item.RemoteFilename, result);
}
-
+
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Deleted, -1, null);
m_statwriter.SendEvent(BackendActionType.Delete, BackendEventType.Completed, item.RemoteFilename, item.Size);
}
-
+
private void DoCreateFolder(FileEntryItem item)
{
m_statwriter.SendEvent(BackendActionType.CreateFolder, BackendEventType.Started, null, -1);
@@ -1170,7 +1121,7 @@ namespace Duplicati.Library.Main
try
{
m_backend.CreateFolder();
- }
+ }
catch (Exception ex)
{
result = ex.ToString();
@@ -1180,15 +1131,15 @@ namespace Duplicati.Library.Main
{
m_db.LogDbOperation("createfolder", item.RemoteFilename, result);
}
-
+
m_statwriter.SendEvent(BackendActionType.CreateFolder, BackendEventType.Completed, null, -1);
}
-
+
public void PutUnencrypted(string remotename, string localpath)
{
if (m_lastException != null)
throw m_lastException;
-
+
var req = new FileEntryItem(OperationType.Put, remotename, null);
req.SetLocalfilename(localpath);
req.Encrypted = true; //Prevent encryption
@@ -1208,7 +1159,7 @@ namespace Duplicati.Library.Main
{
m_statwriter.BackendProgressUpdater.SetBlocking(false);
}
-
+
if (m_lastException != null)
throw m_lastException;
}
@@ -1217,23 +1168,23 @@ namespace Duplicati.Library.Main
{
if (m_lastException != null)
throw m_lastException;
-
+
item.Close();
var req = new FileEntryItem(OperationType.Put, item.RemoteFilename, null);
req.LocalTempfile = item.TempFile;
-
+
if (m_lastException != null)
throw m_lastException;
FileEntryItem req2 = null;
-
+
// As the network link is the bottleneck,
// we encrypt the dblock volume before the
// upload is enqueued (i.e. on the worker thread)
if (m_encryption != null)
lock (m_encryptionLock)
req.Encrypt(m_encryption, m_statwriter);
-
+
req.UpdateHashAndSize(m_options);
m_db.LogDbUpdate(item.RemoteFilename, RemoteVolumeState.Uploading, req.Size, req.Hash);
@@ -1270,7 +1221,7 @@ namespace Duplicati.Library.Main
{
m_statwriter.BackendProgressUpdater.SetBlocking(false);
}
-
+
if (m_lastException != null)
throw m_lastException;
}
@@ -1344,12 +1295,12 @@ namespace Duplicati.Library.Main
else
throw new InvalidOperationException("GetAsync called after backend is shut down");
}
-
+
public void GetForTesting(string remotename, long size, string hash)
{
if (m_lastException != null)
throw m_lastException;
-
+
if (string.IsNullOrWhiteSpace(hash))
throw new InvalidOperationException("Cannot test a file without the hash");
@@ -1372,7 +1323,7 @@ namespace Duplicati.Library.Main
if (m_lastException != null)
throw m_lastException;
- }
+ }
public IList List()
{
@@ -1477,7 +1428,7 @@ namespace Duplicati.Library.Main
{
if (m_lastException != null)
throw m_lastException;
-
+
m_db.LogDbUpdate(remotename, RemoteVolumeState.Deleting, size, null);
var req = new FileEntryItem(OperationType.Delete, remotename, size, null);
try
@@ -1498,12 +1449,12 @@ namespace Duplicati.Library.Main
if (m_lastException != null)
throw m_lastException;
}
-
+
public bool FlushDbMessages(LocalDatabase database, System.Data.IDbTransaction transaction)
{
return m_db.FlushDbMessages(database, transaction);
}
-
+
public bool FlushDbMessages()
{
return m_db.FlushDbMessages(false);
@@ -1513,7 +1464,7 @@ namespace Duplicati.Library.Main
{
if (m_queue != null && !m_queue.Completed)
m_queue.SetCompleted();
-
+
if (m_thread != null)
{
if (!m_thread.Join(TimeSpan.FromSeconds(10)))
@@ -1524,7 +1475,7 @@ namespace Duplicati.Library.Main
m_thread = null;
}
-
+
//TODO: We cannot null this, because it will be recreated
//Should we wait for queue completion or abort immediately?
if (m_backend != null)
@@ -1532,7 +1483,7 @@ namespace Duplicati.Library.Main
m_backend.Dispose();
m_backend = null;
}
-
+
try { m_db.FlushDbMessages(true); }
catch (Exception ex) { m_statwriter.AddError(string.Format("Backend Shutdown error: {0}", ex.Message), ex); }
}
diff --git a/Duplicati/Library/Main/Controller.cs b/Duplicati/Library/Main/Controller.cs
index 66d64a9a0..e89cc3f3c 100644
--- a/Duplicati/Library/Main/Controller.cs
+++ b/Duplicati/Library/Main/Controller.cs
@@ -205,7 +205,7 @@ namespace Duplicati.Library.Main
{
List expandedSources = new List();
- if (Library.Utility.Utility.IsClientWindows && (inputsources[i].StartsWith("*:") || inputsources[i].StartsWith("?:")))
+ if (Library.Utility.Utility.IsClientWindows && (inputsources[i].StartsWith("*:", StringComparison.Ordinal) || inputsources[i].StartsWith("?:", StringComparison.Ordinal)))
{
// *: drive paths are only supported on Windows clients
// Lazily load the drive info
diff --git a/Duplicati/Library/Main/Database/LocalBackupDatabase.cs b/Duplicati/Library/Main/Database/LocalBackupDatabase.cs
index d4d7bea62..45450e2a5 100644
--- a/Duplicati/Library/Main/Database/LocalBackupDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalBackupDatabase.cs
@@ -275,7 +275,6 @@ namespace Duplicati.Library.Main.Database
/// Adds a block to the local database, returning a value indicating if the value presents a new block
///
/// The block key
- /// The name of the archive that holds the data
/// True if the block should be added to the current output
public bool AddBlock (string key, long size, long volumeid, System.Data.IDbTransaction transaction = null)
{
@@ -319,8 +318,6 @@ namespace Duplicati.Library.Main.Database
///
/// The hash of the blockset
/// The size of the blockset
- /// The fragmentoffset for the last block
- /// The hash of the fragment
/// The list of hashes
/// The id of the blockset, new or old
/// True if the blockset was created, false otherwise
@@ -396,7 +393,7 @@ namespace Duplicati.Library.Main.Database
///
/// Adds a metadata set to the database, and returns a value indicating if the record was new
///
- /// The metadata hash
+ /// The metadata hash
/// The id of the metadata set
/// True if the set was added to the database, false otherwise
public bool AddMetadataset(string filehash, long size, int blocksize, IEnumerable blockhashes, IEnumerable blocklisthashes, out long metadataid, System.Data.IDbTransaction transaction = null)
@@ -436,7 +433,6 @@ namespace Duplicati.Library.Main.Database
/// The ID of the hashkey for the file
/// The ID for the metadata
/// The transaction to use for insertion, or null for no transaction
- /// The operationId to use, or -1 to use the current operation
public void AddFile(string filename, DateTime lastmodified, long blocksetID, long metadataID, System.Data.IDbTransaction transaction)
{
var fileidobj = -1L;
diff --git a/Duplicati/Library/Main/Database/LocalDatabase.cs b/Duplicati/Library/Main/Database/LocalDatabase.cs
index 35da7c843..6ffe4bd35 100644
--- a/Duplicati/Library/Main/Database/LocalDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalDatabase.cs
@@ -63,8 +63,6 @@ namespace Duplicati.Library.Main.Database
///
/// Creates a new database instance and starts a new operation
///
- /// The path to the database
- /// The name of the operation
public LocalDatabase(LocalDatabase db)
: this(db.m_connection)
{
@@ -77,7 +75,6 @@ namespace Duplicati.Library.Main.Database
///
/// Creates a new database instance and starts a new operation
///
- /// The path to the database
/// The name of the operation
public LocalDatabase(System.Data.IDbConnection connection, string operation)
: this(connection)
@@ -1198,7 +1195,7 @@ ORDER BY
{
cmd.Transaction = tr.Parent;
cmd.ExecuteNonQuery(string.Format(@"CREATE TEMPORARY TABLE ""{0}"" (""Path"" TEXT NOT NULL)", Tablename));
- cmd.ExecuteNonQuery(string.Format(@"INSERT INTO ""{0}"" SELECT DISTINCT ""Path"" FROM ""File"" WHERE " + sb.ToString(), Tablename), args.ToArray());
+ cmd.ExecuteNonQuery(string.Format(@"INSERT INTO ""{0}"" SELECT DISTINCT ""Path"" FROM ""File"" WHERE " + sb, Tablename), args.ToArray());
tr.Commit();
}
}
diff --git a/Duplicati/Library/Main/Database/LocalDeleteDatabase.cs b/Duplicati/Library/Main/Database/LocalDeleteDatabase.cs
index c2d179e80..28f4f5b4c 100644
--- a/Duplicati/Library/Main/Database/LocalDeleteDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalDeleteDatabase.cs
@@ -304,7 +304,6 @@ namespace Duplicati.Library.Main.Database
///
/// Builds a lookup table to enable faster response to block queries
///
- /// The name of the volume to prepare for
public IBlockQuery CreateBlockQueryHelper(Options options, System.Data.IDbTransaction transaction)
{
return new BlockQuery(m_connection, options, transaction);
diff --git a/Duplicati/Library/Main/Database/LocalListDatabase.cs b/Duplicati/Library/Main/Database/LocalListDatabase.cs
index 9c9072ce5..0bcaf42b6 100644
--- a/Duplicati/Library/Main/Database/LocalListDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalListDatabase.cs
@@ -201,7 +201,7 @@ namespace Duplicati.Library.Main.Database
while (rd.Read())
{
var s = rd.GetString(0);
- if (!s.StartsWith(prefix))
+ if (!s.StartsWith(prefix, StringComparison.Ordinal))
continue;
var dirsep = Duplicati.Library.Utility.Utility.GuessDirSeparator(s);
@@ -266,8 +266,8 @@ namespace Duplicati.Library.Main.Database
//Then we select the matching results
var filesets = string.Format(@"SELECT ""FilesetID"", ""Timestamp"" FROM ""{0}"" ORDER BY ""Timestamp"" DESC", m_tablename);
var cartesianPathFileset = string.Format(@"SELECT ""A"".""Path"", ""B"".""FilesetID"" FROM ""{0}"" A, (" + filesets + @") B ORDER BY ""A"".""Path"" ASC, ""B"".""Timestamp"" DESC", tbname, m_tablename);
-
- var filesWithSizes = @"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" ";
+
+ var filesWithSizes = string.Format(@"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" AND FilesetEntry.""FilesetID"" IN (SELECT DISTINCT ""FilesetID"" FROM ""{0}"") ", m_tablename);
var query = @"SELECT ""C"".""Path"", ""D"".""Length"", ""C"".""FilesetID"" FROM (" + cartesianPathFileset + @") C LEFT OUTER JOIN (" + filesWithSizes + @") D ON ""C"".""FilesetID"" = ""D"".""FilesetID"" AND ""C"".""Path"" = ""D"".""Path""";
cmd.AddParameter(pathprefix, "1");
@@ -318,7 +318,7 @@ namespace Duplicati.Library.Main.Database
//Then we select the matching results
var filesets = string.Format(@"SELECT ""FilesetID"", ""Timestamp"" FROM ""{0}"" ORDER BY ""Timestamp"" DESC", m_tablename);
var cartesianPathFileset = string.Format(@"SELECT ""A"".""Path"", ""B"".""FilesetID"" FROM ""{0}"" A, (" + filesets + @") B ORDER BY ""A"".""Path"" ASC, ""B"".""Timestamp"" DESC", tmpnames.Tablename, m_tablename);
- var filesWithSizes = @"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" ";
+ var filesWithSizes = string.Format(@"SELECT ""Length"", ""FilesetEntry"".""FilesetID"", ""File"".""Path"" FROM ""Blockset"", ""FilesetEntry"", ""File"" WHERE ""File"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" AND FilesetEntry.""FilesetID"" IN (SELECT DISTINCT ""FilesetID"" FROM ""{0}"") ", m_tablename);
var query = @"SELECT ""C"".""Path"", ""D"".""Length"", ""C"".""FilesetID"" FROM (" + cartesianPathFileset + @") C LEFT OUTER JOIN (" + filesWithSizes + @") D ON ""C"".""FilesetID"" = ""D"".""FilesetID"" AND ""C"".""Path"" = ""D"".""Path""";
using(var rd = cmd.ExecuteReader(query))
if(rd.Read())
@@ -368,7 +368,7 @@ namespace Duplicati.Library.Main.Database
for(var i = 0; i < m_filesets.Length; i++)
dict[m_filesets[i].Key] = i;
- var summation = @"SELECT ""A"".""FilesetID"" AS ""FilesetID"", COUNT(*) AS ""FileCount"", SUM(""C"".""Length"") AS ""FileSizes"" FROM ""FilesetEntry"" A, ""File"" B, ""Blockset"" C WHERE ""A"".""FileID"" = ""B"".""ID"" AND ""B"".""BlocksetID"" = ""C"".""ID"" GROUP BY ""A"".""FilesetID"" ";
+ var summation = string.Format(@"SELECT ""A"".""FilesetID"" AS ""FilesetID"", COUNT(*) AS ""FileCount"", SUM(""C"".""Length"") AS ""FileSizes"" FROM ""FilesetEntry"" A, ""File"" B, ""Blockset"" C WHERE ""A"".""FileID"" = ""B"".""ID"" AND ""B"".""BlocksetID"" = ""C"".""ID"" AND ""A"".""FilesetID"" IN (SELECT DISTINCT ""FilesetID"" FROM ""{0}"") GROUP BY ""A"".""FilesetID"" ",m_tablename);
using(var cmd = m_connection.CreateCommand())
using (var rd = cmd.ExecuteReader(string.Format(@"SELECT DISTINCT ""A"".""FilesetID"", ""B"".""FileCount"", ""B"".""FileSizes"" FROM ""{0}"" A LEFT OUTER JOIN ( " + summation + @" ) B ON ""A"".""FilesetID"" = ""B"".""FilesetID"" ORDER BY ""A"".""Timestamp"" DESC ", m_tablename)))
diff --git a/Duplicati/Library/Main/Database/LocalRestoreDatabase.cs b/Duplicati/Library/Main/Database/LocalRestoreDatabase.cs
index 41f034efa..61a217450 100644
--- a/Duplicati/Library/Main/Database/LocalRestoreDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalRestoreDatabase.cs
@@ -165,7 +165,7 @@ namespace Duplicati.Library.Main.Database
{
m_fileprogtable = null;
m_totalprogtable = null;
- Console.WriteLine(ex.ToString());
+ Console.WriteLine(ex);
throw;
}
}
diff --git a/Duplicati/Library/Main/DatabaseLocator.cs b/Duplicati/Library/Main/DatabaseLocator.cs
index c2d54616f..1d38c22a4 100644
--- a/Duplicati/Library/Main/DatabaseLocator.cs
+++ b/Duplicati/Library/Main/DatabaseLocator.cs
@@ -217,7 +217,6 @@ namespace Duplicati.Library.Main
return false;
var file = System.IO.Path.Combine(folder, "dbconfig.json");
- List configs;
if (!System.IO.File.Exists(file))
return false;
diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs
index 226a9f5c5..1bf05ab9b 100644
--- a/Duplicati/Library/Main/Operation/BackupHandler.cs
+++ b/Duplicati/Library/Main/Operation/BackupHandler.cs
@@ -1339,9 +1339,6 @@ namespace Duplicati.Library.Main.Operation
///
/// The name of the file to record
/// The value of the lastModified timestamp
- /// The list of hashes that make up the file
- /// The size of the file
- /// The offset into a fragment block where the last few bytes are stored
/// A lookup table with various metadata values describing the file
private void AddFolderToOutput(BackendManager backend, string filename, DateTime lastModified, IMetahash metadata)
{
@@ -1354,9 +1351,6 @@ namespace Duplicati.Library.Main.Operation
///
/// The name of the file to record
/// The value of the lastModified timestamp
- /// The list of hashes that make up the file
- /// The size of the file
- /// The offset into a fragment block where the last few bytes are stored
/// A lookup table with various metadata values describing the file
private void AddSymlinkToOutput(BackendManager backend, string filename, DateTime lastModified, IMetahash metadata)
{
@@ -1368,10 +1362,9 @@ namespace Duplicati.Library.Main.Operation
/// Adds a file to the output,
///
/// The name of the file to record
- /// The value of the lastModified timestamp
+ /// The value of the lastModified timestamp
/// The list of hashes that make up the file
/// The size of the file
- /// The offset into a fragment block where the last few bytes are stored
/// A lookup table with various metadata values describing the file
private void AddFileToOutput(BackendManager backend, string filename, long size, DateTime lastmodified, IMetahash metadata, IEnumerable hashlist, string filehash, IEnumerable blocklisthashes)
{
diff --git a/Duplicati/Library/Main/Operation/CreateBugReportHandler.cs b/Duplicati/Library/Main/Operation/CreateBugReportHandler.cs
index 066d0d4d4..5586f99df 100644
--- a/Duplicati/Library/Main/Operation/CreateBugReportHandler.cs
+++ b/Duplicati/Library/Main/Operation/CreateBugReportHandler.cs
@@ -16,6 +16,7 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
+using System.IO;
using Duplicati.Library.Interface;
using Duplicati.Library.Main.Database;
@@ -33,7 +34,7 @@ namespace Duplicati.Library.Main.Operation
m_options = options;
m_result = result;
}
-
+
public void Run()
{
var ext = System.IO.Path.GetExtension(m_targetpath);
@@ -41,22 +42,22 @@ namespace Duplicati.Library.Main.Operation
if (ext == "" || string.Compare(ext, 1, module, 0, module.Length, StringComparison.OrdinalIgnoreCase) != 0)
m_targetpath = m_targetpath + "." + module;
-
+
if (System.IO.File.Exists(m_targetpath))
throw new UserInformationException(string.Format("Output file already exists, not overwriting: {0}", m_targetpath));
-
+
if (!System.IO.File.Exists(m_options.Dbpath))
throw new UserInformationException(string.Format("Database file does not exist: {0}", m_options.Dbpath));
-
+
m_result.OperationProgressUpdater.UpdatePhase(OperationPhase.BugReport_Running);
m_result.OperationProgressUpdater.UpdateProgress(0);
m_result.AddMessage("Scrubbing filenames from database, this may take a while, please wait");
- using(var tmp = new Library.Utility.TempFile())
+ using (var tmp = new Library.Utility.TempFile())
{
System.IO.File.Copy(m_options.Dbpath, tmp, true);
- using(var db = new LocalBugReportDatabase(tmp))
+ using (var db = new LocalBugReportDatabase(tmp))
{
m_result.SetDatabase(db);
db.Fix();
@@ -65,20 +66,21 @@ namespace Duplicati.Library.Main.Operation
db.Vacuum();
}
}
-
- using(var cm = DynamicLoader.CompressionLoader.GetModule(module, m_targetpath, m_options.RawOptions))
+
+ using (var stream = new System.IO.FileStream(m_targetpath, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (ICompression cm = DynamicLoader.CompressionLoader.GetModule(module, stream, Interface.ArchiveMode.Write, m_options.RawOptions))
{
- using(var cs = cm.CreateFile("log-database.sqlite", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow))
- using(var fs = System.IO.File.Open(tmp, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
+ using (var cs = cm.CreateFile("log-database.sqlite", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow))
+ using (var fs = System.IO.File.Open(tmp, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
Library.Utility.Utility.CopyStream(fs, cs);
-
- using(var cs = new System.IO.StreamWriter(cm.CreateFile("system-info.txt", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow)))
- foreach(var line in SystemInfoHandler.GetSystemInfo())
+
+ using (var cs = new System.IO.StreamWriter(cm.CreateFile("system-info.txt", Duplicati.Library.Interface.CompressionHint.Compressible, DateTime.UtcNow)))
+ foreach (var line in SystemInfoHandler.GetSystemInfo())
cs.WriteLine(line);
}
m_result.TargetPath = m_targetpath;
- }
+ }
}
}
}
diff --git a/Duplicati/Library/Main/Operation/ListAffected.cs b/Duplicati/Library/Main/Operation/ListAffected.cs
index b5a491269..aee7c2bec 100644
--- a/Duplicati/Library/Main/Operation/ListAffected.cs
+++ b/Duplicati/Library/Main/Operation/ListAffected.cs
@@ -18,7 +18,8 @@
using System;
using System.Linq;
using System.Collections.Generic;
-
+using Duplicati.Library.Interface;
+
namespace Duplicati.Library.Main.Operation
{
internal class ListAffected
@@ -34,6 +35,9 @@ namespace Duplicati.Library.Main.Operation
public void Run(List args, Action callback = null)
{
+ if (!System.IO.File.Exists(m_options.Dbpath))
+ throw new UserInformationException(string.Format("Database file does not exist: {0}", m_options.Dbpath));
+
using(var db = new Database.LocalListAffectedDatabase(m_options.Dbpath))
{
m_result.SetDatabase(db);
diff --git a/Duplicati/Library/Main/Operation/RestoreHandler.cs b/Duplicati/Library/Main/Operation/RestoreHandler.cs
index 1d02eb6ed..724fcc76f 100644
--- a/Duplicati/Library/Main/Operation/RestoreHandler.cs
+++ b/Duplicati/Library/Main/Operation/RestoreHandler.cs
@@ -523,7 +523,7 @@ namespace Duplicati.Library.Main.Operation
if (dryrun)
return;
- var isDirTarget = path.EndsWith(DIRSEP);
+ var isDirTarget = path.EndsWith(DIRSEP, StringComparison.Ordinal);
var targetpath = isDirTarget ? path.Substring(0, path.Length - 1) : path;
// Make the symlink first, otherwise we cannot apply metadata to it
@@ -996,7 +996,7 @@ namespace Duplicati.Library.Main.Operation
{
//Select a new filename
var ext = m_systemIO.PathGetExtension(targetpath) ?? "";
- if (!string.IsNullOrEmpty(ext) && !ext.StartsWith("."))
+ if (!string.IsNullOrEmpty(ext) && !ext.StartsWith(".", StringComparison.Ordinal))
ext = "." + ext;
// First we try with a simple date append, assuming that there are not many conflicts there
diff --git a/Duplicati/Library/Main/Operation/SystemInfoHandler.cs b/Duplicati/Library/Main/Operation/SystemInfoHandler.cs
index 2eb04da8e..56fd95bbd 100644
--- a/Duplicati/Library/Main/Operation/SystemInfoHandler.cs
+++ b/Duplicati/Library/Main/Operation/SystemInfoHandler.cs
@@ -35,7 +35,7 @@ namespace Duplicati.Library.Main.Operation
yield return string.Format("Autoupdate urls: {0}", string.Join(";", Duplicati.Library.AutoUpdater.AutoUpdateSettings.URLs));
yield return string.Format("Update folder: {0}", Duplicati.Library.AutoUpdater.UpdaterManager.INSTALLDIR);
yield return string.Format("Base install folder: {0}", Duplicati.Library.AutoUpdater.UpdaterManager.InstalledBaseDir);
- yield return string.Format("Version name: \"{0}\" ({1})", Duplicati.Library.AutoUpdater.UpdaterManager.SelfVersion.Displayname, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
+ yield return string.Format("Version name: \"{0}\" ({1})", Duplicati.Library.AutoUpdater.UpdaterManager.SelfVersion.Displayname, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
yield return string.Format("Current Version folder {0}", System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
yield return string.Format("OS: {0}", Environment.OSVersion);
diff --git a/Duplicati/Library/Main/ResultClasses.cs b/Duplicati/Library/Main/ResultClasses.cs
index 290c98035..78c4733ec 100644
--- a/Duplicati/Library/Main/ResultClasses.cs
+++ b/Duplicati/Library/Main/ResultClasses.cs
@@ -34,11 +34,11 @@ namespace Duplicati.Library.Main
void AddDryrunMessage(string message);
void WriteLogMessageDirect(string message, LogMessageType type, Exception ex);
}
-
+
internal interface IBackendWriter : ILogWriter
{
void AddRetryAttempt(string message, Exception ex);
-
+
long UnknownFileSize { set; }
long UnknownFileCount { set; }
long KnownFileCount { set; }
@@ -57,32 +57,32 @@ namespace Duplicati.Library.Main
/// Path to the resource
/// Size of the file or progress
void SendEvent(BackendActionType action, BackendEventType type, string path, long size);
-
+
///
/// Gets the backend progress updater.
///
/// The backend progress updater.
IBackendProgressUpdater BackendProgressUpdater { get; }
}
-
+
internal interface ISetCommonOptions : ILogWriter
{
- bool VerboseOutput { set; }
+ new bool VerboseOutput { set; }
bool VerboseErrors { set; }
-
+
DateTime EndTime { get; set; }
DateTime BeginTime { set; }
-
+
void SetDatabase(LocalDatabase db);
-
+
OperationMode MainOperation { get; }
- IMessageSink MessageSink { set; }
+ IMessageSink MessageSink { set; }
}
-
+
internal class BackendWriter : BasicResults, IBackendWriter, IBackendStatstics, IParsedBackendStatistics
{
public BackendWriter(BasicResults p) : base(p) { }
-
+
protected long m_remoteCalls = 0;
protected long m_bytesUploaded = 0;
protected long m_bytesDownloaded = 0;
@@ -91,22 +91,22 @@ namespace Duplicati.Library.Main
protected long m_filesDeleted = 0;
protected long m_foldersCreated = 0;
protected long m_retryAttemptCount = 0;
-
+
public void AddNumberOfRemoteCalls(long count)
{
System.Threading.Interlocked.Add(ref m_remoteCalls, count);
}
-
+
public void AddBytesUploaded(long count)
{
System.Threading.Interlocked.Add(ref m_bytesUploaded, count);
}
-
+
public void AddBytesDownloaded(long count)
{
System.Threading.Interlocked.Add(ref m_bytesDownloaded, count);
}
-
+
public long RemoteCalls { get { return m_remoteCalls; } }
public long BytesUploaded { get { return m_bytesUploaded; } }
public long BytesDownloaded { get { return m_bytesDownloaded; } }
@@ -125,9 +125,9 @@ namespace Duplicati.Library.Main
public long TotalQuotaSpace { get; set; }
public long FreeQuotaSpace { get; set; }
public long AssignedQuotaSpace { get; set; }
-
+
public override OperationMode MainOperation { get { return m_parent.MainOperation; } }
-
+
public void SendEvent(BackendActionType action, BackendEventType type, string path, long size)
{
if (type == BackendEventType.Started)
@@ -160,13 +160,13 @@ namespace Duplicati.Library.Main
break;
}
}
-
+
base.AddBackendEvent(action, type, path, size);
}
-
- IBackendProgressUpdater IBackendWriter.BackendProgressUpdater { get { return base.BackendProgressUpdater; } }
+
+ IBackendProgressUpdater IBackendWriter.BackendProgressUpdater { get { return base.BackendProgressUpdater; } }
}
-
+
public interface ITaskControl
{
void Pause();
@@ -174,7 +174,7 @@ namespace Duplicati.Library.Main
void Stop();
void Abort();
}
-
+
internal enum TaskControlState
{
Run,
@@ -182,7 +182,7 @@ namespace Duplicati.Library.Main
Stop,
Abort
}
-
+
internal abstract class BasicResults : IBasicResults, ILogWriter, ISetCommonOptions, ITaskControl, Logging.ILog
{
protected class DbMessage
@@ -190,7 +190,7 @@ namespace Duplicati.Library.Main
public readonly string Type;
public readonly string Message;
public readonly Exception Exception;
-
+
public DbMessage(string type, string message, Exception ex)
{
this.Type = type;
@@ -198,28 +198,28 @@ namespace Duplicati.Library.Main
this.Exception = ex;
}
}
-
+
protected LocalDatabase m_db;
protected readonly BasicResults m_parent;
protected System.Threading.Thread m_callerThread;
protected readonly object m_lock = new object();
protected Queue m_dbqueue;
-
+
private TaskControlState m_controlState = TaskControlState.Run;
private System.Threading.ManualResetEvent m_pauseEvent = new System.Threading.ManualResetEvent(true);
-
+
private bool m_verboseOutput;
private bool m_verboseErrors;
-
+
public bool VerboseOutput
- {
+ {
get
- {
+ {
if (m_parent != null)
return m_parent.VerboseOutput;
else
return m_verboseOutput;
- }
+ }
set
{
if (m_parent != null)
@@ -228,15 +228,16 @@ namespace Duplicati.Library.Main
m_verboseOutput = value;
}
}
+
public bool VerboseErrors
- {
+ {
get
- {
+ {
if (m_parent != null)
return m_parent.VerboseErrors;
else
return m_verboseErrors;
- }
+ }
set
{
if (m_parent != null)
@@ -257,24 +258,24 @@ namespace Duplicati.Library.Main
else
return ParsedResultType.Success;
}
- }
+ }
public DateTime EndTime { get; set; }
public DateTime BeginTime { get; set; }
public TimeSpan Duration { get { return EndTime.Ticks == 0 ? new TimeSpan(0) : EndTime - BeginTime; } }
-
+
public abstract OperationMode MainOperation { get; }
-
+
protected Library.Utility.FileBackedStringList m_messages;
protected Library.Utility.FileBackedStringList m_warnings;
protected Library.Utility.FileBackedStringList m_errors;
protected Library.Utility.FileBackedStringList m_retryAttempts;
-
+
protected IMessageSink m_messageSink;
public IMessageSink MessageSink
{
get { return m_messageSink; }
- set
+ set
{
m_messageSink = value;
if (value != null)
@@ -284,7 +285,7 @@ namespace Duplicati.Library.Main
}
}
}
-
+
protected internal IOperationProgressUpdaterAndReporter m_operationProgressUpdater;
internal IOperationProgressUpdaterAndReporter OperationProgressUpdater
{
@@ -296,7 +297,7 @@ namespace Duplicati.Library.Main
return m_operationProgressUpdater;
}
}
-
+
protected internal IBackendProgressUpdaterAndReporter m_backendProgressUpdater;
internal IBackendProgressUpdaterAndReporter BackendProgressUpdater
{
@@ -308,7 +309,7 @@ namespace Duplicati.Library.Main
return m_backendProgressUpdater;
}
}
-
+
public void SetDatabase(LocalDatabase db)
{
if (m_parent != null)
@@ -317,7 +318,7 @@ namespace Duplicati.Library.Main
}
else
{
- lock(m_lock)
+ lock (m_lock)
{
m_db = db;
if (m_db != null)
@@ -325,14 +326,14 @@ namespace Duplicati.Library.Main
}
}
}
-
+
public void FlushLog()
{
if (m_parent != null)
m_parent.FlushLog();
else
{
- lock(m_lock)
+ lock (m_lock)
{
while (m_dbqueue.Count > 0)
{
@@ -342,7 +343,7 @@ namespace Duplicati.Library.Main
}
}
}
-
+
private void LogDbMessage(string type, string message, Exception ex)
{
if (System.Threading.Thread.CurrentThread != m_callerThread)
@@ -366,7 +367,7 @@ namespace Duplicati.Library.Main
}
else
{
- lock(Logging.Log.Lock)
+ lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -388,7 +389,7 @@ namespace Duplicati.Library.Main
}
}
}
-
+
}
public void AddDryrunMessage(string message)
@@ -397,7 +398,7 @@ namespace Duplicati.Library.Main
m_parent.AddDryrunMessage(message);
else
{
- lock(Logging.Log.Lock)
+ lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -451,12 +452,12 @@ namespace Duplicati.Library.Main
}
public void AddMessage(string message)
- {
+ {
if (m_parent != null)
m_parent.AddMessage(message);
else
{
- lock(Logging.Log.Lock)
+ lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -471,7 +472,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.MessageEvent(message);
- lock(m_lock)
+ lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Message", message, null);
}
@@ -489,7 +490,7 @@ namespace Duplicati.Library.Main
m_parent.AddWarning(message, ex);
else
{
- lock(Logging.Log.Lock)
+ lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -505,7 +506,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.WarningEvent(message, ex);
- lock(m_lock)
+ lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Warning", message, ex);
}
@@ -523,7 +524,7 @@ namespace Duplicati.Library.Main
m_parent.AddRetryAttempt(message, ex);
else
{
- lock(Logging.Log.Lock)
+ lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -539,7 +540,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.RetryEvent(message, ex);
- lock(m_lock)
+ lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Retry", message, ex);
}
@@ -557,7 +558,7 @@ namespace Duplicati.Library.Main
m_parent.AddError(message, ex);
else
{
- lock(Logging.Log.Lock)
+ lock (Logging.Log.Lock)
{
if (m_is_reporting)
return;
@@ -573,7 +574,7 @@ namespace Duplicati.Library.Main
if (MessageSink != null)
MessageSink.ErrorEvent(message, ex);
- lock(m_lock)
+ lock (m_lock)
if (m_db != null && !m_db.IsDisposed)
LogDbMessage("Error", message, ex);
}
@@ -584,14 +585,14 @@ namespace Duplicati.Library.Main
}
}
}
-
+
public IEnumerable Messages { get { return m_messages; } }
public IEnumerable Warnings { get { return m_warnings; } }
public IEnumerable Errors { get { return m_errors; } }
-
- public BasicResults()
- {
- this.BeginTime = DateTime.UtcNow;
+
+ public BasicResults()
+ {
+ this.BeginTime = DateTime.UtcNow;
this.m_parent = null;
m_messages = new Library.Utility.FileBackedStringList();
m_warnings = new Library.Utility.FileBackedStringList();
@@ -605,27 +606,27 @@ namespace Duplicati.Library.Main
}
public BasicResults(BasicResults p)
- {
- this.BeginTime = DateTime.UtcNow;
+ {
+ this.BeginTime = DateTime.UtcNow;
this.m_parent = p;
}
-
+
protected IBackendStatstics m_backendStatistics;
public IBackendStatstics BackendStatistics
- {
+ {
get
{
if (this.m_parent != null)
return this.m_parent.BackendStatistics;
-
+
return m_backendStatistics;
}
}
-
+
public IBackendWriter BackendWriter { get { return (IBackendWriter)this.BackendStatistics; } }
-
+
public event Action StateChangedEvent;
-
+
///
/// Request that this task pauses.
///
@@ -646,7 +647,7 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
-
+
///
/// Request that this task resumes.
///
@@ -667,11 +668,11 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
-
+
///
/// Request that this task stops.
///
- public void Stop()
+ public void Stop()
{
if (m_parent != null)
m_parent.Stop();
@@ -688,7 +689,7 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
-
+
///
/// Request that this task aborts.
///
@@ -708,7 +709,7 @@ namespace Duplicati.Library.Main
StateChangedEvent(m_controlState);
}
}
-
+
///
/// Helper method that the current running task can call to obtain the current state
///
@@ -752,7 +753,7 @@ namespace Duplicati.Library.Main
public override string ToString()
{
return Library.Utility.Utility.PrintSerializeObject(
- this,
+ this,
filter: (prop, item) =>
!typeof(IBackendProgressUpdater).IsAssignableFrom(prop.PropertyType) &&
!typeof(IMessageSink).IsAssignableFrom(prop.PropertyType) &&
@@ -855,11 +856,11 @@ namespace Duplicati.Library.Main
else if ((Warnings != null && Warnings.Any()) || PartialBackup)
return ParsedResultType.Warning;
else
- return ParsedResultType.Success;
+ return ParsedResultType.Success;
}
}
}
-
+
internal class RestoreResults : BasicResults, Library.Interface.IRestoreResults
{
public long FilesRestored { get; internal set; }
@@ -870,9 +871,9 @@ namespace Duplicati.Library.Main
public long FilesDeleted { get; internal set; }
public long FoldersDeleted { get; internal set; }
public long SymlinksDeleted { get; internal set; }
-
+
public override OperationMode MainOperation { get { return OperationMode.Restore; } }
-
+
public IRecreateDatabaseResults RecreateDatabaseResults { get; internal set; }
public override ParsedResultType ParsedResult
@@ -914,7 +915,7 @@ namespace Duplicati.Library.Main
this.FileSizes = fileSizes;
}
}
-
+
internal class ListResults : BasicResults, Duplicati.Library.Interface.IListResults
{
private IEnumerable m_filesets;
@@ -926,10 +927,10 @@ namespace Duplicati.Library.Main
m_filesets = filesets;
m_files = files;
}
-
+
public IEnumerable Filesets { get { return m_filesets; } }
public IEnumerable Files { get { return m_files; } }
-
+
public override OperationMode MainOperation { get { return OperationMode.List; } }
}
@@ -947,7 +948,7 @@ namespace Duplicati.Library.Main
m_logs = logs;
m_volumes = volumes;
}
-
+
public IEnumerable Filesets { get { return m_filesets; } }
public IEnumerable Files { get { return m_files; } }
public IEnumerable LogMessages { get { return m_logs; } }
@@ -960,23 +961,23 @@ namespace Duplicati.Library.Main
{
public IEnumerable> DeletedSets { get; private set; }
public bool Dryrun { get; private set; }
-
+
public void SetResults(IEnumerable> deletedSets, bool dryrun)
{
EndTime = DateTime.UtcNow;
DeletedSets = deletedSets;
Dryrun = dryrun;
}
-
+
public override OperationMode MainOperation { get { return OperationMode.Delete; } }
public DeleteResults() : base() { }
public DeleteResults(BasicResults p) : base(p) { }
-
+
private ICompactResults m_compactResults;
public ICompactResults CompactResults
- {
+ {
get
{
if (m_parent != null && m_parent is BackupResults)
@@ -988,33 +989,33 @@ namespace Duplicati.Library.Main
{
if (m_parent != null && m_parent is BackupResults)
((BackupResults)m_parent).CompactResults = value;
-
+
m_compactResults = value;
}
}
- }
-
+ }
+
internal class RecreateDatabaseResults : BasicResults, Library.Interface.IRecreateDatabaseResults
{
public override OperationMode MainOperation { get { return OperationMode.Repair; } }
-
+
public RecreateDatabaseResults() : base() { }
public RecreateDatabaseResults(BasicResults p) : base(p) { }
- }
+ }
internal class CreateLogDatabaseResults : BasicResults, Library.Interface.ICreateLogDatabaseResults
{
public override OperationMode MainOperation { get { return OperationMode.CreateLogDb; } }
public string TargetPath { get; internal set; }
- }
-
+ }
+
internal class RestoreControlFilesResults : BasicResults, Library.Interface.IRestoreControlFilesResults
{
public IEnumerable Files { get; private set; }
-
+
public override OperationMode MainOperation { get { return OperationMode.RestoreControlfiles; } }
public void SetResult(IEnumerable files) { this.Files = files; }
- }
+ }
internal class ListRemoteResults : BasicResults, Library.Interface.IListRemoteResults
{
@@ -1027,12 +1028,12 @@ namespace Duplicati.Library.Main
internal class RepairResults : BasicResults, Library.Interface.IRepairResults
{
public override OperationMode MainOperation { get { return OperationMode.Repair; } }
-
+
public RepairResults() : base() { }
public RepairResults(BasicResults p) : base(p) { }
public Library.Interface.IRecreateDatabaseResults RecreateDatabaseResults { get; internal set; }
- }
-
+ }
+
internal class CompactResults : BasicResults, Library.Interface.ICompactResults
{
public long DeletedFileCount { get; internal set; }
@@ -1044,22 +1045,22 @@ namespace Duplicati.Library.Main
public bool Dryrun { get; internal set; }
public override OperationMode MainOperation { get { return OperationMode.Compact; } }
-
+
public CompactResults() : base() { }
public CompactResults(BasicResults p) : base(p) { }
- }
+ }
internal class ListChangesResults : BasicResults, Library.Interface.IListChangesResults
{
public override OperationMode MainOperation { get { return OperationMode.ListChanges; } }
-
+
public DateTime BaseVersionTimestamp { get; internal set; }
public DateTime CompareVersionTimestamp { get; internal set; }
public long BaseVersionIndex { get; internal set; }
public long CompareVersionIndex { get; internal set; }
-
- public IEnumerable> ChangeDetails { get; internal set; }
-
+
+ public IEnumerable> ChangeDetails { get; internal set; }
+
public long AddedFolders { get; internal set; }
public long AddedSymlinks { get; internal set; }
public long AddedFiles { get; internal set; }
@@ -1071,13 +1072,13 @@ namespace Duplicati.Library.Main
public long ModifiedFolders { get; internal set; }
public long ModifiedSymlinks { get; internal set; }
public long ModifiedFiles { get; internal set; }
-
+
public long PreviousSize { get; internal set; }
public long CurrentSize { get; internal set; }
public long AddedSize { get; internal set; }
public long DeletedSize { get; internal set; }
-
+
public void SetResult(
DateTime baseVersionTime, long baseVersionIndex, DateTime compareVersionTime, long compareVersionIndex,
long addedFolders, long addedSymlinks, long addedFiles,
@@ -1091,38 +1092,38 @@ namespace Duplicati.Library.Main
this.BaseVersionIndex = baseVersionIndex;
this.CompareVersionTimestamp = compareVersionTime;
this.CompareVersionIndex = compareVersionIndex;
-
- this.AddedFolders = addedFolders;
+
+ this.AddedFolders = addedFolders;
this.AddedSymlinks = addedSymlinks;
this.AddedFiles = addedFiles;
-
+
this.DeletedFolders = deletedFolders;
this.DeletedSymlinks = deletedSymlinks;
this.DeletedFiles = deletedFiles;
-
+
this.ModifiedFolders = modifiedFolders;
this.ModifiedSymlinks = modifiedSymlinks;
this.ModifiedFiles = modifiedFiles;
-
+
this.AddedSize = addedSize;
this.DeletedSize = deletedSize;
-
+
this.PreviousSize = previousSize;
this.CurrentSize = currentSize;
-
+
this.ChangeDetails = changeDetails;
}
}
-
+
internal class TestResults : BasicResults, ITestResults
{
public TestResults() : base() { }
public TestResults(BasicResults p) : base(p) { }
-
+
public override OperationMode MainOperation { get { return OperationMode.Test; } }
public IEnumerable>>> Verifications { get { return m_verifications; } }
private List>>> m_verifications = new List>>>();
-
+
public KeyValuePair>> AddResult(string volume, IEnumerable> changes)
{
var res = new KeyValuePair>>(volume, changes);
@@ -1130,7 +1131,7 @@ namespace Duplicati.Library.Main
return res;
}
}
-
+
internal class TestFilterResults : BasicResults, ITestFilterResults
{
public long FileCount { get; set; }
diff --git a/Duplicati/Library/Main/Strings.cs b/Duplicati/Library/Main/Strings.cs
index 39b0f27ad..7f774ea91 100644
--- a/Duplicati/Library/Main/Strings.cs
+++ b/Duplicati/Library/Main/Strings.cs
@@ -87,7 +87,7 @@ namespace Duplicati.Library.Main.Strings
public static string DisablemoduleShort { get { return LC.L(@"Disabled one or more modules"); } }
public static string EnablemoduleLong { get { return LC.L(@"Supply one or more module names, separated by commas to load them"); } }
public static string EnablemoduleShort { get { return LC.L(@"Enables one or more modules"); } }
- public static string SnapshotpolicyLong { get { return LC.L(@"This setting controls the usage of snapshots, which allows Duplicati to backup files that are locked by other programs. If this is set to ""off"", Duplicati will not attempt to create a disk snapshot. Setting this to ""auto"" makes Duplicati attempt to create a snapshot, and fail silently if that was not allowed or supported. A setting of ""on"" will also make Duplicati attempt to create a snapshot, but will produce a warning message in the log if it fails. Setting it to ""required"" will make Duplicati abort the backup if the snapshot creation fails. On windows this uses the Volume Shadow Copy Services (VSS) and requires administrative privileges. On Linux this uses Logical Volume Management (LVM) and requires root privileges."); } }
+ public static string SnapshotpolicyLong { get { return LC.L(@"This setting controls the usage of snapshots, which allows Duplicati to backup files that are locked by other programs. If this is set to ""off"", Duplicati will not attempt to create a disk snapshot. Setting this to ""auto"" makes Duplicati attempt to create a snapshot, and fail silently if that was not allowed or supported (note that the OS may still log system warnings). A setting of ""on"" will also make Duplicati attempt to create a snapshot, but will produce a warning message in the log if it fails. Setting it to ""required"" will make Duplicati abort the backup if the snapshot creation fails. On windows this uses the Volume Shadow Copy Services (VSS) and requires administrative privileges. On Linux this uses Logical Volume Management (LVM) and requires root privileges."); } }
public static string SnapshotpolicyShort { get { return LC.L(@"Controls the use of disk snapshots"); } }
public static string AsynchronousuploadfolderLong { get { return LC.L(@"The pre-generated volumes will be placed into the temporary folder by default, this option can set a different folder for placing the temporary volumes, despite the name, this also works for synchronous runs"); } }
public static string AsynchronousuploadfolderShort { get { return LC.L(@"The path where ready volumes are placed until uploaded"); } }
diff --git a/Duplicati/Library/Main/Volumes/VolumeReaderBase.cs b/Duplicati/Library/Main/Volumes/VolumeReaderBase.cs
index 6db4c63a1..e2f0218fc 100644
--- a/Duplicati/Library/Main/Volumes/VolumeReaderBase.cs
+++ b/Duplicati/Library/Main/Volumes/VolumeReaderBase.cs
@@ -1,9 +1,8 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Duplicati.Library.Interface;
+using Duplicati.Library.Interface;
+using Duplicati.Library.Utility;
using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
using System.IO;
namespace Duplicati.Library.Main.Volumes
@@ -12,18 +11,37 @@ namespace Duplicati.Library.Main.Volumes
{
protected bool m_disposeCompression = false;
protected ICompression m_compression;
+ protected Stream m_stream;
- private static ICompression LoadCompressor(string compressor, string file, Options options)
+ private static ICompression LoadCompressor(string compressor, Stream stream, Options options)
{
- var tmp = DynamicLoader.CompressionLoader.GetModule(compressor, file, options.RawOptions);
+ var tmp = DynamicLoader.CompressionLoader.GetModule(compressor, stream, Interface.ArchiveMode.Read, options.RawOptions);
if (tmp == null)
- throw new Exception(string.Format("Unable to create {0} decompressor on file {1}", compressor, file));
+ {
+ var name = "[stream]";
+ if (stream is FileStream)
+ name = ((FileStream)stream).Name;
+ if (stream is TempFileStream)
+ name = ((TempFileStream)stream).Name;
+
+ throw new Exception(string.Format("Unable to create {0} decompressor on file {1}", compressor, name));
+ }
+
return tmp;
}
- public VolumeReaderBase(string compressor, string file, Options options)
- : this(LoadCompressor(compressor, file, options), options)
+ private static ICompression LoadCompressor(string compressor, string file, Options options, out Stream stream)
{
+ stream = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
+ return LoadCompressor(compressor, stream, options);
+ }
+
+ public VolumeReaderBase(string compressor, string file, Options options)
+ : base(options)
+ {
+ m_compression = LoadCompressor(compressor, file, options, out m_stream);
+ ReadManifests(options);
+
m_disposeCompression = true;
}
@@ -31,13 +49,18 @@ namespace Duplicati.Library.Main.Volumes
: base(options)
{
m_compression = compression;
+ ReadManifests(options);
+ }
+
+ private void ReadManifests(Options options)
+ {
if (!options.DontReadManifests)
{
using (var s = m_compression.OpenRead(MANIFEST_FILENAME))
{
if (s == null)
throw new InvalidManifestException("No manifest file found in volume");
-
+
using (var fs = new StreamReader(s, ENCODING))
ManifestData.VerifyManifest(fs.ReadToEnd(), m_blocksize, options.BlockHashAlgorithm, options.FileHashAlgorithm);
}
@@ -46,7 +69,8 @@ namespace Duplicati.Library.Main.Volumes
public static void UpdateOptionsFromManifest(string compressor, string file, Options options)
{
- using(var c = LoadCompressor(compressor, file, options))
+ using (var stream = new System.IO.FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (var c = LoadCompressor(compressor, stream, options))
using (var s = c.OpenRead(MANIFEST_FILENAME))
using (var fs = new StreamReader(s, ENCODING))
{
@@ -75,17 +99,20 @@ namespace Duplicati.Library.Main.Volumes
{
m_compression.Dispose();
m_compression = null;
+
+ m_stream?.Dispose();
+ m_stream = null;
}
}
public static IEnumerable ReadBlocklist(ICompression compression, string filename, long hashsize)
{
var buffer = new byte[hashsize];
- using(var fs = compression.OpenRead(filename))
+ using (var fs = compression.OpenRead(filename))
{
int s;
while ((s = Library.Utility.Utility.ForceStreamRead(fs, buffer, buffer.Length)) != 0)
- {
+ {
if (s != buffer.Length)
throw new InvalidDataException("Premature End-of-stream encountered while reading blocklist hashes");
diff --git a/Duplicati/Library/Main/Volumes/VolumeWriterBase.cs b/Duplicati/Library/Main/Volumes/VolumeWriterBase.cs
index d3b14404f..be78eda4d 100644
--- a/Duplicati/Library/Main/Volumes/VolumeWriterBase.cs
+++ b/Duplicati/Library/Main/Volumes/VolumeWriterBase.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using Duplicati.Library.Interface;
+using Duplicati.Library.Utility;
+using System;
using System.IO;
-using Duplicati.Library.Interface;
namespace Duplicati.Library.Main.Volumes
{
@@ -11,6 +9,7 @@ namespace Duplicati.Library.Main.Volumes
{
protected ICompression m_compression;
protected Library.Utility.TempFile m_localfile;
+ protected Stream m_localFileStream;
protected string m_volumename;
public string LocalFilename { get { return m_localfile; } }
public string RemoteFilename { get { return m_volumename; } }
@@ -19,7 +18,7 @@ namespace Duplicati.Library.Main.Volumes
public abstract RemoteVolumeType FileType { get; }
public long VolumeID { get; set; }
-
+
public void SetRemoteFilename(string name)
{
m_volumename = name;
@@ -29,15 +28,15 @@ namespace Duplicati.Library.Main.Volumes
: this(options, DateTime.UtcNow)
{
}
-
+
public static string GenerateGuid(Options options)
{
var s = Guid.NewGuid().ToString("N");
-
+
//We can choose shorter GUIDs here
-
+
return s;
-
+
}
public void ResetRemoteFilename(Options options, DateTime timestamp)
@@ -54,14 +53,16 @@ namespace Duplicati.Library.Main.Volumes
m_localfile = new Library.Utility.TempFile();
ResetRemoteFilename(options, timestamp);
- m_compression = DynamicLoader.CompressionLoader.GetModule(options.CompressionModule, m_localfile, options.RawOptions);
-
- if(m_compression == null)
+
+ m_localFileStream = new System.IO.FileStream(m_localfile, FileMode.Create, FileAccess.Write, FileShare.Read);
+ m_compression = DynamicLoader.CompressionLoader.GetModule(options.CompressionModule, m_localFileStream, ArchiveMode.Write, options.RawOptions);
+
+ if (m_compression == null)
throw new UserInformationException(string.Format("Unsupported compression module: {0}", options.CompressionModule));
-
+
if ((this is IndexVolumeWriter || this is FilesetVolumeWriter) && m_compression is Library.Interface.ICompressionHinting)
((Library.Interface.ICompressionHinting)m_compression).LowOverheadMode = true;
-
+
AddManifestfile();
}
@@ -77,6 +78,10 @@ namespace Duplicati.Library.Main.Volumes
try { m_compression.Dispose(); }
finally { m_compression = null; }
+ if (m_localFileStream != null)
+ try { m_localFileStream.Dispose(); }
+ finally { m_localFileStream = null; }
+
if (m_localfile != null)
try { m_localfile.Dispose(); }
finally { m_localfile = null; }
@@ -89,16 +94,13 @@ namespace Duplicati.Library.Main.Volumes
if (m_compression != null)
try { m_compression.Dispose(); }
finally { m_compression = null; }
+
+ if (m_localFileStream != null)
+ try { m_localFileStream.Dispose(); }
+ finally { m_localFileStream = null; }
}
public long Filesize { get { return m_compression.Size + m_compression.FlushBufferSize; } }
- public void SetTempFile(Library.Utility.TempFile tmpfile)
- {
- if (m_localfile != null)
- m_localfile.Dispose();
-
- m_localfile = tmpfile;
- }
}
}
diff --git a/Duplicati/Library/Modules/Builtin/HyperVOptions.cs b/Duplicati/Library/Modules/Builtin/HyperVOptions.cs
index 81938bf9b..d33ebdbbf 100644
--- a/Duplicati/Library/Modules/Builtin/HyperVOptions.cs
+++ b/Duplicati/Library/Modules/Builtin/HyperVOptions.cs
@@ -101,9 +101,9 @@ namespace Duplicati.Library.Modules.Builtin
{
var filters = filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries);
- filtersInclude = filters.Where(x => x.StartsWith("+") && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
+ filtersInclude = filters.Where(x => x.StartsWith("+", StringComparison.Ordinal) && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
- filtersExclude = filters.Where(x => x.StartsWith("-") && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
+ filtersExclude = filters.Where(x => x.StartsWith("-", StringComparison.Ordinal) && Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
var remainingfilters = filters.Where(x => !Regex.IsMatch(x, m_HyperVPathGuidRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).ToArray();
@@ -186,7 +186,7 @@ namespace Duplicati.Library.Modules.Builtin
var pathsForBackup = new List(paths);
var filterhandler = new Utility.FilterExpression(
- filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-")).Select(x => x.Substring(1)).ToList());
+ filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-", StringComparison.Ordinal)).Select(x => x.Substring(1)).ToList());
foreach (var guestForBackup in guestsForBackup)
foreach (var pathForBackup in guestForBackup.DataPaths)
diff --git a/Duplicati/Library/Modules/Builtin/MSSQLOptions.cs b/Duplicati/Library/Modules/Builtin/MSSQLOptions.cs
index 3c8bcbf3c..4c3c42136 100644
--- a/Duplicati/Library/Modules/Builtin/MSSQLOptions.cs
+++ b/Duplicati/Library/Modules/Builtin/MSSQLOptions.cs
@@ -101,9 +101,9 @@ namespace Duplicati.Library.Modules.Builtin
{
var filters = filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries);
- filtersInclude = filters.Where(x => x.StartsWith("+") && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
+ filtersInclude = filters.Where(x => x.StartsWith("+", StringComparison.Ordinal) && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
- filtersExclude = filters.Where(x => x.StartsWith("-") && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
+ filtersExclude = filters.Where(x => x.StartsWith("-", StringComparison.Ordinal) && Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
.Select(x => Regex.Match(x.Substring(1), m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Groups[1].Value).ToList();
var remainingfilters = filters.Where(x => !Regex.IsMatch(x, m_MSSQLPathDBRegExp, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).ToArray();
@@ -183,7 +183,7 @@ namespace Duplicati.Library.Modules.Builtin
var pathsForBackup = new List(paths);
var filterhandler = new Utility.FilterExpression(
- filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-")).Select(x => x.Substring(1)).ToList());
+ filter.Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith("-", StringComparison.Ordinal)).Select(x => x.Substring(1)).ToList());
foreach (var dbForBackup in dbsForBackup)
foreach (var pathForBackup in dbForBackup.DataPaths)
diff --git a/Duplicati/Library/Modules/Builtin/RunScript.cs b/Duplicati/Library/Modules/Builtin/RunScript.cs
index 6f9f69cf6..d624dad76 100644
--- a/Duplicati/Library/Modules/Builtin/RunScript.cs
+++ b/Duplicati/Library/Modules/Builtin/RunScript.cs
@@ -143,7 +143,7 @@ namespace Duplicati.Library.Modules.Builtin
sw.WriteLine("{0}: {1}", key, value);
}
else
- sw.WriteLine(c.ToString());
+ sw.WriteLine(c);
}
}
else if (result.GetType().IsArray)
@@ -164,7 +164,7 @@ namespace Duplicati.Library.Modules.Builtin
sw.WriteLine("{0}: {1}", key, value);
}
else
- sw.WriteLine(c.ToString());
+ sw.WriteLine(c);
}
}
else if (result is Exception)
@@ -172,7 +172,7 @@ namespace Duplicati.Library.Modules.Builtin
//No localization, must be parseable by script
Exception e = (Exception)result;
sw.WriteLine("Failed: {0}", e.Message);
- sw.WriteLine("Details: {0}", e.ToString());
+ sw.WriteLine("Details: {0}", e);
}
else
{
@@ -252,7 +252,7 @@ namespace Duplicati.Library.Modules.Builtin
foreach(string rawline in stdout.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
{
string line = rawline.Trim();
- if (!line.StartsWith("--"))
+ if (!line.StartsWith("--", StringComparison.Ordinal))
continue; //Ingore anything that does not start with --
line = line.Substring(2);
@@ -273,7 +273,7 @@ namespace Duplicati.Library.Modules.Builtin
key = line.Substring(0, lix).Trim();
value = line.Substring(lix + 1).Trim();
- if (value.Length >= 2 && value.StartsWith("\"") && value.EndsWith("\""))
+ if (value.Length >= 2 && value.StartsWith("\"", StringComparison.Ordinal) && value.EndsWith("\"", StringComparison.Ordinal))
value = value.Substring(1, value.Length - 2);
}
diff --git a/Duplicati/Library/Modules/Builtin/SendJabberMessage.cs b/Duplicati/Library/Modules/Builtin/SendJabberMessage.cs
index e57d3bbf3..586b40c00 100644
--- a/Duplicati/Library/Modules/Builtin/SendJabberMessage.cs
+++ b/Duplicati/Library/Modules/Builtin/SendJabberMessage.cs
@@ -310,7 +310,7 @@ namespace Duplicati.Library.Modules.Builtin
};
agsXMPP.XmppElementHandler loginErroHandler = (sender, e) => {
- ex = new Exception(string.Format("Failed to log in: {0}", e.ToString()));
+ ex = new Exception(string.Format("Failed to log in: {0}", e));
waitEvent.Set();
};
diff --git a/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs b/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs
index f3a9a987a..994d7ed95 100644
--- a/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs
+++ b/Duplicati/Library/SQLiteHelper/DatabaseUpgrader.cs
@@ -152,11 +152,11 @@ namespace Duplicati.Library.SQLiteHelper
{
//The resource name will be "Duplicati.GUI.Database_schema.1.Sample upgrade.sql"
//The number indicates the version that will be upgraded to
- if (s.StartsWith(prefix) && !s.Equals(prefix + SCHEMA_NAME))
+ if (s.StartsWith(prefix, StringComparison.Ordinal) && !s.Equals(prefix + SCHEMA_NAME))
{
try
{
- string version = s.Substring(prefix.Length, s.IndexOf(".", prefix.Length + 1) - prefix.Length);
+ string version = s.Substring(prefix.Length, s.IndexOf(".", prefix.Length + 1, StringComparison.Ordinal) - prefix.Length);
int fileversion = int.Parse(version);
string prev;
diff --git a/Duplicati/Library/SQLiteHelper/SQLiteLoader.cs b/Duplicati/Library/SQLiteHelper/SQLiteLoader.cs
index 1424aa506..4830277d7 100644
--- a/Duplicati/Library/SQLiteHelper/SQLiteLoader.cs
+++ b/Duplicati/Library/SQLiteHelper/SQLiteLoader.cs
@@ -110,7 +110,7 @@ namespace Duplicati.Library.SQLiteHelper
if (!Duplicati.Library.Utility.Utility.IsMono)
{
//If we run with MS.Net we can use the mixed mode assemblies
- if (Library.Utility.Utility.Is64BitProcess)
+ if (Environment.Is64BitProcess)
{
if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win64"), filename)))
assemblyPath = System.IO.Path.Combine(basePath, "win64");
diff --git a/Duplicati/Library/Snapshots/DefineDosDevice.cs b/Duplicati/Library/Snapshots/DefineDosDevice.cs
index adc45c668..762d7512a 100644
--- a/Duplicati/Library/Snapshots/DefineDosDevice.cs
+++ b/Duplicati/Library/Snapshots/DefineDosDevice.cs
@@ -97,7 +97,7 @@ namespace Duplicati.Library.Snapshots
List drives = new List("DEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray());
foreach (DriveInfo di in DriveInfo.GetDrives())
{
- if ((di.RootDirectory.FullName.Length == 2 && di.RootDirectory.FullName[1] == ':') || ((di.RootDirectory.FullName.Length == 3 && di.RootDirectory.FullName.EndsWith(":\\"))))
+ if ((di.RootDirectory.FullName.Length == 2 && di.RootDirectory.FullName[1] == ':') || ((di.RootDirectory.FullName.Length == 3 && di.RootDirectory.FullName.EndsWith(":\\", StringComparison.Ordinal))))
{
int i = drives.IndexOf(di.RootDirectory.FullName[0]);
if (i >= 0)
@@ -110,10 +110,10 @@ namespace Duplicati.Library.Snapshots
drive = drives[0].ToString() + ':';
}
- while (drive.EndsWith("\\"))
+ while (drive.EndsWith("\\", StringComparison.Ordinal))
drive = drive.Substring(0, drive.Length - 1);
- if (!drive.EndsWith(":"))
+ if (!drive.EndsWith(":", StringComparison.Ordinal))
throw new ArgumentException("The drive specification must end with a colon.", nameof(drive));
Win32API.DDD_Flags flags = 0;
diff --git a/Duplicati/Library/Snapshots/ISnapshotService.cs b/Duplicati/Library/Snapshots/ISnapshotService.cs
index 4e8d5c20c..db43b7d47 100644
--- a/Duplicati/Library/Snapshots/ISnapshotService.cs
+++ b/Duplicati/Library/Snapshots/ISnapshotService.cs
@@ -97,7 +97,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
///
/// The hardlink ID
- /// The file or folder to examine
+ /// The file or folder to examine
string HardlinkTargetID(string path);
}
}
diff --git a/Duplicati/Library/Snapshots/LinuxSnapshot.cs b/Duplicati/Library/Snapshots/LinuxSnapshot.cs
index ff5ff1375..714ea00ef 100644
--- a/Duplicati/Library/Snapshots/LinuxSnapshot.cs
+++ b/Duplicati/Library/Snapshots/LinuxSnapshot.cs
@@ -101,7 +101,7 @@ namespace Duplicati.Library.Snapshots
/// The local path
public string ConvertToLocalPath(string path)
{
- if (!path.StartsWith(m_mountPoint))
+ if (!path.StartsWith(m_mountPoint, StringComparison.Ordinal))
throw new InvalidOperationException();
return m_tmpDir + path.Substring(m_mountPoint.Length);
@@ -114,7 +114,7 @@ namespace Duplicati.Library.Snapshots
/// The snapshot path
public string ConvertToSnapshotPath(string path)
{
- if (!path.StartsWith(m_tmpDir))
+ if (!path.StartsWith(m_tmpDir, StringComparison.Ordinal))
throw new InvalidOperationException();
return m_mountPoint + path.Substring(m_tmpDir.Length);
@@ -261,7 +261,7 @@ namespace Duplicati.Library.Snapshots
///
/// Constructs a new snapshot module using LVM
///
- /// The list of folders to create snapshots for
+ /// The list of folders to create snapshots for
/// A set of commandline options
public LinuxSnapshot(string[] sources, Dictionary options)
{
@@ -342,7 +342,7 @@ namespace Duplicati.Library.Snapshots
KeyValuePair? best = null;
foreach (KeyValuePair s in m_entries)
- if (name.StartsWith(s.Key) && (best == null || s.Key.Length > best.Value.Key.Length))
+ if (name.StartsWith(s.Key, StringComparison.Ordinal) && (best == null || s.Key.Length > best.Value.Key.Length))
best = s;
if (best == null)
@@ -494,7 +494,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
///
/// The hardlink ID
- /// The file or folder to examine
+ /// The file or folder to examine
public string HardlinkTargetID(string path)
{
var local = ConvertToSnapshotPath(FindSnapShotByLocalPath(path), path);
diff --git a/Duplicati/Library/Snapshots/NoSnapshot.cs b/Duplicati/Library/Snapshots/NoSnapshot.cs
index 7c4f7c5de..994018154 100644
--- a/Duplicati/Library/Snapshots/NoSnapshot.cs
+++ b/Duplicati/Library/Snapshots/NoSnapshot.cs
@@ -40,7 +40,7 @@ namespace Duplicati.Library.Snapshots
///
/// Constructs a new backup snapshot, using all the required disks
///
- /// The folders that are about to be backed up
+ /// The folders that are about to be backed up
public NoSnapshot(string[] sources)
: this(sources, new Dictionary())
{
@@ -49,7 +49,7 @@ namespace Duplicati.Library.Snapshots
///
/// Constructs a new backup snapshot, using all the required disks
///
- /// The folders that are about to be backed up
+ /// The folders that are about to be backed up
/// A set of system options
public NoSnapshot(string[] sources, Dictionary options)
{
diff --git a/Duplicati/Library/Snapshots/NoSnapshotLinux.cs b/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
index a6d18c3b7..b4e8dc79a 100644
--- a/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
+++ b/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
@@ -83,7 +83,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
///
/// The hardlink ID
- /// The file or folder to examine
+ /// The file or folder to examine
public override string HardlinkTargetID(string path)
{
path = NormalizePath(path);
diff --git a/Duplicati/Library/Snapshots/NoSnapshotWindows.cs b/Duplicati/Library/Snapshots/NoSnapshotWindows.cs
index e708042ac..3aa2fa3e1 100644
--- a/Duplicati/Library/Snapshots/NoSnapshotWindows.cs
+++ b/Duplicati/Library/Snapshots/NoSnapshotWindows.cs
@@ -167,7 +167,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
///
/// The hardlink ID
- /// The file or folder to examine
+ /// The file or folder to examine
public override string HardlinkTargetID(string path)
{
return null;
diff --git a/Duplicati/Library/Snapshots/Program.cs b/Duplicati/Library/Snapshots/Program.cs
index d7dab0da1..1919dbd20 100644
--- a/Duplicati/Library/Snapshots/Program.cs
+++ b/Duplicati/Library/Snapshots/Program.cs
@@ -31,21 +31,21 @@ namespace Duplicati.Library.Snapshots
for (int i = 0; i < args.Count; i++)
{
- if (args[i].StartsWith("--"))
+ if (args[i].StartsWith("--", StringComparison.Ordinal))
{
string key = null;
string value = null;
- if (args[i].IndexOf("=") > 0)
+ if (args[i].IndexOf("=", StringComparison.Ordinal) > 0)
{
- key = args[i].Substring(0, args[i].IndexOf("="));
- value = args[i].Substring(args[i].IndexOf("=") + 1);
+ key = args[i].Substring(0, args[i].IndexOf("=", StringComparison.Ordinal));
+ value = args[i].Substring(args[i].IndexOf("=", StringComparison.Ordinal) + 1);
}
else
key = args[i];
//Skip the leading --
key = key.Substring(2).ToLower();
- if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"") && value.EndsWith("\""))
+ if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"", StringComparison.Ordinal) && value.EndsWith("\"", StringComparison.Ordinal))
value = value.Substring(1, value.Length - 2);
//Last argument overwrites the current
@@ -151,7 +151,7 @@ Where is the folder where files will be locked/created etc");
}
catch (Exception ex)
{
- Console.WriteLine(string.Format("The file {0} was locked even through snapshot, message: {1}", filename, ex.ToString()));
+ Console.WriteLine(string.Format("The file {0} was locked even through snapshot, message: {1}", filename, ex));
Console.WriteLine("* Test failed");
return;
}
@@ -163,7 +163,7 @@ Where is the folder where files will be locked/created etc");
}
catch (Exception ex)
{
- Console.WriteLine(string.Format("The snapshot tester failed: {0}", ex.ToString()));
+ Console.WriteLine(string.Format("The snapshot tester failed: {0}", ex));
Console.WriteLine("* Test failed");
}
diff --git a/Duplicati/Library/Snapshots/SystemIOLinux.cs b/Duplicati/Library/Snapshots/SystemIOLinux.cs
index 2e05cabc9..f97462c66 100644
--- a/Duplicati/Library/Snapshots/SystemIOLinux.cs
+++ b/Duplicati/Library/Snapshots/SystemIOLinux.cs
@@ -196,7 +196,7 @@ namespace Duplicati.Library.Snapshots
var f = NoSnapshot.NormalizePath(file);
- foreach(var x in data.Where(x => x.Key.StartsWith("unix-ext:")).Select(x => new KeyValuePair(x.Key.Substring("unix-ext:".Length), Convert.FromBase64String(x.Value))))
+ foreach(var x in data.Where(x => x.Key.StartsWith("unix-ext:", StringComparison.Ordinal)).Select(x => new KeyValuePair(x.Key.Substring("unix-ext:".Length), Convert.FromBase64String(x.Value))))
UnixSupport.File.SetExtendedAttribute(f, x.Key, x.Value);
if (restorePermissions && data.ContainsKey("unix:uid-gid-perm"))
diff --git a/Duplicati/Library/Snapshots/SystemIOWindows.cs b/Duplicati/Library/Snapshots/SystemIOWindows.cs
index 943611f8e..aba8c26de 100644
--- a/Duplicati/Library/Snapshots/SystemIOWindows.cs
+++ b/Duplicati/Library/Snapshots/SystemIOWindows.cs
@@ -33,7 +33,7 @@ namespace Duplicati.Library.Snapshots
public static bool IsPathTooLong(string path)
{
- if (path.StartsWith(UNCPREFIX) || path.StartsWith(UNCPREFIX_SERVER) || path.Length > 260)
+ if (path.StartsWith(UNCPREFIX, StringComparison.Ordinal) || path.StartsWith(UNCPREFIX_SERVER, StringComparison.Ordinal) || path.Length > 260)
return true;
return false;
@@ -41,13 +41,13 @@ namespace Duplicati.Library.Snapshots
public static string PrefixWithUNC(string path)
{
- if (path.StartsWith(UNCPREFIX_SERVER))
+ if (path.StartsWith(UNCPREFIX_SERVER, StringComparison.Ordinal))
return path;
- if (path.StartsWith(UNCPREFIX))
+ if (path.StartsWith(UNCPREFIX, StringComparison.Ordinal))
return path;
- if (path.StartsWith(PATHPREFIX_SERVER))
+ if (path.StartsWith(PATHPREFIX_SERVER, StringComparison.Ordinal))
return UNCPREFIX_SERVER + path.Remove(0, PATHPREFIX_SERVER.Length);
return UNCPREFIX + path;
@@ -55,7 +55,7 @@ namespace Duplicati.Library.Snapshots
public static string StripUNCPrefix(string path)
{
- if (path.StartsWith(UNCPREFIX))
+ if (path.StartsWith(UNCPREFIX, StringComparison.Ordinal))
return path.Substring(UNCPREFIX.Length);
else
return path;
@@ -510,7 +510,7 @@ namespace Duplicati.Library.Snapshots
public Dictionary GetMetadata(string path, bool isSymlink, bool followSymlink)
{
- var isDirTarget = path.EndsWith(DIRSEP);
+ var isDirTarget = path.EndsWith(DIRSEP, StringComparison.Ordinal);
var targetpath = isDirTarget ? path.Substring(0, path.Length - 1) : path;
var dict = new Dictionary();
@@ -532,7 +532,7 @@ namespace Duplicati.Library.Snapshots
public void SetMetadata(string path, Dictionary data, bool restorePermissions)
{
- var isDirTarget = path.EndsWith(DIRSEP);
+ var isDirTarget = path.EndsWith(DIRSEP, StringComparison.Ordinal);
var targetpath = isDirTarget ? path.Substring(0, path.Length - 1) : path;
System.Security.AccessControl.FileSystemSecurity rules;
diff --git a/Duplicati/Library/Snapshots/Win32USN.cs b/Duplicati/Library/Snapshots/Win32USN.cs
index e9ff53a6d..19479e0fb 100644
--- a/Duplicati/Library/Snapshots/Win32USN.cs
+++ b/Duplicati/Library/Snapshots/Win32USN.cs
@@ -526,13 +526,13 @@ namespace Duplicati.Library.Snapshots
/// Sends the dwIoControlCode to the device specified by hDevice.
///
/// Safe handle to the device
- /// Device IO Control Code to send
- /// Input buffer if required
+ /// Device IO Control Code to send
+ /// Input buffer if required
/// Size of input buffer
- /// Output buffer if required
+ /// Output buffer if required
/// Size of output buffer
- /// Number of bytes returned in output buffer
- /// IntPtr to an 'OVERLAPPED' structure
+ /// Number of bytes returned in output buffer
+ /// IntPtr to an 'OVERLAPPED' structure
/// 'true' if successful, otherwise 'false'
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
@@ -550,13 +550,13 @@ namespace Duplicati.Library.Snapshots
/// Sends the dwIoControlCode to the device specified by hDevice.
///
/// Safe handle to the device
- /// Device IO Control Code to send
- /// Input buffer if required
+ /// Device IO Control Code to send
+ /// Input buffer if required
/// Size of input buffer
- /// Output buffer if required
+ /// Output buffer if required
/// Size of output buffer
- /// Number of bytes returned in output buffer
- /// IntPtr to an 'OVERLAPPED' structure
+ /// Number of bytes returned in output buffer
+ /// IntPtr to an 'OVERLAPPED' structure
/// 'true' if successful, otherwise 'false'
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
@@ -574,13 +574,13 @@ namespace Duplicati.Library.Snapshots
/// Sends the dwIoControlCode to the device specified by hDevice.
///
/// Safe handle to the device
- /// Device IO Control Code to send
- /// Input buffer if required
+ /// Device IO Control Code to send
+ /// Input buffer if required
/// Size of input buffer
- /// Output buffer if required
+ /// Output buffer if required
/// Size of output buffer
- /// Number of bytes returned in output buffer
- /// IntPtr to an 'OVERLAPPED' structure
+ /// Number of bytes returned in output buffer
+ /// IntPtr to an 'OVERLAPPED' structure
/// 'true' if successful, otherwise 'false'
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
diff --git a/Duplicati/Library/Snapshots/WindowsSnapshot.cs b/Duplicati/Library/Snapshots/WindowsSnapshot.cs
index f8f7b4ca1..14104c9c5 100644
--- a/Duplicati/Library/Snapshots/WindowsSnapshot.cs
+++ b/Duplicati/Library/Snapshots/WindowsSnapshot.cs
@@ -281,7 +281,7 @@ namespace Duplicati.Library.Snapshots
localPath = localPath.Replace(root, String.Empty);
- if (!volumePath.EndsWith(SLASH) && !localPath.StartsWith(SLASH))
+ if (!volumePath.EndsWith(SLASH, StringComparison.Ordinal) && !localPath.StartsWith(SLASH, StringComparison.Ordinal))
localPath = localPath.Insert(0, SLASH);
localPath = localPath.Insert(0, volumePath);
diff --git a/Duplicati/Library/UsageReporter/OSInfoHelper.cs b/Duplicati/Library/UsageReporter/OSInfoHelper.cs
index 8246300b9..fa89fd42e 100644
--- a/Duplicati/Library/UsageReporter/OSInfoHelper.cs
+++ b/Duplicati/Library/UsageReporter/OSInfoHelper.cs
@@ -78,9 +78,9 @@ namespace Duplicati.Library.UsageReporter
if (m != null)
{
var lines = m.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
- var product = lines.Where(x => x.Trim().StartsWith("ProductName:")).Select(x => x.Trim().Substring("ProductName:".Length).Trim()).FirstOrDefault();
- var version = lines.Where(x => x.Trim().StartsWith("ProductVersion:")).Select(x => x.Trim().Substring("ProductVersion:".Length).Trim()).FirstOrDefault();
- var build = lines.Where(x => x.Trim().StartsWith("BuildVersion:")).Select(x => x.Trim().Substring("BuildVersion:".Length).Trim()).FirstOrDefault();
+ var product = lines.Where(x => x.Trim().StartsWith("ProductName:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("ProductName:".Length).Trim()).FirstOrDefault();
+ var version = lines.Where(x => x.Trim().StartsWith("ProductVersion:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("ProductVersion:".Length).Trim()).FirstOrDefault();
+ var build = lines.Where(x => x.Trim().StartsWith("BuildVersion:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("BuildVersion:".Length).Trim()).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(product))
return string.Format("{0} {1} {2}", product, version, build);
}
@@ -145,7 +145,7 @@ namespace Duplicati.Library.UsageReporter
if (m != null)
{
var lines = m.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
- var line = lines.Where(x => x.Trim().StartsWith("Description:")).Select(x => x.Trim().Substring("Description:".Length).Trim()).FirstOrDefault();
+ var line = lines.Where(x => x.Trim().StartsWith("Description:", StringComparison.Ordinal)).Select(x => x.Trim().Substring("Description:".Length).Trim()).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(line))
return line;
}
@@ -154,7 +154,6 @@ namespace Duplicati.Library.UsageReporter
return RunProgramAndReadOutput("uname", "-srvmpio");
}
- return null;
}
}
}
diff --git a/Duplicati/Library/Utility/CommandLineParser.cs b/Duplicati/Library/Utility/CommandLineParser.cs
index 4ddeddbb4..de539f994 100644
--- a/Duplicati/Library/Utility/CommandLineParser.cs
+++ b/Duplicati/Library/Utility/CommandLineParser.cs
@@ -41,21 +41,21 @@ namespace Duplicati.Library.Utility
for (int i = 0; i < args.Count; i++)
{
- if (args[i].StartsWith("--"))
+ if (args[i].StartsWith("--", StringComparison.Ordinal))
{
string key = null;
string value = null;
- if (args[i].IndexOf("=") > 0)
+ if (args[i].IndexOf("=", StringComparison.Ordinal) > 0)
{
- key = args[i].Substring(0, args[i].IndexOf("="));
- value = args[i].Substring(args[i].IndexOf("=") + 1);
+ key = args[i].Substring(0, args[i].IndexOf("=", StringComparison.Ordinal));
+ value = args[i].Substring(args[i].IndexOf("=", StringComparison.Ordinal) + 1);
}
else
key = args[i];
//Skip the leading --
key = key.Substring(2).ToLower();
- if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"") && value.EndsWith("\""))
+ if (!string.IsNullOrEmpty(value) && value.Length > 1 && value.StartsWith("\"", StringComparison.Ordinal) && value.EndsWith("\"", StringComparison.Ordinal))
value = value.Substring(1, value.Length - 2);
//Last argument overwrites the current
diff --git a/Duplicati/Library/Utility/DefaultFilters.cs b/Duplicati/Library/Utility/DefaultFilters.cs
index b88a7be8b..7c04ac378 100644
--- a/Duplicati/Library/Utility/DefaultFilters.cs
+++ b/Duplicati/Library/Utility/DefaultFilters.cs
@@ -1,417 +1,421 @@
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
-//
-// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Duplicati.Library.Utility
-{
- [Flags]
- public enum DefaultFilterSet
- {
- None = 0,
- Windows = 1,
- OSX = 2,
- Linux = 4,
- All = Windows | OSX | Linux,
- }
-
- ///
- /// This class defines a set of common filters for files that don't typically need to be backed up.
- /// These filters are largely based on the filters described here:
- /// https://superuser.com/questions/443890/what-files-file-types-and-folders-to-exclude-from-user-folder-backup
- ///
- public static class DefaultFilters
- {
- ///
- /// Case insensitive mapping from strings to filter sets
- /// ///
- private static readonly Dictionary defaultFilterSetMapping =
- ((DefaultFilterSet[])Enum.GetValues(typeof(DefaultFilterSet)))
- .ToDictionary(val => val.ToString(), val => val, StringComparer.OrdinalIgnoreCase);
-
- ///
- /// Filters common for all operating systems
- ///
- public static IEnumerable Common
- {
- get
- {
- return DefaultFilters.CreateCommonFilters();
- }
- }
-
- ///
- /// Filters for Windows
- ///
- public static IEnumerable Windows
- {
- get
- {
- return DefaultFilters.CreateWindowsFilters();
- }
- }
-
- ///
- /// Filters for OSX
- ///
- public static IEnumerable OSX
- {
- get
- {
- return DefaultFilters.CreateOSXFilters();
- }
- }
-
- ///
- /// Filters for Linux
- ///
- public static IEnumerable Linux
- {
- get
- {
- return DefaultFilters.CreateLinuxFilters();
- }
- }
-
- ///
- /// Gets the filters indicated by the given filter sets
- ///
- /// Filter set options
- /// Default filters
- public static IEnumerable GetFilters(string options)
- {
- bool anyOptions = false;
- DefaultFilterSet filterSets = DefaultFilterSet.None;
- foreach (string option in options.Split(new[] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries))
- {
- anyOptions = true;
- DefaultFilterSet filter;
- if (DefaultFilters.defaultFilterSetMapping.TryGetValue(option, out filter))
- {
- filterSets |= filter;
- }
- else
- {
- throw new ArgumentException(Strings.Filters.UnknownDefaultFilterSet(option));
- }
- }
-
- // If no filter sets are specified, we use the default for the platform we're on
- if (!anyOptions)
- {
- if (Utility.IsClientWindows)
- {
- filterSets |= DefaultFilterSet.Windows;
- }
-
- if (Utility.IsClientOSX)
- {
- filterSets |= DefaultFilterSet.OSX;
- }
-
- if (Utility.IsClientLinux)
- {
- filterSets |= DefaultFilterSet.Linux;
- }
- }
-
- if (filterSets == DefaultFilterSet.None)
- {
- return Enumerable.Empty();
- }
-
- IEnumerable filters = DefaultFilters.Common;
-
- if ((filterSets & DefaultFilterSet.Windows) == DefaultFilterSet.Windows)
- {
- filters = filters.Concat(DefaultFilters.Windows);
- }
-
- if ((filterSets & DefaultFilterSet.OSX) == DefaultFilterSet.OSX)
- {
- filters = filters.Concat(DefaultFilters.OSX);
- }
-
- if ((filterSets & DefaultFilterSet.Linux) == DefaultFilterSet.Linux)
- {
- filters = filters.Concat(DefaultFilters.Linux);
- }
-
- // Filter down to distinct filters, then convert them to filter expressions
- return filters.Distinct(Utility.ClientFilenameStringComparer).Select(filter => new FilterExpression(filter, false));
- }
-
- ///
- /// Creates common filters
- ///
- /// Common filters
- private static string[] CreateCommonFilters()
- {
- return new[]
- {
- DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/*cache*"),
- DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/Safe Browsing*"),
- DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Mozilla/Firefox/*cache*"),
- DefaultFilters.CreateRegexFilter(@".*/(cookies|permissions).sqllite(-.{3})?"),
- };
- }
-
- ///
- /// Creates Windows filters
- ///
- /// Windows filters
- private static string[] CreateWindowsFilters()
- {
- var filters = new[]
- {
- DefaultFilters.CreateWildcardFilter(@"*.rbf"),
- DefaultFilters.CreateWildcardFilter(@"*.tmp"),
- DefaultFilters.CreateWildcardFilter(@"*.tmp/*"),
- DefaultFilters.CreateWildcardFilter(@"*/$RECYCLE.BIN/*"),
- DefaultFilters.CreateWildcardFilter(@"*/AppData/Apple Computer/Mobile Sync/*"),
- DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Microsoft/Windows Store/*"),
- DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Packages/*"), // https://superuser.com/questions/490925/explain-windows-8-windows-store-appdata-packages-and-what-to-backup
- DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Temp*"),
- DefaultFilters.CreateWildcardFilter(@"*/AppData/LocalLow/*"),
- DefaultFilters.CreateWildcardFilter(@"*/AppData/Temp*"),
- DefaultFilters.CreateWildcardFilter(@"*/Application Data/Apple Computer/Mobile Sync/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Application Data/Application Data*"),
- DefaultFilters.CreateWildcardFilter(@"*/Cookies/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies"),
- DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies-journal"),
- DefaultFilters.CreateWildcardFilter(@"*/I386*"),
- DefaultFilters.CreateWildcardFilter(@"*/Internet Explorer/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Local Settings/History/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Local Settings/Temp*"),
- DefaultFilters.CreateWildcardFilter(@"*/LocalService/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/RecoveryStore*"),
- DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.edb"),
- DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.log"),
- DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/Cookies*"),
- DefaultFilters.CreateWildcardFilter(@"*/MSOCache*"),
- DefaultFilters.CreateWildcardFilter(@"*/NetHood/*"),
- DefaultFilters.CreateWildcardFilter(@"*/NetworkService/*"),
- DefaultFilters.CreateWildcardFilter(@"*/NTUSER*"),
- DefaultFilters.CreateWildcardFilter(@"*/ntuser.dat*"),
- DefaultFilters.CreateWildcardFilter(@"*/PrintHood/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Recent/*"),
- DefaultFilters.CreateWildcardFilter(@"*/RECYCLER/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Safari/Library/Caches/*"),
- DefaultFilters.CreateWildcardFilter(@"*/SendTo/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Start Menu/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Temporary Internet Files/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Thumbs.db"),
- DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat"),
- DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat.LOG"),
- DefaultFilters.CreateWildcardFilter(@"?:/autoexec.bat"),
- DefaultFilters.CreateWildcardFilter(@"?:/Config.Msi*"),
- DefaultFilters.CreateWildcardFilter(@"?:/hiberfil.sys"),
- DefaultFilters.CreateWildcardFilter(@"?:/pagefile.sys"),
- DefaultFilters.CreateWildcardFilter(@"?:/Program Files (x86)/*"),
- DefaultFilters.CreateWildcardFilter(@"?:/Program Files/*"),
- DefaultFilters.CreateWildcardFilter(@"?:/ProgramData/*"),
- DefaultFilters.CreateWildcardFilter(@"?:/swapfile.sys"),
- DefaultFilters.CreateWildcardFilter(@"?:/System Volume Information/*"),
- DefaultFilters.CreateWildcardFilter(@"?:/Windows.old/*"),
- DefaultFilters.CreateWildcardFilter(@"?:/Windows/*"),
- DefaultFilters.CreateWildcardFilter(@"?:/Windows/Installer*"),
- DefaultFilters.CreateWildcardFilter(@"?:/Windows/Temp*"),
- };
-
- var extra = GetWindowsRegistryFilters();
- if (extra == null || extra.Length == 0)
- return filters;
-
- return filters.Union(extra).ToArray();
- }
-
- ///
- /// Creates OSX filters
- ///
- /// OSX filters
- private static string[] CreateOSXFilters()
- {
- return new[]
- {
- DefaultFilters.CreateWildcardFilter(@"*.fseventsd*"),
- DefaultFilters.CreateWildcardFilter(@"*.hotfiles.btree*"),
- DefaultFilters.CreateWildcardFilter(@"*.Spotlight-*/*"),
- DefaultFilters.CreateWildcardFilter(@"*.Trash*"),
- DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies"),
- DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies-journal"),
- DefaultFilters.CreateWildcardFilter(@"*/backups.backupdb/*"),
- DefaultFilters.CreateWildcardFilter(@"*/iP* Software Updates/*"),
- DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache*"),
- DefaultFilters.CreateWildcardFilter(@"*/iTunes/Album Artwork/Cache/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Application Support/SyncServices/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Caches/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/*/Info.plist"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/Calendar Cache"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/com.apple.appstore.plist"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/Cookies.binarycookies"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Logs/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/*/Info.plist"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/AvailableFeeds/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/Envelope Index"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Mirrors/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Database/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Downloads/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Feeds/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/HistoryIndex.sk"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/Icons.db"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/WebpageIcons.db"),
- DefaultFilters.CreateWildcardFilter(@"*/Library/Saved Application State/*"),
- DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Microsoft User Data/Entourage Temp/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Network Trash Folder/*"),
- DefaultFilters.CreateWildcardFilter(@"*/Trash/*"),
- DefaultFilters.CreateWildcardFilter(@"*/VM Storage"),
- DefaultFilters.CreateWildcardFilter(@"*Mobile.*Backups/*"),
- DefaultFilters.CreateWildcardFilter(@"/.vol/*"),
- DefaultFilters.CreateWildcardFilter(@"/afs/*"),
- DefaultFilters.CreateWildcardFilter(@"/automount/*"),
- DefaultFilters.CreateWildcardFilter(@"/bin/*"),
- DefaultFilters.CreateWildcardFilter(@"/cores/*"),
- DefaultFilters.CreateWildcardFilter(@"/Desktop DB"),
- DefaultFilters.CreateWildcardFilter(@"/Desktop DF"),
- DefaultFilters.CreateWildcardFilter(@"/dev/.*"),
- DefaultFilters.CreateWildcardFilter(@"/etc/*"),
- DefaultFilters.CreateWildcardFilter(@"/mach.sym"),
- DefaultFilters.CreateWildcardFilter(@"/mach_kernel"),
- DefaultFilters.CreateWildcardFilter(@"/net/*"),
- DefaultFilters.CreateWildcardFilter(@"/Network/*"),
- DefaultFilters.CreateWildcardFilter(@"/Network/Servers*"),
- DefaultFilters.CreateWildcardFilter(@"/Previous Systems*"),
- DefaultFilters.CreateWildcardFilter(@"/private/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/Network/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/tmp/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/automount/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/db/dhcpclient/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/db/fseventsd/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/folders/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/run/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/spool/postfix/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/tmp/*"),
- DefaultFilters.CreateWildcardFilter(@"/private/var/vm/*"),
- DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
- DefaultFilters.CreateWildcardFilter(@"/sw/*"),
- DefaultFilters.CreateWildcardFilter(@"/System/*"),
- DefaultFilters.CreateWildcardFilter(@"/System/Library/Extensions/Caches/*"),
- DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
- DefaultFilters.CreateWildcardFilter(@"/Users/Shared/SC Info*"),
- DefaultFilters.CreateWildcardFilter(@"/usr/*"),
- };
- }
-
- ///
- /// Creates Linux filters
- ///
- /// Linux filters
- private static string[] CreateLinuxFilters()
- {
- return new[]
- {
- DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies"),
- DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies-journal"),
- DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
- DefaultFilters.CreateWildcardFilter(@"*~"),
- DefaultFilters.CreateWildcardFilter(@"/bin/*"),
- DefaultFilters.CreateWildcardFilter(@"/boot/*"),
- DefaultFilters.CreateWildcardFilter(@"/dev/*"),
- DefaultFilters.CreateWildcardFilter(@"/etc/*"),
- DefaultFilters.CreateWildcardFilter(@"/initrd/*"),
- DefaultFilters.CreateWildcardFilter(@"/lib/*"),
- DefaultFilters.CreateWildcardFilter(@"/opt/*"),
- DefaultFilters.CreateWildcardFilter(@"/proc/*"),
- DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
- DefaultFilters.CreateWildcardFilter(@"/selinux/*"),
- DefaultFilters.CreateWildcardFilter(@"/srv/*"),
- DefaultFilters.CreateWildcardFilter(@"/sys/*"),
- DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
- DefaultFilters.CreateWildcardFilter(@"/usr/*"),
- DefaultFilters.CreateWildcardFilter(@"/var/*"),
- };
- }
-
- ///
- /// Creates a wildcard filter
- ///
- /// Filter text
- /// Wildcard filter
- private static string CreateWildcardFilter(string filter)
- {
- // Create a filter with the given name.
- // However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
- return filter.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
- }
-
- ///
- /// Creates a Regex filter
- ///
- /// Filter text
- /// Regex filter
- private static string CreateRegexFilter(string filter)
- {
- // Create a filter with the given name.
- // However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
- string escapedAlt = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.AltDirectorySeparatorChar.ToString());
- string escaped = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.DirectorySeparatorChar.ToString());
- return "[" + filter.Replace(escapedAlt, escaped) + "]";
- }
-
- ///
- /// Wrapper method for getting a list of exclude paths from the Windows registry.
- /// This method guards the call to to avoid loader errors.
- ///
- /// The list of paths to exclude.
- private static string[] GetWindowsRegistryFilters()
- {
- try
- {
- return GetWindowsRegistryFiltersInternal();
- }
- catch
- {
- }
-
- return new string[0];
- }
-
- ///
- /// Helper method that reads the Windows registry and finds paths to exclude.
- /// This method should not be called directly as that could cause loader errors on Mono.
- ///
- /// The list of paths to exclude.
- [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- private static string[] GetWindowsRegistryFiltersInternal()
- {
- var rk = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Default);
- if (rk == null)
- return new string[0];
-
- var sk = rk.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\BackupRestore\FilesNotToBackup");
- if (sk == null)
- return new string[0];
-
- // Each value in this key is a potential path
- return sk.GetValueNames()
- .Where(x => x != null)
+// Copyright (C) 2015, The Duplicati Team
+// http://www.duplicati.com, info@duplicati.com
+//
+// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Duplicati.Library.Utility
+{
+ [Flags]
+ public enum DefaultFilterSet
+ {
+ None = 0,
+ Windows = 1,
+ OSX = 2,
+ Linux = 4,
+ All = Windows | OSX | Linux,
+ }
+
+ ///
+ /// This class defines a set of common filters for files that don't typically need to be backed up.
+ /// These filters are largely based on the filters described here:
+ /// https://superuser.com/questions/443890/what-files-file-types-and-folders-to-exclude-from-user-folder-backup
+ ///
+ public static class DefaultFilters
+ {
+ ///
+ /// Case insensitive mapping from strings to filter sets
+ /// ///
+ private static readonly Dictionary defaultFilterSetMapping =
+ ((DefaultFilterSet[])Enum.GetValues(typeof(DefaultFilterSet)))
+ .ToDictionary(val => val.ToString(), val => val, StringComparer.OrdinalIgnoreCase);
+
+ ///
+ /// Filters common for all operating systems
+ ///
+ public static IEnumerable Common
+ {
+ get
+ {
+ return DefaultFilters.CreateCommonFilters();
+ }
+ }
+
+ ///
+ /// Filters for Windows
+ ///
+ public static IEnumerable Windows
+ {
+ get
+ {
+ return DefaultFilters.CreateWindowsFilters();
+ }
+ }
+
+ ///
+ /// Filters for OSX
+ ///
+ public static IEnumerable OSX
+ {
+ get
+ {
+ return DefaultFilters.CreateOSXFilters();
+ }
+ }
+
+ ///
+ /// Filters for Linux
+ ///
+ public static IEnumerable Linux
+ {
+ get
+ {
+ return DefaultFilters.CreateLinuxFilters();
+ }
+ }
+
+ ///
+ /// Gets the filters indicated by the given filter sets
+ ///
+ /// Filter set options
+ /// Default filters
+ public static IEnumerable GetFilters(string options)
+ {
+ bool anyOptions = false;
+ DefaultFilterSet filterSets = DefaultFilterSet.None;
+ foreach (string option in options.Split(new[] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ anyOptions = true;
+ DefaultFilterSet filter;
+ if (DefaultFilters.defaultFilterSetMapping.TryGetValue(option, out filter))
+ {
+ filterSets |= filter;
+ }
+ else
+ {
+ throw new ArgumentException(Strings.Filters.UnknownDefaultFilterSet(option));
+ }
+ }
+
+ // If no filter sets are specified, we use the default for the platform we're on
+ if (!anyOptions)
+ {
+ if (Utility.IsClientWindows)
+ {
+ filterSets |= DefaultFilterSet.Windows;
+ }
+
+ if (Utility.IsClientOSX)
+ {
+ filterSets |= DefaultFilterSet.OSX;
+ }
+
+ if (Utility.IsClientLinux)
+ {
+ filterSets |= DefaultFilterSet.Linux;
+ }
+ }
+
+ if (filterSets == DefaultFilterSet.None)
+ {
+ return Enumerable.Empty();
+ }
+
+ IEnumerable filters = DefaultFilters.Common;
+
+ if ((filterSets & DefaultFilterSet.Windows) == DefaultFilterSet.Windows)
+ {
+ filters = filters.Concat(DefaultFilters.Windows);
+ }
+
+ if ((filterSets & DefaultFilterSet.OSX) == DefaultFilterSet.OSX)
+ {
+ filters = filters.Concat(DefaultFilters.OSX);
+ }
+
+ if ((filterSets & DefaultFilterSet.Linux) == DefaultFilterSet.Linux)
+ {
+ filters = filters.Concat(DefaultFilters.Linux);
+ }
+
+ // Filter down to distinct filters, then convert them to filter expressions
+ return filters.Distinct(Utility.ClientFilenameStringComparer).Select(filter => new FilterExpression(filter, false));
+ }
+
+ ///
+ /// Creates common filters
+ ///
+ /// Common filters
+ private static string[] CreateCommonFilters()
+ {
+ return new[]
+ {
+ DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/*cache*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/Safe Browsing*"),
+ DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Mozilla/Firefox/*cache*"),
+ DefaultFilters.CreateRegexFilter(@".*/(cookies|permissions).sqllite(-.{3})?"),
+ };
+ }
+
+ ///
+ /// Creates Windows filters
+ ///
+ /// Windows filters
+ private static string[] CreateWindowsFilters()
+ {
+ var filters = new[]
+ {
+ DefaultFilters.CreateWildcardFilter(@"*/config.msi/*.rbf"), // https://github.com/duplicati/duplicati/issues/2886
+ DefaultFilters.CreateWildcardFilter(@"*.tmp"),
+ DefaultFilters.CreateWildcardFilter(@"*.tmp/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/$RECYCLE.BIN/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/AppData/Apple Computer/Mobile Sync/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Microsoft/Windows Store/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Packages/*"), // https://superuser.com/questions/490925/explain-windows-8-windows-store-appdata-packages-and-what-to-backup
+ DefaultFilters.CreateWildcardFilter(@"*/AppData/Local/Temp*"),
+ DefaultFilters.CreateWildcardFilter(@"*/AppData/LocalLow/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/AppData/Temp*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Application Data/Apple Computer/Mobile Sync/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Application Data/Application Data*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Cookies/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies"),
+ DefaultFilters.CreateWildcardFilter(@"*/Google/Chrome/User Data/Default/Cookies-journal"),
+ DefaultFilters.CreateWildcardFilter(@"*/I386*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Internet Explorer/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Local Settings/History/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Local Settings/Temp*"),
+ DefaultFilters.CreateWildcardFilter(@"*/LocalService/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/RecoveryStore*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.edb"),
+ DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/*.log"),
+ DefaultFilters.CreateWildcardFilter(@"*/Microsoft*/Windows/Cookies*"),
+ DefaultFilters.CreateWildcardFilter(@"*/MSOCache*"),
+ DefaultFilters.CreateWildcardFilter(@"*/NetHood/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/NetworkService/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/NTUSER*"),
+ DefaultFilters.CreateWildcardFilter(@"*/ntuser.dat*"),
+ DefaultFilters.CreateWildcardFilter(@"*/PrintHood/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Recent/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/RECYCLER/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Safari/Library/Caches/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/SendTo/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Start Menu/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Temporary Internet Files/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Thumbs.db"),
+ DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat"),
+ DefaultFilters.CreateWildcardFilter(@"*UsrClass.dat.LOG"),
+ DefaultFilters.CreateWildcardFilter(@"?:/autoexec.bat"),
+ DefaultFilters.CreateWildcardFilter(@"?:/Config.Msi*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/hiberfil.sys"),
+ DefaultFilters.CreateWildcardFilter(@"?:/pagefile.sys"),
+ DefaultFilters.CreateWildcardFilter(@"?:/Program Files (x86)/*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/Program Files/*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/ProgramData/*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/swapfile.sys"),
+ DefaultFilters.CreateWildcardFilter(@"?:/System Volume Information/*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/Windows.old/*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/Windows/*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/Windows/Installer*"),
+ DefaultFilters.CreateWildcardFilter(@"?:/Windows/Temp*"),
+ };
+
+ var extra = GetWindowsRegistryFilters();
+ if (extra == null || extra.Length == 0)
+ return filters;
+
+ return filters.Union(extra).ToArray();
+ }
+
+ ///
+ /// Creates OSX filters
+ ///
+ /// OSX filters
+ private static string[] CreateOSXFilters()
+ {
+ return new[]
+ {
+ DefaultFilters.CreateWildcardFilter(@"*.fseventsd*"),
+ DefaultFilters.CreateWildcardFilter(@"*.hotfiles.btree*"),
+ DefaultFilters.CreateWildcardFilter(@"*.Spotlight-*/*"),
+ DefaultFilters.CreateWildcardFilter(@"*.Trash*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies"),
+ DefaultFilters.CreateWildcardFilter(@"*/Application Support/Google/Chrome/Default/Cookies-journal"),
+ DefaultFilters.CreateWildcardFilter(@"*/backups.backupdb/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/iP* Software Updates/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache*"),
+ DefaultFilters.CreateWildcardFilter(@"*/iTunes/Album Artwork/Cache/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Application Support/SyncServices/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Caches/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/*/Info.plist"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Calendars/Calendar Cache"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/com.apple.appstore.plist"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Cookies/Cookies.binarycookies"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Logs/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/*/Info.plist"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/AvailableFeeds/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Mail/Envelope Index"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Mirrors/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Database/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Downloads/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/PubSub/Feeds/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/HistoryIndex.sk"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/Icons.db"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Safari/WebpageIcons.db"),
+ DefaultFilters.CreateWildcardFilter(@"*/Library/Saved Application State/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Microsoft User Data/Entourage Temp/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Network Trash Folder/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/Trash/*"),
+ DefaultFilters.CreateWildcardFilter(@"*/VM Storage"),
+ DefaultFilters.CreateWildcardFilter(@"*Mobile.*Backups/*"),
+ DefaultFilters.CreateWildcardFilter(@"/.vol/*"),
+ DefaultFilters.CreateWildcardFilter(@"/afs/*"),
+ DefaultFilters.CreateWildcardFilter(@"/automount/*"),
+ DefaultFilters.CreateWildcardFilter(@"/bin/*"),
+ DefaultFilters.CreateWildcardFilter(@"/cores/*"),
+ DefaultFilters.CreateWildcardFilter(@"/Desktop DB"),
+ DefaultFilters.CreateWildcardFilter(@"/Desktop DF"),
+ DefaultFilters.CreateWildcardFilter(@"/dev/.*"),
+ DefaultFilters.CreateWildcardFilter(@"/etc/*"),
+ DefaultFilters.CreateWildcardFilter(@"/mach.sym"),
+ DefaultFilters.CreateWildcardFilter(@"/mach_kernel"),
+ DefaultFilters.CreateWildcardFilter(@"/net/*"),
+ DefaultFilters.CreateWildcardFilter(@"/Network/*"),
+ DefaultFilters.CreateWildcardFilter(@"/Network/Servers*"),
+ DefaultFilters.CreateWildcardFilter(@"/Previous Systems*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/Network/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/tmp/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/automount/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/db/dhcpclient/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/db/fseventsd/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/folders/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/run/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/spool/postfix/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/tmp/*"),
+ DefaultFilters.CreateWildcardFilter(@"/private/var/vm/*"),
+ DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
+ DefaultFilters.CreateWildcardFilter(@"/sw/*"),
+ DefaultFilters.CreateWildcardFilter(@"/System/*"),
+ DefaultFilters.CreateWildcardFilter(@"/System/Library/Extensions/Caches/*"),
+ DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
+ DefaultFilters.CreateWildcardFilter(@"/Users/Shared/SC Info*"),
+ DefaultFilters.CreateWildcardFilter(@"/usr/*"),
+ };
+ }
+
+ ///
+ /// Creates Linux filters
+ ///
+ /// Linux filters
+ private static string[] CreateLinuxFilters()
+ {
+ return new[]
+ {
+ DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies"),
+ DefaultFilters.CreateWildcardFilter(@"*/.config/google-chrome/Default/Cookies-journal"),
+ DefaultFilters.CreateWildcardFilter(@"*/lost+found/*"),
+ DefaultFilters.CreateWildcardFilter(@"*~"),
+ DefaultFilters.CreateWildcardFilter(@"/bin/*"),
+ DefaultFilters.CreateWildcardFilter(@"/boot/*"),
+ DefaultFilters.CreateWildcardFilter(@"/dev/*"),
+ DefaultFilters.CreateWildcardFilter(@"/etc/*"),
+ DefaultFilters.CreateWildcardFilter(@"/initrd/*"),
+ DefaultFilters.CreateWildcardFilter(@"/lib/*"),
+ DefaultFilters.CreateWildcardFilter(@"/opt/*"),
+ DefaultFilters.CreateWildcardFilter(@"/proc/*"),
+ DefaultFilters.CreateWildcardFilter(@"/sbin/*"),
+ DefaultFilters.CreateWildcardFilter(@"/selinux/*"),
+ DefaultFilters.CreateWildcardFilter(@"/srv/*"),
+ DefaultFilters.CreateWildcardFilter(@"/sys/*"),
+ DefaultFilters.CreateWildcardFilter(@"/tmp/*"),
+ DefaultFilters.CreateWildcardFilter(@"/usr/*"),
+ DefaultFilters.CreateWildcardFilter(@"/var/*"),
+ };
+ }
+
+ ///
+ /// Creates a wildcard filter
+ ///
+ /// Filter text
+ /// Wildcard filter
+ private static string CreateWildcardFilter(string filter)
+ {
+ // Create a filter with the given name.
+ // However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
+ return filter.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
+ }
+
+ ///
+ /// Creates a Regex filter
+ ///
+ /// Filter text
+ /// Regex filter
+ private static string CreateRegexFilter(string filter)
+ {
+ // Create a filter with the given name.
+ // However, in order to match paths correctly, the directory separators need to be normalized to match the system default.
+ string escapedAlt = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.AltDirectorySeparatorChar.ToString());
+ string escaped = System.Text.RegularExpressions.Regex.Escape(System.IO.Path.DirectorySeparatorChar.ToString());
+ return "[" + filter.Replace(escapedAlt, escaped) + "]";
+ }
+
+ ///
+ /// Wrapper method for getting a list of exclude paths from the Windows registry.
+ /// This method guards the call to to avoid loader errors.
+ ///
+ /// The list of paths to exclude.
+ private static string[] GetWindowsRegistryFilters()
+ {
+ if (Utility.IsClientWindows)
+ {
+ // One Windows, filters may also be stored in the registry
+ try
+ {
+ return GetWindowsRegistryFiltersInternal();
+ }
+ catch
+ {
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Helper method that reads the Windows registry and finds paths to exclude.
+ /// This method should not be called directly as that could cause loader errors on Mono.
+ ///
+ /// The list of paths to exclude.
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ private static string[] GetWindowsRegistryFiltersInternal()
+ {
+ var rk = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Default);
+ if (rk == null)
+ return new string[0];
+
+ var sk = rk.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\BackupRestore\FilesNotToBackup");
+ if (sk == null)
+ return new string[0];
+
+ // Each value in this key is a potential path
+ return sk.GetValueNames()
+ .Where(x => x != null)
.SelectMany(x =>
{
var v = sk.GetValue(x);
@@ -421,15 +425,15 @@ namespace Duplicati.Library.Utility
return (string[])v;
else
return new string[0];
- })
- .Where(x => !string.IsNullOrWhiteSpace(x))
- .Select(x => Environment.ExpandEnvironmentVariables(x))
- .Where(x => !string.IsNullOrWhiteSpace(x))
- .Where(x => x.IndexOfAny(System.IO.Path.GetInvalidPathChars()) < 0)
- .Where(x => System.IO.Path.IsPathRooted(x))
- .Select(x => x.EndsWith(" /s", StringComparison.OrdinalIgnoreCase) ? x.Substring(0, x.Length - 3).TrimEnd() : x)
- .ToArray();
- }
-
- }
-}
+ })
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Select(x => Environment.ExpandEnvironmentVariables(x))
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Where(x => x.IndexOfAny(System.IO.Path.GetInvalidPathChars()) < 0)
+ .Where(x => System.IO.Path.IsPathRooted(x))
+ .Select(x => x.EndsWith(" /s", StringComparison.OrdinalIgnoreCase) ? x.Substring(0, x.Length - 3).TrimEnd() : x)
+ .ToArray();
+ }
+
+ }
+}
diff --git a/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj b/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj
index 07ba1b083..e9a9509d5 100644
--- a/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj
+++ b/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj
@@ -51,6 +51,7 @@
+
@@ -102,7 +103,5 @@
Duplicati.Library.Localization
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Duplicati/Library/Utility/FilterExpression.cs b/Duplicati/Library/Utility/FilterExpression.cs
index 7064dc21d..a9ab54409 100644
--- a/Duplicati/Library/Utility/FilterExpression.cs
+++ b/Duplicati/Library/Utility/FilterExpression.cs
@@ -49,10 +49,22 @@ namespace Duplicati.Library.Utility
///
public class FilterExpression : IFilter
{
+ ///
+ /// Implementation of a filter entry
+ ///
private struct FilterEntry
{
+ ///
+ /// The type of the filter
+ ///
public readonly FilterType Type;
+ ///
+ /// The filter string
+ ///
public readonly string Filter;
+ ///
+ /// The regular expression version of the filter
+ ///
public readonly System.Text.RegularExpressions.Regex Regexp;
///
@@ -64,12 +76,18 @@ namespace Duplicati.Library.Utility
///
private const char MULTIPLE_WILDCARD = '*';
-
+ ///
+ /// The regular expression flags
+ ///
private static readonly System.Text.RegularExpressions.RegexOptions REGEXP_OPTIONS =
System.Text.RegularExpressions.RegexOptions.Compiled |
System.Text.RegularExpressions.RegexOptions.ExplicitCapture |
(Library.Utility.Utility.IsFSCaseSensitive ? System.Text.RegularExpressions.RegexOptions.None : System.Text.RegularExpressions.RegexOptions.IgnoreCase);
-
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The filter string to use.
public FilterEntry(string filter)
{
if (string.IsNullOrEmpty(filter))
@@ -78,7 +96,7 @@ namespace Duplicati.Library.Utility
this.Filter = null;
this.Regexp = null;
}
- if (filter.StartsWith("[") && filter.EndsWith("]"))
+ else if (filter.StartsWith("[", StringComparison.Ordinal) && filter.EndsWith("]", StringComparison.Ordinal))
{
this.Type = FilterType.Regexp;
this.Filter = filter.Substring(1, filter.Length - 2);
@@ -255,7 +273,6 @@ namespace Duplicati.Library.Utility
///
/// Creates a new instance, representing an empty filter.
///
- /// The filter string that represents the filter
public FilterExpression()
: this((IEnumerable)null, true)
{
@@ -302,7 +319,7 @@ namespace Duplicati.Library.Utility
if (string.IsNullOrWhiteSpace(filter))
return null;
- if (filter.Length < 2 || (filter.StartsWith("[") && filter.EndsWith("]")))
+ if (filter.Length < 2 || (filter.StartsWith("[", StringComparison.Ordinal) && filter.EndsWith("]", StringComparison.Ordinal)))
return new string[] { filter };
else
return filter.Split(new char[] { System.IO.Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
@@ -340,7 +357,7 @@ namespace Duplicati.Library.Utility
first = false;
}
- combined += "|(" + f.Regexp.ToString() + ")";
+ combined += "|(" + f.Regexp + ")";
}
}
@@ -584,9 +601,9 @@ namespace Duplicati.Library.Utility
foreach(var n in filters)
{
bool include;
- if (n.StartsWith("+"))
+ if (n.StartsWith("+", StringComparison.Ordinal))
include = true;
- else if (n.StartsWith("-"))
+ else if (n.StartsWith("-", StringComparison.Ordinal))
include = false;
else
continue;
diff --git a/Duplicati/Library/Utility/JoinedFilterExpression.cs b/Duplicati/Library/Utility/JoinedFilterExpression.cs
index 9ffaf8b4b..882183077 100644
--- a/Duplicati/Library/Utility/JoinedFilterExpression.cs
+++ b/Duplicati/Library/Utility/JoinedFilterExpression.cs
@@ -65,7 +65,7 @@ namespace Duplicati.Library.Utility
else if (this.Second.Empty)
return this.First.ToString();
else
- return "(" + this.First.ToString() + ") || (" + this.Second.ToString() + ")";
+ return "(" + this.First + ") || (" + this.Second + ")";
}
}
}
diff --git a/Duplicati/Library/Utility/ShaderStream.cs b/Duplicati/Library/Utility/ShaderStream.cs
new file mode 100644
index 000000000..7fc970ca0
--- /dev/null
+++ b/Duplicati/Library/Utility/ShaderStream.cs
@@ -0,0 +1,75 @@
+#region Disclaimer / License
+// Copyright (C) 2015, The Duplicati Team
+// http://www.duplicati.com, info@duplicati.com
+//
+// 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;
+
+namespace Duplicati.Library.Utility
+{
+ ///
+ /// A small utility stream that allows to keep streams open and counts the bytes sent through.
+ ///
+ public class ShaderStream : System.IO.Stream
+ {
+ private readonly System.IO.Stream m_baseStream;
+ private readonly bool m_keepBaseOpen;
+ private long m_read = 0;
+ private long m_written = 0;
+
+ public ShaderStream(System.IO.Stream baseStream, bool keepBaseOpen)
+ {
+ if (baseStream == null)
+ throw new ArgumentNullException(nameof(baseStream));
+ this.m_baseStream = baseStream;
+ this.m_keepBaseOpen = keepBaseOpen;
+ }
+
+ public long TotalBytesRead { get { return m_read; } }
+ public long TotalBytesWritten { get { return m_written; } }
+
+ public override bool CanRead { get { return m_baseStream.CanRead; } }
+ public override bool CanSeek { get { return m_baseStream.CanSeek; } }
+ public override bool CanWrite { get { return m_baseStream.CanWrite; } }
+ public override long Length { get { return m_baseStream.Length; } }
+ public override long Position
+ {
+ get { return m_baseStream.Position; }
+ set { m_baseStream.Position = value; }
+ }
+ public override void Flush() { m_baseStream.Flush(); }
+ public override long Seek(long offset, System.IO.SeekOrigin origin) { return m_baseStream.Seek(offset, origin); }
+ public override void SetLength(long value) { m_baseStream.SetLength(value); }
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int r = m_baseStream.Read(buffer, offset, count);
+ m_read += r;
+ return r;
+ }
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ m_baseStream.Write(buffer, offset, count);
+ m_written += count;
+ }
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && !m_keepBaseOpen)
+ m_baseStream.Close();
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/Duplicati/Library/Utility/Sizeparser.cs b/Duplicati/Library/Utility/Sizeparser.cs
index 409def46a..46a6d8671 100644
--- a/Duplicati/Library/Utility/Sizeparser.cs
+++ b/Duplicati/Library/Utility/Sizeparser.cs
@@ -32,7 +32,7 @@ namespace Duplicati.Library.Utility
size = size.ToLower().Trim();
- if (size.EndsWith("tb") || size.EndsWith("gb") || size.EndsWith("mb") || size.EndsWith("kb") || size.EndsWith("b"))
+ if (size.EndsWith("tb", StringComparison.Ordinal) || size.EndsWith("gb", StringComparison.Ordinal) || size.EndsWith("mb", StringComparison.Ordinal) || size.EndsWith("kb", StringComparison.Ordinal) || size.EndsWith("b", StringComparison.Ordinal))
return ParseSize(size);
else
return ParseSize(size + " " + defaultSuffix);
@@ -49,27 +49,27 @@ namespace Duplicati.Library.Utility
long factor = 1;
- if (size.EndsWith("tb"))
+ if (size.EndsWith("tb", StringComparison.Ordinal))
{
factor = 1024L * 1024 * 1024 * 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
- else if (size.EndsWith("gb"))
+ else if (size.EndsWith("gb", StringComparison.Ordinal))
{
factor = 1024 * 1024 * 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
- else if (size.EndsWith("mb"))
+ else if (size.EndsWith("mb", StringComparison.Ordinal))
{
factor = 1024 * 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
- else if (size.EndsWith("kb"))
+ else if (size.EndsWith("kb", StringComparison.Ordinal))
{
factor = 1024;
size = size.Substring(0, size.Length - 2).Trim();
}
- else if (size.EndsWith("b"))
+ else if (size.EndsWith("b", StringComparison.Ordinal))
size = size.Substring(0, size.Length - 1).Trim();
long r;
diff --git a/Duplicati/Library/Utility/TempFile.cs b/Duplicati/Library/Utility/TempFile.cs
index 21e645ca2..29832eae3 100644
--- a/Duplicati/Library/Utility/TempFile.cs
+++ b/Duplicati/Library/Utility/TempFile.cs
@@ -72,13 +72,17 @@ namespace Duplicati.Library.Utility
m_fileTrace.Add(s, st);
return s;
}
-
+
#else
private static string GenerateUniqueName()
{
return APPLICATION_PREFIX + Guid.NewGuid().ToString();
}
#endif
+ ///
+ /// The name of the temp file
+ ///
+ public string Name => m_path;
///
/// Gets all temporary files found in the current tempdir, that matches the application prefix
diff --git a/Duplicati/Library/Utility/TempFileStream.cs b/Duplicati/Library/Utility/TempFileStream.cs
index 532cce82f..9f814d62e 100644
--- a/Duplicati/Library/Utility/TempFileStream.cs
+++ b/Duplicati/Library/Utility/TempFileStream.cs
@@ -37,6 +37,11 @@ namespace Duplicati.Library.Utility
{
}
+ ///
+ /// Full name of the temp file.
+ ///
+ public string Name => m_file?.Name;
+
public TempFileStream(string file)
: this(TempFile.WrapExistingFile(file))
{
@@ -44,6 +49,8 @@ namespace Duplicati.Library.Utility
public TempFileStream(TempFile file)
{
+ if (file == null)
+ throw new ArgumentNullException(nameof(file));
m_file = file;
m_stream = System.IO.File.Open(file, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
}
diff --git a/Duplicati/Library/Utility/ThrottledStream.cs b/Duplicati/Library/Utility/ThrottledStream.cs
index 7da95e996..a82669aa6 100644
--- a/Duplicati/Library/Utility/ThrottledStream.cs
+++ b/Duplicati/Library/Utility/ThrottledStream.cs
@@ -92,8 +92,6 @@ namespace Duplicati.Library.Utility
/// Creates a throttle around a stream.
///
/// The stream to throttle
- /// The maximum number of bytes pr. second to read. Specify a number less than 1 to allow unlimited speed.
- /// The maximum number of bytes pr. second to write. Specify a number less than 1 to allow unlimited speed.
public ThrottledStream(Stream basestream, Action updateLimits)
: base(basestream)
{
@@ -217,11 +215,6 @@ namespace Duplicati.Library.Utility
///
/// Calculates the speed, and inserts appropriate delays
///
- /// True if the operation is read, false otherwise
- /// The data buffer
- /// The offset into the buffer
- /// The number of bytes to read or write
- /// The number of bytes processed while delaying
private void DelayIfRequired(ref long limit, int count, ref DateTime last_sample, ref long last_count, ref double current_speed)
{
var now = DateTime.Now;
diff --git a/Duplicati/Library/Utility/Uri.cs b/Duplicati/Library/Utility/Uri.cs
index 5f39e5db1..8b5a6baf3 100644
--- a/Duplicati/Library/Utility/Uri.cs
+++ b/Duplicati/Library/Utility/Uri.cs
@@ -165,11 +165,11 @@ namespace Duplicati.Library.Utility
var h = m.Groups["hostname"].Success ? m.Groups["hostname"].Value : "";
var p = m.Groups["path"].Success ? m.Groups["path"].Value : "";
- if (m.Groups["hostname"].Success && p.StartsWith("/"))
+ if (m.Groups["hostname"].Success && p.StartsWith("/", StringComparison.Ordinal))
p = p.Substring(1);
// file://c:\test support
- if (h.Length == 1 && p.StartsWith(":"))
+ if (h.Length == 1 && p.StartsWith(":", StringComparison.Ordinal))
{
h = h + p;
p = "";
@@ -260,7 +260,7 @@ namespace Duplicati.Library.Utility
if (!string.IsNullOrEmpty(path))
{
- if (!string.IsNullOrEmpty(host) && !path.StartsWith("/"))
+ if (!string.IsNullOrEmpty(host) && !path.StartsWith("/", StringComparison.Ordinal))
s += "/";
s += path;
}
@@ -444,7 +444,7 @@ namespace Duplicati.Library.Utility
{
if (query == null)
throw new ArgumentNullException("query");
- if (query.StartsWith("?"))
+ if (query.StartsWith("?", StringComparison.Ordinal))
query = query.Substring(1);
if (string.IsNullOrEmpty(query))
return new NameValueCollection(StringComparer.OrdinalIgnoreCase);
diff --git a/Duplicati/Library/Utility/UrlUtillity.cs b/Duplicati/Library/Utility/UrlUtillity.cs
index 5753bb1d6..84469e019 100644
--- a/Duplicati/Library/Utility/UrlUtillity.cs
+++ b/Duplicati/Library/Utility/UrlUtillity.cs
@@ -47,7 +47,7 @@ namespace Duplicati.Library.Utility
/// The url to open, must start with http:// or https://
public static void OpenURL(string url, string browserprogram = null)
{
- if (!url.StartsWith("http://") && !url.StartsWith("https://"))
+ if (!url.StartsWith("http://", StringComparison.Ordinal) && !url.StartsWith("https://", StringComparison.Ordinal))
throw new Exception("Malformed URL");
if (string.IsNullOrWhiteSpace(browserprogram))
@@ -107,7 +107,7 @@ namespace Duplicati.Library.Utility
try
{
- if (!url.StartsWith("http://") && !url.StartsWith("https://"))
+ if (!url.StartsWith("http://", StringComparison.Ordinal) && !url.StartsWith("https://", StringComparison.Ordinal))
throw new Exception("Malformed URL");
if (string.IsNullOrEmpty(browserprogram))
diff --git a/Duplicati/Library/Utility/Utility.cs b/Duplicati/Library/Utility/Utility.cs
index faf312d25..a1f42e45e 100644
--- a/Duplicati/Library/Utility/Utility.cs
+++ b/Duplicati/Library/Utility/Utility.cs
@@ -34,11 +34,6 @@ namespace Duplicati.Library.Utility
///
public static long DEFAULT_BUFFER_SIZE => SystemContextSettings.Buffersize;
- ///
- /// A value indicating if the current process is running in 64bit mode
- ///
- public static readonly bool Is64BitProcess = IntPtr.Size == 8;
-
///
/// Gets the hash algorithm used for calculating a hash
///
@@ -132,7 +127,6 @@ namespace Duplicati.Library.Utility
/// The search is recursive.
///
/// The folder to look in
- /// The filter to apply.
/// A list of the full filenames
public static IEnumerable EnumerateFiles(string basepath)
{
@@ -170,7 +164,7 @@ namespace Duplicati.Library.Utility
/// A list of the full filenames
public static IEnumerable EnumerateFiles(string basepath, IFilter filter)
{
- return EnumerateFileSystemEntries(basepath, filter).Where(x => !x.EndsWith(DirectorySeparatorString));
+ return EnumerateFileSystemEntries(basepath, filter).Where(x => !x.EndsWith(DirectorySeparatorString, StringComparison.Ordinal));
}
///
@@ -182,7 +176,7 @@ namespace Duplicati.Library.Utility
/// A list of the full paths
public static IEnumerable EnumerateFolders(string basepath, IFilter filter)
{
- return EnumerateFileSystemEntries(basepath, filter).Where(x => x.EndsWith(DirectorySeparatorString));
+ return EnumerateFileSystemEntries(basepath, filter).Where(x => x.EndsWith(DirectorySeparatorString, StringComparison.Ordinal));
}
///
@@ -547,7 +541,7 @@ namespace Duplicati.Library.Utility
///
/// Calculates the hash of a given stream, and returns the results as an base64 encoded string
///
- /// The stream to calculate the hash for
+ /// The stream to calculate the hash for
/// The base64 encoded hash
public static string CalculateHash(System.IO.Stream stream)
{
@@ -638,15 +632,17 @@ namespace Duplicati.Library.Utility
}
///
- /// Parses a string into a boolean value
+ /// Parses a string into a boolean value.
///
- /// The value to parse
- /// The default value, in case the string is not a valid boolean value
- /// The parsed value or the default value
- public static bool ParseBool(string value, bool @default)
+ /// The value to parse.
+ /// A delegate that returns the default value if is not a valid boolean value.
+ /// The parsed value, or the value returned by .
+ public static bool ParseBool(string value, Func defaultFunc)
{
- if (value == null)
- value = "";
+ if (String.IsNullOrWhiteSpace(value))
+ {
+ return defaultFunc();
+ }
switch (value.Trim().ToLower())
{
@@ -661,10 +657,21 @@ namespace Duplicati.Library.Utility
case "no":
return false;
default:
- return @default;
+ return defaultFunc();
}
}
+ ///
+ /// Parses a string into a boolean value.
+ ///
+ /// The value to parse.
+ /// The default value, in case is not a valid boolean value.
+ /// The parsed value, or the default value.
+ public static bool ParseBool(string value, bool @default)
+ {
+ return Utility.ParseBool(value, () => @default);
+ }
+
///
/// Parses an option from the option set, using the convention that if the option is set, it is true unless it parses to false, and false otherwise
///
@@ -827,18 +834,11 @@ namespace Duplicati.Library.Utility
{
var str = Environment.GetEnvironmentVariable("FILESYSTEM_CASE_SENSITIVE");
- if (!string.IsNullOrWhiteSpace(str))
- {
- str = str.Trim();
- if (new[] { "yes", "1", "on", "true" }.Contains(str, StringComparer.OrdinalIgnoreCase))
- return true;
- if (new[] { "no", "0", "off", "false" }.Contains(str, StringComparer.OrdinalIgnoreCase))
- return false;
- }
-
- //TODO: This should probably be determined by filesystem rather than OS,
+ // TODO: This should probably be determined by filesystem rather than OS,
// OSX can actually have the disks formated as Case Sensitive, but insensitive is default
- return IsClientLinux && !IsClientOSX;
+ Func defaultReply = () => Utility.IsClientLinux && !Utility.IsClientOSX;
+
+ return Utility.ParseBool(str, defaultReply);
}
}
@@ -974,16 +974,16 @@ namespace Duplicati.Library.Utility
///
/// The path to the users home directory
///
- private static readonly string HOME_PATH = Environment.GetFolderPath(IsClientLinux ? Environment.SpecialFolder.Personal : Environment.SpecialFolder.UserProfile);
+ public static readonly string HOME_PATH = Environment.GetFolderPath(IsClientLinux ? Environment.SpecialFolder.Personal : Environment.SpecialFolder.UserProfile);
///
- /// Expands environment variables, including the tilde character
+ /// Expands environment variables.
///
/// The expanded string.
/// The string to expand.
public static string ExpandEnvironmentVariables(string str)
{
- return Environment.ExpandEnvironmentVariables(str.Replace("~", HOME_PATH));
+ return Environment.ExpandEnvironmentVariables(str);
}
///
@@ -997,7 +997,7 @@ namespace Duplicati.Library.Utility
private static readonly Regex ENVIRONMENT_VARIABLE_MATCHER_LINUX = new Regex(@"\$(?\w+)|(\{(?[^\}]+)\})");
///
- /// Expands environment variables, including the tilde character, in a RegExp safe format
+ /// Expands environment variables in a RegExp safe format
///
/// The expanded string.
/// The string to expand.
@@ -1012,9 +1012,7 @@ namespace Duplicati.Library.Utility
// TODO: Should we switch to using the native format, instead of following the Windows scheme?
//IsClientLinux ? ENVIRONMENT_VARIABLE_MATCHER_LINUX : ENVIRONMENT_VARIABLE_MATCHER_WINDOWS
- ENVIRONMENT_VARIABLE_MATCHER_WINDOWS
- .Replace(str.Replace("~", Regex.Escape(HOME_PATH)), (m) =>
- Regex.Escape(lookup(m.Groups["name"].Value)));
+ ENVIRONMENT_VARIABLE_MATCHER_WINDOWS.Replace(str, (m) => Regex.Escape(lookup(m.Groups["name"].Value)));
}
///
@@ -1498,5 +1496,80 @@ namespace Duplicati.Library.Utility
}
}
}
+
+ ///
+ /// The regular expression matching all know non-quoted commandline characters
+ ///
+ private static readonly Regex COMMANDLINE_SAFE = new Regex(@"[A-Za-z0-9\-_/:\.]*");
+ ///
+ /// Special characters that needs to be escaped on Linux
+ ///
+ private static readonly Regex COMMANDLINE_ESCAPED_LINUX = new Regex(@"[""|$|`|\\|!]");
+
+ ///
+ /// Wraps a single argument in quotes suitable for the passing on the commandline
+ ///
+ /// The wrapped commandline element.
+ /// The argument to wrap.
+ /// A flag indicating if environment variables are allowed to be expanded
+ public static string WrapCommandLineElement(string arg, bool allowEnvExpansion)
+ {
+ if (string.IsNullOrWhiteSpace(arg))
+ return arg;
+
+ if (!Library.Utility.Utility.IsClientWindows)
+ {
+ // We could consider using single quotes that prevents all expansions
+ //if (!allowEnvExpansion)
+ // return "'" + arg.Replace("'", "\\'") + "'";
+
+ // Linux is using backslash to escape, except for !
+ arg = COMMANDLINE_ESCAPED_LINUX.Replace(arg, (match) =>
+ {
+ if (match.Value == "!")
+ return "\"'!'\"";
+
+ if (match.Value == "$" && allowEnvExpansion)
+ return match.Value;
+
+ return "\\" + match.Value;
+ });
+ }
+ else
+ {
+ // Windows needs only needs " replaced with "",
+ // but is prone to %var% expansion when used in
+ // immediate mode (i.e. from command prompt)
+ // Fortunately it does not expand when processes
+ // are started from within .Net
+
+ // TODO: I have not found a way to avoid escaping %varname%,
+ // and sadly it expands only if the variable exists
+ // making it even rarer and harder to diagnose when
+ // it happens
+ arg = arg.Replace("\"", "\"\"");
+
+ // Also fix the case where the argument ends with a slash
+ if (arg[arg.Length - 1] == '\\')
+ arg += "\\";
+ }
+
+ // Check that all characters are in the safe set
+ if (COMMANDLINE_SAFE.Match(arg).Length != arg.Length)
+ return "\"" + arg + "\"";
+ else
+ return arg;
+ }
+
+ ///
+ /// Wrap a set of commandline arguments suitable for the commandline
+ ///
+ /// A commandline string.
+ /// The arguments to create into a commandline.
+ /// A flag indicating if environment variables are allowed to be expanded
+ public static string WrapAsCommandLine(IEnumerable args, bool allowEnvExpansion = false)
+ {
+ return string.Join(" ", args.Select(x => WrapCommandLineElement(x, allowEnvExpansion)));
+ }
}
}
diff --git a/Duplicati/License/LicenseEntry.cs b/Duplicati/License/LicenseEntry.cs
index d89bca6e9..dc3e87c43 100644
--- a/Duplicati/License/LicenseEntry.cs
+++ b/Duplicati/License/LicenseEntry.cs
@@ -57,7 +57,7 @@ namespace Duplicati.License
if (!string.IsNullOrEmpty(urlfile) && System.IO.File.Exists(urlfile))
Url = System.IO.File.ReadAllText(urlfile).Trim();
License = System.IO.File.ReadAllText(licensefile);
- if (License.IndexOf("\r\n") < 0)
+ if (License.IndexOf("\r\n", StringComparison.Ordinal) < 0)
License = License.Replace("\n", "\r\n").Replace("\r", "\r\n");
if (Environment.NewLine != "\r\n")
License = License.Replace("\r\n", Environment.NewLine);
diff --git a/Duplicati/Server/Database/Backup.cs b/Duplicati/Server/Database/Backup.cs
index a45113880..3317421b2 100644
--- a/Duplicati/Server/Database/Backup.cs
+++ b/Duplicati/Server/Database/Backup.cs
@@ -92,7 +92,7 @@ namespace Duplicati.Server.Database
///
/// Gets a value indicating if this instance is not persisted to the database
///
- public bool IsTemporary { get { return ID == null ? false : ID.IndexOf("-") > 0; } }
+ public bool IsTemporary { get { return ID == null ? false : ID.IndexOf("-", StringComparison.Ordinal) > 0; } }
}
}
diff --git a/Duplicati/Server/Database/Connection.cs b/Duplicati/Server/Database/Connection.cs
index 4e4de0c8a..5d5ec0128 100644
--- a/Duplicati/Server/Database/Connection.cs
+++ b/Duplicati/Server/Database/Connection.cs
@@ -248,7 +248,7 @@ namespace Duplicati.Server.Database
if (tags == null || tags.Length == 0)
return new long[0];
- if (tags.Length == 1 && tags[0].StartsWith("ID="))
+ if (tags.Length == 1 && tags[0].StartsWith("ID=", StringComparison.Ordinal))
return new long[] { long.Parse(tags[0].Substring("ID=".Length)) };
lock(m_lock)
@@ -266,8 +266,8 @@ namespace Duplicati.Server.Database
p.Value = t;
cmd.Parameters.Add(p);
}
-
- cmd.CommandText = @"SELECT ""ID"" FROM ""Backup"" WHERE " + sb.ToString();
+
+ cmd.CommandText = @"SELECT ""ID"" FROM ""Backup"" WHERE " + sb;
return Read(cmd, (rd) => ConvertToInt64(rd, 0)).ToArray();
}
@@ -347,8 +347,8 @@ namespace Duplicati.Server.Database
p.Value = t;
cmd.Parameters.Add(p);
}
-
- cmd.CommandText = @"SELECT ""ID"" FROM ""Schedule"" WHERE " + sb.ToString();
+
+ cmd.CommandText = @"SELECT ""ID"" FROM ""Schedule"" WHERE " + sb;
return Read(cmd, (rd) => ConvertToInt64(rd, 0)).ToArray();
}
@@ -510,7 +510,7 @@ namespace Duplicati.Server.Database
@"INSERT INTO ""Backup"" (""Name"", ""Tags"", ""TargetURL"", ""DBPath"") VALUES (?,?,?,?)",
(n) => {
- if (n.TargetURL.IndexOf(Duplicati.Server.WebServer.Server.PASSWORD_PLACEHOLDER) >= 0)
+ if (n.TargetURL.IndexOf(Duplicati.Server.WebServer.Server.PASSWORD_PLACEHOLDER, StringComparison.Ordinal) >= 0)
throw new Exception("Attempted to save a backup with the password placeholder");
if (update && long.Parse(n.ID) <= 0)
throw new Exception("Invalid update, cannot update application settings through update method");
diff --git a/Duplicati/Server/Database/ServerSettings.cs b/Duplicati/Server/Database/ServerSettings.cs
index 8104bb175..2ba5d3716 100644
--- a/Duplicati/Server/Database/ServerSettings.cs
+++ b/Duplicati/Server/Database/ServerSettings.cs
@@ -89,7 +89,7 @@ namespace Duplicati.Server.Database
m_values.Clear();
foreach(var k in newsettings)
- if (!clearExisting && newsettings[k.Key] == null && k.Key.StartsWith("--"))
+ if (!clearExisting && newsettings[k.Key] == null && k.Key.StartsWith("--", StringComparison.Ordinal))
m_values.Remove(k.Key);
else
m_values[k.Key] = newsettings[k.Key];
diff --git a/Duplicati/Server/Duplicati.Server.csproj b/Duplicati/Server/Duplicati.Server.csproj
index bc5c3a49f..2328b2599 100644
--- a/Duplicati/Server/Duplicati.Server.csproj
+++ b/Duplicati/Server/Duplicati.Server.csproj
@@ -13,6 +13,7 @@
v4.5false
+ truetrue
diff --git a/Duplicati/Server/LiveControls.cs b/Duplicati/Server/LiveControls.cs
index 827651f5f..cafbfa6de 100644
--- a/Duplicati/Server/LiveControls.cs
+++ b/Duplicati/Server/LiveControls.cs
@@ -163,7 +163,6 @@ namespace Duplicati.Server
///
/// Constructs a new instance of the LiveControl
///
- /// The duration that the backups should be initially suspended
public LiveControls(Database.ServerSettings settings)
{
m_state = LiveControlState.Running;
@@ -185,9 +184,24 @@ namespace Duplicati.Server
m_priority = settings.ThreadPriorityOverride;
if (!string.IsNullOrEmpty(settings.DownloadSpeedLimit))
- m_downloadLimit = Library.Utility.Sizeparser.ParseSize(settings.DownloadSpeedLimit, "kb");
+ try
+ {
+ m_downloadLimit = Library.Utility.Sizeparser.ParseSize(settings.DownloadSpeedLimit, "kb");
+ }
+ catch (Exception ex)
+ {
+ Library.Logging.Log.WriteMessage(string.Format("Failed to parse download limit: {0}", settings.DownloadSpeedLimit), Library.Logging.LogMessageType.Error, ex);
+ }
+
if (!string.IsNullOrEmpty(settings.UploadSpeedLimit))
- m_uploadLimit = Library.Utility.Sizeparser.ParseSize(settings.UploadSpeedLimit, "kb");
+ try
+ {
+ m_uploadLimit = Library.Utility.Sizeparser.ParseSize(settings.UploadSpeedLimit, "kb");
+ }
+ catch (Exception ex)
+ {
+ Library.Logging.Log.WriteMessage(string.Format("Failed to parse upload limit: {0}", settings.UploadSpeedLimit), Library.Logging.LogMessageType.Error, ex);
+ }
try
{
@@ -201,7 +215,6 @@ namespace Duplicati.Server
/// Event that occurs when the timeout duration is exceeded
///
/// The sender of the event
- /// An unused event argument
private void m_waitTimer_Tick(object sender)
{
lock (m_lock)
diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs
index 28d116882..a803d7144 100644
--- a/Duplicati/Server/Program.cs
+++ b/Duplicati/Server/Program.cs
@@ -765,8 +765,8 @@ namespace Duplicati.Server
new Duplicati.Library.Interface.CommandLineArgument("log-level", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Enumeration, Strings.Program.LoglevelCommandDescription, Strings.Program.LoglevelCommandDescription, "Warning", null, Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType))),
new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_WEBROOT, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Path, Strings.Program.WebserverWebrootDescription, Strings.Program.WebserverWebrootDescription, Duplicati.Server.WebServer.Server.DEFAULT_OPTION_WEBROOT),
new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_PORT, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverPortDescription, Strings.Program.WebserverPortDescription, Duplicati.Server.WebServer.Server.DEFAULT_OPTION_PORT.ToString()),
- new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILE, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverCertificateFileDescription, Strings.Program.WebserverCertificateFileDescription, Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILE.ToString()),
- new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILEPASSWORD, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverCertificatePasswordDescription, Strings.Program.WebserverCertificatePasswordDescription, Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILEPASSWORD.ToString()),
+ new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILE, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverCertificateFileDescription, Strings.Program.WebserverCertificateFileDescription, Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILE),
+ new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILEPASSWORD, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverCertificatePasswordDescription, Strings.Program.WebserverCertificatePasswordDescription, Duplicati.Server.WebServer.Server.OPTION_SSLCERTIFICATEFILEPASSWORD),
new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_INTERFACE, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverInterfaceDescription, Strings.Program.WebserverInterfaceDescription, Duplicati.Server.WebServer.Server.DEFAULT_OPTION_INTERFACE),
new Duplicati.Library.Interface.CommandLineArgument("webservice-password", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Password, Strings.Program.WebserverPasswordDescription, Strings.Program.WebserverPasswordDescription),
new Duplicati.Library.Interface.CommandLineArgument("ping-pong-keepalive", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.PingpongkeepaliveShort, Strings.Program.PingpongkeepaliveLong),
diff --git a/Duplicati/Server/Runner.cs b/Duplicati/Server/Runner.cs
index fd09be390..45fc863c7 100644
--- a/Duplicati/Server/Runner.cs
+++ b/Duplicati/Server/Runner.cs
@@ -361,8 +361,6 @@ namespace Duplicati.Server
where !string.IsNullOrWhiteSpace(p)
select p).ToArray();
- var cmd = new System.Text.StringBuilder();
-
var exe =
System.IO.Path.Combine(
Library.AutoUpdater.UpdaterManager.InstalledBaseDir,
@@ -371,42 +369,26 @@ namespace Duplicati.Server
)
);
- Func commandLineEscapeValue = x =>
- {
- if (string.IsNullOrWhiteSpace(x))
- return x;
-
- if (x.EndsWith("\\", StringComparison.Ordinal))
- x += "\\";
-
- x = x.Replace("\"", Library.Utility.Utility.IsClientWindows ? "\"\"" : "\\\"");
-
- return "\"" + x + "\"";
- };
-
- exe = commandLineEscapeValue(exe);
-
+ var cmd = new System.Text.StringBuilder();
if (Library.Utility.Utility.IsMono)
- exe = "mono " + exe;
-
+ cmd.Append("mono ");
- cmd.Append(exe);
- cmd.Append(" backup");
- cmd.Append(" ");
- cmd.Append(commandLineEscapeValue(backup.TargetURL));
- cmd.Append(" ");
- cmd.Append(string.Join(" ", sources.Select(x => commandLineEscapeValue(x))));
+ cmd.Append(Library.Utility.Utility.WrapAsCommandLine(new string[] { exe, "backup", backup.TargetURL }, false));
+ cmd.Append(" ");
+ cmd.Append(Library.Utility.Utility.WrapAsCommandLine(sources, true));
+
+ // TODO: We should check each option to see if it is a path, and allow expansion on that
foreach(var opt in options)
- cmd.AppendFormat(" --{0}={1}", opt.Key, commandLineEscapeValue(opt.Value));
+ cmd.AppendFormat(" --{0}={1}", opt.Key, Library.Utility.Utility.WrapCommandLineElement(opt.Value, false));
if (cf != null)
foreach(var f in cf)
- cmd.AppendFormat(" --{0}={1}", f.Include ? "include" : "exclude", commandLineEscapeValue(f.Expression));
+ cmd.AppendFormat(" --{0}={1}", f.Include ? "include" : "exclude", Library.Utility.Utility.WrapCommandLineElement(f.Expression, true));
if (bf != null)
foreach(var f in bf)
- cmd.AppendFormat(" --{0}={1}", f.Include ? "include" : "exclude", commandLineEscapeValue(f.Expression));
+ cmd.AppendFormat(" --{0}={1}", f.Include ? "include" : "exclude", Library.Utility.Utility.WrapCommandLineElement(f.Expression, true));
return cmd.ToString();
}
@@ -851,12 +833,12 @@ namespace Duplicati.Server
// Apply normal options
foreach(var o in backup.Settings)
- if (!o.Name.StartsWith("--") && TestIfOptionApplies(backup, mode, o.Filter))
+ if (!o.Name.StartsWith("--", StringComparison.Ordinal) && TestIfOptionApplies(backup, mode, o.Filter))
options[o.Name] = o.Value;
// Apply override options
foreach(var o in backup.Settings)
- if (o.Name.StartsWith("--") && TestIfOptionApplies(backup, mode, o.Filter))
+ if (o.Name.StartsWith("--", StringComparison.Ordinal) && TestIfOptionApplies(backup, mode, o.Filter))
options[o.Name.Substring(2)] = o.Value;
@@ -873,8 +855,8 @@ namespace Duplicati.Server
{
var nf =
(from n in f2
- let exp =
- n.Expression.StartsWith("[") && n.Expression.EndsWith("]")
+ let exp =
+ n.Expression.StartsWith("[", StringComparison.Ordinal) && n.Expression.EndsWith("]", StringComparison.Ordinal)
? SpecialFolders.ExpandEnvironmentVariablesRegexp(n.Expression)
: SpecialFolders.ExpandEnvironmentVariables(n.Expression)
orderby n.Order
diff --git a/Duplicati/Server/Scheduler.cs b/Duplicati/Server/Scheduler.cs
index 5db794ba7..bc3912810 100644
--- a/Duplicati/Server/Scheduler.cs
+++ b/Duplicati/Server/Scheduler.cs
@@ -73,9 +73,7 @@ namespace Duplicati.Server
///
/// Constructs a new scheduler
///
- /// The database connection
/// The worker thread
- /// The database lock object
public Scheduler(WorkerThread worker)
{
m_thread = new Thread(new ThreadStart(Runner));
diff --git a/Duplicati/Server/Serializable/ServerStatus.cs b/Duplicati/Server/Serializable/ServerStatus.cs
index 1804dc87f..378225270 100644
--- a/Duplicati/Server/Serializable/ServerStatus.cs
+++ b/Duplicati/Server/Serializable/ServerStatus.cs
@@ -84,7 +84,7 @@ namespace Duplicati.Server.Serializable
return (
from n in Program.Scheduler.Schedule
let backupid = (from t in n.Value.Tags
- where t != null && t.StartsWith("ID=")
+ where t != null && t.StartsWith("ID=", StringComparison.Ordinal)
select t.Substring("ID=".Length)).FirstOrDefault()
where !string.IsNullOrWhiteSpace(backupid)
select new Tuple(backupid, n.Key)
diff --git a/Duplicati/Server/SpecialFolders.cs b/Duplicati/Server/SpecialFolders.cs
index fc7b31c9f..504f2be05 100644
--- a/Duplicati/Server/SpecialFolders.cs
+++ b/Duplicati/Server/SpecialFolders.cs
@@ -30,7 +30,7 @@ namespace Duplicati.Server
public static string ExpandEnvironmentVariables(string path)
{
foreach(var n in Nodes)
- if (path.StartsWith(n.id))
+ if (path.StartsWith(n.id, StringComparison.Ordinal))
path = path.Replace(n.id, n.resolvedpath);
return Library.Utility.Utility.ExpandEnvironmentVariables(path);
}
@@ -162,7 +162,7 @@ namespace Duplicati.Server
try
{
var nx = x;
- if (nx.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
+ if (nx.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
nx = nx.Substring(0, nx.Length - 1);
var n = systemIO.PathGetFileName(nx);
if (!string.IsNullOrWhiteSpace(n))
@@ -172,8 +172,8 @@ namespace Duplicati.Server
{
}
- if (x.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()) && x.Length > 1)
- return new KeyValuePair(x, x.Substring(0, x.Length - 1).Substring(x.Substring(0, x.Length - 1).LastIndexOf("/") + 1));
+ if (x.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) && x.Length > 1)
+ return new KeyValuePair(x, x.Substring(0, x.Length - 1).Substring(x.Substring(0, x.Length - 1).LastIndexOf("/", StringComparison.Ordinal) + 1));
else
return new KeyValuePair(x, x);
diff --git a/Duplicati/Server/WebServer/AuthenticationHandler.cs b/Duplicati/Server/WebServer/AuthenticationHandler.cs
index bd8339e4d..908d6ae6f 100644
--- a/Duplicati/Server/WebServer/AuthenticationHandler.cs
+++ b/Duplicati/Server/WebServer/AuthenticationHandler.cs
@@ -250,7 +250,7 @@ namespace Duplicati.Server.WebServer
var expires = DateTime.UtcNow.AddHours(1);
m_prng.GetBytes(buf);
var token = Duplicati.Library.Utility.Utility.Base64UrlEncode(buf);
- while (token.Length > 0 && token.EndsWith("="))
+ while (token.Length > 0 && token.EndsWith("=", StringComparison.Ordinal))
token = token.Substring(0, token.Length - 1);
m_activeTokens.AddOrUpdate(token, key => expires, (key, existingValue) =>
diff --git a/Duplicati/Server/WebServer/IndexHtmlHandler.cs b/Duplicati/Server/WebServer/IndexHtmlHandler.cs
index 35dfee9ac..bc03c4c09 100644
--- a/Duplicati/Server/WebServer/IndexHtmlHandler.cs
+++ b/Duplicati/Server/WebServer/IndexHtmlHandler.cs
@@ -40,7 +40,7 @@ namespace Duplicati.Server.WebServer
if (System.IO.Directory.Exists(path) && (System.IO.File.Exists(html) || System.IO.File.Exists(htm)))
{
- if (!request.Uri.AbsolutePath.EndsWith("/"))
+ if (!request.Uri.AbsolutePath.EndsWith("/", StringComparison.Ordinal))
{
response.Redirect(request.Uri.AbsolutePath + "/");
return true;
@@ -68,7 +68,7 @@ namespace Duplicati.Server.WebServer
if (ForbiddenChars.Where(x => uri.AbsolutePath.Contains(x)).Any())
throw new BadRequestException("Illegal path");
var uripath = Uri.UnescapeDataString(uri.AbsolutePath);
- while(uripath.Length > 0 && (uripath.StartsWith("/") || uripath.StartsWith(DirSep)))
+ while(uripath.Length > 0 && (uripath.StartsWith("/", StringComparison.Ordinal) || uripath.StartsWith(DirSep, StringComparison.Ordinal)))
uripath = uripath.Substring(1);
return System.IO.Path.Combine(m_webroot, uripath.Replace('/', System.IO.Path.DirectorySeparatorChar));
}
diff --git a/Duplicati/Server/WebServer/RESTHandler.cs b/Duplicati/Server/WebServer/RESTHandler.cs
index f006a2711..5c4901572 100644
--- a/Duplicati/Server/WebServer/RESTHandler.cs
+++ b/Duplicati/Server/WebServer/RESTHandler.cs
@@ -188,7 +188,7 @@ namespace Duplicati.Server.WebServer
catch(Exception ex)
{
Program.DataConnection.LogError("", string.Format("Request for {0} gave error", info.Request.Uri), ex);
- Console.WriteLine(ex.ToString());
+ Console.WriteLine(ex);
try
{
diff --git a/Duplicati/Server/WebServer/RESTMethods/CommandLine.cs b/Duplicati/Server/WebServer/RESTMethods/CommandLine.cs
index 72ccc6bd0..addeed592 100644
--- a/Duplicati/Server/WebServer/RESTMethods/CommandLine.cs
+++ b/Duplicati/Server/WebServer/RESTMethods/CommandLine.cs
@@ -62,7 +62,7 @@ namespace Duplicati.Server.WebServer.RESTMethods
if (m_sb.Length != 0)
{
- m_target.Log.Add(m_sb.ToString() + value);
+ m_target.Log.Add(m_sb + value);
m_sb.Length = 0;
m_newlinechars = 0;
}
diff --git a/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs b/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs
index dd44dfe3a..f93f72013 100644
--- a/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs
+++ b/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs
@@ -48,9 +48,9 @@ namespace Duplicati.Server.WebServer.RESTMethods
string specialpath = null;
string specialtoken = null;
- if (path.StartsWith("%"))
+ if (path.StartsWith("%", StringComparison.Ordinal))
{
- var ix = path.IndexOf("%", 1);
+ var ix = path.IndexOf("%", 1, StringComparison.Ordinal);
if (ix > 0)
{
var tk = path.Substring(0, ix + 1);
@@ -65,7 +65,7 @@ namespace Duplicati.Server.WebServer.RESTMethods
path = SpecialFolders.ExpandEnvironmentVariables(path);
- if (Duplicati.Library.Utility.Utility.IsClientLinux && !path.StartsWith("/"))
+ if (Duplicati.Library.Utility.Utility.IsClientLinux && !path.StartsWith("/", StringComparison.Ordinal))
{
info.ReportClientError("The path parameter must start with a forward-slash");
return;
diff --git a/Duplicati/Server/WebServer/SynologyAuthenticationHandler.cs b/Duplicati/Server/WebServer/SynologyAuthenticationHandler.cs
index 37c49f084..f8c7d960d 100644
--- a/Duplicati/Server/WebServer/SynologyAuthenticationHandler.cs
+++ b/Duplicati/Server/WebServer/SynologyAuthenticationHandler.cs
@@ -154,7 +154,7 @@ namespace Duplicati.Server.WebServer
else
throw new Exception("Unable to get XSRF token");
}
- catch (Exception ex)
+ catch (Exception)
{
response.Status = System.Net.HttpStatusCode.InternalServerError;
response.Reason = "The system is incorrectly configured";
@@ -175,7 +175,7 @@ namespace Duplicati.Server.WebServer
{
username = ShellExec(AUTH_CGI, shell: false, exitcode: 0, env: tmpenv).Result;
}
- catch (Exception ex)
+ catch (Exception)
{
response.Status = System.Net.HttpStatusCode.InternalServerError;
response.Reason = "The system is incorrectly configured";
diff --git a/Duplicati/UnitTest/BasicSetupHelper.cs b/Duplicati/UnitTest/BasicSetupHelper.cs
index ce045a3da..8636df956 100644
--- a/Duplicati/UnitTest/BasicSetupHelper.cs
+++ b/Duplicati/UnitTest/BasicSetupHelper.cs
@@ -29,7 +29,7 @@ namespace Duplicati.UnitTest
///
protected static readonly string BASEFOLDER =
string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("UNITTEST_BASEFOLDER"))
- ? Library.Utility.Utility.ExpandEnvironmentVariables(Path.Combine("~", "testdata"))
+ ? Path.Combine(Library.Utility.Utility.HOME_PATH, "testdata")
: Environment.GetEnvironmentVariable("UNITTEST_BASEFOLDER");
///
@@ -60,9 +60,7 @@ namespace Duplicati.UnitTest
/// this can be used to diagnose errors on a CI build instance by setting
/// the environment variable DEBUG_OUTPUT=1 and running the job
///
- public static readonly bool DEBUG_OUTPUT =
- new[] { "1", "true", "on", "yes" }
- .Contains(Environment.GetEnvironmentVariable("DEBUG_OUTPUT") ?? "", StringComparer.OrdinalIgnoreCase);
+ public static readonly bool DEBUG_OUTPUT = Library.Utility.Utility.ParseBool(Environment.GetEnvironmentVariable("DEBUG_OUTPUT"), false);
///
/// Writes a message to TestContext.Progress and Console.Out
diff --git a/Duplicati/UnitTest/CompressionTests.cs b/Duplicati/UnitTest/CompressionTests.cs
index 7ed414d9e..f317ac766 100644
--- a/Duplicati/UnitTest/CompressionTests.cs
+++ b/Duplicati/UnitTest/CompressionTests.cs
@@ -17,6 +17,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using NUnit.Framework;
namespace Duplicati.UnitTest
@@ -25,44 +26,87 @@ namespace Duplicati.UnitTest
[Category("Compression")]
public class CompressionTests
{
- [Test]
- public void TestZipCompressionHints()
- {
- TestCompressionHints("zip");
- }
-
- [Test]
- public void Test7zCompressionHints()
- {
- TestCompressionHints("7z");
- }
-
+ [TestCase("zip")]
+ [TestCase("7z")]
public void TestCompressionHints(string module)
{
const int TESTSIZE = 1024 * 1024;
- using (var tf0 = new Library.Utility.TempFile())
- using (var tf1 = new Library.Utility.TempFile())
+ using (var tf0 = new MemoryStream())
+ using (var tf1 = new MemoryStream())
{
var opts = new Dictionary();
opts["zip-compression-level"] = "9";
- using (var z0 = Library.DynamicLoader.CompressionLoader.GetModule(module, tf0, opts))
+ using (var z0 = Library.DynamicLoader.CompressionLoader.GetModule(module, tf0, Library.Interface.ArchiveMode.Write, opts))
using (var fs0 = z0.CreateFile("sample", Library.Interface.CompressionHint.Noncompressible, DateTime.Now))
fs0.Write(new byte[TESTSIZE], 0, TESTSIZE);
- using (var z1 = Library.DynamicLoader.CompressionLoader.GetModule(module, tf1, opts))
+ using (var z1 = Library.DynamicLoader.CompressionLoader.GetModule(module, tf1, Library.Interface.ArchiveMode.Write, opts))
using (var fs1 = z1.CreateFile("sample", Library.Interface.CompressionHint.Compressible, DateTime.Now))
fs1.Write(new byte[TESTSIZE], 0, TESTSIZE);
- if (new FileInfo(tf0).Length < TESTSIZE)
+ if (tf0.Length < TESTSIZE)
throw new Exception("Compression hint non-compressible is not honored");
- if (new FileInfo(tf1).Length > new FileInfo(tf0).Length * 0.25)
+ if (tf1.Length > tf0.Length * 0.25)
throw new Exception("Compression is not applied");
}
}
-
+
+ ///
+ /// Test compression reversibility
+ ///
+ ///
+ [TestCase("zip")]
+ [TestCase("7z")]
+ public void TestCompressionReversibility(string module)
+ {
+ const int TESTSIZE = 1024 * 1024;
+ var testset1 = Enumerable.Range(0, TESTSIZE).Select(i => (byte)((i * 22695477) % 257));
+ var testset2 = Enumerable.Range(0, TESTSIZE).Select(i => (byte)((i * 48271) % 257));
+
+ using (var stream = new MemoryStream())
+ {
+ var opts = new Dictionary();
+ opts["zip-compression-level"] = "9";
+
+ // Compress test streams
+ using (var z0 = Library.DynamicLoader.CompressionLoader.GetModule(module, stream, Library.Interface.ArchiveMode.Write, opts))
+ {
+ // Add two files to the archive
+ using (var fs1 = z0.CreateFile("sample1", Library.Interface.CompressionHint.Compressible, DateTime.Now))
+ fs1.Write(testset1.ToArray(), 0, TESTSIZE);
+
+ using (var fs1 = z0.CreateFile("sample2", Library.Interface.CompressionHint.Compressible, DateTime.Now))
+ fs1.Write(testset2.ToArray(), 0, TESTSIZE);
+ }
+
+ Console.WriteLine("Compression rate for module {0}: {1:0.00}%", module, 100.0 * stream.Length / (TESTSIZE * 2));
+ // Decompress
+ using (var z0 = Library.DynamicLoader.CompressionLoader.GetModule(module, stream, Library.Interface.ArchiveMode.Read, opts))
+ {
+ // Get files list
+ var files = z0.ListFiles(null);
+
+ // Read second file
+ using (var fd = z0.OpenRead(files[1]))
+ {
+ bool match = testset2.All(b => b == fd.ReadByte()) && fd.ReadByte() == -1;
+ if (!match)
+ throw new Exception("Decompressed file sample2 contents do not match the source file.");
+ }
+
+ // Read first file
+ using (var fd = z0.OpenRead(files[0]))
+ {
+ bool match = testset1.All(b => b == fd.ReadByte()) && fd.ReadByte() == -1;
+ if (!match)
+ throw new Exception("Decompressed file sample1 contents do not match the source file.");
+ }
+ }
+ }
+ }
}
}
diff --git a/Duplicati/UnitTest/GeneralBlackBoxTesting.cs b/Duplicati/UnitTest/GeneralBlackBoxTesting.cs
index 9143d128f..fb9e508ec 100644
--- a/Duplicati/UnitTest/GeneralBlackBoxTesting.cs
+++ b/Duplicati/UnitTest/GeneralBlackBoxTesting.cs
@@ -29,7 +29,7 @@ namespace Duplicati.UnitTest
private static readonly string SOURCE_FOLDERS =
Path.Combine(
string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("UNITTEST_BASEFOLDER"))
- ? Library.Utility.Utility.ExpandEnvironmentVariables(Path.Combine("~", "testdata"))
+ ? Path.Combine(Library.Utility.Utility.HOME_PATH, "testdata")
: Environment.GetEnvironmentVariable("UNITTEST_BASEFOLDER")
, "DSMCBE");
diff --git a/Duplicati/UnitTest/SVNCheckoutsTest.cs b/Duplicati/UnitTest/SVNCheckoutsTest.cs
index 74107abf4..146086dce 100644
--- a/Duplicati/UnitTest/SVNCheckoutsTest.cs
+++ b/Duplicati/UnitTest/SVNCheckoutsTest.cs
@@ -99,11 +99,6 @@ namespace Duplicati.UnitTest
where !string.IsNullOrWhiteSpace(x)
select Library.Utility.Utility.ExpandEnvironmentVariables(x)).ToArray();
- //Expand the tilde to home folder on Linux/OSX
- if (Utility.IsClientLinux)
- folders = (from x in folders
- select x.Replace("~", Environment.GetFolderPath(Environment.SpecialFolder.Personal))).ToArray();
-
foreach (var f in folders)
foreach (var n in f.Split(new char[] { System.IO.Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries))
if (!System.IO.Directory.Exists(n))
@@ -245,7 +240,7 @@ namespace Duplicati.UnitTest
StringBuilder sb = new StringBuilder();
sb.AppendLine("Entry count: " + entries.Count.ToString());
sb.Append(string.Format("Found {0} filelists but there were {1} source folders", entries.Count, folders.Length));
- throw new Exception("Filename parsing problem, or corrupt storage: " + sb.ToString());
+ throw new Exception("Filename parsing problem, or corrupt storage: " + sb);
}
if (!skipfullrestore || !skippartialrestore)
@@ -274,7 +269,7 @@ namespace Duplicati.UnitTest
//Remove all folders from list
for (int j = 0; j < sourcefiles.Count; j++)
- if (sourcefiles[j].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
+ if (sourcefiles[j].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
{
sourcefiles.RemoveAt(j);
j--;
@@ -366,7 +361,7 @@ namespace Duplicati.UnitTest
continue;
if (s == logfilename)
continue;
- if (s.StartsWith(Utility.AppendDirSeparator(tf)))
+ if (s.StartsWith(Utility.AppendDirSeparator(tf), StringComparison.Ordinal))
continue;
Log.WriteMessage(string.Format("Found left-over temp file: {0}", s.Substring(tempdir.Length)), LogMessageType.Warning);
@@ -380,7 +375,7 @@ namespace Duplicati.UnitTest
}
foreach (string s in Utility.EnumerateFolders(tempdir))
- if (!s.StartsWith(Utility.AppendDirSeparator(tf)) && Utility.AppendDirSeparator(s) != Utility.AppendDirSeparator(tf) && Utility.AppendDirSeparator(s) != Utility.AppendDirSeparator(tempdir))
+ if (!s.StartsWith(Utility.AppendDirSeparator(tf), StringComparison.Ordinal) && Utility.AppendDirSeparator(s) != Utility.AppendDirSeparator(tf) && Utility.AppendDirSeparator(s) != Utility.AppendDirSeparator(tempdir))
{
Log.WriteMessage(string.Format("Found left-over temp folder: {0}", s.Substring(tempdir.Length)), LogMessageType.Warning);
BasicSetupHelper.ProgressWriteLine("Found left-over temp folder: {0}", s.Substring(tempdir.Length));
diff --git a/Duplicati/UnitTest/UtilityTests.cs b/Duplicati/UnitTest/UtilityTests.cs
index 18ddbd7ee..208ee933f 100644
--- a/Duplicati/UnitTest/UtilityTests.cs
+++ b/Duplicati/UnitTest/UtilityTests.cs
@@ -57,5 +57,42 @@ namespace Duplicati.UnitTest
Assert.IsNotNull(actualUniqueItems);
Assert.IsNotNull(actualDuplicateItems);
}
+
+ [Test]
+ [Category("Utility")]
+ public void ParseBool()
+ {
+ string[] expectTrue = { "1", "on", "true", "yes" };
+ string[] expectFalse = { "0", "off", "false", "no" };
+ string[] expectDefault = { null, "", "maybe" };
+ Func returnsTrue = () => true;
+ Func returnsFalse = () => false;
+
+ foreach (string value in expectTrue)
+ {
+ string message = $"{value} should be parsed to true.";
+
+ Assert.IsTrue(Utility.ParseBool(value, false), message);
+ Assert.IsTrue(Utility.ParseBool(value.ToUpper(), false), message);
+ Assert.IsTrue(Utility.ParseBool($" {value} ", false), message);
+ }
+
+ foreach (string value in expectFalse)
+ {
+ string message = $"{value} should be parsed to false.";
+
+ Assert.IsFalse(Utility.ParseBool(value, true), message);
+ Assert.IsFalse(Utility.ParseBool(value.ToUpper(), true), message);
+ Assert.IsFalse(Utility.ParseBool($" {value} ", true), message);
+ }
+
+ foreach (string value in expectDefault)
+ {
+ Assert.IsTrue(Utility.ParseBool(value, true));
+ Assert.IsTrue(Utility.ParseBool(value, returnsTrue));
+ Assert.IsFalse(Utility.ParseBool(value, false));
+ Assert.IsFalse(Utility.ParseBool(value, returnsFalse));
+ }
+ }
}
}
diff --git a/Installer/debian/init-script-example.sh b/Installer/debian/init-script-example.sh
new file mode 100644
index 000000000..c60427965
--- /dev/null
+++ b/Installer/debian/init-script-example.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+### BEGIN INIT INFO
+# Provides: duplicati
+# Required-Start:
+# Required-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 1
+# Short-Description: Duplicati
+### END INIT INFO
+
+DAEMON=/usr/bin/duplicati-server
+DAEMON_PROCESS=DuplicatiServer
+NAME=duplicati
+PIDFILE=/var/run/$NAME.pid
+DESC="Duplicati backup service"
+DEFAULT=/etc/default/$NAME
+
+# Use LSB
+. /lib/lsb/init-functions
+
+# If we don't have the basics, don't bother
+test -x $DAEMON || exit 0
+test -f $DEFAULT && . $DEFAULT
+
+running_pid()
+{
+ # Check if a given process pid's cmdline matches a given name
+ pid=$1
+ name=$2
+ [ -z "$pid" ] && return 1
+ [ ! -d /proc/$pid ] && return 1
+ cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
+ # Is this the expected child?
+ [ "$cmd" != "$name" ] && return 1
+ return 0
+}
+
+running()
+{
+# Check if the process is running looking at /proc
+# (works for all users)
+ # No pidfile, probably no daemon present
+ [ ! -f "$PIDFILE" ] && return 1
+ # Obtain the pid and check it against the binary name
+ pid=`cat $PIDFILE`
+ running_pid $pid $DAEMON_PROCESS || return 1
+ return 0
+}
+
+case "$1" in
+ start)
+ log_daemon_msg "Starting $DESC" "$NAME"
+ if running; then
+ log_progress_msg "already running"
+ log_end_msg 0
+ exit 0
+ fi
+ rm -f $PIDFILE
+ start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS
+ log_end_msg $?
+ ;;
+ stop)
+ log_daemon_msg "Stopping $DESC" "$NAME"
+ start-stop-daemon --stop --pidfile $PIDFILE
+ log_end_msg $?
+ ;;
+ restart|force-reload)
+ $0 stop
+ sleep 2
+ $0 start
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
\ No newline at end of file
diff --git a/Localizations/duplicati/localization.pot b/Localizations/duplicati/localization.pot
index 33414d66d..546e433e7 100644
--- a/Localizations/duplicati/localization.pot
+++ b/Localizations/duplicati/localization.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-17 22:31+0200\n"
+"POT-Creation-Date: 2017-11-17 13:00+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -17,301 +17,690 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: CommandLine/Program.cs:301
-msgid "Toggle automatic updates"
+#: Server/Strings.cs:7
+msgid "Another instance is running, and was notified"
msgstr ""
-#: CommandLine/Program.cs:301
-msgid ""
-"Set this option if you prefer to have the commandline version automatically "
-"update"
-msgstr ""
-
-#: CommandLine/Strings.cs:4
-#, csharp-format
-msgid "The command {0} needs at least one of the following options set: {1}"
-msgstr ""
-
-#: CommandLine/Strings.cs:5
+#: Server/Strings.cs:8
#, csharp-format
msgid ""
-"Found {0} commands but expected {1}, commands: \n"
-"{2}"
+"Failed to create, open or upgrade the database.\n"
+"Error message: {0}"
msgstr ""
-#: CommandLine/Strings.cs:7
+#: Server/Strings.cs:10
+msgid "Displays this help"
+msgstr ""
+
+#: Server/Strings.cs:11
+msgid ""
+"Supported commandline arguments:\n"
+"\n"
+msgstr ""
+
+#: Server/Strings.cs:14
#, csharp-format
-msgid "Command not supported: {0}"
+msgid "--{0}: {1}"
msgstr ""
-#: CommandLine/Strings.cs:8
-msgid "No filesets matched the criteria"
+#: Server/Strings.cs:15
+msgid "Outputs log information to the file given"
msgstr ""
-#: CommandLine/Strings.cs:9
-msgid "The following filesets would be deleted:"
+#: Server/Strings.cs:16
+msgid "Determines the amount of information written in the log file"
msgstr ""
-#: CommandLine/Strings.cs:10
-msgid "These filesets were deleted:"
+#: Server/Strings.cs:17
+msgid ""
+"Activates portable mode where the database is placed below the program "
+"executable"
msgstr ""
-#: CommandLine/Strings.cs:11
-msgid "Supported backends:"
-msgstr ""
-
-#: CommandLine/Strings.cs:12
-msgid "Supported compression modules:"
-msgstr ""
-
-#: CommandLine/Strings.cs:13
-msgid "Supported encryption modules:"
-msgstr ""
-
-#: CommandLine/Strings.cs:14
-msgid "Supported options:"
-msgstr ""
-
-#: CommandLine/Strings.cs:15
-msgid "Module is loaded automatically, use --disable-module to prevent this"
-msgstr ""
-
-#: CommandLine/Strings.cs:16
-msgid "Module is not loaded automatically, use --enable-module to load it"
-msgstr ""
-
-#: CommandLine/Strings.cs:17
-msgid "Supported generic modules:"
-msgstr ""
-
-#: CommandLine/Strings.cs:18
+#: Server/Strings.cs:18
#, csharp-format
-msgid "Unable to read the parameters file \"{0}\", reason: {1}"
+msgid "A serious error occurred in Duplicati: {0}"
msgstr ""
-#: CommandLine/Strings.cs:19
+#: Server/Strings.cs:19
#, csharp-format
msgid ""
-"Filters cannot be specified on the commandline if filters are also present "
-"in the parameter file. Use the special --{0}, --{1}, or --{2} options to "
-"specify filters inside the parameter file. Each filter must be prefixed with "
-"either a + or a -, and multiple filters must be joined with {3}"
+"Unable to start up, perhaps another process is already running?\n"
+"Error message: {0}"
msgstr ""
-#: CommandLine/Strings.cs:20
+#: Server/Strings.cs:21
+msgid "Disables database encryption"
+msgstr ""
+
+#: Server/Strings.cs:22
+#, csharp-format
+msgid "Unsupported version of SQLite detected ({0}), must be {1} or higher"
+msgstr ""
+
+#: Server/Strings.cs:23
+msgid ""
+"The path to the folder where the static files for the webserver is present. "
+"The folder must be located beneath the installation folder"
+msgstr ""
+
+#: Server/Strings.cs:24
+msgid ""
+"The port the webserver listens on. Multiple values may be supplied with a "
+"comma in between."
+msgstr ""
+
+#: Server/Strings.cs:25
+msgid ""
+"The certificate and key file in PKCS #12 format the webserver use for SSL. "
+"Only RSA/DSA keys are supported."
+msgstr ""
+
+#: Server/Strings.cs:26
+msgid "The password for decryption of certificate PKCS #12 file."
+msgstr ""
+
+#: Server/Strings.cs:27
+msgid ""
+"The interface the webserver listens on. The special values \"*\" and \"any\" "
+"means any interface. The special value \"loopback\" means the loopback "
+"adapter."
+msgstr ""
+
+#: Server/Strings.cs:28
+msgid ""
+"The password required to access the webserver. This option is saved so you "
+"do not need to set it on each run. Setting an empty value disables the "
+"password."
+msgstr ""
+
+#: Server/Strings.cs:29
+msgid "Enables the ping-pong responder"
+msgstr ""
+
+#: Server/Strings.cs:30
+msgid ""
+"When running as a server, the service daemon must verify that the process is "
+"responding. If this option is enabled, the server reads stdin and writes a "
+"reply to each line read"
+msgstr ""
+
+#: Server/Strings.cs:31 Library/Main/Strings.cs:218
+msgid "Clean up old log data"
+msgstr ""
+
+#: Server/Strings.cs:32 Library/Main/Strings.cs:219
+msgid "Set the time after which log data will be purged from the database."
+msgstr ""
+
+#: Server/Strings.cs:33
+msgid "Sets the folder where settings are stored"
+msgstr ""
+
+#: Server/Strings.cs:34
#, csharp-format
msgid ""
-"The option --{0} was supplied, but it is reserved for internal use and may "
-"not be set on the commandline"
+"Duplicati needs to store a small database with all settings. Use this option "
+"to choose where the settings are stored. This option can also be set with "
+"the environment variable {0}."
msgstr ""
-#: CommandLine/Strings.cs:21
+#: Server/Strings.cs:35
+msgid "Sets the database encryption key"
+msgstr ""
+
+#: Server/Strings.cs:36
#, csharp-format
msgid ""
-"This option can be used to store some or all of the options given to the "
-"commandline client. The file must be a plain text file, UTF-8 encoding is "
-"preferred. Each line in the file should be of the format --option=value. The "
-"special options --{0} and --{1} can be used to override the localpath and "
-"the remote destination uri, respectively. The options in this file take "
-"precedence over the options provided on the commandline. You cannot specify "
-"filters in both the file and on the commandline. Instead, you can use the "
-"special --{2}, --{3}, or --{4} options to specify filters inside the "
-"parameter file. Each filter must be prefixed with either a + or a -, and "
-"multiple filters must be joined with {5} "
+"This option sets the encryption key used to scramble the local settings "
+"database. This option can also be set with the environment variable {0}. Use "
+"the option --{1} to disable the database scrambling."
msgstr ""
-#: CommandLine/Strings.cs:22
-msgid "Path to a file with parameters"
-msgstr ""
-
-#: CommandLine/Strings.cs:23
+#: Server/Strings.cs:39
#, csharp-format
-msgid "An error occured: {0}"
+msgid ""
+"Unable to find a valid date, given the start date {0}, the repetition "
+"interval {1} and the allowed days {2}"
msgstr ""
-#: CommandLine/Strings.cs:24
+#: Server/Strings.cs:44
#, csharp-format
-msgid "The inner error message is: {0}"
+msgid "Server has started and is listening on {0}, port {1}"
msgstr ""
-#: CommandLine/Strings.cs:25
+#: Server/Strings.cs:45
+#, csharp-format
msgid ""
-"Include files that match this filter. The special character * means any "
-"number of character, and the special character ? means any single character, "
-"use *.txt to include all files with a txt extension. Regular expressions are "
-"also supported and can be supplied by using hard braces, i.e. [.*\\.txt]."
+"Unable to create SSL certificate using provided parameters. Exception "
+"detail: {0}"
msgstr ""
-#: CommandLine/Strings.cs:26
-msgid "Include files"
+#: Server/Strings.cs:46
+#, csharp-format
+msgid "Unable to open a socket for listening, tried ports: {0}"
msgstr ""
-#: CommandLine/Strings.cs:27
+#: Library/Encryption/Strings.cs:4
msgid ""
-"Exclude files that match this filter. The special character * means any "
-"number of character, and the special character ? means any single character, "
-"use *.txt to exclude all files with a txt extension. Regular expressions are "
-"also supported and can be supplied by using hard braces, i.e. [.*\\.txt]."
+"This module encrypts all files in the same way that AESCrypt does, using 256 "
+"bit AES encryption."
msgstr ""
-#: CommandLine/Strings.cs:28
-msgid "Exclude files"
+#: Library/Encryption/Strings.cs:5
+msgid "AES-256 encryption, built in"
msgstr ""
-#: CommandLine/Strings.cs:29
+#: Library/Encryption/Strings.cs:6
+msgid "Empty passphrase not allowed"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:7
msgid ""
-"If this option is used with a backup operation, it is interpreted as a list "
-"of files to add to the filesets. When used with list or restore, it will "
-"list or restore the control files instead of the normal files."
+"Use this option to set the thread level allowed for AES crypt operations. "
+"Valid values are 0 (uses default), or from 1 (no multithreading) to 4 (max. "
+"multithreading)"
msgstr ""
-#: CommandLine/Strings.cs:30
-msgid "Use control files"
+#: Library/Encryption/Strings.cs:8
+msgid "Set thread level utilized for crypting (0-4)"
msgstr ""
-#: CommandLine/Strings.cs:31
+#: Library/Encryption/Strings.cs:11
+#, csharp-format
+msgid "Failed to decrypt data (invalid passphrase?): {0}"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:14
msgid ""
-"If this option is set, progress reports and other messages that would "
-"normally go to the console will be redirected to the log."
+"The GPG encryption module uses the GNU Privacy Guard program to encrypt and "
+"decrypt files. It requires that the gpg executable is available on the "
+"system. On Windows it is assumed that this is in the default installation "
+"folder under program files, under Linux and OSX it is assumed that the "
+"program is available via the PATH environment variable. It is possible to "
+"supply the path to GPG using the --gpg-program-path switch."
msgstr ""
-#: CommandLine/Strings.cs:32
-msgid "Disable console output"
+#: Library/Encryption/Strings.cs:15
+msgid "GNU Privacy Guard, external"
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:9
+#: Library/Encryption/Strings.cs:16
msgid ""
-"This backend can read and write data to an FTP based backend using an "
-"alternative FTP client. Allowed formats are \"aftp://hostname/folder\" or "
-"\"aftp://username:password@hostname/folder\""
+"Use this switch to specify any extra options to GPG. You cannot specify the "
+"--passphrase-fd option here. The --decrypt option is always specified."
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:10
-#: Library/Backend/AzureBlob/Strings.cs:13
-#: Library/Backend/Backblaze/Strings.cs:9
-#: Library/Backend/CloudFiles/Strings.cs:6 Library/Backend/File/Strings.cs:9
-#: Library/Backend/FTP/Strings.cs:12 Library/Backend/Jottacloud/Strings.cs:15
-#: Library/Backend/Mega/Strings.cs:5 Library/Backend/S3/Strings.cs:9
-#: Library/Backend/SharePoint/Strings.cs:8 Library/Backend/SSHv2/Strings.cs:23
-#: Library/Backend/WEBDAV/Strings.cs:7
+#: Library/Encryption/Strings.cs:17
+msgid "Extra GPG commandline options for decryption"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:18
+msgid ""
+"The GPG encryption/decryption will use the --armor option for GPG to protect "
+"the files with armor. Specify this switch to remove the --armor option."
+msgstr ""
+
+#: Library/Encryption/Strings.cs:19
+msgid "Don't use GPG Armor"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:20
+msgid ""
+"Use this switch to specify any extra options to GPG. You cannot specify the "
+"--passphrase-fd option here. The --encrypt option is always specified."
+msgstr ""
+
+#: Library/Encryption/Strings.cs:21
+msgid "Extra GPG commandline options for encryption"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:22
+#, csharp-format
+msgid "Failed to execute GPG at \"\"{0}\" {1}\": {2}"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:23
+msgid ""
+"The path to the GNU Privacy Guard program. If not supplied, Duplicati will "
+"assume that the program \"gpg\" is available in the system path."
+msgstr ""
+
+#: Library/Encryption/Strings.cs:24
+msgid "The path to GnuPG"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:25
+#, csharp-format
+msgid ""
+"This option has non-standard handling, please use the --{0} option instead."
+msgstr ""
+
+#: Library/Encryption/Strings.cs:26
+msgid ""
+"Use this option to supply the --armor option to GPG. The files will be "
+"larger but can be sent as pure text files."
+msgstr ""
+
+#: Library/Encryption/Strings.cs:27
+msgid "Use GPG Armor"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:28
+msgid "Overrides the GPG command supplied for decryption"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:29
+msgid "The GPG decryption command"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:30
+#, csharp-format
+msgid ""
+"Overrides the default GPG encryption command \"{0}\", normal usage is to "
+"request asymetric encryption with the setting {1}"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:31
+msgid "The GPG encryption command"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:34
+#, csharp-format
+msgid "Decryption failed: {0}"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:35
+msgid "Failure while invoking GnuPG, program won't flush output"
+msgstr ""
+
+#: Library/Encryption/Strings.cs:36
+msgid "Failure while invoking GnuPG, program won't terminate"
+msgstr ""
+
+#: Library/Interface/Strings.cs:4
+msgid "aliases"
+msgstr ""
+
+#: Library/Interface/Strings.cs:5
+msgid "default value"
+msgstr ""
+
+#: Library/Interface/Strings.cs:6
+msgid "[DEPRECATED]"
+msgstr ""
+
+#: Library/Interface/Strings.cs:7
+msgid "values"
+msgstr ""
+
+#: Library/Interface/Strings.cs:10
+msgid "Boolean"
+msgstr ""
+
+#: Library/Interface/Strings.cs:11
+msgid "Enumeration"
+msgstr ""
+
+#: Library/Interface/Strings.cs:12
+msgid "Flags"
+msgstr ""
+
+#: Library/Interface/Strings.cs:13
+msgid "Integer"
+msgstr ""
+
+#: Library/Interface/Strings.cs:14
+msgid "Path"
+msgstr ""
+
+#: Library/Interface/Strings.cs:15
+msgid "Size"
+msgstr ""
+
+#: Library/Interface/Strings.cs:16
+msgid "String"
+msgstr ""
+
+#: Library/Interface/Strings.cs:17
+msgid "Timespan"
+msgstr ""
+
+#: Library/Interface/Strings.cs:18
+msgid "Unknown"
+msgstr ""
+
+#: Library/Interface/Strings.cs:21
+#, csharp-format
+msgid ""
+"The configuration for the backend is not valid, it is missing the {0} field"
+msgstr ""
+
+#: Library/Interface/Strings.cs:22
+msgid "Do you want to test the connection?"
+msgstr ""
+
+#: Library/Interface/Strings.cs:23
+#, csharp-format
+msgid "Connection Failed: {0}"
+msgstr ""
+
+#: Library/Interface/Strings.cs:24
+msgid "Connection succeeded!"
+msgstr ""
+
+#: Library/Interface/Strings.cs:25
+msgid ""
+"You have not entered a path. This will store all backups in the default "
+"directory. Is this what you want?"
+msgstr ""
+
+#: Library/Interface/Strings.cs:26
+msgid "You must enter a password"
+msgstr ""
+
+#: Library/Interface/Strings.cs:27
+msgid ""
+"You have not entered a password.\n"
+"Proceed without a password?"
+msgstr ""
+
+#: Library/Interface/Strings.cs:29
+msgid "You must enter the name of the server"
+msgstr ""
+
+#: Library/Interface/Strings.cs:30
+msgid "You must enter a username"
+msgstr ""
+
+#: Library/Interface/Strings.cs:31
+msgid ""
+"You have not entered a username.\n"
+"This is fine if the server allows anonymous uploads, but likely a username "
+"is required\n"
+"Proceed without a username?"
+msgstr ""
+
+#: Library/Interface/Strings.cs:34
+msgid ""
+"The connection succeeded but another backup was found in the destination "
+"folder. It is possible to configure Duplicati to store multiple backups in "
+"the same folder, but it is not recommended.\n"
+"\n"
+"Do you want to use the selected folder?"
+msgstr ""
+
+#: Library/Interface/Strings.cs:37
+msgid "The folder cannot be created because it already exists"
+msgstr ""
+
+#: Library/Interface/Strings.cs:38
+msgid "Folder created!"
+msgstr ""
+
+#: Library/Interface/Strings.cs:39
+msgid "The requested folder does not exist"
+msgstr ""
+
+#: Library/Interface/Strings.cs:40
+#, csharp-format
+msgid "The server name \"{0}\" is not valid"
+msgstr ""
+
+#: Library/Interface/Strings.cs:41
+msgid "Cancelled"
+msgstr ""
+
+#: Library/Interface/CustomExceptions.cs:77
+#: Library/Interface/CustomExceptions.cs:85
+msgid "The requested file does not exist"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:4
+#, csharp-format
+msgid ""
+"The external command failed to start.\n"
+"Error message: {0}\n"
+"Command: {1} {2}"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:7
+#, csharp-format
+msgid ""
+"The external command failed to complete within the set time limit: {0} {1}"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:8
+#, csharp-format
+msgid "Unable to match local path {0} with any snapshot path: {1}"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:9
+#, csharp-format
+msgid ""
+"Script returned successfully, but the temporary folder {0} does not exist: "
+"{1}"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:10
+#, csharp-format
+msgid ""
+"Script returned successfully, but the temporary folder {0} still exist: {1}"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:11
+#, csharp-format
+msgid "The script returned exit code {0}, but {1} was expected: {2}"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:12
+#, csharp-format
+msgid ""
+"Script returned successfully, but the output was missing the {0} parameter: "
+"{1}"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:15
+msgid "Unexpected empty response while enumerating"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:16
+msgid "USN is not supported on Linux"
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:17
+msgid ""
+"The number of files returned by USN was zero. This is likely an error. To "
+"remedy this, USN has been disabled."
+msgstr ""
+
+#: Library/Snapshots/Strings.cs:20
+msgid "Calling process does not have the backup privilege"
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid ""
+"This backend can read and write data to Swift (OpenStack Object Storage). "
+"Supported format is \"openstack://container/folder\"."
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid "OpenStack Simple Storage"
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+#, csharp-format
+msgid "Missing required option: {0}"
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+#, csharp-format
msgid ""
"The password used to connect to the server. This may also be supplied as the "
-"environment variable \"AUTH_PASSWORD\"."
+"environment variable \"AUTH_PASSWORD\". If the password is supplied, --{0} "
+"must also be set"
msgstr ""
+#: Library/Backend/OpenStack/Strings.cs:20 Library/Backend/FTP/Strings.cs:13
+#: Library/Backend/CloudFiles/Strings.cs:7 Library/Backend/S3/Strings.cs:10
#: Library/Backend/AlternativeFTP/Strings.cs:11
-#: Library/Backend/AzureBlob/Strings.cs:14
+#: Library/Backend/SSHv2/Strings.cs:24 Library/Backend/File/Strings.cs:10
#: Library/Backend/Backblaze/Strings.cs:10
-#: Library/Backend/CloudFiles/Strings.cs:7 Library/Backend/File/Strings.cs:10
-#: Library/Backend/FTP/Strings.cs:13 Library/Backend/Jottacloud/Strings.cs:14
-#: Library/Backend/Mega/Strings.cs:6 Library/Backend/OpenStack/Strings.cs:20
-#: Library/Backend/S3/Strings.cs:10 Library/Backend/SharePoint/Strings.cs:9
-#: Library/Backend/SSHv2/Strings.cs:24 Library/Backend/WEBDAV/Strings.cs:8
+#: Library/Backend/AzureBlob/Strings.cs:14
+#: Library/Backend/Jottacloud/Strings.cs:14 Library/Backend/Mega/Strings.cs:6
+#: Library/Backend/SharePoint/Strings.cs:9 Library/Backend/WEBDAV/Strings.cs:8
msgid "Supplies the password used to connect to the server"
msgstr ""
+#: Library/Backend/OpenStack/Strings.cs:20 Library/Backend/FTP/Strings.cs:14
+#: Library/Backend/CloudFiles/Strings.cs:8 Library/Backend/S3/Strings.cs:11
#: Library/Backend/AlternativeFTP/Strings.cs:12
-#: Library/Backend/AzureBlob/Strings.cs:15
+#: Library/Backend/SSHv2/Strings.cs:25 Library/Backend/File/Strings.cs:11
#: Library/Backend/Backblaze/Strings.cs:11
-#: Library/Backend/CloudFiles/Strings.cs:8 Library/Backend/File/Strings.cs:11
-#: Library/Backend/FTP/Strings.cs:14 Library/Backend/Jottacloud/Strings.cs:13
-#: Library/Backend/Mega/Strings.cs:7 Library/Backend/OpenStack/Strings.cs:20
-#: Library/Backend/S3/Strings.cs:11 Library/Backend/SharePoint/Strings.cs:10
-#: Library/Backend/SSHv2/Strings.cs:25 Library/Backend/WEBDAV/Strings.cs:9
+#: Library/Backend/AzureBlob/Strings.cs:15
+#: Library/Backend/Jottacloud/Strings.cs:13 Library/Backend/Mega/Strings.cs:7
+#: Library/Backend/SharePoint/Strings.cs:10 Library/Backend/WEBDAV/Strings.cs:9
msgid ""
"The username used to connect to the server. This may also be supplied as the "
"environment variable \"AUTH_USERNAME\"."
msgstr ""
+#: Library/Backend/OpenStack/Strings.cs:20 Library/Backend/FTP/Strings.cs:15
+#: Library/Backend/CloudFiles/Strings.cs:9 Library/Backend/S3/Strings.cs:12
#: Library/Backend/AlternativeFTP/Strings.cs:13
-#: Library/Backend/AzureBlob/Strings.cs:16
+#: Library/Backend/SSHv2/Strings.cs:26 Library/Backend/File/Strings.cs:12
#: Library/Backend/Backblaze/Strings.cs:12
-#: Library/Backend/CloudFiles/Strings.cs:9 Library/Backend/File/Strings.cs:12
-#: Library/Backend/FTP/Strings.cs:15 Library/Backend/Jottacloud/Strings.cs:12
-#: Library/Backend/Mega/Strings.cs:8 Library/Backend/OpenStack/Strings.cs:20
-#: Library/Backend/S3/Strings.cs:12 Library/Backend/SharePoint/Strings.cs:11
-#: Library/Backend/SSHv2/Strings.cs:26 Library/Backend/WEBDAV/Strings.cs:10
+#: Library/Backend/AzureBlob/Strings.cs:16
+#: Library/Backend/Jottacloud/Strings.cs:12 Library/Backend/Mega/Strings.cs:8
+#: Library/Backend/SharePoint/Strings.cs:11
+#: Library/Backend/WEBDAV/Strings.cs:10
msgid "Supplies the username used to connect to the server"
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:14
-msgid "Alternative FTP"
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid ""
+"The Tenant Name is commonly the paying user account name. This option must "
+"be supplied when authenticating with a password, but is not required when "
+"using an API key."
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:15
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid "Supplies the Tenant Name used to connect to the server"
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid ""
+"The API key can be used to connect without supplying a password and tenant "
+"ID with some providers."
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid "Supplies the API key used to connect to the server"
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
#, csharp-format
-msgid "The folder {0} was not found. Message: {1}"
+msgid ""
+"The authentication URL is used to authenticate the user and find the storage "
+"service. The URL commonly ends with \"/v2.0\". Known providers are: {0}{1}"
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid "Supplies the authentication URL"
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid ""
+"This option is only used when creating a container, and is used to indicate "
+"where the container should be placed. Consult your provider for a list of "
+"valid regions, or leave empty for the default region."
+msgstr ""
+
+#: Library/Backend/OpenStack/Strings.cs:20
+msgid "Supplies the region used for creating a container"
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:7
+msgid ""
+"This backend can read and write data to an FTP based backend. Allowed "
+"formats are \"ftp://hostname/folder\" or \"ftp://username:password@hostname/"
+"folder\""
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:8
+msgid ""
+"If this flag is set, the FTP connection is made in active mode. Even if the "
+"\"ftp-passive\" flag is also set, the connection will be made in active mode"
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:9 Library/Backend/FTP/Strings.cs:11
+msgid "Toggles the FTP connections method"
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:10
+msgid ""
+"If this flag is set, the FTP connection is made in passive mode, which works "
+"better with some firewalls. If the \"ftp-regular\" flag is also set, this "
+"flag is ignored"
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:12 Library/Backend/CloudFiles/Strings.cs:6
+#: Library/Backend/S3/Strings.cs:9 Library/Backend/AlternativeFTP/Strings.cs:10
+#: Library/Backend/SSHv2/Strings.cs:23 Library/Backend/File/Strings.cs:9
+#: Library/Backend/Backblaze/Strings.cs:9
+#: Library/Backend/AzureBlob/Strings.cs:13
+#: Library/Backend/Jottacloud/Strings.cs:15 Library/Backend/Mega/Strings.cs:5
+#: Library/Backend/SharePoint/Strings.cs:8 Library/Backend/WEBDAV/Strings.cs:7
+msgid ""
+"The password used to connect to the server. This may also be supplied as the "
+"environment variable \"AUTH_PASSWORD\"."
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:16
+msgid ""
+"Use this flag to communicate using Secure Socket Layer (SSL) over ftp (ftps)."
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:17
+msgid "Instructs Duplicati to use an SSL (ftps) connection"
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:18
+msgid "FTP"
+msgstr ""
+
+#: Library/Backend/FTP/Strings.cs:19 Library/Backend/WEBDAV/Strings.cs:15
+#: Library/Backend/TahoeLAFS/Strings.cs:8
+#, csharp-format
+msgid "The folder {0} was not found, message: {1}"
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:16
#: Library/Backend/FTP/Strings.cs:20
+#: Library/Backend/AlternativeFTP/Strings.cs:16
#, csharp-format
msgid ""
"The file {0} was uploaded but not found afterwards, the file listing "
"returned {1}"
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:17
#: Library/Backend/FTP/Strings.cs:21
+#: Library/Backend/AlternativeFTP/Strings.cs:17
#, csharp-format
msgid ""
"The file {0} was uploaded but the returned size was {1} and it was expected "
"to be {2}"
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:18
#: Library/Backend/FTP/Strings.cs:22
+#: Library/Backend/AlternativeFTP/Strings.cs:18
msgid "Disable upload verification"
msgstr ""
-#: Library/Backend/AlternativeFTP/Strings.cs:19
+#: Library/Backend/FTP/Strings.cs:23
msgid ""
-"To protect against network or server failures, every upload will be "
-"attempted to be verified. Use this option to disable this verification to "
-"make the upload faster but less reliable."
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:20
-msgid ""
-"If this flag is set, the FTP data connection type will be changed to the "
-"selected option."
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:21
-msgid "Configure the FTP data connection type"
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:22
-msgid ""
-"If this flag is set, the FTP encryption mode will be changed to the selected "
-"option."
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:23
-msgid "Configure the FTP encryption mode"
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:24
-msgid "This flag controls the SSL policy to use when encryption is enabled."
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:25
-msgid "Configure the SSL policy to use when encryption is enabled"
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:26
-#, csharp-format
-msgid "Error on deleting file: {0}"
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:27
-#, csharp-format
-msgid "Error reading file: {0}"
-msgstr ""
-
-#: Library/Backend/AlternativeFTP/Strings.cs:28
-#, csharp-format
-msgid "Error writing file: {0}"
+"To protect against network failures, every upload will be attempted "
+"verified. Use this option to disable this verification to make the upload "
+"faster but less reliable."
msgstr ""
#: Library/Backend/AmazonCloudDrive/Strings.cs:23
@@ -321,21 +710,20 @@ msgid ""
msgstr ""
#: Library/Backend/AmazonCloudDrive/Strings.cs:24
-#: Library/Backend/Box/Strings.cs:24 Library/Backend/Dropbox/Strings.cs:24
#: Library/Backend/GoogleServices/Strings.cs:26
#: Library/Backend/GoogleServices/Strings.cs:43
-#: Library/Backend/HubiC/Strings.cs:24 Library/Backend/OAuthHelper/Strings.cs:9
-#: Library/Backend/OneDrive/Strings.cs:11
+#: Library/Backend/Box/Strings.cs:24 Library/Backend/OneDrive/Strings.cs:11
+#: Library/Backend/HubiC/Strings.cs:24 Library/Backend/Dropbox/Strings.cs:24
+#: Library/Backend/OAuthHelper/Strings.cs:9
msgid "The authorization code"
msgstr ""
#: Library/Backend/AmazonCloudDrive/Strings.cs:25
-#: Library/Backend/Box/Strings.cs:25 Library/Backend/Dropbox/Strings.cs:25
#: Library/Backend/GoogleServices/Strings.cs:27
#: Library/Backend/GoogleServices/Strings.cs:44
-#: Library/Backend/HubiC/Strings.cs:25
+#: Library/Backend/Box/Strings.cs:25 Library/Backend/OneDrive/Strings.cs:12
+#: Library/Backend/HubiC/Strings.cs:25 Library/Backend/Dropbox/Strings.cs:25
#: Library/Backend/OAuthHelper/Strings.cs:10
-#: Library/Backend/OneDrive/Strings.cs:12
#, csharp-format
msgid "The authorization token retrieved from {0}"
msgstr ""
@@ -345,10 +733,10 @@ msgid "Amazon Cloud Drive"
msgstr ""
#: Library/Backend/AmazonCloudDrive/Strings.cs:27
-#: Library/Backend/Box/Strings.cs:23
#: Library/Backend/GoogleServices/Strings.cs:24
#: Library/Backend/GoogleServices/Strings.cs:46
-#: Library/Backend/HubiC/Strings.cs:23 Library/Backend/OAuthHelper/Strings.cs:6
+#: Library/Backend/Box/Strings.cs:23 Library/Backend/HubiC/Strings.cs:23
+#: Library/Backend/OAuthHelper/Strings.cs:6
#, csharp-format
msgid "You need an AuthID, you can get it from: {0}"
msgstr ""
@@ -375,121 +763,73 @@ msgstr ""
msgid "Amazon Cloud drive needs a small delay for results to stay consistent."
msgstr ""
-#: Library/Backend/AzureBlob/Strings.cs:4
-msgid "All files will be written to the container specified"
-msgstr ""
-
-#: Library/Backend/AzureBlob/Strings.cs:5
-msgid "The name of the storage container "
-msgstr ""
-
-#: Library/Backend/AzureBlob/Strings.cs:6
-msgid "Azure blob"
-msgstr ""
-
-#: Library/Backend/AzureBlob/Strings.cs:7
-msgid "No Azure storage account name given"
-msgstr ""
-
-#: Library/Backend/AzureBlob/Strings.cs:8
+#: Library/Backend/GoogleServices/Strings.cs:22
msgid ""
-"The Azure storage account name which can be obtained by clicking the "
-"\"Manage Access Keys\" button on the storage account dashboard"
+"This backend can read and write data to Google Cloud Storage. Supported "
+"format is \"googlecloudstore://bucket/folder\"."
msgstr ""
-#: Library/Backend/AzureBlob/Strings.cs:9
-msgid "The storage account name"
+#: Library/Backend/GoogleServices/Strings.cs:23
+msgid "Google Cloud Storage"
msgstr ""
-#: Library/Backend/AzureBlob/Strings.cs:10
+#: Library/Backend/GoogleServices/Strings.cs:25
+#, csharp-format
+msgid "You must supply a project ID with --{0} for creating a bucket"
+msgstr ""
+
+#: Library/Backend/GoogleServices/Strings.cs:29
+#, csharp-format
msgid ""
-"The Azure access key which can be obtained by clicking the \"Manage Access "
-"Keys\" button on the storage account dashboard"
+"This option is only used when creating new buckets. Use this option to "
+"change what region the data is stored in. Charges vary with bucket location. "
+"Known bucket locations:\n"
+"{0}"
msgstr ""
-#: Library/Backend/AzureBlob/Strings.cs:11
-msgid "The access key"
+#: Library/Backend/GoogleServices/Strings.cs:31
+msgid "Specifies location option for creating a bucket"
msgstr ""
-#: Library/Backend/AzureBlob/Strings.cs:12
-msgid "No Azure access key given"
-msgstr ""
-
-#: Library/Backend/AzureBlob/Strings.cs:17
+#: Library/Backend/GoogleServices/Strings.cs:32
+#, csharp-format
msgid ""
-"This backend can read and write data to Azure blob storage. Allowed formats "
-"are: \"azure://bucketname\""
+"This option is only used when creating new buckets. Use this option to "
+"change what storage type the bucket has. Charges and functionality vary with "
+"bucket storage class. Known storage classes:\n"
+"{0}"
msgstr ""
-#: Library/Backend/Backblaze/Strings.cs:4
+#: Library/Backend/GoogleServices/Strings.cs:34
+msgid "Specifies storage class for creating a bucket"
+msgstr ""
+
+#: Library/Backend/GoogleServices/Strings.cs:35
+msgid "Specifies project for creating a bucket"
+msgstr ""
+
+#: Library/Backend/GoogleServices/Strings.cs:36
msgid ""
-"The \"B2 Cloud Storage Application Key\" can be obtained after logging into "
-"your Backblaze account, this can also be supplied through the \"auth-password"
-"\" property"
+"This option is only used when creating new buckets. Use this option to "
+"supply the project ID that the bucket is attached to. The project determines "
+"where usage charges are applied"
msgstr ""
-#: Library/Backend/Backblaze/Strings.cs:5
-msgid "The \"B2 Cloud Storage Application Key\""
-msgstr ""
-
-#: Library/Backend/Backblaze/Strings.cs:6
+#: Library/Backend/GoogleServices/Strings.cs:41
+#, csharp-format
msgid ""
-"The \"B2 Cloud Storage Account ID\" can be obtained after logging into your "
-"Backblaze account, this can also be supplied through the \"auth-username\" "
-"property"
+"The account access has been blocked by Google, please visit this URL and "
+"unlock it: {0}"
msgstr ""
-#: Library/Backend/Backblaze/Strings.cs:7
-msgid "The \"B2 Cloud Storage Account ID\""
-msgstr ""
-
-#: Library/Backend/Backblaze/Strings.cs:8
-msgid "B2 Cloud Storage"
-msgstr ""
-
-#: Library/Backend/Backblaze/Strings.cs:13
-msgid "No \"B2 Cloud Storage Application Key\" given"
-msgstr ""
-
-#: Library/Backend/Backblaze/Strings.cs:14
-msgid "No \"B2 Cloud Storage Account ID\" given"
-msgstr ""
-
-#: Library/Backend/Backblaze/Strings.cs:15
+#: Library/Backend/GoogleServices/Strings.cs:42
msgid ""
-"This backend can read and write data to the Backblaze B2 Cloud Storage. "
-"Allowed formats are: \"b2://bucketname/prefix\""
+"This backend can read and write data to Google Drive. Supported format is "
+"\"googledrive://folder/subfolder\"."
msgstr ""
-#: Library/Backend/Backblaze/Strings.cs:16
-msgid ""
-"By default, a private bucket is created. Use this option to set the bucket "
-"type. Refer to the B2 documentation for allowed types "
-msgstr ""
-
-#: Library/Backend/Backblaze/Strings.cs:17
-msgid "The bucket type used when creating a bucket"
-msgstr ""
-
-#: Library/Backend/Box/Strings.cs:21
-msgid ""
-"This backend can read and write data to Box.com. Supported format is \"box://"
-"folder/subfolder\"."
-msgstr ""
-
-#: Library/Backend/Box/Strings.cs:22
-msgid "Box.com"
-msgstr ""
-
-#: Library/Backend/Box/Strings.cs:26
-msgid "Force delete files"
-msgstr ""
-
-#: Library/Backend/Box/Strings.cs:27
-msgid ""
-"After deleting a file, it may end up in the trash folder where it will be "
-"deleted after a grace period. Use this command to force immediate removal of "
-"delete files."
+#: Library/Backend/GoogleServices/Strings.cs:45
+msgid "Google Drive"
msgstr ""
#: Library/Backend/CloudFiles/Strings.cs:4
@@ -567,406 +907,6 @@ msgstr ""
msgid "Unexpected CloudFiles response, perhaps the API has changed?"
msgstr ""
-#: Library/Backend/Dropbox/Strings.cs:22
-msgid ""
-"This backend can read and write data to Dropbox. Supported format is "
-"\"dropbox://folder/subfolder\"."
-msgstr ""
-
-#: Library/Backend/Dropbox/Strings.cs:23
-msgid "Dropbox"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:4
-#, csharp-format
-msgid ""
-"This option only works when the --{0} option is also specified. If there are "
-"alternate paths specified, this option indicates the name of a marker file "
-"that must be present in the folder. This can be used to handle situations "
-"where an external drive changes drive letter or mount point. By ensuring "
-"that a certain file exists, it is possible to prevent writing data to an "
-"unwanted external drive. The contents of the file are never examined, only "
-"file existence."
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:5
-msgid "Look for a file in the destination folder"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:6
-#, csharp-format
-msgid ""
-"This option allows multiple targets to be specified. The primary target path "
-"is placed before the list of paths supplied with this option. Before "
-"starting the backup, each folder in the list is checked for existence and "
-"optionally the presence of the marker file supplied by --{0}. The first "
-"existing path that optionally contains the marker file is then used as the "
-"destination. Multiple destinations are separated with a \"{1}\". On Windows, "
-"the path may be a UNC path, and the drive letter may be substituted with an "
-"asterisk (*), eg.: \"*:\\backup\", which will examine all drive letters. If "
-"a username and password is supplied, the same credentials are used for all "
-"destinations."
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:7
-msgid "A list of secondary target paths"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:8
-msgid ""
-"This backend can read and write data to an file based backend. Allowed "
-"formats are \"file://hostname/folder\" or \"file://username:"
-"password@hostname/folder\". You may supply UNC paths (eg: \"file://\\\\server"
-"\\folder\") or local paths (eg: (win) \"file://c:\\folder\", (linux) "
-"\"file:///usr/pub/files\")"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:13
-msgid "Local folder or drive"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:14
-#, csharp-format
-msgid "The folder {0} does not exist"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:15
-#, csharp-format
-msgid ""
-"The marker file \"{0}\" was not found in any of the examined destinations: "
-"{1}"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:16
-msgid ""
-"When storing the file, the standard operation is to copy the file and delete "
-"the original. This sequence ensures that the operation can be retried if "
-"something goes wrong. Activating this option may cause the retry operation "
-"to fail. This option has no effect unless the --disable-streaming-transfers "
-"options is activated."
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:17
-msgid "Move the file instead of copying it"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:18
-msgid "Force authentication against remote share"
-msgstr ""
-
-#: Library/Backend/File/Strings.cs:19
-msgid ""
-"If this option is set, any existing authentication against the remote share "
-"is dropped before attempting to authenticate"
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:7
-msgid ""
-"This backend can read and write data to an FTP based backend. Allowed "
-"formats are \"ftp://hostname/folder\" or \"ftp://username:password@hostname/"
-"folder\""
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:8
-msgid ""
-"If this flag is set, the FTP connection is made in active mode. Even if the "
-"\"ftp-passive\" flag is also set, the connection will be made in active mode"
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:9 Library/Backend/FTP/Strings.cs:11
-msgid "Toggles the FTP connections method"
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:10
-msgid ""
-"If this flag is set, the FTP connection is made in passive mode, which works "
-"better with some firewalls. If the \"ftp-regular\" flag is also set, this "
-"flag is ignored"
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:16
-msgid ""
-"Use this flag to communicate using Secure Socket Layer (SSL) over ftp (ftps)."
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:17
-msgid "Instructs Duplicati to use an SSL (ftps) connection"
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:18
-msgid "FTP"
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:19 Library/Backend/TahoeLAFS/Strings.cs:8
-#: Library/Backend/WEBDAV/Strings.cs:15
-#, csharp-format
-msgid "The folder {0} was not found, message: {1}"
-msgstr ""
-
-#: Library/Backend/FTP/Strings.cs:23
-msgid ""
-"To protect against network failures, every upload will be attempted "
-"verified. Use this option to disable this verification to make the upload "
-"faster but less reliable."
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:22
-msgid ""
-"This backend can read and write data to Google Cloud Storage. Supported "
-"format is \"googlecloudstore://bucket/folder\"."
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:23
-msgid "Google Cloud Storage"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:25
-#, csharp-format
-msgid "You must supply a project ID with --{0} for creating a bucket"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:29
-#, csharp-format
-msgid ""
-"This option is only used when creating new buckets. Use this option to "
-"change what region the data is stored in. Charges vary with bucket location. "
-"Known bucket locations:\n"
-"{0}"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:31
-msgid "Specifies location option for creating a bucket"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:32
-#, csharp-format
-msgid ""
-"This option is only used when creating new buckets. Use this option to "
-"change what storage type the bucket has. Charges and functionality vary with "
-"bucket storage class. Known storage classes:\n"
-"{0}"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:34
-msgid "Specifies storage class for creating a bucket"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:35
-msgid "Specifies project for creating a bucket"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:36
-msgid ""
-"This option is only used when creating new buckets. Use this option to "
-"supply the project ID that the bucket is attached to. The project determines "
-"where usage charges are applied"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:41
-#, csharp-format
-msgid ""
-"The account access has been blocked by Google, please visit this URL and "
-"unlock it: {0}"
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:42
-msgid ""
-"This backend can read and write data to Google Drive. Supported format is "
-"\"googledrive://folder/subfolder\"."
-msgstr ""
-
-#: Library/Backend/GoogleServices/Strings.cs:45
-msgid "Google Drive"
-msgstr ""
-
-#: Library/Backend/HubiC/Strings.cs:21
-msgid ""
-"This backend can read and write data to HubiC. Supported format is \"hubic://"
-"container/folder\"."
-msgstr ""
-
-#: Library/Backend/HubiC/Strings.cs:22
-msgid "HubiC"
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:5
-msgid "Jottacloud"
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:6
-msgid ""
-"This backend can read and write data to Jottacloud using it's REST protocol. "
-"Allowed format is \"jottacloud://folder/subfolder\"."
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:7 Library/Backend/Mega/Strings.cs:10
-msgid "No username given"
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:8 Library/Backend/Mega/Strings.cs:9
-msgid "No password given"
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:9 Library/Backend/Mega/Strings.cs:11
-msgid "No path given, cannot upload files to the root folder"
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:10
-msgid "Illegal mount point given."
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:16
-msgid "Supplies the backup device to use"
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:17
-#, csharp-format
-msgid ""
-"The backup device to use. Will be created if not already exists. You can "
-"manage your devices from the backup panel in the Jottacloud web interface. "
-"When you specify a custom device you should also specify the mount point to "
-"use on this device with the \"{0}\" option."
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:18
-msgid "Supplies the mount point to use on the server"
-msgstr ""
-
-#: Library/Backend/Jottacloud/Strings.cs:19
-#, csharp-format
-msgid ""
-"The mount point to use on the server. The default is \"Archive\" for using "
-"the built-in archive mount point. Set this option to \"Sync\" to use the "
-"built-in synchronization mount point instead, or if you have specified a "
-"custom device with option \"{0}\" you are free to name the mount point as "
-"you like."
-msgstr ""
-
-#: Library/Backend/Mega/Strings.cs:4
-msgid "mega.nz"
-msgstr ""
-
-#: Library/Backend/Mega/Strings.cs:12
-msgid ""
-"This backend can read and write data to Mega.co.nz. Allowed formats are: "
-"\"mega://folder/subfolder\""
-msgstr ""
-
-#: Library/Backend/OAuthHelper/Strings.cs:7
-#, csharp-format
-msgid ""
-"Failed to authorize using the OAuth service: {0}. If the problem persists, "
-"try generating a new authid token from: {1}"
-msgstr ""
-
-#: Library/Backend/OAuthHelper/Strings.cs:8
-#: Library/Backend/OneDrive/Strings.cs:7
-#, csharp-format
-msgid "Unexpected error code: {0} - {1}"
-msgstr ""
-
-#: Library/Backend/OAuthHelper/Strings.cs:11
-msgid "The OAuth service is currently over quota, try again in a few hours"
-msgstr ""
-
-#: Library/Backend/OneDrive/Strings.cs:5
-#, csharp-format
-msgid ""
-"Failed to authorize using the WLID service: {0}. If the problem persists, "
-"try generating a new authid token from: {1}"
-msgstr ""
-
-#: Library/Backend/OneDrive/Strings.cs:6
-msgid "Autocreated folder"
-msgstr ""
-
-#: Library/Backend/OneDrive/Strings.cs:8
-#, csharp-format
-msgid "Missing the folder: {0}"
-msgstr ""
-
-#: Library/Backend/OneDrive/Strings.cs:9 Library/Compression/Strings.cs:13
-#, csharp-format
-msgid "File not found: {0}"
-msgstr ""
-
-#: Library/Backend/OneDrive/Strings.cs:10
-msgid "Microsoft OneDrive"
-msgstr ""
-
-#: Library/Backend/OneDrive/Strings.cs:13
-#, csharp-format
-msgid ""
-"Stores files on Microsoft OneDrive. Usage of this backend requires that you "
-"agree to the terms in {0} ({1}) and {2} ({3})"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid ""
-"This backend can read and write data to Swift (OpenStack Object Storage). "
-"Supported format is \"openstack://container/folder\"."
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid "OpenStack Simple Storage"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-#, csharp-format
-msgid "Missing required option: {0}"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-#, csharp-format
-msgid ""
-"The password used to connect to the server. This may also be supplied as the "
-"environment variable \"AUTH_PASSWORD\". If the password is supplied, --{0} "
-"must also be set"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid ""
-"The Tenant Name is commonly the paying user account name. This option must "
-"be supplied when authenticating with a password, but is not required when "
-"using an API key."
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid "Supplies the Tenant Name used to connect to the server"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid ""
-"The API key can be used to connect without supplying a password and tenant "
-"ID with some providers."
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid "Supplies the API key used to connect to the server"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-#, csharp-format
-msgid ""
-"The authentication URL is used to authenticate the user and find the storage "
-"service. The URL commonly ends with \"/v2.0\". Known providers are: {0}{1}"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid "Supplies the authentication URL"
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid ""
-"This option is only used when creating a container, and is used to indicate "
-"where the container should be placed. Consult your provider for a list of "
-"valid regions, or leave empty for the default region."
-msgstr ""
-
-#: Library/Backend/OpenStack/Strings.cs:20
-msgid "Supplies the region used for creating a container"
-msgstr ""
-
#: Library/Backend/S3/Strings.cs:4
msgid ""
"The AWS \"Secret Access Key\" can be obtained after logging into your AWS "
@@ -1097,8 +1037,8 @@ msgid ""
"connections."
msgstr ""
-#: Library/Backend/S3/Strings.cs:34 Library/Backend/TahoeLAFS/Strings.cs:6
-#: Library/Backend/WEBDAV/Strings.cs:20
+#: Library/Backend/S3/Strings.cs:34 Library/Backend/WEBDAV/Strings.cs:20
+#: Library/Backend/TahoeLAFS/Strings.cs:6
msgid "Instructs Duplicati to use an SSL (https) connection"
msgstr ""
@@ -1112,139 +1052,70 @@ msgstr ""
msgid "Specify storage class"
msgstr ""
-#: Library/Backend/SharePoint/Strings.cs:6
-msgid "Microsoft SharePoint"
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:7
+#: Library/Backend/AlternativeFTP/Strings.cs:9
msgid ""
-"Supports connections to a SharePoint server (including OneDrive for "
-"Business). Allowed formats are \"mssp://tennant.sharepoint.com/PathToWeb//"
-"BaseDocLibrary/subfolder\" or \"mssp://username:password@tennant.sharepoint."
-"com/PathToWeb//BaseDocLibrary/subfolder\". Use a double slash '//' in the "
-"path to denote the web from the documents library."
+"This backend can read and write data to an FTP based backend using an "
+"alternative FTP client. Allowed formats are \"aftp://hostname/folder\" or "
+"\"aftp://username:password@hostname/folder\""
msgstr ""
-#: Library/Backend/SharePoint/Strings.cs:12
-#: Library/Backend/WEBDAV/Strings.cs:11
-msgid ""
-"If the server and client both supports integrated authentication, this "
-"option enables that authentication method. This is likely only available "
-"with windows servers and clients."
+#: Library/Backend/AlternativeFTP/Strings.cs:14
+msgid "Alternative FTP"
msgstr ""
-#: Library/Backend/SharePoint/Strings.cs:13
-#: Library/Backend/WEBDAV/Strings.cs:12
-msgid "Use windows integrated authentication to connect to the server"
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:14
-msgid ""
-"Use this option to have files moved to the recycle bin folder instead of "
-"removing them permanently when compacting or deleting backups."
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:15
-msgid "Move deleted files to the recycle bin"
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:17
-msgid ""
-"Use this option to upload files to SharePoint as a whole with BinaryDirect "
-"mode. This is the most efficient way of uploading, but can cause non-"
-"recoverable timeouts under certain conditions. Use this option only with "
-"very fast and stable internet connections."
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:18
-msgid "Upload files using binary direct mode."
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:20
-msgid ""
-"Use this option to specify a custom value for timeouts of web operation when "
-"communicating with SharePoint Server. Recommended value is 180s."
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:21
-msgid "Set timeout for SharePoint web operations."
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:23
-msgid ""
-"Use this option to specify the size of each chunk when uploading to "
-"SharePoint Server. Recommended value is 4MB."
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:24
-msgid "Set block size for chunked uploads to SharePoint."
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:26
+#: Library/Backend/AlternativeFTP/Strings.cs:15
#, csharp-format
-msgid "Element with path '{0}' not found on host '{1}'."
+msgid "The folder {0} was not found. Message: {1}"
msgstr ""
-#: Library/Backend/SharePoint/Strings.cs:27
+#: Library/Backend/AlternativeFTP/Strings.cs:19
+msgid ""
+"To protect against network or server failures, every upload will be "
+"attempted to be verified. Use this option to disable this verification to "
+"make the upload faster but less reliable."
+msgstr ""
+
+#: Library/Backend/AlternativeFTP/Strings.cs:20
+msgid ""
+"If this flag is set, the FTP data connection type will be changed to the "
+"selected option."
+msgstr ""
+
+#: Library/Backend/AlternativeFTP/Strings.cs:21
+msgid "Configure the FTP data connection type"
+msgstr ""
+
+#: Library/Backend/AlternativeFTP/Strings.cs:22
+msgid ""
+"If this flag is set, the FTP encryption mode will be changed to the selected "
+"option."
+msgstr ""
+
+#: Library/Backend/AlternativeFTP/Strings.cs:23
+msgid "Configure the FTP encryption mode"
+msgstr ""
+
+#: Library/Backend/AlternativeFTP/Strings.cs:24
+msgid "This flag controls the SSL policy to use when encryption is enabled."
+msgstr ""
+
+#: Library/Backend/AlternativeFTP/Strings.cs:25
+msgid "Configure the SSL policy to use when encryption is enabled"
+msgstr ""
+
+#: Library/Backend/AlternativeFTP/Strings.cs:26
#, csharp-format
-msgid ""
-"No SharePoint web could be logged in to at path '{0}'. Maybe wrong "
-"credentials. Or try using '//' in path to separate web from folder path."
+msgid "Error on deleting file: {0}"
msgstr ""
-#: Library/Backend/SharePoint/Strings.cs:28
-msgid ""
-"Everything seemed alright, but then web title could not be read to test "
-"connection. Something's wrong."
+#: Library/Backend/AlternativeFTP/Strings.cs:27
+#, csharp-format
+msgid "Error reading file: {0}"
msgstr ""
-#: Library/Backend/SharePoint/Strings.cs:33
-msgid "Microsoft OneDrive for Business"
-msgstr ""
-
-#: Library/Backend/SharePoint/Strings.cs:34
-msgid ""
-"Supports connections to Microsoft OneDrive for Business. Allowed formats are "
-"\"od4b://tennant.sharepoint.com/personal/username_domain/Documents/subfolder"
-"\" or \"od4b://username:password@tennant.sharepoint.com/personal/"
-"username_domain/Documents/folder\". You can use a double slash '//' in the "
-"path to denote the base path from the documents folder."
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:6
-msgid "Sia Decentralized Cloud"
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:7
-msgid "This backend can read and write data to Sia."
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:8
-msgid "Sia address"
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:9
-msgid "Sia address, ie 127.0.0.1:9980"
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:10
-msgid "Backup path"
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:11
-msgid "Target path, ie /backup"
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:12 Library/Backend/Sia/Strings.cs:13
-msgid "Sia password"
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:14
-msgid "3"
-msgstr ""
-
-#: Library/Backend/Sia/Strings.cs:15
-msgid "Minimum value is 3."
+#: Library/Backend/AlternativeFTP/Strings.cs:28
+#, csharp-format
+msgid "Error writing file: {0}"
msgstr ""
#: Library/Backend/SSHv2/Strings.cs:4
@@ -1400,24 +1271,478 @@ msgid ""
"(NOT SECURE) for testing!"
msgstr ""
-#: Library/Backend/TahoeLAFS/Strings.cs:4
+#: Library/Backend/Box/Strings.cs:21
msgid ""
-"This backend can read and write data to a Tahoe-LAFS based backend. Allowed "
-"format is \"tahoe://hostname:port/uri/$DIRCAP\"."
+"This backend can read and write data to Box.com. Supported format is \"box://"
+"folder/subfolder\"."
msgstr ""
-#: Library/Backend/TahoeLAFS/Strings.cs:5 Library/Backend/WEBDAV/Strings.cs:19
+#: Library/Backend/Box/Strings.cs:22
+msgid "Box.com"
+msgstr ""
+
+#: Library/Backend/Box/Strings.cs:26
+msgid "Force delete files"
+msgstr ""
+
+#: Library/Backend/Box/Strings.cs:27
msgid ""
-"Use this flag to communicate using Secure Socket Layer (SSL) over http "
-"(https)."
+"After deleting a file, it may end up in the trash folder where it will be "
+"deleted after a grace period. Use this command to force immediate removal of "
+"delete files."
msgstr ""
-#: Library/Backend/TahoeLAFS/Strings.cs:7
-msgid "Tahoe-LAFS"
+#: Library/Backend/File/Strings.cs:4
+#, csharp-format
+msgid ""
+"This option only works when the --{0} option is also specified. If there are "
+"alternate paths specified, this option indicates the name of a marker file "
+"that must be present in the folder. This can be used to handle situations "
+"where an external drive changes drive letter or mount point. By ensuring "
+"that a certain file exists, it is possible to prevent writing data to an "
+"unwanted external drive. The contents of the file are never examined, only "
+"file existence."
msgstr ""
-#: Library/Backend/TahoeLAFS/Strings.cs:9
-msgid "Unsupported URL format, must start with \"uri/URI:DIR2:\""
+#: Library/Backend/File/Strings.cs:5
+msgid "Look for a file in the destination folder"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:6
+#, csharp-format
+msgid ""
+"This option allows multiple targets to be specified. The primary target path "
+"is placed before the list of paths supplied with this option. Before "
+"starting the backup, each folder in the list is checked for existence and "
+"optionally the presence of the marker file supplied by --{0}. The first "
+"existing path that optionally contains the marker file is then used as the "
+"destination. Multiple destinations are separated with a \"{1}\". On Windows, "
+"the path may be a UNC path, and the drive letter may be substituted with an "
+"asterisk (*), eg.: \"*:\\backup\", which will examine all drive letters. If "
+"a username and password is supplied, the same credentials are used for all "
+"destinations."
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:7
+msgid "A list of secondary target paths"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:8
+msgid ""
+"This backend can read and write data to an file based backend. Allowed "
+"formats are \"file://hostname/folder\" or \"file://username:"
+"password@hostname/folder\". You may supply UNC paths (eg: \"file://\\\\server"
+"\\folder\") or local paths (eg: (win) \"file://c:\\folder\", (linux) "
+"\"file:///usr/pub/files\")"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:13
+msgid "Local folder or drive"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:14
+#, csharp-format
+msgid "The folder {0} does not exist"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:15
+#, csharp-format
+msgid ""
+"The marker file \"{0}\" was not found in any of the examined destinations: "
+"{1}"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:16
+msgid ""
+"When storing the file, the standard operation is to copy the file and delete "
+"the original. This sequence ensures that the operation can be retried if "
+"something goes wrong. Activating this option may cause the retry operation "
+"to fail. This option has no effect unless the --disable-streaming-transfers "
+"options is activated."
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:17
+msgid "Move the file instead of copying it"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:18
+msgid "Force authentication against remote share"
+msgstr ""
+
+#: Library/Backend/File/Strings.cs:19
+msgid ""
+"If this option is set, any existing authentication against the remote share "
+"is dropped before attempting to authenticate"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:4
+msgid ""
+"The \"B2 Cloud Storage Application Key\" can be obtained after logging into "
+"your Backblaze account, this can also be supplied through the \"auth-password"
+"\" property"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:5
+msgid "The \"B2 Cloud Storage Application Key\""
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:6
+msgid ""
+"The \"B2 Cloud Storage Account ID\" can be obtained after logging into your "
+"Backblaze account, this can also be supplied through the \"auth-username\" "
+"property"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:7
+msgid "The \"B2 Cloud Storage Account ID\""
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:8
+msgid "B2 Cloud Storage"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:13
+msgid "No \"B2 Cloud Storage Application Key\" given"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:14
+msgid "No \"B2 Cloud Storage Account ID\" given"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:15
+msgid ""
+"This backend can read and write data to the Backblaze B2 Cloud Storage. "
+"Allowed formats are: \"b2://bucketname/prefix\""
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:16
+msgid ""
+"By default, a private bucket is created. Use this option to set the bucket "
+"type. Refer to the B2 documentation for allowed types "
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:17
+msgid "The bucket type used when creating a bucket"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:18
+msgid ""
+"Use this option to set the page size for listing contents of B2 buckets. A "
+"lower number means less data, but can increase the number of Class C "
+"transaction on B2. Suggested values are between 100 and 1000"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:19
+msgid "The size of file-listing pages"
+msgstr ""
+
+#: Library/Backend/Backblaze/Strings.cs:20
+#, csharp-format
+msgid ""
+"The setting \"{0}\" is invalid for \"{1}\", it must be an integer larger "
+"than zero"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:6
+msgid "Sia Decentralized Cloud"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:7
+msgid "This backend can read and write data to Sia."
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:8
+msgid "Sia address"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:9
+msgid "Sia address, ie 127.0.0.1:9980"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:10
+msgid "Backup path"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:11
+msgid "Target path, ie /backup"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:12 Library/Backend/Sia/Strings.cs:13
+msgid "Sia password"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:14
+msgid "3"
+msgstr ""
+
+#: Library/Backend/Sia/Strings.cs:15
+msgid "Minimum value is 3."
+msgstr ""
+
+#: Library/Backend/OneDrive/Strings.cs:5
+#, csharp-format
+msgid ""
+"Failed to authorize using the WLID service: {0}. If the problem persists, "
+"try generating a new authid token from: {1}"
+msgstr ""
+
+#: Library/Backend/OneDrive/Strings.cs:6
+msgid "Autocreated folder"
+msgstr ""
+
+#: Library/Backend/OneDrive/Strings.cs:7
+#: Library/Backend/OAuthHelper/Strings.cs:8
+#, csharp-format
+msgid "Unexpected error code: {0} - {1}"
+msgstr ""
+
+#: Library/Backend/OneDrive/Strings.cs:8
+#, csharp-format
+msgid "Missing the folder: {0}"
+msgstr ""
+
+#: Library/Backend/OneDrive/Strings.cs:9 Library/Compression/Strings.cs:13
+#, csharp-format
+msgid "File not found: {0}"
+msgstr ""
+
+#: Library/Backend/OneDrive/Strings.cs:10
+msgid "Microsoft OneDrive"
+msgstr ""
+
+#: Library/Backend/OneDrive/Strings.cs:13
+#, csharp-format
+msgid ""
+"Stores files on Microsoft OneDrive. Usage of this backend requires that you "
+"agree to the terms in {0} ({1}) and {2} ({3})"
+msgstr ""
+
+#: Library/Backend/HubiC/Strings.cs:21
+msgid ""
+"This backend can read and write data to HubiC. Supported format is \"hubic://"
+"container/folder\"."
+msgstr ""
+
+#: Library/Backend/HubiC/Strings.cs:22
+msgid "HubiC"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:4
+msgid "All files will be written to the container specified"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:5
+msgid "The name of the storage container "
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:6
+msgid "Azure blob"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:7
+msgid "No Azure storage account name given"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:8
+msgid ""
+"The Azure storage account name which can be obtained by clicking the "
+"\"Manage Access Keys\" button on the storage account dashboard"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:9
+msgid "The storage account name"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:10
+msgid ""
+"The Azure access key which can be obtained by clicking the \"Manage Access "
+"Keys\" button on the storage account dashboard"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:11
+msgid "The access key"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:12
+msgid "No Azure access key given"
+msgstr ""
+
+#: Library/Backend/AzureBlob/Strings.cs:17
+msgid ""
+"This backend can read and write data to Azure blob storage. Allowed formats "
+"are: \"azure://bucketname\""
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:5
+msgid "Jottacloud"
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:6
+msgid ""
+"This backend can read and write data to Jottacloud using it's REST protocol. "
+"Allowed format is \"jottacloud://folder/subfolder\"."
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:7 Library/Backend/Mega/Strings.cs:10
+msgid "No username given"
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:8 Library/Backend/Mega/Strings.cs:9
+msgid "No password given"
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:9 Library/Backend/Mega/Strings.cs:11
+msgid "No path given, cannot upload files to the root folder"
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:10
+msgid "Illegal mount point given."
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:16
+msgid "Supplies the backup device to use"
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:17
+#, csharp-format
+msgid ""
+"The backup device to use. Will be created if not already exists. You can "
+"manage your devices from the backup panel in the Jottacloud web interface. "
+"When you specify a custom device you should also specify the mount point to "
+"use on this device with the \"{0}\" option."
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:18
+msgid "Supplies the mount point to use on the server"
+msgstr ""
+
+#: Library/Backend/Jottacloud/Strings.cs:19
+#, csharp-format
+msgid ""
+"The mount point to use on the server. The default is \"Archive\" for using "
+"the built-in archive mount point. Set this option to \"Sync\" to use the "
+"built-in synchronization mount point instead, or if you have specified a "
+"custom device with option \"{0}\" you are free to name the mount point as "
+"you like."
+msgstr ""
+
+#: Library/Backend/Mega/Strings.cs:4
+msgid "mega.nz"
+msgstr ""
+
+#: Library/Backend/Mega/Strings.cs:12
+msgid ""
+"This backend can read and write data to Mega.co.nz. Allowed formats are: "
+"\"mega://folder/subfolder\""
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:6
+msgid "Microsoft SharePoint"
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:7
+msgid ""
+"Supports connections to a SharePoint server (including OneDrive for "
+"Business). Allowed formats are \"mssp://tennant.sharepoint.com/PathToWeb//"
+"BaseDocLibrary/subfolder\" or \"mssp://username:password@tennant.sharepoint."
+"com/PathToWeb//BaseDocLibrary/subfolder\". Use a double slash '//' in the "
+"path to denote the web from the documents library."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:12
+#: Library/Backend/WEBDAV/Strings.cs:11
+msgid ""
+"If the server and client both supports integrated authentication, this "
+"option enables that authentication method. This is likely only available "
+"with windows servers and clients."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:13
+#: Library/Backend/WEBDAV/Strings.cs:12
+msgid "Use windows integrated authentication to connect to the server"
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:14
+msgid ""
+"Use this option to have files moved to the recycle bin folder instead of "
+"removing them permanently when compacting or deleting backups."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:15
+msgid "Move deleted files to the recycle bin"
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:17
+msgid ""
+"Use this option to upload files to SharePoint as a whole with BinaryDirect "
+"mode. This is the most efficient way of uploading, but can cause non-"
+"recoverable timeouts under certain conditions. Use this option only with "
+"very fast and stable internet connections."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:18
+msgid "Upload files using binary direct mode."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:20
+msgid ""
+"Use this option to specify a custom value for timeouts of web operation when "
+"communicating with SharePoint Server. Recommended value is 180s."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:21
+msgid "Set timeout for SharePoint web operations."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:23
+msgid ""
+"Use this option to specify the size of each chunk when uploading to "
+"SharePoint Server. Recommended value is 4MB."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:24
+msgid "Set block size for chunked uploads to SharePoint."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:26
+#, csharp-format
+msgid "Element with path '{0}' not found on host '{1}'."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:27
+#, csharp-format
+msgid ""
+"No SharePoint web could be logged in to at path '{0}'. Maybe wrong "
+"credentials. Or try using '//' in path to separate web from folder path."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:28
+msgid ""
+"Everything seemed alright, but then web title could not be read to test "
+"connection. Something's wrong."
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:33
+msgid "Microsoft OneDrive for Business"
+msgstr ""
+
+#: Library/Backend/SharePoint/Strings.cs:34
+msgid ""
+"Supports connections to Microsoft OneDrive for Business. Allowed formats are "
+"\"od4b://tennant.sharepoint.com/personal/username_domain/Documents/subfolder"
+"\" or \"od4b://username:password@tennant.sharepoint.com/personal/"
+"username_domain/Documents/folder\". You can use a double slash '//' in the "
+"path to denote the base path from the documents folder."
+msgstr ""
+
+#: Library/Backend/Dropbox/Strings.cs:22
+msgid ""
+"This backend can read and write data to Dropbox. Supported format is "
+"\"dropbox://folder/subfolder\"."
+msgstr ""
+
+#: Library/Backend/Dropbox/Strings.cs:23
+msgid "Dropbox"
msgstr ""
#: Library/Backend/WEBDAV/Strings.cs:4
@@ -1463,6 +1788,12 @@ msgid ""
"Error message: {3}"
msgstr ""
+#: Library/Backend/WEBDAV/Strings.cs:19 Library/Backend/TahoeLAFS/Strings.cs:5
+msgid ""
+"Use this flag to communicate using Secure Socket Layer (SSL) over http "
+"(https)."
+msgstr ""
+
#: Library/Backend/WEBDAV/Strings.cs:21
msgid ""
"To aid in debugging issues, it is possible to set a path to a file that will "
@@ -1473,6 +1804,41 @@ msgstr ""
msgid "Dump the PROPFIND response"
msgstr ""
+#: Library/Backend/TahoeLAFS/Strings.cs:4
+msgid ""
+"This backend can read and write data to a Tahoe-LAFS based backend. Allowed "
+"format is \"tahoe://hostname:port/uri/$DIRCAP\"."
+msgstr ""
+
+#: Library/Backend/TahoeLAFS/Strings.cs:7
+msgid "Tahoe-LAFS"
+msgstr ""
+
+#: Library/Backend/TahoeLAFS/Strings.cs:9
+msgid "Unsupported URL format, must start with \"uri/URI:DIR2:\""
+msgstr ""
+
+#: Library/Backend/OAuthHelper/Strings.cs:7
+#, csharp-format
+msgid ""
+"Failed to authorize using the OAuth service: {0}. If the problem persists, "
+"try generating a new authid token from: {1}"
+msgstr ""
+
+#: Library/Backend/OAuthHelper/Strings.cs:11
+msgid "The OAuth service is currently over quota, try again in a few hours"
+msgstr ""
+
+#: Library/DynamicLoader/Strings.cs:4
+#, csharp-format
+msgid "Failed to load assembly {0}, error message: {1}"
+msgstr ""
+
+#: Library/DynamicLoader/Strings.cs:5
+#, csharp-format
+msgid "Failed to load process type {0} assembly {1}, error message: {2}"
+msgstr ""
+
#: Library/Compression/Strings.cs:4
#, csharp-format
msgid "Please use the {0} option instead"
@@ -1565,319 +1931,36 @@ msgstr ""
msgid "Sets the 7z fast algorithm usage"
msgstr ""
-#: Library/DynamicLoader/Strings.cs:4
+#: Library/SQLiteHelper/Strings.cs:4
+msgid "backup"
+msgstr ""
+
+#: Library/SQLiteHelper/Strings.cs:5
#, csharp-format
-msgid "Failed to load assembly {0}, error message: {1}"
+msgid "Unable to determine database format: {0}"
msgstr ""
-#: Library/DynamicLoader/Strings.cs:5
-#, csharp-format
-msgid "Failed to load process type {0} assembly {1}, error message: {2}"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:4
-msgid ""
-"This module encrypts all files in the same way that AESCrypt does, using 256 "
-"bit AES encryption."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:5
-msgid "AES-256 encryption, built in"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:6
-msgid "Empty passphrase not allowed"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:7
-msgid ""
-"Use this option to set the thread level allowed for AES crypt operations. "
-"Valid values are 0 (uses default), or from 1 (no multithreading) to 4 (max. "
-"multithreading)"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:8
-msgid "Set thread level utilized for crypting (0-4)"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:11
-#, csharp-format
-msgid "Failed to decrypt data (invalid passphrase?): {0}"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:14
-msgid ""
-"The GPG encryption module uses the GNU Privacy Guard program to encrypt and "
-"decrypt files. It requires that the gpg executable is available on the "
-"system. On Windows it is assumed that this is in the default installation "
-"folder under program files, under Linux and OSX it is assumed that the "
-"program is available via the PATH environment variable. It is possible to "
-"supply the path to GPG using the --gpg-program-path switch."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:15
-msgid "GNU Privacy Guard, external"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:16
-msgid ""
-"Use this switch to specify any extra options to GPG. You cannot specify the "
-"--passphrase-fd option here. The --decrypt option is always specified."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:17
-msgid "Extra GPG commandline options for decryption"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:18
-msgid ""
-"The GPG encryption/decryption will use the --armor option for GPG to protect "
-"the files with armor. Specify this switch to remove the --armor option."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:19
-msgid "Don't use GPG Armor"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:20
-msgid ""
-"Use this switch to specify any extra options to GPG. You cannot specify the "
-"--passphrase-fd option here. The --encrypt option is always specified."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:21
-msgid "Extra GPG commandline options for encryption"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:22
-#, csharp-format
-msgid "Failed to execute GPG at \"\"{0}\" {1}\": {2}"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:23
-msgid ""
-"The path to the GNU Privacy Guard program. If not supplied, Duplicati will "
-"assume that the program \"gpg\" is available in the system path."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:24
-msgid "The path to GnuPG"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:25
+#: Library/SQLiteHelper/Strings.cs:6
#, csharp-format
msgid ""
-"This option has non-standard handling, please use the --{0} option instead."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:26
-msgid ""
-"Use this option to supply the --armor option to GPG. The files will be "
-"larger but can be sent as pure text files."
-msgstr ""
-
-#: Library/Encryption/Strings.cs:27
-msgid "Use GPG Armor"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:28
-msgid "Overrides the GPG command supplied for decryption"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:29
-msgid "The GPG decryption command"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:30
-#, csharp-format
-msgid ""
-"Overrides the default GPG encryption command \"{0}\", normal usage is to "
-"request asymetric encryption with the setting {1}"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:31
-msgid "The GPG encryption command"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:34
-#, csharp-format
-msgid "Decryption failed: {0}"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:35
-msgid "Failure while invoking GnuPG, program won't flush output"
-msgstr ""
-
-#: Library/Encryption/Strings.cs:36
-msgid "Failure while invoking GnuPG, program won't terminate"
-msgstr ""
-
-#: Library/Interface/CustomExceptions.cs:77
-#: Library/Interface/CustomExceptions.cs:85
-msgid "The requested file does not exist"
-msgstr ""
-
-#: Library/Interface/Strings.cs:4
-msgid "aliases"
-msgstr ""
-
-#: Library/Interface/Strings.cs:5
-msgid "default value"
-msgstr ""
-
-#: Library/Interface/Strings.cs:6
-msgid "[DEPRECATED]"
-msgstr ""
-
-#: Library/Interface/Strings.cs:7
-msgid "values"
-msgstr ""
-
-#: Library/Interface/Strings.cs:10
-msgid "Boolean"
-msgstr ""
-
-#: Library/Interface/Strings.cs:11
-msgid "Enumeration"
-msgstr ""
-
-#: Library/Interface/Strings.cs:12
-msgid "Flags"
-msgstr ""
-
-#: Library/Interface/Strings.cs:13
-msgid "Integer"
-msgstr ""
-
-#: Library/Interface/Strings.cs:14
-msgid "Path"
-msgstr ""
-
-#: Library/Interface/Strings.cs:15
-msgid "Size"
-msgstr ""
-
-#: Library/Interface/Strings.cs:16
-msgid "String"
-msgstr ""
-
-#: Library/Interface/Strings.cs:17
-msgid "Timespan"
-msgstr ""
-
-#: Library/Interface/Strings.cs:18
-msgid "Unknown"
-msgstr ""
-
-#: Library/Interface/Strings.cs:21
-#, csharp-format
-msgid ""
-"The configuration for the backend is not valid, it is missing the {0} field"
-msgstr ""
-
-#: Library/Interface/Strings.cs:22
-msgid "Do you want to test the connection?"
-msgstr ""
-
-#: Library/Interface/Strings.cs:23
-#, csharp-format
-msgid "Connection Failed: {0}"
-msgstr ""
-
-#: Library/Interface/Strings.cs:24
-msgid "Connection succeeded!"
-msgstr ""
-
-#: Library/Interface/Strings.cs:25
-msgid ""
-"You have not entered a path. This will store all backups in the default "
-"directory. Is this what you want?"
-msgstr ""
-
-#: Library/Interface/Strings.cs:26
-msgid "You must enter a password"
-msgstr ""
-
-#: Library/Interface/Strings.cs:27
-msgid ""
-"You have not entered a password.\n"
-"Proceed without a password?"
-msgstr ""
-
-#: Library/Interface/Strings.cs:29
-msgid "You must enter the name of the server"
-msgstr ""
-
-#: Library/Interface/Strings.cs:30
-msgid "You must enter a username"
-msgstr ""
-
-#: Library/Interface/Strings.cs:31
-msgid ""
-"You have not entered a username.\n"
-"This is fine if the server allows anonymous uploads, but likely a username "
-"is required\n"
-"Proceed without a username?"
-msgstr ""
-
-#: Library/Interface/Strings.cs:34
-msgid ""
-"The connection succeeded but another backup was found in the destination "
-"folder. It is possible to configure Duplicati to store multiple backups in "
-"the same folder, but it is not recommended.\n"
"\n"
-"Do you want to use the selected folder?"
+"The database has version {0} but the largest supported version is {1}.\n"
+"\n"
+"This is likely caused by upgrading to a newer version and then downgrading.\n"
+"If this is the case, there is likely a backup file of the previous database "
+"version in the folder {2}."
msgstr ""
-#: Library/Interface/Strings.cs:37
-msgid "The folder cannot be created because it already exists"
+#: Library/SQLiteHelper/Strings.cs:11
+msgid "Unknown table layout detected"
msgstr ""
-#: Library/Interface/Strings.cs:38
-msgid "Folder created!"
-msgstr ""
-
-#: Library/Interface/Strings.cs:39
-msgid "The requested folder does not exist"
-msgstr ""
-
-#: Library/Interface/Strings.cs:40
+#: Library/SQLiteHelper/Strings.cs:12
#, csharp-format
-msgid "The server name \"{0}\" is not valid"
-msgstr ""
-
-#: Library/Interface/Strings.cs:41
-msgid "Cancelled"
-msgstr ""
-
-#: Library/Main/BackendManager.cs:588
-#, csharp-format
-msgid "Failed to dispose backend instance: {0}"
-msgstr ""
-
-#: Library/Main/BackendManager.cs:611
-#, csharp-format
-msgid "Failed to delete file {0}, testing if file exists"
-msgstr ""
-
-#: Library/Main/BackendManager.cs:617
-#, csharp-format
-msgid "Recovered from problem with attempting to delete non-existing file {0}"
-msgstr ""
-
-#: Library/Main/BackendManager.cs:622
-#, csharp-format
-msgid "Failed to recover from error deleting file {0}"
-msgstr ""
-
-#: Library/Main/BackendManager.cs:1122
-#, csharp-format
-msgid "Delete operation failed for {0} with FileNotFound, listing contents"
-msgstr ""
-
-#: Library/Main/BackendManager.cs:1135
-#, csharp-format
-msgid "Listing indicates file {0} is deleted correctly"
+msgid ""
+"Failed to execute SQL: {0}\n"
+"Error: {1}\n"
+"Database is NOT upgraded."
msgstr ""
#: Library/Main/Database/ExtensionMethods.cs:48
@@ -1905,137 +1988,141 @@ msgstr ""
msgid "{0} records"
msgstr ""
-#: Library/Main/Strings.cs:7
+#: Library/Main/Strings.cs:8
#, csharp-format
msgid "Hash mismatch on file \"{0}\", recorded hash: {1}, actual hash {2}"
msgstr ""
-#: Library/Main/Strings.cs:8
+#: Library/Main/Strings.cs:9
#, csharp-format
msgid ""
"The file {0} was downloaded and had size {1} but the size was expected to be "
"{2}"
msgstr ""
-#: Library/Main/Strings.cs:9
+#: Library/Main/Strings.cs:10
#, csharp-format
msgid "The option {0} is deprecated: {1}"
msgstr ""
-#: Library/Main/Strings.cs:10
+#: Library/Main/Strings.cs:11
#, csharp-format
msgid ""
"The option --{0} exists more than once, please report this to the developers"
msgstr ""
-#: Library/Main/Strings.cs:11
+#: Library/Main/Strings.cs:12
msgid "No source folders specified for backup"
msgstr ""
-#: Library/Main/Strings.cs:12
+#: Library/Main/Strings.cs:13
#, csharp-format
msgid "The source folder {0} does not exist, aborting backup"
msgstr ""
-#: Library/Main/Strings.cs:13
+#: Library/Main/Strings.cs:14
#, csharp-format
msgid ""
"The value \"{1}\" supplied to --{0} does not parse into a valid boolean, "
"this will be treated as if it was set to \"true\""
msgstr ""
-#: Library/Main/Strings.cs:14
+#: Library/Main/Strings.cs:15
#, csharp-format
msgid ""
"The option --{0} does not support the value \"{1}\", supported values are: "
"{2}"
msgstr ""
-#: Library/Main/Strings.cs:15
+#: Library/Main/Strings.cs:16
#, csharp-format
msgid ""
"The option --{0} does not support the value \"{1}\", supported flag values "
"are: {2}"
msgstr ""
-#: Library/Main/Strings.cs:16
+#: Library/Main/Strings.cs:17
#, csharp-format
msgid "The value \"{1}\" supplied to --{0} does not represent a valid integer"
msgstr ""
-#: Library/Main/Strings.cs:17
+#: Library/Main/Strings.cs:18
#, csharp-format
msgid ""
"The option --{0} is not supported because the module {1} is not currently "
"loaded"
msgstr ""
-#: Library/Main/Strings.cs:18
+#: Library/Main/Strings.cs:19
#, csharp-format
msgid "The supplied option --{0} is not supported and will be ignored"
msgstr ""
-#: Library/Main/Strings.cs:19
+#: Library/Main/Strings.cs:20
#, csharp-format
msgid "The value \"{1}\" supplied to --{0} does not represent a valid path"
msgstr ""
-#: Library/Main/Strings.cs:20
+#: Library/Main/Strings.cs:21
#, csharp-format
msgid "The value \"{1}\" supplied to --{0} does not represent a valid size"
msgstr ""
-#: Library/Main/Strings.cs:21
+#: Library/Main/Strings.cs:22
#, csharp-format
msgid "The value \"{1}\" supplied to --{0} does not represent a valid time"
msgstr ""
-#: Library/Main/Strings.cs:22
+#: Library/Main/Strings.cs:23
#, csharp-format
msgid "The operation {0} has started"
msgstr ""
-#: Library/Main/Strings.cs:23
+#: Library/Main/Strings.cs:24
#, csharp-format
msgid "The operation {0} has completed"
msgstr ""
-#: Library/Main/Strings.cs:24
+#: Library/Main/Strings.cs:25
#, csharp-format
msgid "The operation {0} has failed with error: {1}"
msgstr ""
-#: Library/Main/Strings.cs:25
+#: Library/Main/Strings.cs:26
#, csharp-format
msgid "Invalid path: \"{0}\" ({1})"
msgstr ""
-#: Library/Main/Strings.cs:26
+#: Library/Main/Strings.cs:27
#, csharp-format
msgid ""
"Failed to apply 'force-locale' setting. Please try to update .NET-Framework. "
"Exception was: \"{0}\" "
msgstr ""
-#: Library/Main/Strings.cs:27
+#: Library/Main/Strings.cs:28
#, csharp-format
-msgid ""
-"The source folder {0} uses a wildcard drive, which is only supported on "
-"Windows, aborting backup"
+msgid "The source {0} uses an invalid volume name, aborting backup"
msgstr ""
-#: Library/Main/Strings.cs:32
+#: Library/Main/Strings.cs:29
+#, csharp-format
+msgid ""
+"The source {0} is on volume {1}, which could not be found, aborting backup"
+msgstr ""
+
+#: Library/Main/Strings.cs:34
msgid ""
"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."
msgstr ""
-#: Library/Main/Strings.cs:33
+#: Library/Main/Strings.cs:35
msgid "A flag indicating that Duplicati should remove unused files"
msgstr ""
-#: Library/Main/Strings.cs:34
+#: Library/Main/Strings.cs:36
msgid ""
"A string used to prefix the filenames of the remote volumes, can be used to "
"store multiple backups in the same remote folder. The prefix cannot contain "
@@ -2043,11 +2130,11 @@ msgid ""
"storage."
msgstr ""
-#: Library/Main/Strings.cs:35
+#: Library/Main/Strings.cs:37
msgid "Remote filename prefix"
msgstr ""
-#: Library/Main/Strings.cs:36
+#: Library/Main/Strings.cs:38
msgid ""
"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 "
@@ -2055,179 +2142,179 @@ msgid ""
"Duplicati won't work correctly unless this flag is set."
msgstr ""
-#: Library/Main/Strings.cs:37
+#: Library/Main/Strings.cs:39
msgid "Disable checks based on file time"
msgstr ""
-#: Library/Main/Strings.cs:38
+#: Library/Main/Strings.cs:40
msgid ""
"By default, files will be restored in the source folders, use this option to "
"restore to another folder"
msgstr ""
-#: Library/Main/Strings.cs:39
+#: Library/Main/Strings.cs:41
msgid "Restore to another folder"
msgstr ""
-#: Library/Main/Strings.cs:40
+#: Library/Main/Strings.cs:42
msgid "Toggles system sleep mode"
msgstr ""
-#: Library/Main/Strings.cs:41
+#: Library/Main/Strings.cs:43
msgid ""
"Allow system to enter sleep power modes for inactivity during backup/restore "
"operations (Windows/OSX only)"
msgstr ""
-#: Library/Main/Strings.cs:42
+#: Library/Main/Strings.cs:44
msgid ""
"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."
msgstr ""
-#: Library/Main/Strings.cs:43
+#: Library/Main/Strings.cs:45
msgid "Max number of kilobytes to download pr. second"
msgstr ""
-#: Library/Main/Strings.cs:44
+#: Library/Main/Strings.cs:46
msgid ""
"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."
msgstr ""
-#: Library/Main/Strings.cs:45
+#: Library/Main/Strings.cs:47
msgid "Max number of kilobytes to upload pr. second"
msgstr ""
-#: Library/Main/Strings.cs:46
+#: Library/Main/Strings.cs:48
msgid ""
"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."
msgstr ""
-#: Library/Main/Strings.cs:47
+#: Library/Main/Strings.cs:49
msgid "Disable encryption"
msgstr ""
-#: Library/Main/Strings.cs:48
+#: Library/Main/Strings.cs:50
msgid ""
"If an upload or download fails, Duplicati will retry a number of times "
"before failing. Use this to handle unstable network connections better."
msgstr ""
-#: Library/Main/Strings.cs:49
+#: Library/Main/Strings.cs:51
msgid "Number of times to retry a failed transmission"
msgstr ""
-#: Library/Main/Strings.cs:50
+#: Library/Main/Strings.cs:52
msgid ""
"Supply a passphrase that Duplicati will use to encrypt the backup volumes, "
"making them unreadable without the passphrase. This variable can also be "
"supplied through the environment variable PASSPHRASE."
msgstr ""
-#: Library/Main/Strings.cs:51
+#: Library/Main/Strings.cs:53
msgid "Passphrase used to encrypt backups"
msgstr ""
-#: Library/Main/Strings.cs:52
+#: Library/Main/Strings.cs:54
msgid ""
"By default, Duplicati will list and 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."
msgstr ""
-#: Library/Main/Strings.cs:53
+#: Library/Main/Strings.cs:55
msgid "The time to list/restore files"
msgstr ""
-#: Library/Main/Strings.cs:54
+#: Library/Main/Strings.cs:56
msgid ""
"By default, Duplicati will list and restore files from the most recent "
"backup, use this option to select another item. You may enter multiple "
"values separated with comma, and ranges using -, e.g. \"0,2-4,7\" ."
msgstr ""
-#: Library/Main/Strings.cs:55
+#: Library/Main/Strings.cs:57
msgid "The version to list/restore files"
msgstr ""
-#: Library/Main/Strings.cs:56
+#: Library/Main/Strings.cs:58
msgid ""
"When searching for files, only the most recent backup is searched. Use this "
"option to show all previous versions too."
msgstr ""
-#: Library/Main/Strings.cs:57
-msgid "Show all versions"
-msgstr ""
-
-#: Library/Main/Strings.cs:58
-msgid ""
-"When searching for files, all matching files are returned. Use this option "
-"to return only the largest common prefix path."
-msgstr ""
-
#: Library/Main/Strings.cs:59
-msgid "Show largest prefix"
+msgid "Show all versions"
msgstr ""
#: Library/Main/Strings.cs:60
msgid ""
"When searching for files, all matching files are returned. Use this option "
-"to return only the entries found in the folder specified as filter."
+"to return only the largest common prefix path."
msgstr ""
#: Library/Main/Strings.cs:61
-msgid "Show folder contents"
+msgid "Show largest prefix"
msgstr ""
#: Library/Main/Strings.cs:62
msgid ""
+"When searching for files, all matching files are returned. Use this option "
+"to return only the entries found in the folder specified as filter."
+msgstr ""
+
+#: Library/Main/Strings.cs:63
+msgid "Show folder contents"
+msgstr ""
+
+#: Library/Main/Strings.cs:64
+msgid ""
"After a failed transmission, Duplicati will wait a short period before "
"attempting again. This is useful if the network drops out occasionally "
"during transmissions."
msgstr ""
-#: Library/Main/Strings.cs:63
+#: Library/Main/Strings.cs:65
msgid "Time to wait between retries"
msgstr ""
-#: Library/Main/Strings.cs:64
+#: Library/Main/Strings.cs:66
msgid "Use this option to attach extra files to the newly uploaded filelists."
msgstr ""
-#: Library/Main/Strings.cs:65
+#: Library/Main/Strings.cs:67
msgid "Set control files"
msgstr ""
-#: Library/Main/Strings.cs:66
+#: Library/Main/Strings.cs:68
msgid ""
"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."
msgstr ""
-#: Library/Main/Strings.cs:67
+#: Library/Main/Strings.cs:69
msgid "Set this flag to skip hash checks"
msgstr ""
-#: Library/Main/Strings.cs:68
+#: Library/Main/Strings.cs:70
msgid ""
"This option allows you to exclude files that are larger than the given "
"value. Use this to prevent backups becoming extremely large."
msgstr ""
-#: Library/Main/Strings.cs:69
+#: Library/Main/Strings.cs:71
msgid "Limit the size of files being backed up"
msgstr ""
-#: Library/Main/Strings.cs:70
+#: Library/Main/Strings.cs:72
msgid "Temporary storage folder"
msgstr ""
-#: Library/Main/Strings.cs:71
+#: Library/Main/Strings.cs:73
msgid ""
"Duplicati will use the system default temporary folder. This option can be "
"used to supply an alternative folder for temporary storage. Note that SQLite "
@@ -2236,49 +2323,49 @@ msgid ""
"folder for both Duplicati and SQLite."
msgstr ""
-#: Library/Main/Strings.cs:72
+#: Library/Main/Strings.cs:74
msgid ""
"Selects another thread priority for the process. Use this to set Duplicati "
"to be more or less CPU intensive."
msgstr ""
-#: Library/Main/Strings.cs:73
+#: Library/Main/Strings.cs:75
msgid "Thread priority"
msgstr ""
-#: Library/Main/Strings.cs:74
+#: Library/Main/Strings.cs:76
msgid ""
"This option can change the maximum size of dblock files. Changing the size "
"can be useful if the backend has a limit on the size of each individual file"
msgstr ""
-#: Library/Main/Strings.cs:75
+#: Library/Main/Strings.cs:77
msgid "Limit the size of the volumes"
msgstr ""
-#: Library/Main/Strings.cs:76
+#: Library/Main/Strings.cs:78
msgid ""
"Enabling this option will disallow usage of the streaming interface, which "
"means that transfer progress bars will not show, and bandwidth throttle "
"settings will be ignored."
msgstr ""
-#: Library/Main/Strings.cs:77
+#: Library/Main/Strings.cs:79
msgid "Disables use of the streaming transfer method"
msgstr ""
-#: Library/Main/Strings.cs:78
+#: Library/Main/Strings.cs:80
msgid ""
"This option will make sure the contents of the manifest file are not read. "
"This also implies that file hashes are not checked either. Use only for "
"disaster recovery."
msgstr ""
-#: Library/Main/Strings.cs:79
+#: Library/Main/Strings.cs:81
msgid "An option that prevents verifying the manifests"
msgstr ""
-#: Library/Main/Strings.cs:80
+#: Library/Main/Strings.cs:82
msgid ""
"Duplicati supports pluggable compression modules. Use this option to select "
"a module to use for compression. This is only applied when creating new "
@@ -2286,11 +2373,11 @@ msgid ""
"compression module."
msgstr ""
-#: Library/Main/Strings.cs:81
+#: Library/Main/Strings.cs:83
msgid "Select what module to use for compression"
msgstr ""
-#: Library/Main/Strings.cs:82
+#: Library/Main/Strings.cs:84
msgid ""
"Duplicati supports pluggable encryption modules. Use this option to select a "
"module to use for encryption. This is only applied when creating new "
@@ -2298,27 +2385,27 @@ msgid ""
"encryption module."
msgstr ""
-#: Library/Main/Strings.cs:83
+#: Library/Main/Strings.cs:85
msgid "Select what module to use for encryption"
msgstr ""
-#: Library/Main/Strings.cs:84
+#: Library/Main/Strings.cs:86
msgid "Supply one or more module names, separated by commas to unload them"
msgstr ""
-#: Library/Main/Strings.cs:85
+#: Library/Main/Strings.cs:87
msgid "Disabled one or more modules"
msgstr ""
-#: Library/Main/Strings.cs:86
+#: Library/Main/Strings.cs:88
msgid "Supply one or more module names, separated by commas to load them"
msgstr ""
-#: Library/Main/Strings.cs:87
+#: Library/Main/Strings.cs:89
msgid "Enables one or more modules"
msgstr ""
-#: Library/Main/Strings.cs:88
+#: Library/Main/Strings.cs:90
msgid ""
"This setting controls the usage of snapshots, which allows Duplicati to "
"backup files that are locked by other programs. If this is set to \"off\", "
@@ -2332,67 +2419,67 @@ msgid ""
"Logical Volume Management (LVM) and requires root privileges."
msgstr ""
-#: Library/Main/Strings.cs:89
+#: Library/Main/Strings.cs:91
msgid "Controls the use of disk snapshots"
msgstr ""
-#: Library/Main/Strings.cs:90
+#: Library/Main/Strings.cs:92
msgid ""
"The pre-generated volumes will be placed into the temporary folder by "
"default, this option can set a different folder for placing the temporary "
"volumes, despite the name, this also works for synchronous runs"
msgstr ""
-#: Library/Main/Strings.cs:91
+#: Library/Main/Strings.cs:93
msgid "The path where ready volumes are placed until uploaded"
msgstr ""
-#: Library/Main/Strings.cs:92
+#: Library/Main/Strings.cs:94
msgid ""
"When performing asynchronous uploads, Duplicati will create volumes that can "
"be uploaded. To prevent Duplicati from generating too many volumes, this "
"option limits the number of pending uploads. Set to zero to disable the limit"
msgstr ""
-#: Library/Main/Strings.cs:93
+#: Library/Main/Strings.cs:95
msgid "The number of volumes to create ahead of time"
msgstr ""
-#: Library/Main/Strings.cs:94
+#: Library/Main/Strings.cs:96
msgid ""
"Activating this option will make some error messages more verbose, which may "
"help you track down a particular issue"
msgstr ""
-#: Library/Main/Strings.cs:95
+#: Library/Main/Strings.cs:97
msgid "Enables debugging output"
msgstr ""
-#: Library/Main/Strings.cs:96
+#: Library/Main/Strings.cs:98
msgid "Log internal information"
msgstr ""
-#: Library/Main/Strings.cs:97
+#: Library/Main/Strings.cs:99
msgid ""
"Specifies the amount of log information to write into the file specified by "
"--log-file"
msgstr ""
-#: Library/Main/Strings.cs:98
+#: Library/Main/Strings.cs:100
msgid "Log information level"
msgstr ""
-#: Library/Main/Strings.cs:99
+#: Library/Main/Strings.cs:101
msgid ""
"If Duplicati detects that the target folder is missing, it will create it "
"automatically. Activate this option to prevent automatic folder creation."
msgstr ""
-#: Library/Main/Strings.cs:100
+#: Library/Main/Strings.cs:102
msgid "Disables automatic folder creation"
msgstr ""
-#: Library/Main/Strings.cs:101
+#: Library/Main/Strings.cs:103
msgid ""
"Use this option to exclude faulty writers from a snapshot. This is "
"equivalent to the -wx flag of the vshadow.exe tool, except that it only "
@@ -2401,12 +2488,12 @@ msgid ""
"are allowed, including with and without curly braces."
msgstr ""
-#: Library/Main/Strings.cs:102
+#: Library/Main/Strings.cs:104
msgid ""
"A semicolon separated list of guids of VSS writers to exclude (Windows only)"
msgstr ""
-#: Library/Main/Strings.cs:103
+#: Library/Main/Strings.cs:105
msgid ""
"This setting controls the usage of NTFS USN numbers, which allows Duplicati "
"to obtain a list of files and folders much faster. If this is set to \"off"
@@ -2419,11 +2506,11 @@ msgid ""
"privileges."
msgstr ""
-#: Library/Main/Strings.cs:104
+#: Library/Main/Strings.cs:106
msgid "Controls the use of NTFS Update Sequence Numbers"
msgstr ""
-#: Library/Main/Strings.cs:105
+#: Library/Main/Strings.cs:107
msgid ""
"If USN is enabled the USN numbers are used to find all changed files since "
"last backup. Use this option to disable the use of USN numbers, which will "
@@ -2432,11 +2519,11 @@ msgid ""
"If USN is not enabled, this option has no effect."
msgstr ""
-#: Library/Main/Strings.cs:106
+#: Library/Main/Strings.cs:108
msgid "Disables changelist by USN numbers"
msgstr ""
-#: Library/Main/Strings.cs:107
+#: Library/Main/Strings.cs:109
#, csharp-format
msgid ""
"When matching timestamps, Duplicati will adjust the times by a small "
@@ -2449,26 +2536,26 @@ msgid ""
"strict time checking"
msgstr ""
-#: Library/Main/Strings.cs:108
+#: Library/Main/Strings.cs:110
msgid "Deactivates tolerance when comparing times"
msgstr ""
-#: Library/Main/Strings.cs:109
+#: Library/Main/Strings.cs:111
msgid "Verify uploads by listing contents"
msgstr ""
-#: Library/Main/Strings.cs:110
+#: Library/Main/Strings.cs:112
msgid ""
"Duplicati will upload files while scanning the disk and producing volumes, "
"which usually makes the backup faster. Use this flag to turn the behavior "
"off, so that Duplicati will wait for each volume to complete."
msgstr ""
-#: Library/Main/Strings.cs:111
+#: Library/Main/Strings.cs:113
msgid "Upload files synchronously"
msgstr ""
-#: Library/Main/Strings.cs:112
+#: Library/Main/Strings.cs:114
msgid ""
"Duplicati will attempt to perform multiple operations on a single "
"connection, as this avoids repeated login attempts, and thus speeds up the "
@@ -2476,47 +2563,60 @@ msgid ""
"on a seperate connection"
msgstr ""
-#: Library/Main/Strings.cs:113
+#: Library/Main/Strings.cs:115
msgid "Do not re-use connections"
msgstr ""
-#: Library/Main/Strings.cs:114
+#: Library/Main/Strings.cs:116
msgid ""
"When an error occurs, Duplicati will silently retry, and only report the "
"number of retries. Enable this option to have the error messages displayed "
"when a retry is performed."
msgstr ""
-#: Library/Main/Strings.cs:115
+#: Library/Main/Strings.cs:117
msgid "Show error messages when a retry is performed"
msgstr ""
-#: Library/Main/Strings.cs:116
+#: Library/Main/Strings.cs:118
msgid ""
"If no files have changed, Duplicati will not upload a backup set. If the "
"backup data is used to verify that a backup was executed, this option will "
"make Duplicati upload a backupset even if it is empty"
msgstr ""
-#: Library/Main/Strings.cs:117
+#: Library/Main/Strings.cs:119
msgid "Upload empty backup files"
msgstr ""
-#: Library/Main/Strings.cs:118
+#: Library/Main/Strings.cs:120
msgid ""
"This value can be used to set a known upper limit on the amount of space a "
"backend has. If the backend reports the size itself, this value is ignored"
msgstr ""
-#: Library/Main/Strings.cs:119
+#: Library/Main/Strings.cs:121
msgid "A reported maximum storage"
msgstr ""
-#: Library/Main/Strings.cs:120
+#: Library/Main/Strings.cs:122
+#, csharp-format
+msgid ""
+"Exclude files that match the given filter sets. Which default filter sets "
+"should be used. Valid sets are \"{0}\", \"{1}\", \"{2}\", and \"{3}\". If "
+"this parameter is set with no value, the set for the current operating "
+"system will be used."
+msgstr ""
+
+#: Library/Main/Strings.cs:123
+msgid "Default filter sets"
+msgstr ""
+
+#: Library/Main/Strings.cs:124
msgid "Symlink handling"
msgstr ""
-#: Library/Main/Strings.cs:121
+#: Library/Main/Strings.cs:125
#, csharp-format
msgid ""
"Use this option to handle symlinks differently. The \"{0}\" option will "
@@ -2527,11 +2627,11 @@ msgid ""
"included and restore as normal files."
msgstr ""
-#: Library/Main/Strings.cs:122
+#: Library/Main/Strings.cs:126
msgid "Hardlink handling"
msgstr ""
-#: Library/Main/Strings.cs:123
+#: Library/Main/Strings.cs:127
#, csharp-format
msgid ""
"Use this option to handle hardlinks (only works on Linux/OSX). The \"{0}\" "
@@ -2541,11 +2641,11 @@ msgid ""
"will ignore all hardlinks with more than one link."
msgstr ""
-#: Library/Main/Strings.cs:124
+#: Library/Main/Strings.cs:128
msgid "Exclude files by attribute"
msgstr ""
-#: Library/Main/Strings.cs:125
+#: Library/Main/Strings.cs:129
#, csharp-format
msgid ""
"Use this option to exclude files with certain attributes. Use a comma "
@@ -2553,7 +2653,7 @@ msgid ""
"are: {0}"
msgstr ""
-#: Library/Main/Strings.cs:126
+#: Library/Main/Strings.cs:130
msgid ""
"Activate this option to map VSS snapshots to a drive (similar to SUBST, "
"using Win32 DefineDosDevice). This will create temporary drives that are "
@@ -2561,21 +2661,21 @@ msgid ""
"file access on Windows XP."
msgstr ""
-#: Library/Main/Strings.cs:127
+#: Library/Main/Strings.cs:131
msgid "Map snapshots to a drive (Windows only)"
msgstr ""
-#: Library/Main/Strings.cs:128
+#: Library/Main/Strings.cs:132
msgid ""
"A display name that is attached to this backup. Can be used to identify the "
"backup when sending mail or running scripts."
msgstr ""
-#: Library/Main/Strings.cs:129
+#: Library/Main/Strings.cs:133
msgid "Name of the backup"
msgstr ""
-#: Library/Main/Strings.cs:130
+#: Library/Main/Strings.cs:134
#, csharp-format
msgid ""
"This property can be used to point to a text file where each line contains a "
@@ -2587,22 +2687,22 @@ msgid ""
"{0}."
msgstr ""
-#: Library/Main/Strings.cs:131
+#: Library/Main/Strings.cs:135
msgid "Manage non-compressible file extensions"
msgstr ""
-#: Library/Main/Strings.cs:132 Library/Main/Strings.cs:142
-#: Library/Main/Strings.cs:151
+#: Library/Main/Strings.cs:136 Library/Main/Strings.cs:146
+#: Library/Main/Strings.cs:155
msgid ""
"A fragment of memory is used to reduce database lookups. You should not "
"change this value unless you get warnings in the log."
msgstr ""
-#: Library/Main/Strings.cs:133
+#: Library/Main/Strings.cs:137
msgid "Memory used by the block hash"
msgstr ""
-#: Library/Main/Strings.cs:134
+#: Library/Main/Strings.cs:138
msgid ""
"The block size determines how files are fragmented. Choosing a large value "
"will cause a larger overhead on file changes, choosing a small value will "
@@ -2610,93 +2710,93 @@ msgid ""
"be changed after remote files are created."
msgstr ""
-#: Library/Main/Strings.cs:135
+#: Library/Main/Strings.cs:139
msgid "Block size used in hashing"
msgstr ""
-#: Library/Main/Strings.cs:136
+#: Library/Main/Strings.cs:140
msgid ""
"This option can be used to limit the scan to only files that are known to "
"have changed. This is usually only activated in combination with a "
"filesystem watcher that keeps track of file changes."
msgstr ""
-#: Library/Main/Strings.cs:137
+#: Library/Main/Strings.cs:141
msgid "List of files to examine for changes"
msgstr ""
-#: Library/Main/Strings.cs:138
+#: Library/Main/Strings.cs:142
msgid "Path to the file containing the local cache of the remote file database"
msgstr ""
-#: Library/Main/Strings.cs:139
+#: Library/Main/Strings.cs:143
msgid "Path to the local state database"
msgstr ""
-#: Library/Main/Strings.cs:140
+#: Library/Main/Strings.cs:144
#, csharp-format
msgid ""
"This option can be used to supply a list of deleted files. This option will "
"be ignored unless the option --{0} is also set."
msgstr ""
-#: Library/Main/Strings.cs:141
+#: Library/Main/Strings.cs:145
msgid "List of deleted files"
msgstr ""
-#: Library/Main/Strings.cs:143
+#: Library/Main/Strings.cs:147
msgid "Memory used by the file hash"
msgstr ""
-#: Library/Main/Strings.cs:144
+#: Library/Main/Strings.cs:148
msgid ""
"This option can be used to reduce the memory footprint by not keeping paths "
"and modification timestamps in memory"
msgstr ""
-#: Library/Main/Strings.cs:145
+#: Library/Main/Strings.cs:149
msgid "Reduce memory footprint by disabling in-memory lookups"
msgstr ""
-#: Library/Main/Strings.cs:146
+#: Library/Main/Strings.cs:150
msgid ""
"This option can be used to increase speed in exchange for extra memory use."
msgstr ""
-#: Library/Main/Strings.cs:147
+#: Library/Main/Strings.cs:151
msgid "Store an in-memory block cache"
msgstr ""
-#: Library/Main/Strings.cs:148
+#: Library/Main/Strings.cs:152
msgid ""
"Stores metadata, such as file timestamps and attributes. This increases the "
"required storage space as well as the processing time."
msgstr ""
-#: Library/Main/Strings.cs:149
+#: Library/Main/Strings.cs:153
msgid "Enables storing file metadata"
msgstr ""
-#: Library/Main/Strings.cs:150
+#: Library/Main/Strings.cs:154
msgid "This option is no longer used as metadata is now stored by default"
msgstr ""
-#: Library/Main/Strings.cs:152
+#: Library/Main/Strings.cs:156
msgid "Memory used by the metadata hash"
msgstr ""
-#: Library/Main/Strings.cs:153
+#: Library/Main/Strings.cs:157
msgid ""
"If this flag is set, the local database is not compared to the remote "
"filelist on startup. The intended usage for this option is to work correctly "
"in cases where the filelisting is broken or unavailable."
msgstr ""
-#: Library/Main/Strings.cs:154
+#: Library/Main/Strings.cs:158
msgid "Do not query backend at startup"
msgstr ""
-#: Library/Main/Strings.cs:155
+#: Library/Main/Strings.cs:159
msgid ""
"The index files are used to limit the need for downloading dblock files when "
"there is no local database present. The more information is recorded in the "
@@ -2705,11 +2805,11 @@ msgid ""
"never be used."
msgstr ""
-#: Library/Main/Strings.cs:156
+#: Library/Main/Strings.cs:160
msgid "Determines usage of index files"
msgstr ""
-#: Library/Main/Strings.cs:157
+#: Library/Main/Strings.cs:161
msgid ""
"As files are changed, some data stored at the remote destination may not be "
"required. This option controls how much wasted space the destination can "
@@ -2717,43 +2817,43 @@ msgid ""
"volume and the total storage."
msgstr ""
-#: Library/Main/Strings.cs:158
+#: Library/Main/Strings.cs:162
msgid "The maximum wasted space in percent"
msgstr ""
-#: Library/Main/Strings.cs:159
+#: Library/Main/Strings.cs:163
msgid ""
"This option can be used to experiment with different settings and observe "
"the outcome without changing actual files."
msgstr ""
-#: Library/Main/Strings.cs:160
+#: Library/Main/Strings.cs:164
msgid "Does not perform any modifications"
msgstr ""
-#: Library/Main/Strings.cs:161
+#: Library/Main/Strings.cs:165
msgid ""
"This is a very advanced option! This option can be used to select a block "
"hash algorithm with smaller or larger hash size, for performance or storage "
"space reasons."
msgstr ""
-#: Library/Main/Strings.cs:162
+#: Library/Main/Strings.cs:166
msgid "The hash algorithm used on blocks"
msgstr ""
-#: Library/Main/Strings.cs:163
+#: Library/Main/Strings.cs:167
msgid ""
"This is a very advanced option! This option can be used to select a file "
"hash algorithm with smaller or larger hash size, for performance or storage "
"space reasons."
msgstr ""
-#: Library/Main/Strings.cs:164
+#: Library/Main/Strings.cs:168
msgid "The hash algorithm used on files"
msgstr ""
-#: Library/Main/Strings.cs:165
+#: Library/Main/Strings.cs:169
msgid ""
"If a large number of small files are detected during a backup, or wasted "
"space is found after deleting backups, the remote data will be compacted. "
@@ -2761,11 +2861,11 @@ msgid ""
"running the compact command."
msgstr ""
-#: Library/Main/Strings.cs:166
+#: Library/Main/Strings.cs:170
msgid "Disable automatic compacting"
msgstr ""
-#: Library/Main/Strings.cs:167
+#: Library/Main/Strings.cs:171
msgid ""
"When examining the size of a volume in consideration for compacting, a small "
"tolerance value is used, by default 20 percent of the volume size. This "
@@ -2773,114 +2873,128 @@ msgid ""
"downloaded and rewritten."
msgstr ""
-#: Library/Main/Strings.cs:168
+#: Library/Main/Strings.cs:172
msgid "Volume size threshold"
msgstr ""
-#: Library/Main/Strings.cs:169
+#: Library/Main/Strings.cs:173
msgid ""
"To avoid filling the remote storage with small files, this value can force "
"grouping small files. The small volumes will always be combined when they "
"can fill an entire volume."
msgstr ""
-#: Library/Main/Strings.cs:170
+#: Library/Main/Strings.cs:174
msgid "Maximum number of small volumes"
msgstr ""
-#: Library/Main/Strings.cs:171
+#: Library/Main/Strings.cs:175
msgid ""
"Enable this option to look into other files on this machine to find existing "
"blocks. This is a fairly slow operation but can limit the size of downloads."
msgstr ""
-#: Library/Main/Strings.cs:172
+#: Library/Main/Strings.cs:176
msgid "Use local file data when restoring"
msgstr ""
-#: Library/Main/Strings.cs:173
+#: Library/Main/Strings.cs:177
msgid "Disables the local database"
msgstr ""
-#: Library/Main/Strings.cs:174
+#: Library/Main/Strings.cs:178
msgid ""
"When listing contents or when restoring files, the local database can be "
"skipped. This is usually slower, but can be used to verify the actual "
"contents of the remote store"
msgstr ""
-#: Library/Main/Strings.cs:175
+#: Library/Main/Strings.cs:179
msgid "Keep a number of versions"
msgstr ""
-#: Library/Main/Strings.cs:176
+#: Library/Main/Strings.cs:180
msgid ""
"Use this option to set number of versions to keep, supply -1 to keep all "
"versions"
msgstr ""
-#: Library/Main/Strings.cs:177
+#: Library/Main/Strings.cs:181
msgid "Keep all versions within a timespan"
msgstr ""
-#: Library/Main/Strings.cs:178
+#: Library/Main/Strings.cs:182
msgid "Use this option to set the timespan in which backups are kept."
msgstr ""
-#: Library/Main/Strings.cs:179
+#: Library/Main/Strings.cs:183
+msgid "Reduce number of versions by deleting old intermediate backups"
+msgstr ""
+
+#: Library/Main/Strings.cs:184
+msgid ""
+"Use this option to reduce the number of versions that are kept with "
+"increasing version age by deleting most of the old backups. The expected "
+"format is a comma seperated list of collon sperated time frame and interval "
+"pairs. For example the value \"7D:0s,3M:1D,10Y:2M\" means \"For 7 day keep "
+"all backups, for 3 months keep one backup per day and for 10 years one "
+"backup every 2nd month"
+msgstr ""
+
+#: Library/Main/Strings.cs:185
msgid "Ignore missing source elements"
msgstr ""
-#: Library/Main/Strings.cs:180
+#: Library/Main/Strings.cs:186
msgid "Use this option to continue even if some source entries are missing."
msgstr ""
-#: Library/Main/Strings.cs:181
+#: Library/Main/Strings.cs:187
msgid "Overwrite files when restoring"
msgstr ""
-#: Library/Main/Strings.cs:182
+#: Library/Main/Strings.cs:188
msgid ""
"Use this option to overwrite target files when restoring, if this option is "
"not set the files will be restored with a timestamp and a number appended."
msgstr ""
-#: Library/Main/Strings.cs:183
+#: Library/Main/Strings.cs:189
msgid "Output more progress information"
msgstr ""
-#: Library/Main/Strings.cs:184
+#: Library/Main/Strings.cs:190
msgid ""
"Use this option to increase the amount of output generated when running an "
"option. Generally this option will produce a line for each file processed."
msgstr ""
-#: Library/Main/Strings.cs:185
+#: Library/Main/Strings.cs:191
msgid "Output full results"
msgstr ""
-#: Library/Main/Strings.cs:186
+#: Library/Main/Strings.cs:192
msgid ""
"Use this option to increase the amount of output generated as the result of "
"the operation, including all filenames."
msgstr ""
-#: Library/Main/Strings.cs:187
+#: Library/Main/Strings.cs:193
msgid "Determine if verification files are uploaded"
msgstr ""
-#: Library/Main/Strings.cs:188
+#: Library/Main/Strings.cs:194
msgid ""
"Use this option to upload a verification file after changing the remote "
"storage. The file is not encrypted and contains the size and SHA256 hashes "
"of all the remote files and can be used to verify the integrity of the files."
msgstr ""
-#: Library/Main/Strings.cs:189
+#: Library/Main/Strings.cs:195
msgid "The number of samples to test after a backup"
msgstr ""
-#: Library/Main/Strings.cs:190
+#: Library/Main/Strings.cs:196
#, csharp-format
msgid ""
"After a backup is completed, some files are selected for verification on the "
@@ -2888,11 +3002,11 @@ msgid ""
"0 or the option --{0} is set, no remote files are verified"
msgstr ""
-#: Library/Main/Strings.cs:191
+#: Library/Main/Strings.cs:197
msgid "Activates in-depth verification of files"
msgstr ""
-#: Library/Main/Strings.cs:192
+#: Library/Main/Strings.cs:198
#, csharp-format
msgid ""
"After a backup is completed, some files are selected for verification on the "
@@ -2903,109 +3017,101 @@ msgid ""
"performed directly."
msgstr ""
-#: Library/Main/Strings.cs:193
+#: Library/Main/Strings.cs:199
msgid "Size of the file read buffer"
msgstr ""
-#: Library/Main/Strings.cs:194
+#: Library/Main/Strings.cs:200
msgid ""
"Use this size to control how many bytes a read from a file before processing"
msgstr ""
-#: Library/Main/Strings.cs:195
+#: Library/Main/Strings.cs:201
msgid "Allow the passphrase to change"
msgstr ""
-#: Library/Main/Strings.cs:196
+#: Library/Main/Strings.cs:202
msgid ""
"Use this option to allow the passphrase to change, note that this option is "
"not permitted for a backup or repair operation"
msgstr ""
-#: Library/Main/Strings.cs:197
+#: Library/Main/Strings.cs:203
msgid "List only filesets"
msgstr ""
-#: Library/Main/Strings.cs:198
+#: Library/Main/Strings.cs:204
msgid ""
"Use this option to only list filesets and avoid traversing file names and "
"other metadata which slows down the process"
msgstr ""
-#: Library/Main/Strings.cs:200
+#: Library/Main/Strings.cs:206
msgid "Don't store metadata"
msgstr ""
-#: Library/Main/Strings.cs:201
+#: Library/Main/Strings.cs:207
msgid ""
"Use this option to disable the storage of metadata, such as file timestamps. "
"Disabling metadata storage will speed up the backup and restore operations, "
"but does not affect file size much."
msgstr ""
-#: Library/Main/Strings.cs:202
+#: Library/Main/Strings.cs:208
msgid "Restore file permissions"
msgstr ""
-#: Library/Main/Strings.cs:203
+#: Library/Main/Strings.cs:209
msgid ""
"By default permissions are not restored as they might prevent you from "
"accessing your files. Use this option to restore the permissions as well."
msgstr ""
-#: Library/Main/Strings.cs:204
+#: Library/Main/Strings.cs:210
msgid "Skip restored file check"
msgstr ""
-#: Library/Main/Strings.cs:205
+#: Library/Main/Strings.cs:211
msgid ""
"After restoring files, the file hash of all restored files are checked to "
"verify that the restore was successful. Use this option to disable the check "
"and avoid waiting for the verification."
msgstr ""
-#: Library/Main/Strings.cs:206
+#: Library/Main/Strings.cs:212
msgid "Activate caches"
msgstr ""
-#: Library/Main/Strings.cs:207
+#: Library/Main/Strings.cs:213
msgid "Activate in-memory caches, which are now off by default"
msgstr ""
-#: Library/Main/Strings.cs:208
+#: Library/Main/Strings.cs:214
msgid "Do not use local data"
msgstr ""
-#: Library/Main/Strings.cs:209
+#: Library/Main/Strings.cs:215
msgid ""
"Duplicati will attempt to use data from source files to minimize the amount "
"of downloaded data. Use this option to skip this optimization and only use "
"remote data."
msgstr ""
-#: Library/Main/Strings.cs:210
+#: Library/Main/Strings.cs:216
msgid "Check block hashes"
msgstr ""
-#: Library/Main/Strings.cs:211
+#: Library/Main/Strings.cs:217
msgid ""
"Use this option to increase verification by checking the hash of blocks read "
"from a volume before patching restored files with the data."
msgstr ""
-#: Library/Main/Strings.cs:212 Server/Strings.cs:31
-msgid "Clean up old log data"
-msgstr ""
-
-#: Library/Main/Strings.cs:213 Server/Strings.cs:32
-msgid "Set the time after which log data will be purged from the database."
-msgstr ""
-
-#: Library/Main/Strings.cs:214
+#: Library/Main/Strings.cs:220
msgid "Repair database with paths"
msgstr ""
-#: Library/Main/Strings.cs:215
+#: Library/Main/Strings.cs:221
msgid ""
"Use this option to build a searchable local database which only contains "
"path information. This option is usable for quickly building a database to "
@@ -3013,11 +3119,11 @@ msgid ""
"resulting database can be searched, but cannot be used to restore data with."
msgstr ""
-#: Library/Main/Strings.cs:216
+#: Library/Main/Strings.cs:222
msgid "Force the locale setting"
msgstr ""
-#: Library/Main/Strings.cs:217
+#: Library/Main/Strings.cs:223
msgid ""
"By default, your system locale and culture settings will be used. In some "
"cases you may prefer to run with another locale, for example to get messages "
@@ -3025,40 +3131,40 @@ msgid ""
"blank string to choose the \"Invariant Culture\"."
msgstr ""
-#: Library/Main/Strings.cs:218
+#: Library/Main/Strings.cs:224
msgid "Handle file communication with backend using threaded pipes"
msgstr ""
-#: Library/Main/Strings.cs:219
+#: Library/Main/Strings.cs:225
msgid ""
"Use this option to disable multithreaded handling of up- and downloads, that "
"can significantly speed up backend operations depending on the hardware "
"you're running on and the transfer rate of your backend."
msgstr ""
-#: Library/Main/Strings.cs:220
+#: Library/Main/Strings.cs:226
msgid "Perform backup of Hyper-V machines (Windows only)"
msgstr ""
-#: Library/Main/Strings.cs:221
+#: Library/Main/Strings.cs:227
msgid ""
"Use this option to specify the IDs of machines to include in the backup. "
"Specify multiple machine IDs with a semicolon separator. (You can use this "
"Powershell command to get ID 'Get-VM | ft VMName, ID')"
msgstr ""
-#: Library/Main/Strings.cs:222
+#: Library/Main/Strings.cs:228
msgid ""
"If Duplicati detects that the previous backup did not complete, it will "
"generate a filelist that is a merge of the last completed backup and the "
"contents that were uploaded in the incomplete backup session."
msgstr ""
-#: Library/Main/Strings.cs:223
+#: Library/Main/Strings.cs:229
msgid "Disables synthetic filelist"
msgstr ""
-#: Library/Main/Strings.cs:224
+#: Library/Main/Strings.cs:230
msgid ""
"This flag instructs Duplicati to not look at metadata or filesize when "
"deciding to scan a file for changes. Use this option if you have a large "
@@ -3066,15 +3172,15 @@ msgid ""
"unmodified files."
msgstr ""
-#: Library/Main/Strings.cs:225
+#: Library/Main/Strings.cs:231
msgid "Checks only file lastmodified"
msgstr ""
-#: Library/Main/Strings.cs:226
+#: Library/Main/Strings.cs:232
msgid "Disables path compresion on restore"
msgstr ""
-#: Library/Main/Strings.cs:227
+#: Library/Main/Strings.cs:233
msgid ""
"When restore a subset of a backup into a new folder, the shortest possible "
"path is used to avoid generating deep paths with empty folders. Use this "
@@ -3082,22 +3188,22 @@ msgid ""
"structure is preserved, including upper level empty folders."
msgstr ""
-#: Library/Main/Strings.cs:228
+#: Library/Main/Strings.cs:234
msgid "Allow removing all filesets"
msgstr ""
-#: Library/Main/Strings.cs:229
+#: Library/Main/Strings.cs:235
msgid ""
"By default, the last fileset cannot be removed. This is a safeguard to make "
"sure that all remote data is not deleted by a configuration mistake. Use "
"this flag to disable that protection, such that all filesets can be deleted."
msgstr ""
-#: Library/Main/Strings.cs:230
+#: Library/Main/Strings.cs:236
msgid "Allow automatic rebuilding of local database to save space."
msgstr ""
-#: Library/Main/Strings.cs:231
+#: Library/Main/Strings.cs:237
msgid ""
"Some operations that manipulate the local database leave unused entries "
"behind. These entries are not deleted from a hard drive until a VACUUM "
@@ -3107,27 +3213,57 @@ msgid ""
"discretion."
msgstr ""
-#: Library/Main/Strings.cs:236
+#: Library/Main/Strings.cs:242
#, csharp-format
msgid ""
"The cryptolibrary does not support re-usable transforms for the hash "
"algorithm {0}"
msgstr ""
-#: Library/Main/Strings.cs:237
+#: Library/Main/Strings.cs:243
#, csharp-format
msgid "The cryptolibrary does not support the hash algorithm {0}"
msgstr ""
-#: Library/Main/Strings.cs:238
+#: Library/Main/Strings.cs:244
msgid "The passphrase cannot be changed for an existing backup"
msgstr ""
-#: Library/Main/Strings.cs:239
+#: Library/Main/Strings.cs:245
#, csharp-format
msgid "Failed to create a snapshot: {0}"
msgstr ""
+#: Library/Main/BackendManager.cs:595
+#, csharp-format
+msgid "Failed to dispose backend instance: {0}"
+msgstr ""
+
+#: Library/Main/BackendManager.cs:618
+#, csharp-format
+msgid "Failed to delete file {0}, testing if file exists"
+msgstr ""
+
+#: Library/Main/BackendManager.cs:624
+#, csharp-format
+msgid "Recovered from problem with attempting to delete non-existing file {0}"
+msgstr ""
+
+#: Library/Main/BackendManager.cs:629
+#, csharp-format
+msgid "Failed to recover from error deleting file {0}"
+msgstr ""
+
+#: Library/Main/BackendManager.cs:1134
+#, csharp-format
+msgid "Delete operation failed for {0} with FileNotFound, listing contents"
+msgstr ""
+
+#: Library/Main/BackendManager.cs:1147
+#, csharp-format
+msgid "Listing indicates file {0} is deleted correctly"
+msgstr ""
+
#: Library/Modules/Builtin/Strings.cs:7
msgid "Confirm encryption passphrase"
msgstr ""
@@ -3658,100 +3794,6 @@ msgstr ""
msgid "Failed to send http message: {0}"
msgstr ""
-#: Library/Snapshots/Strings.cs:4
-#, csharp-format
-msgid ""
-"The external command failed to start.\n"
-"Error message: {0}\n"
-"Command: {1} {2}"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:7
-#, csharp-format
-msgid ""
-"The external command failed to complete within the set time limit: {0} {1}"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:8
-#, csharp-format
-msgid "Unable to match local path {0} with any snapshot path: {1}"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:9
-#, csharp-format
-msgid ""
-"Script returned successfully, but the temporary folder {0} does not exist: "
-"{1}"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:10
-#, csharp-format
-msgid ""
-"Script returned successfully, but the temporary folder {0} still exist: {1}"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:11
-#, csharp-format
-msgid "The script returned exit code {0}, but {1} was expected: {2}"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:12
-#, csharp-format
-msgid ""
-"Script returned successfully, but the output was missing the {0} parameter: "
-"{1}"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:15
-msgid "Unexpected empty response while enumerating"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:16
-msgid "USN is not supported on Linux"
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:17
-msgid ""
-"The number of files returned by USN was zero. This is likely an error. To "
-"remedy this, USN has been disabled."
-msgstr ""
-
-#: Library/Snapshots/Strings.cs:20
-msgid "Calling process does not have the backup privilege"
-msgstr ""
-
-#: Library/SQLiteHelper/Strings.cs:4
-msgid "backup"
-msgstr ""
-
-#: Library/SQLiteHelper/Strings.cs:5
-#, csharp-format
-msgid "Unable to determine database format: {0}"
-msgstr ""
-
-#: Library/SQLiteHelper/Strings.cs:6
-#, csharp-format
-msgid ""
-"\n"
-"The database has version {0} but the largest supported version is {1}.\n"
-"\n"
-"This is likely caused by upgrading to a newer version and then downgrading.\n"
-"If this is the case, there is likely a backup file of the previous database "
-"version in the folder {2}."
-msgstr ""
-
-#: Library/SQLiteHelper/Strings.cs:11
-msgid "Unknown table layout detected"
-msgstr ""
-
-#: Library/SQLiteHelper/Strings.cs:12
-#, csharp-format
-msgid ""
-"Failed to execute SQL: {0}\n"
-"Error: {1}\n"
-"Database is NOT upgraded."
-msgstr ""
-
#: Library/Utility/Strings.cs:7
#, csharp-format
msgid "Invalid size value: {0}"
@@ -3850,158 +3892,169 @@ msgstr ""
msgid "Cannot read and write on the same stream"
msgstr ""
-#: Server/Strings.cs:7
-msgid "Another instance is running, and was notified"
+#: Library/Utility/Strings.cs:39
+#, csharp-format
+msgid "Unknown default filter set: {0}"
msgstr ""
-#: Server/Strings.cs:8
+#: CommandLine/Strings.cs:4
+#, csharp-format
+msgid "The command {0} needs at least one of the following options set: {1}"
+msgstr ""
+
+#: CommandLine/Strings.cs:5
#, csharp-format
msgid ""
-"Failed to create, open or upgrade the database.\n"
-"Error message: {0}"
+"Found {0} commands but expected {1}, commands: \n"
+"{2}"
msgstr ""
-#: Server/Strings.cs:10
-msgid "Displays this help"
-msgstr ""
-
-#: Server/Strings.cs:11
-msgid ""
-"Supported commandline arguments:\n"
-"\n"
-msgstr ""
-
-#: Server/Strings.cs:14
+#: CommandLine/Strings.cs:7
#, csharp-format
-msgid "--{0}: {1}"
+msgid "Command not supported: {0}"
msgstr ""
-#: Server/Strings.cs:15
-msgid "Outputs log information to the file given"
+#: CommandLine/Strings.cs:8
+msgid "No filesets matched the criteria"
msgstr ""
-#: Server/Strings.cs:16
-msgid "Determines the amount of information written in the log file"
+#: CommandLine/Strings.cs:9
+msgid "The following filesets would be deleted:"
msgstr ""
-#: Server/Strings.cs:17
-msgid ""
-"Activates portable mode where the database is placed below the program "
-"executable"
+#: CommandLine/Strings.cs:10
+msgid "These filesets were deleted:"
msgstr ""
-#: Server/Strings.cs:18
+#: CommandLine/Strings.cs:11
+msgid "Supported backends:"
+msgstr ""
+
+#: CommandLine/Strings.cs:12
+msgid "Supported compression modules:"
+msgstr ""
+
+#: CommandLine/Strings.cs:13
+msgid "Supported encryption modules:"
+msgstr ""
+
+#: CommandLine/Strings.cs:14
+msgid "Supported options:"
+msgstr ""
+
+#: CommandLine/Strings.cs:15
+msgid "Module is loaded automatically, use --disable-module to prevent this"
+msgstr ""
+
+#: CommandLine/Strings.cs:16
+msgid "Module is not loaded automatically, use --enable-module to load it"
+msgstr ""
+
+#: CommandLine/Strings.cs:17
+msgid "Supported generic modules:"
+msgstr ""
+
+#: CommandLine/Strings.cs:18
#, csharp-format
-msgid "A serious error occurred in Duplicati: {0}"
+msgid "Unable to read the parameters file \"{0}\", reason: {1}"
msgstr ""
-#: Server/Strings.cs:19
+#: CommandLine/Strings.cs:19
#, csharp-format
msgid ""
-"Unable to start up, perhaps another process is already running?\n"
-"Error message: {0}"
+"Filters cannot be specified on the commandline if filters are also present "
+"in the parameter file. Use the special --{0}, --{1}, or --{2} options to "
+"specify filters inside the parameter file. Each filter must be prefixed with "
+"either a + or a -, and multiple filters must be joined with {3}"
msgstr ""
-#: Server/Strings.cs:21
-msgid "Disables database encryption"
-msgstr ""
-
-#: Server/Strings.cs:22
-#, csharp-format
-msgid "Unsupported version of SQLite detected ({0}), must be {1} or higher"
-msgstr ""
-
-#: Server/Strings.cs:23
-msgid ""
-"The path to the folder where the static files for the webserver is present. "
-"The folder must be located beneath the installation folder"
-msgstr ""
-
-#: Server/Strings.cs:24
-msgid ""
-"The port the webserver listens on. Multiple values may be supplied with a "
-"comma in between."
-msgstr ""
-
-#: Server/Strings.cs:25
-msgid ""
-"The certificate and key file in PKCS #12 format the webserver use for SSL. "
-"Only RSA/DSA keys are supported."
-msgstr ""
-
-#: Server/Strings.cs:26
-msgid "The password for decryption of certificate PKCS #12 file."
-msgstr ""
-
-#: Server/Strings.cs:27
-msgid ""
-"The interface the webserver listens on. The special values \"*\" and \"any\" "
-"means any interface. The special value \"loopback\" means the loopback "
-"adapter."
-msgstr ""
-
-#: Server/Strings.cs:28
-msgid ""
-"The password required to access the webserver. This option is saved so you "
-"do not need to set it on each run. Setting an empty value disables the "
-"password."
-msgstr ""
-
-#: Server/Strings.cs:29
-msgid "Enables the ping-pong responder"
-msgstr ""
-
-#: Server/Strings.cs:30
-msgid ""
-"When running as a server, the service daemon must verify that the process is "
-"responding. If this option is enabled, the server reads stdin and writes a "
-"reply to each line read"
-msgstr ""
-
-#: Server/Strings.cs:33
-msgid "Sets the folder where settings are stored"
-msgstr ""
-
-#: Server/Strings.cs:34
+#: CommandLine/Strings.cs:20
#, csharp-format
msgid ""
-"Duplicati needs to store a small database with all settings. Use this option "
-"to choose where the settings are stored. This option can also be set with "
-"the environment variable {0}."
+"The option --{0} was supplied, but it is reserved for internal use and may "
+"not be set on the commandline"
msgstr ""
-#: Server/Strings.cs:35
-msgid "Sets the database encryption key"
-msgstr ""
-
-#: Server/Strings.cs:36
+#: CommandLine/Strings.cs:21
#, csharp-format
msgid ""
-"This option sets the encryption key used to scramble the local settings "
-"database. This option can also be set with the environment variable {0}. Use "
-"the option --{1} to disable the database scrambling."
+"This option can be used to store some or all of the options given to the "
+"commandline client. The file must be a plain text file, UTF-8 encoding is "
+"preferred. Each line in the file should be of the format --option=value. The "
+"special options --{0} and --{1} can be used to override the localpath and "
+"the remote destination uri, respectively. The options in this file take "
+"precedence over the options provided on the commandline. You cannot specify "
+"filters in both the file and on the commandline. Instead, you can use the "
+"special --{2}, --{3}, or --{4} options to specify filters inside the "
+"parameter file. Each filter must be prefixed with either a + or a -, and "
+"multiple filters must be joined with {5} "
msgstr ""
-#: Server/Strings.cs:39
+#: CommandLine/Strings.cs:22
+msgid "Path to a file with parameters"
+msgstr ""
+
+#: CommandLine/Strings.cs:23
#, csharp-format
+msgid "An error occured: {0}"
+msgstr ""
+
+#: CommandLine/Strings.cs:24
+#, csharp-format
+msgid "The inner error message is: {0}"
+msgstr ""
+
+#: CommandLine/Strings.cs:25
msgid ""
-"Unable to find a valid date, given the start date {0}, the repetition "
-"interval {1} and the allowed days {2}"
+"Include files that match this filter. The special character * means any "
+"number of character, and the special character ? means any single character, "
+"use *.txt to include all files with a txt extension. Regular expressions are "
+"also supported and can be supplied by using hard braces, i.e. [.*\\.txt]."
msgstr ""
-#: Server/Strings.cs:44
-#, csharp-format
-msgid "Server has started and is listening on {0}, port {1}"
+#: CommandLine/Strings.cs:26
+msgid "Include files"
msgstr ""
-#: Server/Strings.cs:45
-#, csharp-format
+#: CommandLine/Strings.cs:27
msgid ""
-"Unable to create SSL certificate using provided parameters. Exception "
-"detail: {0}"
+"Exclude files that match this filter. The special character * means any "
+"number of character, and the special character ? means any single character, "
+"use *.txt to exclude all files with a txt extension. Regular expressions are "
+"also supported and can be supplied by using hard braces, i.e. [.*\\.txt]."
msgstr ""
-#: Server/Strings.cs:46
-#, csharp-format
-msgid "Unable to open a socket for listening, tried ports: {0}"
+#: CommandLine/Strings.cs:28
+msgid "Exclude files"
+msgstr ""
+
+#: CommandLine/Strings.cs:29
+msgid ""
+"If this option is used with a backup operation, it is interpreted as a list "
+"of files to add to the filesets. When used with list or restore, it will "
+"list or restore the control files instead of the normal files."
+msgstr ""
+
+#: CommandLine/Strings.cs:30
+msgid "Use control files"
+msgstr ""
+
+#: CommandLine/Strings.cs:31
+msgid ""
+"If this option is set, progress reports and other messages that would "
+"normally go to the console will be redirected to the log."
+msgstr ""
+
+#: CommandLine/Strings.cs:32
+msgid "Disable console output"
+msgstr ""
+
+#: CommandLine/Program.cs:302
+msgid "Toggle automatic updates"
+msgstr ""
+
+#: CommandLine/Program.cs:302
+msgid ""
+"Set this option if you prefer to have the commandline version automatically "
+"update"
msgstr ""
diff --git a/Localizations/webroot/localization_webroot.pot b/Localizations/webroot/localization_webroot.pot
index 0f861d1e3..8541bcdac 100644
--- a/Localizations/webroot/localization_webroot.pot
+++ b/Localizations/webroot/localization_webroot.pot
@@ -108,7 +108,7 @@ msgstr ""
msgid "Advanced Options"
msgstr ""
-#: templates/addoredit.html:334
+#: templates/addoredit.html:348
#: templates/edituri.html:28
msgid "Advanced options"
msgstr ""
@@ -117,6 +117,10 @@ msgstr ""
msgid "Advanced:"
msgstr ""
+#: scripts/controllers/EditBackupController.js:22
+msgid "All"
+msgstr ""
+
#: scripts/directives/sourceFolderPicker.js:423
msgid "All Hyper-V Machines"
msgstr ""
@@ -125,7 +129,7 @@ msgstr ""
msgid "All Microsoft SQL Databases"
msgstr ""
-#: templates/settings.html:115
+#: templates/settings.html:116
msgid "All usage reports are sent anonymously and do not contain any personal information. They contain information about hardware and operating system, the type of backend, backup duration, overall size of source data and similar data. They do not contain paths, filenames, usernames, passwords or similar sensitive information."
msgstr ""
@@ -133,7 +137,7 @@ msgstr ""
msgid "Allow remote access (requires restart)"
msgstr ""
-#: templates/addoredit.html:268
+#: templates/addoredit.html:282
msgid "Allowed days"
msgstr ""
@@ -147,7 +151,7 @@ msgid ""
"Are you sure you want the database to point to an existing file?"
msgstr ""
-#: scripts/controllers/EditBackupController.js:399
+#: scripts/controllers/EditBackupController.js:472
msgid ""
"An existing local database for the storage has been found.\n"
"Re-using the database will allow the command-line and server instances to work on the same remote storage.\n"
@@ -155,7 +159,7 @@ msgid ""
" Do you wish to use the existing database?"
msgstr ""
-#: templates/settings.html:103
+#: templates/settings.html:104
msgid "Anonymous usage reports"
msgstr ""
@@ -181,11 +185,11 @@ msgstr ""
msgid "Authentication username"
msgstr ""
-#: scripts/controllers/EditBackupController.js:308
+#: scripts/controllers/EditBackupController.js:381
msgid "Autogenerated passphrase"
msgstr ""
-#: templates/addoredit.html:248
+#: templates/addoredit.html:262
msgid "Automatically run backups."
msgstr ""
@@ -230,7 +234,7 @@ msgstr ""
msgid "Backup:"
msgstr ""
-#: templates/settings.html:80
+#: templates/settings.html:81
msgid "Beta"
msgstr ""
@@ -290,19 +294,19 @@ msgstr ""
msgid "Busy ..."
msgstr ""
-#: templates/settings.html:90
+#: templates/settings.html:91
msgid "Canary"
msgstr ""
#: scripts/controllers/AppController.js:44
#: scripts/controllers/AppController.js:59
-#: scripts/controllers/EditBackupController.js:293
-#: scripts/controllers/EditBackupController.js:308
-#: scripts/controllers/EditBackupController.js:342
-#: scripts/controllers/EditBackupController.js:351
-#: scripts/controllers/EditBackupController.js:378
-#: scripts/controllers/EditBackupController.js:399
-#: scripts/controllers/EditBackupController.js:73
+#: scripts/controllers/EditBackupController.js:366
+#: scripts/controllers/EditBackupController.js:381
+#: scripts/controllers/EditBackupController.js:415
+#: scripts/controllers/EditBackupController.js:424
+#: scripts/controllers/EditBackupController.js:451
+#: scripts/controllers/EditBackupController.js:472
+#: scripts/controllers/EditBackupController.js:82
#: scripts/controllers/LocalDatabaseController.js:72
#: scripts/controllers/LocalDatabaseController.js:88
#: scripts/controllers/RestoreDirectController.js:24
@@ -316,7 +320,7 @@ msgstr ""
#: scripts/services/EditUriBuiltins.js:719
#: templates/delete.html:54
#: templates/export.html:26
-#: templates/settings.html:145
+#: templates/settings.html:146
#: templates/waitarea.html:19
msgid "Cancel"
msgstr ""
@@ -456,7 +460,7 @@ msgstr ""
msgid "Continue"
msgstr ""
-#: scripts/controllers/EditBackupController.js:378
+#: scripts/controllers/EditBackupController.js:451
msgid "Continue without encryption"
msgstr ""
@@ -481,7 +485,7 @@ msgstr ""
msgid "Counting ({{files}} files found, {{size}})"
msgstr ""
-#: templates/settings.html:110
+#: templates/settings.html:111
msgid "Crashes only"
msgstr ""
@@ -556,7 +560,7 @@ msgid "Database ..."
msgstr ""
#: scripts/services/AppUtils.js:90
-#: templates/addoredit.html:314
+#: templates/addoredit.html:328
msgid "Days"
msgstr ""
@@ -564,11 +568,15 @@ msgstr ""
msgid "Default"
msgstr ""
-#: templates/settings.html:67
+#: templates/settings.html:68
msgid "Default ({{channelname}})"
msgstr ""
-#: templates/settings.html:130
+#: templates/addoredit.html:201
+msgid "Default Filters"
+msgstr ""
+
+#: templates/settings.html:131
msgid "Default options"
msgstr ""
@@ -635,7 +643,7 @@ msgstr ""
msgid "Dismiss"
msgstr ""
-#: templates/settings.html:49
+#: templates/settings.html:50
msgid "Display and color theme"
msgstr ""
@@ -662,16 +670,16 @@ msgstr ""
msgid "Donate with PayPal"
msgstr ""
-#: templates/settings.html:57
-#: templates/settings.html:59
+#: templates/settings.html:58
+#: templates/settings.html:60
msgid "Donation messages"
msgstr ""
-#: templates/settings.html:61
+#: templates/settings.html:62
msgid "Donation messages are hidden, click to show"
msgstr ""
-#: templates/settings.html:60
+#: templates/settings.html:61
msgid "Donation messages are visible, click to hide"
msgstr ""
@@ -723,16 +731,16 @@ msgid "Edit ..."
msgstr ""
#: templates/addoredit.html:170
-#: templates/addoredit.html:345
+#: templates/addoredit.html:359
#: templates/edituri.html:39
-#: templates/settings.html:134
+#: templates/settings.html:135
msgid "Edit as list"
msgstr ""
#: templates/addoredit.html:173
-#: templates/addoredit.html:348
+#: templates/addoredit.html:362
#: templates/edituri.html:42
-#: templates/settings.html:140
+#: templates/settings.html:141
msgid "Edit as text"
msgstr ""
@@ -747,7 +755,7 @@ msgstr ""
msgid "Encryption"
msgstr ""
-#: scripts/controllers/EditBackupController.js:351
+#: scripts/controllers/EditBackupController.js:424
msgid "Encryption changed"
msgstr ""
@@ -755,7 +763,7 @@ msgstr ""
msgid "Encryption modules:"
msgstr ""
-#: scripts/controllers/EditBackupController.js:73
+#: scripts/controllers/EditBackupController.js:82
#: scripts/controllers/RestoreDirectController.js:24
msgid "Enter URL"
msgstr ""
@@ -834,11 +842,11 @@ msgstr ""
msgid "Error!"
msgstr ""
-#: templates/settings.html:109
+#: templates/settings.html:110
msgid "Errors and crashes"
msgstr ""
-#: templates/addoredit.html:204
+#: templates/addoredit.html:218
msgid "Exclude"
msgstr ""
@@ -874,7 +882,7 @@ msgstr ""
msgid "Existing file found"
msgstr ""
-#: templates/settings.html:85
+#: templates/settings.html:86
msgid "Experimental"
msgstr ""
@@ -939,7 +947,7 @@ msgstr ""
msgid "Failed to import:"
msgstr ""
-#: scripts/controllers/EditBackupController.js:632
+#: scripts/controllers/EditBackupController.js:737
msgid "Failed to read backup defaults:"
msgstr ""
@@ -960,7 +968,7 @@ msgstr ""
msgid "File"
msgstr ""
-#: templates/addoredit.html:227
+#: templates/addoredit.html:241
msgid "Files larger than:"
msgstr ""
@@ -1021,7 +1029,7 @@ msgstr ""
msgid "General backup settings"
msgstr ""
-#: templates/addoredit.html:295
+#: templates/addoredit.html:309
msgid "General options"
msgstr ""
@@ -1037,7 +1045,7 @@ msgstr ""
msgid "Getting file versions ..."
msgstr ""
-#: scripts/controllers/EditBackupController.js:17
+#: scripts/controllers/EditBackupController.js:26
msgid "Hidden files"
msgstr ""
@@ -1057,7 +1065,7 @@ msgstr ""
#: scripts/services/AppUtils.js:89
#: scripts/services/AppUtils.js:99
-#: templates/settings.html:33
+#: templates/settings.html:34
msgid "Hours"
msgstr ""
@@ -1083,7 +1091,7 @@ msgstr ""
msgid "ID:"
msgstr ""
-#: templates/addoredit.html:251
+#: templates/addoredit.html:265
msgid "If a date was missed, the job will run as soon as possible."
msgstr ""
@@ -1148,7 +1156,7 @@ msgstr ""
msgid "Importing ..."
msgstr ""
-#: scripts/controllers/EditBackupController.js:141
+#: scripts/controllers/EditBackupController.js:150
msgid "Include a file?"
msgstr ""
@@ -1164,7 +1172,7 @@ msgstr ""
msgid "Incorrect answer, try again"
msgstr ""
-#: templates/settings.html:91
+#: templates/settings.html:92
msgid "Individual builds for developers only."
msgstr ""
@@ -1185,8 +1193,8 @@ msgstr ""
msgid "Invalid characters in path"
msgstr ""
-#: scripts/controllers/EditBackupController.js:250
-#: scripts/controllers/EditBackupController.js:257
+#: scripts/controllers/EditBackupController.js:323
+#: scripts/controllers/EditBackupController.js:330
msgid "Invalid retention time"
msgstr ""
@@ -1204,11 +1212,11 @@ msgstr ""
msgid "KByte/s"
msgstr ""
-#: templates/addoredit.html:303
+#: templates/addoredit.html:317
msgid "Keep this number of backups"
msgstr ""
-#: templates/settings.html:39
+#: templates/settings.html:40
msgid "Language in user interface"
msgstr ""
@@ -1228,6 +1236,10 @@ msgstr ""
msgid "Libraries"
msgstr ""
+#: scripts/controllers/EditBackupController.js:21
+msgid "Linux"
+msgstr ""
+
#: scripts/controllers/RestoreDirectController.js:64
msgid "Listing backup dates ..."
msgstr ""
@@ -1333,7 +1345,7 @@ msgstr ""
#: index.html:152
#: templates/addoredit.html:123
#: templates/addoredit.html:165
-#: templates/addoredit.html:340
+#: templates/addoredit.html:354
#: templates/addoredit.html:91
#: templates/edituri.html:34
#: templates/restoredirect.html:34
@@ -1358,19 +1370,19 @@ msgstr ""
#: scripts/services/AppUtils.js:88
#: scripts/services/AppUtils.js:98
-#: templates/settings.html:32
+#: templates/settings.html:33
msgid "Minutes"
msgstr ""
-#: scripts/controllers/EditBackupController.js:217
+#: scripts/controllers/EditBackupController.js:290
msgid "Missing name"
msgstr ""
-#: scripts/controllers/EditBackupController.js:225
+#: scripts/controllers/EditBackupController.js:298
msgid "Missing passphrase"
msgstr ""
-#: scripts/controllers/EditBackupController.js:238
+#: scripts/controllers/EditBackupController.js:311
msgid "Missing sources"
msgstr ""
@@ -1379,7 +1391,7 @@ msgid "Mon"
msgstr ""
#: scripts/services/AppUtils.js:92
-#: templates/addoredit.html:316
+#: templates/addoredit.html:330
msgid "Months"
msgstr ""
@@ -1426,8 +1438,8 @@ msgid ""
msgstr ""
#: templates/addoredit.html:109
-#: templates/addoredit.html:236
-#: templates/addoredit.html:286
+#: templates/addoredit.html:250
+#: templates/addoredit.html:300
#: templates/addoredit.html:78
#: templates/addwizard.html:28
#: templates/restoredirect.html:52
@@ -1447,14 +1459,14 @@ msgstr ""
msgid "Next task:"
msgstr ""
-#: templates/addoredit.html:254
+#: templates/addoredit.html:268
msgid "Next time"
msgstr ""
#: scripts/controllers/DeleteController.js:77
-#: scripts/controllers/EditBackupController.js:131
-#: scripts/controllers/EditBackupController.js:141
-#: scripts/controllers/EditBackupController.js:399
+#: scripts/controllers/EditBackupController.js:140
+#: scripts/controllers/EditBackupController.js:150
+#: scripts/controllers/EditBackupController.js:472
#: scripts/controllers/HomeController.js:7
#: scripts/controllers/LocalDatabaseController.js:28
#: scripts/controllers/LocalDatabaseController.js:72
@@ -1481,7 +1493,7 @@ msgstr ""
msgid "No editor found for the "{{backend}}" storage type"
msgstr ""
-#: scripts/controllers/EditBackupController.js:378
+#: scripts/controllers/EditBackupController.js:451
#: templates/addoredit.html:45
msgid "No encryption"
msgstr ""
@@ -1506,18 +1518,18 @@ msgstr ""
msgid "No, my machine has only a single account"
msgstr ""
-#: scripts/controllers/EditBackupController.js:231
+#: scripts/controllers/EditBackupController.js:304
msgid "Non-matching passphrase"
msgstr ""
-#: templates/settings.html:111
+#: templates/settings.html:112
msgid "None / disabled"
msgstr ""
#: scripts/controllers/AppController.js:44
#: scripts/controllers/AppController.js:59
-#: scripts/controllers/EditBackupController.js:73
-#: scripts/controllers/EditBackupController.js:81
+#: scripts/controllers/EditBackupController.js:82
+#: scripts/controllers/EditBackupController.js:90
#: scripts/controllers/RestoreDirectController.js:24
#: scripts/controllers/RestoreDirectController.js:33
#: scripts/services/CaptchaService.js:22
@@ -1526,10 +1538,14 @@ msgstr ""
#: scripts/services/DialogService.js:58
#: scripts/services/EditUriBuiltins.js:121
#: templates/restore.html:147
-#: templates/settings.html:146
+#: templates/settings.html:147
msgid "OK"
msgstr ""
+#: scripts/controllers/EditBackupController.js:20
+msgid "OSX"
+msgstr ""
+
#: templates/backends/openstack.html:7
msgid "OpenStack AuthURI"
msgstr ""
@@ -1557,12 +1573,12 @@ msgstr ""
#: templates/addoredit.html:28
#: templates/edituri.html:51
-#: templates/settings.html:133
-#: templates/settings.html:139
+#: templates/settings.html:134
+#: templates/settings.html:140
msgid "Options"
msgstr ""
-#: templates/settings.html:131
+#: templates/settings.html:132
msgid "Options added here are applied to all backups, but can be overridden in each individual backup"
msgstr ""
@@ -1588,11 +1604,11 @@ msgstr ""
msgid "Passphrase (if encrypted)"
msgstr ""
-#: scripts/controllers/EditBackupController.js:342
+#: scripts/controllers/EditBackupController.js:415
msgid "Passphrase changed"
msgstr ""
-#: scripts/controllers/EditBackupController.js:231
+#: scripts/controllers/EditBackupController.js:304
msgid "Passphrases are not matching"
msgstr ""
@@ -1609,7 +1625,7 @@ msgstr ""
msgid "Password"
msgstr ""
-#: scripts/controllers/EditBackupController.js:27
+#: scripts/controllers/EditBackupController.js:36
msgid "Passwords do not match"
msgstr ""
@@ -1617,7 +1633,7 @@ msgstr ""
msgid "Patching files with local blocks ..."
msgstr ""
-#: scripts/controllers/EditBackupController.js:131
+#: scripts/controllers/EditBackupController.js:140
msgid "Path not found"
msgstr ""
@@ -1660,9 +1676,9 @@ msgid "Port"
msgstr ""
#: templates/addoredit.html:110
-#: templates/addoredit.html:237
-#: templates/addoredit.html:287
-#: templates/addoredit.html:367
+#: templates/addoredit.html:251
+#: templates/addoredit.html:301
+#: templates/addoredit.html:381
#: templates/restoredirect.html:81
msgid "Previous"
msgstr ""
@@ -1695,7 +1711,7 @@ msgstr ""
msgid "Registering temporary backup ..."
msgstr ""
-#: scripts/controllers/EditBackupController.js:116
+#: scripts/controllers/EditBackupController.js:125
msgid "Relative paths not allowed"
msgstr ""
@@ -1784,7 +1800,7 @@ msgstr ""
msgid "Resume"
msgstr ""
-#: templates/addoredit.html:259
+#: templates/addoredit.html:273
msgid "Run again every"
msgstr ""
@@ -1813,7 +1829,7 @@ msgstr ""
msgid "S3 Compatible"
msgstr ""
-#: templates/settings.html:68
+#: templates/settings.html:69
msgid "Same as the base install version: {{channelname}}"
msgstr ""
@@ -1821,7 +1837,7 @@ msgstr ""
msgid "Sat"
msgstr ""
-#: templates/addoredit.html:366
+#: templates/addoredit.html:380
#: templates/localdatabase.html:32
msgid "Save"
msgstr ""
@@ -1846,7 +1862,7 @@ msgstr ""
msgid "Scanning for local blocks ..."
msgstr ""
-#: templates/addoredit.html:245
+#: templates/addoredit.html:259
#: templates/addoredit.html:27
msgid "Schedule"
msgstr ""
@@ -1860,7 +1876,7 @@ msgid "Search for files"
msgstr ""
#: scripts/services/AppUtils.js:97
-#: templates/settings.html:31
+#: templates/settings.html:32
msgid "Seconds"
msgstr ""
@@ -1968,7 +1984,7 @@ msgstr ""
msgid "Source:"
msgstr ""
-#: templates/settings.html:86
+#: templates/settings.html:87
msgid "Specific builds for developers only."
msgstr ""
@@ -2031,7 +2047,7 @@ msgstr ""
msgid "Stored"
msgstr ""
-#: scripts/controllers/EditBackupController.js:31
+#: scripts/controllers/EditBackupController.js:40
msgid "Strong"
msgstr ""
@@ -2048,11 +2064,11 @@ msgstr ""
msgid "Symbolic link"
msgstr ""
-#: templates/settings.html:106
+#: templates/settings.html:107
msgid "System default ({{levelname}})"
msgstr ""
-#: scripts/controllers/EditBackupController.js:18
+#: scripts/controllers/EditBackupController.js:27
msgid "System files"
msgstr ""
@@ -2080,7 +2096,7 @@ msgstr ""
msgid "Task is running"
msgstr ""
-#: scripts/controllers/EditBackupController.js:19
+#: scripts/controllers/EditBackupController.js:28
msgid "Temporary files"
msgstr ""
@@ -2121,11 +2137,11 @@ msgstr ""
msgid "The connection to the server is lost, attempting again in {{time}} ..."
msgstr ""
-#: templates/settings.html:53
+#: templates/settings.html:54
msgid "The dark theme (by Michal)"
msgstr ""
-#: templates/settings.html:52
+#: templates/settings.html:53
msgid "The default blue on white theme (by Alex)"
msgstr ""
@@ -2142,18 +2158,18 @@ msgid ""
"Do you want to REPLACE your CURRENT host key \"{{prev}}\" with the REPORTED host key: {{key}}?"
msgstr ""
-#: scripts/controllers/EditBackupController.js:131
+#: scripts/controllers/EditBackupController.js:140
msgid "The path does not appear to exist, do you want to add it anyway?"
msgstr ""
-#: scripts/controllers/EditBackupController.js:141
+#: scripts/controllers/EditBackupController.js:150
msgid ""
"The path does not end with a '{{dirsep}}' character, which means that you include a file, not a folder.\n"
"\n"
"Do you want to include the specified file?"
msgstr ""
-#: scripts/controllers/EditBackupController.js:116
+#: scripts/controllers/EditBackupController.js:125
msgid "The path must be an absolute path, i.e. it must start with a forward slash '/'"
msgstr ""
@@ -2234,7 +2250,7 @@ msgstr ""
msgid "Trust server certificate?"
msgstr ""
-#: templates/settings.html:81
+#: templates/settings.html:82
msgid "Try out the new features we are working on. Don't use with important data."
msgstr ""
@@ -2254,7 +2270,7 @@ msgstr ""
msgid "Until resumed"
msgstr ""
-#: templates/settings.html:64
+#: templates/settings.html:65
msgid "Update channel"
msgstr ""
@@ -2266,7 +2282,7 @@ msgstr ""
msgid "Updating with existing database"
msgstr ""
-#: templates/addoredit.html:297
+#: templates/addoredit.html:311
msgid "Upload volume size"
msgstr ""
@@ -2274,15 +2290,15 @@ msgstr ""
msgid "Uploading verification file ..."
msgstr ""
-#: templates/settings.html:113
+#: templates/settings.html:114
msgid "Usage reports help us improve the user experience and evaluate impact of new features. We use them to generate public usage statistics"
msgstr ""
-#: templates/settings.html:101
+#: templates/settings.html:102
msgid "Usage statistics"
msgstr ""
-#: templates/settings.html:107
+#: templates/settings.html:108
msgid "Usage statistics, warnings, errors, and crashes"
msgstr ""
@@ -2291,15 +2307,15 @@ msgstr ""
msgid "Use SSL"
msgstr ""
-#: scripts/controllers/EditBackupController.js:399
+#: scripts/controllers/EditBackupController.js:472
msgid "Use existing database?"
msgstr ""
-#: scripts/controllers/EditBackupController.js:293
+#: scripts/controllers/EditBackupController.js:366
msgid "Use weak passphrase"
msgstr ""
-#: scripts/controllers/EditBackupController.js:28
+#: scripts/controllers/EditBackupController.js:37
msgid "Useless"
msgstr ""
@@ -2311,7 +2327,7 @@ msgstr ""
msgid "User has too many permissions"
msgstr ""
-#: templates/settings.html:37
+#: templates/settings.html:38
msgid "User interface settings"
msgstr ""
@@ -2365,11 +2381,11 @@ msgstr ""
msgid "Verifying restored files ..."
msgstr ""
-#: scripts/controllers/EditBackupController.js:32
+#: scripts/controllers/EditBackupController.js:41
msgid "Very strong"
msgstr ""
-#: scripts/controllers/EditBackupController.js:29
+#: scripts/controllers/EditBackupController.js:38
msgid "Very weak"
msgstr ""
@@ -2397,7 +2413,7 @@ msgstr ""
msgid "Waiting for upload ..."
msgstr ""
-#: templates/settings.html:108
+#: templates/settings.html:109
msgid "Warnings, errors and crashes"
msgstr ""
@@ -2405,11 +2421,11 @@ msgstr ""
msgid "We recommend that you encrypt all backups stored outside your system"
msgstr ""
-#: scripts/controllers/EditBackupController.js:30
+#: scripts/controllers/EditBackupController.js:39
msgid "Weak"
msgstr ""
-#: scripts/controllers/EditBackupController.js:293
+#: scripts/controllers/EditBackupController.js:366
msgid "Weak passphrase"
msgstr ""
@@ -2418,7 +2434,7 @@ msgid "Wed"
msgstr ""
#: scripts/services/AppUtils.js:91
-#: templates/addoredit.html:315
+#: templates/addoredit.html:329
msgid "Weeks"
msgstr ""
@@ -2430,16 +2446,20 @@ msgstr ""
msgid "Where do you want to restore the files to?"
msgstr ""
+#: scripts/controllers/EditBackupController.js:19
+msgid "Windows"
+msgstr ""
+
#: scripts/services/AppUtils.js:93
-#: templates/addoredit.html:317
+#: templates/addoredit.html:331
msgid "Years"
msgstr ""
#: scripts/controllers/AppController.js:172
#: scripts/controllers/DeleteController.js:77
-#: scripts/controllers/EditBackupController.js:131
-#: scripts/controllers/EditBackupController.js:141
-#: scripts/controllers/EditBackupController.js:399
+#: scripts/controllers/EditBackupController.js:140
+#: scripts/controllers/EditBackupController.js:150
+#: scripts/controllers/EditBackupController.js:472
#: scripts/controllers/HomeController.js:7
#: scripts/controllers/LocalDatabaseController.js:28
#: scripts/controllers/LocalDatabaseController.js:72
@@ -2455,15 +2475,15 @@ msgstr ""
msgid "Yes"
msgstr ""
-#: scripts/controllers/EditBackupController.js:308
+#: scripts/controllers/EditBackupController.js:381
msgid "Yes, I have stored the passphrase safely"
msgstr ""
-#: scripts/controllers/EditBackupController.js:351
+#: scripts/controllers/EditBackupController.js:424
msgid "Yes, I'm brave!"
msgstr ""
-#: scripts/controllers/EditBackupController.js:342
+#: scripts/controllers/EditBackupController.js:415
msgid "Yes, please break my backup!"
msgstr ""
@@ -2495,15 +2515,15 @@ msgstr ""
msgid "You can stop the task immediately, or allow the process to continue its current file and the stop."
msgstr ""
-#: scripts/controllers/EditBackupController.js:351
+#: scripts/controllers/EditBackupController.js:424
msgid "You have changed the encryption mode. This may break stuff. You are encouraged to create a new backup instead"
msgstr ""
-#: scripts/controllers/EditBackupController.js:342
+#: scripts/controllers/EditBackupController.js:415
msgid "You have changed the passphrase, which is not supported. You are encouraged to create a new backup instead."
msgstr ""
-#: scripts/controllers/EditBackupController.js:378
+#: scripts/controllers/EditBackupController.js:451
msgid "You have chosen not to encrypt the backup. Encryption is recommended for all data stored on a remote server."
msgstr ""
@@ -2511,23 +2531,23 @@ msgstr ""
msgid "You have chosen to restore to a new location, but not entered one"
msgstr ""
-#: scripts/controllers/EditBackupController.js:308
-msgid "You have generated a strong passphrase. Make sure you have made a safe copy of the passphrase, as the data cannot be recovered if you loose the passphrase."
+#: scripts/controllers/EditBackupController.js:381
+msgid "You have generated a strong passphrase. Make sure you have made a safe copy of the passphrase, as the data cannot be recovered if you lose the passphrase."
msgstr ""
-#: scripts/controllers/EditBackupController.js:238
+#: scripts/controllers/EditBackupController.js:311
msgid "You must choose at least one source folder"
msgstr ""
-#: scripts/controllers/EditBackupController.js:217
+#: scripts/controllers/EditBackupController.js:290
msgid "You must enter a name for the backup"
msgstr ""
-#: scripts/controllers/EditBackupController.js:225
+#: scripts/controllers/EditBackupController.js:298
msgid "You must enter a passphrase or disable encryption"
msgstr ""
-#: scripts/controllers/EditBackupController.js:257
+#: scripts/controllers/EditBackupController.js:330
msgid "You must enter a positive number of backups to keep"
msgstr ""
@@ -2535,7 +2555,7 @@ msgstr ""
msgid "You must enter a tenant name if you do not provide an API Key"
msgstr ""
-#: scripts/controllers/EditBackupController.js:250
+#: scripts/controllers/EditBackupController.js:323
msgid "You must enter a valid duration for the time to keep backups"
msgstr ""
@@ -2556,6 +2576,7 @@ msgid "You must fill in the server name or address"
msgstr ""
#: scripts/services/EditUriBackendConfig.js:106
+#: scripts/services/EditUriBackendConfig.js:115
msgid "You must fill in the username"
msgstr ""
@@ -2579,11 +2600,11 @@ msgstr ""
msgid "Your files and folders have been restored successfully."
msgstr ""
-#: scripts/controllers/EditBackupController.js:293
+#: scripts/controllers/EditBackupController.js:366
msgid "Your passphrase is easy to guess. Consider changing passphrase."
msgstr ""
-#: templates/addoredit.html:307
+#: templates/addoredit.html:321
msgid "a specific number"
msgstr ""
@@ -2600,8 +2621,8 @@ msgstr ""
msgid "byte/s"
msgstr ""
-#: templates/addoredit.html:262
-#: templates/addoredit.html:318
+#: templates/addoredit.html:276
+#: templates/addoredit.html:332
#: templates/advancedoptionseditor.html:28
#: templates/advancedoptionseditor.html:35
msgid "custom"
@@ -2613,11 +2634,11 @@ msgstr ""
msgid "resume now"
msgstr ""
-#: templates/addoredit.html:305
+#: templates/addoredit.html:319
msgid "unlimited"
msgstr ""
-#: templates/addoredit.html:306
+#: templates/addoredit.html:320
msgid "until they are older than"
msgstr ""
diff --git a/README.md b/README.md
index 4bc771c1a..6cd8b1c36 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ If you are looking for an issue to fix, try looking at one of the [minor change]
Thank you to all our existing contributors:
-
+
## Backers
diff --git a/Updates/build_version.txt b/Updates/build_version.txt
index 48082f72f..60d3b2f4a 100644
--- a/Updates/build_version.txt
+++ b/Updates/build_version.txt
@@ -1 +1 @@
-12
+15
diff --git a/changelog.txt b/changelog.txt
index 0a1d18a38..ae1e57fab 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,47 @@
+2018-01-03 - 2.0.2.15_experimental_2018-01-03
+==========
+This is the same as build 2.0.2.14 canary
+
+Overview of major and breaking changes since 2.0.1.73 experimental:
+* Fixed not running the slow VACUUM command after each operation
+* Improved S3 to work better with non AWS hosts
+* Updated backend support libraries to the latest versions: Azure, AWS, SSH, SharpCompress, Mega, AlphaFS
+* Fixed a problem with SSL certificates periodically failing
+* Added a Sia backend
+* Added a number of default excludes
+* Updated the hashing library to use faster native libraries where available
+* Added a new retention policy which allows backup versions to decrease over time
+* Updated the auto-updater, such that new installs will spawn an extra process instead of using the AppDomain method as previously done.
+* Faster browsing of files to restore
+* Removed automatic tilde expansions as it created numerous problems
+* Multiple new languages added to the UI
+
+2017-12-22 - 2.0.2.14_canary_2017-12-22
+==========
+* Multiple code quality fixes, thansk @warwickmm
+* Fixed a case where commandline arguments ending with a backslash were not correctly escaped on Windows
+
+2017-11-22 - 2.0.2.13_canary_2017-11-22
+==========
+* Fixed an issue with AutoUpdater semi-ignoring webroot
+* Fixed an issue where source folders would be omitted from the backup if a file with the same leading name was also added
+* Switched to default "keep forever", thanks @ItEndsWithTens
+* Fixed a startup crash caused by invalid throttle values
+* Added a number of new languages, thanks to all translators!
+* Timestamps in the log are now reported in local time and include a UTC timestamp for easy script parsing
+* Added Windows registry exclusion list to the default exclude filters
+* Added .dv files to compression exclusion list, thanks @jesjimher
+* Fixed some spelling in the UI, thanks @davegold24 and @JonMikeIV
+* Fixed logic of the `affected` command to not return empty results if the local database was not found
+* Fixed a case where the "Resume" button did not appear clickable, thanks @JonMikeIV
+* Improved query time for browsing folders during restore, thanks @RobSauro
+* Commands are now sorted alphabetically, thanks @JonMikeIV
+* Improved an error message when the SSH key failed to parse
+* Removed tilde expansion to fix multiple issues caused by unexpected expansio, thanks @warwickmm
+* Fixed .rbf files to only be excluded if they are part of the `config.msi` folder
+* Improved the commandline export to show commandlines that are correctly escaped for the shell
+* Fixed a case where the AutoUpdater would fail to send the correct commandline to the updated process
+
2017-10-20 - 2.0.2.12_canary_2017-10-20
==========
* Fixed not reporting "Finished" until the backup is actually finished, thanks @warwickmm