diff --git a/Duplicati Console.sln b/Duplicati Console.sln deleted file mode 100644 index ad3489288..000000000 --- a/Duplicati Console.sln +++ /dev/null @@ -1,96 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Main", "Duplicati\Library\Main\Duplicati.Library.Main.csproj", "{10D2D1B7-C664-41D8-9B3A-00040C3D421B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine", "Duplicati\CommandLine\Duplicati.CommandLine.csproj", "{81765A64-3661-4E3E-B850-2F6F87A51F74}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.BackendInterface", "Duplicati\Library\Backend\BackendInterface\Duplicati.Library.Backend.BackendInterface.csproj", "{994F3D0A-E91E-4EEE-80EE-064485E09909}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.BackendLoader", "Duplicati\Library\Backend\BackendLoader\Duplicati.Library.Backend.BackendLoader.csproj", "{0CA86ECF-5BEC-4909-B4F6-110A03B30B92}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.File", "Duplicati\Library\Backend\File\Duplicati.Library.Backend.File.csproj", "{FC9B7611-836F-4127-8B44-A7C31F506807}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Core", "Duplicati\Library\Core\Duplicati.Library.Core.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Compression", "Duplicati\Library\Compression\Duplicati.Library.Compression.csproj", "{19ECCE09-B5EB-406C-8C57-BAC66997D469}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.SharpRSync", "Duplicati\Library\SharpRSync\Duplicati.Library.SharpRSync.csproj", "{47FE73E1-0832-44F7-96B9-2580273206C3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Logging", "Duplicati\Library\Logging\Duplicati.Library.Logging.csproj", "{D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{94484FDB-2EFA-4CF0-9BE6-A561157B4F87}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.FTP", "Duplicati\Library\Backend\FTP\Duplicati.Library.Backend.FTP.csproj", "{F61679A9-E5DE-468A-B5A4-05F92D0143D2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.SSH", "Duplicati\Library\Backend\SSH\Duplicati.Library.Backend.SSH.csproj", "{6BA21DCB-1701-4936-B177-3B6D4DE3474E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.S3", "Duplicati\Library\Backend\S3\Duplicati.Library.Backend.S3.csproj", "{C03F6DFD-805A-4BE0-9338-64870ADDB4A2}" -EndProject -Global - GlobalSection(SubversionScc) = preSolution - Svn-Managed = True - Manager = AnkhSVN - Subversion Support for Visual Studio - EndGlobalSection - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Release|Any CPU.Build.0 = Release|Any CPU - {81765A64-3661-4E3E-B850-2F6F87A51F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {81765A64-3661-4E3E-B850-2F6F87A51F74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {81765A64-3661-4E3E-B850-2F6F87A51F74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {81765A64-3661-4E3E-B850-2F6F87A51F74}.Release|Any CPU.Build.0 = Release|Any CPU - {994F3D0A-E91E-4EEE-80EE-064485E09909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {994F3D0A-E91E-4EEE-80EE-064485E09909}.Debug|Any CPU.Build.0 = Debug|Any CPU - {994F3D0A-E91E-4EEE-80EE-064485E09909}.Release|Any CPU.ActiveCfg = Release|Any CPU - {994F3D0A-E91E-4EEE-80EE-064485E09909}.Release|Any CPU.Build.0 = Release|Any CPU - {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Release|Any CPU.Build.0 = Release|Any CPU - {FC9B7611-836F-4127-8B44-A7C31F506807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC9B7611-836F-4127-8B44-A7C31F506807}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC9B7611-836F-4127-8B44-A7C31F506807}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC9B7611-836F-4127-8B44-A7C31F506807}.Release|Any CPU.Build.0 = Release|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.Build.0 = Release|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.Build.0 = Debug|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.ActiveCfg = Release|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.Build.0 = Release|Any CPU - {47FE73E1-0832-44F7-96B9-2580273206C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47FE73E1-0832-44F7-96B9-2580273206C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47FE73E1-0832-44F7-96B9-2580273206C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47FE73E1-0832-44F7-96B9-2580273206C3}.Release|Any CPU.Build.0 = Release|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.Build.0 = Release|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.Build.0 = Debug|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.ActiveCfg = Release|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.Build.0 = Release|Any CPU - {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Release|Any CPU.Build.0 = Release|Any CPU - {6BA21DCB-1701-4936-B177-3B6D4DE3474E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BA21DCB-1701-4936-B177-3B6D4DE3474E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BA21DCB-1701-4936-B177-3B6D4DE3474E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BA21DCB-1701-4936-B177-3B6D4DE3474E}.Release|Any CPU.Build.0 = Release|Any CPU - {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/Duplicati.sln b/Duplicati.sln index 13f14c3cb..f31a7a4c6 100644 --- a/Duplicati.sln +++ b/Duplicati.sln @@ -33,6 +33,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightDatamodel", "..\LightDatamodel\LightDatamodel\LightDatamodel.csproj", "{CAE8433B-F013-474E-B7FC-4DE0D5933AD1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine", "Duplicati\CommandLine\Duplicati.CommandLine.csproj", "{81765A64-3661-4E3E-B850-2F6F87A51F74}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.Decrypter", "Duplicati\CommandLine\Decrypter\Duplicati.CommandLine.Decrypter.csproj", "{E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F}" +EndProject Global GlobalSection(SubversionScc) = preSolution Svn-Managed = True @@ -140,6 +144,18 @@ Global {CAE8433B-F013-474E-B7FC-4DE0D5933AD1}.Release|Any CPU.Build.0 = Release|Any CPU {CAE8433B-F013-474E-B7FC-4DE0D5933AD1}.Test|Any CPU.ActiveCfg = Test|Any CPU {CAE8433B-F013-474E-B7FC-4DE0D5933AD1}.Test|Any CPU.Build.0 = Test|Any CPU + {81765A64-3661-4E3E-B850-2F6F87A51F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81765A64-3661-4E3E-B850-2F6F87A51F74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81765A64-3661-4E3E-B850-2F6F87A51F74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81765A64-3661-4E3E-B850-2F6F87A51F74}.Release|Any CPU.Build.0 = Release|Any CPU + {81765A64-3661-4E3E-B850-2F6F87A51F74}.Test|Any CPU.ActiveCfg = Release|Any CPU + {81765A64-3661-4E3E-B850-2F6F87A51F74}.Test|Any CPU.Build.0 = Release|Any CPU + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F}.Release|Any CPU.Build.0 = Release|Any CPU + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F}.Test|Any CPU.ActiveCfg = Release|Any CPU + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F}.Test|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Duplicati/CommandLine/Checkout DSMCBE Data.bat b/Duplicati/CommandLine/Checkout DSMCBE Data.bat index 136a3c2c4..c00adac08 100644 --- a/Duplicati/CommandLine/Checkout DSMCBE Data.bat +++ b/Duplicati/CommandLine/Checkout DSMCBE Data.bat @@ -3,4 +3,14 @@ "%ProgramFiles%\CollabNet Subversion\svn" checkout -r 150 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r150 "%ProgramFiles%\CollabNet Subversion\svn" checkout -r 200 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r200 "%ProgramFiles%\CollabNet Subversion\svn" checkout -r 250 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 251 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 252 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 253 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 254 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 255 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 256 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 257 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 258 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 259 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 +"%ProgramFiles%\CollabNet Subversion\svn" checkout -r 260 http://dsmcbe.googlecode.com/svn/trunk/ bin\Debug\DSMCBE\r250 pause \ No newline at end of file diff --git a/Duplicati/CommandLine/Decrypter/Duplicati.CommandLine.Decrypter.csproj b/Duplicati/CommandLine/Decrypter/Duplicati.CommandLine.Decrypter.csproj new file mode 100644 index 000000000..3788fa45c --- /dev/null +++ b/Duplicati/CommandLine/Decrypter/Duplicati.CommandLine.Decrypter.csproj @@ -0,0 +1,53 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F} + Exe + Properties + Duplicati.CommandLine.Decrypter + Duplicati.CommandLine.Decrypter + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + {94484FDB-2EFA-4CF0-9BE6-A561157B4F87} + Duplicati.Library.Encryption + + + + + \ No newline at end of file diff --git a/Duplicati/CommandLine/Decrypter/Program.cs b/Duplicati/CommandLine/Decrypter/Program.cs new file mode 100644 index 000000000..568166cce --- /dev/null +++ b/Duplicati/CommandLine/Decrypter/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Duplicati.Library.Encryption; + +namespace Duplicati.CommandLine.Decrypter +{ + class Program + { + static void Main(string[] args) + { + if (args.Length == 3) + { + Console.WriteLine("Usage: Duplicati.CommandLine.Decrypter.exe "); + return; + } + + AESEncryption c = new AESEncryption(args[0]); + c.Decrypt(args[1], args[2]); + } + } +} diff --git a/Duplicati/CommandLine/Decrypter/Properties/AssemblyInfo.cs b/Duplicati/CommandLine/Decrypter/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..1d564d412 --- /dev/null +++ b/Duplicati/CommandLine/Decrypter/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Duplicati.CommandLine.Decrypter")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("GEOGRAF A/S")] +[assembly: AssemblyProduct("Duplicati.CommandLine.Decrypter")] +[assembly: AssemblyCopyright("Copyright © GEOGRAF A/S 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("31de2d8c-9bab-4357-99c6-6a99058928a2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Duplicati/CommandLine/Run Unittest on DSMCBE data.bat b/Duplicati/CommandLine/Run Unittest on DSMCBE data.bat index 422a53754..2c574ee24 100644 --- a/Duplicati/CommandLine/Run Unittest on DSMCBE data.bat +++ b/Duplicati/CommandLine/Run Unittest on DSMCBE data.bat @@ -1,3 +1,3 @@ cd bin\Debug -Duplicati.CommandLine.exe UNITTEST DSMCBE\r50 DSMCBE\r100 DSMCBE\r150 DSMCBE\r200 DSMCBE\r250 +Duplicati.CommandLine.exe UNITTEST DSMCBE\r50 DSMCBE\r100 DSMCBE\r150 DSMCBE\r200 DSMCBE\r250 DSMCBE\r251 DSMCBE\r252 DSMCBE\r253 DSMCBE\r254 DSMCBE\r255 DSMCBE\r256 DSMCBE\r257 DSMCBE\r258 DSMCBE\r259 DSMCBE\r260 pause \ No newline at end of file diff --git a/Duplicati/CommandLine/UnitTest.cs b/Duplicati/CommandLine/UnitTest.cs index c344ea383..febce3a86 100644 --- a/Duplicati/CommandLine/UnitTest.cs +++ b/Duplicati/CommandLine/UnitTest.cs @@ -63,10 +63,18 @@ namespace Duplicati.CommandLine Log.LogLevel = Duplicati.Library.Logging.LogMessageType.Profiling; string tempdir = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "tempdir"); - if (System.IO.Directory.Exists(tempdir)) - System.IO.Directory.Delete(tempdir, true); - System.IO.Directory.CreateDirectory(tempdir); + try + { + if (System.IO.Directory.Exists(tempdir)) + System.IO.Directory.Delete(tempdir, true); + + System.IO.Directory.CreateDirectory(tempdir); + } + catch(Exception ex) + { + Log.WriteMessage("Failed to clean tempdir", LogMessageType.Error, ex); + } Duplicati.Library.Core.TempFolder.SystemTempPath = tempdir; @@ -167,13 +175,19 @@ namespace Duplicati.CommandLine string relpath = s.Substring(f1.Length); string target = System.IO.Path.Combine(f2, relpath); if (!folders2.Contains(target)) + { + Log.WriteMessage("Missing folder: " + relpath, LogMessageType.Error); Console.WriteLine("Missing folder: " + relpath); + } else folders2.Remove(target); } - foreach(string s in folders2) + foreach (string s in folders2) + { + Log.WriteMessage("Extra folder: " + s.Substring(f2.Length), LogMessageType.Error); Console.WriteLine("Extra folder: " + s.Substring(f2.Length)); + } List files1 = Utility.EnumerateFiles(f1); List files2 = Utility.EnumerateFiles(f2); @@ -182,17 +196,26 @@ namespace Duplicati.CommandLine string relpath = s.Substring(f1.Length); string target = System.IO.Path.Combine(f2, relpath); if (!files2.Contains(target)) + { + Log.WriteMessage("Missing file: " + relpath, LogMessageType.Error); Console.WriteLine("Missing file: " + relpath); + } else { files2.Remove(target); if (!CompareFiles(s, target, relpath)) + { + Log.WriteMessage("File differs: " + relpath, LogMessageType.Error); Console.WriteLine("File differs: " + relpath); + } } } foreach (string s in files2) + { + Log.WriteMessage("Extra file: " + s.Substring(f2.Length), LogMessageType.Error); Console.WriteLine("Extra file: " + s.Substring(f2.Length)); + } } /// @@ -208,6 +231,7 @@ namespace Duplicati.CommandLine using (System.IO.FileStream fs2 = System.IO.File.OpenRead(f2)) if (fs1.Length != fs2.Length) { + Log.WriteMessage("Lengths differ: " + display + ", " + fs1.Length.ToString() + " vs. " + fs2.Length.ToString(), LogMessageType.Error); Console.WriteLine("Lengths differ: " + display + ", " + fs1.Length.ToString() + " vs. " + fs2.Length.ToString()); return false; } @@ -215,6 +239,7 @@ namespace Duplicati.CommandLine for (long l = 0; l < fs1.Length; l++) if (fs1.ReadByte() != fs2.ReadByte()) { + Log.WriteMessage("Mismatch in byte " + l.ToString() + " in file " + display, LogMessageType.Error); Console.WriteLine("Mismatch in byte " + l.ToString() + " in file " + display); return false; } diff --git a/Duplicati/GUI/ApplicationSetup.Designer.cs b/Duplicati/GUI/ApplicationSetup.Designer.cs index 141d0402c..7fa2a302d 100644 --- a/Duplicati/GUI/ApplicationSetup.Designer.cs +++ b/Duplicati/GUI/ApplicationSetup.Designer.cs @@ -30,31 +30,23 @@ namespace Duplicati.GUI { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ApplicationSetup)); this.label1 = new System.Windows.Forms.Label(); - this.RecentDuration = new System.Windows.Forms.TextBox(); - this.PGPPath = new System.Windows.Forms.TextBox(); + this.GPGPath = new System.Windows.Forms.TextBox(); this.label2 = new System.Windows.Forms.Label(); this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.RecentDuration = new Duplicati.GUI.HelperControls.DurationEditor(); this.groupBox2 = new System.Windows.Forms.GroupBox(); - this.BrowseNcFTP = new System.Windows.Forms.Button(); - this.NcFTPPath = new System.Windows.Forms.TextBox(); - this.label5 = new System.Windows.Forms.Label(); - this.BrowseDuplicity = new System.Windows.Forms.Button(); - this.BrowsePython = new System.Windows.Forms.Button(); - this.BrowsePGP = new System.Windows.Forms.Button(); - this.DuplicityPath = new System.Windows.Forms.TextBox(); - this.label4 = new System.Windows.Forms.Label(); - this.PythonPath = new System.Windows.Forms.TextBox(); + this.BrowseSCP = new System.Windows.Forms.Button(); + this.SCPPath = new System.Windows.Forms.TextBox(); this.label3 = new System.Windows.Forms.Label(); + this.BrowseSFTP = new System.Windows.Forms.Button(); + this.SFTPPath = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.BrowsePGP = new System.Windows.Forms.Button(); this.OKBtn = new System.Windows.Forms.Button(); this.CancelBtn = new System.Windows.Forms.Button(); - this.PGPBrowser = new System.Windows.Forms.FolderBrowserDialog(); - this.PythonFileDialog = new System.Windows.Forms.OpenFileDialog(); - this.DuplicityFileDialog = new System.Windows.Forms.OpenFileDialog(); - this.NcFTPBrowser = new System.Windows.Forms.FolderBrowserDialog(); - this.BrowsePutty = new System.Windows.Forms.Button(); - this.PuttyPath = new System.Windows.Forms.TextBox(); - this.label6 = new System.Windows.Forms.Label(); - this.PuttyBrowser = new System.Windows.Forms.FolderBrowserDialog(); + this.BrowseGPGDialog = new System.Windows.Forms.OpenFileDialog(); + this.BrowseSFTPDialog = new System.Windows.Forms.OpenFileDialog(); + this.BrowseSCPDialog = new System.Windows.Forms.OpenFileDialog(); this.groupBox1.SuspendLayout(); this.groupBox2.SuspendLayout(); this.SuspendLayout(); @@ -68,162 +60,142 @@ namespace Duplicati.GUI this.label1.TabIndex = 0; this.label1.Text = "Time a log is shown in recent backups"; // - // RecentDuration + // GPGPath // - this.RecentDuration.Location = new System.Drawing.Point(232, 24); - this.RecentDuration.Name = "RecentDuration"; - this.RecentDuration.Size = new System.Drawing.Size(168, 20); - this.RecentDuration.TabIndex = 1; - this.RecentDuration.TextChanged += new System.EventHandler(this.RecentDuration_TextChanged); - // - // PGPPath - // - this.PGPPath.Location = new System.Drawing.Point(128, 24); - this.PGPPath.Name = "PGPPath"; - this.PGPPath.Size = new System.Drawing.Size(248, 20); - this.PGPPath.TabIndex = 3; - this.PGPPath.TextChanged += new System.EventHandler(this.PGPPath_TextChanged); + this.GPGPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.GPGPath.Location = new System.Drawing.Point(128, 24); + this.GPGPath.Name = "GPGPath"; + this.GPGPath.Size = new System.Drawing.Size(272, 20); + this.GPGPath.TabIndex = 3; + this.GPGPath.TextChanged += new System.EventHandler(this.GPGPath_TextChanged); // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(8, 24); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(58, 13); + this.label2.Size = new System.Drawing.Size(54, 13); this.label2.TabIndex = 2; - this.label2.Text = "PGP folder"; + this.label2.Text = "GPG path"; // // groupBox1 // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.groupBox1.Controls.Add(this.RecentDuration); this.groupBox1.Controls.Add(this.label1); this.groupBox1.Location = new System.Drawing.Point(8, 8); this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(408, 56); + this.groupBox1.Size = new System.Drawing.Size(437, 56); this.groupBox1.TabIndex = 4; this.groupBox1.TabStop = false; this.groupBox1.Text = "User interface settings"; // + // RecentDuration + // + this.RecentDuration.Location = new System.Drawing.Point(208, 24); + this.RecentDuration.Name = "RecentDuration"; + this.RecentDuration.Size = new System.Drawing.Size(221, 21); + this.RecentDuration.TabIndex = 1; + this.RecentDuration.Value = ""; + this.RecentDuration.ValueChanged += new System.EventHandler(this.RecentDuration_ValueChanged); + // // groupBox2 // - this.groupBox2.Controls.Add(this.BrowsePutty); - this.groupBox2.Controls.Add(this.PuttyPath); - this.groupBox2.Controls.Add(this.label6); - this.groupBox2.Controls.Add(this.BrowseNcFTP); - this.groupBox2.Controls.Add(this.NcFTPPath); - this.groupBox2.Controls.Add(this.label5); - this.groupBox2.Controls.Add(this.BrowseDuplicity); - this.groupBox2.Controls.Add(this.BrowsePython); - this.groupBox2.Controls.Add(this.BrowsePGP); - this.groupBox2.Controls.Add(this.DuplicityPath); - this.groupBox2.Controls.Add(this.label4); - this.groupBox2.Controls.Add(this.PythonPath); + this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox2.Controls.Add(this.BrowseSCP); + this.groupBox2.Controls.Add(this.SCPPath); this.groupBox2.Controls.Add(this.label3); - this.groupBox2.Controls.Add(this.PGPPath); + this.groupBox2.Controls.Add(this.BrowseSFTP); + this.groupBox2.Controls.Add(this.SFTPPath); + this.groupBox2.Controls.Add(this.label6); + this.groupBox2.Controls.Add(this.BrowsePGP); + this.groupBox2.Controls.Add(this.GPGPath); this.groupBox2.Controls.Add(this.label2); this.groupBox2.Location = new System.Drawing.Point(8, 72); this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(408, 160); + this.groupBox2.Size = new System.Drawing.Size(437, 104); this.groupBox2.TabIndex = 5; this.groupBox2.TabStop = false; - this.groupBox2.Text = "Path settings (VERY ADVANCED)"; + this.groupBox2.Text = "Path settings (Advanced)"; // - // BrowseNcFTP + // BrowseSCP // - this.BrowseNcFTP.Location = new System.Drawing.Point(376, 96); - this.BrowseNcFTP.Name = "BrowseNcFTP"; - this.BrowseNcFTP.Size = new System.Drawing.Size(24, 20); - this.BrowseNcFTP.TabIndex = 13; - this.BrowseNcFTP.Text = "..."; - this.BrowseNcFTP.UseVisualStyleBackColor = true; - this.BrowseNcFTP.Click += new System.EventHandler(this.BrowseNcFTP_Click); + this.BrowseSCP.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BrowseSCP.Location = new System.Drawing.Point(400, 72); + this.BrowseSCP.Name = "BrowseSCP"; + this.BrowseSCP.Size = new System.Drawing.Size(24, 20); + this.BrowseSCP.TabIndex = 19; + this.BrowseSCP.Text = "..."; + this.BrowseSCP.UseVisualStyleBackColor = true; + this.BrowseSCP.Click += new System.EventHandler(this.BrowseSCP_Click); // - // NcFTPPath + // SCPPath // - this.NcFTPPath.Location = new System.Drawing.Point(128, 96); - this.NcFTPPath.Name = "NcFTPPath"; - this.NcFTPPath.Size = new System.Drawing.Size(248, 20); - this.NcFTPPath.TabIndex = 12; - this.NcFTPPath.TextChanged += new System.EventHandler(this.NcFTPPath_TextChanged); + this.SCPPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.SCPPath.Location = new System.Drawing.Point(128, 72); + this.SCPPath.Name = "SCPPath"; + this.SCPPath.Size = new System.Drawing.Size(272, 20); + this.SCPPath.TabIndex = 18; + this.SCPPath.TextChanged += new System.EventHandler(this.SCPPath_TextChanged); // - // label5 + // label3 // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(8, 96); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(70, 13); - this.label5.TabIndex = 11; - this.label5.Text = "NcFTP folder"; + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(8, 72); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(48, 13); + this.label3.TabIndex = 17; + this.label3.Text = "scp path"; // - // BrowseDuplicity + // BrowseSFTP // - this.BrowseDuplicity.Location = new System.Drawing.Point(376, 72); - this.BrowseDuplicity.Name = "BrowseDuplicity"; - this.BrowseDuplicity.Size = new System.Drawing.Size(24, 20); - this.BrowseDuplicity.TabIndex = 10; - this.BrowseDuplicity.Text = "..."; - this.BrowseDuplicity.UseVisualStyleBackColor = true; - this.BrowseDuplicity.Click += new System.EventHandler(this.BrowseDuplicity_Click); + this.BrowseSFTP.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BrowseSFTP.Location = new System.Drawing.Point(400, 48); + this.BrowseSFTP.Name = "BrowseSFTP"; + this.BrowseSFTP.Size = new System.Drawing.Size(24, 20); + this.BrowseSFTP.TabIndex = 16; + this.BrowseSFTP.Text = "..."; + this.BrowseSFTP.UseVisualStyleBackColor = true; + this.BrowseSFTP.Click += new System.EventHandler(this.BrowseSFTP_Click); // - // BrowsePython + // SFTPPath // - this.BrowsePython.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.BrowsePython.Location = new System.Drawing.Point(376, 48); - this.BrowsePython.Name = "BrowsePython"; - this.BrowsePython.Size = new System.Drawing.Size(24, 20); - this.BrowsePython.TabIndex = 9; - this.BrowsePython.Text = "..."; - this.BrowsePython.UseVisualStyleBackColor = true; - this.BrowsePython.Click += new System.EventHandler(this.BrowsePython_Click); + this.SFTPPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.SFTPPath.Location = new System.Drawing.Point(128, 48); + this.SFTPPath.Name = "SFTPPath"; + this.SFTPPath.Size = new System.Drawing.Size(272, 20); + this.SFTPPath.TabIndex = 15; + this.SFTPPath.TextChanged += new System.EventHandler(this.SFTPPath_TextChanged); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(8, 48); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(48, 13); + this.label6.TabIndex = 14; + this.label6.Text = "sftp path"; // // BrowsePGP // - this.BrowsePGP.Location = new System.Drawing.Point(376, 24); + this.BrowsePGP.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BrowsePGP.Location = new System.Drawing.Point(400, 24); this.BrowsePGP.Name = "BrowsePGP"; this.BrowsePGP.Size = new System.Drawing.Size(24, 20); this.BrowsePGP.TabIndex = 8; this.BrowsePGP.Text = "..."; this.BrowsePGP.UseVisualStyleBackColor = true; - this.BrowsePGP.Click += new System.EventHandler(this.BrowsePGP_Click); - // - // DuplicityPath - // - this.DuplicityPath.Location = new System.Drawing.Point(128, 72); - this.DuplicityPath.Name = "DuplicityPath"; - this.DuplicityPath.Size = new System.Drawing.Size(248, 20); - this.DuplicityPath.TabIndex = 7; - this.DuplicityPath.TextChanged += new System.EventHandler(this.DuplicityPath_TextChanged); - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(8, 72); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(91, 13); - this.label4.TabIndex = 6; - this.label4.Text = "Duplicity script file"; - // - // PythonPath - // - this.PythonPath.Location = new System.Drawing.Point(128, 48); - this.PythonPath.Name = "PythonPath"; - this.PythonPath.Size = new System.Drawing.Size(248, 20); - this.PythonPath.TabIndex = 5; - this.PythonPath.TextChanged += new System.EventHandler(this.PythonPath_TextChanged); - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(8, 48); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(101, 13); - this.label3.TabIndex = 4; - this.label3.Text = "Python executeable"; + this.BrowsePGP.Click += new System.EventHandler(this.BrowseGPG_Click); // // OKBtn // this.OKBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.OKBtn.Location = new System.Drawing.Point(120, 251); + this.OKBtn.Location = new System.Drawing.Point(135, 186); this.OKBtn.Name = "OKBtn"; this.OKBtn.Size = new System.Drawing.Size(80, 24); this.OKBtn.TabIndex = 6; @@ -235,77 +207,40 @@ namespace Duplicati.GUI // this.CancelBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBtn.Location = new System.Drawing.Point(216, 251); + this.CancelBtn.Location = new System.Drawing.Point(231, 186); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(72, 24); this.CancelBtn.TabIndex = 7; this.CancelBtn.Text = "Cancel"; this.CancelBtn.UseVisualStyleBackColor = true; // - // PGPBrowser + // BrowseGPGDialog // - this.PGPBrowser.Description = "Select the folder where the PGP files are located"; - this.PGPBrowser.ShowNewFolderButton = false; + this.BrowseGPGDialog.AddExtension = false; + this.BrowseGPGDialog.FileName = "gpg.exe"; + this.BrowseGPGDialog.Filter = "Executables (*.exe)|*.exe|All files (*.*)|*.*"; + this.BrowseGPGDialog.Title = "Select the GNU Privacy Guard executable"; // - // PythonFileDialog + // BrowseSFTPDialog // - this.PythonFileDialog.DefaultExt = "exe"; - this.PythonFileDialog.FileName = "Python.exe"; - this.PythonFileDialog.Filter = "Executeables (*.exe)|*.exe|All files (*.*)|*.*"; - this.PythonFileDialog.SupportMultiDottedExtensions = true; - this.PythonFileDialog.Title = "Select the Python executeable to use"; + this.BrowseSFTPDialog.AddExtension = false; + this.BrowseSFTPDialog.FileName = "psftp.exe"; + this.BrowseSFTPDialog.Filter = "Executables (*.exe)|*.exe|All files (*.*)|*.*"; + this.BrowseSFTPDialog.Title = "Select the SFTP executable"; // - // DuplicityFileDialog + // BrowseSCPDialog // - this.DuplicityFileDialog.DefaultExt = "py"; - this.DuplicityFileDialog.FileName = "Duplicity.py"; - this.DuplicityFileDialog.Filter = "Python files (*.py)|*.py|All files (*.*)|*.*"; - this.DuplicityFileDialog.SupportMultiDottedExtensions = true; - this.DuplicityFileDialog.Title = "Select the main Duplicity script file"; - // - // NcFTPBrowser - // - this.NcFTPBrowser.Description = "Select the folder where the NcFTP files are located"; - this.NcFTPBrowser.ShowNewFolderButton = false; - // - // BrowsePutty - // - this.BrowsePutty.Location = new System.Drawing.Point(376, 120); - this.BrowsePutty.Name = "BrowsePutty"; - this.BrowsePutty.Size = new System.Drawing.Size(24, 20); - this.BrowsePutty.TabIndex = 16; - this.BrowsePutty.Text = "..."; - this.BrowsePutty.UseVisualStyleBackColor = true; - this.BrowsePutty.Click += new System.EventHandler(this.BrowsePutty_Click); - // - // PuttyPath - // - this.PuttyPath.Location = new System.Drawing.Point(128, 120); - this.PuttyPath.Name = "PuttyPath"; - this.PuttyPath.Size = new System.Drawing.Size(248, 20); - this.PuttyPath.TabIndex = 15; - this.PuttyPath.TextChanged += new System.EventHandler(this.PuttyPath_TextChanged); - // - // label6 - // - this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(8, 120); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(60, 13); - this.label6.TabIndex = 14; - this.label6.Text = "Putty folder"; - // - // PuttyBrowser - // - this.PuttyBrowser.Description = "Select the folder where the putty files are located"; - this.PuttyBrowser.ShowNewFolderButton = false; + this.BrowseSCPDialog.AddExtension = false; + this.BrowseSCPDialog.FileName = "pscp.exe"; + this.BrowseSCPDialog.Filter = "Executables (*.exe)|*.exe|All files (*.*)|*.*"; + this.BrowseSCPDialog.Title = "Select the SCP executable"; // // ApplicationSetup // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CancelBtn; - this.ClientSize = new System.Drawing.Size(423, 286); + this.ClientSize = new System.Drawing.Size(453, 224); this.Controls.Add(this.CancelBtn); this.Controls.Add(this.OKBtn); this.Controls.Add(this.groupBox2); @@ -328,30 +263,22 @@ namespace Duplicati.GUI #endregion private System.Windows.Forms.Label label1; - private System.Windows.Forms.TextBox RecentDuration; - private System.Windows.Forms.TextBox PGPPath; + private System.Windows.Forms.TextBox GPGPath; private System.Windows.Forms.Label label2; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox groupBox2; - private System.Windows.Forms.Button BrowseDuplicity; - private System.Windows.Forms.Button BrowsePython; private System.Windows.Forms.Button BrowsePGP; - private System.Windows.Forms.TextBox DuplicityPath; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.TextBox PythonPath; - private System.Windows.Forms.Label label3; private System.Windows.Forms.Button OKBtn; private System.Windows.Forms.Button CancelBtn; - private System.Windows.Forms.FolderBrowserDialog PGPBrowser; - private System.Windows.Forms.OpenFileDialog PythonFileDialog; - private System.Windows.Forms.OpenFileDialog DuplicityFileDialog; - private System.Windows.Forms.Button BrowseNcFTP; - private System.Windows.Forms.TextBox NcFTPPath; - private System.Windows.Forms.Label label5; - private System.Windows.Forms.FolderBrowserDialog NcFTPBrowser; - private System.Windows.Forms.Button BrowsePutty; - private System.Windows.Forms.TextBox PuttyPath; + private System.Windows.Forms.Button BrowseSFTP; + private System.Windows.Forms.TextBox SFTPPath; private System.Windows.Forms.Label label6; - private System.Windows.Forms.FolderBrowserDialog PuttyBrowser; + private Duplicati.GUI.HelperControls.DurationEditor RecentDuration; + private System.Windows.Forms.Button BrowseSCP; + private System.Windows.Forms.TextBox SCPPath; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.OpenFileDialog BrowseGPGDialog; + private System.Windows.Forms.OpenFileDialog BrowseSFTPDialog; + private System.Windows.Forms.OpenFileDialog BrowseSCPDialog; } } \ No newline at end of file diff --git a/Duplicati/GUI/ApplicationSetup.cs b/Duplicati/GUI/ApplicationSetup.cs index 47327f413..249d5330a 100644 --- a/Duplicati/GUI/ApplicationSetup.cs +++ b/Duplicati/GUI/ApplicationSetup.cs @@ -44,11 +44,10 @@ namespace Duplicati.GUI try { m_isUpdating = true; - RecentDuration.Text = m_settings.RecentBackupDuration; - PGPPath.Text = m_settings.PGPPath; - PythonPath.Text = m_settings.PythonPath; - DuplicityPath.Text = m_settings.DuplicityPath; - NcFTPPath.Text = m_settings.NcFTPPath; + RecentDuration.Value = m_settings.RecentBackupDuration; + GPGPath.Text = m_settings.GPGPath; + SFTPPath.Text = m_settings.SFtpPath; + SCPPath.Text = m_settings.ScpPath; } finally { @@ -74,91 +73,65 @@ namespace Duplicati.GUI return true; } - private void BrowsePGP_Click(object sender, EventArgs e) + private void BrowseGPG_Click(object sender, EventArgs e) { - if (PGPBrowser.ShowDialog(this) == DialogResult.OK) - if (TestForFiles(PGPBrowser.SelectedPath, "pgp.exe")) - PGPPath.Text = PGPBrowser.SelectedPath; - } - - private void BrowsePython_Click(object sender, EventArgs e) - { - if (PythonFileDialog.ShowDialog(this) == DialogResult.OK) - if (TestForFiles(System.IO.Path.GetDirectoryName(PythonFileDialog.FileName), "python.exe")) - PythonPath.Text = PythonFileDialog.FileName; - } - - private void BrowseDuplicity_Click(object sender, EventArgs e) - { - if (DuplicityFileDialog.ShowDialog(this) == DialogResult.OK) - if (TestForFiles(System.IO.Path.GetDirectoryName(DuplicityFileDialog.FileName), "duplicity.py")) - DuplicityPath.Text = DuplicityFileDialog.FileName; + if (BrowseGPGDialog.ShowDialog(this) == DialogResult.OK) + GPGPath.Text = BrowseGPGDialog.FileName; } private void RecentDuration_TextChanged(object sender, EventArgs e) { if (m_isUpdating) return; - m_settings.RecentBackupDuration = RecentDuration.Text; + m_settings.RecentBackupDuration = RecentDuration.Value; } - private void PGPPath_TextChanged(object sender, EventArgs e) + private void GPGPath_TextChanged(object sender, EventArgs e) { if (m_isUpdating) return; - m_settings.PGPPath = PGPPath.Text; - } - - private void PythonPath_TextChanged(object sender, EventArgs e) - { - if (m_isUpdating) - return; - m_settings.PythonPath = PythonPath.Text; - } - - private void DuplicityPath_TextChanged(object sender, EventArgs e) - { - if (m_isUpdating) - return; - m_settings.DuplicityPath = DuplicityPath.Text; + m_settings.GPGPath = GPGPath.Text; } private void OKBtn_Click(object sender, EventArgs e) { - //TODO: Fix once CommitRecursive is done - m_connection.CommitAll(); - Program.DataConnection.CommitAll(); + m_connection.CommitRecursive(m_connection.GetObjects()); this.DialogResult = DialogResult.OK; this.Close(); } - private void BrowseNcFTP_Click(object sender, EventArgs e) + private void BrowseSFTP_Click(object sender, EventArgs e) { - if (NcFTPBrowser.ShowDialog(this) == DialogResult.OK) - if (TestForFiles(NcFTPBrowser.SelectedPath, "ncftp.exe", "ncftpbatch.exe", "ncftpget.exe", "ncftpls.exe", "ncftpput.exe")) - NcFTPPath.Text = NcFTPBrowser.SelectedPath; - } + if (BrowseSFTPDialog.ShowDialog(this) == DialogResult.OK) + SFTPPath.Text = BrowseSFTPDialog.FileName; + } - private void NcFTPPath_TextChanged(object sender, EventArgs e) + private void SFTPPath_TextChanged(object sender, EventArgs e) { if (m_isUpdating) return; - m_settings.NcFTPPath = NcFTPPath.Text; + m_settings.SFtpPath = SFTPPath.Text; } - private void BrowsePutty_Click(object sender, EventArgs e) - { - if (PuttyBrowser.ShowDialog(this) == DialogResult.OK) - if (TestForFiles(PuttyBrowser.SelectedPath, "pscp.exe", "psftp.exe")) - PuttyPath.Text = PuttyBrowser.SelectedPath; - } - - private void PuttyPath_TextChanged(object sender, EventArgs e) + private void SCPPath_TextChanged(object sender, EventArgs e) { if (m_isUpdating) return; - m_settings.PuttyPath = PuttyPath.Text; + m_settings.ScpPath = SCPPath.Text; + } + + private void BrowseSCP_Click(object sender, EventArgs e) + { + if (BrowseSCPDialog.ShowDialog(this) == DialogResult.OK) + SCPPath.Text = BrowseSCPDialog.FileName; + } + + private void RecentDuration_ValueChanged(object sender, EventArgs e) + { + RecentDuration_TextChanged(sender, e); + } + } } \ No newline at end of file diff --git a/Duplicati/GUI/ApplicationSetup.resx b/Duplicati/GUI/ApplicationSetup.resx index c9988311d..0df775dba 100644 --- a/Duplicati/GUI/ApplicationSetup.resx +++ b/Duplicati/GUI/ApplicationSetup.resx @@ -117,20 +117,14 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 17, 17 - - 129, 17 + + 147, 17 - - 263, 17 - - - 405, 17 - - - 528, 17 + + 277, 17 diff --git a/Duplicati/GUI/Database schema/Schema.sql b/Duplicati/GUI/Database schema/Schema.sql index c1a7eb5ed..188abbe8b 100644 --- a/Duplicati/GUI/Database schema/Schema.sql +++ b/Duplicati/GUI/Database schema/Schema.sql @@ -13,7 +13,8 @@ CREATE TABLE "Task" ( "VolumeSize" TEXT NULL, "FullAfter" TEXT NULL, "ThreadPriority" TEXT NULL, - "AsyncTransfer" BOOLEAN NULL + "AsyncTransfer" BOOLEAN NULL, + "GPGEncryption" BOOLEAN NULL ); CREATE TABLE "Schedule" ( diff --git a/Duplicati/GUI/Datamodel/ApplicationSettings.cs b/Duplicati/GUI/Datamodel/ApplicationSettings.cs index e86481b64..3916f7d58 100644 --- a/Duplicati/GUI/Datamodel/ApplicationSettings.cs +++ b/Duplicati/GUI/Datamodel/ApplicationSettings.cs @@ -30,12 +30,11 @@ namespace Duplicati.Datamodel private const string RECENT_DURATION = "Recent duration"; private const string PGP_PATH = "PGP path"; - private const string PYTHON_PATH = "Python path"; - private const string DUPLICITY_PATH = "Duplicity path"; - private const string NCFTP_PATH = "NcFTP Path"; - private const string PUTTY_PATH = "Putty Path"; + private const string SFTP_PATH = "SFTP Path"; + private const string SCP_PATH = "SCP Path"; - public const string APP_PATH_ENV = "%APP_PATH%"; + //TODO: Deal with this on Linux + public const string PROGRAM_FILES = "%PROGRAMFILES%"; public ApplicationSettings(IDataFetcher dataparent) { @@ -54,47 +53,60 @@ namespace Duplicati.Datamodel /// /// Gets or sets the path to PGP. May contain environment variables /// - public string PGPPath + public string GPGPath { - get { return string.IsNullOrEmpty(m_appset[PGP_PATH]) ? APP_PATH_ENV + "gpg" : m_appset[PGP_PATH]; } + get + { + if (string.IsNullOrEmpty(m_appset[PGP_PATH])) + { + if (System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == PlatformID.MacOSX) + return "gpg"; + else + return PROGRAM_FILES + "GNU\\GnuPG\\gpg.exe"; + } + + return m_appset[PGP_PATH]; + } set { m_appset[PGP_PATH] = value; } } /// - /// Gets or sets the path to NcFTP. May contain environment variables + /// Gets or sets the path to SFtp. May contain environment variables /// - public string NcFTPPath + public string SFtpPath { - get { return string.IsNullOrEmpty(m_appset[NCFTP_PATH]) ? APP_PATH_ENV + "ncftp" : m_appset[NCFTP_PATH]; } - set { m_appset[NCFTP_PATH] = value; } + get + { + if (string.IsNullOrEmpty(m_appset[SFTP_PATH])) + { + if (System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == PlatformID.MacOSX) + return "sftp"; + else + return PROGRAM_FILES + "\\putty\\psftp.exe"; + } + return m_appset[SFTP_PATH]; + } + set { m_appset[SFTP_PATH] = value; } } + /// - /// Gets or sets the path to Putty. May contain environment variables + /// Gets or sets the path to SFtp. May contain environment variables /// - public string PuttyPath + public string ScpPath { - get { return string.IsNullOrEmpty(m_appset[PUTTY_PATH]) ? APP_PATH_ENV + "putty" : m_appset[PUTTY_PATH]; } - set { m_appset[PUTTY_PATH] = value; } + get + { + if (string.IsNullOrEmpty(m_appset[SCP_PATH])) + { + if (System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == PlatformID.MacOSX) + return "scp"; + else + return PROGRAM_FILES + "\\putty\\pscp.exe"; + } + return m_appset[SCP_PATH]; + } + set { m_appset[SCP_PATH] = value; } } - - /// - /// Gets or sets the path to the pyhon executeable. May contain environment variables - /// - public string PythonPath - { - get { return string.IsNullOrEmpty(m_appset[PYTHON_PATH]) ? APP_PATH_ENV + "python25\\python.exe" : m_appset[PYTHON_PATH]; } - set { m_appset[PYTHON_PATH] = value; } - } - - /// - /// Gets or sets the path to the duplicity main script. May contain environment variables - /// - public string DuplicityPath - { - get { return string.IsNullOrEmpty(m_appset[DUPLICITY_PATH]) ? APP_PATH_ENV + "duplicity\\duplicity.py" : m_appset[DUPLICITY_PATH]; } - set { m_appset[DUPLICITY_PATH] = value; } - } - } } diff --git a/Duplicati/GUI/Datamodel/Backends/SSH.cs b/Duplicati/GUI/Datamodel/Backends/SSH.cs index 80c26436d..43122af2a 100644 --- a/Duplicati/GUI/Datamodel/Backends/SSH.cs +++ b/Duplicati/GUI/Datamodel/Backends/SSH.cs @@ -110,6 +110,9 @@ namespace Duplicati.Datamodel.Backends if (this.Port != 22) options["ssh-options"] += "-P " + this.Port; + + options["scp-command"] = new ApplicationSettings(m_owner.DataParent).ScpPath; + options["sftp-command"] = new ApplicationSettings(m_owner.DataParent).SFtpPath; } public string FriendlyName { get { return "SSH host"; } } diff --git a/Duplicati/GUI/Datamodel/Duplicati.sqlite b/Duplicati/GUI/Datamodel/Duplicati.sqlite index ac76670df..b80b20d75 100644 Binary files a/Duplicati/GUI/Datamodel/Duplicati.sqlite and b/Duplicati/GUI/Datamodel/Duplicati.sqlite differ diff --git a/Duplicati/GUI/Datamodel/LightDataModel.Mapping.xml b/Duplicati/GUI/Datamodel/LightDataModel.Mapping.xml index 38bee9490..51a43e1ac 100644 --- a/Duplicati/GUI/Datamodel/LightDataModel.Mapping.xml +++ b/Duplicati/GUI/Datamodel/LightDataModel.Mapping.xml @@ -171,6 +171,17 @@ false false + + System.Boolean + GPGEncryption + GPGEncryption + false + false + false + false + false + false + false diff --git a/Duplicati/GUI/Datamodel/Task.Extension.cs b/Duplicati/GUI/Datamodel/Task.Extension.cs index 992c04ed8..d547a6b9d 100644 --- a/Duplicati/GUI/Datamodel/Task.Extension.cs +++ b/Duplicati/GUI/Datamodel/Task.Extension.cs @@ -84,6 +84,11 @@ namespace Duplicati.Datamodel options["thread-priority"] = this.ThreadPriority; if (this.AsyncTransfer) options["asynchronous-upload"] = ""; + if (this.GPGEncryption) + { + options["gpg-encryption"] = ""; + options["gpg-program-path"] = new ApplicationSettings(this.DataParent).GPGPath; + } } public string EncodedFilter diff --git a/Duplicati/GUI/Datamodel/Task.cs b/Duplicati/GUI/Datamodel/Task.cs index 82e81b45e..be2ad8dd0 100644 --- a/Duplicati/GUI/Datamodel/Task.cs +++ b/Duplicati/GUI/Datamodel/Task.cs @@ -49,6 +49,8 @@ namespace Duplicati.Datamodel private System.String m_ThreadPriority = ""; [DatabaseField("AsyncTransfer")] private System.Boolean m_AsyncTransfer = false; + [DatabaseField("GPGEncryption")] + private System.Boolean m_GPGEncryption = false; #endregion #region " properties " @@ -143,6 +145,12 @@ namespace Duplicati.Datamodel set{object oldvalue = m_AsyncTransfer;OnBeforeDataChange(this, "AsyncTransfer", oldvalue, value);m_AsyncTransfer = value;OnAfterDataChange(this, "AsyncTransfer", oldvalue, value);} } + public System.Boolean GPGEncryption + { + get{return m_GPGEncryption;} + set{object oldvalue = m_GPGEncryption;OnBeforeDataChange(this, "GPGEncryption", oldvalue, value);m_GPGEncryption = value;OnAfterDataChange(this, "GPGEncryption", oldvalue, value);} + } + #endregion #region " referenced properties " diff --git a/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.Designer.cs index 9f64f0d5f..1ab9b111e 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.Designer.cs @@ -38,6 +38,7 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup this.GeneratePassword = new System.Windows.Forms.Button(); this.PassphraseLength = new System.Windows.Forms.NumericUpDown(); this.label2 = new System.Windows.Forms.Label(); + this.UseGPG = new System.Windows.Forms.CheckBox(); this.PasswordGeneratorSettings.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.PassphraseLength)).BeginInit(); this.SuspendLayout(); @@ -155,10 +156,22 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup this.label2.TabIndex = 5; this.label2.Text = "Characters"; // + // UseGPG + // + this.UseGPG.AutoSize = true; + this.UseGPG.Location = new System.Drawing.Point(24, 176); + this.UseGPG.Name = "UseGPG"; + this.UseGPG.Size = new System.Drawing.Size(365, 17); + this.UseGPG.TabIndex = 5; + this.UseGPG.Text = "Use GNU Privacy Guard for encryption (requires that GnuPG is installed)"; + this.UseGPG.UseVisualStyleBackColor = true; + this.UseGPG.CheckedChanged += new System.EventHandler(this.UseGPG_CheckedChanged); + // // PasswordSettings // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.UseGPG); this.Controls.Add(this.PasswordGeneratorSettings); this.Controls.Add(this.PasswordHelptext); this.Controls.Add(this.Password); @@ -184,5 +197,6 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup private System.Windows.Forms.Label label2; private System.Windows.Forms.ComboBox PasswordCharacterSet; private System.Windows.Forms.Label label3; + private System.Windows.Forms.CheckBox UseGPG; } } diff --git a/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.cs b/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.cs index 2823f02da..0e58f0051 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/PasswordSettings.cs @@ -32,6 +32,7 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup public partial class PasswordSettings : WizardControl { private bool m_warnedNoPassword = false; + private bool m_warnedNoGPG = false; private WizardSettingsWrapper m_wrapper; public PasswordSettings() @@ -46,6 +47,8 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup void PasswordSettings_PageLeave(object sender, PageChangedArgs args) { m_settings["Password:WarnedNoPassword"] = m_warnedNoPassword; + m_settings["Password:WarnedNoGPG"] = m_warnedNoGPG; + if (args.Direction == PageChangedDirection.Back) return; @@ -67,8 +70,29 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup m_warnedNoPassword = true; } + if (!m_warnedNoGPG && UseGPG.Checked) + { + ApplicationSettings apps = new ApplicationSettings(Program.DataConnection); + System.IO.FileInfo fi = null; + try { fi = new System.IO.FileInfo(System.Environment.ExpandEnvironmentVariables(apps.GPGPath)); } + catch { } + + if (fi == null || !fi.Exists) + { + if (MessageBox.Show(this, "Duplicati was unable to verify the existence of GNU Privacy Guard.\nGPG may work regardless, if it is located in the system search path.\nIf the encryption fails, no files will be backed up\nDo you want to continue anyway?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes) + { + args.Cancel = true; + return; + } + + m_warnedNoGPG = true; + } + } + m_settings["Password:WarnedNoPassword"] = m_warnedNoPassword; + m_settings["Password:WarnedNoGPG"] = m_warnedNoGPG; m_wrapper.BackupPassword = EnablePassword.Checked ? Password.Text : ""; + m_wrapper.GPGEncryption = UseGPG.Checked; args.NextPage = new SelectBackend(); } @@ -81,10 +105,13 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup { EnablePassword.Checked = !string.IsNullOrEmpty(m_wrapper.BackupPassword) || m_wrapper.PrimayAction == WizardSettingsWrapper.MainAction.Add; Password.Text = m_wrapper.BackupPassword; + UseGPG.Checked = m_wrapper.GPGEncryption; } if (m_settings.ContainsKey("Password:WarnedNoPassword")) m_warnedNoPassword = (bool)m_settings["Password:WarnedNoPassword"]; + if (m_settings.ContainsKey("Password:WarnedNoGPG")) + m_warnedNoPassword = (bool)m_settings["Password:WarnedNoGPG"]; } private void GeneratePassword_Click(object sender, EventArgs e) @@ -94,8 +121,9 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup private void EnablePassword_CheckedChanged(object sender, EventArgs e) { - Password.Enabled = PasswordGeneratorSettings.Enabled = EnablePassword.Checked; + Password.Enabled = UseGPG.Enabled = PasswordGeneratorSettings.Enabled = EnablePassword.Checked; m_warnedNoPassword = false; + } private void Password_TextChanged(object sender, EventArgs e) @@ -103,5 +131,10 @@ namespace Duplicati.GUI.Wizard_pages.Add_backup m_warnedNoPassword = false; } + private void UseGPG_CheckedChanged(object sender, EventArgs e) + { + m_warnedNoGPG = false; + } + } } diff --git a/Duplicati/GUI/Wizard pages/Backends/SSH/SSHOptions.cs b/Duplicati/GUI/Wizard pages/Backends/SSH/SSHOptions.cs index 7708af226..309538bf0 100644 --- a/Duplicati/GUI/Wizard pages/Backends/SSH/SSHOptions.cs +++ b/Duplicati/GUI/Wizard pages/Backends/SSH/SSHOptions.cs @@ -33,6 +33,8 @@ namespace Duplicati.GUI.Wizard_pages.Backends.SSH { private bool m_warnedPath = false; private bool m_hasTested = false; + private bool m_warnedNoSCP = false; + private bool m_warnedNoSFTP = false; private SSHSettings m_wrapper; @@ -49,6 +51,8 @@ namespace Duplicati.GUI.Wizard_pages.Backends.SSH { m_settings["SSH:HasWarned"] = m_hasTested; m_settings["SSH:WarnedPath"] = m_warnedPath; + m_settings["SSH:WarnedNoSCP"] = m_warnedNoSCP; ; + m_settings["SSH:WarnedNoSFTP"] = m_warnedNoSFTP; if (args.Direction == PageChangedDirection.Back) return; @@ -69,6 +73,8 @@ namespace Duplicati.GUI.Wizard_pages.Backends.SSH m_settings["SSH:HasWarned"] = m_hasTested; m_settings["SSH:WarnedPath"] = m_warnedPath; + m_settings["SSH:WarnedNoSCP"] = m_warnedNoSCP; ; + m_settings["SSH:WarnedNoSFTP"] = m_warnedNoSFTP; m_wrapper.Passwordless = !UsePassword.Checked; m_wrapper.Password = m_wrapper.Passwordless ? "" : Password.Text; @@ -99,6 +105,12 @@ namespace Duplicati.GUI.Wizard_pages.Backends.SSH if (m_settings.ContainsKey("SSH:WarnedPath")) m_warnedPath = (bool)m_settings["SSH:WarnedPath"]; + + if (m_settings.ContainsKey("SSH:WarnedNoSCP")) + m_warnedNoSCP = (bool)m_settings["SSH:WarnedNoSCP"]; + + if (m_settings.ContainsKey("SSH:WarnedNoSFTP")) + m_warnedNoSFTP = (bool)m_settings["SSH:WarnedNoSFTP"]; } private void TestConnection_Click(object sender, EventArgs e) @@ -172,6 +184,42 @@ namespace Duplicati.GUI.Wizard_pages.Backends.SSH m_warnedPath = true; } + if (!m_warnedNoSCP) + { + ApplicationSettings appset = new ApplicationSettings(Program.DataConnection); + System.IO.FileInfo fi = null; + try { fi = new System.IO.FileInfo(System.Environment.ExpandEnvironmentVariables(appset.ScpPath)); } + catch { } + + if (fi == null || !fi.Exists) + { + if (MessageBox.Show(this, "Duplicati was unable to verify the existence of the scp program.\nscp may work regardless, if it is located in the system search path.\n\nDo you want to continue anyway?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes) + { + return false; + } + + m_warnedNoSCP = true; + } + } + + if (!m_warnedNoSFTP) + { + ApplicationSettings appset = new ApplicationSettings(Program.DataConnection); + System.IO.FileInfo fi = null; + try { fi = new System.IO.FileInfo(System.Environment.ExpandEnvironmentVariables(appset.SFtpPath)); } + catch { } + + if (fi == null || !fi.Exists) + { + if (MessageBox.Show(this, "Duplicati was unable to verify the existence of the sftp program.\nscp may work regardless, if it is located in the system search path.\n\nDo you want to continue anyway?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes) + { + return false; + } + + m_warnedNoSFTP = true; + } + } + return true; } diff --git a/Duplicati/GUI/Wizard pages/WizardSettingsWrapper.cs b/Duplicati/GUI/Wizard pages/WizardSettingsWrapper.cs index 7a477657a..318a769e5 100644 --- a/Duplicati/GUI/Wizard pages/WizardSettingsWrapper.cs +++ b/Duplicati/GUI/Wizard pages/WizardSettingsWrapper.cs @@ -109,7 +109,8 @@ namespace Duplicati.GUI.Wizard_pages this.ThreadPriority = schedule.Task.ThreadPriority; this.AsyncTransfer = schedule.Task.AsyncTransfer; this.EncodedFilters = schedule.Task.EncodedFilter; - + this.GPGEncryption = schedule.Task.GPGEncryption; + this.PrimayAction = action; } @@ -192,6 +193,7 @@ namespace Duplicati.GUI.Wizard_pages schedule.Task.ThreadPriority = this.ThreadPriority; schedule.Task.AsyncTransfer = this.AsyncTransfer; schedule.Task.EncodedFilter = this.EncodedFilters; + schedule.Task.GPGEncryption = this.GPGEncryption; } /// @@ -386,7 +388,7 @@ namespace Duplicati.GUI.Wizard_pages } /// - /// The size of each volume in the backup set + /// Allow async transfer of files /// public bool AsyncTransfer { @@ -457,6 +459,14 @@ namespace Duplicati.GUI.Wizard_pages set { SetItem("RestoreFileList:" + this.RestoreTime.ToString(), value); } } + /// + /// True if the GPG encryption method is enabled + /// + public bool GPGEncryption + { + get { return GetItem("GPGEncryption", false); } + set { SetItem("GPGEncryption", value); } + } } /// diff --git a/Duplicati/Library/Compression/Compression.cs b/Duplicati/Library/Compression/Compression.cs index dfc2ab275..378a793e3 100644 --- a/Duplicati/Library/Compression/Compression.cs +++ b/Duplicati/Library/Compression/Compression.cs @@ -23,87 +23,8 @@ using System.Text; namespace Duplicati.Library.Compression { - public class Compression : IDisposable + public class Compression { - private bool m_writing; - private ICSharpCode.SharpZipLib.Zip.ZipOutputStream m_zipfile; - private ICSharpCode.SharpZipLib.Zip.ZipEntryFactory m_zef; - private System.IO.FileStream m_filestream; - private string m_filename; - private string m_basename; - - public Compression(string basefolder, string zipfile) - { - m_basename = basefolder; - m_writing = true; - m_filename = zipfile; - m_filestream = System.IO.File.Create(zipfile); - m_zipfile = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(m_filestream); - m_zef = new ICSharpCode.SharpZipLib.Zip.ZipEntryFactory(); - m_zef.NameTransform = new ICSharpCode.SharpZipLib.Zip.ZipNameTransform(m_basename); - } - - public void AddFolder(string folder) - { - ICSharpCode.SharpZipLib.Zip.ZipEntry ze = m_zef.MakeDirectoryEntry(folder, true); - m_zipfile.PutNextEntry(ze); - } - - public long AddFile(string file) - { - if (!m_writing) - throw new InvalidOperationException("Cannot write to a file while reading it"); - - ICSharpCode.SharpZipLib.Zip.ZipEntry ze = m_zef.MakeFileEntry(file, true); - m_zipfile.PutNextEntry(ze); - - using(System.IO.FileStream fs = System.IO.File.OpenRead(file)) - Core.Utility.CopyStream(fs, m_zipfile); - - return new System.IO.FileInfo(m_filename).Length; - } - - public long Size - { - get - { - m_zipfile.Flush(); - return m_zipfile.Length; - } - } - - - public System.IO.Stream AddStream(string filename) - { - if (!m_writing) - throw new InvalidOperationException("Cannot write to a file while reading it"); - - - ICSharpCode.SharpZipLib.Zip.ZipEntry ze; - try - { - //This call breaks on long filenames... - ze = m_zef.MakeFileEntry(filename, true); - ze.DateTime = System.IO.File.GetLastWriteTime(filename); - } - catch(System.IO.PathTooLongException) - { - ze = new ICSharpCode.SharpZipLib.Zip.ZipEntry(m_zef.NameTransform.TransformFile(filename)); - //Does not work when the path is too long - //ze.DateTime = System.IO.File.GetLastWriteTime(filename); - } - - m_zipfile.PutNextEntry(ze); - return m_zipfile; - } - - public Compression(string zipfile) - { - m_writing = false; - m_filename = zipfile; - ICSharpCode.SharpZipLib.Zip.ZipFile zip = new ICSharpCode.SharpZipLib.Zip.ZipFile(zipfile); - } - /// /// Compresses a list of files into a single archive /// @@ -209,41 +130,5 @@ namespace Duplicati.Library.Compression return results; } - - public static List GetAllLines(string file, string path) - { - List res = new List(); - using (new Logging.Timer("GetAllLines " + file + ", path " + path + " (" + new System.IO.FileInfo(file).Length.ToString() + ")")) - { - ICSharpCode.SharpZipLib.Zip.ZipFile zip = new ICSharpCode.SharpZipLib.Zip.ZipFile(file); - ICSharpCode.SharpZipLib.Zip.ZipEntry ze = zip.GetEntry(path); - if (ze != null) - - using (System.IO.StreamReader sr = new System.IO.StreamReader(zip.GetInputStream(ze))) - while(!sr.EndOfStream) - res.Add(sr.ReadLine()); - - zip.Close(); - } - - return res; - } - - #region IDisposable Members - - public void Dispose() - { - try - { - if (m_zipfile != null) - m_zipfile.Close(); - m_zipfile = null; - } - catch - { - } - } - - #endregion } } diff --git a/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj b/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj index 3250ac747..a5af2c1d7 100644 --- a/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj +++ b/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj @@ -17,7 +17,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG prompt 4 @@ -40,6 +40,7 @@ + diff --git a/Duplicati/Library/Compression/FileArchiveZip.cs b/Duplicati/Library/Compression/FileArchiveZip.cs new file mode 100644 index 000000000..62828653b --- /dev/null +++ b/Duplicati/Library/Compression/FileArchiveZip.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Text; + +//The compile flag SHARPZIPLIBWORKS can be set if #ZipLib is able to update archives without corrupting them + +namespace Duplicati.Library.Compression +{ + /// + /// An abstraction of a zip archive as a FileArchive. + /// Due to a very unfortunate Zip implementation, the archive is either read or write, never both + /// + public class FileArchiveZip : Core.IFileArchive + { + /// + /// The archive used for read access + /// + private ICSharpCode.SharpZipLib.Zip.ZipFile m_zip; +#if !SHARPZIPLIBWORKS + private ICSharpCode.SharpZipLib.Zip.ZipOutputStream m_stream; + private FileArchiveZip(ICSharpCode.SharpZipLib.Zip.ZipOutputStream stream) + { + m_stream = stream; + } +#endif + + + public static FileArchiveZip CreateArchive(string filename) + { +#if SHARPZIPLIBWORKS + FileArchiveZip z = new FileArchiveZip(ICSharpCode.SharpZipLib.Zip.ZipFile.Create(filename)); + z.m_zip.BeginUpdate(); + return z; +#else + return new FileArchiveZip(new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(System.IO.File.Create(filename))); +#endif + } + + private FileArchiveZip(ICSharpCode.SharpZipLib.Zip.ZipFile zip) + { + m_zip = zip; + } + + public FileArchiveZip(string file) + : this(new ICSharpCode.SharpZipLib.Zip.ZipFile(file)) + { + } + + private string PathToFilesystem(string path) + { + if (System.IO.Path.DirectorySeparatorChar != '/') + return path.Replace('/', System.IO.Path.DirectorySeparatorChar); + else + return path; + } + + private string PathFromFilesystem(string path) + { + if (System.IO.Path.DirectorySeparatorChar != '/') + return path.Replace(System.IO.Path.DirectorySeparatorChar, '/'); + else + return path; + } + + #region IFileArchive Members + + public string[] ListFiles(string prefix) + { +#if !SHARPZIPLIBWORKS + if (m_zip == null) + throw new Exception("Cannot read data while writing"); +#endif + List results = new List(); + foreach (ICSharpCode.SharpZipLib.Zip.ZipEntry ze in m_zip) + { + string name = PathToFilesystem(ze.Name); + if (prefix == null || name.StartsWith(prefix)) + { + if (!ze.IsDirectory) + results.Add(name); + } + } + + return results.ToArray(); + } + + public string[] ListDirectories(string prefix) + { +#if !SHARPZIPLIBWORKS + if (m_zip == null) + throw new Exception("Cannot read data while writing"); +#endif + + List results = new List(); + foreach (ICSharpCode.SharpZipLib.Zip.ZipEntry ze in m_zip) + { + string name = PathToFilesystem(ze.Name); + if (prefix == null || name.StartsWith(prefix)) + { + if (ze.IsDirectory) + results.Add(Core.Utility.AppendDirSeperator(name)); + } + } + + return results.ToArray(); + } + + public string[] ListEntries(string prefix) + { +#if !SHARPZIPLIBWORKS + if (m_zip == null) + throw new Exception("Cannot read data while writing"); +#endif + + List results = new List(); + foreach (ICSharpCode.SharpZipLib.Zip.ZipEntry ze in m_zip) + { + string name = PathToFilesystem(ze.Name); + if (prefix == null || name.StartsWith(prefix)) + { + if (ze.IsDirectory) + results.Add(Core.Utility.AppendDirSeperator(name)); + else + results.Add(name); + } + } + + return results.ToArray(); + } + + public byte[] ReadAllBytes(string file) + { + using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) + using(System.IO.Stream s = OpenRead(file)) + { + Core.Utility.CopyStream(s, ms); + return ms.ToArray(); + } + } + + public string[] ReadAllLines(string file) + { + List lines = new List(); + using (System.IO.StreamReader sr = new System.IO.StreamReader(OpenRead(file))) + while (!sr.EndOfStream) + lines.Add(sr.ReadLine()); + return lines.ToArray(); + } + + public System.IO.Stream OpenRead(string file) + { +#if !SHARPZIPLIBWORKS + if (m_zip == null) + throw new Exception("Cannot read data while writing"); +#endif + + ICSharpCode.SharpZipLib.Zip.ZipEntry ze = GetEntry(file); + + if (ze == null) + return null; + else + return m_zip.GetInputStream(ze); + } + + public System.IO.Stream OpenWrite(string file) + { + return CreateFile(file); + } + + public void WriteAllBytes(string file, byte[] data) + { + using (System.IO.Stream s = CreateFile(file)) + s.Write(data, 0, data.Length); + } + + public void WriteAllLines(string file, string[] data) + { + using (System.IO.StreamWriter sw = new System.IO.StreamWriter(CreateFile(file))) + foreach (string s in data) + sw.WriteLine(s); + } + + private ICSharpCode.SharpZipLib.Zip.ZipEntry GetEntry(string file) + { + ICSharpCode.SharpZipLib.Zip.ZipEntry ze = m_zip.GetEntry(PathFromFilesystem(file)); + + if (ze == null) //Grrr... The zip library has a pretty relaxed take on seperators + ze = m_zip.GetEntry(PathToFilesystem(file)); + + return ze; + } + + public void DeleteFile(string file) + { +#if !SHARPZIPLIBWORKS + throw new MissingMethodException("Zip does not support deleting"); +#else + if (FileExists(file)) + m_zip.Delete(GetEntry(file)); +#endif + + } + + public System.IO.Stream CreateFile(string file) + { +#if !SHARPZIPLIBWORKS + if (m_stream == null) + throw new Exception("Cannot write while reading"); +#endif + +#if SHARPZIPLIBWORKS + if (FileExists(file)) + DeleteFile(file); + + return new StreamWrapper(new Duplicati.Library.Core.TempFile(), PathFromFilesystem(file), m_zip); +#else + m_stream.PutNextEntry(new ICSharpCode.SharpZipLib.Zip.ZipEntry(PathFromFilesystem(file))); + return new StreamWrapper2(m_stream); +#endif + } + + public void DeleteDirectory(string file) + { +#if SHARPZIPLIBWORKS + if (DirectoryExists(file)) + m_zip.Delete(GetEntry(file)); +#else + throw new MissingMethodException("Zip does not support deleting"); +#endif + } + + public void AddDirectory(string file) + { +#if !SHARPZIPLIBWORKS + if (m_stream == null) + throw new Exception("Cannot write while reading"); +#endif + m_zip.AddDirectory(PathFromFilesystem(file)); + } + + public bool FileExists(string file) + { +#if !SHARPZIPLIBWORKS + if (m_zip == null) + throw new Exception("Cannot read data while writing"); +#endif + return GetEntry(file) != null && GetEntry(file).IsFile; + } + + public bool DirectoryExists(string file) + { +#if !SHARPZIPLIBWORKS + if (m_zip == null) + throw new Exception("Cannot read data while writing"); +#endif + return GetEntry(file) != null && GetEntry(file).IsDirectory; + } + + public long Size + { + get + { +#if !SHARPZIPLIBWORKS + if (m_zip == null) + { + m_stream.Flush(); + return m_stream.Length; + } + else +#endif + return new System.IO.FileInfo(m_zip.Name).Length; + } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (m_zip != null) + { + if (m_zip.IsUpdating) + m_zip.CommitUpdate(); + m_zip.Close(); + +#if SHARPZIPLIBWORKS + //This breaks, because the updates are not flushed correctly + m_zip = new ICSharpCode.SharpZipLib.Zip.ZipFile(m_zip.Name); + m_zip.Close(); +#endif + } + m_zip = null; + +#if !SHARPZIPLIBWORKS + if (m_stream != null) + { + m_stream.Flush(); + m_stream.Finish(); + m_stream.Close(); + } +#endif + } + + #endregion + +#if !SHARPZIPLIBWORKS + + private class StreamWrapper2 : Core.OverrideableStream + { + public StreamWrapper2(System.IO.Stream stream) + : base(stream) + { + } + + protected override void Dispose(bool disposing) + { + ((ICSharpCode.SharpZipLib.Zip.ZipOutputStream)m_basestream).CloseEntry(); + //Don't dispose the stream! + //base.Dispose(disposing); + } + } +#else + + private class StreamWrapper : Core.OverrideableStream, ICSharpCode.SharpZipLib.Zip.IStaticDataSource + { + private ICSharpCode.SharpZipLib.Zip.ZipFile m_zip; + private string m_filename; + private Core.TempFile m_file; + + public StreamWrapper(Core.TempFile file, string filename, ICSharpCode.SharpZipLib.Zip.ZipFile zip) + : base(System.IO.File.Create(file)) + { + m_file = file; + m_zip = zip; + m_filename = filename; + } + + protected override void Dispose(bool disposing) + { + if (m_zip != null) + { + m_basestream.Flush(); + m_basestream.Position = 0; + + if (!m_zip.IsUpdating) + m_zip.BeginUpdate(); + m_zip.Add(this, m_filename); + m_file.Dispose(); + } + m_zip = null; + base.Dispose(disposing); + } + + #region IStaticDataSource Members + + public System.IO.Stream GetSource() + { + return m_basestream; + } + + #endregion + } +#endif + } +} diff --git a/Duplicati/Library/Core/Duplicati.Library.Core.csproj b/Duplicati/Library/Core/Duplicati.Library.Core.csproj index 8a87a8901..77b4f7d95 100644 --- a/Duplicati/Library/Core/Duplicati.Library.Core.csproj +++ b/Duplicati/Library/Core/Duplicati.Library.Core.csproj @@ -35,8 +35,11 @@ + + + diff --git a/Duplicati/Library/Core/FileArchiveDirectory.cs b/Duplicati/Library/Core/FileArchiveDirectory.cs new file mode 100644 index 000000000..9f4c6a5e1 --- /dev/null +++ b/Duplicati/Library/Core/FileArchiveDirectory.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Duplicati.Library.Core +{ + public class FileArchiveDirectory : IFileArchive + { + string m_folder; + + public FileArchiveDirectory(string basefolder) + { + m_folder = Utility.AppendDirSeperator(basefolder); + } + + #region IFileArchive Members + + public string[] ListFiles(string prefix) + { + if (prefix == null) prefix = ""; + return Utility.EnumerateFiles(System.IO.Path.Combine(m_folder, prefix)).ToArray(); + } + + public string[] ListDirectories(string prefix) + { + if (prefix == null) prefix = ""; + return Utility.EnumerateFolders(System.IO.Path.Combine(m_folder, prefix)).ToArray(); + } + + public string[] ListEntries(string prefix) + { + if (prefix == null) prefix = ""; + return Utility.EnumerateFileSystemEntries(System.IO.Path.Combine(m_folder, prefix)).ToArray(); + } + + public byte[] ReadAllBytes(string file) + { + return System.IO.File.ReadAllBytes(System.IO.Path.Combine(m_folder, file)); + } + + public string[] ReadAllLines(string file) + { + return System.IO.File.ReadAllLines(System.IO.Path.Combine(m_folder, file)); + } + + public System.IO.Stream OpenRead(string file) + { + return System.IO.File.OpenRead(System.IO.Path.Combine(m_folder, file)); + } + + public System.IO.Stream OpenWrite(string file) + { + return System.IO.File.OpenWrite(System.IO.Path.Combine(m_folder, file)); + } + + public void WriteAllBytes(string file, byte[] data) + { + System.IO.File.WriteAllBytes(System.IO.Path.Combine(m_folder, file), data); + } + + public void WriteAllLines(string file, string[] data) + { + System.IO.File.WriteAllLines(System.IO.Path.Combine(m_folder, file), data); + } + + public void DeleteFile(string file) + { + System.IO.File.Delete(System.IO.Path.Combine(m_folder, file)); + } + + public System.IO.Stream CreateFile(string file) + { + return System.IO.File.Create(System.IO.Path.Combine(m_folder, file)); + } + + public void DeleteDirectory(string file) + { + System.IO.Directory.Delete(System.IO.Path.Combine(m_folder, file), false); + } + + public void AddDirectory(string file) + { + System.IO.Directory.CreateDirectory(System.IO.Path.Combine(m_folder, file)); + } + + public bool FileExists(string file) + { + return System.IO.File.Exists(System.IO.Path.Combine(m_folder, file)); + } + + public bool DirectoryExists(string file) + { + return System.IO.Directory.Exists(System.IO.Path.Combine(m_folder, file)); + } + + public long Size + { + get + { + //TODO: Much faster with a callback + long size = 0; + foreach (string s in Core.Utility.EnumerateFiles(m_folder)) + size += new System.IO.FileInfo(s).Length; + + return size; + } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + m_folder = null; + } + + #endregion + } +} diff --git a/Duplicati/Library/Core/IFileArchive.cs b/Duplicati/Library/Core/IFileArchive.cs new file mode 100644 index 000000000..19748ad40 --- /dev/null +++ b/Duplicati/Library/Core/IFileArchive.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Duplicati.Library.Core +{ + /// + /// An interface for accessing files in an archive, such as a folder or compressed file + /// + public interface IFileArchive : IDisposable + { + /// + /// Returns all files in the archive, matching the prefix, if any. + /// + /// An optional prefix for limiting the files returned + /// All files in the archive, matching the prefix, if any + string[] ListFiles(string prefix); + /// + /// Returns all directories in the archive, matching the prefix, if any. + /// + /// An optional prefix for limiting the directories returned + /// All directories in the archive, matching the prefix, if any + string[] ListDirectories(string prefix); + /// + /// Returns all files and directories in the archive, matching the prefix, if any. + /// + /// An optional prefix for limiting the files and directories returned + /// All files and directories in the archive, matching the prefix, if any + string[] ListEntries(string prefix); + + /// + /// Returns all the bytes from a given file + /// + /// The file to read data from + /// All bytes from the given file + byte[] ReadAllBytes(string file); + /// + /// Returns all lines in the given file + /// + /// The file to read the data from + /// All lines in the given file + string[] ReadAllLines(string file); + /// + /// Returns a stream with data from the given file + /// + /// The file to read the data from + /// A stream with data from the given file + System.IO.Stream OpenRead(string file); + + /// + /// Returns a stream with data from the given file + /// + /// The file to write the data to + /// A stream with data from the given file + System.IO.Stream OpenWrite(string file); + + /// + /// Writes data to a file + /// + /// The file to write to + /// The data to write + void WriteAllBytes(string file, byte[] data); + /// + /// Writes the given lines to the files + /// + /// The file to write to + /// The data to write + void WriteAllLines(string file, string[] data); + + /// + /// Removes a file from the archive + /// + /// The file to remove + void DeleteFile(string file); + /// + /// Creates a file in the archive + /// + /// The file to create + /// A stream with the data to write into the file + System.IO.Stream CreateFile(string file); + + /// + /// Deletes the named directory, if it is empty + /// + /// The directory to remove + void DeleteDirectory(string file); + + /// + /// Adds a directory to the archive + /// + /// The name of the archive to create + void AddDirectory(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); + + /// + /// Returns a value indicating if the specified directory exists + /// + /// The name of the directory to examine + /// True if the directory exists, false otherwise + bool DirectoryExists(string file); + + /// + /// The total size of the archive + /// + long Size { get; } + } +} diff --git a/Duplicati/Library/Core/OverrideableStream.cs b/Duplicati/Library/Core/OverrideableStream.cs new file mode 100644 index 000000000..ce92c4a67 --- /dev/null +++ b/Duplicati/Library/Core/OverrideableStream.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Duplicati.Library.Core +{ + /// + /// This class is a wrapper for a stream. + /// It's only purpose is to free other wrappers from implementing the boilerplate functions, + /// and allow the derived classes to override single functions. + /// + public class OverrideableStream : Stream + { + protected System.IO.Stream m_basestream; + + public OverrideableStream(Stream basestream) + { + if (basestream == null) + throw new ArgumentNullException("basestream"); + m_basestream = basestream; + } + + 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 void Flush() + { + m_basestream.Flush(); + } + + 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 int Read(byte[] buffer, int offset, int count) + { + return m_basestream.Read(buffer, offset, count); + } + + 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 void Write(byte[] buffer, int offset, int count) + { + m_basestream.Write(buffer, offset, count); + } + + protected override void Dispose(bool disposing) + { + if (m_basestream != null) + m_basestream.Dispose(); + m_basestream = null; + base.Dispose(disposing); + } + + } +} diff --git a/Duplicati/Library/Core/ThrottledStream.cs b/Duplicati/Library/Core/ThrottledStream.cs index 673942c5d..82c930ffe 100644 --- a/Duplicati/Library/Core/ThrottledStream.cs +++ b/Duplicati/Library/Core/ThrottledStream.cs @@ -12,12 +12,8 @@ namespace Duplicati.Library.Core /// This class throttles the rate data can be read or written to the underlying stream. /// This creates a bandwith throttle option for any stream, including a network stream. /// - public class ThrottledStream : Stream + public class ThrottledStream : OverrideableStream { - /// - /// The stream being throttled - /// - private Stream m_basestream; /// /// The max number of bytes pr. second to write /// @@ -64,10 +60,8 @@ namespace Duplicati.Library.Core /// 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, long readspeed, long writespeed) + : base(basestream) { - if (basestream == null) - throw new NullReferenceException("basestream"); - m_basestream = basestream; m_readspeed = readspeed; m_writespeed = writespeed; @@ -77,43 +71,6 @@ namespace Duplicati.Library.Core m_datawritten = new List>(); } - 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 void Flush() - { - m_basestream.Flush(); - } - - 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 int Read(byte[] buffer, int offset, int count) { int bytesRead = DelayIfRequired(true, buffer, ref offset, ref count); @@ -123,16 +80,6 @@ namespace Duplicati.Library.Core return m_basestream.Read(buffer, offset, count) + bytesRead; } - 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 void Write(byte[] buffer, int offset, int count) { DelayIfRequired(true, buffer, ref offset, ref count); diff --git a/Duplicati/Library/Encryption/CryptoStreamWrapper.cs b/Duplicati/Library/Encryption/CryptoStreamWrapper.cs index a0a5d5024..4c4df5fe9 100644 --- a/Duplicati/Library/Encryption/CryptoStreamWrapper.cs +++ b/Duplicati/Library/Encryption/CryptoStreamWrapper.cs @@ -5,85 +5,23 @@ using System.IO; namespace Duplicati.Library.Encryption { - internal class CryptoStreamWrapper : Stream + internal class CryptoStreamWrapper : Core.OverrideableStream { private bool m_hasFlushed = false; - private System.Security.Cryptography.CryptoStream m_basestream; /// /// Wraps a crypto stream, ensuring that it is correctly disposed /// /// The stream to wrape public CryptoStreamWrapper(System.Security.Cryptography.CryptoStream basestream) + : base(basestream) { - if (basestream == null) - throw new NullReferenceException("basestream"); - m_basestream = basestream; } - - 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 void Flush() - { - m_basestream.Flush(); - } - - 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 int Read(byte[] buffer, int offset, int count) - { - return m_basestream.Read(buffer, offset, count); - } - - 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 void Write(byte[] buffer, int offset, int count) - { - m_basestream.Write(buffer, offset, count); - } - protected override void Dispose(bool disposing) { - //This is actually the only thing we want to wrap :( if (!m_hasFlushed && m_basestream.CanWrite) { - m_basestream.FlushFinalBlock(); + ((System.Security.Cryptography.CryptoStream)m_basestream).FlushFinalBlock(); m_hasFlushed = true; } diff --git a/Duplicati/Library/Encryption/EncryptedBackendWrapper.cs b/Duplicati/Library/Encryption/EncryptedBackendWrapper.cs index f8b54a9aa..71e8259c9 100644 --- a/Duplicati/Library/Encryption/EncryptedBackendWrapper.cs +++ b/Duplicati/Library/Encryption/EncryptedBackendWrapper.cs @@ -45,7 +45,11 @@ namespace Duplicati.Library.Encryption m_realbackend = backend; if (options.ContainsKey("gpg-encryption")) + { + if (options.ContainsKey("gpg-program-path")) + GPGEncryption.PGP_PROGRAM = options["gpg-program-path"]; m_encryptionEngine = new GPGEncryption(passphrase, options.ContainsKey("sign-key") ? options["sign-key"] : null); + } else m_encryptionEngine = new AESEncryption(passphrase); } diff --git a/Duplicati/Library/Encryption/GPGStreamWrapper.cs b/Duplicati/Library/Encryption/GPGStreamWrapper.cs index 44099ea50..f150d1c5f 100644 --- a/Duplicati/Library/Encryption/GPGStreamWrapper.cs +++ b/Duplicati/Library/Encryption/GPGStreamWrapper.cs @@ -5,17 +5,17 @@ using System.IO; namespace Duplicati.Library.Encryption { - internal class GPGStreamWrapper : Stream + internal class GPGStreamWrapper : Core.OverrideableStream { private System.Diagnostics.Process m_p; private System.Threading.Thread m_t; - private Stream m_basestream; /// /// Wraps a crypto stream, ensuring that it is correctly disposed /// /// The stream to wrape public GPGStreamWrapper(System.Diagnostics.Process p, System.Threading.Thread t, Stream basestream) + : base(basestream) { if (p == null) throw new NullReferenceException("p"); @@ -24,69 +24,10 @@ namespace Duplicati.Library.Encryption m_p = p; m_t = t; - m_basestream = basestream; - } - - 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 void Flush() - { - m_basestream.Flush(); - } - - 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 int Read(byte[] buffer, int offset, int count) - { - return m_basestream.Read(buffer, offset, count); - } - - 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 void Write(byte[] buffer, int offset, int count) - { - m_basestream.Write(buffer, offset, count); } protected override void Dispose(bool disposing) { - //This is actually the only thing we want to wrap :( if (m_p != null) { m_basestream.Close(); diff --git a/Duplicati/Library/Main/Interface.cs b/Duplicati/Library/Main/Interface.cs index 1a009d534..49e098996 100644 --- a/Duplicati/Library/Main/Interface.cs +++ b/Duplicati/Library/Main/Interface.cs @@ -40,6 +40,12 @@ namespace Duplicati.Library.Main Core.FilenameFilter filter = new Duplicati.Library.Core.FilenameFilter(options); bool full = options.ContainsKey("full"); + int volumesize = (int)Core.Sizeparser.ParseSize(options.ContainsKey("volsize") ? options["volsize"] : "5", "mb"); + volumesize = Math.Max(1024 * 1024, volumesize); + + long totalmax = options.ContainsKey("totalsize") ? Core.Sizeparser.ParseSize(options["totalsize"], "mb") : long.MaxValue; + totalmax = Math.Max(volumesize, totalmax); + backend = Backend.BackendLoader.GetBackend(target, options); if (backend == null) throw new Exception("Unable to find backend for target: " + target); @@ -54,50 +60,34 @@ namespace Duplicati.Library.Main if (!full && options.ContainsKey("full-if-older-than")) full = DateTime.Now > Core.Timeparser.ParseTimeInterval(options["full-if-older-than"], prev[prev.Count - 1].Time); - //TODO: Empty folder creation takes up too much of the processing time - using (Core.TempFolder basefolder = new Duplicati.Library.Core.TempFolder()) + using (Core.TempFolder tempfolder = new Duplicati.Library.Core.TempFolder()) { + List patches = new List(); if (!full) { using (new Logging.Timer("Reading incremental data")) { - foreach (BackupEntry be in prev[prev.Count - 1].SignatureFile) - using (Core.TempFile t = new Duplicati.Library.Core.TempFile()) - { - using (new Logging.Timer("Get " + be.Filename)) - backend.Get(be.Filename, t); - Compression.Compression.Decompress(t, basefolder); - } + List entries = new List(); + entries.Add(prev[prev.Count - 1]); + entries.AddRange(prev[prev.Count - 1].Incrementals); - foreach (BackupEntry be in prev[prev.Count - 1].Incrementals) + foreach (BackupEntry be in entries) foreach (BackupEntry bes in be.SignatureFile) - using (Core.TempFile t = new Duplicati.Library.Core.TempFile()) - { - using (new Logging.Timer("Get " + bes.Filename)) - backend.Get(bes.Filename, t); - - using (Core.TempFolder tf = new Duplicati.Library.Core.TempFolder()) - { - Compression.Compression.Decompress(t, tf); - using (new Logging.Timer("Full signature merge")) - Main.RSync.RSyncDir.MergeSignatures(basefolder, tf); - } - } + { + string filename = System.IO.Path.Combine(tempfolder, "patch-" + patches.Count.ToString() + ".zip"); + using (new Logging.Timer("Get " + bes.Filename)) + backend.Get(bes.Filename, filename); + patches.Add(new Compression.FileArchiveZip(filename)); + } } } DateTime backuptime = DateTime.Now; - using (RSync.RSyncDir dir = new Duplicati.Library.Main.RSync.RSyncDir(source, basefolder, bs)) + using (RSync.RSyncDir dir = new Duplicati.Library.Main.RSync.RSyncDir(source, bs, patches)) { using (new Logging.Timer("Initiating multipass")) dir.InitiateMultiPassDiff(full, filter); - int volumesize = (int)Core.Sizeparser.ParseSize(options.ContainsKey("volsize") ? options["volsize"] : "5", "mb"); - volumesize = Math.Max(1024 * 1024, volumesize); - - long totalmax = options.ContainsKey("totalsize") ? Core.Sizeparser.ParseSize(options["totalsize"], "mb") : long.MaxValue; - totalmax = Math.Max(volumesize, totalmax); - int vol = 0; long totalsize = 0; @@ -109,65 +99,61 @@ namespace Duplicati.Library.Main { using (new Logging.Timer("Multipass " + (vol + 1).ToString())) { - using (Core.TempFile sigzip = new Duplicati.Library.Core.TempFile()) + //The backendwrapper will remove these + Core.TempFile signaturefile = new Duplicati.Library.Core.TempFile(); + Core.TempFile contentfile = new Duplicati.Library.Core.TempFile(); + + signaturefile.Protected = true; + contentfile.Protected = true; + + using (Compression.FileArchiveZip signaturearchive = Duplicati.Library.Compression.FileArchiveZip.CreateArchive(signaturefile)) + using (Compression.FileArchiveZip contentarchive = Duplicati.Library.Compression.FileArchiveZip.CreateArchive(contentfile)) { - using (Compression.Compression signature = new Duplicati.Library.Compression.Compression(dir.m_targetfolder, sigzip)) - { - using (Core.TempFile zf = new Duplicati.Library.Core.TempFile()) - { - done = dir.MakeMultiPassDiff(signature, zf, volumesize); - totalsize += new System.IO.FileInfo(zf).Length; - contenthashes.Add(Core.Utility.CalculateHash(zf)); - using (new Logging.Timer("Writing delta file " + (vol + 1).ToString())) - backend.Put(fns.GenerateFilename(BackupEntry.EntryType.Content, full, backuptime, vol + 1) + ".zip", zf); - - //The backendwrapper will remove it - zf.Protected = true; - } + done = dir.MakeMultiPassDiff(signaturearchive, contentarchive, Math.Min(volumesize, totalmax - totalsize)); + totalsize += new System.IO.FileInfo(contentfile).Length; - totalsize += signature.Size; + totalsize += signaturearchive.Size; - if (totalsize >= totalmax) - dir.FinalizeMultiPass(signature); - } - - signaturehashes.Add(Core.Utility.CalculateHash(sigzip)); - - - using (new Logging.Timer("Writing remote signatures")) - backend.Put(fns.GenerateFilename(BackupEntry.EntryType.Signature, full, backuptime, vol + 1) + ".zip", sigzip); - - //The backendwrapper will remove it - sigzip.Protected = true; + if (totalsize >= totalmax) + dir.FinalizeMultiPass(signaturearchive, contentarchive); } + - using (Core.TempFile mf = new Duplicati.Library.Core.TempFile()) - using (new Logging.Timer("Writing manifest")) - { - System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); - System.Xml.XmlNode root = doc.AppendChild(doc.CreateElement("Manifest")); - root.Attributes.Append(doc.CreateAttribute("version")).Value = "1"; - root.AppendChild(doc.CreateElement("VolumeCount")).InnerText = (vol + 1).ToString(); - System.Xml.XmlNode contentroot = root.AppendChild(doc.CreateElement("ContentFiles")); - System.Xml.XmlNode signatureroot = root.AppendChild(doc.CreateElement("SignatureFiles")); - - foreach (string s in contenthashes) - contentroot.AppendChild(doc.CreateElement("Hash")).InnerText = s; - foreach (string s in signaturehashes) - signatureroot.AppendChild(doc.CreateElement("Hash")).InnerText = s; - - //TODO: Actually read the manifest on restore - doc.Save(mf); - - backend.Put(fns.GenerateFilename(BackupEntry.EntryType.Manifest, full, backuptime) + ".manifest", mf); - - //The backendwrapper will remove it - mf.Protected = true; - } - - vol++; + contenthashes.Add(Core.Utility.CalculateHash(contentfile)); + using (new Logging.Timer("Writing delta file " + (vol + 1).ToString())) + backend.Put(fns.GenerateFilename(BackupEntry.EntryType.Content, full, backuptime, vol + 1) + ".zip", contentfile); + signaturehashes.Add(Core.Utility.CalculateHash(signaturefile)); + using (new Logging.Timer("Writing remote signatures")) + backend.Put(fns.GenerateFilename(BackupEntry.EntryType.Signature, full, backuptime, vol + 1) + ".zip", signaturefile); } + + //The backend wrapper will remove this + Core.TempFile mf = new Duplicati.Library.Core.TempFile(); + mf.Protected = true; + + using (new Logging.Timer("Writing manifest")) + { + System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); + System.Xml.XmlNode root = doc.AppendChild(doc.CreateElement("Manifest")); + root.Attributes.Append(doc.CreateAttribute("version")).Value = "1"; + root.AppendChild(doc.CreateElement("VolumeCount")).InnerText = (vol + 1).ToString(); + System.Xml.XmlNode contentroot = root.AppendChild(doc.CreateElement("ContentFiles")); + System.Xml.XmlNode signatureroot = root.AppendChild(doc.CreateElement("SignatureFiles")); + + foreach (string s in contenthashes) + contentroot.AppendChild(doc.CreateElement("Hash")).InnerText = s; + foreach (string s in signaturehashes) + signatureroot.AppendChild(doc.CreateElement("Hash")).InnerText = s; + + //TODO: Actually read the manifest on restore + doc.Save(mf); + + backend.Put(fns.GenerateFilename(BackupEntry.EntryType.Manifest, full, backuptime) + ".manifest", mf); + } + + vol++; + } } } @@ -207,76 +193,33 @@ namespace Duplicati.Library.Main if (bestFit.EncryptionMode != null) backend = Encryption.EncryptedBackendWrapper.WrapWithEncryption(backend, bestFit.EncryptionMode, options); - using (Core.TempFolder basefolder = new Duplicati.Library.Core.TempFolder()) + List entries = new List(); + entries.Add(bestFit); + entries.AddRange(bestFit.Incrementals); + int patchno = 0; + + using (RSync.RSyncDir sync = new Duplicati.Library.Main.RSync.RSyncDir(target, rs)) { - foreach (BackupEntry be in bestFit.SignatureFile) - using (Core.TempFile basezip = new Duplicati.Library.Core.TempFile()) - { - if (be.CompressionMode != "zip") - throw new Exception("Unexpected compression mode"); - - using (new Logging.Timer("Get " + be.Filename)) - backend.Get(be.Filename, basezip); - - Compression.Compression.Decompress(basezip, basefolder); - } - - foreach (BackupEntry vol in bestFit.ContentVolumes) - using (Core.TempFile basezip = new Duplicati.Library.Core.TempFile()) + foreach (BackupEntry be in entries) + { + //Patch with last volume first, as that has the file/folder lists + be.ContentVolumes.Reverse(); + foreach (BackupEntry vol in be.ContentVolumes) { if (vol.CompressionMode != "zip") throw new Exception("Unexpected compression mode"); - using (new Logging.Timer("Get " + vol.Filename)) - backend.Get(vol.Filename, basezip); - - Compression.Compression.Decompress(basezip, basefolder); - } - - using (RSync.RSyncDir sync = new Duplicati.Library.Main.RSync.RSyncDir(target, basefolder, rs)) - { - - using (new Logging.Timer("Full restore to " + target)) - sync.Restore(target, new List()); - - foreach (BackupEntry p in bestFit.Incrementals) - { - if (p.SignatureFile.Count == 0 || p.ContentVolumes.Count == 0) - throw new Exception("Unable to parse filenames for the volume: " + p.Filename); - - using (Core.TempFolder t = new Duplicati.Library.Core.TempFolder()) + using (Core.TempFile patchzip = new Duplicati.Library.Core.TempFile()) { - foreach (BackupEntry be in p.SignatureFile) - using (Core.TempFile patchzip = new Duplicati.Library.Core.TempFile()) - { - if (be.CompressionMode != "zip") - throw new Exception("Unexpected compression mode"); - - using (new Logging.Timer("Get " + be.Filename)) - backend.Get(be.Filename, patchzip); - - Compression.Compression.Decompress(patchzip, t); - } - - foreach (BackupEntry vol in p.ContentVolumes) - using (Core.TempFile patchzip = new Duplicati.Library.Core.TempFile()) - { - if (vol.CompressionMode != "zip") - throw new Exception("Unexpected compression mode"); - - using (new Logging.Timer("Get " + vol.Filename)) - backend.Get(vol.Filename, patchzip); - - Compression.Compression.Decompress(patchzip, t); - } - - using (new Logging.Timer("Incremental patch " + p.Time.ToString())) - sync.Patch(target, t); + using (new Logging.Timer("Get " + vol.Filename)) + backend.Get(vol.Filename, patchzip); + using (new Logging.Timer((patchno == 0 ? "Full restore to: " : "Incremental restore " + patchno.ToString() + " to: ") + target)) + sync.Patch(target, new Compression.FileArchiveZip(patchzip)); } + patchno++; } } - } } finally @@ -554,26 +497,11 @@ namespace Duplicati.Library.Main using (backend) using (Core.TempFolder basefolder = new Duplicati.Library.Core.TempFolder()) { - foreach (BackupEntry be in bestFit.SignatureFile) - using (Core.TempFile basezip = new Duplicati.Library.Core.TempFile()) - { - if (be.CompressionMode != "zip") - throw new Exception("Unexpected compression mode"); + List entries = new List(); + entries.Add(bestFit); + entries.AddRange(bestFit.Incrementals); - using (new Logging.Timer("Get " + be.Filename)) - backend.Get(be.Filename, basezip); - - foreach (string s in Compression.Compression.ListFiles(basezip)) - if (s.StartsWith(RSync.RSyncDir.SIGNATURE_ROOT)) - res[s.Substring(RSync.RSyncDir.SIGNATURE_ROOT.Length + 1)] = null; - - foreach (string s in Compression.Compression.GetAllLines(basezip, RSync.RSyncDir.ADDED_FOLDERS)) - res[NormalizeZipDirName(s)] = null; - - //No deleted files or folders are possible here - } - - foreach (BackupEntry p in bestFit.Incrementals) + foreach (BackupEntry p in entries) { if (p.SignatureFile.Count == 0) throw new Exception("Unable to parse filenames for the volume: " + p.Filename); @@ -589,21 +517,24 @@ namespace Duplicati.Library.Main using (new Logging.Timer("Get " + be.Filename)) backend.Get(be.Filename, patchzip); + Core.IFileArchive archive = new Compression.FileArchiveZip(patchzip); - foreach (string s in Compression.Compression.GetAllLines(patchzip, RSync.RSyncDir.DELETED_FOLDERS)) - if (res.ContainsKey(NormalizeZipDirName(s))) - res.Remove(NormalizeZipDirName(s)); + if (archive.FileExists(RSync.RSyncDir.DELETED_FOLDERS)) + foreach (string s in archive.ReadAllLines(RSync.RSyncDir.DELETED_FOLDERS)) + if (res.ContainsKey(NormalizeZipDirName(s))) + res.Remove(NormalizeZipDirName(s)); - foreach (string s in Compression.Compression.GetAllLines(patchzip, RSync.RSyncDir.DELETED_FILES)) - if (res.ContainsKey(NormalizeZipFileName(s))) - res.Remove(NormalizeZipFileName(s)); + if (archive.FileExists(RSync.RSyncDir.DELETED_FILES)) + foreach (string s in archive.ReadAllLines(RSync.RSyncDir.DELETED_FILES)) + if (res.ContainsKey(NormalizeZipFileName(s))) + res.Remove(NormalizeZipFileName(s)); - foreach (string s in Compression.Compression.ListFiles(patchzip)) - if (s.StartsWith(RSync.RSyncDir.SIGNATURE_ROOT)) - res[s.Substring(RSync.RSyncDir.SIGNATURE_ROOT.Length + 1)] = null; + foreach (string s in archive.ListFiles(RSync.RSyncDir.SIGNATURE_ROOT)) + res[s.Substring(RSync.RSyncDir.SIGNATURE_ROOT.Length + 1)] = null; - foreach (string s in Compression.Compression.GetAllLines(patchzip, RSync.RSyncDir.ADDED_FOLDERS)) - res[NormalizeZipDirName(s)] = null; + if (archive.FileExists(RSync.RSyncDir.ADDED_FOLDERS)) + foreach (string s in archive.ReadAllLines(RSync.RSyncDir.ADDED_FOLDERS)) + res[NormalizeZipDirName(s)] = null; } } } diff --git a/Duplicati/Library/Main/RSyncDir.cs b/Duplicati/Library/Main/RSyncDir.cs index 294707d2b..2154fee41 100644 --- a/Duplicati/Library/Main/RSyncDir.cs +++ b/Duplicati/Library/Main/RSyncDir.cs @@ -43,21 +43,13 @@ namespace Duplicati.Library.Main.RSync /// This is the folder being backed up /// public string m_sourcefolder; - /// - /// This is the folder containing signatures for the previous backup - /// - public string m_basefolder; - /// - /// This is the folder that gets filled with the next backup - /// - public Core.TempFolder m_targetfolder; /// /// This is a list of existing file signatures. /// Key is path to the file. /// value is path to the signature file. /// - private Dictionary m_oldSignatures; + private Dictionary m_oldSignatures; /// /// This is a list of existing folders. /// @@ -126,58 +118,52 @@ namespace Duplicati.Library.Main.RSync /// private bool m_finalized = false; - - - /// /// This is a list of unprocessed files, used in multipass runs /// private List m_unproccesed; - public RSyncDir(string sourcefolder, string basefolder, CommunicationStatistics stat) + public RSyncDir(string sourcefolder, CommunicationStatistics stat, List patches) + : this(sourcefolder, stat) + { + string prefix = Core.Utility.AppendDirSeperator(SIGNATURE_ROOT); + + foreach (Core.IFileArchive z in patches) + { + if (z.FileExists(DELETED_FILES)) + foreach (string s in FilenamesFromPlatformIndependant(z.ReadAllLines(DELETED_FILES))) + if (m_oldSignatures.ContainsKey(s)) + m_oldSignatures.Remove(s); + + foreach (string f in FilenamesFromPlatformIndependant(z.ListFiles(prefix))) + m_oldSignatures[f.Substring(prefix.Length)] = z; + + if (z.FileExists(DELETED_FOLDERS)) + foreach (string s in FilenamesFromPlatformIndependant(z.ReadAllLines(DELETED_FOLDERS))) + if (m_oldFolders.ContainsKey(s)) + m_oldFolders.Remove(s); + + if (z.FileExists(ADDED_FOLDERS)) + foreach (string s in FilenamesFromPlatformIndependant(z.ReadAllLines(ADDED_FOLDERS))) + m_oldFolders.Add(s, s); + } + } + + public RSyncDir(string sourcefolder, CommunicationStatistics stat) { if (!System.IO.Path.IsPathRooted(sourcefolder)) sourcefolder = System.IO.Path.GetFullPath(sourcefolder); - if (!System.IO.Path.IsPathRooted(basefolder)) - sourcefolder = System.IO.Path.GetFullPath(basefolder); - m_sourcefolder = Core.Utility.AppendDirSeperator(sourcefolder); - m_basefolder = Core.Utility.AppendDirSeperator(basefolder); - m_targetfolder = new Duplicati.Library.Core.TempFolder(); - m_stat = stat; - - PrepareTargetFolder(); - BuildIndex(); - } - - private void PrepareTargetFolder() - { - System.IO.Directory.CreateDirectory(System.IO.Path.Combine(m_targetfolder, SIGNATURE_ROOT)); - System.IO.Directory.CreateDirectory(System.IO.Path.Combine(m_targetfolder, CONTENT_ROOT)); - System.IO.Directory.CreateDirectory(System.IO.Path.Combine(m_targetfolder, DELTA_ROOT)); - } - - private void BuildIndex() - { - if (m_basefolder == null || !System.IO.Directory.Exists(m_basefolder)) - { - m_oldSignatures = new Dictionary(); - m_oldFolders = new Dictionary(); - return; - } - m_basefolder = Core.Utility.AppendDirSeperator(m_basefolder); - - m_oldSignatures = new Dictionary(); - string sigfolder = Core.Utility.AppendDirSeperator(System.IO.Path.Combine(m_basefolder, SIGNATURE_ROOT)); - - foreach (string s in Core.Utility.EnumerateFiles(sigfolder)) - m_oldSignatures.Add(s.Substring(sigfolder.Length), s); - + m_oldSignatures = new Dictionary(); m_oldFolders = new Dictionary(); - if (System.IO.File.Exists(System.IO.Path.Combine(m_basefolder, ADDED_FOLDERS))) - foreach (string s in FilenamesFromPlatformIndependant(System.IO.File.ReadAllLines(System.IO.Path.Combine(m_basefolder, ADDED_FOLDERS)))) - m_oldFolders.Add(s, s); + m_sourcefolder = Core.Utility.AppendDirSeperator(sourcefolder); + m_stat = stat; + } - + public void CreatePatch(Core.IFileArchive signatures, Core.IFileArchive content) + { + InitiateMultiPassDiff(false, null); + MakeMultiPassDiff(signatures, content, long.MaxValue); + FinalizeMultiPass(signatures, content); } public void InitiateMultiPassDiff(bool full, Core.FilenameFilter filter) @@ -185,7 +171,7 @@ namespace Duplicati.Library.Main.RSync if (full) { m_oldFolders = new Dictionary(); - m_oldSignatures = new Dictionary(); + m_oldSignatures = new Dictionary(); } m_newfiles = new Dictionary(); @@ -195,9 +181,12 @@ namespace Duplicati.Library.Main.RSync m_deletedfolders = new List(); //TODO: Figure out how to make this faster, but still random + //Perhaps use itterative callbacks, with random recurse or itterate on each folder m_unproccesed = Core.Utility.EnumerateFileSystemEntries(m_sourcefolder, filter); m_totalfiles = m_unproccesed.Count; + + //Build folder diffs string dirmarker = System.IO.Path.DirectorySeparatorChar.ToString(); for(int i = 0; i < m_unproccesed.Count; i++) { @@ -213,44 +202,35 @@ namespace Duplicati.Library.Main.RSync m_unproccesed.RemoveAt(i); i--; } - } m_deletedfolders = new List(); m_deletedfolders.AddRange(m_oldFolders.Keys); } - public void FinalizeMultiPass(Compression.Compression sigfile) + public void FinalizeMultiPass(Core.IFileArchive signaturefile, Core.IFileArchive contentfile) { if (!m_finalized) { if (m_unproccesed.Count == 0) m_deletedfiles.AddRange(m_oldSignatures.Keys); + if (m_deletedfiles.Count > 0) { - System.IO.StreamWriter sw = new StreamWriter(sigfile.AddStream(DELETED_FILES)); - foreach (string s in FilenamesToPlatformIndependant(m_deletedfiles.ToArray())) - sw.WriteLine(s); - - sw.Flush(); + signaturefile.WriteAllLines(DELETED_FILES, m_deletedfiles.ToArray()); + contentfile.WriteAllLines(DELETED_FILES, m_deletedfiles.ToArray()); } if (m_deletedfolders.Count > 0) { - System.IO.StreamWriter sw = new StreamWriter(sigfile.AddStream(DELETED_FOLDERS)); - foreach (string s in FilenamesToPlatformIndependant(m_deletedfolders.ToArray())) - sw.WriteLine(s); - - sw.Flush(); + signaturefile.WriteAllLines(DELETED_FOLDERS, m_deletedfolders.ToArray()); + contentfile.WriteAllLines(DELETED_FOLDERS, m_deletedfolders.ToArray()); } if (m_newfolders.Count > 0) { - System.IO.StreamWriter sw = new StreamWriter(sigfile.AddStream(ADDED_FOLDERS)); - foreach (string s in FilenamesToPlatformIndependant(m_newfolders.ToArray())) - sw.WriteLine(s); - - sw.Flush(); + signaturefile.WriteAllLines(ADDED_FOLDERS, m_newfolders.ToArray()); + contentfile.WriteAllLines(ADDED_FOLDERS, m_newfolders.ToArray()); } if (m_stat is BackupStatistics) @@ -273,7 +253,7 @@ namespace Duplicati.Library.Main.RSync } } - public bool MakeMultiPassDiff(Compression.Compression sigfile, string zipfile, long volumesize) + public bool MakeMultiPassDiff(Core.IFileArchive signaturefile, Core.IFileArchive contentfile, long volumesize) { if (m_unproccesed == null) throw new Exception("Multi pass is not initialized"); @@ -281,39 +261,37 @@ namespace Duplicati.Library.Main.RSync Random r = new Random(); long totalSize = 0; - using (Compression.Compression c = new Duplicati.Library.Compression.Compression(m_targetfolder, zipfile)) + while (m_unproccesed.Count > 0 && totalSize < volumesize) { - while (m_unproccesed.Count > 0 && totalSize < volumesize) + int next = r.Next(0, m_unproccesed.Count); + string s = m_unproccesed[next]; + m_unproccesed.RemoveAt(next); + + try { - int next = r.Next(0, m_unproccesed.Count); - string s = m_unproccesed[next]; - m_unproccesed.RemoveAt(next); - - try - { - using (System.IO.FileStream fs = System.IO.File.Open(s, FileMode.Open, FileAccess.Read, FileShare.Read)) - if (ProccessDiff(fs, s, sigfile)) - totalSize = AddFileToCompression(fs, s, c); - } - catch (Exception ex) - { - if (m_stat != null) - m_stat.LogError("Failed to process file: \"" + s + "\", Error message: " + ex.Message); - Logging.Log.WriteMessage("Failed to process file: \"" + s + "\"", Duplicati.Library.Logging.LogMessageType.Error, ex); - } + using (System.IO.FileStream fs = System.IO.File.Open(s, FileMode.Open, FileAccess.Read, FileShare.Read)) + if (ProccessDiff(fs, s, signaturefile)) + totalSize = AddFileToCompression(fs, s, contentfile); + } + catch (Exception ex) + { + if (m_stat != null) + m_stat.LogError("Failed to process file: \"" + s + "\", Error message: " + ex.Message); + Logging.Log.WriteMessage("Failed to process file: \"" + s + "\"", Duplicati.Library.Logging.LogMessageType.Error, ex); } - - if (m_unproccesed.Count == 0) - FinalizeMultiPass(sigfile); } + if (m_unproccesed.Count == 0) + FinalizeMultiPass(signaturefile, contentfile); + + return m_unproccesed.Count == 0; } - private bool ProccessDiff(System.IO.FileStream fs, string s, Compression.Compression sigfile) + private bool ProccessDiff(System.IO.FileStream fs, string s, Core.IFileArchive signaturefile) { string relpath = s.Substring(m_sourcefolder.Length); - string target = System.IO.Path.Combine(System.IO.Path.Combine(m_targetfolder, SIGNATURE_ROOT), relpath); + string target = System.IO.Path.Combine(SIGNATURE_ROOT, relpath); using (System.IO.MemoryStream ms = new MemoryStream()) { @@ -323,7 +301,8 @@ namespace Duplicati.Library.Main.RSync if (!m_oldSignatures.ContainsKey(relpath)) { - Core.Utility.CopyStream(ms, sigfile.AddStream(target), true); + using(System.IO.Stream s2 = signaturefile.CreateFile(target)) + Core.Utility.CopyStream(ms, s2, true); m_newfiles.Add(s, null); return true; } @@ -332,14 +311,17 @@ namespace Duplicati.Library.Main.RSync ms.Position = 0; bool equals; - using (System.IO.FileStream fs2 = System.IO.File.OpenRead(m_oldSignatures[relpath])) - equals = Core.Utility.CompareStreams(fs2, ms, true); + + //File in archive + using (System.IO.Stream s2 = m_oldSignatures[relpath].OpenRead(target)) + equals = Core.Utility.CompareStreams(s2, ms, false); if (!equals) { - Core.Utility.CopyStream(ms, sigfile.AddStream(target), true); + //TODO: It's really bad if the signature is present, but not the content + using(System.IO.Stream s3 = signaturefile.CreateFile(target)) + Core.Utility.CopyStream(ms, s3, true); m_modifiedFiles.Add(s, null); - m_oldSignatures.Remove(relpath); return true; } else @@ -352,73 +334,78 @@ namespace Duplicati.Library.Main.RSync } - private long AddFileToCompression(System.IO.FileStream fs, string s, Compression.Compression c) + private long AddFileToCompression(System.IO.FileStream fs, string s, Core.IFileArchive contentfile) { fs.Position = 0; if (m_modifiedFiles.ContainsKey(s)) { string relpath = s.Substring(m_sourcefolder.Length); - string target = System.IO.Path.Combine(System.IO.Path.Combine(m_targetfolder, DELTA_ROOT), relpath); - string signature = System.IO.Path.Combine(System.IO.Path.Combine(m_basefolder, SIGNATURE_ROOT), relpath); + string target = System.IO.Path.Combine(DELTA_ROOT, relpath); + string signature = System.IO.Path.Combine(SIGNATURE_ROOT, relpath); - using (System.IO.FileStream sigfs = System.IO.File.OpenRead(signature)) + using (System.IO.Stream sigfs = m_oldSignatures[relpath].OpenRead(signature)) { - System.IO.Stream s3 = c.AddStream(target); - SharpRSync.Interface.GenerateDelta(sigfs, fs, s3); - //TODO: s3.Length the size of the archive + using(System.IO.Stream s3 = contentfile.CreateFile(target)) + SharpRSync.Interface.GenerateDelta(sigfs, fs, s3); + + //TODO: s3.Length is the size of the archive //m_diffsize += s3.Length; m_diffedfilessize += fs.Length; m_diffedfiles++; } m_modifiedFiles.Remove(s); + m_oldSignatures.Remove(relpath); } else { string relpath = s.Substring(m_sourcefolder.Length); - string target = System.IO.Path.Combine(System.IO.Path.Combine(m_targetfolder, CONTENT_ROOT), relpath); + string target = System.IO.Path.Combine(CONTENT_ROOT, relpath); - Core.Utility.CopyStream(fs, c.AddStream(target)); + using(System.IO.Stream s3 = contentfile.CreateFile(target)) + Core.Utility.CopyStream(fs, s3); + m_addedfiles++; m_addedfilessize += fs.Length; m_newfiles.Remove(s); } - return c.Size; + + return contentfile.Size; } public bool HasChanges { get { return m_newfiles.Count > 0 || m_modifiedFiles.Count > 0; } } - public List EnumerateSourceFolders() + /*public List EnumerateSourceFolders() { List folders = Core.Utility.EnumerateFolders(m_sourcefolder); for (int i = 0; i < folders.Count; i++) folders[i] = System.IO.Path.Combine(System.IO.Path.Combine(m_targetfolder, SIGNATURE_ROOT), folders[i].Substring(m_sourcefolder.Length)); return folders; - } + }*/ - public void Restore(string destination, List patchfolders) + public void Restore(string destination, List patches) { - Patch(destination, m_basefolder); - - if (patchfolders != null) - foreach (string s in patchfolders) + if (patches != null) + foreach (Core.IFileArchive s in patches) Patch(destination, s); } - public void Patch(string destination, string patch) + public void Patch(string destination, Core.IFileArchive patch) { - string deletedfiles = System.IO.Path.Combine(patch, DELETED_FILES); - if (System.IO.File.Exists(deletedfiles)) - foreach (string s in FilenamesFromPlatformIndependant(System.IO.File.ReadAllLines(deletedfiles))) + destination = Core.Utility.AppendDirSeperator(destination); + + if (patch.FileExists(DELETED_FILES)) + foreach (string s in FilenamesFromPlatformIndependant(patch.ReadAllLines(DELETED_FILES))) { string target = System.IO.Path.Combine(destination, s.Trim()); if (System.IO.File.Exists(target)) { try { + //TODO: Perhaps read ahead in patches to prevent creation System.IO.File.Delete(target); } catch (Exception ex) @@ -434,20 +421,20 @@ namespace Duplicati.Library.Main.RSync } } - string deletedfolders = System.IO.Path.Combine(patch, DELETED_FOLDERS); - if (System.IO.File.Exists(deletedfolders)) + if (patch.FileExists(DELETED_FOLDERS)) { + string[] deletedfolders = FilenamesFromPlatformIndependant(patch.ReadAllLines(DELETED_FOLDERS)); //Make sure subfolders are deleted first - string[] folderlist = FilenamesFromPlatformIndependant(System.IO.File.ReadAllLines(deletedfolders)); - Array.Sort(folderlist); - Array.Reverse(folderlist); + Array.Sort(deletedfolders); + Array.Reverse(deletedfolders); - foreach (string s in folderlist) + foreach (string s in deletedfolders) { string target = System.IO.Path.Combine(destination, s.Trim()); if (System.IO.Directory.Exists(target)) try { + //TODO: Perhaps read ahead in patches to prevent creation System.IO.Directory.Delete(target, false); } catch (Exception ex) @@ -456,19 +443,18 @@ namespace Duplicati.Library.Main.RSync m_stat.LogError("Failed to remove folder: \"" + target + "\", Error message: " + ex.Message); Logging.Log.WriteMessage("Failed to remove folder: " + target, Duplicati.Library.Logging.LogMessageType.Warning, ex); } - } } - string addedfolders = System.IO.Path.Combine(patch, ADDED_FOLDERS); - if (System.IO.File.Exists(addedfolders)) - { - //Make sure subfolders are deleted first - string[] folderlist = FilenamesFromPlatformIndependant(System.IO.File.ReadAllLines(addedfolders)); - Array.Sort(folderlist); - Array.Reverse(folderlist); - foreach (string s in folderlist) + if (patch.FileExists(ADDED_FOLDERS)) + { + string[] addedfolders = FilenamesFromPlatformIndependant(patch.ReadAllLines(ADDED_FOLDERS)); + + //Make sure topfolders are created first + Array.Sort(addedfolders); + + foreach (string s in addedfolders) { string target = System.IO.Path.Combine(destination, s.Trim()); if (!System.IO.Directory.Exists(target)) @@ -485,50 +471,63 @@ namespace Duplicati.Library.Main.RSync } } + string prefix = Core.Utility.AppendDirSeperator(CONTENT_ROOT); - string sourcefolder = Core.Utility.AppendDirSeperator(System.IO.Path.Combine(patch, CONTENT_ROOT)); - destination = Core.Utility.AppendDirSeperator(destination); - - //TODO: Handle file access exceptions - foreach (string s in Core.Utility.EnumerateFiles(sourcefolder)) + foreach (string s in patch.ListFiles(prefix)) { - string relpath = s.Substring(sourcefolder.Length); - string target = System.IO.Path.Combine(destination, relpath); + string target = System.IO.Path.Combine(destination, s.Substring(prefix.Length)); try { - System.IO.File.Copy(s, target); + if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(target))) + { + Logging.Log.WriteMessage("Folder did not exist on restore " + target, Duplicati.Library.Logging.LogMessageType.Warning); + System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(target)); + } + + using (System.IO.Stream s1 = patch.OpenRead(s)) + using (System.IO.FileStream s2 = System.IO.File.Create(target)) + Core.Utility.CopyStream(s1, s2); } catch (Exception ex) { - if (m_stat != null) - m_stat.LogError("Failed to restore file: \"" + relpath + "\", Error message: " + ex.Message); - Logging.Log.WriteMessage("Failed to restore file " + relpath, Duplicati.Library.Logging.LogMessageType.Error, ex); + if (m_stat != null) + m_stat.LogError("Failed to restore file: \"" + s + "\", Error message: " + ex.Message); + Logging.Log.WriteMessage("Failed to restore file " + s, Duplicati.Library.Logging.LogMessageType.Error, ex); } } - sourcefolder = Core.Utility.AppendDirSeperator(System.IO.Path.Combine(patch, DELTA_ROOT)); - - foreach (string s in Core.Utility.EnumerateFiles(sourcefolder)) + prefix = Core.Utility.AppendDirSeperator(DELTA_ROOT); + foreach (string s in patch.ListFiles(prefix)) { - string relpath = s.Substring(sourcefolder.Length); - string target = System.IO.Path.Combine(destination, relpath); - //string source = System.IO.Path.Combine(sourcefolder, relpath) - - string tempfile = System.IO.Path.GetTempFileName(); + string target = System.IO.Path.Combine(destination, s.Substring(prefix.Length)); try { - SharpRSync.Interface.PatchFile(target, s, tempfile); - System.IO.File.Delete(target); - System.IO.File.Move(tempfile, target); + if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(target))) + { + Logging.Log.WriteMessage("Folder did not exist on restore by delta " + target, Duplicati.Library.Logging.LogMessageType.Warning); + System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(target)); + } + + using (Core.TempFile tempfile = new Core.TempFile()) + { + using (System.IO.FileStream s1 = System.IO.File.OpenRead(target)) + using (System.IO.Stream s2 = patch.OpenRead(s)) + using (System.IO.FileStream s3 = System.IO.File.Create(tempfile)) + SharpRSync.Interface.PatchFile(s1, s2, s3); + + System.IO.File.Delete(target); + System.IO.File.Move(tempfile, target); + } } catch (Exception ex) { - if (m_stat != null) - m_stat.LogError("Failed to restore file: \"" + relpath + "\", Error message: " + ex.Message); - Logging.Log.WriteMessage("Failed to restore file " + relpath, Duplicati.Library.Logging.LogMessageType.Error, ex); - System.IO.File.Delete(target); - System.IO.File.Delete(tempfile); + if (m_stat != null) + m_stat.LogError("Failed to restore file: \"" + s + "\", Error message: " + ex.Message); + Logging.Log.WriteMessage("Failed to restore file " + s, Duplicati.Library.Logging.LogMessageType.Error, ex); + + try { System.IO.File.Delete(target); } + catch { } } } @@ -539,11 +538,6 @@ namespace Duplicati.Library.Main.RSync public void Dispose() { - if (m_targetfolder != null) - { - m_targetfolder.Dispose(); - m_targetfolder = null; - } } #endregion diff --git a/thirdparty/SharpZipLib/ICSharpCode.SharpZipLib.dll b/thirdparty/SharpZipLib/ICSharpCode.SharpZipLib.dll index bcdb00ea3..e829ebf4b 100644 Binary files a/thirdparty/SharpZipLib/ICSharpCode.SharpZipLib.dll and b/thirdparty/SharpZipLib/ICSharpCode.SharpZipLib.dll differ