diff --git a/BuildTools/LocalizationTool/LocalizationTool/LocalizationTool.csproj b/BuildTools/LocalizationTool/LocalizationTool/LocalizationTool.csproj index 31688c6e6..7cced1e30 100644 --- a/BuildTools/LocalizationTool/LocalizationTool/LocalizationTool.csproj +++ b/BuildTools/LocalizationTool/LocalizationTool/LocalizationTool.csproj @@ -1,4 +1,4 @@ - + Debug @@ -13,7 +13,7 @@ 3.5 - v3.5 + v4.0 publish\ @@ -31,6 +31,7 @@ false false true + true diff --git a/BuildTools/LocalizationTool/LocalizationTool/Properties/Resources.Designer.cs b/BuildTools/LocalizationTool/LocalizationTool/Properties/Resources.Designer.cs index 16112a443..715b5c1f5 100644 --- a/BuildTools/LocalizationTool/LocalizationTool/Properties/Resources.Designer.cs +++ b/BuildTools/LocalizationTool/LocalizationTool/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.239 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/BuildTools/LocalizationTool/LocalizationTool/Properties/Settings.Designer.cs b/BuildTools/LocalizationTool/LocalizationTool/Properties/Settings.Designer.cs index 77898c028..5bedeaa55 100644 --- a/BuildTools/LocalizationTool/LocalizationTool/Properties/Settings.Designer.cs +++ b/BuildTools/LocalizationTool/LocalizationTool/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.239 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/BuildTools/LocalizationTool/LocalizationTool/app.config b/BuildTools/LocalizationTool/LocalizationTool/app.config index 7a88f0c69..ad9e257c4 100644 --- a/BuildTools/LocalizationTool/LocalizationTool/app.config +++ b/BuildTools/LocalizationTool/LocalizationTool/app.config @@ -1,5 +1,5 @@ - + - \ No newline at end of file + diff --git a/BuildTools/WixProjBuilder/Program.cs b/BuildTools/WixProjBuilder/Program.cs index 8e9413d1d..9ee5e411c 100644 --- a/BuildTools/WixProjBuilder/Program.cs +++ b/BuildTools/WixProjBuilder/Program.cs @@ -28,7 +28,7 @@ namespace WixProjBuilder static void Main(string[] _args) { List args = new List(_args); - Dictionary options = Duplicati.CommandLine.CommandLineParser.ExtractOptions(args); + Dictionary options = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args); if (args.Count != 1) { diff --git a/BuildTools/WixProjBuilder/WixProjBuilder.csproj b/BuildTools/WixProjBuilder/WixProjBuilder.csproj index 1c0b515c2..2b32bc59c 100644 --- a/BuildTools/WixProjBuilder/WixProjBuilder.csproj +++ b/BuildTools/WixProjBuilder/WixProjBuilder.csproj @@ -10,12 +10,13 @@ Properties WixProjBuilder WixProjBuilder - v2.0 + v4.0 512 3.5 + true @@ -46,15 +47,18 @@ - - CommandLineParser.cs - + + + {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65} + Duplicati.Library.Utility + + - \ No newline at end of file + diff --git a/Duplicati/CommandLine/BackendTester/Program.cs b/Duplicati/CommandLine/BackendTester/Program.cs index bd2a38b99..eb97fe079 100644 --- a/Duplicati/CommandLine/BackendTester/Program.cs +++ b/Duplicati/CommandLine/BackendTester/Program.cs @@ -51,7 +51,7 @@ namespace Duplicati.CommandLine.BackendTester try { List args = new List(_args); - Dictionary options = CommandLineParser.ExtractOptions(args); + Dictionary options = Library.Utility.CommandLineParser.ExtractOptions(args); if (!options.ContainsKey("ftp_password") && !string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("FTP_PASSWORD"))) options["ftp_password"] = System.Environment.GetEnvironmentVariable("FTP_PASSWORD"); diff --git a/Duplicati/CommandLine/BackendTester/app.config b/Duplicati/CommandLine/BackendTester/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/CommandLine/BackendTester/app.config +++ b/Duplicati/CommandLine/BackendTester/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/CommandLine/Decrypter/Duplicati.CommandLine.Decrypter.csproj b/Duplicati/CommandLine/Decrypter/Duplicati.CommandLine.Decrypter.csproj index 0e62b8804..571bb4d74 100644 --- a/Duplicati/CommandLine/Decrypter/Duplicati.CommandLine.Decrypter.csproj +++ b/Duplicati/CommandLine/Decrypter/Duplicati.CommandLine.Decrypter.csproj @@ -1,4 +1,4 @@ - + Debug @@ -14,7 +14,6 @@ 3.5 - v2.0 true @@ -44,6 +43,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -52,6 +53,7 @@ pdbonly x86 prompt + 4 @@ -80,6 +82,7 @@ + diff --git a/Duplicati/CommandLine/Decrypter/app.config b/Duplicati/CommandLine/Decrypter/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/CommandLine/Decrypter/app.config +++ b/Duplicati/CommandLine/Decrypter/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/CommandLine/Duplicati.CommandLine.csproj b/Duplicati/CommandLine/Duplicati.CommandLine.csproj index 93b92136e..56f259321 100644 --- a/Duplicati/CommandLine/Duplicati.CommandLine.csproj +++ b/Duplicati/CommandLine/Duplicati.CommandLine.csproj @@ -1,4 +1,4 @@ - + Debug @@ -16,7 +16,6 @@ 3.5 - v2.0 @@ -44,6 +43,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -52,6 +53,7 @@ pdbonly x86 prompt + 4 @@ -59,7 +61,6 @@ - @@ -94,13 +95,9 @@ {CCD76347-7DC7-4B42-B7E1-E500E624CAC3} Duplicati.Library.Backend.SkyDrive - - {6BA21DCB-1701-4936-B177-3B6D4DE3474E} - Duplicati.Library.Backend.SSH - - - {C0270709-2A40-43B5-8CF1-69581B9FA2A1} - Duplicati.Library.Backend.TahoeLAFS + + {FF2BF37C-E502-4C98-BEA0-701671DDFA08} + Duplicati.Library.Backend.SSHv2 {BAE27510-8B5D-44B2-B33E-372A98908041} diff --git a/Duplicati/CommandLine/Program.cs b/Duplicati/CommandLine/Program.cs index 520fc43fd..d26e94897 100644 --- a/Duplicati/CommandLine/Program.cs +++ b/Duplicati/CommandLine/Program.cs @@ -33,7 +33,7 @@ namespace Duplicati.CommandLine { List cargs = new List(args); string filter = Duplicati.Library.Utility.FilenameFilter.EncodeAsFilter(Duplicati.Library.Utility.FilenameFilter.ParseCommandLine(cargs, true)); - Dictionary options = CommandLineParser.ExtractOptions(cargs); + Dictionary options = Library.Utility.CommandLineParser.ExtractOptions(cargs); foreach (string internaloption in Library.Main.Options.InternalOptions) if (options.ContainsKey(internaloption)) diff --git a/Duplicati/CommandLine/app.config b/Duplicati/CommandLine/app.config index 41bc14719..a7d04fa82 100644 --- a/Duplicati/CommandLine/app.config +++ b/Duplicati/CommandLine/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/GUI/Datamodel/Datamodel.csproj b/Duplicati/GUI/Datamodel/Datamodel.csproj index b9ed9d672..abb6c662a 100644 --- a/Duplicati/GUI/Datamodel/Datamodel.csproj +++ b/Duplicati/GUI/Datamodel/Datamodel.csproj @@ -1,4 +1,4 @@ - + Local @@ -7,7 +7,6 @@ {B60D0993-B0C5-457A-90D9-DB6EEB33CE47} Debug AnyCPU - Datamodel Duplicati.snk @@ -18,19 +17,15 @@ Library Duplicati.Datamodel OnBuildSuccess - true 3.5 - v2.0 bin\Debug\ - false 285212672 - false DEBUG;TRACE @@ -38,36 +33,25 @@ true 4096 - false - - false false false - false 4 full prompt bin\Release\ - false 285212672 - false TRACE - false 4096 - false - - true false false - false 4 none prompt @@ -81,6 +65,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -90,21 +76,20 @@ 4096 x86 prompt + none + 4 - - False - ..\..\..\thirdparty\LightDataModel\LightDatamodel.dll - System System.Data - - System.XML + + ..\..\..\thirdparty\LightDataModel\LightDatamodel.dll + diff --git a/Duplicati/GUI/Datamodel/app.config b/Duplicati/GUI/Datamodel/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/GUI/Datamodel/app.config +++ b/Duplicati/GUI/Datamodel/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Duplicati.GUI.MacTrayIcon.csproj b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Duplicati.GUI.MacTrayIcon.csproj new file mode 100644 index 000000000..c6dcab7f2 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Duplicati.GUI.MacTrayIcon.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {9E9675C7-1198-44CD-85E0-FD582E9EEAFF} + {948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Exe + TestMonoMac + TestMonoMac + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + none + false + bin\Release + prompt + 4 + false + + + none + false + bin\X86\Debug + 4 + + + none + false + bin\x86\Release + 4 + x86 + + + none + false + bin\Release + 4 + + + none + false + bin\Debug + 4 + + + + + + + + + + + + + + + + + {17566860-3D98-4604-AA5B-47661F75609F} + Duplicati.GUI.TrayIcon + + + + + PreserveNewest + + + + + + + + + + + + \ No newline at end of file diff --git a/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Info.plist b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Info.plist new file mode 100644 index 000000000..5335dd288 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleIdentifier + com.hexad.DuplicatiTrayMac + CFBundleName + TestMonoMac + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.6 + LSUIElement + TRUE + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Main.cs b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Main.cs new file mode 100644 index 000000000..01c9f2988 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/Main.cs @@ -0,0 +1,16 @@ +using System; +using System.Drawing; + +namespace TestMonoMac +{ + class MainClass + { + //We just call the correct implementation + static void Main (string [] args) + { + Duplicati.GUI.TrayIcon.Program.Main(args); + } + + } +} + diff --git a/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/MainMenu.xib b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/MainMenu.xib new file mode 100644 index 000000000..57749a8a9 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/MainMenu.xib @@ -0,0 +1,151 @@ + + + + 1070 + 11C74 + 1938 + 1138.23 + 567.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 1938 + + + YES + NSCustomObject + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + NSFontManager + + + AppDelegate + + + + + YES + + + delegate + + + + 534 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 420 + + + + + 533 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 420.IBPluginDependency + 533.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + YES + + + + + + YES + + + + + 538 + + + + YES + + AppDelegate + NSObject + + IBProjectSource + ./Classes/AppDelegate.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + diff --git a/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/SQLite/pinvoke/System.Data.SQLite.dll b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/SQLite/pinvoke/System.Data.SQLite.dll new file mode 100644 index 000000000..ebf200483 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/SQLite/pinvoke/System.Data.SQLite.dll differ diff --git a/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/SQLite/pinvoke/readme.txt b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/SQLite/pinvoke/readme.txt new file mode 100644 index 000000000..43a2d1561 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/SQLite/pinvoke/readme.txt @@ -0,0 +1,3 @@ +This folder is only used during development. +It is used to place the sqlite.dll inside the Mac app bundle so the hosted instance can start. +It is not copied to the resulting bundle, and it is only dynamically loaded, so it is not a problem if it is outdated, but may give weird debugging errors. diff --git a/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/readme.txt b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/readme.txt new file mode 100644 index 000000000..489c7d3e6 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.MacTrayIcon/readme.txt @@ -0,0 +1,3 @@ +The project in this folder is supposed to be loaded under MonoDevelop on a Mac with MonoMac installed. +You can use this project to debug the Cocoa tray-icon, but it is not required to build it. +This project does not work on a non-Mac platform. \ No newline at end of file diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/AppIndicatorRunner.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/AppIndicatorRunner.cs new file mode 100644 index 000000000..5c8ff6f38 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/AppIndicatorRunner.cs @@ -0,0 +1,68 @@ +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using AppIndicator; +using Gtk; +using Duplicati.Server.Serialization; +using System.Collections.Generic; + +namespace Duplicati.GUI.TrayIcon +{ + public class AppIndicatorRunner : GtkRunner + { + protected ApplicationIndicator m_appIndicator; + private string m_themeFolder; + + protected override void CreateTrayInstance() + { + m_themeFolder = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "SVGIcons"); + m_themeFolder = System.IO.Path.Combine(m_themeFolder, "dark"); + + m_appIndicator = new ApplicationIndicator("duplicati", "duplicati-logo", Category.ApplicationStatus, m_themeFolder); + } + + protected override void Run(string[] args) + { + m_appIndicator.Menu.ShowAll(); + m_appIndicator.Status = Status.Attention; + Gtk.Application.Run(); + } + + + protected override void SetMenu (IEnumerable items) + { + base.SetMenu(items); + m_appIndicator.Menu = m_popupMenu; + } + + protected override TrayIcons Icon + { + set + { + switch(value) + { + case TrayIcons.Idle: + default: + m_appIndicator.IconName = "duplicati-logo"; + break; + } + } + } + + } +} + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/MonoMac.dll b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/MonoMac.dll new file mode 100755 index 000000000..9bc3d8906 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/MonoMac.dll differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/appindicator-sharp.dll b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/appindicator-sharp.dll new file mode 100644 index 000000000..220b9ed70 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/appindicator-sharp.dll differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/atk-sharp.dll b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/atk-sharp.dll new file mode 100644 index 000000000..908f620f4 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/atk-sharp.dll differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/gdk-sharp.dll b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/gdk-sharp.dll new file mode 100644 index 000000000..10dfe6b21 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/gdk-sharp.dll differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/glib-sharp.dll b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/glib-sharp.dll new file mode 100644 index 000000000..4b3464175 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/glib-sharp.dll differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/gtk-sharp.dll b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/gtk-sharp.dll new file mode 100644 index 000000000..b4bf5e6f7 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/gtk-sharp.dll differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/readme.txt b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/readme.txt new file mode 100644 index 000000000..af4e1d953 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/BuildSupport/readme.txt @@ -0,0 +1,7 @@ +The files in this folder are ONLY used for building. +They will not work by themselves because they are merely wrappers for native libraries. +By placing them here, it is possible to build the solutions without having the respective frameworks present on the developer machine. +They are not placed in "thirdparty" because they should never be distributed. +Instead, the target system should have the required toolkits installed. + +Much of the logic can handle missing frameworks and will do some fallback if the libraries are missing. \ No newline at end of file diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/CocoaRunner.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/CocoaRunner.cs new file mode 100644 index 000000000..9539da654 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/CocoaRunner.cs @@ -0,0 +1,195 @@ +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using MonoMac.Foundation; +using MonoMac.AppKit; +using MonoMac.ObjCRuntime; +using System.Collections.Generic; + +namespace Duplicati.GUI.TrayIcon +{ + [MonoMac.Foundation.Register("AppDelegate")] + public class AppDelegate : NSApplicationDelegate + { + public override void AwakeFromNib () + { + base.AwakeFromNib(); + + CocoaRunner._instance.AwakeFromNib(this); + } + } + + public class CocoaRunner : TrayIconBase + { + public static CocoaRunner _instance; + + private AppDelegate m_appDelegate; + + public CocoaRunner() + : base() + { + _instance = this; + } + + private class MenuItemWrapper : IMenuItem + { + private NSMenuItem m_item; + private Action m_callback; + + public NSMenuItem MenuItem { get { return m_item; } } + + public MenuItemWrapper(string text, MenuIcons icon, Action callback, IList subitems) + { + if (text == "-") + m_item = NSMenuItem.SeparatorItem; + else + { + m_item = new NSMenuItem(text, ClickHandler); + m_callback = callback; + + if (subitems != null && subitems.Count > 0) + { + m_item.Submenu = new NSMenu(); + foreach(var itm in subitems) + m_item.Submenu.AddItem(((MenuItemWrapper)itm).MenuItem); + } + } + } + + private void ClickHandler(object sender, EventArgs args) + { + if (m_callback != null) + m_callback(); + } + + #region IMenuItem implementation + public string Text { + set { + m_item.Title = value; + } + } + + public MenuIcons Icon { + set { + } + } + + public bool Enabled { + set { + m_item.Enabled = value; + } + } + #endregion + } + + private static readonly string ICON_PATH = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "OSX Icons"); + + private static readonly string ICON_NORMAL = System.IO.Path.Combine(ICON_PATH, "normal.png"); + private static readonly string ICON_PAUSED = System.IO.Path.Combine(ICON_PATH, "normal-pause.png"); + private static readonly string ICON_RUNNING = System.IO.Path.Combine(ICON_PATH, "normal-running.png"); + private static readonly string ICON_ERROR = System.IO.Path.Combine(ICON_PATH, "normal-error.png"); + + private NSAutoreleasePool POOL; + private NSStatusItem m_statusItem; + private Dictionary m_images = new Dictionary(); + + public override void Init (string[] args) + { + POOL = new NSAutoreleasePool(); + NSApplication.Init(); + NSApplication.Main(args); + } + + public void AwakeFromNib(AppDelegate caller) + { + m_appDelegate = caller; + m_statusItem = NSStatusBar.SystemStatusBar.CreateStatusItem(32); + SetMenu(BuildMenu()); + RegisterStatusUpdateCallback(); + OnStatusUpdated(Program.Connection.Status); + + base.Init(null); + } + + private NSImage GetIcon(TrayIcons icon) + { + if (!m_images.ContainsKey(icon)) + { + switch(icon) + { + case TrayIcons.IdleError: + m_images[icon] = new NSImage(ICON_ERROR); + break; + case TrayIcons.Paused: + m_images[icon] = new NSImage(ICON_PAUSED); + break; + case TrayIcons.PausedError: + m_images[icon] = new NSImage(ICON_PAUSED); + break; + case TrayIcons.Running: + m_images[icon] = new NSImage(ICON_RUNNING); + break; + case TrayIcons.RunningError: + m_images[icon] = new NSImage(ICON_RUNNING); + break; + case TrayIcons.Idle: + default: + m_images[icon] = new NSImage(ICON_NORMAL); + break; + } + } + + return m_images[icon]; + } + + #region implemented abstract members of Duplicati.GUI.TrayIcon.TrayIconBase + protected override void Run (string[] args) + { + } + + protected override TrayIcons Icon + { + set + { + m_statusItem.Image = GetIcon(value); + } + } + + protected override IMenuItem CreateMenuItem (string text, MenuIcons icon, Action callback, System.Collections.Generic.IList subitems) + { + return new MenuItemWrapper(text, icon, callback, subitems); + } + + protected override void Exit () + { + NSApplication.SharedApplication.Terminate(m_appDelegate); + } + + protected override void SetMenu (System.Collections.Generic.IEnumerable items) + { + m_statusItem.Menu = new NSMenu(); + foreach(var itm in items) + m_statusItem.Menu.AddItem(((MenuItemWrapper)itm).MenuItem); + } + + public override void Dispose () + { + } + #endregion + } +} + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj new file mode 100644 index 000000000..6a4ab07f0 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj @@ -0,0 +1,279 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {17566860-3D98-4604-AA5B-47661F75609F} + WinExe + Properties + Duplicati.GUI.TrayIcon + Duplicati.GUI.TrayIcon + 512 + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + bin\Debug + full + true + true + true + 4 + false + DEBUG + true + + + bin\Release + false + none + true + true + true + 4 + true + + + Duplicati.GUI.TrayIcon.Program + + + Duplicati.snk + + + + BuildSupport\atk-sharp.dll + False + + + BuildSupport\glib-sharp.dll + False + + + + + + + + + + + + BuildSupport\appindicator-sharp.dll + False + + + BuildSupport\gdk-sharp.dll + False + + + BuildSupport\gtk-sharp.dll + False + + + ..\..\..\thirdparty\MonoMac\MonoMac.dll + + + + + + True + True + WindowsMainForm.resx + + + Form + + + WindowsMainForm.cs + + + + + + ResXFileCodeGenerator + WindowsMainForm.Designer.cs + + + WindowsMainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + PreserveNewest + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + {19E661D2-C5DA-4F35-B3EE-7586E5734B5F} + Duplicati.Server + + + {33FD1D24-C28F-4C71-933F-98F1586EA76C} + Duplicati.Server.Serialization + + + {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65} + Duplicati.Library.Utility + + + {C5899F45-B0FF-483C-9D38-24A9FCAAB237} + Duplicati.Library.Interface + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + \ No newline at end of file diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj.user b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj.user new file mode 100644 index 000000000..49d5ea28e --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj.user @@ -0,0 +1,16 @@ + + + + true + + + publish\ + + + + + + en-US + false + + \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSH/Duplicati.snk b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.snk similarity index 100% rename from Duplicati/Library/Backend/SSH/Duplicati.snk rename to Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.snk diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/GtkRunner.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/GtkRunner.cs new file mode 100644 index 000000000..05068b825 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/GtkRunner.cs @@ -0,0 +1,303 @@ +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Collections.Generic; +using Gdk; +using Gtk; + +namespace Duplicati.GUI.TrayIcon +{ + public class GtkRunner : TrayIconBase + { + /// + /// Static constructor that ensures the Gtk environment is initialized + /// + static GtkRunner() + { + Gtk.Application.Init(); + } + + private class StatusEventArgs : EventArgs + { + public readonly Duplicati.Server.Serialization.ISerializableStatus Status; + public StatusEventArgs(Duplicati.Server.Serialization.ISerializableStatus args) { this.Status = args; } + } + + private class MenuItemWrapper : IMenuItem + { + private MenuItem m_item; + private System.Action m_callback; + private static Dictionary _icons = new Dictionary(); + + public MenuItem MenuItem { get { return m_item; } } + + private Gtk.Image GetIcon(MenuIcons icon) + { + if (!_icons.ContainsKey(icon)) + { + switch(icon) + { + case MenuIcons.None: + _icons[icon] = null; + break; + case MenuIcons.Options: + _icons[icon] = ImageToGtk(Properties.Resources.SettingsMenuIcon); + break; + case MenuIcons.Pause: + _icons[icon] = ImageToGtk(Properties.Resources.Pause); + break; + case MenuIcons.Pause5: + _icons[icon] = ImageToGtk(Properties.Resources.Clock05); + break; + case MenuIcons.Pause15: + _icons[icon] = ImageToGtk(Properties.Resources.Clock15); + break; + case MenuIcons.Pause30: + _icons[icon] = ImageToGtk(Properties.Resources.Clock30); + break; + case MenuIcons.Pause60: + _icons[icon] = ImageToGtk(Properties.Resources.Clock60); + break; + case MenuIcons.Quit: + _icons[icon] = ImageToGtk(Properties.Resources.CloseMenuIcon); + break; + case MenuIcons.Resume: + _icons[icon] = ImageToGtk(Properties.Resources.Play); + break; + case MenuIcons.Status: + _icons[icon] = ImageToGtk(Properties.Resources.StatusMenuIcon); + break; + case MenuIcons.Stop: + _icons[icon] = ImageToGtk(Properties.Resources.Stop); + break; + case MenuIcons.Throttle: + _icons[icon] = ImageToGtk(Properties.Resources.Throttle); + break; + case MenuIcons.Wizard: + _icons[icon] = ImageToGtk(Properties.Resources.WizardMenuIcon); + break; + default: + _icons[icon] = null; + break; + } + } + + return _icons[icon]; + } + + public MenuItemWrapper(string text, MenuIcons icon, System.Action callback, IList subitems) + { + if (text == "-") + m_item = new SeparatorMenuItem(); + else + { + m_item = new ImageMenuItem(text); + if (icon != MenuIcons.None) { + ((ImageMenuItem)m_item).Image = GetIcon(icon); + + //TODO: Not sure we should do this, it overrides policy? + m_item.ExposeEvent += DrawImageMenuItemImage; + } + + if (subitems != null && subitems.Count > 0) + { + Menu s = new Menu(); + foreach(var sm in subitems) + s.Add(((MenuItemWrapper)sm).m_item); + + m_item.Submenu = s; + } + + if (callback != null) + { + m_item.Activated += ClickHandler; + m_callback = callback; + } + } + } + + private void ClickHandler(object sender, EventArgs e) + { + m_callback(); + } + + /// Draw the image to the image menu item. Taken from: http://mono.1490590.n4.nabble.com/ImageMenuItem-does-not-display-the-image-Linux-platform-tp3510861p3511376.html + /// The event source. + /// The event args. + private void DrawImageMenuItemImage(object o, Gtk.ExposeEventArgs args) + { + if (o as Gtk.ImageMenuItem == null) + return; + + Gtk.Image image = (o as Gtk.ImageMenuItem).Image as Gtk.Image; + if (image == null || image.Pixbuf == null) + return; + + Gdk.GC mainGC = ((Gtk.Widget)o).Style.ForegroundGCs[(int)Gtk.StateType.Normal]; + Gdk.Rectangle r = args.Event.Area; + + args.Event.Window.DrawPixbuf(mainGC, image.Pixbuf, 0, 0, r.Left + 2, + r.Top + (r.Height - image.Pixbuf.Height) / 2, -1, -1, Gdk.RgbDither.None, 0, 0); + } + + public string Text + { + get { return ((Gtk.Label)m_item.Child).Text; } + set { ((Gtk.Label)m_item.Child).Text = value; } + } + + public MenuIcons Icon + { + set { ((ImageMenuItem)m_item).Image = GetIcon(value); } + } + + public bool Enabled + { + get { return m_item.Sensitive; } + set { m_item.Sensitive = value; } + } + } + + protected StatusIcon m_trayIcon; + protected Menu m_popupMenu; + + protected static Dictionary _images = new Dictionary(); + + protected override void Exit () + { + Application.Quit(); + } + + public override void Init (string[] args) + { + CreateTrayInstance(); + base.Init (args); + } + + protected virtual void CreateTrayInstance() + { + m_trayIcon = new StatusIcon(); + } + + protected override void Run (string[] args) + { + m_trayIcon.Visible = true; + m_trayIcon.PopupMenu += HandleTrayIconPopupMenu; + Application.Run(); + } + + protected override IMenuItem CreateMenuItem (string text, MenuIcons icon, System.Action callback, IList subitems) + { + return new MenuItemWrapper(text, icon, callback, subitems); + } + + private void HandleTrayIconPopupMenu (object o, Gtk.PopupMenuArgs args) + { + m_popupMenu.ShowAll(); + m_popupMenu.Popup(null, null, null, 0u, 0u); + } + + public static Gdk.Pixbuf ImageToPixbuf(System.Drawing.Image image) + { + using (var stream = new System.IO.MemoryStream()) + { + image.Save(stream, System.Drawing.Imaging.ImageFormat.Png); + stream.Position = 0; + Gdk.Pixbuf pixbuf = new Gdk.Pixbuf(stream); + return pixbuf; + } + } + + public static Gtk.Image ImageToGtk(System.Drawing.Image image) + { + using (var stream = new System.IO.MemoryStream()) + { + image.Save(stream, System.Drawing.Imaging.ImageFormat.Png); + stream.Position = 0; + Gtk.Image img = new Gtk.Image(stream); + return img; + } + } + + protected static Pixbuf GetIcon(TrayIcons icon) + { + if (!_images.ContainsKey(icon)) + { + switch(icon) + { + case TrayIcons.Paused: + _images[icon] = ImageToPixbuf(Properties.Resources.TrayNormalPause.ToBitmap()); + break; + case TrayIcons.Running: + _images[icon] = ImageToPixbuf(Properties.Resources.TrayWorking.ToBitmap()); + break; + case TrayIcons.IdleError: + _images[icon] = ImageToPixbuf(Properties.Resources.TrayNormalError.ToBitmap()); + break; + case TrayIcons.RunningError: + _images[icon] = ImageToPixbuf(Properties.Resources.TrayWorking.ToBitmap()); + break; + case TrayIcons.PausedError: + _images[icon] = ImageToPixbuf(Properties.Resources.TrayNormalPause.ToBitmap()); + break; + case TrayIcons.Idle: + default: + _images[icon] = ImageToPixbuf(Properties.Resources.TrayNormal.ToBitmap()); + break; + } + } + + return _images[icon]; + } + + protected override TrayIcons Icon + { + set + { + m_trayIcon.Pixbuf = GetIcon(value); + } + } + + protected override void SetMenu (IEnumerable items) + { + m_popupMenu = new Menu(); + foreach(var itm in items) + m_popupMenu.Add(((MenuItemWrapper)itm).MenuItem); + } + + protected override void RegisterStatusUpdateCallback () + { + Program.Connection.StatusUpdated += delegate(Duplicati.Server.Serialization.ISerializableStatus status) { + Gtk.Application.Invoke(this, new StatusEventArgs(status), StatusUpdateEvent); + }; + } + + protected void StatusUpdateEvent(object sender, EventArgs a) + { + if (a as StatusEventArgs == null) + return; + + this.OnStatusUpdated(((StatusEventArgs)a).Status); + } + + public override void Dispose () + { + } + + } +} + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/HostedInstanceKeeper.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HostedInstanceKeeper.cs new file mode 100644 index 000000000..c78b7600f --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HostedInstanceKeeper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Duplicati.GUI.TrayIcon +{ + /// + /// We keep the hosted instance here to allow the TrayIcon executable to load without requiring the Server.exe + /// + internal class HostedInstanceKeeper : IDisposable + { + private System.Threading.Thread m_runner; + private System.Exception m_runnerException = null; + + public HostedInstanceKeeper(string[] args) + { + m_runner = new System.Threading.Thread(ThreadRunner); + m_runner.Start(args); + + if (!Duplicati.Server.Program.ServerStartedEvent.WaitOne(TimeSpan.FromSeconds(10), true)) + { + if (m_runnerException != null) + throw m_runnerException; + else + throw new Exception("Hosted server startup timed out"); + } + } + + private void ThreadRunner(object a) + { + try + { + Duplicati.Server.Program.Main((string[])a); + } catch (Exception ex) { + m_runnerException = ex; + } + + } + + public void Dispose() + { + m_runner.Abort(); + m_runner.Join(TimeSpan.FromSeconds(10)); + } + } +} diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs new file mode 100644 index 000000000..76bcdedcb --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Duplicati.Server.Serialization; + +namespace Duplicati.GUI.TrayIcon +{ + public class HttpServerConnection : IDisposable + { + private Uri m_uri; + private System.Net.NetworkCredential m_credentials; + private static readonly System.Text.Encoding ENCODING = System.Text.Encoding.GetEncoding("utf-8"); + public delegate void StatusUpdate(ISerializableStatus status); + public event StatusUpdate StatusUpdated; + + private ISerializableStatus m_status; + + private TimeSpan m_updateIntervalIdle = TimeSpan.FromSeconds(15); + private TimeSpan m_updateIntervalActive = TimeSpan.FromSeconds(5); + private TimeSpan m_currentInterval; + + private volatile bool m_shutdown = false; + private volatile System.Threading.Thread m_thread; + private System.Threading.AutoResetEvent m_waitLock; + + private Serializer m_serializer; + private readonly Dictionary m_updateRequest; + + public ISerializableStatus Status { get { return m_status; } } + + private object m_lock = new object(); + private Queue> m_workQueue = new Queue>(); + + public HttpServerConnection(Uri server, System.Net.NetworkCredential credentials) + { + m_uri = server; + m_serializer = new Serializer(); + m_updateRequest = new Dictionary(); + m_updateRequest.Add("action", "get-current-state"); + + UpdateStatus(); + + m_waitLock = new System.Threading.AutoResetEvent(false); + m_thread = new System.Threading.Thread(ThreadRunner); + m_thread.Start(); + } + + private void UpdateStatus() + { + ISerializableStatus old_status = m_status; + m_status = PerformRequest(m_updateRequest); + + m_currentInterval = m_status.ProgramState == Server.Serialization.LiveControlState.Paused ? m_updateIntervalIdle : + m_status.ActiveScheduleId < 0 ? m_updateIntervalIdle : m_updateIntervalActive; + + bool somethingChanged = false; + if (old_status != null) + { + somethingChanged |= + old_status.ActiveBackupState != m_status.ActiveBackupState + || + old_status.ActiveScheduleId != m_status.ActiveScheduleId + || + old_status.ProgramState != m_status.ProgramState + || + old_status.SchedulerQueueIds.Count != m_status.SchedulerQueueIds.Count; + + if (!somethingChanged && old_status.RunningBackupStatus != null && m_status.RunningBackupStatus != null && m_status.ActiveScheduleId >= 0) + { + somethingChanged |= + old_status.RunningBackupStatus.Message != m_status.RunningBackupStatus.Message + || + old_status.RunningBackupStatus.Mode != m_status.RunningBackupStatus.Mode + || + old_status.RunningBackupStatus.Operation != m_status.RunningBackupStatus.Operation + || + old_status.RunningBackupStatus.Progress != m_status.RunningBackupStatus.Progress + || + old_status.RunningBackupStatus.SubMessage != m_status.RunningBackupStatus.SubMessage + || + old_status.RunningBackupStatus.SubProgress != m_status.RunningBackupStatus.SubProgress; + } + + if (!somethingChanged) + { + for (int i = 0; i < m_status.SchedulerQueueIds.Count; i++) + somethingChanged |= old_status.SchedulerQueueIds[i] != m_status.SchedulerQueueIds[i]; + } + + if (somethingChanged && StatusUpdated != null) + StatusUpdated(m_status); + } + } + + private void ThreadRunner() + { + while (!m_shutdown) + { + try + { + Dictionary req; + bool any = false; + do + { + req = null; + + lock (m_lock) + if (m_workQueue.Count > 0) + req = m_workQueue.Dequeue(); + + if (m_shutdown) + return; + + if (req != null) + { + any = true; + PerformRequest(req); + } + + } while (req != null); + + if (!(any || m_shutdown)) + m_waitLock.WaitOne(m_currentInterval, true); + + if (m_shutdown) + return; + + UpdateStatus(); + } + catch (Exception ex) + { + Console.WriteLine("Request error: " + ex.Message); + } + } + } + + public void Close() + { + m_shutdown = true; + m_waitLock.Set(); + if (!m_thread.Join(TimeSpan.FromSeconds(10))) + m_thread.Abort(); + m_thread.Join(TimeSpan.FromSeconds(10)); + } + + private static string EncodeQueryString(Dictionary dict) + { + return string.Join("&", Array.ConvertAll(dict.Keys.ToArray(), key => string.Format("{0}={1}", Uri.EscapeUriString(key), Uri.EscapeUriString(dict[key])))); + } + + private T PerformRequest(Dictionary queryparams) + { + queryparams["format"] = "json"; + + string query = EncodeQueryString(queryparams); + byte[] data = ENCODING.GetBytes(query); + + System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(m_uri); + req.Method = "POST"; + req.ContentLength = data.Length; + req.ContentType = "application/x-www-form-urlencoded ; charset=" + ENCODING.BodyName; + req.Headers.Add("Accept-Charset", ENCODING.BodyName); + + using (System.IO.Stream s = req.GetRequestStream()) + s.Write(data, 0, data.Length); + + using(System.Net.HttpWebResponse r = (System.Net.HttpWebResponse)req.GetResponse()) + using (System.IO.Stream s = r.GetResponseStream()) + if (typeof(T) == typeof(string)) + { + using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) + { + s.CopyTo(ms); + return (T)(object)ENCODING.GetString(ms.ToArray()); + } + } + else + { + using (var sr = new System.IO.StreamReader(s, ENCODING, true)) + return m_serializer.Deserialize(sr); + } + + } + + private void ExecuteAndNotify(Dictionary req) + { + lock (m_lock) + { + m_workQueue.Enqueue(req); + m_waitLock.Set(); + } + } + + public void Pause(string duration = null) + { + Dictionary req = new Dictionary(); + req.Add("action", "send-command"); + req.Add("command", "pause"); + if (!string.IsNullOrWhiteSpace(duration)) + req.Add("duration", duration); + + ExecuteAndNotify(req); + } + + public void Resume() + { + Dictionary req = new Dictionary(); + req.Add("action", "send-command"); + req.Add("command", "resume"); + ExecuteAndNotify(req); + } + + public void StopBackup() + { + Dictionary req = new Dictionary(); + req.Add("action", "send-command"); + req.Add("command", "stop"); + ExecuteAndNotify(req); + } + + public void AbortBackup() + { + Dictionary req = new Dictionary(); + req.Add("action", "send-command"); + req.Add("command", "abort"); + ExecuteAndNotify(req); + } + + public void RunBackup(long id, bool forcefull = false) + { + Dictionary req = new Dictionary(); + req.Add("action", "send-command"); + req.Add("command", "run-backup"); + req.Add("id", id.ToString()); + if (forcefull) + req.Add("full", "true"); + ExecuteAndNotify(req); + } + + public void ClearWarning() + { + Dictionary req = new Dictionary(); + req.Add("action", "send-command"); + req.Add("command", "clear-warning"); + ExecuteAndNotify(req); + } + + public void ClearError() + { + Dictionary req = new Dictionary(); + req.Add("action", "send-command"); + req.Add("command", "clear-error"); + ExecuteAndNotify(req); + } + + public void Dispose() + { + Close(); + } + } +} diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Info.plist b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Info.plist new file mode 100644 index 000000000..f7905a495 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Info.plist @@ -0,0 +1,30 @@ + + + + + LSApplicationCategoryType + public.app-category.productivity + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.hexad.Duplicati + CFBundleDisplayName + Duplicati + CFBundleName + Duplicati + CFBundleExecutable + Duplicati + CFBundleIconFile + Duplicati.icns + CFBundleInfoDictionaryVersion + 6.0 + CFBundleShortVersionString + 1.3 + CFBundleVersion + 1.3.0.973 + CFBundleSignature + hand + NSAppleScriptEnabled + NO + + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-error.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-error.png new file mode 100644 index 000000000..3d78d9cf3 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-error.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-pause.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-pause.png new file mode 100644 index 000000000..6e815e957 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-pause.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-running.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-running.png new file mode 100644 index 000000000..64ad3e61e Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal-running.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal.png new file mode 100644 index 000000000..de15882a3 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX Icons/normal.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX app help/Info.plist b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX app help/Info.plist new file mode 100644 index 000000000..5335dd288 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX app help/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleIdentifier + com.hexad.DuplicatiTrayMac + CFBundleName + TestMonoMac + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.6 + LSUIElement + TRUE + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX app help/MainMenu.xib b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX app help/MainMenu.xib new file mode 100644 index 000000000..57749a8a9 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/OSX app help/MainMenu.xib @@ -0,0 +1,151 @@ + + + + 1070 + 11C74 + 1938 + 1138.23 + 567.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 1938 + + + YES + NSCustomObject + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + NSFontManager + + + AppDelegate + + + + + YES + + + delegate + + + + 534 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 420 + + + + + 533 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 420.IBPluginDependency + 533.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + YES + + + + + + YES + + + + + 538 + + + + YES + + AppDelegate + NSObject + + IBProjectSource + ./Classes/AppDelegate.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs new file mode 100644 index 000000000..ace2e6269 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using Duplicati.Library.Interface; + +namespace Duplicati.GUI.TrayIcon +{ + public static class Program + { + public static HttpServerConnection Connection; + + private const string TOOLKIT_OPTION = "toolkit"; + private const string TOOLKIT_WINDOWS_FORMS = "winforms"; + private const string TOOLKIT_GTK = "gtk"; + private const string TOOLKIT_GTK_APP_INDICATOR = "gtk-appindicator"; + private const string TOOLKIT_COCOA = "cocoa"; + + private static readonly string DEFAULT_TOOLKIT = GetDefaultToolKit(); + + private static string GetDefaultToolKit() + { + if (Duplicati.Library.Utility.Utility.IsClientOSX && SupportsCocoaStatusIcon) + { + //Determine if we are running in an app bundle, otherwise we cannot run Cocoa + //The Duplicat.GUI.TrayIcon project, does not create the app bundle, + // so this ensures that we can run the normal project when debugging on mac, + // and it will just fall-back to Gtk. If we need to debug something Cocoa specific, + // we can load the Duplicati.GUI.MacTrayIcon project and use that as start project + string basefolder = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + string plist = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(basefolder), "Info.plist"); + if (System.IO.File.Exists(plist)) + return TOOLKIT_COCOA; + } + + if (Duplicati.Library.Utility.Utility.IsClientLinux) + { + if (SupportsAppIndicator) + return TOOLKIT_GTK_APP_INDICATOR; + else + return TOOLKIT_GTK; + } + else + { + //Windows users expect a WinForms element + return TOOLKIT_WINDOWS_FORMS; + } + } + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] _args) + { + List args = new List(_args); + Dictionary options = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args); + + string toolkit; + if (!options.TryGetValue(TOOLKIT_OPTION, out toolkit)) + toolkit = DEFAULT_TOOLKIT; + else + { + if (TOOLKIT_WINDOWS_FORMS.Equals(toolkit, StringComparison.InvariantCultureIgnoreCase)) + toolkit = TOOLKIT_WINDOWS_FORMS; + else if (TOOLKIT_GTK.Equals(toolkit, StringComparison.InvariantCultureIgnoreCase)) + toolkit = TOOLKIT_GTK; + else if (TOOLKIT_GTK_APP_INDICATOR.Equals(toolkit, StringComparison.InvariantCultureIgnoreCase)) + toolkit = TOOLKIT_GTK_APP_INDICATOR; + else if (TOOLKIT_COCOA.Equals(toolkit, StringComparison.InvariantCultureIgnoreCase)) + toolkit = TOOLKIT_COCOA; + else + toolkit = DEFAULT_TOOLKIT; + } + + + //TODO: Fix options for non-hosted + using (new HostedInstanceKeeper(_args)) + { + using (Connection = new HttpServerConnection(new Uri("http://localhost:8080/control.cgi"), null)) + { + using(var tk = RunTrayIcon(toolkit)) + { + tk.Init(_args); + } + } + } + } + + private static TrayIconBase RunTrayIcon(string toolkit) + { + if (toolkit == TOOLKIT_WINDOWS_FORMS) + return GetWinformsInstance(); + else if (toolkit == TOOLKIT_GTK) + return GetGtkInstance(); + else if (toolkit == TOOLKIT_GTK_APP_INDICATOR) + return GetAppIndicatorInstance(); + else if (toolkit == TOOLKIT_COCOA) + return GetCocoaRunnerInstance(); + else + throw new Exception(string.Format("The selected toolkit '{0}' is invalid", toolkit)); + } + + //We keep these in functions to avoid attempting to load the instance, + // because the required assemblies may not exist on the machine + private static TrayIconBase GetGtkInstance() { return new GtkRunner(); } + private static TrayIconBase GetWinformsInstance() { return new WinFormsRunner(); } + private static TrayIconBase GetAppIndicatorInstance() { return new AppIndicatorRunner(); } + private static TrayIconBase GetCocoaRunnerInstance() { return new CocoaRunner(); } + + //The functions below simply load the requested type, + // and if the type is not present, calling the function will result in an exception. + //This seems to be more reliable than attempting to load the assembly, + // as there are too many complex rules for when an updated assembly is also + // acceptable. This is fairly error proof, as it is just asks the runtime + // to load the required types + private static bool TryGetGtk() + { + return typeof(Gtk.StatusIcon) != null && typeof(Gdk.Image) != null; + } + + private static bool TryGetWinforms() + { + return typeof(System.Windows.Forms.NotifyIcon) != null; + } + + private static bool TryGetAppIndicator() + { + return typeof(AppIndicator.ApplicationIndicator) != null; + } + + private static bool TryGetMonoMac() + { + return typeof(MonoMac.AppKit.NSStatusItem) != null; + } + + //The functions below here, simply wrap the call to the above functions, + // converting the exception to a simple boolean value, so the calling + // code can be kept free of error handling + private static bool SupportsGtk + { + get + { + try { return TryGetGtk(); } + catch {} + + return false; + } + } + + private static bool SupportsAppIndicator + { + get + { + try { return TryGetAppIndicator(); } + catch {} + + return false; + } + } + + private static bool SupportsCocoaStatusIcon + { + get + { + try { return TryGetMonoMac(); } + catch {} + + return false; + } + } + + private static bool SupportsWinForms + { + get + { + try { return TryGetWinforms(); } + catch {} + + return false; + } + } + + public static Duplicati.Library.Interface.ICommandLineArgument[] SupportedCommands + { + get + { + return new Duplicati.Library.Interface.ICommandLineArgument[] + { + new Duplicati.Library.Interface.CommandLineArgument(TOOLKIT_OPTION, CommandLineArgument.ArgumentType.Enumeration, "Selects the toolkit to use", "Choose the toolkit used to generate the TrayIcon, note that it will fail if the selected toolkit is not supported on this machine", DEFAULT_TOOLKIT, null, new string[] {TOOLKIT_WINDOWS_FORMS, TOOLKIT_GTK, TOOLKIT_GTK_APP_INDICATOR, TOOLKIT_COCOA}), + }; + } + } + } +} diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/AssemblyInfo.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..c5edf1394 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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.GUI.TrayIcon")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Duplicati.GUI.TrayIcon")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("33531def-3214-4568-bfb0-0bdeabe4a798")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Resources.Designer.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Resources.Designer.cs new file mode 100644 index 000000000..9b94f5679 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Resources.Designer.cs @@ -0,0 +1,196 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.239 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Duplicati.GUI.TrayIcon.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.GUI.TrayIcon.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap Clock05 { + get { + object obj = ResourceManager.GetObject("Clock05", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Clock15 { + get { + object obj = ResourceManager.GetObject("Clock15", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Clock30 { + get { + object obj = ResourceManager.GetObject("Clock30", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Clock60 { + get { + object obj = ResourceManager.GetObject("Clock60", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap CloseMenuIcon { + get { + object obj = ResourceManager.GetObject("CloseMenuIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Delay { + get { + object obj = ResourceManager.GetObject("Delay", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Pause { + get { + object obj = ResourceManager.GetObject("Pause", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Play { + get { + object obj = ResourceManager.GetObject("Play", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap SettingsMenuIcon { + get { + object obj = ResourceManager.GetObject("SettingsMenuIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap StatusMenuIcon { + get { + object obj = ResourceManager.GetObject("StatusMenuIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Stop { + get { + object obj = ResourceManager.GetObject("Stop", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Throttle { + get { + object obj = ResourceManager.GetObject("Throttle", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Icon TrayNormal { + get { + object obj = ResourceManager.GetObject("TrayNormal", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon TrayNormalError { + get { + object obj = ResourceManager.GetObject("TrayNormalError", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon TrayNormalPause { + get { + object obj = ResourceManager.GetObject("TrayNormalPause", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon TrayNormalWarning { + get { + object obj = ResourceManager.GetObject("TrayNormalWarning", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon TrayWorking { + get { + object obj = ResourceManager.GetObject("TrayWorking", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon TrayWorkingPause { + get { + object obj = ResourceManager.GetObject("TrayWorkingPause", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Bitmap WizardMenuIcon { + get { + object obj = ResourceManager.GetObject("WizardMenuIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Resources.resx b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Resources.resx new file mode 100644 index 000000000..73314d036 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Resources.resx @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Clock05.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Clock15.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Clock30.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Clock60.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CloseMenuIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Delay.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Pause.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Play.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SettingsMenuIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\StatusMenuIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Stop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Throttle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TrayNormal.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TrayNormalError.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TrayNormalPause.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TrayNormalWarning.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TrayWorking.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TrayWorkingPause.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\WizardMenuIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Settings.Designer.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Settings.Designer.cs new file mode 100644 index 000000000..828ce2482 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.225 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Duplicati.GUI.TrayIcon.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Settings.settings b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Settings.settings new file mode 100644 index 000000000..abf36c5d3 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock05.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock05.png new file mode 100644 index 000000000..6873b0b7f Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock05.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock15.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock15.png new file mode 100644 index 000000000..78fe0d0ac Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock15.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock30.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock30.png new file mode 100644 index 000000000..ca138602a Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock30.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock60.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock60.png new file mode 100644 index 000000000..7f0915d73 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Clock60.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/CloseMenuIcon.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/CloseMenuIcon.png new file mode 100644 index 000000000..f41600e44 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/CloseMenuIcon.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Delay.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Delay.png new file mode 100644 index 000000000..e8376aba0 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Delay.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Pause.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Pause.png new file mode 100644 index 000000000..3d717e46a Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Pause.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Play.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Play.png new file mode 100644 index 000000000..f513b25ff Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Play.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/SettingsMenuIcon.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/SettingsMenuIcon.png new file mode 100644 index 000000000..5f0cad037 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/SettingsMenuIcon.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/StatusMenuIcon.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/StatusMenuIcon.png new file mode 100644 index 000000000..c342475a2 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/StatusMenuIcon.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Stop.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Stop.png new file mode 100644 index 000000000..635251190 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Stop.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Throttle.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Throttle.png new file mode 100644 index 000000000..5c09d3849 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/Throttle.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormal.ico b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormal.ico new file mode 100644 index 000000000..f4b15562a Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormal.ico differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalError.ico b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalError.ico new file mode 100644 index 000000000..d1c54b982 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalError.ico differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalPause.ico b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalPause.ico new file mode 100644 index 000000000..cfde181ac Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalPause.ico differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalWarning.ico b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalWarning.ico new file mode 100644 index 000000000..90a9b030e Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayNormalWarning.ico differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayWorking.ico b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayWorking.ico new file mode 100644 index 000000000..d04740be3 Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayWorking.ico differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayWorkingPause.ico b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayWorkingPause.ico new file mode 100644 index 000000000..eaad5208b Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/TrayWorkingPause.ico differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/WizardMenuIcon.png b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/WizardMenuIcon.png new file mode 100644 index 000000000..a72337d3d Binary files /dev/null and b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Resources/WizardMenuIcon.png differ diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/SVGIcons/dark/duplicati-logo.svg b/Duplicati/GUI/Duplicati.GUI.TrayIcon/SVGIcons/dark/duplicati-logo.svg new file mode 100644 index 000000000..c0ab64c28 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/SVGIcons/dark/duplicati-logo.svg @@ -0,0 +1,79 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/SVGIcons/light/duplicati-logo.svg b/Duplicati/GUI/Duplicati.GUI.TrayIcon/SVGIcons/light/duplicati-logo.svg new file mode 100644 index 000000000..12f97f0ce --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/SVGIcons/light/duplicati-logo.svg @@ -0,0 +1,113 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHSettingsControl.Designer.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Strings/WindowsMainForm.Designer.cs similarity index 77% rename from Duplicati/Library/Backend/SSH/Strings/SSHSettingsControl.Designer.cs rename to Duplicati/GUI/Duplicati.GUI.TrayIcon/Strings/WindowsMainForm.Designer.cs index 9c885027e..eda351cb2 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SSHSettingsControl.Designer.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Strings/WindowsMainForm.Designer.cs @@ -1,14 +1,14 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.239 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace Duplicati.Library.Backend.Strings { +namespace Duplicati.GUI.TrayIcon.Strings { using System; @@ -22,14 +22,14 @@ namespace Duplicati.Library.Backend.Strings { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class SSHSettingsControl { + internal class WindowsMainForm { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal SSHSettingsControl() { + internal WindowsMainForm() { } /// @@ -39,7 +39,7 @@ namespace Duplicati.Library.Backend.Strings { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Library.Backend.Strings.SSHSettingsControl", typeof(SSHSettingsControl).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.GUI.TrayIcon.Strings.WindowsMainForm", typeof(WindowsMainForm).Assembly); resourceMan = temp; } return resourceMan; @@ -61,20 +61,20 @@ namespace Duplicati.Library.Backend.Strings { } /// - /// Looks up a localized string similar to Settings for the SSH backend. + /// Looks up a localized string similar to Pause. /// - internal static string PageDescription { + internal static string PauseMenuText { get { - return ResourceManager.GetString("PageDescription", resourceCulture); + return ResourceManager.GetString("PauseMenuText", resourceCulture); } } /// - /// Looks up a localized string similar to SSH Settings. + /// Looks up a localized string similar to Resume. /// - internal static string PageTitle { + internal static string ResumeMenuText { get { - return ResourceManager.GetString("PageTitle", resourceCulture); + return ResourceManager.GetString("ResumeMenuText", resourceCulture); } } } diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Strings/WindowsMainForm.resx b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Strings/WindowsMainForm.resx new file mode 100644 index 000000000..e88f78c78 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Strings/WindowsMainForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Pause + + + Resume + + \ No newline at end of file diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/TrayIconBase.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/TrayIconBase.cs new file mode 100644 index 000000000..663ffdabb --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/TrayIconBase.cs @@ -0,0 +1,212 @@ +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using Duplicati.Server.Serialization; +using System.Collections.Generic; + +namespace Duplicati.GUI.TrayIcon +{ + public enum MenuIcons + { + None, + Wizard, + Status, + Quit, + Options, + Pause, + Resume, + Stop, + Pause5, + Pause15, + Pause30, + Pause60, + Throttle + } + + public enum TrayIcons + { + Idle, + Paused, + Running, + IdleError, + PausedError, + RunningError + } + + public interface IMenuItem + { + string Text { set; } + MenuIcons Icon { set; } + bool Enabled { set; } + } + + public abstract class TrayIconBase : IDisposable + { + protected IMenuItem m_stopMenu; + protected IMenuItem m_pauseMenu; + protected bool m_stateIsPaused; + + public virtual void Init(string[] args) + { + SetMenu(BuildMenu()); + RegisterStatusUpdateCallback(); + OnStatusUpdated(Program.Connection.Status); + Run(args); + } + + protected abstract void Run(string[] args); + + + protected abstract TrayIcons Icon { set; } + + protected abstract IMenuItem CreateMenuItem(string text, MenuIcons icon, Action callback, IList subitems); + + protected abstract void Exit(); + + protected abstract void SetMenu(IEnumerable items); + + protected virtual void RegisterStatusUpdateCallback() + { + Program.Connection.StatusUpdated += OnStatusUpdated; + } + + protected IEnumerable BuildMenu() + { + return new IMenuItem[] { + CreateMenuItem("Status", MenuIcons.Status, OnStatusClicked, null), + CreateMenuItem("Wizard...", MenuIcons.Wizard, OnWizardClicked, null), + CreateMenuItem("-", MenuIcons.None, null, null), + CreateMenuItem("Options...", MenuIcons.Options, OnOptionsClicked, null), + CreateMenuItem("-", MenuIcons.None, null, null), + CreateMenuItem("Control", MenuIcons.None, null, new IMenuItem[] { + m_pauseMenu = CreateMenuItem("Pause", MenuIcons.Pause, OnPauseClicked, null ), + CreateMenuItem("Pause period", MenuIcons.None, null, new IMenuItem[] { + CreateMenuItem("5 minutes", MenuIcons.Pause5, OnPause5Clicked, null), + CreateMenuItem("15 minutes", MenuIcons.Pause15, OnPause15Clicked, null), + CreateMenuItem("30 minutes", MenuIcons.Pause30, OnPause30Clicked, null), + CreateMenuItem("60 minutes", MenuIcons.Pause60, OnPause60Clicked, null) + }), + m_stopMenu = CreateMenuItem("Stop", MenuIcons.Stop, OnStopClicked, null), + CreateMenuItem("Throttle options", MenuIcons.Throttle, OnThrottleClicked, null), + }), + CreateMenuItem("-", MenuIcons.None, null, null), + CreateMenuItem("Quit", MenuIcons.Quit, OnQuitClicked, null), + }; + } + + protected void OnStatusClicked() + { + } + + protected void OnWizardClicked() + { + } + + protected void OnOptionsClicked() + { + } + + protected void OnStopClicked() + { + } + + protected void OnQuitClicked() + { + Exit(); + } + + protected void OnThrottleClicked() + { + } + + protected void OnPauseClicked() + { + if (m_stateIsPaused) + Program.Connection.Resume(); + else + Program.Connection.Pause(); + } + + protected void OnPause5Clicked() + { + Program.Connection.Pause("5m"); + } + + protected void OnPause15Clicked() + { + Program.Connection.Pause("15m"); + } + + protected void OnPause30Clicked() + { + Program.Connection.Pause("30m"); + } + + protected void OnPause60Clicked() + { + Program.Connection.Pause("60m"); + } + + protected void OnStatusUpdated(ISerializableStatus status) + { + switch(status.SuggestedStatusIcon) + { + case SuggestedStatusIcon.Active: + Icon = TrayIcons.Running; + break; + case SuggestedStatusIcon.ActivePaused: + Icon = TrayIcons.Paused; + break; + case SuggestedStatusIcon.ReadyError: + Icon = TrayIcons.IdleError; + break; + case SuggestedStatusIcon.ReadyWarning: + Icon = TrayIcons.IdleError; + break; + case SuggestedStatusIcon.Paused: + Icon = TrayIcons.Paused; + break; + case SuggestedStatusIcon.Ready: + default: + Icon = TrayIcons.Idle; + break; + + } + + if (status.ProgramState == LiveControlState.Running) + { + m_pauseMenu.Icon = MenuIcons.Pause; + m_pauseMenu.Text = "Pause"; + m_stateIsPaused = false; + } + else + { + m_pauseMenu.Icon = MenuIcons.Resume; + m_pauseMenu.Text = "Resume"; + m_stateIsPaused = true; + } + + m_stopMenu.Enabled = status.ActiveScheduleId >= 0; + + } + + #region IDisposable implementation + public abstract void Dispose(); + #endregion + } +} + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/WinFormsRunner.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WinFormsRunner.cs new file mode 100644 index 000000000..38e2d16e9 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WinFormsRunner.cs @@ -0,0 +1,188 @@ +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using Duplicati.Server.Serialization; + +namespace Duplicati.GUI.TrayIcon +{ + //We use a separate class to start the runner to avoid attempts to load WinForms from Program.cs + public class WinFormsRunner : TrayIconBase + { + private class MenuItemWrapper : IMenuItem + { + private ToolStripItem m_menu; + private Action m_callback; + + public ToolStripItem MenuItem { get { return m_menu; } } + + public MenuItemWrapper(string text, MenuIcons icon, Action callback, IList subitems) + { + if (text == "-") + m_menu = new ToolStripSeparator(); + else + { + m_menu = new ToolStripMenuItem(text); + if (icon != MenuIcons.None) + m_menu.Image = GetIcon(icon); + + if (callback != null) + { + m_callback = callback; + m_menu.Click += OnMenuClick; + } + + if (subitems != null && subitems.Count > 0) + foreach(var itm in subitems) + ((ToolStripMenuItem)m_menu).DropDownItems.Add(((MenuItemWrapper)itm).MenuItem); + } + } + + private void OnMenuClick (object sender, EventArgs e) + { + m_callback(); + } + + private System.Drawing.Image GetIcon(MenuIcons icon) + { + switch(icon) + { + case MenuIcons.Options: + return Properties.Resources.SettingsMenuIcon; + case MenuIcons.Pause: + return Properties.Resources.Pause; + case MenuIcons.Pause5: + return Properties.Resources.Clock05; + case MenuIcons.Pause15: + return Properties.Resources.Clock15; + case MenuIcons.Pause30: + return Properties.Resources.Clock30; + case MenuIcons.Pause60: + return Properties.Resources.Clock60; + case MenuIcons.Quit: + return Properties.Resources.CloseMenuIcon; + case MenuIcons.Resume: + return Properties.Resources.Play; + case MenuIcons.Status: + return Properties.Resources.StatusMenuIcon; + case MenuIcons.Stop: + return Properties.Resources.Stop; + case MenuIcons.Throttle: + return Properties.Resources.Throttle; + case MenuIcons.Wizard: + return Properties.Resources.WizardMenuIcon; + case MenuIcons.None: + default: + return null; + } + } + + #region IMenuItem implementation + public string Text { + set { + m_menu.Text = value; + } + } + + public MenuIcons Icon { + set { + m_menu.Image = GetIcon(value); + } + } + + public bool Enabled { + set { + m_menu.Enabled = value; + } + } + #endregion + } + + private NotifyIcon m_trayIcon; + + public override void Init (string[] args) + { + m_trayIcon = new NotifyIcon(); + base.Init (args); + } + + protected override void RegisterStatusUpdateCallback () + { + Program.Connection.StatusUpdated += delegate(ISerializableStatus status) { + m_trayIcon.ContextMenuStrip.Invoke(new Action(OnStatusUpdated), status); + }; + } + + #region implemented abstract members of Duplicati.GUI.TrayIcon.TrayIconBase + protected override void Run (string[] args) + { + m_trayIcon.Visible = true; + Application.Run(); + } + + protected override TrayIcons Icon { + set { + switch (value) + { + case TrayIcons.IdleError: + m_trayIcon.Icon = Properties.Resources.TrayNormalError; + break; + case TrayIcons.Paused: + m_trayIcon.Icon = Properties.Resources.TrayNormalPause; + break; + case TrayIcons.PausedError: + m_trayIcon.Icon = Properties.Resources.TrayNormalPause; + break; + case TrayIcons.Running: + m_trayIcon.Icon = Properties.Resources.TrayWorking; + break; + case TrayIcons.RunningError: + m_trayIcon.Icon = Properties.Resources.TrayWorking; + break; + case TrayIcons.Idle: + default: + m_trayIcon.Icon = Properties.Resources.TrayNormal; + break; + } + } + } + + protected override IMenuItem CreateMenuItem (string text, MenuIcons icon, Action callback, IList subitems) + { + return new MenuItemWrapper(text, icon, callback, subitems); + } + + protected override void Exit () + { + Application.Exit(); + } + + protected override void SetMenu (System.Collections.Generic.IEnumerable items) + { + m_trayIcon.ContextMenuStrip = new ContextMenuStrip(); + foreach(var itm in items) + m_trayIcon.ContextMenuStrip.Items.Add(((MenuItemWrapper)itm).MenuItem); + } + + public override void Dispose () + { + } + #endregion + } +} + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.Designer.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.Designer.cs new file mode 100644 index 000000000..7767e0e20 --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.Designer.cs @@ -0,0 +1,251 @@ +namespace Duplicati.GUI.TrayIcon +{ + partial class WindowsMainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WindowsMainForm)); + this.label1 = new System.Windows.Forms.Label(); + this.TrayMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.statusToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.wizardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.controlToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.pauseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.pausePeriodToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.DelayDuration05Menu = new System.Windows.Forms.ToolStripMenuItem(); + this.DelayDuration15Menu = new System.Windows.Forms.ToolStripMenuItem(); + this.DelayDuration30Menu = new System.Windows.Forms.ToolStripMenuItem(); + this.DelayDuration60Menu = new System.Windows.Forms.ToolStripMenuItem(); + this.stopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.throttleOptionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.quitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.TrayIcon = new System.Windows.Forms.NotifyIcon(this.components); + this.TrayMenu.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.ImeMode = System.Windows.Forms.ImeMode.NoControl; + this.label1.Location = new System.Drawing.Point(8, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(123, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Duplicati dummy window"; + // + // TrayMenu + // + this.TrayMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.statusToolStripMenuItem, + this.wizardToolStripMenuItem, + this.toolStripSeparator1, + this.optionsToolStripMenuItem, + this.toolStripSeparator2, + this.controlToolStripMenuItem, + this.toolStripSeparator3, + this.quitToolStripMenuItem}); + this.TrayMenu.Name = "TrayMenu"; + this.TrayMenu.Size = new System.Drawing.Size(153, 154); + // + // statusToolStripMenuItem + // + this.statusToolStripMenuItem.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold); + this.statusToolStripMenuItem.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.StatusMenuIcon; + this.statusToolStripMenuItem.Name = "statusToolStripMenuItem"; + this.statusToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.statusToolStripMenuItem.Text = "Status"; + this.statusToolStripMenuItem.Click += new System.EventHandler(this.statusToolStripMenuItem_Click); + // + // wizardToolStripMenuItem + // + this.wizardToolStripMenuItem.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.WizardMenuIcon; + this.wizardToolStripMenuItem.Name = "wizardToolStripMenuItem"; + this.wizardToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.wizardToolStripMenuItem.Text = "Wizard..."; + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(149, 6); + // + // optionsToolStripMenuItem + // + this.optionsToolStripMenuItem.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.SettingsMenuIcon; + this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; + this.optionsToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.optionsToolStripMenuItem.Text = "Options..."; + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(149, 6); + // + // controlToolStripMenuItem + // + this.controlToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.pauseToolStripMenuItem, + this.pausePeriodToolStripMenuItem, + this.stopToolStripMenuItem, + this.throttleOptionsToolStripMenuItem}); + this.controlToolStripMenuItem.Name = "controlToolStripMenuItem"; + this.controlToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.controlToolStripMenuItem.Text = "Control"; + // + // pauseToolStripMenuItem + // + this.pauseToolStripMenuItem.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.Pause; + this.pauseToolStripMenuItem.Name = "pauseToolStripMenuItem"; + this.pauseToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.pauseToolStripMenuItem.Text = "Pause"; + this.pauseToolStripMenuItem.Click += new System.EventHandler(this.pauseToolStripMenuItem_Click); + // + // pausePeriodToolStripMenuItem + // + this.pausePeriodToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.DelayDuration05Menu, + this.DelayDuration15Menu, + this.DelayDuration30Menu, + this.DelayDuration60Menu}); + this.pausePeriodToolStripMenuItem.Name = "pausePeriodToolStripMenuItem"; + this.pausePeriodToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.pausePeriodToolStripMenuItem.Text = "Pause period"; + // + // DelayDuration05Menu + // + this.DelayDuration05Menu.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.Clock05; + this.DelayDuration05Menu.Name = "DelayDuration05Menu"; + this.DelayDuration05Menu.Size = new System.Drawing.Size(152, 22); + this.DelayDuration05Menu.Tag = "5m"; + this.DelayDuration05Menu.Text = "5 minutes"; + this.DelayDuration05Menu.Click += new System.EventHandler(this.DelayDuration05Menu_Click); + // + // DelayDuration15Menu + // + this.DelayDuration15Menu.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.Clock15; + this.DelayDuration15Menu.Name = "DelayDuration15Menu"; + this.DelayDuration15Menu.Size = new System.Drawing.Size(152, 22); + this.DelayDuration15Menu.Tag = "15m"; + this.DelayDuration15Menu.Text = "15 minutes"; + this.DelayDuration15Menu.Click += new System.EventHandler(this.DelayDuration15Menu_Click); + // + // DelayDuration30Menu + // + this.DelayDuration30Menu.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.Clock30; + this.DelayDuration30Menu.Name = "DelayDuration30Menu"; + this.DelayDuration30Menu.Size = new System.Drawing.Size(152, 22); + this.DelayDuration30Menu.Tag = "30m"; + this.DelayDuration30Menu.Text = "30 minutes"; + this.DelayDuration30Menu.Click += new System.EventHandler(this.DelayDuration30Menu_Click); + // + // DelayDuration60Menu + // + this.DelayDuration60Menu.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.Clock60; + this.DelayDuration60Menu.Name = "DelayDuration60Menu"; + this.DelayDuration60Menu.Size = new System.Drawing.Size(152, 22); + this.DelayDuration60Menu.Tag = "1h"; + this.DelayDuration60Menu.Text = "1 hour"; + this.DelayDuration60Menu.Click += new System.EventHandler(this.DelayDuration60Menu_Click); + // + // stopToolStripMenuItem + // + this.stopToolStripMenuItem.Enabled = false; + this.stopToolStripMenuItem.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.Stop; + this.stopToolStripMenuItem.Name = "stopToolStripMenuItem"; + this.stopToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.stopToolStripMenuItem.Text = "Stop"; + this.stopToolStripMenuItem.Click += new System.EventHandler(this.stopToolStripMenuItem_Click); + // + // throttleOptionsToolStripMenuItem + // + this.throttleOptionsToolStripMenuItem.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.Throttle; + this.throttleOptionsToolStripMenuItem.Name = "throttleOptionsToolStripMenuItem"; + this.throttleOptionsToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.throttleOptionsToolStripMenuItem.Text = "Throttle options..."; + // + // toolStripSeparator3 + // + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(149, 6); + // + // quitToolStripMenuItem + // + this.quitToolStripMenuItem.Image = global::Duplicati.GUI.TrayIcon.Properties.Resources.CloseMenuIcon; + this.quitToolStripMenuItem.Name = "quitToolStripMenuItem"; + this.quitToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.quitToolStripMenuItem.Text = "Quit"; + // + // TrayIcon + // + this.TrayIcon.ContextMenuStrip = this.TrayMenu; + this.TrayIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("TrayIcon.Icon"))); + // + // WindowsMainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(142, 29); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Name = "WindowsMainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Duplicati Control Window"; + this.Load += new System.EventHandler(this.MainForm_Load); + this.TrayMenu.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ContextMenuStrip TrayMenu; + private System.Windows.Forms.ToolStripMenuItem statusToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem wizardToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripMenuItem controlToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem pauseToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem pausePeriodToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem DelayDuration05Menu; + private System.Windows.Forms.ToolStripMenuItem DelayDuration15Menu; + private System.Windows.Forms.ToolStripMenuItem DelayDuration30Menu; + private System.Windows.Forms.ToolStripMenuItem DelayDuration60Menu; + private System.Windows.Forms.ToolStripMenuItem stopToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem throttleOptionsToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + private System.Windows.Forms.ToolStripMenuItem quitToolStripMenuItem; + private System.Windows.Forms.NotifyIcon TrayIcon; + } +} + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.cs new file mode 100644 index 000000000..452b829dc --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Duplicati.Server.Serialization; + +namespace Duplicati.GUI.TrayIcon +{ + public partial class WindowsMainForm : Form + { + private bool m_stateIsPaused = false; + private bool m_hasError = false; + private bool m_hasWarning = false; + + public WindowsMainForm() + { + InitializeComponent(); + } + + private void MainForm_Load(object sender, EventArgs e) + { + BeginInvoke((Action)(() => { this.Visible = false; })); + + TrayIcon.Visible = true; + + Program.Connection.StatusUpdated += new HttpServerConnection.StatusUpdate(Connection_StatusUpdated); + Connection_StatusUpdated(Program.Connection.Status); + } + + void Connection_StatusUpdated(ISerializableStatus status) + { + if (this.InvokeRequired) + this.Invoke(new HttpServerConnection.StatusUpdate(Connection_StatusUpdated), status); + else + { + switch(status.SuggestedStatusIcon) + { + case SuggestedStatusIcon.Active: + TrayIcon.Icon = Properties.Resources.TrayWorking; + break; + case SuggestedStatusIcon.ActivePaused: + TrayIcon.Icon = Properties.Resources.TrayWorkingPause; + break; + case SuggestedStatusIcon.ReadyError: + TrayIcon.Icon = Properties.Resources.TrayNormalError; + break; + case SuggestedStatusIcon.ReadyWarning: + TrayIcon.Icon = Properties.Resources.TrayNormalWarning; + break; + case SuggestedStatusIcon.Paused: + TrayIcon.Icon = Properties.Resources.TrayNormalPause; + break; + case SuggestedStatusIcon.Ready: + default: + TrayIcon.Icon = Properties.Resources.TrayNormal; + break; + + } + + if (status.ProgramState == LiveControlState.Running) + { + pauseToolStripMenuItem.Image = Properties.Resources.Pause; + pauseToolStripMenuItem.Text = Strings.WindowsMainForm.PauseMenuText; + m_stateIsPaused = false; + } + else + { + pauseToolStripMenuItem.Image = Properties.Resources.Play; + pauseToolStripMenuItem.Text = Strings.WindowsMainForm.ResumeMenuText; + m_stateIsPaused = true; + } + } + } + + private void statusToolStripMenuItem_Click(object sender, EventArgs e) + { + + } + + private void pauseToolStripMenuItem_Click(object sender, EventArgs e) + { + if (m_stateIsPaused) + Program.Connection.Resume(); + else + Program.Connection.Pause(); + } + + private void stopToolStripMenuItem_Click(object sender, EventArgs e) + { + Program.Connection.StopBackup(); + } + + private void DelayDuration05Menu_Click(object sender, EventArgs e) + { + Program.Connection.Pause("5m"); + } + + private void DelayDuration15Menu_Click(object sender, EventArgs e) + { + Program.Connection.Pause("15m"); + } + + private void DelayDuration30Menu_Click(object sender, EventArgs e) + { + Program.Connection.Pause("30m"); + } + + private void DelayDuration60Menu_Click(object sender, EventArgs e) + { + Program.Connection.Pause("1h"); + } + } +} diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.resx b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.resx new file mode 100644 index 000000000..df3b7134c --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/WindowsMainForm.resx @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 221, 17 + + + 327, 17 + + + + + AAABAAwAMDAQAAEABABoBgAAxgAAACAgEAABAAQA6AIAAC4HAAAYGBAAAQAEAOgBAAAWCgAAEBAQAAEA + BAAoAQAA/gsAADAwAAABAAgAqA4AACYNAAAgIAAAAQAIAKgIAADOGwAAGBgAAAEACADIBgAAdiQAABAQ + AAABAAgAaAUAAD4rAAAwMAAAAQAgAKglAACmMAAAICAAAAEAIACoEAAATlYAABgYAAABACAAiAkAAPZm + AAAQEAAAAQAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4d3d3d3d3 + d3d3d3d3d3d3d3cAAAAAAIeIiPj4+Pj4+Pj4+Pj4+Pj4+Ph4AAAACHh3JyNiNiNiNiNiNiNiNiNiNniH + cAAACIcnInI2I2I2I2I2I2I2I2I2MiN/cAAAh4JyemJjanJ6cnpyenJ6cnpyY2poiAAAiHIqNjOmMjYj + ajY2JycjajYnpycnhwAAiCcnImJyemOoeHh4eHh4eHh4dyMniAAAh3KnJ6cqNjY4////////////h6an + hwAAiCciejJyY6Jn/4+PiPiI+I+P9ycj9wAAiCJ6JyajYnJ4+P+P+Pj4j4iPh6cmiAAAiHo2NqNjp6Ko + /4+Pj4+PiPiP9yJ69wAAiCYno2NiYnJ3//j4iIiIiIj492OniAAAiCOmJjpjo2p4+PiIiIiIiIiPh6Yy + 9wAAiHJzeHd4d3d3//j/j/j4+Ij49yemiAAAiCpqiP/4///4//+P+Pj4j4+Ph6Yz+AAAiHI3j4j4+IiI + +PiIiPiIiIiP9yNqiAAA96emf/j4+Pj3//j4+Ij4+IiPh6aniAAAiHI6j4+PiPiI+P+P/4//j/iP92On + iAAA9yp2iPj4j4iI/4iIj4j4+Pj496cm+AAAiHp6j4+IiIiI//j4j4j//4iPh6ajiAAA+CcjiPiIiIiI + +P//+Pj4+Pj/9yen+AAAiCp6j////4+I/4iIj4j4+PiPh6ei9wAAiHpjiIiIiIiI//iIj4iIiIj/96Y2 + +AAA+Cenj/+IiPiIj4//////+Pj492p6iAAAiKeniPj/j4iI//+Pj4+Pj4+Ph6en+AAAiHp6j/+IiPj4 + +P///////4iIiHp6iAAA+Cd3j4+I+PiI/4+Pj4+Pj4iIhqh3+AAAiKeoj/j/j4+I////////+I+Id3p6 + iAAAiHeniP+I+PiI/4+Pj4+Pj4iIp6in9wAAiKeKj/j4iPj4////////+IiGiod6+AAAiDqHj/+P+P+I + +Pj4+Pj4/4eoqKiniAAAiGioj4/4/4/4iIiIiIiIeH53d3qK9wAAiKeKj/j/j/j///iPiH6neoqKioh6 + 9wAAiHqIj/+P+P+Pj4iIiIeKiKiHeKin9wAAh6h3j4/4/4//+Pj4iKiod4qKioiKiAAA93d3j///j/j4 + /4j4joeHd4eHiKin9wAAiKh4j4+P////j/iId3eoeoqKh4eKiAAAiDioj///j4+P+PiOuKiIeIiIqKh6 + 9wAA92eIiIiIiIiIiIh4qIiod6ioiId3iAAAiHqKh4eHiHiHjod4iKiIeIiHd3eohwAAD3eoioeKh4qH + h3h3eIioioeHh6eHgAAACIh3p6p6emp6qqqqqqenp6qqp3h4cAAAAIiHh4eHh4h4eIiIh4eHeHh4h4eI + AAAAAACIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA/AAAAAA/AADwAAAAAA8AAOAAAAAABwAA4AAAAAAH + AADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADgAAAAAAcAAOAA + AAAABwAA8AAAAAAPAAD8AAAAAD8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAId3d3d3d3d3d3d3eAAAAAeIiHiI + eIh4iHiIeIhwAACHciJyInIiciJyInInhwAIgicnononpypypyejYniACHcqcnI2d3d3d3d3d3oocAhy + ciKnInj///////hyeHAIenJ6cnp4+Pj4iIj/aihwCHJ6cmOmP/+Pj/j4j3NogAh6NiOmMn+IiIiIiI9q + OHAIgmeIiIh///+PiI+PcmiACHOn+PiPiPiI+IiIj2OocAiCd/j4+I//j4j/+P96eIAIemf4+IiPj4j4 + +PiPY6hwCIKo+IiIiPj4+P+I/2pogAh3p//4+I/4+PiP+PhzqHAIinf4iIiPj4j4iIj/aniACHp3/4/4 + j//////4+HeocAiHqPiIj4j4+Pj4+Ih6eIAIenj//4+P/////4j3eniACIeo+Ij4j4+Pj4+IioeocAh6 + iI/4/4//////iH6oqIAIh6j/j4+IiIiHeOeod4hwCHqI//////iIjqioiop4cAh4ePj4+Pj4+HiKh3h4 + qIAIeoj/////+Ih6iHioqIhwCHio+I+I+IiOiKh4h4eocAiKiIiIiOiId3iHeoeHqIAAh3qKinqKioqK + ioiop4gAAAiHd3eHd3d3d3d3d4hwAAAAiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAA//////AA + AA/gAAAHwAAAA4AAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAA + AAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAcAAAAPgAAAH8AAAD/// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAACI + iIiIiIiIiIh4AAh3IiIiIiIiIid4gAhycnNnNnNnNjIngAgiNiY2iIiIiPgigAh6Y6On////j4hjgAgj + anYn/4iI+IimgAhqdzd3+Pj4iPgjgAg2j4+I/4+IiIhqgAgjj4+I+Pj/+PgngAh6j4iI+I+I+PimgAin + j4+I//j/iPgjgAh6j4iI+I+I+Pim8Ainj4+I////+Ih6gAh3j4+I/4+PiIengAinj4j4////+IqK8Ah3 + j//4iIiIiGh3gAioj4+P+PiKioqKgAh4j///+IjoeHiogAioj/j4+IioqKiKgAh4iIiIiHiIiIeHgAiK + enp6eoqnp6iogACIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAP///wDAAAMAgAABAIAAAQCAAAEAgAABAIAA + AQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAA + AQDAAAMA////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAId3d3d3d3gAcioqenp6JwB6cnj4+P93AHInKI+IiGcAd4iI+Pj/KACn+Pj4+I9jAHeIiPj/j3oAev + +P+Pj4eAB4j4j//4ioAHf4+P+PiGgAeI//iId6igCo/4+Iioh4AIiIiIh4d4oAh6h3qKioeAAAAAAAAA + AAD//wAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAIAB + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAAvkS8AL5YvAC+a + LwAvnS8ANYk1ADWMNQA/hj8AN484ADiPOAAwkTAAMJUwADaRNgA2lTYAMJgwADCeMAA3kzgAOJQ4ADiZ + OAA5nTkAMKMwADigNwAwqzAAOqE6ADumOwA8ojwAPaY8ADyrPAAxsDEAPbQ9AECLQABdi10AQK9BAEK0 + QgBDu0QATb5NAFukXABZvlkAYY1hAGGQYgBrkGkAdJJyAHWUcwB2lnQAd5h0AHicdgB8n3oAYaphAH6i + fAB7sXkARMREAEjASABUwlQAW8ZbAF/KXwBjxWMAY8xjAGrMagBr0WsAbNpsAHPUcwB22HYAe9V7AHzZ + fACAk30AgaJ/AIDXfwCLi4sAi4uMAI2NjgCFkYEAg5eDAIaShQCIlIQAiZWIAI+PkACRkZIAlJaQAJSU + lQCWmZMAl5qUAJiblQCZnJYAl5eYAJiYmQCbnpgAnJ6YAJqanACdnZ0AhKSFAI+jjwCGsIQAi7OJAJC1 + jwCYoZkAnaCaAJq6mACjp58An5+gAKGhogCjpKAApaaiAKOjpACkpKYAqqamAKOooAClqaEApqikAKio + pACnp6gAp66oAKioqQCrrKgAqqqsAKysrgCyrq0AqrSoALOwrwCurrAAsLCyALWxsQC2tbIAs7O0ALa2 + tgC8tbIAtby1ALq5tQC5uLkAvbq6AL2+ugC+vLwAwreyAMK4swDBubYAxbq2AMG8uQDGvbkAwb+/AMW/ + vADIv7wAgt2CAIrdigCSwpEAmcSXAJ7AnQCF4IUAi+KLAJPkkwClwaIAqcOnAK/HrQCzzrEAvsy8AMPA + vgDIwb4AwM2+AMHBwADFwsEAxcXCAMPDxADGxcYAy8PAAMrEwQDMxcIAyMbFAM3HxADCzcAAzcjGAMnJ + yADNysgAy8zJAM3OywDNzc4A2s3HAN3MxgDSzcoA2M7JAN7OyADP0M0A1dDOAN7QygDa0s0A3tHNAM/P + 0ADR0dEA1tLQANPT1ADV1dUA29XSAN7Y1QDX19gA2dnZANvb3ADc3N0A4dHKAODTzQDk08wA4tTPAOXV + zgDr1s4A7NfOAOzYzwDh1tIA5tfQAOjX0ADm2NIA4drWAOXa1QDo2dMA7drSAO3c0wDp2tQA7tvUAOnc + 1gDu3dUA4tzZAOXd2gDh390A5d/cAOne2QDt39gA8N/XAOXg3QDr4d0A8OHaAPDi3ADx5N4A4+LhAOrk + 4ADs5eEA6ubkAOzn5ADu6OUA6urpAOvs6wDv7+0A8ubgAPLo4gDx6eYA9OrmAPTs5wDw6+gA8u3qAPXu + 6gDz7uwA9O/tAPbw7QD48u8A8vLxAPn08QD5+PcA+fn4AAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUU1NERERERERERERERERERERERERERERERERE + RERERERERFMAAAAAAAAAAAAAZ2dUiauruLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLjAuHJEVAAA + AAAAAAByZ2duHwcFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUHJ324S1QAAAAAAACeZ0cFCgoK + AQoBAQEKAQEKAQEKAQEKAQEKAQEKAQoBAQoBAQoKCgVauFMAAAAAAH9tVAUKCgoKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgoKCgoKCgoKCgoJfXpTAAAAALhnHwELAQEKAQoBAQEKAQEKCgkSCQkJEgkJCRIJCQkS + CQkJEgkKCgoBJ8BTAAAAALhUHgEKAQEKAQoBAQEKAQESSmFfX19fX19fVVVVVVVVUVVRUVEoAQoBHsBV + AAAAAKtUCRICAhIBAhIBAhIBAhISYfv7+/v7+/v7+/v7+/r6+vr7++kpChICCr5TAAAAALdUEQECAgIC + AgICAgICAgESYfvw8Ozs7Ozd3d3d3cnd3cnd3ekpAgICCbhUAAAAALhUCQ8PAQICAgICAgICAgIVavvw + 7Ozs3d3d3d3HycnHx8fH7OkpAgICCcBUAAAAALhnEQIPAgIPAgIPAgIPAg8Vavvw8PDs7Ozs3d3d3cfJ + x8fH3ekpAhUCEr5nAAAAALhnChICDwIUAhQCDw8PAg8Vavvw8N3OzsXFtraxsbGurq7H7OksAg8CEr5n + AAAAALhnEg8PDxQUFQ8PDw8PDw8Xavv38KShoaGIiIiGhoaGg4PH7OkpDw8PEr9nAAAAALhnEg8PFUBH + R0dHR0dGRkZHaPv28PDn593n3dvb3c7dxczd7OksDw8PE79nAAAAALhyEhQUF2fp6enp6enp6enncvv2 + 9vDw6e3w8N3n293O3d3M7OksFBQPE8BuAAAAALhyEhQVF2Hw59vb293O3c7Fc/v28KSkpaHavaSkiIiI + hobd8OksFBQUE8ByAAAAAMByFw8WF2rp3dvdxc7HxcWxc/v29trazs7wvbbn3efd2ojb8OksFBUPF8By + AAAAALhyFxYWGmr43d7a293FzsfFc/v29vD38/P3vbbw8PDw7Yjd8OksFhYWF8ByAAAAAMByFxYVGmrr + 593Oxb22xa6uc/v28LOvr6fnvb329vDw8Ijp8OktFBQWF8B2AAAAAL92FxYWGmrr6dqIf4Z8fHx8c/v2 + 9raztq/tvb3w9vfw8KHt8OktFhYWF8B6AAAAAL92GhwWHXDr593Avb22tranc/v39vb28Pbwvb3w9vf3 + 8J7w9+ktFhYWGsF2AAAAAMB2GhwWHWfr5+fd593j3d7devv38L29vb3pva/n3ePn3Z7w9ukuHBYcGsF6 + AAAAAMB2GhwcHXDp6d2eiYl/3q+Ic/v28K+ppKTd2qukqaSkpKTw+ekuHBwcGsF6AAAAAMB2HSIdIXL2 + 596vr6+n3amkevv29vDw8/D28PDw8PDw8PDw8OkuIiIiHcB6AAAAAL92HSMjJXr26fDn6fDp6a+zfvv3 + 9vf39/P58Pn58/fw59ra4+MuIyMjIMB2AAAAAL9yITQ0NXz48Oe2s7Oz3bi9ifv59/f39/n5+ff5+fnw + r56IqKtBNDQ0IcB2AAAAAL5yHTQlN3746eevr6+p6ba4ofv59/f39/f39/f38/nnc6ief2gxNDQ0IcB2 + AAAAALh2ITU1OH/48Onw6fDp8L29nvv59/f59/f59/f5+fPngOnZiVs1NTU1IcByAAAAAMByHTg1OYH4 + 8Ona2trO6b24qfv5+fn5+fn5+fn3+fnnf9mJXDg2NTg1IsByAAAAALhyHTg4OZ748Omvr6un3cCvnvv7 + +fv5+/n7+fn5+fnwiaBdOTg4ODg4IsBuAAAAALhnIjk6PIn48PDp6fDp6fDnpPj4+Pj4+Pj4+Pj4+Pjp + e2A5OTo6Ojo6IsBnAAAAALhnIjo6PKT49unw8PDw8Onwr6urpKSknqSedpSUlJSUjzw6Ojo6Ojo6IsBn + AAAAALhnIjw8PqT66fbw8Pjw+PD48PDw6b2vqbezXTw8PD08PDw8PDw8PDw8M71jAAAAALhhIj09Qqv4 + 9vD48Pjw8Pjw+PD424CeiYl6jj09PT09PT09PT09PT09M75jAAAAALdUIj1CQqv49vbw+PD4+PD48Pjw + 457p2aiQPUI9jD2MPYw9jD2MPYw9I7hjAAAAALdUM4yMkqv4+Pjw+Pj48Pjw+PD446jZt5RCjIyMQoxC + QoyMjEKMjIyMI75UAAAAALhUMoyMkrP4+Pj4+Pj3+Pj4+Pj456i3lY2MkYySjJKMkpKMkoySjIyMMrhU + AAAAAMBUIZKSk7L4+Pn4+Pj4+Pj3+Pn46YmWjZKSkpKSkpKSkpKSkpKSkpKRI7hjAAAAAMBULzuSk5um + pqampqSmpJukm6Sbm5eTjZKSk5KSkpKSkpKSkpKSkpI7L6RUAAAAAH9/SjKSk5OTk5OTk5OTk5OTkpOS + k5OTk5OTk5OTk5OTkpOSk5KTk5IylGNjAAAAAADjUykyPJOTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OT + k5OTk5OTOzJZbW0AAAAAAAB6uFNKJCIyMzMzMzIyMzIyMzI0MjIjMjMzMjMyMzIzMjMyNDIzL15fbm0A + AAAAAAAAesB6U0tMTExNTFFMTUxRTExMTExMTExMTExMTExMTUxNSkxTU2OfcgAAAAAAAAAAAAB6uMCr + pKCgpJ+goKCkn6CboKCfoKCgoKCgoKCgoKCgoKC3t30AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA/AAAAAA/AADwAAAAAA8AAOAAAAAABwAA4AAAAAAH + AADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADgAAAAAAcAAOAA + AAAABwAA8AAAAAAPAAD8AAAAAD8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE + AAAAAAAAAAAAAAABAAAAAQAAAAAAADCPMAAwkTAAMJYwADCaMAAwnTAAMaIxADGlMQAxqjEAMa0xADKw + MgA1sjUARItEAEWMRQBGlkUATJJLAFWaVABWnlUAXZNcAF6VXABemF0AYJteAGCeXgBGu0YAS79LAEy9 + TABVu1YAYaJfAHmXdwBzm3EAc5p0AHSadAB9kH4Ae5R8AH2XfgB6mHgAfJp6AH6cewB+nXwAYqVgAG+q + bQB0r3IAfaJ+AFHCUQBVx1UAVshWAFrHWgBfw18AWstaAFzMXABnx2YAYcthAGHPYQBoz2gAb8tuAGXS + ZQBo02gAa9ZrAG/TbwBu124AbNpsAHbXdgBy2nIAeNJ4AH3bfQB94H0AhoaGAI2NjQCBlIIAhJKEAIeU + hwCAmoEAgp2DAISYhQCQkJEAk5OUAJWVlgCXl5gAmZmaAJubnACenp8AgaWCAIWghgCDp4QAhqaEAIao + hQCIqoYAiayHAIejiACJpooAiamJAIuqjACMq40Ai6yMAIysjQCZopoAmqiaAJe3lQCetp4AnrybAKOn + nwCgsp4An7egAKGhogClpaYApa2iAKavpACmpqgAqamqAKqqrACtra4AprKmAKiwpQCjvKQApL6kAKm4 + pwCsuakAqr6rALixrgCvr7AAr7uwALGxsQC1t7MAs7O0ALW1tQC7trQAsr+yALy4twC5ubkAvrq5ALu7 + vAC+vb0Awru3AMa6tQDDvLkAxL+9AMi9uQCOzo0AhN6EAJPDkgCSx5EAlMiSAJXRlACd1J0Ant6eAKDf + nwCK4YoAkeSRAJrgmgCiwKAApsKlAKPIowCmyqYAp8OoAKnGqQCpyqoArsqtALLNsQC6w7oAt9G1ALrU + uQDGwL4AysG9AM3BvAC/v8AAwcHBAMXCwgDDw8QAxcXGAMjCwADNxMEAz8fEAMjIxgDHx8gAycnJAM3K + yQDNzMsAzMzMANDFwQDRyMUA1MvHANHNywDUzcoA0s7NANXPzADW0M4A2NHOANXU1ADY09AA2tTRANzW + 0wDa1dQA3djVANrZ2QDf29gA3d3dAODSzQDl1M0A7NfOAOzYzwDi1tEA5tfQAOfZ0wDg2dYA5NrVAOrZ + 0gDs2dEA6dvVAO7b1ADu3dYA4drYAOLd2gDl3tsA6t7YAO/f2ADl4N0A6uDbAO7h2wDp4t0A7uPdAO/k + 3wDw4doA8ePcAPHk3gDj4uEA5OPiAOTk4wDl5eQA6OLgAOrk4ADt5eEA6OfmAOzn5ADu6OQA6urpAO3q + 6QDt7OoA7u7sAPDm4QDx6eUA9OnkAPXs5wDw6+gA8u3qAPXu6QDy7uwA8vDuAPbw7QD48u8A8/HwAPXz + 8QD09PIA+PPwAPj08gAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHlK + QkJCQkJCQkJCQkJCQkJCQkJCQkJDbQAAAAAAAABQbW9iYmJiYmJiYmJiYmJiYmJiYmJiZnilSgAAAAAA + bWANAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ2eUAAAAK5tDQECAgICAgICAgICAgICAgICAgICAgICAg2l + eQAApEYBAQICAgICAgIOJSUlJSUlJSUlJRwlHA8CAn5NAACkHAIEAgQCBAIEAhDg/f39+Pj29vb29vbs + EgIEcUoAAKQcAgQEBAQCBAQEEODw7u7W1tbGxsbGxvYSBAKWUAAApEcEBAQEBAQEBAQQ4Pju7u7W1tbO + xsbG6RIEBHFQAAClRwQEBAQEBgQEBhHg+MSysrKIiIiIiMbsFAQGllAAAKVYBgYdb29qb2pqZOD48PDm + 3Nzczs7OzuwUBgSZZwAArVgGBlTs5tzc1Nyr4PjCvLjmuLS0qqrW7BUGBplnAAClWwYGVezWzMzJybLg + +Obm1Pa08ObctNzsFQYGm2sAAK5bCAZV7NzMyMm6iOD31MK+6br29PC07vYWCAiZbQAArlsICFfs1KmH + hIR24P3cwszpxPb097rw7BsICJttAACuWwgIV+zm5tzc1rTg9+bUwva45vDmuPD2GwgIm20AAK5bCwtV + 7NyppKm6fef4wsLE5r6+usS6+OwnCwubbQAArlsXGGHs6dTm5r6l5vj49vb3+Pb29tTm4CgYGJttAACu + WxgrY/jmvK68wq7s+Pf39/f39/fCh32eKSsYm20AAK1bKy6W7Ozg5uDCuOz99/399/39977CwmYvLiub + bQAApVssM5b46cLCwsK47P399/399/33wrxzMjMzLJtnAAClVy43mf3swtTg8Lzg/f39/f39/f3CczY1 + NTUum2cAAKVVNDqb/fb29vb25sK8vK6upYuNjYs/Ojo6OjeYZwAApVE3PZ347Pbw9vb29vaqpap5PT09 + PT0/PT0/N5hQAACkKjlAn/329vbw9vbw9q7srYlAQEBAQEBAQEA7mEoAAKQqO4qg/fb2+Pj29vj2uK6O + ioqKioqKioqKij2YTQAApUY5kqDg4Obg4ODg4OCuj5KSkpKSkpKSkpKSPJZQAACuahqSlJGRkZGRkZGR + kZSTk5OTk5OTk5OTk5IreXkAAACtRho9QUFBQUFBQUFBQUFBQUFBQUFBQUE7K2BnAAAAAAClZxwdHh4e + HR4dHh0eHR0dHR0dHR0dHUZndwAAAAAAAACupYB5gHmAeYB5gHmAeYB5gHmAeYB7gK0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////8AAAD+AAAAfAAAADgAAAAYAAAAGAAAABgAAAAYAA + AAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAA + AAGAAAABgAAAAYAAAAGAAAABwAAAA+AAAAfwAAAP/////ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA + AAAAAAAAAAEAAAABAAAAAAAAL5EvAC+WLwAvmi8AMowyADWLNQAwkTAAMZQxADSSNAAwmjAAMZ4xADSZ + NAA1nzUAMKEwADClMAA1ojUANqY2ADCpMAAwrTAANqk2ADetNgA7rzsAObM5ADyxPABWklYAW5ZaAFuY + WQBcmFoAX5pfAEi4SABNvE0AZ5tlAG6ibABmtmcAabppAG2/bQBRwFEAVsBWAFbFVgBYxlgAXsZeAF3J + XQBizGIAZcxlAGvRawBs0WwAbdVtAHDRbwB11nUAedZ5AH/bfwB+3H4AhKGBAIK0gACRvY8Al6iVAJuh + nACcpJ0AoaGiAKenqACpqaoArq6uAKu7qQCvvq4AsL+uAK+vsACwsLAAsbaxALW1tgC+t7QAvrm3ALe3 + uAC4uLkAvLy9AIPJggCC24IAid+JAJfBlQCH4IcAjeONAJDjkACjyaIArM6rALDArgCl2KQAqdmoALHC + sACyxLEAysC7AM3DvwC/v8AAwcHCAMPDxADFxMUAycLAAMrFwgDPx8MAycbGAM3HxQDIycYAx8fIAMnJ + yADOy8oAy8zIAM3MygDPz88A1cfBANHKxgDVycQA18zHANjLxgDVzswA2c7JAMnXxwDN1ssAy9rKANrQ + ywDa0c0Az8/QANbS0ADV1NIA1tbVANrV0QDc1dEA2tbUANnZ1wDe2NUA3NrZANvc2gDe3NsA3t7dAOXS + ygDl08wA5dXOAOfY0gDi2tYA6NnTAO3Z0QDp3NYA7t3WAOHb2ADi3NoA5N3ZAOXf3ADq39oA4OHfAOfg + 3QDr4NsA6OHeAPDg2QDw4twA4uLhAOnj4ADq5OEA7OXiAO/p5QDv6ugA8ebhAPHq5gDy7eoA9u7qAPPv + 7AD17+wA9vHuAPjy7gD08vEA+PTyAPn29AD6+PYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJOkE9PT09PT09PT09PT09 + PUI7QgAAAGQ4GQUFBQUFBQUFBQUFBQUFBQUcREkAAEIZAQEBAQEBAQcHBwcHBwcHBwcBHD0AAEcFBwcB + AQEBH2dnZ2dnYWddYTcBB0kAAEILCQEDAwMDIKSdlpWLi4mJiz8LB0kAAEkHCwMDCwMDIKadk4uGhYWF + iz8DB0kAAEkLDRwaHBoZNKaTenVwbmpqi1MNC1oAAEkND2eYmJiYYaaZfo6GdXBullMNDV0AAF0PEGeT + hoaFWKaemZ56np11nVYND10AAF0NFWeTZm5YRaaOb4Z6pKB6nlYSDWcAAF0VEmeZmH6GXaaemZt6np56 + oFYSFWQAAF0VF2eZYWF1RaaZjpiOjn5+pFYXF10AAF0dJXmejo6OZ6ampKCmoKB6eT4kHV0AAF0eKHmb + jn+OZ6ampKampqBdYTUnHl0AAEkkK36cenqOZ6impqampqBkNisrJF0AAEkkL3+cnJygf5F/f39yc3JN + LCwsJF0AAEkpMZGkoJymoKB6ZWFKLzAvMS8wKkkAAEIrS5GcppygnKR5f1FLMzMzMzMzK0gAAEkrTJil + pqWmpaV5UkxOTk5OTk5OK0gAAEghTFVVVVVVVVVUUFBQUFBQUFBMIzwAAHY4IiwuLi4uLiwsLC4sLi4u + LiwiOFsAAAByR0E9PDw8PDxBPTw9PTw9PT1DZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AMAA + AwCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAA + AQCAAAEAgAABAIAAAQCAAAEAgAABAMAAAwD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA + AAAAAQAAAAEAAAAAAAAwkjAAMJYwADeSNgAwmzAAR5VGAEeZRgBInUcASZVIAFWUUwBWllUAWJtZAEii + RwBJp0gAVqBVAFSvUwBcp1sAW6FcAF2mXQBcq1sAXqtfAGiWaQBynnIAZ6NnAGmqagBqrmsAZLFlAGe0 + ZgBgv2AAbLJsAGy2bABuuW4AeaZ6AHupewByu3MAdb11AHqzegB5tHoAeL14AGTNZAB0wXUAe8F6AHnG + eQB8xnwActVyAH7RfQCcvpsAp6moAKusqwCrr6wAt7q3AIbBhwCByoEAhsmFAIXOhgCF0IQAgN2AAIXZ + hACK0IoAncGcAJHQkQCS35IAnNecAJvemwChw58AjeKNAL3AuwC52rkAx8e/AMnGxgDIycIAyszFAM7O + ygDZycMA28zGANbOygDE08MA1NHPAN3QygDY0s8A39TPANbS0ADZ09AA2tTRANrW1QDe2NYA3dvaAN/d + 2wDj1c4A4NbRAOPZ1QDl2dQA4NvYAOLc2QDk3dkA4d7cAOjf2wDC4sEA5eDeAOjh3gDt4dwA8ePdAPHk + 3wDt5eEA6+bkAO3o5QDy5+IA8ermAPTq5QDx7OkA9e7qAPPw7gD08O0A+PLvAPXy8AD29PEA+PPwAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAADAWFxcXFxcXFxcXFyAvAAAVAQEBAQgKCgoKCgoB + IQAACgECAQIubmxqZmZqCBgAAAsCBAIEO2pZTkpKZAYYAAARDkdHREhsXGBOTmQHHQAAERNnW1hZZ1xi + bFtnDR0AABMTZ1lITW5cXG5cbw0fAAAaG21NWU1uZGdqXG0PKAAAHSttVFZWb3Nzb01CHCsAAB81bVxi + VnNzc3NIKyc1AAAiPHNubmpNR0A3LSwsNQAAIj5zbW9vX0w5OTg4ODoAACU+YWFhYUM9QUFBQUEzAAAy + JSYmJiYmJiYrIiYlMAAAAAAAAAAAAAAAAAAAAAAA//8AAIABAACAAQAAgAEAAIABAACAAQAAgAEAAIAB + AACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9fX4GeHh5Onp6 + emmAgIF8gYGCf4GBgn+BgYJ/gYGCf4GBgn+BgYJ/gYGCf4GBgn+BgYJ/gYGCf4GBgn+BgYJ/gYGCf4GB + gn+BgYJ/gYGCf4GBgn+BgYJ/gYGCf4GBgn+BgYJ/gYGCf4GBgn+BgYJ/gYGCf4GBgn+BgYJ/gYGCf39/ + gHx4eHlpdnZ3Onp6ewYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhISFBnl5 + enSOjo7klpaX/pKSk/6Ojo7/i4uM/ouLi/+Li4v+i4uM/ouLjP6Li4v/i4uL/ouLi/6Li4v/i4uL/ouL + i/6Li4v/i4uL/ouLi/6Li4z/i4uL/ouLi/6Li4z/i4uM/ouLi/6Li4v/i4uL/ouLjP+Li4z+i4uM/ouL + i/+Li4z+i4uL/oyMjP+Ojo/+jY2O/oWFhuR1dXZ0enp7BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACHh4gVjIyNxqSkpf+ZmZr/wMDB/87Ozv/Pz8//0NDQ/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR + 0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR + 0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0v/Y2Nn/1tbW/62trf+Pj4//fn5/xnh4eRYAAAAAAAAAAAAA + AAAAAAAAAAAAAJOTkwaUlJXGpqan/qSkpf6nrqj/X4tf/j+GP/42iDb/NYk1/jWJNf81iTX+NYk1/jWJ + Nf41iTX/NYk1/jWJNf41iTX/NYk1/jWJNf41iTX/NYk1/jWJNf41iTX/NYk1/jWJNf41iTX/NYk1/jWJ + Nf41iTX/NYk1/jWJNf81iTX+NYk1/jWJNf81iTX+NYk1/jaINv8/hj/+YY1h/rW8tf/U1NT+jIyM/oCA + gMZ9fX4GAAAAAAAAAAAAAAAAAAAAAImJinTFxcX/n5+g/oOXg/41izX/L5Av/i+QL/4wkDD/L5Av/jCQ + MP8vkC/+L5Av/i+QL/4wkDD/L5Av/i+QL/4wkDD/L5Av/i+QL/4wkDD/L5Av/i+QL/4wkDD/L5Av/i+Q + L/4wkDD/L5Av/i+QL/4wkDD/L5Av/jCQMP8vkC/+L5Av/jCQMP8vkC/+L5Av/jCQMP8vkC/+L5Av/jWM + Nf+Po4/+1NTV/pKSk/96ent0AAAAAAAAAAAAAAAAmpqbB7CwsOSnp6j/maCa/zWNNf8wkTD/MJEw/zCR + MP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCR + MP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCR + MP8wkTD/MJEw/zCRMP81jTX/tby1/7Ozs/+Li4zkg4OEBwAAAAAAAAAAl5eYO9PT1P6fn5//XItc/i+S + L/4wkjD/L5Iv/i+SL/4wkjD/L5Iv/jCSMP8vki/+L5Iv/i+SL/4wkjD/L5Iv/i+SL/4wkjD/N5Q3/jmU + Of45lTn/OZQ5/jmUOf45lDn/OZQ5/jmUOf45lDn/OZQ5/jmUOf45lDn/OZQ5/jmUOf85lDn+OZQ5/jmU + Of85lDn+OZQ5/jmUOf80kzT+L5Iv/jCSMP8vki/+YZBi/tra2v+VlZb+gICBOwAAAAAAAAAAnJydatPT + 0/6cnJ3/QItA/i+TL/4wkzD/L5Mv/i+TL/4wkzD/L5Mv/jCTMP8vky/+L5Mv/i+TL/4wkzD/L5Mv/i+T + L/42ljb/i5eI/p6hm/6eoZv/naCa/p2fmf6cn5n/nJ6Y/puemP6bnZf/mp2X/pmclv6ZnJb/mZuV/pib + lf+Ym5X+l5qU/pealP+XmZP+lpmT/pSWkP9rkGn+L5Mv/jCTMP8vky/+QItB/tvb2/+ZmZn+g4OEagAA + AAAAAAAAq6urfc3Nzv+ZmZr/N484/zCVMP8wlTD/MJUw/zCVMP8wlTD/MJUw/zCVMP8wlTD/MJUw/zCV + MP8wlTD/MJUw/zCVMP85mDj/o6ef//n6+f/6+vj/+fn4//n5+P/5+fj/+fn3//n59//5+fj/+fn3//n4 + 9//5+Pf/+fj3//n49//5+Pf/+fn3//n49//5+Pf/+fn3/+nq6f90knL/MJUw/zCVMP8wlTD/OI84/9XV + 1v+YmJn/jIyNfQAAAAAAAAAAr6+wf87Ozv6bm5z/N5E3/i+WL/4wljD/L5Yv/i+WL/4wljD/L5Yv/jCW + MP8vli/+L5Yv/i+WL/4wljD/L5Yv/i+WL/45mjj/o6ig/vn5+P706+b/8+ji/vLm4P7y5d//8ePd/vDi + 2/7w4Nr/79/Y/u/e1/7u3dX/7tzU/u3c0//t29P+7tvT/u3b0//t29P+8eXf/urq6f90knL+L5Yv/jCW + MP8vli/+N5E3/tXV1v+ampv+j4+QfwAAAAAAAAAAsbGyf8/Pz/+dnZ7/N5M4/zCYMP8wmDD/MJgw/zCY + MP8wmDD/MJgw/zCYMP8wmDD/MJgw/zCYMP8wmDD/MJgw/zCYMP85mzj/pKmh//n5+P/06+b/8+fi//Lm + 4P/y5N7/8eLb//Dg2f/w39f/7t3V/+7b1P/t2tL/7dnR/+zYz//s187/7NfO/+zXzv/s187/8OLc/+rq + 6f91k3L/MJgw/zCYMP8wmDD/N5M4/9bW1/+dnZ7/kZGSfwAAAAAAAAAAs7Ozf9DQ0f6fn6D/OJQ4/i+Z + L/4wmTD/L5kv/i+ZL/4wmTD/L5kv/jCZMP8vmS/+L5kv/i+ZL/4wmTD/L5kv/i+ZL/45nDj/pamh/vn5 + +P717en/9Orl/vPp4/7z5+H/8uXf/vHj3f7x4tv/8ODZ/u/e1/7u3NX/7tvT/u3Z0f/s2ND+7NfP/uzX + zv/r1s3+8OLc/urq6f91lHP+L5kv/jCZMP8vmS/+OJQ4/tfX1/+fn6D+lJSVfwAAAAAAAAAAtLS1f9HR + 0v6hoaL/OJY4/i+bL/4wmzD/L5sv/i+bL/4wmzD/L5sv/jCbMP8vmy/+L5sv/i+bL/4wmzD/L5sv/i+b + L/45njj/paqi/vn6+f727+v/9ezn/ufe2v7l29f/5NrV/uLX0/7i1tH/4dTP/uDTzf7f0cz/39DK/t7O + yP/dzcf+3czG/t/Nxv/s187+8OPd/urq6v91lHP+L5sv/jCbMP8vmy/+OJY4/tjY2P+hoaP+lpaXfwAA + AAAAAAAAtra2f9LS0/6jo6T/OJg4/i+dL/4wnTD/L50v/jGdMf4xnjH/MZ4x/jGeMf8xnjH+MZ4x/jGe + Mf4xnjH/MZ4x/jGeMf46oDr/pqqi/vn6+P738O3/8erm/s3Gw/7MxMH/y8PA/sjAvf7Hv7v/xr26/sa8 + uf7Fu7f/xbq2/sO5tf/CuLP+wrey/sO3sv/r18/+8ePd/urq6v92lXP+L50v/jCdMP8vnS/+OJg4/tnZ + 2f+kpKX+mJiZfwAAAAAAAAAAt7e4f9PT1P+lpaf/OZo5/zCfMP8wnzD/OKA3/4CTff+IlIX/iJSE/4eT + hP+Gk4P/hpOD/4WSgv+EkYH/hJGB/4SQgP+GkYL/qqem//n6+f/38e3/9u/r//Dp5f/u5+P/7ubh/+3k + 3//s497/6+Hc/+vf2v/q3tj/6dzW/+ja1P/o2dP/59fR/+jX0P/t29P/8eXf/+rr6v92lnT/MJ8w/zCf + MP8wnzD/OZo5/9nZ2v+mpqf/mpqbfwAAAAAAAAAAuLi5f9TU1f6np6j/Opw6/jCgMP4xoDH/PKI8/qOk + oP7r7Ov/6+vq/uvr6v/r6+r+6+vq/uvr6v7r6+r/6uvq/urr6v7n5+b/sa6u/vn6+P738e3/9u/r/vLr + 6P7x6ub/8enl/vDo5P716+f/8+nk/u7k3/7u4t3/7ODb/uzf2f/r3df+6tvV/uvb1P/u3dX+8ubg/urr + 6v93l3T+MKAw/jGgMf8woDD+Opw6/tra2/+oqKn+nJydfwAAAAAAAAAAubm6f9XV1v6pqar/Op06/jCi + MP4xojH/PaM8/qSmov7v7+3/6uLd/urg2//p3tr+6N3Y/ujc1v7n29X/5trU/ubZ0v7j1c//sq2s/vn6 + +P738e3/8uvn/s/Jx/7Nx8X/zcfE/sjCv/7o4d3/39jU/szFwv7Lw8D/x7+8/se+uv/Gvbn+xby4/sa8 + uP/u3tf+8uji/urr6v93mHT+MKIw/jGiMf8wojD+Op06/tvb2/+qqqv+np6ffwAAAAAAAAAAurq7f9bW + 1/+rq6z/Op86/zGkMf8xpDH/PaU8/6Wmov/v7+3/6uDb/+nd1//o29b/59nU/+fY0v/m19D/5dXO/+TT + zP/h0Mn/sq6t//n6+f/38e3/9e/r/+Xe2//j3Nn/49zZ/+Ha1//z7Oj/3dfT/9jSzv/u5uL/7eTg/+3j + 3v/s4tz/6t7Z/8W8uP/u4Nr/8+nl/+rr6v93mXX/MaQx/zGkMf8xpDH/Op86/9vb3P+srK3/n5+hfwAA + AAAAAAAAu7u8f9fX2P6srK7/OqE7/jCmMP4xpjH/Pac8/qano/7v7+3/6+Ld/urf2v/p3tj+6dzX/uja + 1f7n2dP/5tfR/uXWz/7i08v/sq6t/vn6+f738e3/9u/r/vbv6/738Oz/9u/r/vbv6/738Oz/3dfU/tvV + 0f727ur/9e3p/vXs5//06uX+8ufi/sa+uv/v493+9Ovn/urr6v94mnX+MKYw/jGmMf8wpjD+OqE7/tzc + 3P+tra/+oaGifwAAAAAAAAAAvLy9f9jY2P6tra//O6M7/jCoMP4xqDH/Pak8/qaopP7v7+7/7OTg/uvi + 3f/i2dT+4NfS/t/V0P7e087/3tHN/t3Qy/7azcf/s6+u/vr6+f738e3/9O3p/tXPzf7Tzcr/0szJ/s/I + xv7t5uL/3tfV/tvV0v738Oz/9+/r/vbu6v/27ej+8+nl/si/vP/x5uH+9e3p/uvr6v94m3b+MKgw/jGo + Mf8wqDD+O6M7/tzc3f+vr7D+oqKjfwAAAAAAAAAAvb2+f9jY2f+urrD/O6U7/zGqMf8xqjH/Pas8/6eo + pP/v7+7/7ebi/+Xd2f/Cvbr/wry5/8C6t/++uLT/vbaz/721sf+8tLD/sa2s//n6+f/38e3/9O3p/9rT + 0f/X0c7/1tDO/9PNyv/v6OT/3dfU/9vV0v/38Oz/9/Ds//fv6//27+v/9Ozn/8jBvv/y6OP/9u/r/+vr + 6v95nHb/Maox/zGqMf8xqjH/O6U7/93d3f+wsLH/o6OlfwAAAAAAAAAAvb2+f9jY2f6vr7H/O6c7/jCs + MP4xrDH/Pa08/qiopP7v7+7/7ebi/uzk4P/f2dX+3tfT/t3W0v7c1ND/29PO/tvRzP7Yzsn/s7Cv/vn6 + +f738e3/9u/r/vbv6/738Oz/9u/r/vbv6/738Oz/3dfU/tvV0v738Oz/9u/r/vfw7P/38Oz+9O3p/sjC + v//z6ub+9/Ht/uvr6v95nXb+MKww/jGsMf8wrDD+O6c7/t3d3f+wsLL+pKSlfwAAAAAAAAAAvr6+f9nZ + 2f6wsLH/O6g7/jCuMP4xrjH/Pa49/qippf7v7+7/7ebi/u3m4v/s5eH+6+Tg/uvj3/7q4t7/6+Ld/urg + 2/7m29b/tbGw/vn6+f738e3/9e7q/uDa1/7e2NX/3tfV/tzV0v7x6ub/3dfU/tfRz/7r5eH/6uTg/urk + 4P/r5OD+6eLf/sjCv//z7Oj+9/Lv/uvr6v96nnf+MK4w/jGuMf8wrjD+O6g7/t3d3f+xsbP+pKSmfwAA + AAAAAAAAvr6+f9nZ2f+wsLH/O6o8/zGwMf8xsDH/PbA9/6mppf/v7+7/7ebi/+fg3P/Gwb//xcC+/8S/ + vf/AvLn/49vX/9TNyv/Dvbr/s6+v//n6+f/38e3/8+zo/9HLyf/Pycf/zsjG/8rEwf/q5OD/4tzZ/8/J + x//OyMb/y8XC/8rEwv/LxML/ysTB/83Gw//17ur/+PPw/+vr6v96n3j/MbAx/zGwMf8xsDH/O6o8/93d + 3f+xsbP/paWmfwAAAAAAAAAAvr6+f9nZ2f6vr7H/Pq0+/kG3Qf49tj3/RrVG/qusqP7v7+7/7ebi/urj + 3//Uzsv+083K/tLNyv7Qy8j/6OHd/s/Kxv7MxsP/tbGx/vn6+f738e3/9u/r/vXu6v717ur/9O7q/vTt + 6v738Oz/9u/r/vTu6v707er/9O3p/vTt6f/07en+8uvn/vHr5//z7Oj+9PDt/urq6f9+onz+P7c//kK4 + Qv9GuUb+P60//t3d3f+xsbL+pKSmfwAAAAAAAAAAvb2+f9jY2f6vr7D/P68//k6+Tv5Ovk7/Wb5Z/ra2 + sv7x8fD/7+nm/u/p5f/v6OX+7+jl/u7o5P7u6OT/7ujk/tPOy/7Uz8z/u7e3/vr6+f738u7/9/Dt/vfw + 7f738O3/9/Dt/vfw7f738e3/9/Dt/vfx7f738e3/9/Ht/vfx7f/07uv+7Obj/ubg3f/l39z+5+Ph/uLi + 4f9+n3z+Tr5O/k6+Tv9Ovk7+QK9B/t3d3f+wsLL+o6OlfwAAAAAAAAAAvb2+f9jY2f+urq//QLFA/1LB + Uv9SwVL/XcFd/7i4tf/y8vH/8Orn/+3n5P/Y09H/19LQ/9bRz//U0M3/6+Xi/9fT0f/Z1NL/wr+///v7 + +v/48/D/+PLv//jy7//48u//+PLv//jy7//48u//+PLv//jy7//48u//+PLv//jy7//x7On/0s3L/8bB + v//FwL7/yMXE/8rJyP+Bon//UsFS/1LBUv9SwVL/QbFB/9zc3f+vr7D/o6OkfwAAAAAAAAAAvLy9f9fX + 2P6tra7/QLJA/lfEV/5XxFf/YcRh/rq7t/7y8vH/8Ovn/uvm4//Szsz+0c3L/tHMy/7Oy8j/6eTh/tjU + 0f7Z1dP/xMHA/vv7+v749PD/+PLv/vjy7/748+//+PLv/vjy7/748+//+PLv/vjy7/748+//+PLv/vjy + 7//s5uP+s6+v/sjGx//FxMT+v729/qqmpv97sXn+V8RX/lfEV/9XxFf+QrNC/tzc3f+urq/+oaGjfwAA + AAAAAAAAu7u8f9bW1/6rq63/QLRB/lvHW/5bx1v/Zcdl/ry9uf7y8vH/8evo/vDr6P/w6uf+8Orn/vDq + 5/7w6uf/8Orn/tnV0/7a1tT/xsPC/vv7+v759PH/+PLv/vjy7/758/D/+PLv/vjy7/758/D/+PLv/vjy + 7/758/D/+PLv/vjy7//s5+T+vbq6/urq6v/g39/+vry8/oawhP9cx1z+W8db/lvHW/9bx1v+QrRC/tvb + 3P+srK7+oKChfwAAAAAAAAAAurq7f9bW1v+qqqv/QbVB/1/KX/9fyl//acpp/76/u//y8/H/8ezp/+/q + 5//j39z/497b/+Ld2//h3dr/7+nm/9rW1P/a1tT/x8XE//v7+v/59PH/+fPw//nz8P/58/D/+fPw//nz + 8P/58/D/+fPw//nz8P/58/D/+fPw//jz7//t6OX/vru7/+Hh4f/BwL//i7OJ/2HKYf9fyl//X8pf/1/K + X/9fyl//Q7ZD/9vb2/+qqqz/np6gfwAAAAAAAAAAubm6f9XV1f6oqKn/QbdB/mTNZP5kzWT/bc1t/sDB + vv7z8/L/8ezp/uzn5P/Szs3+0s7N/tHOzP7Oy8j/6eTh/t3Z1/7Rzsz/x8XE/vv7+v769/X/+vf0/vr3 + 9P769/T/+vf0/vr39P769/T/+vf0/vr39P769/T/+vf0/vr39P/w7ev+vbu7/sXDw/+RtY/+Zsxm/mTN + ZP9kzWT+ZM1k/mTNZP9kzWT+Q7hE/tra2/+oqKr+nZ2efwAAAAAAAAAAt7e4f9TU1P+mpqf/QbhC/2nQ + af9p0Gn/ctBy/8LDwP/z8/L/8u3q//Hs6f/u6eb/7ejm/+3o5f/t6OX/8ezp//Dr6P/r5uT/ycbG//Hx + 8P/y8vH/8fHx//Hx8f/x8fH/8fHx//Hx8P/x8vH/8fHw//Hx8P/x8fD/8fHw//Hx8P/q6un/t7Sz/5q6 + mP9qz2r/adBp/2nQaf9p0Gn/adBp/2nQaf9p0Gn/RLlF/9ra2v+np6j/m5ucfwAAAAAAAAAAtra3f9LS + 0/6kpKX/QrpC/m3Tbf5t023/dtJ2/sTGwv7z9PP/8u3q/vLt6v/x7er+8u3q/vLt6v7y7er/8e3q/vHt + 6v7w7On/0s7N/s3Kyf7Oysn/zMnH/snGxf7HxMP/x8PC/sfExP7FwsL/rLWp/qbCo/6mw6T/psOk/qXB + o/+jwKH+mcSX/nHScf9t023+bdNt/m3Tbf9t023+bdNt/m3Tbf9t023+RbtF/tnZ2f+kpKb+mZmafwAA + AAAAAAAAtbW1f9HR0v6ioqP/Q7tD/nPVc/5z1XP/e9V7/sfIxf709PT/8+7r/vPu6//z7uv+8+7r/vPu + 6/7z7uv/8+7r/vPu6/7z7uv/8+7r/vPu6/7z7ev/7ejm/tnV0/7RzMr/0MvK/tTR0P7R0ND/kLWP/nPV + c/5z1XP/c9Vz/nPVc/9z1XP+c9Vz/nPVc/9z1XP+c9Vz/nPVc/9z1XP+c9Vz/nPVc/9z1XP+RrxG/tjY + 2P+ioqP+l5eYfwAAAAAAAAAAs7O0f9DQ0f+goKH/Q7xD/3fYd/932Hf/gNd//8nKxv/09fT/8+7s//Pu + 7P/z7uv/8+7s//Pu7P/z7uz/8+7r//Pu6//z7uz/8+7r//Pu6//z7uv/5eHf/7u5uf/GxMT/xMLC/8G/ + v/+1srL/ksKR/3fYd/932Hf/d9h3/3fYd/932Hf/d9h3/3fYd/932Hf/d9h3/3fYd/932Hf/d9h3/3fY + d/932Hf/R71H/9fX2P+goKH/lJSWfwAAAAAAAAAAsrKyf8/P0P6dnZ7/Q75D/nzafP582nz/hNqE/svM + yf709fT/8+/s/vPv7P/z7uz+8+/s/vPv7P7z7+z/8+7s/vPu7P7z7+z/8+7s/vPu7P7z7uz/5eHf/sfG + xv7o6Oj/4eHh/sjHxv6ewJ3/fdl9/nzafP582nz/fNp8/nzafP982nz+fNp8/nzafP982nz+fNp8/nza + fP982nz+fNp8/nzafP982nz+SL9I/tbW1/+dnZ7+kpKTfwAAAAAAAAAAsLCxf87Oz/6bm5z/RL9E/oDd + gP6B3YH/iNyI/s3Oy/719fT/9PDt/vTv7f/z7+z+9O/t/vTv7f707+3/8+/s/vPv7P707+3/8+/s/vPv + 7P7z7+3/5+Lg/snHx/7i4uL/y8rK/qTCov6C3IL/gN2A/oDdgP6B3YH/gN2A/oHdgf+A3YD+gN2A/oHd + gf+A3YD+gN2A/oHdgf+A3YD+gN2A/oHdgf+A3YD+ScFJ/tXV1f+bm5z+kJCRfwAAAAAAAAAArKytfdHR + 0f+ZmZr/RL9E/4TfhP+F34X/jN6M/8/Qzf/19vX/9fLv//Xx7//18e//9fHv//Xx7//18e//9fHv//Xx + 7//18e//9fHv//Xx7//08e//6ebl/8jGxv/Ozc3/qcOn/4fdh/+F34X/hd+F/4Xfhf+F34X/hd+F/4Xf + hf+F34X/hd+F/4Xfhf+F34X/hd+F/4Xfhf+F34X/hd+F/4Xfhf+F34X/SMBI/9PT1P+ampz/jY2OfQAA + AAAAAAAAnp6fatvb2/6YmJn/RbVG/oXghf6J4Yn/keCQ/tDRzv7z9PP/8/Pz/vP08//z8/P+8/Tz/vPz + 8/7z8/P/8/Pz/vPz8/7z8/P/8/Pz/vPz8/7z8/P/6urq/sLAwP6vx63/i9+L/onhif6J4Yn/ieGJ/onh + if6J4Yn/ieGJ/onhif+J4Yn+ieGJ/onhif+J4Yn+ieGJ/onhif+J4Yn+ieGJ/onhif+F4IX+R7ZH/tLS + 0v+cnJ3+hYWGagAAAAAAAAAAm5ubO9vb2/6cnJz/XKVc/nDbcP6O447/kuKS/r7PvP7Cz8D/ws/A/sLP + wP/Cz8D+ws/A/sLOv/7Bzr//wc6//sHNv/7Bzb//wM2+/sDNvv6/zL3/vcq7/rPOsf6R4pH/juOO/o7j + jv6O447/juOO/o7jjv6O447/juOO/o7jjv+O447+juOO/o7jjv+O447+juOO/o7jjv+O447+juOO/o7j + jv9s2mz+Yaph/sbGxv+enp/+hISFOwAAAAAAAAAAoKCgB7GxsuS3t7f/iJSJ/0XGRf+K44r/kuSS/5Lk + kv+T5JP/k+ST/5Pkk/+T5JP/k+ST/5Pkk/+T5JP/k+ST/5Pkk/+T5JP/k+ST/5Pkk/+T5JP/k+ST/5Lk + kv+S5JL/kuSS/5Lkkv+S5JL/kuSS/5Lkkv+S5JL/kuSS/5Lkkv+S5JL/kuSS/5Lkkv+S5JL/kuSS/5Lk + kv+S5JL/kuSS/4Xhhf9AxUD/qLSo/6Cgof+UlJXkioqKBwAAAAAAAAAAAAAAAI+PkHTg4OD/kpKS/naV + dv5Fx0b/dd51/pHkkf6V5pX/luaW/pbmlv+W5pb+luaW/pbmlv6W5pb/luaW/pbmlv6W5pb/luaW/pbm + lv6W5pb/luaW/pbmlv6W5pb/luaW/pbmlv6W5pb/luaW/pbmlv6W5pb/luaW/pbmlv+W5pb+luaW/pbm + lv+W5pb+luaW/pXmlf+P5I/+cN1w/kDGQP+EpIX+pqan/qenqP9/f4B0AAAAAAAAAAAAAAAAAAAAAKCg + oAaenp/G1tbX/pCQkf6GkYf/WqNb/kS2Rf5Fw0X/RcRF/kXERf9FxEX+RcRF/kXERf5FxEX/RcRF/kXE + Rf5FxEX/RcRF/kXERf5FxEX/RcRF/kXERf5FxEX/RcRF/kXERf5FxEX/RcRF/kXERf5FxEX/RcRF/kXE + Rf9FxEX+RcRF/kXERf9FxEX+RcRF/kXDRf9EtkT+XKRd/pijmf+enp/+p6eo/o+Pj8aKiosGAAAAAAAA + AAAAAAAAAAAAAAAAAACampoVnZ2dxd/f3/+zs7T/lJSV/4+PkP+Ojo//jo6P/46Oj/+Ojo//jo6P/46O + j/+Ojo//jo6P/46Oj/+Ojo//jo6P/46Oj/+Ojo//jo6P/46Oj/+Ojo//jo6P/46Oj/+Ojo//jo6P/46O + j/+Ojo//jo6P/46Oj/+Ojo//jo6P/46Oj/+Ojo//jo6P/4+PkP+UlJX/mJiZ/6Ojo//Dw8T/k5OUxYmJ + ihUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn5+fBoyMjHSurq7k19fY/tjY2P7MzMz/x8fH/sbG + x//Gxsf+xsbH/sbGx/7Gxsf/xsbH/sbGx/7Gxsf/xsbH/sbGx/7Gxsf/xsbH/sbGx/7Gxsf/xsbH/sbG + x/7Gxsf/xsbH/sbGx/7Gxsf/xsbH/sbGx//Gxsf+xsbH/sbGx//Gxsf+xsbH/sjIyP/Pz9D+0NDQ/qys + rOOHh4d0lZWVBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgoKAGl5eXOpaW + l2ihoaJ8pKSkf6SkpH+kpKR/pKSkf6SkpH+kpKR/pKSkf6SkpH+kpKR/pKSkf6SkpH+kpKR/pKSkf6Sk + pH+kpKR/pKSkf6SkpH+kpKR/pKSkf6SkpH+kpKR/pKSkf6SkpH+kpKR/pKSkf6SkpH+kpKR/pKSkf6Gh + oXyVlZZolpaWOp6engYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAPwA + AAAAPwAA8AAAAAAPAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA + AAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA + AAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAADAAAAAAAMAAMAAAAAAAwAA4AAAAAAHAADwAAAAAA8AAPwA + AAAAPwAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB9fX02ioqKqoaGh+mHh4f9hoaH/4aGh/+Ghof/hoaH/4aGh/+Ghof/hoaH/4aG + h/+Ghof/hoaH/4aGh/+Ghof/hoaH/4aGh/+Ghof/hoaH/4aGh/+Ghof/hoaG/YODg+mCgoOqd3d4NgAA + AAAAAAAAAAAAAAAAAAAAAAAAjIyNap2dnvytra7/prKm/561nv+ftp//n7ef/5+3n/+ft5//n7ef/5+3 + n/+ft5//n7ef/5+3n/+ft5//n7ef/5+3n/+ft5//n7ef/5+3n/+ft5//n7ef/5+3n/+ft6D/r7uw/8HB + wf+VlZX8e3t8agAAAAAAAAAAAAAAAI+Pjzavr6/8maKa/0SLRP8wjzD/MI8w/zCPMP8wjzD/MI8w/zCP + MP8wjzD/MI8w/zCPMP8wjzD/MI8w/zCPMP8wjzD/MI8w/zCPMP8wjzD/MI8w/zCPMP8wjzD/MI8w/zCP + MP8wjzD/RYxG/7rDuv+Xl5j8fHx9NgAAAAAAAAAAr6+vq6enqP9EjET/MJEw/zCRMP8wkTD/MJEw/zCR + MP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCRMP8wkTD/MJEw/zCR + MP8wkTD/MJEw/zCRMP8wkTD/Ro5G/8XFxf+JiYqrAAAAAAAAAAC5ubnph5SH/zCTMP8wkzD/MJMw/zCT + MP8wkzD/MJMw/zCTMP8wkzD/MJMw/0aWRf99m3v/f559/3+dfP9+nHz/fpx7/32be/98mnr/fJp6/3ya + ef97mXn/e5l5/3qYeP95l3f/TJJL/zCTMP8wkzD/sr+y/42NjukAAAAAAAAAALu7vP57lHz/MJUw/zCV + MP8wlTD/MJUw/zCVMP8wlTD/MJUw/zCVMP8wlTD/VZlU/+Pi4f/49fP/9/Tx//fz8P/38u//9/Lv//bx + 7v/28O3/9fDt//bw7f/28O3/9vDt/+7u7P9dk1z/MJUw/zCVMP+jvKT/k5OU/gAAAAAAAAAAvb2+/32X + fv8wlzD/MJcw/zCXMP8wlzD/MJcw/zCXMP8wlzD/MJcw/zCXMP9Wm1T/4+Ph//Tp5P/y5d//8ePc//Dg + 2f/v3tb/7tvT/+3Z0f/s2M//7NfO/+zXzv/s187/7evp/16UXP8wlzD/MJcw/6S+pP+Xl5j/AAAAAAAA + AAC/v8D/gJqB/zCaMP8wmjD/MJow/zCaMP8wmjD/MJow/zCaMP8wmjD/MJow/1adVf/k4+L/9e3o//Tp + 5P/y5+H/8eTe//Di2//v39j/7t3V/+3a0v/s2ND/7NfO/+zXzv/t6+n/XpZc/zCaMP8wmjD/pb+l/5qa + m/8AAAAAAAAAAMHBwv+CnYP/MJww/zCcMP8wnTD/MZ0x/zGdMf8xnTH/MZ0x/zGdMf8xnTH/V59W/+Tj + 4//38Oz/29TQ/8/HxP/NxMH/y8K+/8rAvP/Jvrr/yLy4/8a7tv/HurX/7NjQ/+7r6v9emF3/MJww/zCc + MP+mwab/np6f/wAAAAAAAAAAw8PE/4Wghv8wnzD/MJ8w/3Obcf+osKX/p6+l/6avpP+mrqP/pa6j/6Wt + ov+jp5//5OTj//fw7f/07On/8enl//Dn4//v5eD/7uPd/+3g2//r3tj/6tvV/+rZ0v/u29T/7uzq/1+Z + Xf8wnzD/MJ8w/6fDqP+hoaL/AAAAAAAAAADFxcb/h6OI/zGiMf8xojH/hqWE/+7s6v/s5eL/6+Tg/+rj + 3//q4t3/6eDc/9HKxv/k5OP/9/Dt/+Hb2P/X0c//1M7L/+7m4v/Xz8z/1MvH/9HIxP/RxsL/0MXA/+7e + 1//u7Ov/YJte/zGiMf8xojH/qMSp/6Skpf8AAAAAAAAAAMfHx/+Jpor/MaQx/zGkMf+Gp4T/7ero/+ne + 2f/o29b/59nT/+bX0P/l1M3/zsG8/+Xk4//38O3/7ujk/+nj3//o4t7/8uvn/9XPy//x6OT/8Obh/+/k + 3//SyMT/7+Lb/+7t6/9gnV7/MaQx/zGkMf+pxqn/pqao/wAAAAAAAAAAyMjJ/4qoi/8xpzH/Macx/4eo + hf/u6+n/6+Ld/+Ta1f/j19P/4dXQ/+DSzf/Mwbz/5eXk//fw7f/n4N3/39jV/93W0//v6OX/19DO//fv + 6//27un/9ezn/9XMyP/x5uD/7+7s/2CfX/8xpzH/Macx/6rHqv+oqKr/AAAAAAAAAADJycr/i6qM/zGq + Mf8xqjH/iKqG/+7s6v/o4d3/x8K//8bAvf/DvLn/wru3/7ixrv/l5eT/9/Dt/+ni3//i29j/4NnW//Dp + 5f/X0M7/9/Ds//fv6//27+v/1s7L//Pq5f/v7u3/YaFf/zGqMf8xqjH/qsmr/6qqq/8AAAAAAAAAAMrK + yv+Mq43/Ma0x/zGtMf+Iq4b/7uzq/+3m4v/s5eH/6+Pf/+ri3f/q4Nv/0snG/+Xl5P/38O3/7OXi/+bg + 3f/l3tv/8erm/9XPzP/v6OT/7ujk/+/o5P/Uzcr/9O3o/+/v7f9ho1//Ma0x/zGtMf+ryqv/q6ut/wAA + AAAAAAAAysrK/4ysjf8ysTL/MbAx/4msh//u7Or/6eLe/8XAvv/Ev73/yMPA/9vU0P+7trT/5uXk//fw + 7f/j3dr/29XS/9jSz//w6eX/3NbT/9nT0P/Y0c7/2NHO/9jSz//27+v/8O/t/2KlYP8zsTP/NbI1/6vL + q/+rq63/AAAAAAAAAADJycr/jKyN/0a7Rv9MvUz/l7eV//Du7P/u6OT/6+Xh/+vk4f/r5eH/29XS/8jC + wP/n5uX/9/Ht//fw7P/38Oz/9/Ds//fw7f/38Oz/9/Dt//fw7f/z7en/6uTh/+nj4P/n5uX/b6pt/029 + Tf9Hu0f/q8ur/6qqrP8AAAAAAAAAAMjIyf+LrIz/S79L/1PCU/+evJv/8e/u/+3n5P/Uz83/087N/9bS + z//g2tj/z8vJ/+rq6f/48/D/+PLv//jy7//48u//+PLv//jy7//48u//+PLv/+Pe2/++urn/vLi3/7+8 + vP90r3L/U8JT/0y/TP+qy6v/qamq/wAAAAAAAAAAx8fI/4mriv9Qw1D/Wsda/6LAoP/x8O7/8Orn/+ji + 4P/n4t//6eTh/+Hc2f/Rzcv/6+vq//nz8P/48/D/+PPw//jz8P/58/D/+PPw//jz8P/48u//2NPR/93d + 3f/a2tr/oLKe/1/DX/9ax1r/UcNR/6nLqv+np6j/AAAAAAAAAADFxcb/iKqI/1XHVf9hy2H/psOk//Lw + 7//v6eb/3dnW/9zY1v/f29j/4t3b/8/Lyv/s7Ov/+fTx//nz8P/58/D/+fPw//nz8P/58/D/+fPw//nz + 8P/a1tT/1dTU/6m4p/9nx2b/Ycth/2HLYf9WyFb/qcup/6Skpv8AAAAAAAAAAMTExf+FqYb/Wsta/2jP + aP+qx6j/8/Hw//Dr6P/l4N7/5ODd/+bi3//t6OX/2NTS/+jn5v/19PP/9PTz//T08//09PP/9PTz//T0 + 8//09PL/9PTy/9vZ2f+suan/b8tu/2jPaP9oz2j/aM9o/1zMXP+oy6j/oqKj/wAAAAAAAAAAwsLC/4On + hP9gz2D/b9Nv/67Krf/z8vD/8u3q//Lt6v/y7ev/8u3q//Lt6v/s5+X/2tbU/9rV1P/Tz87/zcnI/87L + yv/DwcH/k8OS/5XJk/+UyJL/kseR/3jReP9v02//b9Nv/2/Tb/9v02//Ys9i/6fKp/+enp//AAAAAAAA + AADAwMD/gaWC/2XSZf9213b/ss2x//Tz8f/z7uv/8+7r//Pu6//z7uv/8+7r//Pu6//z7uv/8u3q/8zJ + yP/Gw8P/xMLC/7W3s/9403j/dtd2/3bXdv9213b/dtd2/3bXdv9213b/dtd2/3bXdv9o02j/psqm/5ub + nP8AAAAAAAAAAL6+vv9+o3//a9Zr/33bff+30bX/9PPy//Pv7P/z7+z/8+/s//Pv7P/z7+z/8+/s//Pv + 7P/y7uv/zszL/+Xl5f/Iycb/js6N/33bff99233/fdt9/33bff99233/fdt9/33bff99233/fdt9/27X + bv+lyqX/l5eY/wAAAAAAAAAAvb29/nyhff9v2W//hN6E/7rUuf/19PP/9PHu//Tx7v/08e7/9PHu//Tx + 7v/08e7/9PHu//Pw7v/Qzs3/zc3L/5XRlP+E3oT/hN6E/4TehP+E3oT/hN6E/4TehP+E3oT/hN6E/4Te + hP+E3oT/ctpy/6PIo/+UlJX+AAAAAAAAAAC+vr/phJiF/2nZaf+K4Yr/u9S6/+bl5f/m5eX/5uXl/+bl + 5f/l5eX/5eXk/+Xl5P/l5OT/5eTk/8jIx/+d1J3/iuGK/4rhiv+K4Yr/iuGK/4rhiv+K4Yr/iuGK/4rh + iv+K4Yr/iuGK/4rhiv9o2Wj/qr6r/5GRkekAAAAAAAAAALOzs6unp6f/VrtX/43jjf+a4Zr/oN+f/6Df + n/+g35//n9+f/5/fn/+f35//n9+e/5/fnv+e3p7/m+Ca/5Hkkf+R5JH/keSR/5Hkkf+R5JH/keSR/5Hk + kf+R5JH/keSR/5Hkkf+R5JH/i+OL/1W9Vf+ysrL/kpKSqwAAAAAAAAAAlpaXNsbGx/yEkoT/V7tX/2/d + b/994H3/feB9/33gff994H3/feB9/33gff994H3/feB9/33gff994H3/feB9/33gff994H3/feB9/33g + ff994H3/feB9/33gff994H3/feB9/27dbv9UvFX/mqia/5+foPyFhYU2AAAAAAAAAAAAAAAAnJydacXF + xvyjo6P/fZB+/3SadP9zmnT/c5p0/3OadP9zmnT/c5p0/3OadP9zmnT/c5p0/3OadP9zmnT/c5p0/3Oa + dP9zmnT/c5p0/3OadP9zmnT/c5p0/3OadP90mnT/gZSC/6Ojo/+tra78jY2OaQAAAAAAAAAAAAAAAAAA + AAAAAAAAlZWVNq+vr6q5ubnotbW1/bOztP+zs7T/s7O0/7OztP+zs7T/s7O0/7OztP+zs7T/s7O0/7Oz + tP+zs7T/s7O0/7OztP+zs7T/s7O0/7OztP+zs7T/s7O0/7OztP2zs7PorKysqpCQkTYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP/////gAAAHwAAAA4AAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAA + AAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAA + AAGAAAABwAAAA+AAAAf/////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGNjYxBsbGw5cHBxP3FxcT9xcXE/cXFxP3FxcT9xcXE/cXFxP3FxcT9xcXE/cXFxP3Fx + cT9xcXE/cXFxP3BwcT9ra2w5Y2NjEAAAAAAAAAAAAAAAAAAAAABubm8Fi4uMj5+foPivr7D/rq6u/q6u + rv+urq7/rq6u/q6urv+urq7/rq6u/66urv6urq7/rq6u/q6urv+urq7/rq6u/q6urv+xsbH/paWm+H9/ + gI9ra2wFAAAAAAAAAACdnZ6Qm6Cc/luVW/41izX+Mowy/jKMMv4yjDL+Mowy/jKMMv4yjDL+Mowy/jKM + Mv4yjDL+Mowy/jKMMv4yjDL+Mowy/jKMMv41izX+X5lf/rG2sf6CgoOQAAAAAHh4eBCysrP4VpJW/jCR + MP8wkTD/L5Ev/jCRMP8wkTD/L5Ev/jGSMf80kzT/NJM0/zSTNP40kzT/NJI0/jSTNP80kjT/NJI0/jSS + NP8zkjP/L5Ev/l+bX/+rq6z4dnZ3EImJiTm1tbb/NpA2/jCUMP8wlDD/L5Qv/jCUMP8wlDD/L5Qv/meb + Zf/Mzcr/y8zJ/8vMyP7Ky8f/ycrH/snKxv/Iysb/yMnF/sjJxf+XqJX/L5Qv/jaRNv+4uLn/g4OEOZWV + lj+1tbb+M5Qz/i+XL/4vly/+L5cv/i+XL/4vly/+L5cv/m6hbP738u/+8+fh/vHj3f7w4Nn+793W/u3b + 0/7t2dH+7dnQ/u/e1/6vvq7+L5cv/jOUM/65ubn+jY2OP5iYmT+4uLn/NJg0/jCaMP8wmjD/L5ov/jCa + MP8wmjD/L5ov/m+jbf/49PH/8efi/+vg2/7q3Nf/6NnT/ubWz//l08z/5dLK/u7d1f+wv67/L5ov/jSY + NP+8vL3/kZGTP5ubnD+8vLz/NJs0/jKeMv9bmVn/XJlb/lyYWv9bmFn/W5dZ/oShgf/49fP/6eLe/9zV + 0f7a0c3/2c7K/tfMx//VycT/1cfB/u/e1/+wwK7/MJ4w/jSbNP+/v8D/lpaXP56enz++vr/+NZ81/jai + Nv7Iycb+6ubj/urk4v7p4+D+6OLe/svGxP749fP+6uTg/t/Y1f7l3dr+49vW/tvRzf7Zzsn+2MvG/vDi + 3P6xwa/+MKEw/jWfNf7CwsP+mpqbP6CgoT/BwcL/NaM1/jemNv/Ky8j/6t/a/ujc1v/n2NL/5dXO/srA + u//49fP/8uvn/+3m4v7w6eb/29XS/vLq5f/x5+L/2tDL/vHm4f+xwrD/MKUw/jWjNv/ExMX/nZ2eP6Ki + oz/Dw8T/NqY2/jepNv/LzMn/6uLe/tHKxv/Px8P/zcO//r63tP/49fP/5t/c/9XOzP7f2db/3NbT/vfv + 6//27ur/3dXR/vPr5v+yw7D/MKkw/jamNv/Gxsf/n5+hP6Ojoz/ExMX/Nqo2/jetNv/LzMn/7Obi/uXe + 2v/k3Nj/49nV/snCwP/49fP/8erm/+rk4P7v6OT/29XS/vHq5v/x6ub/29XR/vXu6/+yxLD/MK0w/jaq + Nv/Hx8j/oaGiP6OjpD/ExMX+O687/jyzPP7Mzcr+6uTg/s3Hxf7KxcL+29XR/r65t/749fP+7OXi/uLb + 2P7o4d7+5+Dd/uDa1/7f2dX+3tjV/vXv7P6zxbL+ObM5/jywPP7Hx8j+oaGiP6Kioz/Dw8T/SLhI/lbA + Vv/U1NL/7+nm/uPd2v/i3Nn/4dvY/svGxf/59vT/9/Hu//fx7v748e7/9/Hu/vfx7v/17+z/2tXT/tbS + 0P+ru6n/UMBQ/km4Sf/Gxsf/oKChP6CgoT/BwcL+TbxN/l7GXv7W19X+7+rm/uHc2v7g29j+497b/s/M + yv769/X++PLv/vjy7/748+/++PLv/vjy7/7y7On+yMbH/snHx/6CtID+WcZZ/k29Tv7ExMX+nZ2fP56e + nz+/v8D/UcFR/mbMZv/Z2df/7+rn/trW1P/Z1dL/5N/d/s7Lyv/6+Pb/+fXy//n18v759fL/+fXy/vn1 + 8v/08O3/yMfG/pG9j/9izGL/Ysxi/lLBUv/CwsP/mpqcP5ycnT+8vL3/VsVW/nDRb//b3Nr/8u3q/u/r + 6P/v6uj/8ezp/t7a2P/f3t3/3tzb/9za2v7c2tr/zdbL/svayv/J18f/l8GV/mzRbP9r0Wv/a9Fr/ljG + WP+/v8D/l5eYP5mZmj+5ubr+XMlc/nnWef7e393+8+7r/vPu6/7z7uv+8+7r/vPu6/7z7uv+2tbU/srH + x/7HxcT+g8mC/nXWdf511nX+ddZ1/nXWdf511nX+ddZ1/l7JXv68vL3+kpKTP5aWlj+1tbb/Yc1h/oLb + gv/g4d//8+/s/vPv7P/z7+z/8+/s/vPv7P/z7+z/19TT/97d3f6jyaL/f9t//n7cfv9+3H7/ftx+/n7c + fv9+3H7/ftx+/mPOY/+5ubr/jo6PP4uLizm3t7j/Zc1l/ovfi//i4uH/9PLx/vTy8f/08vH/9PLx/vTy + 8f/08vH/19bV/6zOq/6I34j/h+CH/ofgh/+H4If/h+CH/ofgh/+H4If/h+CH/mbNZv+3t7j/hYWGOXx8 + fBC4uLj4ZrZn/o/jj/6p2qj+qtqp/qraqf6q2an+qtmp/qrZqf6p2Kj+pdik/pDjkP6Q45D+kOOQ/pDj + kP6Q45D+kOOQ/pDjkP6Q45D+jOOM/m2/bf6mpqf4e3t8EAAAAACrq6yQm6Oc/me2Z/9s0Wz/bdVt/m3V + bf9t1W3/bdVt/m3Vbf9t1W3/bdVt/23Vbf5t1W3/bdVt/m3Vbf9t1W3/bdVt/m3Vbf9r0Wv/abpp/pyk + nf+QkJGQAAAAAAAAAACAgIAFqqqqj7OztPiwsLH/qqqr/qqqq/+qqqv/qqqr/qqqq/+qqqv/qqqr/6qq + q/6qqqv/qqqr/qqqq/+qqqv/qqqr/qqqq/+vr6//rq6u+J2dnY99fX4FAAAAAAAAAAAAAAAAAAAAAIKC + gxCRkZI5m5ucP5ycnD+cnJw/nJycP5ycnD+cnJw/nJycP5ycnD+cnJw/nJycP5ycnD+cnJw/nJycP5ub + nD+SkpI5hISEEAAAAAAAAAAAAAAAAOAABwCAAAEAgAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAQCAAAEA4AAHACgA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS0tMDXR0dGR5eXp/eXl6f3l5 + en95eXp/eXl6f3l5en95eXp/eXl6f3l5en95eXl/cXFyZExMTA0AAAAAU1NUDZyendhynnL/Z6Nn/2ej + Z/9no2f/Z6Nn/2ejZ/9no2f/Z6Nn/2ejZ/9no2f/Z6Nn/3mmev+YmpnYU1NTDZWVlWVolmn/MJIw/zCS + MP8wkjD/MJIw/0mVSP9Xl1b/V5dW/1aWVf9WlVT/VZVU/1WUU/83kjb/e6l7/4KCg2Wmpqd/VpZW/zCW + MP8wljD/MJYw/zCWMP+cvpv/9e7q//Tq5f/y5+L/8eTf//Hj3f/v6OT/R5VG/2mqav+QkJF/rKysf1ib + Wf8wmzD/MJsw/zCbMP8wmzD/ncGc/+/m4v/g1tH/3dDL/9vMxv/ZycP/7eHc/0eZRv9qrmv/l5eZf6+v + sH9boVz/VqBV/8rMxf/IycL/x8e//8/Oy//x6ub/49zY/+jf2//f1M//3dDK/+7k4P9InUf/bLJs/56e + n3+zs7N/XaZd/1ynW//s5eL/5dnU/+PVzv/Z09D/8erm/+Pd2f/j3dn/8+vn/+PZ1f/v6OX/SKJH/222 + bf+jo6R/tLS1f16rX/9cq1v/7Ojl/9nSz//Wzsr/1dHP//Hq5v/j3dn/493Z//Ps6P/k3dn/8e3q/0mn + SP9tum7/pqanf7W1tX9ksWX/Z7Rm/+3p5v/Y0s//2tTR/9TRz//y7Oj/6OLe/+7o5P/o4d7/493a/+3q + 5/9Ur1P/dMF1/6amqH+zs7R/bLZs/3vBev/w7Or/3tjW/+Db2P/d29r/+PPw//jy7//48u//+PLv/9bS + 0P+9wLv/YL9g/3zGfP+jo6V/sLCxf2+5b/+GyYX/8e7r/+Hc2v/l4N7/393b//f08v/29PH/9vTx//bz + 8f/Nz8r/ecZ5/2TNZP+ByoH/np6gf6ysrX9yu3P/kdCR//Pw7v/y7ev/8u3r/+vm5P/a1tX/ycbG/6HD + n/+F0IT/ftF9/3LVcv9y1XL/hc6G/5iYmX+oqKh/db11/5zXnP/08vD/9PDt//Tw7f/08O3/4d7c/8TT + w/+F2YT/gN2A/4DdgP+A3YD/gN2A/4rQiv+RkZJ/mZmZZXq1e/+b3pv/w+LC/8Piwv/C4sL/wuLB/7na + uf+S35L/juOO/47jjv+O447/juOO/4zijP+GwYf/h4eIZVtbXA2rrqvYerN6/3i9eP94vXj/eL14/3i9 + eP94vXj/eL14/3i9eP94vXj/eL14/3i9eP95tHr/naGe2FxcXA0AAAAAXl5eDZycnWSpqap/qqqqf6qq + qn+qqqp/qqqqf6qqqn+qqqp/qqqqf6qqqn+pqal/m5ubZGBgYA0AAAAAgAEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAA== + + + \ No newline at end of file diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config b/Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config new file mode 100644 index 000000000..02451de7f --- /dev/null +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Duplicati/GUI/Duplicati.csproj b/Duplicati/GUI/Duplicati.csproj index 70167a5fb..5a26973ca 100644 --- a/Duplicati/GUI/Duplicati.csproj +++ b/Duplicati/GUI/Duplicati.csproj @@ -1,4 +1,4 @@ - + Debug @@ -16,11 +16,8 @@ 3.5 - v2.0 - - true @@ -47,6 +44,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -55,21 +54,20 @@ pdbonly x86 prompt + 4 - - False - ..\..\thirdparty\LightDataModel\LightDatamodel.dll - - ..\..\thirdparty\UnixSupport\UnixSupport.dll + + ..\..\thirdparty\LightDataModel\LightDatamodel.dll + @@ -628,14 +626,14 @@ {C03F6DFD-805A-4BE0-9338-64870ADDB4A2} Duplicati.Library.Backend.S3 + + {FF2BF37C-E502-4C98-BEA0-701671DDFA08} + Duplicati.Library.Backend.SSHv2 + {CCD76347-7DC7-4B42-B7E1-E500E624CAC3} Duplicati.Library.Backend.SkyDrive - - {6BA21DCB-1701-4936-B177-3B6D4DE3474E} - Duplicati.Library.Backend.SSH - {C0270709-2A40-43B5-8CF1-69581B9FA2A1} Duplicati.Library.Backend.TahoeLAFS diff --git a/Duplicati/GUI/MainForm.cs b/Duplicati/GUI/MainForm.cs index b4af92564..820e14e84 100644 --- a/Duplicati/GUI/MainForm.cs +++ b/Duplicati/GUI/MainForm.cs @@ -38,8 +38,8 @@ namespace Duplicati.GUI private Datamodel.ApplicationSettings m_settings; private delegate void EmptyDelegate(); - - private string[] m_initialArguments; + + private string[] m_initialArguments; private bool m_hasAttemptedBackupTermination = false; @@ -48,6 +48,11 @@ namespace Duplicati.GUI private string m_currentTooltip; private TrayIconProxy m_trayIcon; + public string[] InitialArguments + { + get { return m_initialArguments; } + set { m_initialArguments = value; } + } /// /// A recursion protection helper flag /// @@ -110,12 +115,6 @@ namespace Duplicati.GUI } } - public string[] InitialArguments - { - get { return m_initialArguments; } - set { m_initialArguments = value; } - } - public MainForm() { InitializeComponent(); @@ -302,10 +301,10 @@ namespace Duplicati.GUI else if (InitialArguments != null) HandleCommandlineArguments(InitialArguments); - if (Library.Utility.Utility.IsMono) - MonoSupport.BeginInvoke (this, new EmptyDelegate(HideWindow)); - else - BeginInvoke(new EmptyDelegate(HideWindow)); + if (Library.Utility.Utility.IsMono) + MonoSupport.BeginInvoke (this, new EmptyDelegate(HideWindow)); + else + BeginInvoke(new EmptyDelegate(HideWindow)); if (Program.TraylessMode) ShowStatus(); } @@ -319,16 +318,16 @@ namespace Duplicati.GUI { TrayIcon_MouseClick(sender, e); } - - private static void AbortOwnThread(object thread) - { - System.Threading.Thread.Sleep(5000); - if (Program.IsRunningMainLoop) - { - Program.IsRunningMainLoop = false; - ((System.Threading.Thread)thread).Abort(); - } - } + + private static void AbortOwnThread(object thread) + { + System.Threading.Thread.Sleep(5000); + if (Program.IsRunningMainLoop) + { + Program.IsRunningMainLoop = false; + ((System.Threading.Thread)thread).Abort(); + } + } private void TrayIcon_MouseClick(object sender, MouseEventArgs e) { @@ -531,11 +530,11 @@ namespace Duplicati.GUI Program.SingleInstance.SecondInstanceDetected -= new SingleInstance.SecondInstanceDelegate(SingleInstance_SecondInstanceDetected); EnsureBackupIsTerminated(e.CloseReason == CloseReason.UserClosing ? CloseReason.ApplicationExitCall : e.CloseReason); - - //The form occasionally hangs on Mono, so we force kill it after 5 secs if it did not quit itself - if (Library.Utility.Utility.IsMono) - new System.Threading.Thread(AbortOwnThread).Start (System.Threading.Thread.CurrentThread); - } + + //The form occasionally hangs on Mono, so we force kill it after 5 secs if it did not quit itself + if (Library.Utility.Utility.IsMono) + new System.Threading.Thread(AbortOwnThread).Start (System.Threading.Thread.CurrentThread); + } private void SingleInstance_SecondInstanceDetected(string[] commandlineargs) { @@ -558,10 +557,10 @@ namespace Duplicati.GUI private bool HandleCommandlineArguments(string[] _args) { - bool anyUiShown = false; - + bool anyUiShown = false; + List args = new List(_args); - Dictionary options = CommandLine.CommandLineParser.ExtractOptions(args); + Dictionary options = Library.Utility.CommandLineParser.ExtractOptions(args); //Backwards compatible options if (args.Count == 2 && args[0].ToLower().Trim() == "run-backup") @@ -593,26 +592,26 @@ namespace Duplicati.GUI MessageBox.Show(this, string.Format(Strings.MainForm.PauseOperationFailed, ex), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } - - anyUiShown = true; + + anyUiShown = true; } if (options.ContainsKey("run-backup")) { - string backupname = options["run-backup"]; + string backupname = options["run-backup"]; if (string.IsNullOrEmpty(backupname)) { if (WizardDialog == null || !WizardDialog.Visible) { WizardDialog = new WizardHandler(new System.Windows.Forms.Wizard.IWizardControl[] { new Wizard_pages.SelectBackup(Duplicati.GUI.Wizard_pages.WizardSettingsWrapper.MainAction.RunNow) }); WizardDialog.Show(); - anyUiShown = true; + anyUiShown = true; } } else { - anyUiShown = true; - + anyUiShown = true; + Datamodel.Schedule[] schedules = Program.DataConnection.GetObjects("Name LIKE ?", backupname.Trim()); if (schedules == null || schedules.Length == 0) { @@ -622,13 +621,13 @@ namespace Duplicati.GUI { MessageBox.Show(string.Format(Strings.MainForm.MultipleNamedBackupsFound, backupname, schedules.Length), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); } - else - { - if (Library.Utility.Utility.ParseBoolOption(options, "full")) - Program.WorkThread.AddTask(new FullBackupTask(schedules[0])); - else - Program.WorkThread.AddTask(new IncrementalBackupTask(schedules[0])); - } + else + { + if (Library.Utility.Utility.ParseBoolOption(options, "full")) + Program.WorkThread.AddTask(new FullBackupTask(schedules[0])); + else + Program.WorkThread.AddTask(new IncrementalBackupTask(schedules[0])); + } } } @@ -641,7 +640,7 @@ namespace Duplicati.GUI { WizardDialog = new WizardHandler(new System.Windows.Forms.Wizard.IWizardControl[] { new Wizard_pages.SelectBackup(Duplicati.GUI.Wizard_pages.WizardSettingsWrapper.MainAction.RunNow) }); WizardDialog.Show(); - anyUiShown = true; + anyUiShown = true; } } else @@ -651,40 +650,40 @@ namespace Duplicati.GUI { MessageBox.Show(string.Format(Strings.MainForm.NamedBackupGroupNotFound, groupname), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); } - else - { - bool full = Library.Utility.Utility.ParseBoolOption(options, "full"); - - foreach(Datamodel.Schedule s in schedules) - if (full) - Program.WorkThread.AddTask(new FullBackupTask(s)); - else - Program.WorkThread.AddTask(new IncrementalBackupTask(s)); - } - - anyUiShown = true; + else + { + bool full = Library.Utility.Utility.ParseBoolOption(options, "full"); + + foreach(Datamodel.Schedule s in schedules) + if (full) + Program.WorkThread.AddTask(new FullBackupTask(s)); + else + Program.WorkThread.AddTask(new IncrementalBackupTask(s)); + } + + anyUiShown = true; } } if (Library.Utility.Utility.ParseBoolOption(options, "show-status")) - { + { ShowStatus(); - anyUiShown = true; - } + anyUiShown = true; + } - if (Library.Utility.Utility.ParseBoolOption(options, "show-wizard")) - { + if (Library.Utility.Utility.ParseBoolOption(options, "show-wizard")) + { ShowWizard(); - anyUiShown = true; - } + anyUiShown = true; + } //Resume if requested if (Library.Utility.Utility.ParseBoolOption(options, "resume")) - { + { Program.LiveControl.Resume(); - anyUiShown = true; - } - + anyUiShown = true; + } + return anyUiShown; } diff --git a/Duplicati/GUI/Program.cs b/Duplicati/GUI/Program.cs index b0886adeb..7d3bc92fc 100644 --- a/Duplicati/GUI/Program.cs +++ b/Duplicati/GUI/Program.cs @@ -153,7 +153,7 @@ namespace Duplicati.GUI //Find commandline options here for handling special startup cases - Dictionary commandlineOptions = CommandLine.CommandLineParser.ExtractOptions(new List(args)); + Dictionary commandlineOptions = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(new List(args)); foreach (string s in args) if ( diff --git a/Duplicati/GUI/Properties/Resources.Designer.cs b/Duplicati/GUI/Properties/Resources.Designer.cs index fbb8c089a..a887d4e3e 100644 --- a/Duplicati/GUI/Properties/Resources.Designer.cs +++ b/Duplicati/GUI/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Properties/Settings.Designer.cs b/Duplicati/GUI/Properties/Settings.Designer.cs index 0018bdd90..5d441df8c 100644 --- a/Duplicati/GUI/Properties/Settings.Designer.cs +++ b/Duplicati/GUI/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/ApplicationSetup.Designer.cs b/Duplicati/GUI/Strings/ApplicationSetup.Designer.cs index f06b2d513..d1fa66873 100644 --- a/Duplicati/GUI/Strings/ApplicationSetup.Designer.cs +++ b/Duplicati/GUI/Strings/ApplicationSetup.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/Common.Designer.cs b/Duplicati/GUI/Strings/Common.Designer.cs index 2fea82211..142a11736 100644 --- a/Duplicati/GUI/Strings/Common.Designer.cs +++ b/Duplicati/GUI/Strings/Common.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/DatabaseUpgrader.Designer.cs b/Duplicati/GUI/Strings/DatabaseUpgrader.Designer.cs index 424beef75..86997122e 100644 --- a/Duplicati/GUI/Strings/DatabaseUpgrader.Designer.cs +++ b/Duplicati/GUI/Strings/DatabaseUpgrader.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/DuplicatiRunner.Designer.cs b/Duplicati/GUI/Strings/DuplicatiRunner.Designer.cs index 7301fa895..e950b5c63 100644 --- a/Duplicati/GUI/Strings/DuplicatiRunner.Designer.cs +++ b/Duplicati/GUI/Strings/DuplicatiRunner.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/DynamicSetupHelper.Designer.cs b/Duplicati/GUI/Strings/DynamicSetupHelper.Designer.cs index 2278b7358..474e52f6f 100644 --- a/Duplicati/GUI/Strings/DynamicSetupHelper.Designer.cs +++ b/Duplicati/GUI/Strings/DynamicSetupHelper.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/ListBackupFiles.Designer.cs b/Duplicati/GUI/Strings/ListBackupFiles.Designer.cs index 7dbc67baf..17f03a3df 100644 --- a/Duplicati/GUI/Strings/ListBackupFiles.Designer.cs +++ b/Duplicati/GUI/Strings/ListBackupFiles.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/MainForm.Designer.cs b/Duplicati/GUI/Strings/MainForm.Designer.cs index abe0ce1c0..b64a192c4 100644 --- a/Duplicati/GUI/Strings/MainForm.Designer.cs +++ b/Duplicati/GUI/Strings/MainForm.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/Program.Designer.cs b/Duplicati/GUI/Strings/Program.Designer.cs index 9d70b2f7c..27e36d3ac 100644 --- a/Duplicati/GUI/Strings/Program.Designer.cs +++ b/Duplicati/GUI/Strings/Program.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/Scheduler.Designer.cs b/Duplicati/GUI/Strings/Scheduler.Designer.cs index 65ebf98a4..94891c0d5 100644 --- a/Duplicati/GUI/Strings/Scheduler.Designer.cs +++ b/Duplicati/GUI/Strings/Scheduler.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/ServiceStatus.Designer.cs b/Duplicati/GUI/Strings/ServiceStatus.Designer.cs index 0926c0e4e..3f3b68004 100644 --- a/Duplicati/GUI/Strings/ServiceStatus.Designer.cs +++ b/Duplicati/GUI/Strings/ServiceStatus.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/TaskType.Designer.cs b/Duplicati/GUI/Strings/TaskType.Designer.cs index c7703693d..04d6fcacb 100644 --- a/Duplicati/GUI/Strings/TaskType.Designer.cs +++ b/Duplicati/GUI/Strings/TaskType.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/TrayIconWrapper.Designer.cs b/Duplicati/GUI/Strings/TrayIconWrapper.Designer.cs index bdd047590..e01360d5a 100644 --- a/Duplicati/GUI/Strings/TrayIconWrapper.Designer.cs +++ b/Duplicati/GUI/Strings/TrayIconWrapper.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Strings/WizardHandler.Designer.cs b/Duplicati/GUI/Strings/WizardHandler.Designer.cs index 52e439200..18547f5b5 100644 --- a/Duplicati/GUI/Strings/WizardHandler.Designer.cs +++ b/Duplicati/GUI/Strings/WizardHandler.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/AdvancedOptions.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/AdvancedOptions.Designer.cs index 39e8036e5..fc648df33 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/AdvancedOptions.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/AdvancedOptions.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/CleanupSettings.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/CleanupSettings.Designer.cs index a33dafe12..192b84c66 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/CleanupSettings.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/CleanupSettings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/EditFilters.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/EditFilters.Designer.cs index d09427c5d..48238b1ef 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/EditFilters.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/EditFilters.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/FinishedAdd.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/FinishedAdd.Designer.cs index 83e30b8e3..8bb18b8fe 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/FinishedAdd.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/FinishedAdd.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/PasswordSettings.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/PasswordSettings.Designer.cs index 2c65cac73..ff8288bde 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/PasswordSettings.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/PasswordSettings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectFiles.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectFiles.Designer.cs index c94a13ec7..1fd3e22b4 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectFiles.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectFiles.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectName.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectName.Designer.cs index 57777e572..f1a665a07 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectName.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectName.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectWhen.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectWhen.Designer.cs index e6a891372..0fc595418 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectWhen.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/SelectWhen.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/SettingOverrides.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/SettingOverrides.Designer.cs index 6b88139c4..e46101e6e 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/SettingOverrides.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/SettingOverrides.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Add backup/Strings/ThrottleOptions.Designer.cs b/Duplicati/GUI/Wizard pages/Add backup/Strings/ThrottleOptions.Designer.cs index 3db10a58f..4821f3d26 100644 --- a/Duplicati/GUI/Wizard pages/Add backup/Strings/ThrottleOptions.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Add backup/Strings/ThrottleOptions.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Delete backup/Strings/DeleteFinished.Designer.cs b/Duplicati/GUI/Wizard pages/Delete backup/Strings/DeleteFinished.Designer.cs index ff1c8e5d5..7a59695c5 100644 --- a/Duplicati/GUI/Wizard pages/Delete backup/Strings/DeleteFinished.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Delete backup/Strings/DeleteFinished.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Restore/Strings/FinishedRestore.Designer.cs b/Duplicati/GUI/Wizard pages/Restore/Strings/FinishedRestore.Designer.cs index c7d5165a6..bda656913 100644 --- a/Duplicati/GUI/Wizard pages/Restore/Strings/FinishedRestore.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Restore/Strings/FinishedRestore.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Restore/Strings/SelectBackupVersion.Designer.cs b/Duplicati/GUI/Wizard pages/Restore/Strings/SelectBackupVersion.Designer.cs index b4c5ab4b5..1e5c9516a 100644 --- a/Duplicati/GUI/Wizard pages/Restore/Strings/SelectBackupVersion.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Restore/Strings/SelectBackupVersion.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Restore/Strings/TargetFolder.Designer.cs b/Duplicati/GUI/Wizard pages/Restore/Strings/TargetFolder.Designer.cs index 8dd24f453..e0cadfca6 100644 --- a/Duplicati/GUI/Wizard pages/Restore/Strings/TargetFolder.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Restore/Strings/TargetFolder.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/RestoreSetup/Strings/FinishedRestoreSetup.Designer.cs b/Duplicati/GUI/Wizard pages/RestoreSetup/Strings/FinishedRestoreSetup.Designer.cs index 8fdfe1b6e..cc5e0fa4d 100644 --- a/Duplicati/GUI/Wizard pages/RestoreSetup/Strings/FinishedRestoreSetup.Designer.cs +++ b/Duplicati/GUI/Wizard pages/RestoreSetup/Strings/FinishedRestoreSetup.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/RunNow/Strings/RunNowFinished.Designer.cs b/Duplicati/GUI/Wizard pages/RunNow/Strings/RunNowFinished.Designer.cs index 80c32df25..1507d49d5 100644 --- a/Duplicati/GUI/Wizard pages/RunNow/Strings/RunNowFinished.Designer.cs +++ b/Duplicati/GUI/Wizard pages/RunNow/Strings/RunNowFinished.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Strings/FirstLaunch.Designer.cs b/Duplicati/GUI/Wizard pages/Strings/FirstLaunch.Designer.cs index de359a57d..9178ca680 100644 --- a/Duplicati/GUI/Wizard pages/Strings/FirstLaunch.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Strings/FirstLaunch.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Strings/MainPage.Designer.cs b/Duplicati/GUI/Wizard pages/Strings/MainPage.Designer.cs index 566db8e00..d28dbc79a 100644 --- a/Duplicati/GUI/Wizard pages/Strings/MainPage.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Strings/MainPage.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Strings/SelectBackend.Designer.cs b/Duplicati/GUI/Wizard pages/Strings/SelectBackend.Designer.cs index 68d3f793f..95ff498e9 100644 --- a/Duplicati/GUI/Wizard pages/Strings/SelectBackend.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Strings/SelectBackend.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/Wizard pages/Strings/SelectBackup.Designer.cs b/Duplicati/GUI/Wizard pages/Strings/SelectBackup.Designer.cs index c9b1ce15d..b3d780a99 100644 --- a/Duplicati/GUI/Wizard pages/Strings/SelectBackup.Designer.cs +++ b/Duplicati/GUI/Wizard pages/Strings/SelectBackup.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/GUI/app.config b/Duplicati/GUI/app.config index bab23adca..34c68c314 100644 --- a/Duplicati/GUI/app.config +++ b/Duplicati/GUI/app.config @@ -1,3 +1,7 @@ - + + + + + diff --git a/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj b/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj index b6193fcb4..4dd7ded28 100644 --- a/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj +++ b/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj @@ -1,4 +1,4 @@ - + Debug @@ -13,7 +13,6 @@ 3.5 - v2.0 true @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Library/Backend/CloudFiles/Strings/CloudFilesUI.Designer.cs b/Duplicati/Library/Backend/CloudFiles/Strings/CloudFilesUI.Designer.cs index 29fb3f61e..dec7e828c 100644 --- a/Duplicati/Library/Backend/CloudFiles/Strings/CloudFilesUI.Designer.cs +++ b/Duplicati/Library/Backend/CloudFiles/Strings/CloudFilesUI.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.237 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Backend/CloudFiles/app.config b/Duplicati/Library/Backend/CloudFiles/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Backend/CloudFiles/app.config +++ b/Duplicati/Library/Backend/CloudFiles/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Backend/FTP/Duplicati.Library.Backend.FTP.csproj b/Duplicati/Library/Backend/FTP/Duplicati.Library.Backend.FTP.csproj index 2b4c16117..96951199e 100644 --- a/Duplicati/Library/Backend/FTP/Duplicati.Library.Backend.FTP.csproj +++ b/Duplicati/Library/Backend/FTP/Duplicati.Library.Backend.FTP.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Library/Backend/FTP/Strings/FTPBackend.Designer.cs b/Duplicati/Library/Backend/FTP/Strings/FTPBackend.Designer.cs index 3b62f7152..fefb024e5 100644 --- a/Duplicati/Library/Backend/FTP/Strings/FTPBackend.Designer.cs +++ b/Duplicati/Library/Backend/FTP/Strings/FTPBackend.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -69,6 +69,42 @@ namespace Duplicati.Library.Backend.Strings { } } + /// + /// Looks up a localized string similar to Use this option to accept any server certificate, regardless of what errors it may have. Please use --accept-specified-ssl-hash instead, whenever possible.. + /// + internal static string DescriptionAcceptAnyCertificateLong { + get { + return ResourceManager.GetString("DescriptionAcceptAnyCertificateLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Accept any server certificate. + /// + internal static string DescriptionAcceptAnyCertificateShort { + get { + return ResourceManager.GetString("DescriptionAcceptAnyCertificateShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If your server certificate is reported as invalid (eg. with self-signed certificates), you can supply the certificate hash to approve it anyway. The hash value must be entered in hex format without spaces.. + /// + internal static string DescriptionAcceptHashLong { + get { + return ResourceManager.GetString("DescriptionAcceptHashLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Optionally accept a known SSL certificate. + /// + internal static string DescriptionAcceptHashShort { + get { + return ResourceManager.GetString("DescriptionAcceptHashShort", resourceCulture); + } + } + /// /// Looks up a localized string similar to If this flag is set, the FTP connection is made in active mode. Even if the "ftp-passive" flag is also set, the connection will be made in active mode. /// @@ -159,6 +195,24 @@ namespace Duplicati.Library.Backend.Strings { } } + /// + /// Looks up a localized string similar to Since the FTP protocol is unable to determine if an upload was correctly completed or interrupted, the upload is verified after the upload. Use this option to disable the verification, which increases the posibillity of errors, but also increases performance.. + /// + internal static string DescriptionNolistverifyLong { + get { + return ResourceManager.GetString("DescriptionNolistverifyLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not verify uploads. + /// + internal static string DescriptionNolistverifyShort { + get { + return ResourceManager.GetString("DescriptionNolistverifyShort", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use this flag to communicate using Secure Socket Layer (SSL) over ftp (ftps).. /// @@ -194,5 +248,23 @@ namespace Duplicati.Library.Backend.Strings { return ResourceManager.GetString("MissingFolderError", resourceCulture); } } + + /// + /// Looks up a localized string similar to The file {0} was uploaded to the server, but the reported filesize was {1} and the size was expected to be {2}. + /// + internal static string UploadSizeVerificationFailure { + get { + return ResourceManager.GetString("UploadSizeVerificationFailure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file {0} was uploaded but not found on the server. + /// + internal static string UploadVerificationFailure { + get { + return ResourceManager.GetString("UploadVerificationFailure", resourceCulture); + } + } } } diff --git a/Duplicati/Library/Backend/FTP/Strings/FTPUI.Designer.cs b/Duplicati/Library/Backend/FTP/Strings/FTPUI.Designer.cs index 8aca2ee68..1e370ccf6 100644 --- a/Duplicati/Library/Backend/FTP/Strings/FTPUI.Designer.cs +++ b/Duplicati/Library/Backend/FTP/Strings/FTPUI.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Backend/FTP/app.config b/Duplicati/Library/Backend/FTP/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Backend/FTP/app.config +++ b/Duplicati/Library/Backend/FTP/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Backend/File/Duplicati.Library.Backend.File.csproj b/Duplicati/Library/Backend/File/Duplicati.Library.Backend.File.csproj index 0b03e11db..180272802 100644 --- a/Duplicati/Library/Backend/File/Duplicati.Library.Backend.File.csproj +++ b/Duplicati/Library/Backend/File/Duplicati.Library.Backend.File.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Library/Backend/File/Strings/FileBackend.Designer.cs b/Duplicati/Library/Backend/File/Strings/FileBackend.Designer.cs index 1fff857d6..b8e54832a 100644 --- a/Duplicati/Library/Backend/File/Strings/FileBackend.Designer.cs +++ b/Duplicati/Library/Backend/File/Strings/FileBackend.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Backend/File/Strings/FileUI.Designer.cs b/Duplicati/Library/Backend/File/Strings/FileUI.Designer.cs index f8c226c87..02a2ff805 100644 --- a/Duplicati/Library/Backend/File/Strings/FileUI.Designer.cs +++ b/Duplicati/Library/Backend/File/Strings/FileUI.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Backend/File/app.config b/Duplicati/Library/Backend/File/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Backend/File/app.config +++ b/Duplicati/Library/Backend/File/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Backend/GoogleDocs/Duplicati.Library.Backend.GoogleDocs.csproj b/Duplicati/Library/Backend/GoogleDocs/Duplicati.Library.Backend.GoogleDocs.csproj index 9a7f100b8..3ce8c6808 100644 --- a/Duplicati/Library/Backend/GoogleDocs/Duplicati.Library.Backend.GoogleDocs.csproj +++ b/Duplicati/Library/Backend/GoogleDocs/Duplicati.Library.Backend.GoogleDocs.csproj @@ -1,4 +1,4 @@ - + Debug @@ -11,8 +11,8 @@ Duplicati.Library.Backend Duplicati.Library.Backend.GoogleDocs true - Duplicati.snk - v2.0 + Duplicati.snk + v4.0 512 @@ -36,33 +36,43 @@ prompt 4 + + none + false + bin\x86\Debug + 4 + x86 + + + none + true + bin\x86\Release + 4 + x86 + - - False - ..\..\..\..\thirdparty\gdata\Google.GData.AccessControl.DLL - - - False - ..\..\..\..\thirdparty\gdata\Google.GData.Client.dll - - - False - ..\..\..\..\thirdparty\gdata\Google.GData.Documents.dll - - - False - ..\..\..\..\thirdparty\gdata\Google.GData.Extensions.dll - + + ..\..\..\..\thirdparty\gdata\Google.GData.AccessControl.DLL + + + ..\..\..\..\thirdparty\gdata\Google.GData.Client.dll + + + ..\..\..\..\thirdparty\gdata\Google.GData.Documents.dll + + + ..\..\..\..\thirdparty\gdata\Google.GData.Extensions.dll + @@ -85,6 +95,19 @@ + + + GoogleDocsUI.cs + + + ResXFileCodeGenerator + GoogleDocs.Designer.cs + + + ResXFileCodeGenerator + GoogleDocsUI.Designer.cs + + {7886BA79-A38A-4288-8B8A-9F29D0E15AC0} @@ -99,19 +122,6 @@ Duplicati.Library.Utility - - - GoogleDocsUI.cs - - - ResXFileCodeGenerator - GoogleDocs.Designer.cs - - - ResXFileCodeGenerator - GoogleDocsUI.Designer.cs - - - \ No newline at end of file + diff --git a/Duplicati/Library/Backend/S3/Duplicati.Library.Backend.S3.csproj b/Duplicati/Library/Backend/S3/Duplicati.Library.Backend.S3.csproj index b3fe23b60..74b2f969d 100644 --- a/Duplicati/Library/Backend/S3/Duplicati.Library.Backend.S3.csproj +++ b/Duplicati/Library/Backend/S3/Duplicati.Library.Backend.S3.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,18 +52,18 @@ pdbonly x86 prompt + 4 - - False - ..\..\..\..\thirdparty\AWS SDK\AWSSDK.dll - + + ..\..\..\..\thirdparty\AWS SDK\AWSSDK.dll + diff --git a/Duplicati/Library/Backend/S3/app.config b/Duplicati/Library/Backend/S3/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Backend/S3/app.config +++ b/Duplicati/Library/Backend/S3/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Backend/SSH/Duplicati.Library.Backend.SSH.csproj b/Duplicati/Library/Backend/SSH/Duplicati.Library.Backend.SSH.csproj deleted file mode 100644 index 39478b2d7..000000000 --- a/Duplicati/Library/Backend/SSH/Duplicati.Library.Backend.SSH.csproj +++ /dev/null @@ -1,171 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {6BA21DCB-1701-4936-B177-3B6D4DE3474E} - Library - Properties - Duplicati.Library.Backend - Duplicati.Library.Backend.SSH - false - Duplicati.snk - - - 3.5 - v2.0 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - prompt - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - prompt - - - - - - - - - False - ..\..\..\..\thirdparty\SharpSSH\Tamir.SharpSSH.dll - - - - - - - - - UserControl - - - SSHCommonOptions.cs - - - - UserControl - - - SSHUI.cs - - - - True - True - SharpExpectProcess.resx - - - True - True - SSHBackend.resx - - - True - True - SSHCommonOptions.resx - - - True - True - SSHSettingsControl.resx - - - True - True - SSHUI.resx - - - - - {7886BA79-A38A-4288-8B8A-9F29D0E15AC0} - Duplicati.Winforms.Controls - - - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65} - Duplicati.Library.Utility - - - {C5899F45-B0FF-483C-9D38-24A9FCAAB237} - Duplicati.Library.Interface - - - {F61679A9-E5DE-468A-B5A4-05F92D0143D2} - Duplicati.Library.Backend.FTP - - - - - - - - - SSHCommonOptions.cs - - - SSHUI.cs - - - Designer - ResXFileCodeGenerator - SharpExpectProcess.Designer.cs - - - Designer - ResXFileCodeGenerator - SSHBackend.Designer.cs - - - ResXFileCodeGenerator - SSHCommonOptions.Designer.cs - - - ResXFileCodeGenerator - SSHSettingsControl.Designer.cs - - - ResXFileCodeGenerator - SSHUI.Designer.cs - - - - - \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSH/OutputStreamWrapper.cs b/Duplicati/Library/Backend/SSH/OutputStreamWrapper.cs deleted file mode 100644 index 1f6cc9acc..000000000 --- a/Duplicati/Library/Backend/SSH/OutputStreamWrapper.cs +++ /dev/null @@ -1,138 +0,0 @@ -#region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede -// http://www.hexad.dk, opensource@hexad.dk -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion -using System; -using System.Collections.Generic; -using System.Text; - -namespace Duplicati.Library.Backend -{ - /// - /// This class wraps a regular .Net stream for use with SharpSSH - /// - internal class OutputStreamWrapper: Tamir.SharpSsh.java.io.OutputStream - { - private System.IO.Stream m_stream; - public OutputStreamWrapper(System.IO.Stream s) - { - m_stream = s; - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - - public override bool CanRead - { - get - { - return m_stream.CanRead; - } - } - - public override bool CanSeek - { - get - { - return m_stream.CanSeek; - } - } - - public override bool CanTimeout - { - get - { - return m_stream.CanTimeout; - } - } - - public override bool CanWrite - { - get - { - return m_stream.CanWrite; - } - } - - public override void close() - { - base.close(); - m_stream.Close(); - } - - public override void Close() - { - base.Close(); - m_stream.Close(); - } - - public override void Flush() - { - base.Flush(); - m_stream.Flush(); - } - - public override void flush() - { - base.flush(); - m_stream.Flush(); - } - - public override long Length - { - get - { - return m_stream.Length; - } - } - - public override long Position - { - get - { - return m_stream.Position; - } - set - { - m_stream.Position = value; - } - } - - public override void WriteByte(byte value) - { - m_stream.WriteByte(value); - } - - public override long Seek(long offset, System.IO.SeekOrigin origin) - { - return m_stream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - m_stream.SetLength(value); - } - - public override void write(byte[] buffer, int offset, int count) - { - m_stream.Write(buffer, offset, count); - } - } -} diff --git a/Duplicati/Library/Backend/SSH/Properties/AssemblyInfo.cs b/Duplicati/Library/Backend/SSH/Properties/AssemblyInfo.cs deleted file mode 100644 index ffe2a9c69..000000000 --- a/Duplicati/Library/Backend/SSH/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -#region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede -// http://www.hexad.dk, opensource@hexad.dk -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion -using System.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("SSH")] -[assembly: AssemblyDescription("An SSH backend for Duplicati")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("HexaD")] -[assembly: AssemblyProduct("Duplicati.Backend.SSH")] -[assembly: AssemblyCopyright("LGPL, Copyright © HexaD 2010")] -[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("71ab6748-ce94-483b-a1e6-3c84e06567a9")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.1.276")] -[assembly: AssemblyFileVersion("1.0.1.276")] diff --git a/Duplicati/Library/Backend/SSH/SFTPCon.cs b/Duplicati/Library/Backend/SSH/SFTPCon.cs deleted file mode 100644 index de354300d..000000000 --- a/Duplicati/Library/Backend/SSH/SFTPCon.cs +++ /dev/null @@ -1,97 +0,0 @@ -#region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede -// http://www.hexad.dk, opensource@hexad.dk -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion -using System; -using System.Collections.Generic; -using System.Text; - -namespace Duplicati.Library.Backend -{ - /// - /// Internal wrapper class for the SFTP exposed by SharpSSH - /// - internal class SFTPCon: Tamir.SharpSsh.Sftp, IDisposable - { - public SFTPCon(string hostname, string username) - : base(hostname, username) - { - } - - public SFTPCon(string hostname, string username, string password) - : base(hostname, username, password) - { - } - - protected Tamir.SharpSsh.jsch.ChannelSftp SFTPChannel - { - get { return (Tamir.SharpSsh.jsch.ChannelSftp)m_channel; } - } - - /// - /// The internal GetFileList removes the size and modified data - /// - /// The path to list files from - /// - public List ListFiles(string path) - { - List files = new List(); - foreach (Tamir.SharpSsh.jsch.ChannelSftp.LsEntry e in this.SFTPChannel.ls(path)) - files.Add(e); - - return files; - } - - public void Delete(string filename) - { - this.SFTPChannel.rm(filename); - } - - public void SetCurrenDir(string path) - { - this.SFTPChannel.cd(path); - } - - public void Get(string filename, System.IO.Stream outputstream) - { - this.SFTPChannel.get( - filename, - new OutputStreamWrapper(outputstream) - ); - } - - public void Put(string filename, System.IO.Stream inputstream) - { - this.SFTPChannel.put( - new Tamir.Streams.InputStreamWrapper(inputstream), - filename, - Tamir.SharpSsh.jsch.ChannelSftp.OVERWRITE - ); - } - - #region IDisposable Members - - public void Dispose() - { - if (this.Connected) - this.Close(); - } - - #endregion - } -} diff --git a/Duplicati/Library/Backend/SSH/SSHBackend.cs b/Duplicati/Library/Backend/SSH/SSHBackend.cs deleted file mode 100644 index 5c9819c06..000000000 --- a/Duplicati/Library/Backend/SSH/SSHBackend.cs +++ /dev/null @@ -1,779 +0,0 @@ -#region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede -// http://www.hexad.dk, opensource@hexad.dk -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; -using Duplicati.Library.Interface; - -namespace Duplicati.Library.Backend -{ - public class SSH : IBackend_v2, IStreamingBackend, IBackendGUI - { - public const string SFTP_PATH_OPTION = "sftp-command"; - public const string USE_UNMANAGED_OPTION = "use-sftp-application"; - public const string SSH_KEYFILE_OPTION = "ssh-keyfile"; - public const string SSH_NO_CD_OPTION = "ssh-no-cd-command"; - - private string m_server; - private string m_path; - private string m_username; - private string m_password; - Dictionary m_options; - private int m_transfer_timeout; - - private string m_sftp; - private string m_ssh_options; - - private const int SSH_TIMEOUT = 30 * 1000; - - private bool m_write_log_info = false; - - private bool m_useManaged = true; - private bool m_noCdCommand = false; - - private int m_port = 22; - - /// - /// The managed connection - /// - private SFTPCon m_con; - - /// - /// A value indicating if the *CLIENT* is a linux client. - /// Fortunately the sftp program seems to be client dependent, and not server dependant like the ftp program. - /// - private bool m_isLinux = false; - - public SSH() - { - m_isLinux = Library.Utility.Utility.IsClientLinux; - } - - public SSH(string url, Dictionary options) - : this() - { - m_options = options; - Uri u = new Uri(url); - if (!string.IsNullOrEmpty(u.UserInfo)) - { - if (u.UserInfo.IndexOf(":") >= 0) - { - m_username = u.UserInfo.Substring(0, u.UserInfo.IndexOf(":")); - m_password = u.UserInfo.Substring(u.UserInfo.IndexOf(":") + 1); - } - else - { - m_username = u.UserInfo; - if (options.ContainsKey("ftp-password")) - m_password = options["ftp-password"]; - } - } - else - { - if (options.ContainsKey("ftp-username")) - m_username = options["ftp-username"]; - if (options.ContainsKey("ftp-password")) - m_password = options["ftp-password"]; - } - - m_path = u.AbsolutePath; - - m_useManaged = !Utility.Utility.ParseBoolOption(options, USE_UNMANAGED_OPTION); - - m_noCdCommand = Utility.Utility.ParseBoolOption(options, SSH_NO_CD_OPTION); - - if (m_isLinux) - { - //HACK: "AbsolutePath" strips extra slashes under mono, so we re-add them here - int ix = url.IndexOf(m_path); - if (ix > 0 && url[ix - 1] == '/') - m_path = "/" + m_path; - } - - //Remove 1 leading slash so server/path is mapped to "path", - // and server//path is mapped to "/path" - m_path = m_path.Substring(1); - - if (!m_path.EndsWith("/")) - m_path += "/"; - - m_server = u.Host; - - if (options.ContainsKey(SFTP_PATH_OPTION)) - m_sftp = options[SFTP_PATH_OPTION]; - - if (string.IsNullOrEmpty(m_sftp)) - { - if (m_isLinux) - m_sftp = "sftp"; - else - m_sftp = "psftp.exe"; - } - - if (options.ContainsKey("ssh-options")) - m_ssh_options = options["ssh-options"]; - else - m_ssh_options = "-C"; - - if (!u.IsDefaultPort) - { - m_ssh_options += " -P " + u.Port; - m_port = u.Port; - } - - if (m_options.ContainsKey("transfer-timeout")) - m_transfer_timeout = Math.Min(1000 * 60 * 60, Math.Max(1000 * 60, (int)Duplicati.Library.Utility.Timeparser.ParseTimeSpan(m_options["transfer-timeout"]).TotalMilliseconds)); - else - m_transfer_timeout = 1000 * 60 * 15; - - m_write_log_info = Utility.Utility.ParseBoolOption(options, "debug-to-console"); - } - - #region IBackendInterface Members - - public string DisplayName - { - get { return Strings.SSHBackend.DisplayName; } - } - - public string ProtocolKey - { - get { return "ssh"; } - } - - public List List() - { - if (m_useManaged) - return ListManaged(); - else - return ListUnmanaged(); - } - - public void Put(string remotename, string filename) - { - if (m_useManaged) - PutManaged(getFullPath(remotename), filename); - else - PutUnmanaged(getFullPath(remotename), filename); - } - - public void Get(string remotename, string filename) - { - if (m_useManaged) - GetManaged(getFullPath(remotename), filename); - else - GetUnmanaged(getFullPath(remotename), filename); - } - - public void Delete(string remotename) - { - if (m_useManaged) - DeleteManaged(getFullPath(remotename)); - else - DeleteUnmanaged(getFullPath(remotename)); - } - - public IList SupportedCommands - { - get - { - return new List(new ICommandLineArgument[] { - new CommandLineArgument(SFTP_PATH_OPTION, CommandLineArgument.ArgumentType.Path, Strings.SSHBackend.DescriptionSFTPCommandShort, Strings.SSHBackend.DescriptionSFTPCommandLong, Library.Utility.Utility.IsClientLinux ? "sftp" : "psftp.exe"), - new CommandLineArgument("ssh-options", CommandLineArgument.ArgumentType.String, Strings.SSHBackend.DescriptionSSHOptionsShort, Strings.SSHBackend.DescriptionSSHOptionsLong, "-C"), - new CommandLineArgument("ftp-password", CommandLineArgument.ArgumentType.String, Strings.SSHBackend.DescriptionFTPPasswordShort, Strings.SSHBackend.DescriptionFTPPasswordLong), - new CommandLineArgument("ftp-username", CommandLineArgument.ArgumentType.String, Strings.SSHBackend.DescriptionFTPUsernameShort, Strings.SSHBackend.DescriptionFTPUsernameLong), - new CommandLineArgument("debug-to-console", CommandLineArgument.ArgumentType.Boolean, Strings.SSHBackend.DescriptionDebugToConsoleShort, Strings.SSHBackend.DescriptionDebugToConsoleLong), - new CommandLineArgument("transfer-timeout", CommandLineArgument.ArgumentType.Timespan, Strings.SSHBackend.DescriptionTransferTimeoutShort, Strings.SSHBackend.DescriptionTransferTimeoutLong, "15m"), - new CommandLineArgument(USE_UNMANAGED_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.SSHBackend.DescriptionUnmanagedShort, Strings.SSHBackend.DescriptionUnmanagedLong, "false"), - new CommandLineArgument(SSH_KEYFILE_OPTION, CommandLineArgument.ArgumentType.Path, Strings.SSHBackend.DescriptionSshkeyfileShort, Strings.SSHBackend.DescriptionSshkeyfileLong), - new CommandLineArgument(SSH_NO_CD_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.SSHBackend.DescriptionSshnocdShort, Strings.SSHBackend.DescriptionSshnocdLong), - }); - - } - } - - public string Description - { - get - { - return Strings.SSHBackend.Description; - } - } - - #endregion - - #region IBackend_v2 Implementation - - public void Test() - { - List(); - } - - public void CreateFolder() - { - if (m_useManaged) - CreateFolderManaged(); - else - CreateFolderUnmanaged(); - } - - #endregion - - #region IDisposable Members - - public void Dispose() - { - if (m_con != null) - { - m_con.Dispose(); - m_con = null; - } - } - - #endregion - - #region Unmanaged Implementation - - private List ListUnmanaged() - { - - if (m_write_log_info) - Console.WriteLine("******** " + Strings.SSHBackend.DebugListHeader + " *******"); - List files = new List(); - - using (SharpExpect.SharpExpectProcess p = GetUnmanagedConnection(true)) - { - string path = m_noCdCommand ? m_path : "."; - - if (m_isLinux) - p.Sendline("ls -la " + path); - else - p.Sendline("ls " + path); - p.Sendline("exit"); - - string s; - bool first = true; - - int timeout = 30000; - if (m_options.ContainsKey("transfer-timeout")) - timeout = m_transfer_timeout; - else //No overrides, pick the longest default time - timeout = Math.Max(m_transfer_timeout, timeout); - - - while ((s = p.GetNextOutputLine(first ? timeout : 5000)) != null) - { - first = false; - FileEntry fe = FTP.ParseLine(s.Trim()); - if (fe != null && fe.Name != "." && fe.Name != "..") - files.Add(fe); - else if (m_write_log_info) - Console.WriteLine(string.Format(Strings.SSHBackend.DebugParseFailed, s)); - } - - if (!p.Process.WaitForExit(5000)) - throw new Exception(Strings.SSHBackend.CloseTimeoutError + "\r\n" + p.LogKillAndDispose()); - - if (m_write_log_info) - { - Console.WriteLine("******** " + Strings.SSHBackend.DebugListFooter + " *******"); - Console.WriteLine(p.LogKillAndDispose()); - } - - return files; - } - - } - - - private void PutUnmanaged(string remotename, string filename) - { - if (m_write_log_info) - Console.WriteLine("******** " + Strings.SSHBackend.DebugPutHeader + " ********"); - using (SharpExpect.SharpExpectProcess p = GetUnmanagedConnection(true)) - { - string cmd = "put \"" + filename + "\" \"" + remotename + "\""; - - p.Sendline(cmd); - p.Sendline("exit"); - - //We assume 1kb pr. second - int timeout = (int)Math.Min(int.MaxValue, (new System.IO.FileInfo(filename).Length / 1024.0) * 1000); - - //Obey user overrides - if (m_options.ContainsKey("transfer-timeout")) - timeout = m_transfer_timeout; - else //No overrides, pick the longest default time - timeout = Math.Max(m_transfer_timeout, timeout); - - //Assume that all went well and wait for the process to exit - //For some reason the output is sometimes buffered until process exit - if (!p.Process.WaitForExit(timeout)) - throw new Exception(Strings.SSHBackend.UploadTimeoutError + "\r\n" + p.LogKillAndDispose()); - - //After the process is completed, the output is flushed, and we need to verify that the response was as expected - if (p.Expect(5000, "local\\:.+", "Uploading .*") < 0) - throw new Exception(string.Format(Strings.SSHBackend.UnexpectedResponseError, cmd)); - - if ((m_isLinux ? p.Expect(5000, "exit", "sftp> exit") : p.Expect(5000, "Using username .*")) < 0) - throw new Exception(Strings.SSHBackend.UnexpectedExitResponseError + "\r\n" + p.LogKillAndDispose()); - - if (m_write_log_info) - { - Console.WriteLine("******** " + Strings.SSHBackend.DebugPutFooter + " ********"); - Console.WriteLine(p.LogKillAndDispose()); - } - } - } - - private void GetUnmanaged(string remotename, string filename) - { - if (m_write_log_info) - Console.WriteLine("******** " + Strings.SSHBackend.DebugGetHeader + " ********"); - - using (SharpExpect.SharpExpectProcess p = GetUnmanagedConnection(true)) - { - string cmd = "get \"" + remotename + "\" \"" + filename + "\""; - - p.Sendline(cmd); - p.Sendline("exit"); - - //We assume 1kb pr. second - int timeout = (int)Math.Min(int.MaxValue, (new System.IO.FileInfo(filename).Length / 1024.0) * 1000); - - //Obey user overrides - if (m_options.ContainsKey("transfer-timeout")) - timeout = m_transfer_timeout; - else //No overrides, pick the longest default time - timeout = Math.Max(m_transfer_timeout, timeout); - - //Assume that all went well and wait for the process to exit - //For some reason the output is sometimes buffered until process exit - if (!p.Process.WaitForExit(timeout)) - throw new Exception(Strings.SSHBackend.DownloadTimeoutError + "\r\n" + p.LogKillAndDispose()); - - //After the process is completed, the output is flushed, and we need to verify that the response was as expected - if (p.Expect(SSH_TIMEOUT, "remote\\:.+", "Downloading .*", "Fetching .*") < 0) - throw new Exception(string.Format(Strings.SSHBackend.UnexpectedResponseError, cmd)); - - if ((m_isLinux ? p.Expect(5000, "exit", "sftp> exit") : p.Expect(5000, "Using username .*")) < 0) - throw new Exception(Strings.SSHBackend.UnexpectedExitResponseError + "\r\n" + p.LogKillAndDispose()); - - if (m_write_log_info) - { - Console.WriteLine("******** " + Strings.SSHBackend.DebugGetFooter + " ********"); - Console.WriteLine(p.LogKillAndDispose()); - } - } - } - - private void DeleteUnmanaged(string remotename) - { - if (m_write_log_info) - Console.WriteLine("******** " + Strings.SSHBackend.DebugDeleteHeader + " ********"); - - using (SharpExpect.SharpExpectProcess p = GetUnmanagedConnection(true)) - { - p.Sendline("rm \"" + remotename + "\""); - p.Sendline("exit"); - - if (!p.Process.WaitForExit(SSH_TIMEOUT)) - throw new Exception(Strings.SSHBackend.CloseTimeoutError + "\r\n" + p.LogKillAndDispose()); - - if (p.Expect(5000, ".*No such file or directory.*", ".*Couldn't.*") != -1) - throw new Exception(string.Format(Strings.SSHBackend.DeleteError, p.LogKillAndDispose())); - - if (m_write_log_info) - { - Console.WriteLine("******** " + Strings.SSHBackend.DebugDeleteFooter + " ********"); - Console.WriteLine(p.LogKillAndDispose()); - } - } - } - - private SharpExpect.SharpExpectProcess GetUnmanagedConnection(bool changeDir) - { - System.Diagnostics.Process p = new System.Diagnostics.Process(); - p.StartInfo.CreateNoWindow = true; - p.StartInfo.RedirectStandardError = true; - p.StartInfo.RedirectStandardInput = true; - p.StartInfo.RedirectStandardOutput = true; - p.StartInfo.UseShellExecute = false; - p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - - string server = m_server; - if (!string.IsNullOrEmpty(m_username)) - server = m_username + "@" + server; - - SharpExpect.SharpExpectProcess proc = null; - - try - { - if (m_isLinux) - { - //Since SSH uses direct tty/pty manipulation, and SharpExpect does not simulate this, - //we wrap the command with expect, which allows us to use stdin/stdout - p.StartInfo.FileName = "expect"; - - p.StartInfo.Arguments = @"-c ""set timeout 30"" -c ""spawn \""" + System.Environment.ExpandEnvironmentVariables(m_sftp) + @"\"" " + server + " " + m_ssh_options + @""" -c ""interact {~~}"""; - } - else - { - p.StartInfo.FileName = System.Environment.ExpandEnvironmentVariables(m_sftp); - p.StartInfo.Arguments = server + " " + m_ssh_options; - } - - try - { - //Console.WriteLine("Command: " + p.StartInfo.FileName); - //Console.WriteLine("Arguments: " + p.StartInfo.Arguments); - proc = SharpExpect.SharpExpectProcess.Spawn(p.StartInfo); - proc.LogEnabled = m_write_log_info; - proc.DefaultTimeout = SSH_TIMEOUT; - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - if (m_isLinux) - throw new Exception(string.Format(Strings.SSHBackend.LaunchErrorLinux, p.StartInfo.FileName, ex.Message), ex); - else - throw new Exception(string.Format(Strings.SSHBackend.LaunchErrorWindows, p.StartInfo.FileName, ex.Message), ex); - - } - - bool ready = false; - - while (!ready) - { - switch (proc.Expect(".*timeout.*", ".*denied.*", ".*authenticity.*", ".*Store key in cache\\?.*", ".*login as: .*", ".*(password|passphrase)\\:.*", "sftp>")) - { - case -1: - case 0: //Timeout - throw new Exception(string.Format(Strings.SSHBackend.ConnectionTimeoutError, proc.LogKillAndDispose())); - case 1: //Access denied - throw new Exception(string.Format(Strings.SSHBackend.AuthenticationError, proc.LogKillAndDispose())); - case 2: //Host authentication missing - case 3: - throw new Exception(string.Format(Strings.SSHBackend.HostNotAuthenticatedError, proc.LogKillAndDispose())); - case 4: //Send username (does not happen on linux) - if (string.IsNullOrEmpty(m_username)) - throw new Exception(Strings.SSHBackend.UsernameMissingError); - proc.Sendline(m_username); - continue; //Read next line - case 5: //Send password - //TODO: Allow the user to enter it with the option --ssh-askpass? - if (string.IsNullOrEmpty(m_password)) - throw new Exception(Strings.SSHBackend.PasswordMissingError); - proc.Sendpassword(m_password); - continue; //Wait for sftp - case 6: //We are ready! - ready = true; - break; - default: - throw new Exception(string.Format(Strings.SSHBackend.UnexpectedConnectionError, proc.LogKillAndDispose())); - } - } - - if (!string.IsNullOrEmpty(m_path) && changeDir && !m_noCdCommand) - { - proc.Sendline("cd \"" + m_path + "\""); - if (proc.Expect(".*not found.*", ".*No such file or directory.*", "sftp>", "Remote directory is now") < 2) - throw new Interface.FolderMissingException(string.Format(Strings.SSHBackend.FolderNotFoundError, m_path, proc.LogKillAndDispose())); - - string matchpath = m_path; - if (matchpath.EndsWith("/")) - matchpath = matchpath.Substring(0, matchpath.Length - 1); - - if (!matchpath.StartsWith("/")) - matchpath = "/" + matchpath; - - proc.Sendline("pwd"); - if (proc.Expect(".*" + System.Text.RegularExpressions.Regex.Escape(matchpath) + ".*") < 0) - throw new Exception(string.Format(Strings.SSHBackend.FolderVerificationError, proc.LogKillAndDispose())); - - while (proc.GetNextOutputLine(1000) != null) - { } //Clean output - - //Console.WriteLine("Connection is ready!"); - } - - } - catch (Exception ex) - { - if (m_write_log_info) - Console.WriteLine(ex.ToString()); - - if (proc != null) - { - if (m_write_log_info) - Console.WriteLine(proc.LogKillAndDispose()); - else - proc.Dispose(); - } - - throw; - } - - return proc; - } - - private void CreateFolderUnmanaged() - { - using (SharpExpect.SharpExpectProcess p = GetUnmanagedConnection(false)) - { - p.Sendline("mkdir \"" + m_path + "\""); - p.Sendline("exit"); - - if (!p.Process.WaitForExit(SSH_TIMEOUT)) - throw new Exception(Strings.SSHBackend.CloseTimeoutError + "\r\n" + p.LogKillAndDispose()); - - if (p.Expect(5000, ".*cannot create.*", ".*Failed.*") != -1) - throw new Exception(string.Format(Strings.SSHBackend.DeleteError, p.LogKillAndDispose())); - } - } - - #endregion - - #region Managed Implementation - - private SFTPCon CreateManagedConnection(bool changeDir) - { - //If the request is for a connection initialized to the right dir, we can use the cached version - if (changeDir && m_con != null) - return m_con; - - SFTPCon con; - - string keyfile; - m_options.TryGetValue(SSH_KEYFILE_OPTION, out keyfile); - - if ((keyfile ?? "").Trim().Length > 0) - { - ValidateKeyFile(m_options[SSH_KEYFILE_OPTION]); - - con = new SFTPCon(m_server, m_username); - con.AddIdentityFile(m_options[SSH_KEYFILE_OPTION], m_password); - } - else - con = new SFTPCon(m_server, m_username, m_password); - - con.Connect(m_port); - - try - { - if (!m_noCdCommand && !string.IsNullOrEmpty(m_path) && changeDir) - con.SetCurrenDir(m_path); - } - catch (Exception ex) - { - throw new Interface.FolderMissingException(string.Format(Strings.SSHBackend.FolderNotFoundManagedError, m_path, ex.Message), ex); - } - - //If the connection is initialized to the right folder, we cache it - if (changeDir) - m_con = con; - - return con; - } - - private string getFullPath(string path) - { - if (m_noCdCommand) - return m_path + path; - else - return path; - } - - private List ListManaged() - { - List files = new List(); - - DateTime epochOffset = new DateTime(1970, 1, 1); - - string path = m_noCdCommand ? m_path : "."; - - foreach (Tamir.SharpSsh.jsch.ChannelSftp.LsEntry ls in CreateManagedConnection(true).ListFiles(path)) - if (ls.getFilename().ToString() != "." && ls.getFilename().ToString() != "..") - files.Add(new FileEntry(ls.getFilename().ToString(), ls.getAttrs().getSize(), epochOffset.Add(new TimeSpan(ls.getAttrs().getATime() * TimeSpan.TicksPerSecond)), epochOffset.Add(new TimeSpan(ls.getAttrs().getMTime() * TimeSpan.TicksPerSecond)))); - - return files; - } - - private void PutManaged(string remotename, string filename) - { - using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)) - Put(remotename, fs); - } - - private void GetManaged(string remotename, string filename) - { - using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) - Get(remotename, fs); - } - - private void DeleteManaged(string remotename) - { - CreateManagedConnection(true).Delete(remotename); - } - - private void CreateFolderManaged() - { - string p = m_path; - - using (SFTPCon con = CreateManagedConnection(false)) - { - try - { - //Some SSH implementations fail if the folder name has a trailing slash - if (p.EndsWith ("/")) - p = p.Substring (0, p.Length - 1); - - con.Mkdir(p); - } - catch - { - //For backwards compatibility, we also try to create the folder WITH a trailing slash, - // this should never work, but in case there is a SSH implementation that relies - // on this we try that too - if (p != m_path) - { - try - { - //If this succeeds, we continue - con.Mkdir(m_path); - return; - } - catch { } - } - - //We report the original error - throw; - } - } - } - - #endregion - - #region IBackendGUI Members - - public string PageTitle - { - get { return SSHUI.PageTitle; } - } - - public string PageDescription - { - get { return SSHUI.PageDescription; } - } - - public System.Windows.Forms.Control GetControl(IDictionary applicationSettings, IDictionary options) - { - return new SSHUI(applicationSettings, options); - } - - public void Leave(System.Windows.Forms.Control control) - { - ((SSHUI)control).Save(false); - } - - public bool Validate(System.Windows.Forms.Control control) - { - return ((SSHUI)control).Save(true); - } - - public string GetConfiguration(IDictionary applicationSettings, IDictionary guiOptions, IDictionary commandlineOptions) - { - return SSHUI.GetConfiguration(applicationSettings, guiOptions, commandlineOptions); - } - #endregion - - #region IStreamingBackend Members - - public void Put(string remotename, System.IO.Stream stream) - { - if (!m_useManaged) - throw new Exception(Strings.SSHBackend.StreamingNotSupportedError); - - CreateManagedConnection(true).Put(getFullPath(remotename), stream); - } - - public void Get(string remotename, System.IO.Stream stream) - { - if (!m_useManaged) - throw new Exception(Strings.SSHBackend.StreamingNotSupportedError); - - CreateManagedConnection(true).Get(getFullPath(remotename), stream); - } - - #endregion - - /// - /// A helper function for validating SSH key files, as the support for those is limited in the SharpSSH library - /// - /// The key file to validate - public static void ValidateKeyFile(string filename) - { - if (!System.IO.File.Exists(filename)) - throw new System.IO.FileNotFoundException(string.Format(Strings.SSHBackend.KeyfileNotFoundError, filename)); - - string[] lines = System.IO.File.ReadAllLines(filename); - - //Step 1, find the "-----BEGIN RSA PRIVATE KEY-----" header - System.Text.RegularExpressions.Regex header = new Regex("BEGIN (RSA|DSA|SSH) PRIVATE KEY", RegexOptions.IgnoreCase); - - int firstline = -1; - for (int i = 0; i < Math.Min(100, lines.Length); i++) - if (header.Match(lines[i]).Success) - { - firstline = i; - break; - } - - if (firstline == -1) - throw new System.IO.InvalidDataException(Strings.SSHBackend.IncorrectFileHeaderError); - - //Step 2, read all headers, the first line without a ":" marks the start of the header - firstline += 1; - bool encrypted = false; - string encryptionFormat = null; - int lastLine; - - for (lastLine = firstline; lastLine < lines.Length; lastLine++) - { - if (!lines[lastLine].Contains(":")) - break; - - if (lines[lastLine].StartsWith("Proc-Type:") && lines[lastLine].Contains("ENCRYPTED")) - encrypted = true; - - if (lines[lastLine].StartsWith("DEK-Info:")) - encryptionFormat = lines[lastLine].Substring("DEK-Info:".Length).Split(',')[0].Trim(); - } - - //Step 3, validate the contents - if ((encrypted && encryptionFormat == null) || (!encrypted && encryptionFormat != null)) - throw new System.IO.InvalidDataException(string.Format(Strings.SSHBackend.KeyfileParseError, string.Join(Environment.NewLine, lines, 0, lastLine))); - - if (encryptionFormat != null && !encryptionFormat.Equals("DES-EDE3-CBC", StringComparison.InvariantCultureIgnoreCase)) - throw new System.IO.InvalidDataException(string.Format(Strings.SSHBackend.UnsupportedKeyfileEncryptionError, string.Join(Environment.NewLine, lines, 0, lastLine))); - } - } -} diff --git a/Duplicati/Library/Backend/SSH/SSHCommonOptions.Designer.cs b/Duplicati/Library/Backend/SSH/SSHCommonOptions.Designer.cs deleted file mode 100644 index 74a6d7783..000000000 --- a/Duplicati/Library/Backend/SSH/SSHCommonOptions.Designer.cs +++ /dev/null @@ -1,101 +0,0 @@ -namespace Duplicati.Library.Backend -{ - partial class SSHCommonOptions - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SSHCommonOptions)); - this.label1 = new System.Windows.Forms.Label(); - this.UseManagedAsDefault = new System.Windows.Forms.CheckBox(); - this.BrowseForSFTPButton = new System.Windows.Forms.Button(); - this.SFTPPath = new System.Windows.Forms.ComboBox(); - this.BrowseForSFTPDialog = new System.Windows.Forms.OpenFileDialog(); - this.toolTip = new System.Windows.Forms.ToolTip(this.components); - this.SuspendLayout(); - // - // label1 - // - resources.ApplyResources(this.label1, "label1"); - this.label1.Name = "label1"; - // - // UseManagedAsDefault - // - resources.ApplyResources(this.UseManagedAsDefault, "UseManagedAsDefault"); - this.UseManagedAsDefault.Checked = true; - this.UseManagedAsDefault.CheckState = System.Windows.Forms.CheckState.Checked; - this.UseManagedAsDefault.Name = "UseManagedAsDefault"; - this.UseManagedAsDefault.UseVisualStyleBackColor = true; - // - // BrowseForSFTPButton - // - resources.ApplyResources(this.BrowseForSFTPButton, "BrowseForSFTPButton"); - this.BrowseForSFTPButton.Name = "BrowseForSFTPButton"; - this.BrowseForSFTPButton.UseVisualStyleBackColor = true; - this.BrowseForSFTPButton.Click += new System.EventHandler(this.BrowseForSFTPButton_Click); - // - // SFTPPath - // - resources.ApplyResources(this.SFTPPath, "SFTPPath"); - this.SFTPPath.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; - this.SFTPPath.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.FileSystem; - this.SFTPPath.FormattingEnabled = true; - this.SFTPPath.Name = "SFTPPath"; - this.SFTPPath.TextChanged += new System.EventHandler(this.SFTPPath_TextChanged); - // - // BrowseForSFTPDialog - // - this.BrowseForSFTPDialog.DefaultExt = "exe"; - this.BrowseForSFTPDialog.FileName = "psftp.exe"; - resources.ApplyResources(this.BrowseForSFTPDialog, "BrowseForSFTPDialog"); - // - // SSHCommonOptions - // - resources.ApplyResources(this, "$this"); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.SystemColors.Window; - this.Controls.Add(this.SFTPPath); - this.Controls.Add(this.BrowseForSFTPButton); - this.Controls.Add(this.UseManagedAsDefault); - this.Controls.Add(this.label1); - this.Name = "SSHCommonOptions"; - this.Load += new System.EventHandler(this.SSHCommonOptions_Load); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Label label1; - private System.Windows.Forms.CheckBox UseManagedAsDefault; - private System.Windows.Forms.Button BrowseForSFTPButton; - private System.Windows.Forms.ComboBox SFTPPath; - private System.Windows.Forms.OpenFileDialog BrowseForSFTPDialog; - private System.Windows.Forms.ToolTip toolTip; - } -} diff --git a/Duplicati/Library/Backend/SSH/SSHCommonOptions.cs b/Duplicati/Library/Backend/SSH/SSHCommonOptions.cs deleted file mode 100644 index 871619608..000000000 --- a/Duplicati/Library/Backend/SSH/SSHCommonOptions.cs +++ /dev/null @@ -1,159 +0,0 @@ -#region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede -// http://www.hexad.dk, opensource@hexad.dk -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Data; -using System.Text; -using System.Windows.Forms; - -namespace Duplicati.Library.Backend -{ - public partial class SSHCommonOptions : UserControl - { - public const string SFTP_PATH = "SFTP Path"; - public const string DEFAULT_MANAGED = "Default Managed"; - public const string PATH_TESTED = "SFTP Path Tested"; - - private IDictionary m_applicationSettings; - private IDictionary m_options; - - private bool m_pathTested = false; - - private SSHCommonOptions() - { - InitializeComponent(); - } - - public SSHCommonOptions(IDictionary applicationSettings, IDictionary options) - : this() - { - m_applicationSettings = applicationSettings; - m_options = options; - } - - public bool Save(bool validate) - { - if (validate && !m_pathTested) - { - if (string.IsNullOrEmpty(SFTPPath.Text)) - { - if (MessageBox.Show(this, Strings.SSHCommonOptions.UseEmptyPathWarning, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes) - return false; - } - else - { - try - { - if (Utility.Utility.LocateFileInSystemPath(SFTPPath.Text) == null && MessageBox.Show(this, Strings.SSHCommonOptions.LocatingSftpError, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes) - return false; - } - catch (Exception ex) - { - if (MessageBox.Show(this, string.Format(Strings.SSHCommonOptions.SftpProgramNotFoundError, ex), Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes) - return false; - } - } - - m_pathTested = true; - } - - m_options[SFTP_PATH] = SFTPPath.Text; - m_options[DEFAULT_MANAGED] = UseManagedAsDefault.Checked.ToString(); - m_options[PATH_TESTED] = m_pathTested.ToString(); - - return true; - } - - private void SSHCommonOptions_Load(object sender, EventArgs e) - { - try - { - SFTPPath.Items.Clear(); - SFTPPath.Items.Add(ExecutableFilename); - - string autofind = Utility.Utility.LocateFileInSystemPath(ExecutableFilename); - if (!string.IsNullOrEmpty(autofind)) - { - BrowseForSFTPDialog.FileName = System.IO.Path.GetFileName(autofind); - BrowseForSFTPDialog.InitialDirectory = System.IO.Path.GetDirectoryName(autofind); - SFTPPath.Items.Add(autofind); - toolTip.SetToolTip(SFTPPath, string.Format(Strings.SSHCommonOptions.SftpAutodetectedInfo, autofind)); - } - else - { - toolTip.SetToolTip(SFTPPath, Strings.SSHCommonOptions.SftpNotAutodetectedWarning); - } - - if (m_options.ContainsKey(SFTP_PATH)) - SFTPPath.Text = m_options[SFTP_PATH]; - - bool b; - if (!m_options.ContainsKey(DEFAULT_MANAGED) || !bool.TryParse(m_options[DEFAULT_MANAGED], out b)) - b = true; - - UseManagedAsDefault.Checked = b; - - if (!m_options.ContainsKey(PATH_TESTED)) - m_pathTested = true; //Initially true because we don't want to prompt if there has been no change - else if (!bool.TryParse(m_options[PATH_TESTED], out m_pathTested)) - m_pathTested = false; - } - catch (Exception ex) - { - MessageBox.Show(this, string.Format(Strings.SSHCommonOptions.LoadError, ex), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - private void SFTPPath_TextChanged(object sender, EventArgs e) - { - m_pathTested = false; - } - - private void BrowseForSFTPButton_Click(object sender, EventArgs e) - { - if (BrowseForSFTPDialog.ShowDialog(this) == DialogResult.OK) - SFTPPath.Text = BrowseForSFTPDialog.FileName; - } - - private static string ExecutableFilename { get { return Utility.Utility.IsClientLinux ? "sftp" : "psftp.exe"; } } - - public static string GetConfiguration(IDictionary applicationSettings, IDictionary guiOptions, IDictionary commandlineOptions) - { - if (guiOptions.ContainsKey(SFTP_PATH) && !string.IsNullOrEmpty(guiOptions[SFTP_PATH])) - { - applicationSettings[SFTP_PATH] = guiOptions[SFTP_PATH]; - - //If the backend module is not set, assume we need this module, otherwise only apply settings if the module is actually ssh - if (commandlineOptions != null) - if (!applicationSettings.ContainsKey("backend-module") || applicationSettings["backend-module"] == "ssh") - commandlineOptions[SSH.SFTP_PATH_OPTION] = guiOptions[SFTP_PATH]; - } - - if (guiOptions.ContainsKey(DEFAULT_MANAGED)) - { - applicationSettings[DEFAULT_MANAGED] = guiOptions[DEFAULT_MANAGED]; - } - - return null; - } - } -} diff --git a/Duplicati/Library/Backend/SSH/SSHCommonOptions.resx b/Duplicati/Library/Backend/SSH/SSHCommonOptions.resx deleted file mode 100644 index 781782e73..000000000 --- a/Duplicati/Library/Backend/SSH/SSHCommonOptions.resx +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - True - - - - 8, 8 - - - 71, 13 - - - 0 - - - Path to SFTP - - - label1 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 3 - - - True - - - 16, 40 - - - 166, 17 - - - 1 - - - Use managed SSH as default - - - UseManagedAsDefault - - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 2 - - - - Top, Right - - - 184, 8 - - - 24, 20 - - - 3 - - - ... - - - BrowseForSFTPButton - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 - - - Top, Left, Right - - - 80, 8 - - - 104, 21 - - - 4 - - - SFTPPath - - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 0 - - - 17, 17 - - - Executables (*.exe)|*.exe|All files (*.*)|*.* - - - 187, 17 - - - True - - - 6, 13 - - - 212, 67 - - - BrowseForSFTPDialog - - - System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - toolTip - - - System.Windows.Forms.ToolTip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - SSHCommonOptions - - - System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSH/SSHSettingsControl.cs b/Duplicati/Library/Backend/SSH/SSHSettingsControl.cs deleted file mode 100644 index 9a595964c..000000000 --- a/Duplicati/Library/Backend/SSH/SSHSettingsControl.cs +++ /dev/null @@ -1,81 +0,0 @@ -#region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede -// http://www.hexad.dk, opensource@hexad.dk -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion -using System; -using System.Collections.Generic; -using System.Text; - -namespace Duplicati.Library.Backend -{ - public class SSHSettingsControl : Library.Interface.ISettingsControl, Library.Interface.IGUIMiniControl - { - #region ISettingsControl Members - - public string Key - { - get { return "ssh-settings"; } - } - - public void BeginEdit(IDictionary applicationSettings, IDictionary guiOptions) - { - GetConfiguration(applicationSettings, guiOptions, null); - } - - public void EndEdit(IDictionary applicationSettings, IDictionary guiOptions) - { - //No need to save - } - - #endregion - - #region IGUIControl Members - - public string PageTitle - { - get { return Strings.SSHSettingsControl.PageTitle; } - } - - public string PageDescription - { - get { return Strings.SSHSettingsControl.PageDescription; } - } - - public System.Windows.Forms.Control GetControl(IDictionary applicationSettings, IDictionary options) - { - return new SSHCommonOptions(applicationSettings, options); - } - - public void Leave(System.Windows.Forms.Control control) - { - ((SSHCommonOptions)control).Save(false); - } - - public bool Validate(System.Windows.Forms.Control control) - { - return ((SSHCommonOptions)control).Save(true); - } - - public string GetConfiguration(IDictionary applicationSettings, IDictionary guiOptions, IDictionary commandlineOptions) - { - return SSHCommonOptions.GetConfiguration(applicationSettings, guiOptions, commandlineOptions); - } - - #endregion - } -} diff --git a/Duplicati/Library/Backend/SSH/SharpExpectProcess.cs b/Duplicati/Library/Backend/SSH/SharpExpectProcess.cs deleted file mode 100644 index 967d0029b..000000000 --- a/Duplicati/Library/Backend/SSH/SharpExpectProcess.cs +++ /dev/null @@ -1,653 +0,0 @@ -#region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede -// http://www.hexad.dk, opensource@hexad.dk -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion -using System; -using System.Collections.Generic; -using System.Text; - -namespace Duplicati.Library.SharpExpect -{ - /// - /// This class is a primitive version of the Expect program for Linux. - /// It eases the manipulation of a remote process by waiting for certain - /// patterns in the program output. - /// It does not yet support raw reading and writing of the tty/pty devices in Linux. - /// The timimings are not entirely excact, and does not account for time spent in code, but only the waited time. - /// - public class SharpExpectProcess : IDisposable - { - public enum OutputSource - { - StdOut = 1, - StedErr = 2, - Both = 3 - } - - private System.Diagnostics.Process m_process; - private object m_lock = new object(); - private List m_stdOut = new List(); - private List m_stdErr = new List(); - private System.Threading.AutoResetEvent m_event = new System.Threading.AutoResetEvent(false); - private int m_defaultTimeout = 30 * 1000; - private int m_logLimit = 100; - private volatile bool m_stderr_done = true; - private volatile bool m_stdout_done = true; - private List m_log = new List(); - - /// - /// A value indicating if the log mechanism is enabled - /// - public bool LogEnabled - { - get { return m_log != null; } - set - { - if (value != this.LogEnabled) - { - if (value) - m_log = new List(); - else - m_log = null; - } - } - } - - /// - /// The number of messages to store in the process log - /// - public int LogLimit - { - get { return m_logLimit; } - set { m_logLimit = Math.Max(0, value); } - } - - /// - /// The timeout used if no timeout is specified in the function call - /// - public int DefaultTimeout - { - get { return m_defaultTimeout; } - set { m_defaultTimeout = value; } - } - - /// - /// Records a message in the log - /// - /// The prefix to store with the message - /// The message to record - /// The recorded message (without the prefix) - private string RecordInLog(string prefix, string message) - { - if (m_log == null || message == null) - return message; - - m_log.Add("*" + prefix + "*: " + message); - while (m_log.Count > m_logLimit) - m_log.RemoveAt(0); - - return message; - } - - /// - /// Constructs a new process helper around a running process - /// - /// The proccess to wrap - public SharpExpectProcess(System.Diagnostics.Process process) - { - m_process = process; - - if (m_process.StartInfo.RedirectStandardError) - { - m_stderr_done = false; - System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(StreamReader), m_process.StandardError); - } - - if (m_process.StartInfo.RedirectStandardOutput) - { - m_stdout_done = false; - System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(StreamReader), m_process.StandardOutput); - } - } - - /// - /// Returns the next output line (from stdout or stderr) but does not remove it - /// - /// The maximum time to wait for a new line - /// The next output line (from stdout or stderr) but does not remove it - public string PeekNextOutputLine(int maxWaitTime) - { - return PeekNextOutputLine(OutputSource.Both, maxWaitTime); - } - - /// - /// Returns the next output line from the selected source but does not remove it - /// - /// A value indicating what streams to examine for data - /// The maximum number of milliseconds to wait for a line to be received - /// The next output line from the selected source but does not remove it, returns null if no data is available - public string PeekNextOutputLine(OutputSource source, int maxWaitTime) - { - //Is there buffered output? - lock (m_lock) - { - if ((source == OutputSource.Both || source == OutputSource.StdOut) && m_stdOut.Count > 0) - return FindNextLine(m_stdOut, maxWaitTime); - - if ((source == OutputSource.Both || source == OutputSource.StedErr) && m_stdErr.Count > 0) - return FindNextLine(m_stdErr, maxWaitTime); - } - - return null; - } - - /// - /// Returns the next output line from either stdout or stderr, returns null if the operation times out. - /// - /// The next output line from either stdout or stderr, or null if the operation times out. - public string GetNextOutputLine() - { - return GetNextOutputLine(m_defaultTimeout); - } - - /// - /// Returns the next output line from the selected sources, returns null if the operation times out. - /// - /// A value indicating what streams to examine for data - /// The next output line from the selected sources, or null if the operation times out. - public string GetNextOutputLine(OutputSource source) - { - return GetNextOutputLine(source, m_defaultTimeout); - } - - /// - /// Returns the next output line from either stdout or stderr, returns null if the operation times out. - /// - /// The number of milliseconds to wait for the line to become ready - /// The next output line from either stdout or stderr, or null if the operation times out. - public string GetNextOutputLine(int millisecondsTimeout) - { - return GetNextOutputLine(OutputSource.Both, millisecondsTimeout); - } - - /// - /// Returns the next output line from the selected sources, returns null if the operation times out. - /// - /// A value indicating what streams to examine for data - /// The number of milliseconds to wait for the line to become ready - /// The next output line from the selected sources, or null if the operation times out. - public string GetNextOutputLine(OutputSource source, int millisecondsTimeout) - { - DateTime begin = DateTime.Now; - - //Is there buffered output? - string line = GetBufferedLine(source, millisecondsTimeout); - if (line != null) - return line; - - //Adjust the remaining time - if (millisecondsTimeout != System.Threading.Timeout.Infinite) - millisecondsTimeout = Math.Max(0, millisecondsTimeout - (int)((DateTime.Now - begin).TotalMilliseconds)); - - //If we are disposed, or done, there is no point in waiting - if (m_process == null || m_process.HasExited) - { - //Make sure the readers are done - while (m_process != null && !m_stderr_done) - System.Threading.Thread.Sleep(100); - - while (m_process != null && !m_stdout_done) - System.Threading.Thread.Sleep(100); - - //Make sure queue is empty - return GetBufferedLine(source, System.Threading.Timeout.Infinite); - } - - begin = DateTime.Now; - //If we can wait, do so, and try again, otherwise return null - if (millisecondsTimeout == System.Threading.Timeout.Infinite || millisecondsTimeout > 0) - m_event.WaitOne(millisecondsTimeout, false); - - //Adjust the remaining time - if (millisecondsTimeout != System.Threading.Timeout.Infinite) - millisecondsTimeout = Math.Max(0, millisecondsTimeout - (int)((DateTime.Now - begin).TotalMilliseconds)); - - return GetBufferedLine(source, millisecondsTimeout); - } - - /// - /// Helper method to extract a buffered line - /// - /// A value indicating what streams to examine - /// The time to wait for a new line - /// The next buffered line or null - private string GetBufferedLine(OutputSource source, int maxWaitTime) - { - bool returnStdOut; - bool returnStdErr; - - lock (m_lock) - { - //Check stdout - returnStdOut = ((source == OutputSource.Both || source == OutputSource.StdOut) && m_stdOut.Count > 0); - - //Check stderr - returnStdErr = ((source == OutputSource.Both || source == OutputSource.StedErr) && m_stdErr.Count > 0); - - //No data, so make sure the event is not set - if (!returnStdOut && !returnStdErr) - { - m_event.Reset(); - return null; - } - } - - if (returnStdOut) - return RecordInLog("O", ExtractNextLine(m_stdOut, maxWaitTime)); - if (returnStdErr) - return RecordInLog("E", ExtractNextLine(m_stdErr, maxWaitTime)); - - //Notify that there was no waiting data, we should not get here - return null; - } - - /// - /// Internal helper to remove the next line from the queue - /// - /// The queue to extract from - /// The maximum time to wait for a new line - /// The extracted line - private string ExtractNextLine(List queue, int maxWaitTime) - { - string line = FindNextLine(queue, maxWaitTime); - if (line == null) - return null; - - lock (m_lock) - if (queue[0] == line) - queue.RemoveAt(0); - else - queue[0] = queue[0].Substring(line.Length); - - return line; - } - - /// - /// This function attempts to reconstruct output into lines. - /// The reader threads put raw input into their respective queues, - /// but timing issues may mean that a line may be split over multiple - /// queue entries, and a single entry may contain more than one line. - /// - /// The queue to extract from - /// The time to wait for a new line - /// The next line, or null if there is no data - private string FindNextLine(List queue, int maxWaitTime) - { - return FindNextLine(queue, maxWaitTime, true); - } - - /// - /// This function attempts to reconstruct output into lines. - /// The reader threads put raw input into their respective queues, - /// but timing issues may mean that a line may be split over multiple - /// queue entries, and a single entry may contain more than one line. - /// - /// The queue to extract from - /// A value indicating if a wait should be performed if the extracted line does not end with a linefeed - /// The time to wait for a new line - /// The extracted line, or null if there is no data - private string FindNextLine(List queue, int maxWaitTime, bool allowWait) - { - char leadChar = '\r'; - char trailChar = '\n'; - string tmp; - - //Make sure no-one else touches the queue - lock (m_lock) - { - if (queue.Count == 0) - return null; - - //If the line is split over multiple entries, try to combine it back into a single line - while (queue[0].LastIndexOfAny(new char[] { leadChar, trailChar }) < 0 && queue.Count > 1) - { - queue[0] = queue[0] + queue[1]; - queue.RemoveAt(1); - } - - tmp = queue[0]; - } - - //If the line ends with return, then wait for a linefeed. - //The \r\n is windows style, and the remote output may be from a windows machine, - //regardless of the local OS. - if (tmp.EndsWith(leadChar.ToString())) - { - if(queue.Count == 1) - System.Threading.Thread.Sleep(500); - - lock(m_lock) - if (queue.Count > 1 && queue[1].Length > 0 && queue[1][0] == trailChar) - { - tmp += trailChar; - if (queue[1].Length > 1) - queue[1] = queue[1].Substring(1); - else - queue.RemoveAt(1); - } - } - - //Make sure no-one else touches the queue - lock (m_lock) - { - //If there is more than a single newline in the first entry, split it - - //Find out if there are newlines in the entry - int lineBreakPos = queue[0].IndexOf(leadChar); - if (lineBreakPos >= 0) - { - lineBreakPos++; - if (queue[0].Length > lineBreakPos && queue[0][lineBreakPos] == trailChar) - lineBreakPos++; - } - else - { - lineBreakPos = queue[0].IndexOf(trailChar); - if (lineBreakPos >= 0) - lineBreakPos++; - } - - - //If yes, extract the first line, and insert the rest of the string in the next slot - if (lineBreakPos >= 0 && lineBreakPos != queue[0].Length) - { - tmp = queue[0].Substring(0, lineBreakPos); - queue[0] = queue[0].Substring(tmp.Length); - queue.Insert(0, tmp); //This entry may also have newlines - } - } - - //If there is no more data, and the extracted line does not end in a linefeed, wait a little - if (allowWait && maxWaitTime != 0 && tmp.IndexOfAny(new char[] { leadChar, trailChar }) < 0) - { - DateTime begin = DateTime.Now; - m_event.WaitOne(maxWaitTime, false); - if (maxWaitTime != System.Threading.Timeout.Infinite) - { - maxWaitTime -= (int)((DateTime.Now - begin).TotalMilliseconds); - maxWaitTime = Math.Max(0, maxWaitTime); - } - - lock (m_lock) - if (queue.Count == 1) //No new data - return tmp; - - return FindNextLine(queue, maxWaitTime, maxWaitTime != 0); //Extract again, but don't wait - } - else - return tmp; //The line is good, just return - } - - /// - /// Waits until the output matches any of the patterns given - /// - /// Any number of patterns to look for, the strings must be regular expressions - /// The index of the matched pattern, or -1 if none was matched - public int Expect(params string[] possibilities) - { - return Expect(m_defaultTimeout, possibilities); - } - - /// - /// Waits until the output matches any of the patterns given - /// - /// The number of milliseconds to wait for the pattern to be matched - /// Any number of patterns to look for, the strings must be regular expressions - /// The index of the matched pattern, or -1 if none was matched - public int Expect(int millisecondsTimeout, params string[] possibilities) - { - List> lst = new List>(); - for (int i = 0; i < possibilities.Length; i++) - if (!string.IsNullOrEmpty(possibilities[i])) - lst.Add(new KeyValuePair(new System.Text.RegularExpressions.Regex(possibilities[i]), i)); - - KeyValuePair match = Expect(millisecondsTimeout, lst); - if (match.Value == null) - return -1; - else - return match.Key; - } - - /// - /// Waits until the output matches any of the patterns given, and returns the string as well as the value for the pattern. - /// - /// The number of milliseconds to wait for the pattern to be matched - /// Any number of patterns to look for, the strings must be regular expressions - /// The type of the value object to return - /// A pair with the value given, and the string matched - public KeyValuePair Expect(int millisecondsTimeout, List> possibilities) - { - List> lst = new List>(); - for (int i = 0; i < possibilities.Count; i++) - if (!string.IsNullOrEmpty(possibilities[i].Key)) - lst.Add(new KeyValuePair(new System.Text.RegularExpressions.Regex(possibilities[i].Key), possibilities[i].Value)); - - return Expect(millisecondsTimeout, possibilities); - } - - /// - /// Waits until the output matches any of the patterns given, and returns the string as well as the value for the pattern. - /// - /// The number of milliseconds to wait for the pattern to be matched - /// Any number of patterns to look for - /// The type of the value object to return - /// A pair with the value given, and the string matched - public KeyValuePair Expect(int millisecondsTimeout, List> possibilities) - { - if (possibilities == null || possibilities.Count == 0) - return new KeyValuePair(default(T), null); - - DateTime expiration = millisecondsTimeout < 0 ? DateTime.Now.AddYears(5) : DateTime.Now.AddMilliseconds(millisecondsTimeout); - - string combinedLines = ""; - - //string line = null; - while (DateTime.Now <= expiration) //|| line != null) - { - string line = GetNextOutputLine(1000); - if (line == null && DateTime.Now > expiration) - return new KeyValuePair(default(T), null); - - if (line != null) - { - combinedLines += line; - - foreach (KeyValuePair expr in possibilities) - if (expr.Key.Match(combinedLines).Success) - return new KeyValuePair(expr.Value, combinedLines); - } - } - - return new KeyValuePair(default(T), null); - } - - /// - /// Sends a line to the process - /// - /// The line to send - public void Sendline(string line) - { - RecordInLog("I", line); - if (m_process.HasExited) - throw new Exception(string.Format(Backend.Strings.SharpExpectProcess.WriteAfterExitError, LogKillAndDispose())); - m_process.StandardInput.WriteLine(line); - } - - /// - /// Send a password to the remote process. - /// This is the same as the Sendline call, but does not log the value being sent. - /// - /// The password to send - public void Sendpassword(string password) - { - RecordInLog("I", Backend.Strings.SharpExpectProcess.PasswordMarker); - if (m_process.HasExited) - throw new Exception(string.Format(Backend.Strings.SharpExpectProcess.WriteAfterExitError, LogKillAndDispose())); - m_process.StandardInput.WriteLine(password); - } - - /// - /// The thread method which reads data from the given StreamReader - /// - /// A StreamReader object - private void StreamReader(object input) - { - //Setup the threads local data and buffer - System.IO.StreamReader sr = null; - - try - { - sr = (System.IO.StreamReader)input; - char[] buf = new char[1024]; - bool lastRun = false; - List queue = sr == m_process.StandardError ? m_stdErr : m_stdOut; - - //Keep reading until the process exits - while (true) - { - int r = sr.Read(buf, 0, buf.Length); - if (r > 0) - { - lock (m_lock) - { - //Notify about the waiting data - queue.Add(new string(buf, 0, r)); - m_event.Set(); - } - } - - if (m_process.HasExited) - { - //After the process exists, try one last time - //This protects against races with the reader and the process - - if (!lastRun) - { - System.Threading.Thread.Sleep(100); - lastRun = true; - } - else - break; //Exit while loop - } - } - } - catch - { - } - finally - { - try - { - //Flag completion - if (sr == m_process.StandardError) - m_stderr_done = true; - else - m_stdout_done = true; - } - catch - { - //If this instance is disposed, m_process is null - } - } - } - - /// - /// Returns the wrapped process, do not use this to read or write data - /// - public System.Diagnostics.Process Process { get { return m_process; } } - - /// - /// Creates a process helper object from the given command - /// - /// The file to run - /// Any arguments to pass to the command - /// A process helper - public static SharpExpectProcess Spawn(string FileName, string Arguments) - { - System.Diagnostics.ProcessStartInfo pi = new System.Diagnostics.ProcessStartInfo(FileName, Arguments); - pi.RedirectStandardError = true; - pi.RedirectStandardOutput = true; - pi.RedirectStandardInput = true; - return Spawn(pi); - } - - /// - /// Creates a process helper object from the given command - /// - /// A startinfo object describing the process to start - /// A process helper - public static SharpExpectProcess Spawn(System.Diagnostics.ProcessStartInfo startInfo) - { - return new SharpExpectProcess(System.Diagnostics.Process.Start(startInfo)); - } - - /// - /// Destroys the helper object and returns logged input and output, usefull for returning a debug message - /// - /// A string with logmessages - public string LogKillAndDispose() - { - if (this.LogEnabled) - { - this.Dispose(); - - StringBuilder sb = new StringBuilder(); - foreach (string s in m_log) - sb.AppendLine(s); - - while (PeekNextOutputLine(1000) != null) - sb.AppendLine("*U*: " + GetNextOutputLine(0)); - - return sb.ToString(); - } - else - return Backend.Strings.SharpExpectProcess.LogDisabled; - } - - #region IDisposable Members - - /// - /// Kills the remote process - /// - public void Dispose() - { - if (m_process != null) - { - try { if (!m_process.HasExited) m_process.Kill(); } - catch { } - - try { m_process.Dispose(); } - catch { } - - m_process = null; - } - } - - #endregion - } -} diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHBackend.Designer.cs b/Duplicati/Library/Backend/SSH/Strings/SSHBackend.Designer.cs deleted file mode 100644 index d1938eca4..000000000 --- a/Duplicati/Library/Backend/SSH/Strings/SSHBackend.Designer.cs +++ /dev/null @@ -1,538 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.237 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Duplicati.Library.Backend.Strings { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class SSHBackend { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal SSHBackend() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Library.Backend.Strings.SSHBackend", typeof(SSHBackend).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Login failed due to bad credentials, log: {0}. - /// - internal static string AuthenticationError { - get { - return ResourceManager.GetString("AuthenticationError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Timeout while closing session. - /// - internal static string CloseTimeoutError { - get { - return ResourceManager.GetString("CloseTimeoutError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Timeout occured while connecting, log: {0}. - /// - internal static string ConnectionTimeoutError { - get { - return ResourceManager.GetString("ConnectionTimeoutError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to create folder: {0}. - /// - internal static string CreateFolderError { - get { - return ResourceManager.GetString("CreateFolderError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delete complete. - /// - internal static string DebugDeleteFooter { - get { - return ResourceManager.GetString("DebugDeleteFooter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delete start. - /// - internal static string DebugDeleteHeader { - get { - return ResourceManager.GetString("DebugDeleteHeader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Get complete. - /// - internal static string DebugGetFooter { - get { - return ResourceManager.GetString("DebugGetFooter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Get start. - /// - internal static string DebugGetHeader { - get { - return ResourceManager.GetString("DebugGetHeader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to List complete. - /// - internal static string DebugListFooter { - get { - return ResourceManager.GetString("DebugListFooter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to List start. - /// - internal static string DebugListHeader { - get { - return ResourceManager.GetString("DebugListHeader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to parse line: {0}. - /// - internal static string DebugParseFailed { - get { - return ResourceManager.GetString("DebugParseFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Put complete. - /// - internal static string DebugPutFooter { - get { - return ResourceManager.GetString("DebugPutFooter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Put start. - /// - internal static string DebugPutHeader { - get { - return ResourceManager.GetString("DebugPutHeader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to delete file: {0}. - /// - internal static string DeleteError { - get { - return ResourceManager.GetString("DeleteError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This backend can read and write data to an SSH based backend, using SFTP. Allowed formats are "ssh://hostname/folder" or "ssh://username:password@hostname/folder".. - /// - internal static string Description { - get { - return ResourceManager.GetString("Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The SSH backend relies on an external program (sftp) to work. Since the external program may change at any time, this may break the backend. Enable this option to get debug information about the ssh connection written to the console. Only used if the --use-sftp-application option is supplied.. - /// - internal static string DescriptionDebugToConsoleLong { - get { - return ResourceManager.GetString("DescriptionDebugToConsoleLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prints debug info to the console. - /// - internal static string DescriptionDebugToConsoleShort { - get { - return ResourceManager.GetString("DescriptionDebugToConsoleShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The password used to connect to the server. This may also be supplied as the environment variable "FTP_PASSWORD".. - /// - internal static string DescriptionFTPPasswordLong { - get { - return ResourceManager.GetString("DescriptionFTPPasswordLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Supplies the password used to connect to the server. - /// - internal static string DescriptionFTPPasswordShort { - get { - return ResourceManager.GetString("DescriptionFTPPasswordShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The username used to connect to the server. This may also be supplied as the environment variable "FTP_USERNAME".. - /// - internal static string DescriptionFTPUsernameLong { - get { - return ResourceManager.GetString("DescriptionFTPUsernameLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Supplies the username used to connect to the server. - /// - internal static string DescriptionFTPUsernameShort { - get { - return ResourceManager.GetString("DescriptionFTPUsernameShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The full path to the "sftp" application. Only used if the --use-sftp-application option is supplied.. - /// - internal static string DescriptionSFTPCommandLong { - get { - return ResourceManager.GetString("DescriptionSFTPCommandLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The path to the "sftp" program. - /// - internal static string DescriptionSFTPCommandShort { - get { - return ResourceManager.GetString("DescriptionSFTPCommandShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Points to a valid OpenSSH keyfile. If the file is encrypted, the password supplied is used to decrypt the keyfile. If this option is supplied, the password is not used to authenticate. This option only works when using the managed SSH client.. - /// - internal static string DescriptionSshkeyfileLong { - get { - return ResourceManager.GetString("DescriptionSshkeyfileLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uses a SSH private key to authenticate. - /// - internal static string DescriptionSshkeyfileShort { - get { - return ResourceManager.GetString("DescriptionSshkeyfileShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Disable the use of the cd command. This is used to support SSH backends where the cd/pwd is broken. If this option is enabled all paths will be transmitted with full paths.. - /// - internal static string DescriptionSshnocdLong { - get { - return ResourceManager.GetString("DescriptionSshnocdLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not use the cd command. - /// - internal static string DescriptionSshnocdShort { - get { - return ResourceManager.GetString("DescriptionSshnocdShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Supply any extra commandline arguments, which are passed unaltered to the ssh application. Only used if the --use-sftp-application option is supplied.. - /// - internal static string DescriptionSSHOptionsLong { - get { - return ResourceManager.GetString("DescriptionSSHOptionsLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Extra options to the ssh commands. - /// - internal static string DescriptionSSHOptionsShort { - get { - return ResourceManager.GetString("DescriptionSSHOptionsShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The SSH backend relies on an external program (sftp) to work. Since the external program may hang, Duplicati must use a timeout to detect a stall in the external program. Use this option to adjust the timeout. Minimum allowed value is one minute, maximum allowed is one hour. Only used if the --use-sftp-application option is supplied.. - /// - internal static string DescriptionTransferTimeoutLong { - get { - return ResourceManager.GetString("DescriptionTransferTimeoutLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sets the timeout for transfering a file. - /// - internal static string DescriptionTransferTimeoutShort { - get { - return ResourceManager.GetString("DescriptionTransferTimeoutShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Set this flag to use the sftp command from --sftp-command to communicate with the server. Setting this flag also requires that --disable-streaming-transfers is set. - /// - internal static string DescriptionUnmanagedLong { - get { - return ResourceManager.GetString("DescriptionUnmanagedLong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use an external sftp application. - /// - internal static string DescriptionUnmanagedShort { - get { - return ResourceManager.GetString("DescriptionUnmanagedShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SSH based. - /// - internal static string DisplayName { - get { - return ResourceManager.GetString("DisplayName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Timeout while downloading file. - /// - internal static string DownloadTimeoutError { - get { - return ResourceManager.GetString("DownloadTimeoutError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Folder not found: {0}, log: {1}. - /// - internal static string FolderNotFoundError { - get { - return ResourceManager.GetString("FolderNotFoundError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to set folder to {0}, error message: {1}. - /// - internal static string FolderNotFoundManagedError { - get { - return ResourceManager.GetString("FolderNotFoundManagedError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to validate the remote directory: {0}. - /// - internal static string FolderVerificationError { - get { - return ResourceManager.GetString("FolderVerificationError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The host is not authenticated, please connect to the host using SSH, and then re-rerun Duplicati, log: {0}. - /// - internal static string HostNotAuthenticatedError { - get { - return ResourceManager.GetString("HostNotAuthenticatedError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to File must have the OpenSSH private key header, eg: ---- BEGIN RSA PRIVATE KEY ----. - /// - internal static string IncorrectFileHeaderError { - get { - return ResourceManager.GetString("IncorrectFileHeaderError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The supplied keyfile was not found: {0}. - /// - internal static string KeyfileNotFoundError { - get { - return ResourceManager.GetString("KeyfileNotFoundError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to parse content of the file. Please include the following information in a bug report (you may omit any key data present): - ///{0}. - /// - internal static string KeyfileParseError { - get { - return ResourceManager.GetString("KeyfileParseError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to start the SSH application ({0}). - ///Make sure that "expect" is installed. - ///Error message: {1}. - /// - internal static string LaunchErrorLinux { - get { - return ResourceManager.GetString("LaunchErrorLinux", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to start the SSH application ({0}). - ///Make sure that "putty" is installed, and you have set the correct path. - ///Error message: {1}. - /// - internal static string LaunchErrorWindows { - get { - return ResourceManager.GetString("LaunchErrorWindows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A password was expected, but passwordless login was specified. - /// - internal static string PasswordMissingError { - get { - return ResourceManager.GetString("PasswordMissingError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The SSH backend does not support streaming when using the unmanaged SSH backend, if you supply --use-sftp-application, you must also specify --disable-streaming-transfers. - /// - internal static string StreamingNotSupportedError { - get { - return ResourceManager.GetString("StreamingNotSupportedError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unexpected error: {0}. - /// - internal static string UnexpectedConnectionError { - get { - return ResourceManager.GetString("UnexpectedConnectionError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Got unexpected exit response. - /// - internal static string UnexpectedExitResponseError { - get { - return ResourceManager.GetString("UnexpectedExitResponseError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to get expected response to command: {0}. - /// - internal static string UnexpectedResponseError { - get { - return ResourceManager.GetString("UnexpectedResponseError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unsupported encryption format for OpenSSH private key, must be 3des-cbc (DES-EDE3-CBC). - ///Please include this information in a bug report: - ///{0}. - /// - internal static string UnsupportedKeyfileEncryptionError { - get { - return ResourceManager.GetString("UnsupportedKeyfileEncryptionError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Timeout while uploading file. - /// - internal static string UploadTimeoutError { - get { - return ResourceManager.GetString("UploadTimeoutError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A username was expected, but none was supplied. - /// - internal static string UsernameMissingError { - get { - return ResourceManager.GetString("UsernameMissingError", resourceCulture); - } - } - } -} diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHBackend.resx b/Duplicati/Library/Backend/SSH/Strings/SSHBackend.resx deleted file mode 100644 index a81971aea..000000000 --- a/Duplicati/Library/Backend/SSH/Strings/SSHBackend.resx +++ /dev/null @@ -1,283 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Login failed due to bad credentials, log: {0} - - - Timeout while closing session - - - Timeout occured while connecting, log: {0} - - - Failed to create folder: {0} - - - Delete complete - - - Delete start - - - Get complete - - - Get start - - - List complete - - - List start - - - Failed to parse line: {0} - - - Put complete - - - Put start - - - Failed to delete file: {0} - - - This backend can read and write data to an SSH based backend, using SFTP. Allowed formats are "ssh://hostname/folder" or "ssh://username:password@hostname/folder". - - - The SSH backend relies on an external program (sftp) to work. Since the external program may change at any time, this may break the backend. Enable this option to get debug information about the ssh connection written to the console. Only used if the --use-sftp-application option is supplied. - - - Prints debug info to the console - - - The password used to connect to the server. This may also be supplied as the environment variable "FTP_PASSWORD". - - - Supplies the password used to connect to the server - - - The username used to connect to the server. This may also be supplied as the environment variable "FTP_USERNAME". - - - Supplies the username used to connect to the server - - - The full path to the "sftp" application. Only used if the --use-sftp-application option is supplied. - - - The path to the "sftp" program - - - Points to a valid OpenSSH keyfile. If the file is encrypted, the password supplied is used to decrypt the keyfile. If this option is supplied, the password is not used to authenticate. This option only works when using the managed SSH client. - - - Uses a SSH private key to authenticate - - - Supply any extra commandline arguments, which are passed unaltered to the ssh application. Only used if the --use-sftp-application option is supplied. - - - Extra options to the ssh commands - - - The SSH backend relies on an external program (sftp) to work. Since the external program may hang, Duplicati must use a timeout to detect a stall in the external program. Use this option to adjust the timeout. Minimum allowed value is one minute, maximum allowed is one hour. Only used if the --use-sftp-application option is supplied. - - - Sets the timeout for transfering a file - - - Set this flag to use the sftp command from --sftp-command to communicate with the server. Setting this flag also requires that --disable-streaming-transfers is set - - - Use an external sftp application - - - SSH based - - - Timeout while downloading file - - - Folder not found: {0}, log: {1} - - - Unable to set folder to {0}, error message: {1} - - - Failed to validate the remote directory: {0} - - - The host is not authenticated, please connect to the host using SSH, and then re-rerun Duplicati, log: {0} - - - File must have the OpenSSH private key header, eg: ---- BEGIN RSA PRIVATE KEY ---- - - - The supplied keyfile was not found: {0} - - - Unable to parse content of the file. Please include the following information in a bug report (you may omit any key data present): -{0} - - - Failed to start the SSH application ({0}). -Make sure that "expect" is installed. -Error message: {1} - - - Failed to start the SSH application ({0}). -Make sure that "putty" is installed, and you have set the correct path. -Error message: {1} - - - A password was expected, but passwordless login was specified - - - The SSH backend does not support streaming when using the unmanaged SSH backend, if you supply --use-sftp-application, you must also specify --disable-streaming-transfers - - - Unexpected error: {0} - - - Got unexpected exit response - - - Failed to get expected response to command: {0} - - - Unsupported encryption format for OpenSSH private key, must be 3des-cbc (DES-EDE3-CBC). -Please include this information in a bug report: -{0} - - - Timeout while uploading file - - - A username was expected, but none was supplied - - - Disable the use of the cd command. This is used to support SSH backends where the cd/pwd is broken. If this option is enabled all paths will be transmitted with full paths. - - - Do not use the cd command - - \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSH/app.config b/Duplicati/Library/Backend/SSH/app.config deleted file mode 100644 index 73859b00e..000000000 --- a/Duplicati/Library/Backend/SSH/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Duplicati/Library/Backend/SSHv2/Duplicati.Library.Backend.SSHv2.csproj b/Duplicati/Library/Backend/SSHv2/Duplicati.Library.Backend.SSHv2.csproj new file mode 100644 index 000000000..975f7143b --- /dev/null +++ b/Duplicati/Library/Backend/SSHv2/Duplicati.Library.Backend.SSHv2.csproj @@ -0,0 +1,130 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {FF2BF37C-E502-4C98-BEA0-701671DDFA08} + Library + Properties + Duplicati.Library.Backend + Duplicati.Library.Backend.SSHv2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + Duplicati.snk + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + false + false + 4 + false + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + 4 + + + + + + + + + + + + + ..\..\..\..\thirdparty\SSH.NET\Renci.SshNet.dll + + + + + UserControl + + + SSHv2UI.cs + + + + + True + True + SSHv2Backend.resx + + + True + True + SSHv2UI.resx + + + + + {7886BA79-A38A-4288-8B8A-9F29D0E15AC0} + Duplicati.Winforms.Controls + + + {C5899F45-B0FF-483C-9D38-24A9FCAAB237} + Duplicati.Library.Interface + + + {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65} + Duplicati.Library.Utility + + + + + + + + SSHv2UI.cs + + + ResXFileCodeGenerator + SSHv2Backend.Designer.cs + + + ResXFileCodeGenerator + SSHv2UI.Designer.cs + + + + + \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSHv2/Duplicati.snk b/Duplicati/Library/Backend/SSHv2/Duplicati.snk new file mode 100644 index 000000000..e0c1e2dd8 Binary files /dev/null and b/Duplicati/Library/Backend/SSHv2/Duplicati.snk differ diff --git a/Duplicati/Library/Backend/SSHv2/Properties/AssemblyInfo.cs b/Duplicati/Library/Backend/SSHv2/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..2f3f7469d --- /dev/null +++ b/Duplicati/Library/Backend/SSHv2/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("SSHv2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SSHv2")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("2664a6d7-4a72-481c-b753-f2bb63c2a0e8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs new file mode 100644 index 000000000..f617e353c --- /dev/null +++ b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs @@ -0,0 +1,282 @@ +#region Disclaimer / License +// Copyright (C) 2010, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Duplicati.Library.Interface; +using Renci.SshNet; + +namespace Duplicati.Library.Backend +{ + public class SSHv2 : IBackend_v2, IStreamingBackend, IBackendGUI + { + public const string SSH_KEYFILE_OPTION = "ssh-keyfile"; + + Dictionary m_options; + + private string m_server; + private string m_path; + private string m_username; + private string m_password; + private string m_ssh_options; + + private int m_port = 22; + + private SftpClient m_con; + + public SSHv2() + { + } + + public SSHv2(string url, Dictionary options) + : this() + { + m_options = options; + Uri u = new Uri(url); + if (!string.IsNullOrEmpty(u.UserInfo)) + { + if (u.UserInfo.IndexOf(":") >= 0) + { + m_username = u.UserInfo.Substring(0, u.UserInfo.IndexOf(":")); + m_password = u.UserInfo.Substring(u.UserInfo.IndexOf(":") + 1); + } + else + { + m_username = u.UserInfo; + if (options.ContainsKey("ftp-password")) + m_password = options["ftp-password"]; + } + } + else + { + if (options.ContainsKey("ftp-username")) + m_username = options["ftp-username"]; + if (options.ContainsKey("ftp-password")) + m_password = options["ftp-password"]; + } + + m_path = u.AbsolutePath; + + //Remove 1 leading slash so server/path is mapped to "path", + // and server//path is mapped to "/path" + m_path = m_path.Substring(1); + + if (!m_path.EndsWith("/")) + m_path += "/"; + + m_server = u.Host; + + if (!u.IsDefaultPort) + { + m_ssh_options += " -P " + u.Port; + m_port = u.Port; + } + } + + #region IBackend_v2 Implementation + + public void Test() + { + List(); + } + + public void CreateFolder() + { + using (SftpClient con = CreateConnection(false)) + { + //Bugfix, some SSH servers do not like a trailing slash + string p = m_path; + if (p.EndsWith("/")) + p.Substring(0, p.Length - 1); + con.CreateDirectory(p); + } + } + + public string DisplayName + { + get { return Strings.SSHv2Backend.DisplayName; } + } + + public string ProtocolKey + { + get { return "ssh"; } + } + + public void Put(string remotename, string filename) + { + using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)) + Put(remotename, fs); + } + + public void Get(string remotename, string filename) + { + using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) + Get(remotename, fs); + } + + public void Delete(string remotename) + { + CreateConnection(true).DeleteFile(remotename); + } + + public IList SupportedCommands + { + get + { + return new List(new ICommandLineArgument[] { + new CommandLineArgument("ftp-password", CommandLineArgument.ArgumentType.String, Strings.SSHv2Backend.DescriptionFTPPasswordShort, Strings.SSHv2Backend.DescriptionFTPPasswordLong), + new CommandLineArgument("ftp-username", CommandLineArgument.ArgumentType.String, Strings.SSHv2Backend.DescriptionFTPUsernameShort, Strings.SSHv2Backend.DescriptionFTPUsernameLong), + new CommandLineArgument(SSH_KEYFILE_OPTION, CommandLineArgument.ArgumentType.Path, Strings.SSHv2Backend.DescriptionSshkeyfileShort, Strings.SSHv2Backend.DescriptionSshkeyfileLong), + }); + + } + } + + public string Description + { + get { return Strings.SSHv2Backend.Description; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (m_con != null) + { + m_con.Dispose(); + m_con = null; + } + } + + #endregion + + #region IStreamingBackend Implementation + + public void Put(string remotename, System.IO.Stream stream) + { + CreateConnection(true).UploadFile(stream, remotename); + } + + public void Get(string remotename, System.IO.Stream stream) + { + CreateConnection(true).DownloadFile(remotename, stream); + } + + #endregion + + #region IBackendGUI Implementation + + public string PageTitle + { + get { return SSHv2UI.PageTitle; } + } + + public string PageDescription + { + get { return SSHv2UI.PageDescription; } + } + + public System.Windows.Forms.Control GetControl(IDictionary applicationSettings, IDictionary options) + { + return new SSHv2UI(applicationSettings, options); + } + + public void Leave(System.Windows.Forms.Control control) + { + ((SSHv2UI)control).Save(false); + } + + public bool Validate(System.Windows.Forms.Control control) + { + return ((SSHv2UI)control).Save(true); + } + + public string GetConfiguration(IDictionary applicationSettings, IDictionary guiOptions, IDictionary commandlineOptions) + { + return SSHv2UI.GetConfiguration(applicationSettings, guiOptions, commandlineOptions); + } + + #endregion + + #region Implementation + + private SftpClient CreateConnection(bool changeDir) + { + if (changeDir && m_con != null) + return m_con; + + SftpClient con; + + string keyfile; + m_options.TryGetValue(SSH_KEYFILE_OPTION, out keyfile); + + if ((keyfile ?? "").Trim().Length > 0) + con = new SftpClient(m_server, m_port, m_username, ValidateKeyFile(m_options[SSH_KEYFILE_OPTION], m_password)); + else + con = new SftpClient(m_server, m_username, m_password); + + con.Connect(); + + try + { + if (!string.IsNullOrEmpty(m_path) && changeDir) + con.ChangeDirectory(m_path); + } + catch (Exception ex) + { + throw new Interface.FolderMissingException(string.Format(Strings.SSHv2Backend.FolderNotFoundManagedError, m_path, ex.Message), ex); + } + + if (changeDir) + m_con = con; + + return con; + } + + public List List() + { + List files = new List(); + + string path = "."; + + foreach (Renci.SshNet.Sftp.SftpFile ls in CreateConnection(true).ListDirectory(path)) + if (ls.Name.ToString() != "." && ls.Name.ToString() != "..") + files.Add(new FileEntry(ls.Name.ToString(), ls.Length, ls.LastAccessTime, ls.LastWriteTime)); + + return files; + } + + public static Renci.SshNet.PrivateKeyFile ValidateKeyFile(string filename, string password) + { + if (String.IsNullOrEmpty(password)) + return new Renci.SshNet.PrivateKeyFile(filename); + else + return new Renci.SshNet.PrivateKeyFile(filename, password); + } + + #endregion + } +} + + + \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSH/SSHUI.Designer.cs b/Duplicati/Library/Backend/SSHv2/SSHv2UI.Designer.cs similarity index 91% rename from Duplicati/Library/Backend/SSH/SSHUI.Designer.cs rename to Duplicati/Library/Backend/SSHv2/SSHv2UI.Designer.cs index 08bbd3b9b..d069482bd 100644 --- a/Duplicati/Library/Backend/SSH/SSHUI.Designer.cs +++ b/Duplicati/Library/Backend/SSHv2/SSHv2UI.Designer.cs @@ -1,6 +1,6 @@ namespace Duplicati.Library.Backend { - partial class SSHUI + partial class SSHv2UI { /// /// Required designer variable. @@ -28,7 +28,7 @@ namespace Duplicati.Library.Backend /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SSHUI)); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SSHv2UI)); this.TestConnection = new System.Windows.Forms.Button(); this.Port = new System.Windows.Forms.NumericUpDown(); this.Password = new Duplicati.Winforms.Controls.PasswordControl(); @@ -41,7 +41,6 @@ namespace Duplicati.Library.Backend this.label1 = new System.Windows.Forms.Label(); this.UsePassword = new System.Windows.Forms.CheckBox(); this.GenerateDebugOutput = new System.Windows.Forms.CheckBox(); - this.UseUnmanagedSSH = new System.Windows.Forms.CheckBox(); this.CreateFolderButton = new System.Windows.Forms.Button(); this.Keyfile = new System.Windows.Forms.TextBox(); this.Keyfilelabel = new System.Windows.Forms.Label(); @@ -81,6 +80,7 @@ namespace Duplicati.Library.Backend // Password // this.Password.AskToEnterNewPassword = false; + this.Password.InitialPassword = null; this.Password.IsPasswordVisible = false; resources.ApplyResources(this.Password, "Password"); this.Password.MaximumSize = new System.Drawing.Size(5000, 20); @@ -141,13 +141,6 @@ namespace Duplicati.Library.Backend this.GenerateDebugOutput.Name = "GenerateDebugOutput"; this.GenerateDebugOutput.UseVisualStyleBackColor = true; // - // UseUnmanagedSSH - // - resources.ApplyResources(this.UseUnmanagedSSH, "UseUnmanagedSSH"); - this.UseUnmanagedSSH.Name = "UseUnmanagedSSH"; - this.UseUnmanagedSSH.UseVisualStyleBackColor = true; - this.UseUnmanagedSSH.CheckedChanged += new System.EventHandler(this.UseUnmanagedSSH_CheckedChanged); - // // CreateFolderButton // resources.ApplyResources(this.CreateFolderButton, "CreateFolderButton"); @@ -177,7 +170,7 @@ namespace Duplicati.Library.Backend // resources.ApplyResources(this.OpenSSHKeyFileDialog, "OpenSSHKeyFileDialog"); // - // SSHUI + // SSHv2UI // resources.ApplyResources(this, "$this"); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -185,7 +178,6 @@ namespace Duplicati.Library.Backend this.Controls.Add(this.Keyfile); this.Controls.Add(this.Keyfilelabel); this.Controls.Add(this.CreateFolderButton); - this.Controls.Add(this.UseUnmanagedSSH); this.Controls.Add(this.GenerateDebugOutput); this.Controls.Add(this.UsePassword); this.Controls.Add(this.TestConnection); @@ -198,7 +190,7 @@ namespace Duplicati.Library.Backend this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.label1); - this.Name = "SSHUI"; + this.Name = "SSHv2UI"; this.Load += new System.EventHandler(this.SSHUI_PageLoad); ((System.ComponentModel.ISupportInitialize)(this.Port)).EndInit(); this.ResumeLayout(false); @@ -220,7 +212,6 @@ namespace Duplicati.Library.Backend private System.Windows.Forms.Label label1; private System.Windows.Forms.CheckBox UsePassword; private System.Windows.Forms.CheckBox GenerateDebugOutput; - private System.Windows.Forms.CheckBox UseUnmanagedSSH; private System.Windows.Forms.Button CreateFolderButton; private System.Windows.Forms.TextBox Keyfile; private System.Windows.Forms.Label Keyfilelabel; diff --git a/Duplicati/Library/Backend/SSH/SSHUI.cs b/Duplicati/Library/Backend/SSHv2/SSHv2UI.cs similarity index 67% rename from Duplicati/Library/Backend/SSH/SSHUI.cs rename to Duplicati/Library/Backend/SSHv2/SSHv2UI.cs index 5cdbec28e..d6614837f 100644 --- a/Duplicati/Library/Backend/SSH/SSHUI.cs +++ b/Duplicati/Library/Backend/SSHv2/SSHv2UI.cs @@ -1,5 +1,5 @@ #region Disclaimer / License -// Copyright (C) 2011, Kenneth Skovhede +// Copyright (C) 2010, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or @@ -27,7 +27,7 @@ using System.Windows.Forms; namespace Duplicati.Library.Backend { - public partial class SSHUI : UserControl + public partial class SSHv2UI : UserControl { private const string USERNAME = "Username"; private const string PASSWORD = "Password"; @@ -36,32 +36,26 @@ namespace Duplicati.Library.Backend private const string PASWORDLESS = "Passwordless"; private const string PORT = "Port"; private const string DEBUG_ENABLED = "Debug enabled"; - private const string USE_UNMANAGED_SSH = "Use Unmanaged SSH"; private const string SSH_KEYFILE = "Keyfile"; private const string HAS_WARNED_PATH = "UI: Has warned path"; private const string HAS_TESTED = "UI: Has tested"; - private const string HAS_WARNED_NO_SFTP = "UI: Has warned SFTP"; private const string INITIALPASSWORD = "UI: Temp password"; private bool m_warnedPath = false; private bool m_hasTested = false; - private bool m_warnedNoSFTP = false; - - private const string DUPLICATI_ACTION_MARKER = "*duplicati-action*"; - private string m_uiAction = null; private IDictionary m_options; private IDictionary m_applicationSettings; - public SSHUI(IDictionary applicationSettings, IDictionary options) + public SSHv2UI(IDictionary applicationSettings, IDictionary options) : this() { m_applicationSettings = applicationSettings; m_options = options; } - private SSHUI() + private SSHv2UI() { InitializeComponent(); } @@ -105,7 +99,6 @@ namespace Duplicati.Library.Backend m_options.Clear(); m_options[HAS_TESTED] = m_hasTested.ToString(); m_options[HAS_WARNED_PATH] = m_warnedPath.ToString(); - m_options[HAS_WARNED_NO_SFTP] = m_warnedNoSFTP.ToString(); m_options[PASWORDLESS]= (!UsePassword.Checked).ToString(); m_options[PASSWORD] = UsePassword.Checked ? Password.Text : ""; @@ -114,19 +107,15 @@ namespace Duplicati.Library.Backend m_options[FOLDER] = Path.Text; m_options[USERNAME] = Username.Text; m_options[DEBUG_ENABLED] = GenerateDebugOutput.Checked.ToString(); - m_options[USE_UNMANAGED_SSH] = UseUnmanagedSSH.Checked.ToString(); m_options[SSH_KEYFILE] = Keyfile.Text; if (hasInitial) m_options[INITIALPASSWORD] = initialPwd; - if (!string.IsNullOrEmpty(m_uiAction)) - m_options.Add(DUPLICATI_ACTION_MARKER, m_uiAction); } void SSHUI_PageLoad(object sender, EventArgs args) { bool passwordless; bool debug; - bool useUnmanaged; int port; if (!m_options.ContainsKey(PASWORDLESS) || !bool.TryParse(m_options[PASWORDLESS], out passwordless)) @@ -136,13 +125,6 @@ namespace Duplicati.Library.Backend if (!m_options.ContainsKey(DEBUG_ENABLED) || !bool.TryParse(m_options[DEBUG_ENABLED], out debug)) debug = false; - if (!m_options.ContainsKey(USE_UNMANAGED_SSH) || !bool.TryParse(m_options[USE_UNMANAGED_SSH], out useUnmanaged)) - { - useUnmanaged = false; - if (m_applicationSettings.ContainsKey(SSHCommonOptions.DEFAULT_MANAGED)) - useUnmanaged = !Utility.Utility.ParseBool(m_applicationSettings[SSHCommonOptions.DEFAULT_MANAGED], true); - } - if (m_options.ContainsKey(HOST)) Servername.Text = m_options[HOST]; if (m_options.ContainsKey(FOLDER)) @@ -162,25 +144,11 @@ namespace Duplicati.Library.Backend Port.Value = port; GenerateDebugOutput.Checked = debug; - UseUnmanagedSSH.Checked = useUnmanaged; if (!m_options.ContainsKey(HAS_TESTED) || !bool.TryParse(m_options[HAS_TESTED], out m_hasTested)) m_hasTested = false; if (!m_options.ContainsKey(HAS_WARNED_PATH) || !bool.TryParse(m_options[HAS_WARNED_PATH], out m_warnedPath)) m_warnedPath = false; - - if (Library.Utility.Utility.IsClientLinux) - { - //sftp is likely present on linux/mac - m_warnedNoSFTP = true; - } - else - { - if (!m_options.ContainsKey(HAS_WARNED_NO_SFTP) || !bool.TryParse(m_options[HAS_WARNED_NO_SFTP], out m_warnedNoSFTP)) - m_warnedNoSFTP = false; - } - - m_options.TryGetValue(DUPLICATI_ACTION_MARKER, out m_uiAction); } private void TestConnection_Click(object sender, EventArgs e) @@ -198,32 +166,22 @@ namespace Duplicati.Library.Backend options["debug-to-console"] = ""; - bool existingBackup = false; - using (SSH ssh = new SSH(destination, options)) - { - foreach (Interface.IFileEntry n in ssh.List()) - if (n.Name.StartsWith("duplicati-")) - { - existingBackup = true; - break; - } - } + SSHv2 ssh = new SSHv2(destination, options); + ssh.List(); - bool isUiAdd = string.IsNullOrEmpty(m_uiAction) || string.Equals(m_uiAction, "add", StringComparison.InvariantCultureIgnoreCase); - if (existingBackup && isUiAdd) - { - if (MessageBox.Show(this, string.Format(Interface.CommonStrings.ExistingBackupDetectedQuestion), Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3) != DialogResult.Yes) - return; - } - else - { - MessageBox.Show(this, Interface.CommonStrings.ConnectionSuccess, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); - } + MessageBox.Show(this, Interface.CommonStrings.ConnectionSuccess, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); m_hasTested = true; } + /* + catch (Renci.SshClient.Common.SshPasswordNeededForEncryptedKeyfile ex) + { + // TODO: If we get this exception, we should promt the user for the password! + throw ex; + } + */ catch (Interface.FolderMissingException) { - switch (MessageBox.Show(this, Strings.SSHUI.CreateMissingFolderQuestion, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)) + switch (MessageBox.Show(this, Strings.SSHv2UI.CreateMissingFolderQuestion, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)) { case DialogResult.Yes: CreateFolderButton.PerformClick(); @@ -255,15 +213,6 @@ namespace Duplicati.Library.Backend return false; } - if (!Library.Utility.Utility.IsValidHostname(Servername.Text)) - { - MessageBox.Show(this, string.Format(Library.Interface.CommonStrings.InvalidServernameError, Servername.Text), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); - try { Servername.Focus(); } - catch { } - - return false; - } - if (Username.Text.Trim().Length <= 0) { MessageBox.Show(this, Interface.CommonStrings.EmptyUsernameError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); @@ -275,7 +224,7 @@ namespace Duplicati.Library.Backend if (Password.Text.Trim().Length <= 0 && UsePassword.Checked) { - if (!UseUnmanagedSSH.Checked && Keyfile.Text.Trim().Length == 0) + if (Keyfile.Text.Trim().Length == 0) { MessageBox.Show(this, Interface.CommonStrings.EmptyPasswordError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); try { Password.Focus(); } @@ -285,9 +234,9 @@ namespace Duplicati.Library.Backend } } - if (!UsePassword.Checked && !UseUnmanagedSSH.Checked && Keyfile.Text.Trim().Length == 0) + if (!UsePassword.Checked && Keyfile.Text.Trim().Length == 0) { - MessageBox.Show(this, Strings.SSHUI.PasswordRequiredManagedError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); + MessageBox.Show(this, Strings.SSHv2UI.PasswordRequiredManagedError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); UsePassword.Checked = true; try { Password.Focus(); } catch { } @@ -304,32 +253,11 @@ namespace Duplicati.Library.Backend m_warnedPath = true; } - if (!m_warnedNoSFTP && UseUnmanagedSSH.Checked) - { - string sftpPath = ""; - if (m_applicationSettings.ContainsKey(SSHCommonOptions.SFTP_PATH)) - sftpPath = m_applicationSettings[SSHCommonOptions.SFTP_PATH]; - - System.IO.FileInfo fi = null; - try { fi = new System.IO.FileInfo(System.Environment.ExpandEnvironmentVariables(sftpPath)); } - catch { } - - if (fi == null || !fi.Exists) - { - if (MessageBox.Show(this, Strings.SSHUI.MissingSCPWarning, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes) - { - return false; - } - - m_warnedNoSFTP = true; - } - } - - if (!UseUnmanagedSSH.Checked && !string.IsNullOrEmpty(Keyfile.Text)) + if (!string.IsNullOrEmpty(Keyfile.Text)) { try { - SSH.ValidateKeyFile(Keyfile.Text); + SSHv2.ValidateKeyFile(Keyfile.Text, UsePassword.Checked ? Password.Text : ""); } catch (Exception ex) { @@ -342,6 +270,9 @@ namespace Duplicati.Library.Backend } } + if (UsePassword.Checked && !Password.VerifyPasswordIfChanged()) + return false; + return true; } @@ -379,12 +310,12 @@ namespace Duplicati.Library.Backend public static string PageTitle { - get { return Strings.SSHUI.PageTitle; } + get { return Strings.SSHv2UI.PageTitle; } } public static string PageDescription { - get { return Strings.SSHUI.PageDescription; } + get { return Strings.SSHv2UI.PageDescription; } } public static string GetConfiguration(IDictionary applicationSettings, IDictionary guiOptions, IDictionary commandlineOptions) @@ -406,9 +337,6 @@ namespace Duplicati.Library.Backend if (!guiOptions.ContainsKey(PORT) || !int.TryParse(guiOptions[PORT], out port)) port = 22; - if (applicationSettings.ContainsKey(SSHCommonOptions.SFTP_PATH)) - commandlineOptions[SSH.SFTP_PATH_OPTION] = applicationSettings[SSHCommonOptions.SFTP_PATH]; - bool debug; if (!guiOptions.ContainsKey(DEBUG_ENABLED) || !bool.TryParse(guiOptions[DEBUG_ENABLED], out debug)) debug = false; @@ -416,21 +344,11 @@ namespace Duplicati.Library.Backend if (debug) commandlineOptions["debug-to-console"] = ""; - bool useUnmanaged = guiOptions.ContainsKey(USE_UNMANAGED_SSH) ? Utility.Utility.ParseBool(guiOptions[USE_UNMANAGED_SSH], false) : false; + string keyfile; + guiOptions.TryGetValue(SSH_KEYFILE, out keyfile); - if (useUnmanaged) - { - commandlineOptions[SSH.USE_UNMANAGED_OPTION] = ""; - commandlineOptions["disable-streaming-transfers"] = ""; - } - else - { - string keyfile; - guiOptions.TryGetValue(SSH_KEYFILE, out keyfile); - - if ((keyfile ?? "").Trim().Length > 0) - commandlineOptions[SSH.SSH_KEYFILE_OPTION] = guiOptions[SSH_KEYFILE]; - } + if ((keyfile ?? "").Trim().Length > 0) + commandlineOptions[SSHv2.SSH_KEYFILE_OPTION] = guiOptions[SSH_KEYFILE]; if (!guiOptions.ContainsKey(HOST)) throw new Exception(string.Format(Interface.CommonStrings.ConfigurationIsMissingItemError, HOST)); @@ -438,14 +356,6 @@ namespace Duplicati.Library.Backend return "ssh://" + guiOptions[HOST] + ":" + port.ToString() + "/" + (guiOptions.ContainsKey(FOLDER) ? guiOptions[FOLDER] : ""); } - private void UseUnmanagedSSH_CheckedChanged(object sender, EventArgs e) - { - Keyfilelabel.Enabled = - Keyfile.Enabled = - BrowseForKeyFileButton.Enabled = - !UseUnmanagedSSH.Checked; - } - private void CreateFolderButton_Click(object sender, EventArgs e) { if (ValidateForm()) @@ -461,7 +371,7 @@ namespace Duplicati.Library.Backend options["debug-to-console"] = ""; - SSH ssh = new SSH(destination, options); + SSHv2 ssh = new SSHv2(destination, options); ssh.CreateFolder(); MessageBox.Show(this, Interface.CommonStrings.FolderCreated, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); @@ -484,7 +394,7 @@ namespace Duplicati.Library.Backend { try { - SSH.ValidateKeyFile(OpenSSHKeyFileDialog.FileName); + SSHv2.ValidateKeyFile(OpenSSHKeyFileDialog.FileName, UsePassword.Checked ? Password.Text : ""); } catch (Exception ex) { diff --git a/Duplicati/Library/Backend/SSH/SSHUI.resx b/Duplicati/Library/Backend/SSHv2/SSHv2UI.resx similarity index 85% rename from Duplicati/Library/Backend/SSH/SSHUI.resx rename to Duplicati/Library/Backend/SSHv2/SSHv2UI.resx index 98c1f30ec..974dc5724 100644 --- a/Duplicati/Library/Backend/SSH/SSHUI.resx +++ b/Duplicati/Library/Backend/SSHv2/SSHv2UI.resx @@ -112,19 +112,19 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 368, 168 112, 24 - + 21 @@ -135,13 +135,13 @@ TestConnection - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 7 + 6 120, 144 @@ -156,13 +156,13 @@ Port - System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 8 + 7 120, 88 @@ -183,7 +183,7 @@ $this - 9 + 8 120, 64 @@ -198,13 +198,13 @@ Username - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 10 + 9 120, 40 @@ -219,13 +219,13 @@ Path - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 11 + 10 120, 16 @@ -240,13 +240,13 @@ Servername - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 12 + 11 True @@ -267,13 +267,13 @@ label5 - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 13 + 12 True @@ -294,13 +294,13 @@ label3 - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 14 + 13 True @@ -321,13 +321,13 @@ label2 - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 15 + 14 True @@ -348,13 +348,13 @@ label1 - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 16 + 15 True @@ -375,19 +375,19 @@ UsePassword - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 6 + 5 True - 20, 208 + 20, 173 136, 17 @@ -402,45 +402,15 @@ GenerateDebugOutput - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this - 5 - - - True - - - - NoControl - - - 20, 176 - - - 302, 17 - - - 24 - - - Use installed sftp program instead of the built-in SSH library - - - UseUnmanagedSSH - - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - 4 + NoControl @@ -460,7 +430,7 @@ CreateFolderButton - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this @@ -481,7 +451,7 @@ Keyfile - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this @@ -511,7 +481,7 @@ Keyfilelabel - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this @@ -535,7 +505,7 @@ BrowseForKeyFileButton - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this @@ -543,7 +513,7 @@ 0 - + 17, 17 @@ -552,25 +522,25 @@ Select the OpenSSH keyfile - + True 6, 13 - 506, 242 + 506, 206 OpenSSHKeyFileDialog - System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - SSHUI + SSHv2UI - System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.UserControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSHv2/Strings/SSHv2Backend.Designer.cs b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2Backend.Designer.cs new file mode 100644 index 000000000..5c3488d63 --- /dev/null +++ b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2Backend.Designer.cs @@ -0,0 +1,198 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Duplicati.Library.Backend.Strings { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class SSHv2Backend { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SSHv2Backend() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Library.Backend.Strings.SSHv2Backend", typeof(SSHv2Backend).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to This backend can read and write data to an SSH based backend, using SFTP. Allowed formats are "ssh://hostname/folder" or "ssh://username:password@hostname/folder".. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The SSH backend relies on an external program (sftp) to work. Since the external program may change at any time, this may break the backend. Enable this option to get debug information about the ssh connection written to the console. Only used if the --use-sftp-application option is supplied.. + /// + internal static string DescriptionDebugToConsoleLong { + get { + return ResourceManager.GetString("DescriptionDebugToConsoleLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prints debug info to the console. + /// + internal static string DescriptionDebugToConsoleShort { + get { + return ResourceManager.GetString("DescriptionDebugToConsoleShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The password used to connect to the server. This may also be supplied as the environment variable "FTP_PASSWORD".. + /// + internal static string DescriptionFTPPasswordLong { + get { + return ResourceManager.GetString("DescriptionFTPPasswordLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supplies the password used to connect to the server. + /// + internal static string DescriptionFTPPasswordShort { + get { + return ResourceManager.GetString("DescriptionFTPPasswordShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The username used to connect to the server. This may also be supplied as the environment variable "FTP_USERNAME".. + /// + internal static string DescriptionFTPUsernameLong { + get { + return ResourceManager.GetString("DescriptionFTPUsernameLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supplies the username used to connect to the server. + /// + internal static string DescriptionFTPUsernameShort { + get { + return ResourceManager.GetString("DescriptionFTPUsernameShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Points to a valid OpenSSH keyfile. If the file is encrypted, the password supplied is used to decrypt the keyfile. If this option is supplied, the password is not used to authenticate. This option only works when using the managed SSH client.. + /// + internal static string DescriptionSshkeyfileLong { + get { + return ResourceManager.GetString("DescriptionSshkeyfileLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uses a SSH private key to authenticate. + /// + internal static string DescriptionSshkeyfileShort { + get { + return ResourceManager.GetString("DescriptionSshkeyfileShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supply any extra commandline arguments, which are passed unaltered to the ssh application. Only used if the --use-sftp-application option is supplied.. + /// + internal static string DescriptionSSHOptionsLong { + get { + return ResourceManager.GetString("DescriptionSSHOptionsLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extra options to the ssh commands. + /// + internal static string DescriptionSSHOptionsShort { + get { + return ResourceManager.GetString("DescriptionSSHOptionsShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The SSH backend relies on an external program (sftp) to work. Since the external program may hang, Duplicati must use a timeout to detect a stall in the external program. Use this option to adjust the timeout. Minimum allowed value is one minute, maximum allowed is one hour. Only used if the --use-sftp-application option is supplied.. + /// + internal static string DescriptionTransferTimeoutLong { + get { + return ResourceManager.GetString("DescriptionTransferTimeoutLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets the timeout for transfering a file. + /// + internal static string DescriptionTransferTimeoutShort { + get { + return ResourceManager.GetString("DescriptionTransferTimeoutShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SSH based on SSH.NET. + /// + internal static string DisplayName { + get { + return ResourceManager.GetString("DisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to set folder to {0}, error message: {1}. + /// + internal static string FolderNotFoundManagedError { + get { + return ResourceManager.GetString("FolderNotFoundManagedError", resourceCulture); + } + } + } +} diff --git a/Duplicati/Library/Backend/SSHv2/Strings/SSHv2Backend.resx b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2Backend.resx new file mode 100644 index 000000000..a0047435c --- /dev/null +++ b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2Backend.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + This backend can read and write data to an SSH based backend, using SFTP. Allowed formats are "ssh://hostname/folder" or "ssh://username:password@hostname/folder". + + + The SSH backend relies on an external program (sftp) to work. Since the external program may change at any time, this may break the backend. Enable this option to get debug information about the ssh connection written to the console. Only used if the --use-sftp-application option is supplied. + + + Prints debug info to the console + + + The password used to connect to the server. This may also be supplied as the environment variable "FTP_PASSWORD". + + + Supplies the password used to connect to the server + + + The username used to connect to the server. This may also be supplied as the environment variable "FTP_USERNAME". + + + Supplies the username used to connect to the server + + + Points to a valid OpenSSH keyfile. If the file is encrypted, the password supplied is used to decrypt the keyfile. If this option is supplied, the password is not used to authenticate. This option only works when using the managed SSH client. + + + Uses a SSH private key to authenticate + + + Supply any extra commandline arguments, which are passed unaltered to the ssh application. Only used if the --use-sftp-application option is supplied. + + + Extra options to the ssh commands + + + The SSH backend relies on an external program (sftp) to work. Since the external program may hang, Duplicati must use a timeout to detect a stall in the external program. Use this option to adjust the timeout. Minimum allowed value is one minute, maximum allowed is one hour. Only used if the --use-sftp-application option is supplied. + + + Sets the timeout for transfering a file + + + SSH based on SSH.NET + + + Unable to set folder to {0}, error message: {1} + + \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHUI.Designer.cs b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2UI.Designer.cs similarity index 94% rename from Duplicati/Library/Backend/SSH/Strings/SSHUI.Designer.cs rename to Duplicati/Library/Backend/SSHv2/Strings/SSHv2UI.Designer.cs index fbb527d62..efc67f208 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SSHUI.Designer.cs +++ b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2UI.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -22,14 +22,14 @@ namespace Duplicati.Library.Backend.Strings { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class SSHUI { + internal class SSHv2UI { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal SSHUI() { + internal SSHv2UI() { } /// @@ -39,7 +39,7 @@ namespace Duplicati.Library.Backend.Strings { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Library.Backend.Strings.SSHUI", typeof(SSHUI).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Library.Backend.Strings.SSHv2UI", typeof(SSHv2UI).Assembly); resourceMan = temp; } return resourceMan; diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHUI.resx b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2UI.resx similarity index 96% rename from Duplicati/Library/Backend/SSH/Strings/SSHUI.resx rename to Duplicati/Library/Backend/SSHv2/Strings/SSHv2UI.resx index b76243d99..f0b7f7ba4 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SSHUI.resx +++ b/Duplicati/Library/Backend/SSHv2/Strings/SSHv2UI.resx @@ -112,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 The folder does not exist, do you want to create it? diff --git a/Duplicati/Library/Backend/SkyDrive/Duplicati.Library.Backend.SkyDrive.csproj b/Duplicati/Library/Backend/SkyDrive/Duplicati.Library.Backend.SkyDrive.csproj index 719ea1e27..920031398 100644 --- a/Duplicati/Library/Backend/SkyDrive/Duplicati.Library.Backend.SkyDrive.csproj +++ b/Duplicati/Library/Backend/SkyDrive/Duplicati.Library.Backend.SkyDrive.csproj @@ -1,4 +1,4 @@ - + Debug @@ -11,8 +11,7 @@ Duplicati.Library.Backend Duplicati.Library.Backend.SkyDrive true - Duplicati.snk - v2.0 + Duplicati.snk 512 @@ -104,4 +103,4 @@ --> - \ No newline at end of file + diff --git a/Duplicati/Library/Backend/TahoeLAFS/Duplicati.Library.Backend.TahoeLAFS.csproj b/Duplicati/Library/Backend/TahoeLAFS/Duplicati.Library.Backend.TahoeLAFS.csproj index 1e139588b..09380c154 100644 --- a/Duplicati/Library/Backend/TahoeLAFS/Duplicati.Library.Backend.TahoeLAFS.csproj +++ b/Duplicati/Library/Backend/TahoeLAFS/Duplicati.Library.Backend.TahoeLAFS.csproj @@ -1,4 +1,4 @@ - + Debug @@ -10,11 +10,7 @@ Properties Duplicati.Library.Backend Duplicati.Library.Backend.TahoeLAFS - v2.0 512 - - - false Duplicati.snk @@ -29,6 +25,7 @@ DEBUG;TRACE prompt 4 + true pdbonly @@ -37,6 +34,7 @@ TRACE prompt 4 + true true @@ -45,6 +43,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -53,18 +53,18 @@ pdbonly x86 prompt + 4 - - False - ..\..\..\..\thirdparty\LitJSON\LitJson.dll - + + ..\..\..\..\thirdparty\LitJSON\LitJson.dll + diff --git a/Duplicati/Library/Backend/TahoeLAFS/Properties/Resources.Designer.cs b/Duplicati/Library/Backend/TahoeLAFS/Properties/Resources.Designer.cs index 61f8519b1..549506e94 100644 --- a/Duplicati/Library/Backend/TahoeLAFS/Properties/Resources.Designer.cs +++ b/Duplicati/Library/Backend/TahoeLAFS/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Backend/TahoeLAFS/Properties/Settings.Designer.cs b/Duplicati/Library/Backend/TahoeLAFS/Properties/Settings.Designer.cs index 3480cd8fa..24bcb016c 100644 --- a/Duplicati/Library/Backend/TahoeLAFS/Properties/Settings.Designer.cs +++ b/Duplicati/Library/Backend/TahoeLAFS/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeBackend.Designer.cs b/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeBackend.Designer.cs index f2ffc044b..a6462cd21 100644 --- a/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeBackend.Designer.cs +++ b/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeBackend.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -69,6 +69,42 @@ namespace Duplicati.Library.Backend.Strings { } } + /// + /// Looks up a localized string similar to Use this option to accept any server certificate, regardless of what errors it may have. Please use --accept-specified-ssl-hash instead, whenever possible.. + /// + internal static string DescriptionAcceptAnyCertificateLong { + get { + return ResourceManager.GetString("DescriptionAcceptAnyCertificateLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Accept any server certificate. + /// + internal static string DescriptionAcceptAnyCertificateShort { + get { + return ResourceManager.GetString("DescriptionAcceptAnyCertificateShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If your server certificate is reported as invalid (eg. with self-signed certificates), you can supply the certificate hash to approve it anyway. The hash value must be entered in hex format without spaces.. + /// + internal static string DescriptionAcceptHashLong { + get { + return ResourceManager.GetString("DescriptionAcceptHashLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Optionally accept a known SSL certificate. + /// + internal static string DescriptionAcceptHashShort { + get { + return ResourceManager.GetString("DescriptionAcceptHashShort", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use this flag to communicate using Secure Socket Layer (SSL) over http (https).. /// diff --git a/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeUI.Designer.cs b/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeUI.Designer.cs index ed2c71594..778507d26 100644 --- a/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeUI.Designer.cs +++ b/Duplicati/Library/Backend/TahoeLAFS/Strings/TahoeUI.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Backend/WEBDAV/Duplicati.Library.Backend.WEBDAV.csproj b/Duplicati/Library/Backend/WEBDAV/Duplicati.Library.Backend.WEBDAV.csproj index 6ac8c03cc..e7ec6ac91 100644 --- a/Duplicati/Library/Backend/WEBDAV/Duplicati.Library.Backend.WEBDAV.csproj +++ b/Duplicati/Library/Backend/WEBDAV/Duplicati.Library.Backend.WEBDAV.csproj @@ -1,4 +1,4 @@ - + Debug @@ -13,7 +13,6 @@ 3.5 - v2.0 true @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Library/Backend/WEBDAV/app.config b/Duplicati/Library/Backend/WEBDAV/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Backend/WEBDAV/app.config +++ b/Duplicati/Library/Backend/WEBDAV/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj b/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj index 5c994514a..17b7c2ac5 100644 --- a/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj +++ b/Duplicati/Library/Compression/Duplicati.Library.Compression.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,15 +52,15 @@ pdbonly x86 prompt + 4 - - False - ..\..\..\thirdparty\SharpZipLib\ICSharpCode.SharpZipLib.dll - + + ..\..\..\thirdparty\SharpZipLib\ICSharpCode.SharpZipLib.dll + diff --git a/Duplicati/Library/Compression/Strings/FileArchiveZip.Designer.cs b/Duplicati/Library/Compression/Strings/FileArchiveZip.Designer.cs index 059ab2e21..6a46bb00d 100644 --- a/Duplicati/Library/Compression/Strings/FileArchiveZip.Designer.cs +++ b/Duplicati/Library/Compression/Strings/FileArchiveZip.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Compression/app.config b/Duplicati/Library/Compression/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Compression/app.config +++ b/Duplicati/Library/Compression/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/DynamicLoader/Duplicati.Library.DynamicLoader.csproj b/Duplicati/Library/DynamicLoader/Duplicati.Library.DynamicLoader.csproj index 1a1112666..9f01ecb25 100644 --- a/Duplicati/Library/DynamicLoader/Duplicati.Library.DynamicLoader.csproj +++ b/Duplicati/Library/DynamicLoader/Duplicati.Library.DynamicLoader.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly AnyCPU prompt + 4 diff --git a/Duplicati/Library/DynamicLoader/Strings/BackendLoader.Designer.cs b/Duplicati/Library/DynamicLoader/Strings/BackendLoader.Designer.cs index 8f809527f..382dbd7b9 100644 --- a/Duplicati/Library/DynamicLoader/Strings/BackendLoader.Designer.cs +++ b/Duplicati/Library/DynamicLoader/Strings/BackendLoader.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/DynamicLoader/Strings/DynamicLoader.Designer.cs b/Duplicati/Library/DynamicLoader/Strings/DynamicLoader.Designer.cs index da5083132..7c22198ba 100644 --- a/Duplicati/Library/DynamicLoader/Strings/DynamicLoader.Designer.cs +++ b/Duplicati/Library/DynamicLoader/Strings/DynamicLoader.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/DynamicLoader/app.config b/Duplicati/Library/DynamicLoader/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/DynamicLoader/app.config +++ b/Duplicati/Library/DynamicLoader/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Encryption/Duplicati.Library.Encryption.csproj b/Duplicati/Library/Encryption/Duplicati.Library.Encryption.csproj index f4cea0be0..4af6924df 100644 --- a/Duplicati/Library/Encryption/Duplicati.Library.Encryption.csproj +++ b/Duplicati/Library/Encryption/Duplicati.Library.Encryption.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Library/Encryption/Strings/AESEncryption.Designer.cs b/Duplicati/Library/Encryption/Strings/AESEncryption.Designer.cs index 10777aeaf..70e3cfbf8 100644 --- a/Duplicati/Library/Encryption/Strings/AESEncryption.Designer.cs +++ b/Duplicati/Library/Encryption/Strings/AESEncryption.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Encryption/Strings/EncryptionBase.Designer.cs b/Duplicati/Library/Encryption/Strings/EncryptionBase.Designer.cs index b8118e278..c69bc8bb6 100644 --- a/Duplicati/Library/Encryption/Strings/EncryptionBase.Designer.cs +++ b/Duplicati/Library/Encryption/Strings/EncryptionBase.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Encryption/Strings/GPGCommonOptions.Designer.cs b/Duplicati/Library/Encryption/Strings/GPGCommonOptions.Designer.cs index 742bd21d4..9c848119e 100644 --- a/Duplicati/Library/Encryption/Strings/GPGCommonOptions.Designer.cs +++ b/Duplicati/Library/Encryption/Strings/GPGCommonOptions.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Encryption/Strings/GPGEncryption.Designer.cs b/Duplicati/Library/Encryption/Strings/GPGEncryption.Designer.cs index 93389efff..e7f9dda4d 100644 --- a/Duplicati/Library/Encryption/Strings/GPGEncryption.Designer.cs +++ b/Duplicati/Library/Encryption/Strings/GPGEncryption.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Encryption/Strings/GPGSettingsControl.Designer.cs b/Duplicati/Library/Encryption/Strings/GPGSettingsControl.Designer.cs index 43c254145..f815de968 100644 --- a/Duplicati/Library/Encryption/Strings/GPGSettingsControl.Designer.cs +++ b/Duplicati/Library/Encryption/Strings/GPGSettingsControl.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Encryption/Strings/GPGStreamWrapper.Designer.cs b/Duplicati/Library/Encryption/Strings/GPGStreamWrapper.Designer.cs index 8676dff01..f8ec87b12 100644 --- a/Duplicati/Library/Encryption/Strings/GPGStreamWrapper.Designer.cs +++ b/Duplicati/Library/Encryption/Strings/GPGStreamWrapper.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Encryption/app.config b/Duplicati/Library/Encryption/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Encryption/app.config +++ b/Duplicati/Library/Encryption/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Interface/CommandLineArgument.cs b/Duplicati/Library/Interface/CommandLineArgument.cs index 815d1b2f6..eacab5722 100644 --- a/Duplicati/Library/Interface/CommandLineArgument.cs +++ b/Duplicati/Library/Interface/CommandLineArgument.cs @@ -26,6 +26,7 @@ namespace Duplicati.Library.Interface /// /// Primary implementation of the ICommandLineArgument interface. /// + [Serializable] public class CommandLineArgument : Duplicati.Library.Interface.ICommandLineArgument { public enum ArgumentType diff --git a/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj b/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj index fdc51411e..671c85e7b 100644 --- a/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj +++ b/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj @@ -1,4 +1,4 @@ - + Debug @@ -10,7 +10,6 @@ Properties Duplicati.Library.Interface Duplicati.Library.Interface - v2.0 512 true Duplicati.snk @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Library/Interface/Strings/CommandLineArgument.Designer.cs b/Duplicati/Library/Interface/Strings/CommandLineArgument.Designer.cs index 97164cae9..c46fb573f 100644 --- a/Duplicati/Library/Interface/Strings/CommandLineArgument.Designer.cs +++ b/Duplicati/Library/Interface/Strings/CommandLineArgument.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Interface/Strings/Common.Designer.cs b/Duplicati/Library/Interface/Strings/Common.Designer.cs index 8fe74fdb3..6f54862e1 100644 --- a/Duplicati/Library/Interface/Strings/Common.Designer.cs +++ b/Duplicati/Library/Interface/Strings/Common.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.237 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Interface/Strings/DataTypes.Designer.cs b/Duplicati/Library/Interface/Strings/DataTypes.Designer.cs index 79b40ae81..03133991f 100644 --- a/Duplicati/Library/Interface/Strings/DataTypes.Designer.cs +++ b/Duplicati/Library/Interface/Strings/DataTypes.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Logging/Duplicati.Library.Logging.csproj b/Duplicati/Library/Logging/Duplicati.Library.Logging.csproj index ef503815c..e4e073a57 100644 --- a/Duplicati/Library/Logging/Duplicati.Library.Logging.csproj +++ b/Duplicati/Library/Logging/Duplicati.Library.Logging.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Library/Logging/app.config b/Duplicati/Library/Logging/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Logging/app.config +++ b/Duplicati/Library/Logging/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Main/Duplicati.Library.Main.csproj b/Duplicati/Library/Main/Duplicati.Library.Main.csproj index e26c0815a..a0c4ab784 100644 --- a/Duplicati/Library/Main/Duplicati.Library.Main.csproj +++ b/Duplicati/Library/Main/Duplicati.Library.Main.csproj @@ -1,4 +1,4 @@ - + Debug @@ -15,7 +15,6 @@ 3.5 - v2.0 @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,10 +52,10 @@ pdbonly x86 prompt + 4 - diff --git a/Duplicati/Library/Main/Strings/BackendWrapper.Designer.cs b/Duplicati/Library/Main/Strings/BackendWrapper.Designer.cs index f8f6557cd..8b5f9d329 100644 --- a/Duplicati/Library/Main/Strings/BackendWrapper.Designer.cs +++ b/Duplicati/Library/Main/Strings/BackendWrapper.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.237 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Main/Strings/Options.Designer.cs b/Duplicati/Library/Main/Strings/Options.Designer.cs index 99821e97d..a2cf2e339 100644 --- a/Duplicati/Library/Main/Strings/Options.Designer.cs +++ b/Duplicati/Library/Main/Strings/Options.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.237 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Library/Main/app.config b/Duplicati/Library/Main/app.config index 73859b00e..89dc7d426 100644 --- a/Duplicati/Library/Main/app.config +++ b/Duplicati/Library/Main/app.config @@ -1,3 +1,3 @@ - + diff --git a/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj b/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj index 523cee8a6..b9342f31b 100644 --- a/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj +++ b/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj @@ -1,4 +1,4 @@ - + Debug @@ -10,7 +10,6 @@ Properties Duplicati.Library.Modules.Builtin Duplicati.Library.Modules.Builtin - v2.0 512 true Duplicati.snk @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 @@ -82,6 +84,10 @@ Duplicati.Library.Utility + + + + ResXFileCodeGenerator @@ -92,9 +98,6 @@ HttpOptions.Designer.cs - - - + \ No newline at end of file diff --git a/Duplicati/Server/Duplicati.Server.Serialization/Duplicati.snk b/Duplicati/Server/Duplicati.Server.Serialization/Duplicati.snk new file mode 100644 index 000000000..e0c1e2dd8 Binary files /dev/null and b/Duplicati/Server/Duplicati.Server.Serialization/Duplicati.snk differ diff --git a/Duplicati/Server/Duplicati.Server.Serialization/Enums.cs b/Duplicati/Server/Duplicati.Server.Serialization/Enums.cs new file mode 100755 index 000000000..ffdef921e --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/Enums.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Duplicati.Server.Serialization +{ + public enum RunnerState + { + Started, + Suspended, + Running, + Stopped, + } + + public enum RunnerResult + { + OK, + Partial, + Warning, + Error + } + + public enum CloseReason + { + None, + ApplicationExitCall, + TaskManagerClosing, + WindowsShutDown, + UserClosing + } + + public enum LiveControlState + { + Running, + Paused + } + + + public enum DuplicatiOperation + { + Backup, + Restore, + List, + Remove + } + + public enum DuplicatiOperationMode + { + Backup, + BackupFull, + BackupIncremental, + Restore, + RestoreControlfiles, + List, + GetBackupSets, + ListCurrentFiles, + ListSourceFolders, + ListActualSignatureFiles, + DeleteAllButNFull, + DeleteAllButN, + DeleteOlderThan, + CleanUp, + CreateFolder, + FindLastFileVersion, + Verify + } + + public enum SuggestedStatusIcon + { + Ready, + ReadyWarning, + ReadyError, + Paused, + Active, + ActivePaused + } + + public static class EnumConverter + { + public static T Convert(Enum o) + { + return (T)Enum.Parse(typeof(T), o.ToString(), false); + } + } +} diff --git a/Duplicati/Server/Duplicati.Server.Serialization/IProgressEventData.cs b/Duplicati/Server/Duplicati.Server.Serialization/IProgressEventData.cs new file mode 100644 index 000000000..721b490b5 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/IProgressEventData.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Duplicati.Server.Serialization +{ + public interface IProgressEventData + { + DuplicatiOperation Operation { get; } + DuplicatiOperationMode Mode { get; } + string Message { get; } + string SubMessage { get; } + int Progress { get; } + int SubProgress { get; } + } +} diff --git a/Duplicati/Server/Duplicati.Server.Serialization/ISerializableStatus.cs b/Duplicati/Server/Duplicati.Server.Serialization/ISerializableStatus.cs new file mode 100644 index 000000000..e22776097 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/ISerializableStatus.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace Duplicati.Server.Serialization +{ + public interface ISerializableStatus + { + RunnerState ActiveBackupState { get; } + long ActiveScheduleId { get; } + LiveControlState ProgramState { get; } + IProgressEventData RunningBackupStatus { get; } + System.Collections.Generic.IList SchedulerQueueIds { get; } + bool HasWarning { get; } + bool HasError { get; } + SuggestedStatusIcon SuggestedStatusIcon { get; } + } +} diff --git a/Duplicati/Server/Duplicati.Server.Serialization/Implementations/ProgressEventData.cs b/Duplicati/Server/Duplicati.Server.Serialization/Implementations/ProgressEventData.cs new file mode 100644 index 000000000..4a68de8a7 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/Implementations/ProgressEventData.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Duplicati.Server.Serialization.Implementations +{ + internal class ProgressEventData : IProgressEventData + { + public DuplicatiOperation Operation { get; set; } + + public DuplicatiOperationMode Mode { get; set; } + + public string Message { get; set; } + + public string SubMessage { get; set; } + + public int Progress { get; set; } + + public int SubProgress { get; set; } + } +} diff --git a/Duplicati/Server/Duplicati.Server.Serialization/Implementations/SerializableStatus.cs b/Duplicati/Server/Duplicati.Server.Serialization/Implementations/SerializableStatus.cs new file mode 100644 index 000000000..ae2fb8510 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/Implementations/SerializableStatus.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Duplicati.Server.Serialization.Implementations +{ + internal class SerializableStatus : ISerializableStatus + { + public RunnerState ActiveBackupState { get; set; } + public long ActiveScheduleId { get; set; } + public LiveControlState ProgramState { get; set; } + public IProgressEventData RunningBackupStatus { get; set; } + public IList SchedulerQueueIds { get; set; } + public bool HasError { get; set; } + public bool HasWarning { get; set; } + public SuggestedStatusIcon SuggestedStatusIcon { get; set; } + } +} diff --git a/Duplicati/Server/Duplicati.Server.Serialization/InterfaceResolver.cs b/Duplicati/Server/Duplicati.Server.Serialization/InterfaceResolver.cs new file mode 100644 index 000000000..9f27847e6 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/InterfaceResolver.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; + +namespace Duplicati.Server.Serialization +{ + public class SerializableStatusCreator : CustomCreationConverter + { + public override ISerializableStatus Create(Type objectType) + { + return new Implementations.SerializableStatus(); + } + } + + public class ProgressEventDataCreator : CustomCreationConverter + { + public override IProgressEventData Create(Type objectType) + { + return new Implementations.ProgressEventData(); + } + } +} diff --git a/Duplicati/Server/Duplicati.Server.Serialization/JsonSerializer.cs b/Duplicati/Server/Duplicati.Server.Serialization/JsonSerializer.cs new file mode 100644 index 000000000..46dbf4c86 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/JsonSerializer.cs @@ -0,0 +1,180 @@ +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Linq; +using Newtonsoft.Json.Serialization; +using System.Reflection; +using Newtonsoft.Json; +using System.Globalization; +using System.Collections.Generic; + +namespace Duplicati.Server.Serialization +{ + internal class JsonSerializer : DefaultContractResolver + { + private Dictionary m_ignoreList = GetIgnoreList(); + + private static Dictionary GetIgnoreList() + { + Dictionary lst = new Dictionary(); + lst.Add("Datamodel.ApplicationSettings!RawOptions", null); + + return lst; + } + + private static string MemberInfoAsKey(MemberInfo mi) { return mi.DeclaringType.FullName + "!" + mi.Name; } + + protected override List GetSerializableMembers(Type objectType) + { + return base.GetSerializableMembers(objectType).Where(x => !m_ignoreList.ContainsKey(MemberInfoAsKey(x))).ToList(); + } + + protected override JsonObjectContract CreateObjectContract (Type objectType) + { + if (typeof(System.Data.LightDatamodel.IDataFetcher).IsAssignableFrom(objectType)) + { + //If this type was ever serialized, it would expose the entire internal object cache, and we do NOT want that :) + throw new Exception("Internal error, a non-serializable type was hit"); + } + else if (typeof(System.Data.LightDatamodel.IDataClass).IsAssignableFrom(objectType)) + { + JsonObjectContract joc = new JsonObjectContract(objectType); + + foreach(var pi in objectType.GetProperties( BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance )) + { + var customs = pi.GetCustomAttributes(false); + bool isRef = customs.Select(x => x.GetType()).Contains(typeof(System.Data.LightDatamodel.DataClassAttributes.Affects)); + bool isParent = typeof(System.Data.LightDatamodel.IDataFetcher).IsAssignableFrom(pi.PropertyType); + isRef |= typeof(System.Data.LightDatamodel.IDataClass).IsAssignableFrom(pi.PropertyType); + + joc.Properties.Add(new JsonProperty() { + PropertyName = pi.Name, + PropertyType = pi.PropertyType, + Ignored = isRef || isParent || m_ignoreList.ContainsKey(MemberInfoAsKey(pi)), + UnderlyingName = pi.Name, + Readable = pi.CanRead, + Writable = pi.CanWrite, + ValueProvider = CreateMemberValueProvider(pi) + }); + } + + return joc; + } + else + return base.CreateObjectContract (objectType); + } + + protected override IValueProvider CreateMemberValueProvider(MemberInfo member) + { + if (member.MemberType == System.Reflection.MemberTypes.Property && ((PropertyInfo)member).PropertyType.IsEnum) + return new EnumValueProvider(member); + else if (member.MemberType == System.Reflection.MemberTypes.Field && ((FieldInfo)member).FieldType.IsEnum) + return new EnumValueProvider(member); + else + return base.CreateMemberValueProvider(member); + } + + /// + /// Get and set values for a Enum using reflection. + /// + private class EnumValueProvider : IValueProvider + { + private Type m_enumType; + + private MemberInfo m_member; + + /// + /// Initializes a new instance of the class. + /// + /// The property info. + public EnumValueProvider(MemberInfo mi) + { + if (mi == null) + throw new ArgumentNullException("mi"); + if (mi.MemberType == MemberTypes.Property) + m_enumType = ((PropertyInfo)mi).PropertyType; + else if (mi.MemberType == MemberTypes.Field) + m_enumType = ((FieldInfo)mi).FieldType; + else + throw new ArgumentException(string.Format("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo", CultureInfo.InvariantCulture, m_member.Name, "member")); + + m_member = mi; + } + + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + public void SetValue(object target, object value) + { + try + { + if (target == null) + throw new ArgumentNullException("target"); + + object parsedValue = value is string ? Enum.Parse(m_enumType, value as String, true) : value; + + switch (m_member.MemberType) + { + case MemberTypes.Field: + ((FieldInfo)m_member).SetValue(target, parsedValue); + break; + case MemberTypes.Property: + ((PropertyInfo)m_member).SetValue(target, parsedValue, null); + break; + default: + throw new ArgumentException(string.Format("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo", CultureInfo.InvariantCulture, m_member.Name, "member")); + } + } + catch (Exception ex) + { + throw new JsonSerializationException(string.Format("Error setting value to '{0}' on '{1}'.", CultureInfo.InvariantCulture, m_member.Name, (target == null ? "null" : target.GetType().FullName)), ex); + } + } + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + public object GetValue(object target) + { + try + { + if (target == null) + throw new ArgumentNullException("target"); + + switch (m_member.MemberType) + { + case MemberTypes.Field: + return ((FieldInfo)m_member).GetValue(target).ToString(); + case MemberTypes.Property: + return ((PropertyInfo)m_member).GetValue(target, null).ToString(); + default: + throw new ArgumentException(string.Format("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo", CultureInfo.InvariantCulture, m_member.Name, "member")); + } + } + catch (Exception ex) + { + throw new JsonSerializationException(string.Format("Error getting value to '{0}' on '{1}'.", CultureInfo.InvariantCulture, m_member.Name, (target == null ? "null" : target.GetType().FullName)), ex); + } + } + } + } +} + diff --git a/Duplicati/Server/Duplicati.Server.Serialization/Properties/AssemblyInfo.cs b/Duplicati/Server/Duplicati.Server.Serialization/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..69e515799 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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.GUI.Serialization")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Duplicati.GUI.Serialization")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("acd58890-bd78-42fa-965a-fa3c0ab2f86c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Duplicati/Server/Duplicati.Server.Serialization/Serializer.cs b/Duplicati/Server/Duplicati.Server.Serialization/Serializer.cs new file mode 100644 index 000000000..9bdbfaa06 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.Serialization/Serializer.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace Duplicati.Server.Serialization +{ + public class Serializer + { + private JsonSerializerSettings m_jsonSettings; + private Formatting m_jsonFormatting = Formatting.Indented; + + public Serializer() + { + m_jsonSettings = new JsonSerializerSettings(); + m_jsonSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + m_jsonSettings.ContractResolver = new JsonSerializer(); + m_jsonSettings.Converters = new JsonConverter[]{ + new SerializableStatusCreator(), + new ProgressEventDataCreator() + }.ToList(); + } + + public void SerializeJson(System.IO.StreamWriter sw, object o) + { + Newtonsoft.Json.JsonSerializer jsonSerializer = Newtonsoft.Json.JsonSerializer.Create(m_jsonSettings); + using (var jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = m_jsonFormatting; + jsonSerializer.Serialize(jsonWriter, o); + jsonWriter.Flush(); + } + } + + public T Deserialize(System.IO.StreamReader sr) + { + Newtonsoft.Json.JsonSerializer jsonSerializer = Newtonsoft.Json.JsonSerializer.Create(m_jsonSettings); + using (var jsonReader = new JsonTextReader(sr)) + { + jsonReader.Culture = System.Globalization.CultureInfo.InvariantCulture; + return jsonSerializer.Deserialize(jsonReader); + } + } + } +} diff --git a/Duplicati/Server/Duplicati.Server.csproj b/Duplicati/Server/Duplicati.Server.csproj new file mode 100644 index 000000000..4a3c23c44 --- /dev/null +++ b/Duplicati/Server/Duplicati.Server.csproj @@ -0,0 +1,287 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {19E661D2-C5DA-4F35-B3EE-7586E5734B5F} + Exe + Properties + Duplicati.Server + Duplicati.Server + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\Debug\ + DEBUG;TRACE + full + AnyCPU + prompt + false + false + 4 + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + true + true + 4 + + + true + + + Duplicati.snk + + + + ..\..\thirdparty\HttpServer\HttpServer.dll + + + ..\..\thirdparty\HttpServer\HttpServer.MVC.dll + + + ..\..\thirdparty\LightDataModel\LightDatamodel.dll + + + + + + + + + + ..\..\thirdparty\UnixSupport\UnixSupport.dll + + + + + + + + + + + + + + + DatabaseUpgrader.resx + True + True + + + DuplicatiRunner.resx + True + True + + + DynamicSetupHelper.resx + True + True + + + Program.resx + True + True + + + Scheduler.resx + True + True + + + TaskType.resx + True + True + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + {E7280DCA-7776-4A73-B9B5-41FD77FC8799} + Duplicati.CommandLine.BackendTester + + + {E2AF781D-D1E1-4512-BF2C-D6DA8D0F797F} + Duplicati.CommandLine.Decrypter + + + {81765A64-3661-4E3E-B850-2F6F87A51F74} + Duplicati.CommandLine + + + {B60D0993-B0C5-457A-90D9-DB6EEB33CE47} + Datamodel + + + {42C91180-11EB-4590-82EA-80FE9D4DEBEC} + Duplicati.Library.Backend.GoogleDocs + + + {CCD76347-7DC7-4B42-B7E1-E500E624CAC3} + Duplicati.Library.Backend.SkyDrive + + + {1BFAE226-8364-4086-825C-BB83F6F3EE4C} + Duplicati.Library.Backend.CloudFiles + + + {FC9B7611-836F-4127-8B44-A7C31F506807} + Duplicati.Library.Backend.File + + + {F61679A9-E5DE-468A-B5A4-05F92D0143D2} + Duplicati.Library.Backend.FTP + + + {C03F6DFD-805A-4BE0-9338-64870ADDB4A2} + Duplicati.Library.Backend.S3 + + + {FF2BF37C-E502-4C98-BEA0-701671DDFA08} + Duplicati.Library.Backend.SSHv2 + + + {C0270709-2A40-43B5-8CF1-69581B9FA2A1} + Duplicati.Library.Backend.TahoeLAFS + + + {BAE27510-8B5D-44B2-B33E-372A98908041} + Duplicati.Library.Backend.WEBDAV + + + {19ECCE09-B5EB-406C-8C57-BAC66997D469} + Duplicati.Library.Compression + + + {0CA86ECF-5BEC-4909-B4F6-110A03B30B92} + Duplicati.Library.DynamicLoader + + + {94484FDB-2EFA-4CF0-9BE6-A561157B4F87} + Duplicati.Library.Encryption + + + {C5899F45-B0FF-483C-9D38-24A9FCAAB237} + Duplicati.Library.Interface + + + {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798} + Duplicati.Library.Logging + + + {10D2D1B7-C664-41D8-9B3A-00040C3D421B} + Duplicati.Library.Main + + + {52826615-7964-47FE-B4B3-1B2DBDF605B9} + Duplicati.Library.Modules.Builtin + + + {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65} + Duplicati.Library.Utility + + + {4D012CB1-4B92-47F4-89B7-BF80A73A2E99} + Duplicati.License + + + {33FD1D24-C28F-4C71-933F-98F1586EA76C} + Duplicati.Server.Serialization + + + + + ResXFileCodeGenerator + DatabaseUpgrader.Designer.cs + Designer + + + ResXFileCodeGenerator + DuplicatiRunner.Designer.cs + + + ResXFileCodeGenerator + DynamicSetupHelper.Designer.cs + + + ResXFileCodeGenerator + Program.Designer.cs + Designer + + + ResXFileCodeGenerator + Scheduler.Designer.cs + + + ResXFileCodeGenerator + TaskType.Designer.cs + Designer + + + + + \ No newline at end of file diff --git a/Duplicati/Server/Duplicati.snk b/Duplicati/Server/Duplicati.snk new file mode 100644 index 000000000..e0c1e2dd8 Binary files /dev/null and b/Duplicati/Server/Duplicati.snk differ diff --git a/Duplicati/Server/DuplicatiOutputParser.cs b/Duplicati/Server/DuplicatiOutputParser.cs new file mode 100644 index 000000000..e92740905 --- /dev/null +++ b/Duplicati/Server/DuplicatiOutputParser.cs @@ -0,0 +1,106 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; +using Duplicati.Datamodel; + +namespace Duplicati.Server +{ + public class DuplicatiOutputParser + { + public const string ErrorStatus = "Error"; + public const string OKStatus = "OK"; + public const string WarningStatus = "Warning"; + public const string PartialStatus = "Partial"; + public const string InterruptedStatus = "InProgress"; + public const string NoChangedFiles = "Empty"; + + private const string OK_INDICATOR = "Duration"; + private const string WARNING_INDICATOR = "NumberOfErrors"; + + private const string UPLOAD_SIZE_INDICATOR = "BytesUploaded"; + private const string DOWNLOAD_SIZE_INDICATOR = "BytesDownloaded"; + private const string PARTIAL_INDICATOR = "Unprocessed"; + + private const string METHOD_INDICATOR = "OperationName"; + + public static void ParseData(Log log) + { + string text = log.Blob.StringData; + + log.ParsedStatus = ErrorStatus; + + if (text.IndexOf(OK_INDICATOR) >= 0) + log.ParsedStatus = OKStatus; + + long c = ExtractNumber(text, WARNING_INDICATOR, -1); + if (c > 0) + log.ParsedStatus = WarningStatus; + + bool isBackup = ExtractValue(text, METHOD_INDICATOR, "").StartsWith("backup", StringComparison.InvariantCultureIgnoreCase); + + if (isBackup) + c = ExtractNumber(text, UPLOAD_SIZE_INDICATOR, -1); + else + c = ExtractNumber(text, DOWNLOAD_SIZE_INDICATOR, -1); + + if (c == 0) + { + if (isBackup) + log.ParsedStatus = NoChangedFiles; + else + log.ParsedStatus = WarningStatus; + } + + log.Transfersize = c; + + if (log.ParsedStatus == WarningStatus) + { + long missing = ExtractNumber(text, PARTIAL_INDICATOR, 0); + if (missing > 0) + log.ParsedStatus = PartialStatus; + } + } + + private static long ExtractNumber(string output, string key, long @default) + { + long count; + if (!long.TryParse(ExtractValue(output, key, @default.ToString()), out count)) + count = @default; + + return count; + } + + private static string ExtractValue(string output, string key, string @default) + { + string result = @default; + if (output.IndexOf(key) >= 0) + { + int pos = output.IndexOf(key) + key.Length; + pos = output.IndexOf(':', pos) + 1; + int p2 = output.IndexOfAny(new char[] { '\r', '\n' }, pos); + result = output.Substring(pos, p2 - pos).Trim(); + } + + return result; + } + } +} diff --git a/Duplicati/Server/DuplicatiRunner.cs b/Duplicati/Server/DuplicatiRunner.cs new file mode 100644 index 000000000..b637fb349 --- /dev/null +++ b/Duplicati/Server/DuplicatiRunner.cs @@ -0,0 +1,603 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; +using Duplicati.Datamodel; +//using Duplicati.Library.Main; +using Duplicati.Server.Serialization; + +namespace Duplicati.Server +{ + /// + /// This class translates tasks into Duplicati calls, and executes them + /// + public class DuplicatiRunner : Duplicati.Library.Main.LiveControl.ILiveControl + { + public class ProgressEventData : IProgressEventData + { + public DuplicatiOperation Operation { get; set; } + public DuplicatiOperationMode Mode { get; set; } + public string Message { get; set; } + public string SubMessage { get; set; } + public int Progress { get; set; } + public int SubProgress { get; set; } + } + + public delegate void ResultEventDelegate(RunnerResult result, string parsedMessage, string message); + public event ResultEventDelegate ResultEvent; + + public delegate void ProgressEventDelegate(DuplicatiOperation operation, RunnerState state, string message, string submessage, int progress, int subprogress); + public event ProgressEventDelegate ProgressEvent; + + private const int PERCENT_PR_EXTRA_OPERATION = 2; + private int m_extraOperations = 0; + + private ProgressEventData m_lastEvent = new ProgressEventData(); + + private object m_lock = new object(); + private CloseReason m_stopReason = CloseReason.None; + private Library.Main.LiveControl.ILiveControl m_currentBackupControlInterface; + private bool m_isAborted = false; + + private RunnerState m_currentRunnerState = RunnerState.Suspended; + + public DuplicatiRunner() + { + ProgressEvent += new ProgressEventDelegate(DuplicatiRunner_ProgressEvent); + } + + void DuplicatiRunner_ProgressEvent(DuplicatiOperation operation, RunnerState state, string message, string submessage, int progress, int subprogress) + { + m_currentRunnerState = state; + } + + public void ExecuteTask(IDuplicityTask task) + { + Dictionary options = new Dictionary(); + + //Set the log level to be that of the GUI + options["log-level"] = Duplicati.Library.Logging.Log.LogLevel.ToString(); + + string destination = task.GetConfiguration(options); + + string results = ""; + string parsedMessage = ""; + m_isAborted = false; + + try + { + //TODO: Its a bit dirty to set the options after creating the instance + using (Duplicati.Library.Main.Interface i = new Duplicati.Library.Main.Interface(destination, options)) + { + lock (m_lock) + { + m_stopReason = CloseReason.None; + m_currentBackupControlInterface = i; + } + + SetupControlInterface(); + + i.OperationProgress += new Duplicati.Library.Main.OperationProgressEvent(Duplicati_OperationProgress); + + switch (task.TaskType) + { + case DuplicityTaskType.FullBackup: + case DuplicityTaskType.IncrementalBackup: + { + //Activate auto-cleanup + options["auto-cleanup"] = ""; + options["force"] = ""; + if (task.Schedule.Task.KeepFull > 0) + m_extraOperations++; + if (!string.IsNullOrEmpty(task.Schedule.Task.KeepTime)) + m_extraOperations++; + + Library.Utility.TempFolder tf = null; + try + { + if (ProgressEvent != null) + ProgressEvent(DuplicatiOperation.Backup, RunnerState.Started, task.Schedule.Name, "", 0, -1); + + if (task.Task.IncludeSetup) + { + //Make a copy of the current database + tf = new Duplicati.Library.Utility.TempFolder(); + string filename = System.IO.Path.Combine(tf, System.IO.Path.GetFileName(Program.DatabasePath)); + + System.IO.File.Copy(Program.DatabasePath, filename, true); + using (System.Data.IDbConnection con = (System.Data.IDbConnection)Activator.CreateInstance(SQLiteLoader.SQLiteConnectionType)) + { + con.ConnectionString = "Data Source=" + filename; + + //Open the database, handle any encryption issues automatically + Program.OpenDatabase(con); + + using (System.Data.IDbCommand cmd = con.CreateCommand()) + { + //Remove all log data to minimize the size of the database + cmd.CommandText = "DELETE FROM CommandQueue;"; + cmd.ExecuteNonQuery(); + cmd.CommandText = "DELETE FROM Log;"; + cmd.ExecuteNonQuery(); + cmd.CommandText = "DELETE FROM LogBlob;"; + cmd.ExecuteNonQuery(); + + //Free up unused space + cmd.CommandText = "VACUUM;"; + cmd.ExecuteNonQuery(); + } + } + + options["signature-control-files"] = filename; + } + + options["full-if-sourcefolder-changed"] = ""; + + List> filters = new List>(); + string[] sourceFolders = DynamicSetupHelper.GetSourceFolders(task.Task, new ApplicationSettings(task.Task.DataParent), filters); + + if (options.ContainsKey("filter")) + filters.AddRange(Library.Utility.FilenameFilter.DecodeFilter(options["filter"])); + + options["filter"] = Library.Utility.FilenameFilter.EncodeAsFilter(filters); + + //At this point we register the backup as being in progress + ((FullOrIncrementalTask)task).WriteBackupInProgress(Strings.DuplicatiRunner.ShutdownWhileBackupInprogress); + + results = i.Backup(sourceFolders); + } + finally + { + if (tf != null) + tf.Dispose(); + + if (ProgressEvent != null) + ProgressEvent(DuplicatiOperation.Backup, RunnerState.Stopped, task.Schedule.Name, "", 100, -1); + } + break; + } + case DuplicityTaskType.ListBackups: + + List res = new List(); + foreach (Duplicati.Library.Main.ManifestEntry be in i.GetBackupSets()) + { + res.Add(be.Time.ToString()); + foreach (Duplicati.Library.Main.ManifestEntry bei in be.Incrementals) + res.Add(bei.Time.ToString()); + } + + (task as ListBackupsTask).Backups = res.ToArray(); + break; + case DuplicityTaskType.ListBackupEntries: + (task as ListBackupEntriesTask).Backups = i.GetBackupSets(); + break; + case DuplicityTaskType.ListFiles: + (task as ListFilesTask).Files = i.ListCurrentFiles(); + break; + case DuplicityTaskType.ListSourceFolders: + (task as ListSourceFoldersTask).Files = new List(i.ListSourceFolders() ?? new string[0]); + break; + case DuplicityTaskType.ListActualFiles: + (task as ListActualFilesTask).Files = i.ListActualSignatureFiles(); + break; + case DuplicityTaskType.RemoveAllButNFull: + results = i.DeleteAllButNFull(); + break; + case DuplicityTaskType.RemoveOlderThan: + results = i.DeleteOlderThan(); + break; + case DuplicityTaskType.Restore: + options["file-to-restore"] = ((RestoreTask)task).SourceFiles; + if (options.ContainsKey("filter")) + options.Remove("filter"); + + try + { + if (ProgressEvent != null) + ProgressEvent(DuplicatiOperation.Restore, RunnerState.Started, task.Schedule.Name, "", 0, -1); + results = i.Restore(task.LocalPath.Split(System.IO.Path.PathSeparator)); + } + finally + { + if (ProgressEvent != null) + ProgressEvent(DuplicatiOperation.Restore, RunnerState.Stopped, task.Schedule.Name, "", 100, -1); + } + break; + + case DuplicityTaskType.RestoreSetup: + i.RestoreControlFiles(task.LocalPath); + break; + default: + return; + } + } + } + catch (Exception ex) + { + while (ex is System.Reflection.TargetInvocationException && ex.InnerException != null) + ex = ex.InnerException; + + if (ex is System.Threading.ThreadAbortException) + { + m_isAborted = true; + System.Threading.Thread.ResetAbort(); + } + else if (ex is Library.Main.LiveControl.ExecutionStoppedException) + m_isAborted = true; + + if (m_isAborted && m_stopReason != CloseReason.None) + { + //If the user has stopped the backup for some reason, write a nicer message + switch (m_stopReason) + { + case CloseReason.ApplicationExitCall: + parsedMessage = Strings.DuplicatiRunner.ApplicationExitLogMesssage; + break; + case CloseReason.TaskManagerClosing: + parsedMessage = Strings.DuplicatiRunner.TaskManagerCloseMessage; + break; + case CloseReason.UserClosing: + parsedMessage = Strings.DuplicatiRunner.UserClosingMessage; + break; + case CloseReason.WindowsShutDown: + parsedMessage = Strings.DuplicatiRunner.WindowsShutdownMessage; + break; + default: + parsedMessage = string.Format(Strings.DuplicatiRunner.OtherAbortMessage, m_stopReason); + break; + } + + if (task.Schedule != null) + { + //If the application is going down, the backup should resume on next launch + switch (m_stopReason) + { + case CloseReason.ApplicationExitCall: + case CloseReason.TaskManagerClosing: + case CloseReason.WindowsShutDown: + task.Schedule.ScheduledRunFailed(); + break; + } + } + } + else + parsedMessage = string.Format(Strings.DuplicatiRunner.ErrorMessage, ex.Message); + + results = "Error: " + ex.ToString(); //Don't localize + + while (ex.InnerException != null) + { + ex = ex.InnerException; + results += Environment.NewLine + "InnerError: " + ex.ToString(); //Don't localize + } + + } + finally + { + lock (m_lock) + m_currentBackupControlInterface = null; + } + + try + { + if (!m_isAborted && (task.TaskType == DuplicityTaskType.FullBackup || task.TaskType == DuplicityTaskType.IncrementalBackup)) + { + if (task.Schedule.Task.KeepFull > 0) + { + m_lastEvent.Progress = 100; + m_lastEvent.Message = Strings.DuplicatiRunner.CleaningUpMessage; + m_lastEvent.SubMessage = ""; + m_lastEvent.SubProgress = -1; + + ReinvokeLastProgressEvent(); + m_extraOperations--; + + RemoveAllButNFullTask tmpTask = new RemoveAllButNFullTask(task.Schedule, (int)task.Schedule.Task.KeepFull); + ExecuteTask(tmpTask); + results += Environment.NewLine + Strings.DuplicatiRunner.CleanupLogdataHeader + Environment.NewLine + tmpTask.Result; + } + + if (!string.IsNullOrEmpty(task.Schedule.Task.KeepTime)) + { + m_lastEvent.Progress = 100; + m_lastEvent.Message = Strings.DuplicatiRunner.CleaningUpMessage; + m_lastEvent.SubMessage = ""; + m_lastEvent.SubProgress = -1; + + ReinvokeLastProgressEvent(); + m_extraOperations--; + + RemoveOlderThanTask tmpTask = new RemoveOlderThanTask(task.Schedule, task.Schedule.Task.KeepTime); + ExecuteTask(tmpTask); + results += Environment.NewLine + Strings.DuplicatiRunner.CleanupLogdataHeader + Environment.NewLine + tmpTask.Result; + } + + if (task.Schedule.Task.KeepFull > 0 || !string.IsNullOrEmpty(task.Schedule.Task.KeepTime)) + ReinvokeLastProgressEvent(); + + if (ProgressEvent != null) + ProgressEvent(DuplicatiOperation.Backup, RunnerState.Stopped, task.Schedule.Name, "", 100, -1); + } + } + catch (Exception ex) + { + results += Environment.NewLine + string.Format(Strings.DuplicatiRunner.CleanupError, ex.Message); + } + + task.IsAborted = m_isAborted; + task.Result = results; + task.RaiseTaskCompleted(results, parsedMessage); + + if (ResultEvent != null && task is FullBackupTask || task is IncrementalBackupTask) + { + Log[] logs = Program.DataConnection.GetObjects("TaskID = ? AND SubAction LIKE ? ORDER BY EndTime DESC", task.Task.ID, "Primary"); + if (logs != null && logs.Length > 0) + { + Datamodel.Log l = logs[0]; + RunnerResult r = RunnerResult.Error; + if (l.ParsedStatus == DuplicatiOutputParser.ErrorStatus) + r = RunnerResult.Error; + else if (l.ParsedStatus == DuplicatiOutputParser.OKStatus || l.ParsedStatus == DuplicatiOutputParser.NoChangedFiles) + r = RunnerResult.OK; + else if (l.ParsedStatus == DuplicatiOutputParser.PartialStatus) + r = RunnerResult.Partial; + else if (l.ParsedStatus == DuplicatiOutputParser.WarningStatus) + r = RunnerResult.Warning; + + ResultEvent(r, parsedMessage, results); + } + } + + if (task.Schedule != null && !m_isAborted) + task.Schedule.ScheduledRunCompleted(); //Register as completed if not aborted + } + + void Duplicati_OperationProgress(Duplicati.Library.Main.Interface caller, Duplicati.Library.Main.DuplicatiOperation operation, Duplicati.Library.Main.DuplicatiOperationMode specificmode, int progress, int subprogress, string message, string submessage) + { + Duplicati_OperationProgress(caller, EnumConverter.Convert(operation), EnumConverter.Convert(specificmode), progress, subprogress, message, submessage); + } + + void Duplicati_OperationProgress(Duplicati.Library.Main.Interface caller, DuplicatiOperation operation, DuplicatiOperationMode specificmode, int progress, int subprogress, string message, string submessage) + { + m_lastEvent.Operation = operation; + m_lastEvent.Mode = specificmode; + m_lastEvent.Progress = progress; + m_lastEvent.SubProgress = subprogress; + m_lastEvent.Message = message; + m_lastEvent.SubMessage = submessage; + + //If there are extra operations, reserve some space for it by reducing the displayed progress + if (m_extraOperations > 0 && progress > 0) + progress = (int)((m_lastEvent.Progress / 100.0) * (100 - (m_extraOperations * PERCENT_PR_EXTRA_OPERATION))); + + if (ProgressEvent != null) + try { ProgressEvent(operation, RunnerState.Running, message, submessage, progress, subprogress); } + catch { } + } + + public void ReinvokeLastProgressEvent() + { + Duplicati_OperationProgress(null, m_lastEvent.Operation, m_lastEvent.Mode, m_lastEvent.Progress, m_lastEvent.SubProgress, m_lastEvent.Message, m_lastEvent.SubMessage ); + } + + private void PerformBackup(Schedule schedule, bool forceFull, string fullAfter) + { + if (forceFull) + ExecuteTask(new FullBackupTask(schedule)); + else + ExecuteTask(new IncrementalBackupTask(schedule, fullAfter)); + } + + public void Restore(Schedule schedule, DateTime when, string where) + { + ExecuteTask(new RestoreTask(schedule, where, when)); + } + + public string[] ListBackups(Schedule schedule) + { + ListBackupsTask task = new ListBackupsTask(schedule); + ExecuteTask(task); + + if (task.IsAborted) + return null; + + return task.Backups; + } + + public List ListBackupEntries(Schedule schedule) + { + ListBackupEntriesTask task = new ListBackupEntriesTask(schedule); + ExecuteTask(task); + + if (task.IsAborted) + return null; + + if (task.Result.StartsWith("Error:")) + throw new Exception(task.Result); + + return task.Backups; + } + + public List> ListActualFiles(Schedule schedule, DateTime when) + { + ListActualFilesTask task = new ListActualFilesTask(schedule, when); + ExecuteTask(task); + + if (task.IsAborted) + return null; + + if (task.Result.StartsWith("Error:")) + throw new Exception(task.Result); + + return task.Files; + } + + public IList ListFiles(Schedule schedule, DateTime when) + { + ListFilesTask task = new ListFilesTask(schedule, when); + ExecuteTask(task); + + if (task.IsAborted) + return null; + + if (task.Result.StartsWith("Error:")) + throw new Exception(task.Result); + return task.Files; + } + + public IList ListSourceFolders(Schedule schedule, DateTime when) + { + ListSourceFoldersTask task = new ListSourceFoldersTask(schedule, when); + ExecuteTask(task); + + if (task.IsAborted) + return null; + + if (task.Result.StartsWith("Error:")) + throw new Exception(task.Result); + + return task.Files; + } + + public void IncrementalBackup(Schedule schedule) + { + PerformBackup(schedule, false, null); + } + + public void FullBackup(Schedule schedule) + { + PerformBackup(schedule, true, null); + } + + /// + /// Gets a value indicating if the last run was aborted + /// + public bool IsAborted { get { return m_isAborted; } } + + /// + /// Function used to apply settings to a new interface + /// + private void SetupControlInterface() + { + //Copy the values to avoid thread race problems + System.Threading.ThreadPriority? priority = Program.LiveControl.ThreadPriority; + long? uploadLimit = Program.LiveControl.UploadLimit; + long? downloadLimit = Program.LiveControl.DownloadLimit; + + if (priority != null) + m_currentBackupControlInterface.SetThreadPriority(priority.Value); + if (uploadLimit != null) + m_currentBackupControlInterface.SetUploadLimit(uploadLimit.Value.ToString() + "b"); + if (downloadLimit != null) + m_currentBackupControlInterface.SetDownloadLimit(downloadLimit.Value.ToString() + "b"); + } + + public void Pause() + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + m_currentBackupControlInterface.Pause(); + } + + public void Resume() + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + m_currentBackupControlInterface.Resume(); + } + + public RunnerState CurrentState { get { return m_currentRunnerState; } } + public ProgressEventData LastEvent { get { return m_lastEvent; } } + + public void Stop() + { + Stop(CloseReason.None); + } + + public void Stop(CloseReason reason) + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + { + m_stopReason = reason; + if (m_currentBackupControlInterface.IsStopRequested) + m_currentBackupControlInterface.Terminate(); + else + m_currentBackupControlInterface.Stop(); + } + } + + public void Terminate(CloseReason reason) + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + { + m_stopReason = reason; + m_currentBackupControlInterface.Terminate(); + } + } + + public void Terminate() + { + Terminate(CloseReason.None); + } + + public bool IsStopRequested + { + get + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + return m_currentBackupControlInterface.IsStopRequested; + else + return false; + } + } + + public void SetUploadLimit(string limit) + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + m_currentBackupControlInterface.SetUploadLimit(limit); + } + + public void SetDownloadLimit(string limit) + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + m_currentBackupControlInterface.SetDownloadLimit(limit); + } + + public void SetThreadPriority(System.Threading.ThreadPriority priority) + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + m_currentBackupControlInterface.SetThreadPriority(priority); + } + + public void UnsetThreadPriority() + { + lock (m_lock) + if (m_currentBackupControlInterface != null) + m_currentBackupControlInterface.UnsetThreadPriority(); + } + } +} diff --git a/Duplicati/Server/DynamicSetupHelper.cs b/Duplicati/Server/DynamicSetupHelper.cs new file mode 100644 index 000000000..f362da9d7 --- /dev/null +++ b/Duplicati/Server/DynamicSetupHelper.cs @@ -0,0 +1,168 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; +using Duplicati.Datamodel; + +namespace Duplicati.Server +{ + /// + /// This class generates a list of source folders and matching filters, based on the users choices + /// + public static class DynamicSetupHelper + { + /* + /// + /// Gets a source folder setup, based on the users filter selection + /// + /// The wrapper instance with the user selection + /// The filter settings + /// The source folder string + public static string[] GetSourceFolders(Wizard_pages.WizardSettingsWrapper wrapper, ApplicationSettings settings, List> filters) + { + if (wrapper.SelectFilesUI.Version >= 2 && wrapper.SelectFilesUI.UseSimpleMode) + { + return GetSourceFolders( + wrapper.SelectFilesUI.IncludeDocuments, + wrapper.SelectFilesUI.IncludeMusic, + wrapper.SelectFilesUI.IncludeImages, + wrapper.SelectFilesUI.IncludeDesktop, + wrapper.SelectFilesUI.IncludeSettings, + settings, + filters); + } + else + { + return PrependFilterList(wrapper.SourcePath.Split(System.IO.Path.PathSeparator), settings, filters); + } + }*/ + + /// + /// Gets a source folder setup, based on the users filter selection + /// + /// The Task instance with the user setup + /// The filter settings + /// The source folder string + public static string[] GetSourceFolders(Task task, ApplicationSettings settings, List> filters) + { + if (task.Extensions.SelectFiles_Version >= 2 && task.Extensions.SelectFiles_UseSimpleMode) + { + return GetSourceFolders( + task.Extensions.SelectFiles_IncludeDocuments, + task.Extensions.SelectFiles_IncludeMusic, + task.Extensions.SelectFiles_IncludeImages, + task.Extensions.SelectFiles_IncludeDesktop, + task.Extensions.SelectFiles_IncludeAppData, + settings, + filters); + } + else + { + return PrependFilterList(task.SourcePath.Split(System.IO.Path.PathSeparator), settings, filters); + } + } + + /// + /// Prepends extra filters to exclude the temp folder and the Duplicati signature cache + /// + /// The list of folders being backed up + /// The current set of filters to prepend to + /// The sourceFolders + private static string[] PrependFilterList(string[] sourceFolders, ApplicationSettings settings, List> filters) + { + string[] exFolders = new string[] { + Library.Utility.Utility.AppendDirSeparator(System.Environment.ExpandEnvironmentVariables(settings.SignatureCachePath)), + Library.Utility.Utility.AppendDirSeparator(System.Environment.ExpandEnvironmentVariables(settings.TempPath)) + }; + + foreach (string i in sourceFolders) + foreach (string x in exFolders) + if (x.StartsWith(i, Library.Utility.Utility.ClientFilenameStringComparision)) + filters.Insert(0, new KeyValuePair(false, Library.Utility.FilenameFilter.ConvertGlobbingToRegExp(x))); + + return sourceFolders; + } + + /// + /// Gets a source folder setup, based on the users folder selection + /// + /// The filter settings + /// True if documents should be included + /// True if music should be included + /// True if images should be included + /// True if desktop files should be included + /// True if settings should be included + /// The source folder string + private static string[] GetSourceFolders(bool includeDocuments, bool includeMusic, bool includeImages, bool includeDesktop, bool includeSettings, ApplicationSettings settings, List> filters) + { + string myPictures = Library.Utility.Utility.AppendDirSeparator(System.Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)); + string myMusic = Library.Utility.Utility.AppendDirSeparator(System.Environment.GetFolderPath(Environment.SpecialFolder.MyMusic)); + string desktop = Library.Utility.Utility.AppendDirSeparator(System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop)); + string appData = Library.Utility.Utility.AppendDirSeparator(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + string myDocuments = Library.Utility.Utility.AppendDirSeparator(System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); + + List folders = new List(); + List exfolders = new List(); + + (includeDocuments ? folders : exfolders).Add(myDocuments); + (includeImages ? folders : exfolders).Add(myPictures); + (includeMusic ? folders : exfolders).Add(myMusic); + (includeDesktop ? folders : exfolders).Add(desktop); + (includeSettings ? folders : exfolders).Add(appData); + + if (folders.Count == 0) + throw new Exception(Strings.DynamicSetupHelper.NoFoldersInSetupError); + + //Figure out if any folders are subfolders, and only include the parents + for (int i = 0; i < folders.Count; i++) + for (int j = i + 1; j < folders.Count; j++) + if (folders[i].StartsWith(folders[j], Library.Utility.Utility.ClientFilenameStringComparision)) + { + folders.RemoveAt(i); + i--; + break; //Break inner, continue outer + } + else if (folders[j].StartsWith(folders[i], Library.Utility.Utility.ClientFilenameStringComparision)) + { + folders.RemoveAt(j); + i = -1; //Restart loop + break; //Break inner, continue outer + } + + //Add filters to exclude de-selected folders + foreach (string s in exfolders) + { + foreach (string s2 in folders) + if (s.StartsWith(s2)) + { + filters.Insert(0, new KeyValuePair(false, Library.Utility.FilenameFilter.ConvertGlobbingToRegExp(s))); + break; + } + } + + string fi = ""; + foreach (KeyValuePair x in filters) + fi += x.Value + ";"; + + return PrependFilterList(folders.ToArray(), settings, filters); + } + } +} diff --git a/Duplicati/Server/LiveControls.cs b/Duplicati/Server/LiveControls.cs new file mode 100644 index 000000000..62636ca59 --- /dev/null +++ b/Duplicati/Server/LiveControls.cs @@ -0,0 +1,260 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; + +namespace Duplicati.Server +{ + /// + /// This class keeps track of the users modifications regarding + /// throttling and pause/resume + /// + public class LiveControls + { + /// + /// An event that is activated when the pause state changes + /// + public event EventHandler StateChanged; + + /// + /// An event that is activated when the thread priority changes + /// + public event EventHandler ThreadPriorityChanged; + + /// + /// An event that is activated when the throttle speed changes + /// + public event EventHandler ThrottleSpeedChanged; + + /// + /// The possible states for the live control + /// + public enum LiveControlState + { + /// + /// Indicates that the backups are running + /// + Running, + /// + /// Indicates that the backups are currently suspended + /// + Paused + } + + /// + /// The current control state + /// + private LiveControlState m_state; + + /// + /// Gets the current state for the control + /// + public LiveControlState State { get { return m_state; } } + + /// + /// The internal variable that tracks the the priority + /// + private System.Threading.ThreadPriority? m_priority; + + /// + /// The internal variable that tracks the upload limit + /// + private long? m_uploadLimit; + + /// + /// The internal variable that tracks the download limit + /// + private long? m_downloadLimit; + + /// + /// The object that ensures concurrent operations + /// + private object m_lock = new object(); + + /// + /// Gets the current overridden thread priority + /// + public System.Threading.ThreadPriority? ThreadPriority + { + get { return m_priority; } + set + { + if (m_priority != value) + { + m_priority = value; + if (ThreadPriorityChanged != null) + ThreadPriorityChanged(this, null); + } + } + } + + /// + /// Gets the current upload limit in bps + /// + public long? UploadLimit + { + get { return m_uploadLimit; } + set + { + if (m_uploadLimit != value) + { + m_uploadLimit = value; + if (ThrottleSpeedChanged != null) + ThrottleSpeedChanged(this, null); + } + } + } + + /// + /// Gets the download limit in bps + /// + public long? DownloadLimit + { + get { return m_downloadLimit; } + set + { + if (m_downloadLimit != value) + { + m_downloadLimit = value; + if (ThrottleSpeedChanged != null) + ThrottleSpeedChanged(this, null); + } + } + } + + /// + /// The timer that is activated after a pause period. + /// + private System.Threading.Timer m_waitTimer; + + /// + /// Constructs a new instance of the LiveControl + /// + /// The duration that the backups should be initially suspended + public LiveControls(Datamodel.ApplicationSettings settings) + { + m_state = LiveControlState.Running; + m_waitTimer = new System.Threading.Timer(m_waitTimer_Tick, this, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); + if (!string.IsNullOrEmpty(settings.StartupDelayDuration) && settings.StartupDelayDuration != "0") + { + m_waitTimer.Change((long)Duplicati.Library.Utility.Timeparser.ParseTimeSpan(settings.StartupDelayDuration).TotalMilliseconds, System.Threading.Timeout.Infinite); + m_state = LiveControlState.Paused; + } + + m_priority = settings.ThreadPriorityOverride; + if (!string.IsNullOrEmpty(settings.DownloadSpeedLimit)) + m_downloadLimit = Library.Utility.Sizeparser.ParseSize(settings.DownloadSpeedLimit, "kb"); + if (!string.IsNullOrEmpty(settings.UploadSpeedLimit)) + m_uploadLimit = Library.Utility.Sizeparser.ParseSize(settings.UploadSpeedLimit, "kb"); + } + + /// + /// Event that occurs when the timeout duration is exceeded + /// + /// The sender of the event + /// An unused event argument + private void m_waitTimer_Tick(object sender) + { + lock (m_lock) + Resume(); + } + + /// + /// Internal helper to reset the timeout timer + /// + /// The time to wait + private void ResetTimer(string timeout) + { + lock (m_lock) + if (!string.IsNullOrEmpty(timeout)) + m_waitTimer.Change((long)Duplicati.Library.Utility.Timeparser.ParseTimeSpan(timeout).TotalMilliseconds, System.Threading.Timeout.Infinite); + else + m_waitTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); + } + + /// + /// Pauses the backups until resumed + /// + public void Pause() + { + lock (m_lock) + { + if (m_state == LiveControlState.Running) + { + m_state = LiveControlState.Paused; + if (StateChanged != null) + StateChanged(this, null); + } + } + } + + /// + /// Resumes a backups to the running state + /// + public void Resume() + { + lock (m_lock) + { + if (m_state == LiveControlState.Paused) + { + //Make sure that the timer is cleared + m_waitTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); + + m_state = LiveControlState.Running; + if (StateChanged != null) + StateChanged(this, null); + } + } + } + + /// + /// Suspends the backups for a given period + /// + /// The duration to wait + public void Pause(string timeout) + { + lock (m_lock) + { + if (m_state == LiveControlState.Running) + { + Pause(); + m_waitTimer.Change((long)Duplicati.Library.Utility.Timeparser.ParseTimeSpan(timeout).TotalMilliseconds, System.Threading.Timeout.Infinite); + } + } + } + + /// + /// Suspends the backups for a given period + /// + /// The duration to wait + public void Pause(TimeSpan timeout) + { + lock (m_lock) + { + if (m_state == LiveControlState.Running) + { + Pause(); + m_waitTimer.Change((long)timeout.TotalMilliseconds, System.Threading.Timeout.Infinite); + } + } + } + } +} diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs new file mode 100644 index 000000000..e073e7992 --- /dev/null +++ b/Duplicati/Server/Program.cs @@ -0,0 +1,480 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Data.LightDatamodel; + +namespace Duplicati.Server +{ + public class Program + { + /// + /// The name of the application, change this to re-brand it + /// + public const string ApplicationName = "Duplicati"; + + /// + /// The path to the directory that contains the main executable + /// + public static readonly string StartupPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + + /// + /// The name of the environment variable that holds the path to the data folder used by Duplicati + /// + public static readonly string DATAFOLDER_ENV_NAME = ApplicationName.ToUpper() + "_HOME"; + + /// + /// The environment variable that holdes the database key used to encrypt the SQLite database + /// + public static readonly string DB_KEY_ENV_NAME = ApplicationName.ToUpper() + "_DB_KEY"; + + /// + /// Gets the folder where Duplicati data is stored + /// + public static string DATAFOLDER { get { return Library.Utility.Utility.AppendDirSeparator(Environment.ExpandEnvironmentVariables("%" + DATAFOLDER_ENV_NAME + "%").TrimStart('"').TrimEnd('"')); } } + + /// + /// A flag indicating if database encryption is in use + /// + public static bool UseDatabaseEncryption; + + /// + /// This is the only access to the database + /// + public static IDataFetcherWithRelations DataConnection; + + /// + /// This is the lock to be used before manipulating the shared resources + /// + public static object MainLock = new object(); + + /// + /// This is the scheduling thread + /// + public static Scheduler Scheduler; + + /// + /// This is the working thread + /// + public static Duplicati.Library.Utility.WorkerThread WorkThread; + + /// + /// The path to the file that contains the current database + /// + public static string DatabasePath; + + /// + /// The actual runner, do not call directly. Only used for events. + /// + public static DuplicatiRunner Runner; + + /// + /// The controller interface for pause/resume and throttle options + /// + public static LiveControls LiveControl; + + /// + /// The application exit event + /// + public static System.Threading.ManualResetEvent ApplicationExitEvent; + + /// + /// The webserver instance + /// + public static WebServer WebServer; + + /// + /// An event that is set once the server is ready to respond to requests + /// + public static System.Threading.ManualResetEvent ServerStartedEvent = new System.Threading.ManualResetEvent(false); + + //TODO: These should be persisted to the database + public static bool HasError; + public static bool HasWarning; + + /// + /// The main entry point for the application. + /// Commandline arguments + /// + [STAThread] + public static void Main(string[] args) + { + //If we are on Windows, append the bundled "win-tools" programs to the search path + //We add it last, to allow the user to override with other versions + if (!Library.Utility.Utility.IsClientLinux) + { + Environment.SetEnvironmentVariable("PATH", + Environment.GetEnvironmentVariable("PATH") + + System.IO.Path.PathSeparator.ToString() + + System.IO.Path.Combine( + System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), + "win-tools") + ); + } + + //If we are on windows we encrypt the database by default + //We do not encrypt on Linux as most distros use a SQLite library without encryption support, + //Linux users can use an encrypted home folder, or install a SQLite library with encryption support + if (!Library.Utility.Utility.IsClientLinux && string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DB_KEY_ENV_NAME))) + { + //Note that the password here is a default password and public knowledge + // + //The purpose of this is to prevent casual read of the database, as well + // as protect from harddisk string scans, not to protect from determined + // attacks. + // + //If you desire better security, start Duplicati once with the commandline option + // --unencrypted-database to decrypt the database. + //Then set the environment variable DUPLICATI_DB_KEY to the desired key, + // and run Duplicati again without the --unencrypted-database option + // to re-encrypt it with the new key + // + //If you change the key, please note that you need to supply the same + // key when restoring the setup, as the setup being backed up will + // be encrypted as well. + Environment.SetEnvironmentVariable(DB_KEY_ENV_NAME, ApplicationName + "_Key_42"); + } + + + //Find commandline options here for handling special startup cases + Dictionary commandlineOptions = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(new List(args)); + + foreach (string s in args) + if ( + s.Equals("help", StringComparison.InvariantCultureIgnoreCase) || + s.Equals("/help", StringComparison.InvariantCultureIgnoreCase) || + s.Equals("usage", StringComparison.InvariantCultureIgnoreCase) || + s.Equals("/usage", StringComparison.InvariantCultureIgnoreCase)) + commandlineOptions["help"] = ""; + + //If the commandline issues --help, just stop here + if (commandlineOptions.ContainsKey("help")) + { + Console.WriteLine(Strings.Program.HelpDisplayDialog); + + foreach (Library.Interface.ICommandLineArgument arg in SupportedCommands) + Console.WriteLine(Strings.Program.HelpDisplayFormat, arg.Name, arg.LongDescription); + + return; + } + +#if DEBUG + //Log various information in the logfile + if (!commandlineOptions.ContainsKey("log-file")) + { + commandlineOptions["log-file"] = System.IO.Path.Combine(StartupPath, "Duplicati.debug.log"); + commandlineOptions["log-level"] = Duplicati.Library.Logging.LogMessageType.Profiling.ToString(); + } +#endif + + if (commandlineOptions.ContainsKey("log-level")) + foreach (string s in Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType))) + if (s.Equals(commandlineOptions["log-level"].Trim(), StringComparison.InvariantCultureIgnoreCase)) + Duplicati.Library.Logging.Log.LogLevel = (Duplicati.Library.Logging.LogMessageType)Enum.Parse(typeof(Duplicati.Library.Logging.LogMessageType), s); + + if (commandlineOptions.ContainsKey("log-file")) + { + if (System.IO.File.Exists(commandlineOptions["log-file"])) + System.IO.File.Delete(commandlineOptions["log-file"]); + Duplicati.Library.Logging.Log.CurrentLog = new Duplicati.Library.Logging.StreamLog(commandlineOptions["log-file"]); + } + + //Set the %DUPLICATI_HOME% env variable, if it is not already set + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DATAFOLDER_ENV_NAME))) + { +#if DEBUG + //debug mode uses a lock file located in the app folder + Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); +#else + bool portableMode = commandlineOptions.ContainsKey("portable-mode") ? Library.Utility.Utility.ParseBool(commandlineOptions["portable-mode"], true) : false; + + if (portableMode) + { + //Portable mode uses a data folder in the application home dir + Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.Combine(StartupPath, "data")); + } + else + { + //Normal release mode uses the systems "Application Data" folder + Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ApplicationName)); + } +#endif + } + + SingleInstance instance = null; + + try + { + try + { + //This will also create Program.DATAFOLDER if it does not exist + instance = new SingleInstance(ApplicationName, Program.DATAFOLDER); + } + catch (Exception ex) + { + Console.WriteLine(Strings.Program.StartupFailure, ex.ToString()); + return; + } + + if (!instance.IsFirstInstance) + { + Console.WriteLine(Strings.Program.AnotherInstanceDetected); + return; + } + + Version sqliteVersion = new Version((string)SQLiteLoader.SQLiteConnectionType.GetProperty("SQLiteVersion").GetValue(null, null)); + if (sqliteVersion < new Version(3, 6, 3)) + { + //The official Mono SQLite provider is also broken with less than 3.6.3 + Console.WriteLine(Strings.Program.WrongSQLiteVersion, sqliteVersion, "3.6.3"); + return; + } + + //Create the connection instance + System.Data.IDbConnection con = (System.Data.IDbConnection)Activator.CreateInstance(SQLiteLoader.SQLiteConnectionType); + + try + { + DatabasePath = System.IO.Path.Combine(Program.DATAFOLDER, "Duplicati.sqlite"); + if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(DatabasePath))) + System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(DatabasePath)); + +#if DEBUG + //Default is to not use encryption for debugging + Program.UseDatabaseEncryption = commandlineOptions.ContainsKey("unencrypted-database") ? !Library.Utility.Utility.ParseBool(commandlineOptions["unencrypted-database"], true) : false; +#else + Program.UseDatabaseEncryption = commandlineOptions.ContainsKey("unencrypted-database") ? !Library.Utility.Utility.ParseBool(commandlineOptions["unencrypted-database"], true) : true; +#endif + con.ConnectionString = "Data Source=" + DatabasePath; + + //Attempt to open the database, handling any encryption present + OpenDatabase(con); + + DatabaseUpgrader.UpgradeDatabase(con, DatabasePath); + } + catch (Exception ex) + { + //Unwrap the reflection exceptions + if (ex is System.Reflection.TargetInvocationException && ex.InnerException != null) + ex = ex.InnerException; + + Console.WriteLine(Strings.Program.DatabaseOpenError, ex.Message); + return; + } + + DataConnection = new DataFetcherWithRelations(new SQLiteDataProvider(con)); + + ApplicationExitEvent = new System.Threading.ManualResetEvent(false); + + LiveControl = new LiveControls(new Duplicati.Datamodel.ApplicationSettings(DataConnection)); + LiveControl.StateChanged += new EventHandler(LiveControl_StateChanged); + LiveControl.ThreadPriorityChanged += new EventHandler(LiveControl_ThreadPriorityChanged); + LiveControl.ThrottleSpeedChanged += new EventHandler(LiveControl_ThrottleSpeedChanged); + + Runner = new DuplicatiRunner(); + WorkThread = new Duplicati.Library.Utility.WorkerThread(new Duplicati.Library.Utility.WorkerThread.ProcessItemDelegate(Runner.ExecuteTask), LiveControl.State == LiveControls.LiveControlState.Paused); + Scheduler = new Scheduler(DataConnection, WorkThread, MainLock); + + /*WorkThread.StartingWork += new EventHandler(Events.WorkThread_StartingWork); + WorkThread.CompletedWork += new EventHandler(Events.WorkThread_CompletedWork); + WorkThread.WorkQueueChanged += new EventHandler(Events.WorkThread_WorkQueueChanged); + Scheduler.NewSchedule += new EventHandler(Events.Scheduler_NewSchedule); + Runner.ProgressEvent += new DuplicatiRunner.ProgressEventDelegate(Events.Runner_DuplicatiProgress); + DataConnection.AfterDataConnection += new System.Data.LightDatamodel.DataConnectionEventHandler(Events.DataConnection_AfterDataConnection); + + LiveControl.StateChanged += new EventHandler(Events.LiveControl_StateChanged); + LiveControl.ThreadPriorityChanged += new EventHandler(Events.LiveControl_ThreadPriorityChanged); + LiveControl.ThrottleSpeedChanged += new EventHandler(Events.LiveControl_ThrottleSpeedChanged);*/ + + + Program.WebServer = new Server.WebServer(8080); + + DataConnection.AfterDataConnection += new DataConnectionEventHandler(DataConnection_AfterDataConnection); + + ServerStartedEvent.Set(); + ApplicationExitEvent.WaitOne(); + } + catch (Exception ex) + { + Console.WriteLine(Strings.Program.SeriousError, ex.ToString()); + } + + + if (Scheduler != null) + Scheduler.Terminate(true); + if (WorkThread != null) + WorkThread.Terminate(true); + if (instance != null) + instance.Dispose(); + +#if DEBUG + //Flush the file + using (Duplicati.Library.Logging.Log.CurrentLog as Duplicati.Library.Logging.StreamLog) + Duplicati.Library.Logging.Log.CurrentLog = null; +#endif + } + + /// + /// Handles a change in the LiveControl and updates the Runner + /// + /// + /// + private static void LiveControl_ThreadPriorityChanged(object sender, EventArgs e) + { + if (LiveControl.ThreadPriority == null) + Runner.UnsetThreadPriority(); + else + Runner.SetThreadPriority(LiveControl.ThreadPriority.Value); + } + + /// + /// Handles a change in the LiveControl and updates the Runner + /// + /// + /// + private static void LiveControl_ThrottleSpeedChanged(object sender, EventArgs e) + { + if (LiveControl.DownloadLimit == null) + Runner.SetDownloadLimit(null); + else + Runner.SetDownloadLimit(LiveControl.DownloadLimit.Value.ToString() + "b"); + + if (LiveControl.UploadLimit == null) + Runner.SetUploadLimit(null); + else + Runner.SetUploadLimit(LiveControl.UploadLimit.Value.ToString() + "b"); + } + + /// + /// This event handler updates the trayicon menu with the current state of the runner. + /// + static void LiveControl_StateChanged(object sender, EventArgs e) + { + switch (LiveControl.State) + { + case LiveControls.LiveControlState.Paused: + WorkThread.Pause(); + Runner.Pause(); + break; + case LiveControls.LiveControlState.Running: + WorkThread.Resume(); + Runner.Resume(); + break; + } + } + + + private static void DataConnection_AfterDataConnection(object sender, DataActions action) + { + if (action == DataActions.Insert || action == DataActions.Update) + Scheduler.Reschedule(); + } + + /// + /// Returns a localized name for a task type + /// + /// + /// + public static string LocalizeTaskType(DuplicityTaskType type) + { + switch (type) + { + case DuplicityTaskType.FullBackup: + return Strings.TaskType.FullBackup; + case DuplicityTaskType.IncrementalBackup: + return Strings.TaskType.IncrementalBackup; + case DuplicityTaskType.ListActualFiles: + return Strings.TaskType.ListActualFiles; + case DuplicityTaskType.ListBackupEntries: + return Strings.TaskType.ListBackupEntries; + case DuplicityTaskType.ListBackups: + return Strings.TaskType.ListBackups; + case DuplicityTaskType.ListFiles: + return Strings.TaskType.ListFiles; + case DuplicityTaskType.RemoveAllButNFull: + return Strings.TaskType.RemoveAllButNFull; + case DuplicityTaskType.RemoveOlderThan: + return Strings.TaskType.RemoveOlderThan; + case DuplicityTaskType.Restore: + return Strings.TaskType.Restore; + case DuplicityTaskType.RestoreSetup: + return Strings.TaskType.RestoreSetup; + default: + return type.ToString(); + } + } + + /// + /// Helper method with logic to handle opening a database in possibly encrypted format + /// + /// The SQLite connection object + internal static void OpenDatabase(System.Data.IDbConnection con) + { + bool noEncryption = !Program.UseDatabaseEncryption; + string password = Environment.GetEnvironmentVariable(DB_KEY_ENV_NAME); + + System.Reflection.MethodInfo setPwdMethod = con.GetType().GetMethod("SetPassword", new Type[] { typeof(string) }); + string attemptedPassword; + + if (noEncryption || string.IsNullOrEmpty(password)) + attemptedPassword = null; //No encryption specified, attempt to open without + else + attemptedPassword = password; //Encryption specified, attempt to open with + + if (setPwdMethod != null) + setPwdMethod.Invoke(con, new object[] { attemptedPassword }); + + try + { + //Attempt to open in preferred state + con.Open(); + } + catch + { + try + { + //We can't try anything else without a password + if (string.IsNullOrEmpty(password)) + throw; + + //Open failed, now try the reverse + if (attemptedPassword == null) + attemptedPassword = password; + else + attemptedPassword = null; + + setPwdMethod.Invoke(con, new object[] { attemptedPassword }); + con.Open(); + } + catch + { + } + + //If the db is not open now, it won't open + if (con.State != System.Data.ConnectionState.Open) + throw; //Report original error + + //The open method succeeded with the non-default method, now change the password + System.Reflection.MethodInfo changePwdMethod = con.GetType().GetMethod("ChangePassword", new Type[] { typeof(string) }); + changePwdMethod.Invoke(con, new object[] { noEncryption ? null : password }); + } + } + + /// + /// Gets a list of all supported commandline options + /// + private static Library.Interface.ICommandLineArgument[] SupportedCommands + { + get + { + return new Duplicati.Library.Interface.ICommandLineArgument[] { + new Duplicati.Library.Interface.CommandLineArgument("help", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.HelpCommandDescription, Strings.Program.HelpCommandDescription), + new Duplicati.Library.Interface.CommandLineArgument("unencrypted-database", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.UnencrypteddatabaseCommandDescription, Strings.Program.UnencrypteddatabaseCommandDescription), + new Duplicati.Library.Interface.CommandLineArgument("portable-mode", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.PortablemodeCommandDescription, Strings.Program.PortablemodeCommandDescription), + new Duplicati.Library.Interface.CommandLineArgument("log-file", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Path, Strings.Program.LogfileCommandDescription, Strings.Program.LogfileCommandDescription), + new Duplicati.Library.Interface.CommandLineArgument("log-level", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Enumeration, Strings.Program.LoglevelCommandDescription, Strings.Program.LoglevelCommandDescription, "Warning", null, Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType))) + }; + } + } + } +} diff --git a/Duplicati/Server/Properties/AssemblyInfo.cs b/Duplicati/Server/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f9d6b89c5 --- /dev/null +++ b/Duplicati/Server/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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.Server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Duplicati.Server")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("94819ef2-1899-420c-8a91-77cde270c53e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Duplicati/Server/SQLite/pinvoke/System.Data.SQLite.dll b/Duplicati/Server/SQLite/pinvoke/System.Data.SQLite.dll new file mode 100644 index 000000000..ebf200483 Binary files /dev/null and b/Duplicati/Server/SQLite/pinvoke/System.Data.SQLite.dll differ diff --git a/Duplicati/Server/SQLite/win32/System.Data.SQLite.dll b/Duplicati/Server/SQLite/win32/System.Data.SQLite.dll new file mode 100644 index 000000000..080c74cd5 Binary files /dev/null and b/Duplicati/Server/SQLite/win32/System.Data.SQLite.dll differ diff --git a/Duplicati/Server/SQLite/win64/System.Data.SQLite.dll b/Duplicati/Server/SQLite/win64/System.Data.SQLite.dll new file mode 100644 index 000000000..ae73ed68c Binary files /dev/null and b/Duplicati/Server/SQLite/win64/System.Data.SQLite.dll differ diff --git a/Duplicati/Server/SQLiteLoader.cs b/Duplicati/Server/SQLiteLoader.cs new file mode 100644 index 000000000..a1e1dac46 --- /dev/null +++ b/Duplicati/Server/SQLiteLoader.cs @@ -0,0 +1,89 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; + +namespace Duplicati.Server +{ + public static class SQLiteLoader + { + /// + /// A cached copy of the type + /// + private static Type m_type = null; + + /// + /// Returns the SQLiteCommand type for the current architecture + /// + public static Type SQLiteConnectionType + { + get + { + if (m_type == null) + { + string filename = "System.Data.SQLite.dll"; + string basePath = System.IO.Path.Combine(Program.StartupPath, "SQLite"); + + //Default is to use the pinvoke version which requires a native .dll/.so + string assemblyPath = System.IO.Path.Combine(basePath, "pinvoke"); + + //If we run on windows with MS.Net we can use the mixed mode assemblies + if (!Duplicati.Library.Utility.Utility.IsClientLinux && !Duplicati.Library.Utility.Utility.IsMono) + { + //On my x64 system, 32bit appears to work correctly as 32 bit, where loading the 64bit dll fails + if (IntPtr.Size == 8 /*|| (IntPtr.Size == 4 && Is32BitProcessOn64BitProcessor())*/) + { + if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win64"), filename))) + assemblyPath = System.IO.Path.Combine(basePath, "win64"); + } + else + { + if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.Combine(basePath, "win32"), filename))) + assemblyPath = System.IO.Path.Combine(basePath, "win32"); + } + } + + m_type = System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(assemblyPath, filename)).GetType("System.Data.SQLite.SQLiteConnection"); + } + + return m_type; + } + } + + [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true, CallingConvention = System.Runtime.InteropServices.CallingConvention.Winapi)] + [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)] + private static extern bool IsWow64Process([System.Runtime.InteropServices.In] IntPtr hProcess, [System.Runtime.InteropServices.Out] out bool lpSystemInfo); + + private static bool Is32BitProcessOn64BitProcessor() + { + try + { + bool retVal; + IsWow64Process(System.Diagnostics.Process.GetCurrentProcess().Handle, out retVal); + return retVal; + } + catch + { + return false; //In case the OS is old enough not to have the Wow64 function + } + } + } +} diff --git a/Duplicati/Server/Scheduler.cs b/Duplicati/Server/Scheduler.cs new file mode 100644 index 000000000..642987ab0 --- /dev/null +++ b/Duplicati/Server/Scheduler.cs @@ -0,0 +1,275 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using Duplicati.Datamodel; +using System.Data.LightDatamodel; +using Duplicati.Library.Utility; + +namespace Duplicati.Server +{ + /// + /// This class handles scheduled runs of backups + /// + public class Scheduler + { + /// + /// The thread that runs the scheduler + /// + private Thread m_thread; + /// + /// The connection to the database + /// + private IDataFetcherCached m_connection; + /// + /// A termination flag + /// + private volatile bool m_terminate; + /// + /// The worker thread that is invoked to do work + /// + private WorkerThread m_worker; + /// + /// The wait event + /// + private AutoResetEvent m_event; + /// + /// The data syncronization lock + /// + private object m_lock = new object(); + /// + /// The program lock for the database connection + /// + private object m_datalock; + + /// + /// An event that is raised when the schedule changes + /// + public event EventHandler NewSchedule; + + /// + /// The currently scheduled items + /// + private Schedule[] m_schedule; + + /// + /// Constructs a new scheduler + /// + /// The database connection + /// The worker thread + /// The database lock object + public Scheduler(IDataFetcherCached connection, WorkerThread worker, object datalock) + { + m_datalock = datalock; + m_connection = connection; + m_thread = new Thread(new ThreadStart(Runner)); + m_worker = worker; + m_schedule = new Schedule[0]; + m_terminate = false; + m_event = new AutoResetEvent(false); + m_thread.IsBackground = true; + m_thread.Start(); + } + + /// + /// Forces the scheduler to re-evaluate the order. + /// Call this method if something changes + /// + public void Reschedule() + { + m_event.Set(); + } + + /// + /// A copy of the current schedule list + /// + public List Schedule + { + get + { + lock (m_lock) + return new List(m_schedule); + } + } + + /// + /// Terminates the thread. Any items still in queue will be removed + /// + /// True if the call should block until the thread has exited, false otherwise + public void Terminate(bool wait) + { + m_terminate = true; + m_event.Set(); + + if (wait) + m_thread.Join(); + } + + /// + /// Returns the next valid date, given the start and the interval + /// + /// The starting time + /// The repetition interval + /// The days the backup is allowed to run + /// The next valid date, or throws an exception if no such date can be found + public static DateTime GetNextValidTime(DateTime start, string repetition, DayOfWeek[] allowedDays) + { + DateTime res = start; + + int i = 50000; + + while (!IsDateAllowed(res, allowedDays) && i-- > 0) + res = Timeparser.ParseTimeInterval(repetition, res); + + if (!IsDateAllowed(res, allowedDays)) + { + StringBuilder sb = new StringBuilder(); + if (allowedDays != null) + foreach (DayOfWeek w in allowedDays) + { + if (sb.Length != 0) + sb.Append(", "); + sb.Append(w.ToString()); + } + + throw new Exception(string.Format(Strings.Scheduler.InvalidTimeSetupError, start, repetition, sb.ToString())); + } + + return res; + } + + /// + /// The actual scheduling procedure + /// + private void Runner() + { + while (!m_terminate) + { + List reps = new List(); + List tmp; + lock (m_datalock) + tmp = new List(m_connection.GetObjects()); + + //Determine schedule list + foreach (Schedule sc in tmp) + { + if (!string.IsNullOrEmpty(sc.Repeat)) + { + DateTime start = sc.NextScheduledTime; + + try { start = GetNextValidTime(sc.NextScheduledTime, sc.Repeat, sc.AllowedWeekdays); } + catch { } //TODO: Report this somehow + + //If time is exceeded, run it now + if (start <= DateTime.Now) + { + //See if it is already queued + List tmplst = m_worker.CurrentTasks; + IDuplicityTask tastTemp = m_worker.CurrentTask; + if (tastTemp != null) + tmplst.Add(tastTemp); + + bool found = false; + foreach (IDuplicityTask t in tmplst) + if (t != null && t is IncrementalBackupTask && ((IncrementalBackupTask)t).Schedule.ID == sc.ID) + { + found = true; + break; + } + + //If it is not already in queue, put it there + if (!found) + m_worker.AddTask(new IncrementalBackupTask(sc)); + + //Caluclate next time, by adding the interval to the start until we have + // passed the current date and time + //TODO: Make this more efficient + int i = 50000; + while (start <= DateTime.Now && i-- > 0) + try { start = GetNextValidTime(Timeparser.ParseTimeInterval(sc.Repeat, start), sc.Repeat, sc.AllowedWeekdays); } + catch + { + //TODO: Report this somehow + continue; + } + + if (start < DateTime.Now) + continue; + } + + //Add to schedule list at the new time + reps.Add(sc); + sc.NextScheduledTime = start; + } + } + + System.Data.LightDatamodel.QueryModel.OperationOrParameter op = System.Data.LightDatamodel.QueryModel.Parser.ParseQuery("ORDER BY When ASC"); + + //Sort them, lock as we assign the m_schedule variable + lock(m_lock) + m_schedule = op.EvaluateList(reps).ToArray(); + + //Raise event if needed + if (NewSchedule != null) + NewSchedule(this, null); + + int waittime = 0; + + //Figure out a sensible amount of time to sleep the thread + if (m_schedule.Length > 0) + { + //When is the next run scheduled? + TimeSpan nextrun = m_schedule[0].NextScheduledTime - DateTime.Now; + if (nextrun.TotalMilliseconds < 0) + continue; + + //Don't sleep for more than 5 minutes + waittime = (int)Math.Min(nextrun.TotalMilliseconds, 60 * 1000 * 5); + } + else + { + //No tasks, check back later + waittime = 60 * 1000; + } + + //Waiting on the event, enables a wakeup call from termination + // never use waittime = 0 + m_event.WaitOne(Math.Max(100, waittime), false); + } + } + + /// + /// Returns true if the time is at an allowed weekday, false otherwise + /// + /// The time to evaluate + /// The allowed days + /// True if the backup is allowed to run, false otherwise + private static bool IsDateAllowed(DateTime time, DayOfWeek[] allowedDays) + { + if (allowedDays == null || allowedDays.Length == 0) + return true; + else + return Array.IndexOf(allowedDays, time.DayOfWeek) >= 0; + } + + } +} diff --git a/Duplicati/Server/SerializableStatus.cs b/Duplicati/Server/SerializableStatus.cs new file mode 100644 index 000000000..227a9dab9 --- /dev/null +++ b/Duplicati/Server/SerializableStatus.cs @@ -0,0 +1,95 @@ +#region "Disclaimer / License" +// Copyright (C) 2011, Kenneth Skovhede + +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#endregion +using System; +using System.Linq; +using System.Collections.Generic; +using Duplicati.Server.Serialization; + +namespace Duplicati.Server +{ + /// + /// This class collects all reportable status properties into a single class that can be exported as JSON + /// + public class SerializableStatus : ISerializableStatus + { + public LiveControlState ProgramState + { + get { return EnumConverter.Convert(Program.LiveControl.State); } + } + + public RunnerState ActiveBackupState + { + get { return Program.Runner.CurrentState; } + } + + public long ActiveScheduleId + { + get + { + IDuplicityTask t = Program.WorkThread.CurrentTask; + if (t == null || t.Schedule == null) + return -1; + else + return t.Schedule.ID; + + } + } + + public IList SchedulerQueueIds + { + get { return Program.Scheduler.Schedule.Select(x => x.ID).ToList(); } + } + + public IProgressEventData RunningBackupStatus + { + get { return Program.Runner.LastEvent; } + } + + public bool HasWarning { get { return Program.HasWarning; } } + public bool HasError { get { return Program.HasError; } } + + public SuggestedStatusIcon SuggestedStatusIcon + { + get + { + if (this.ActiveScheduleId < 0) + { + if (this.ProgramState == LiveControlState.Paused) + return SuggestedStatusIcon.Paused; + + if (this.HasError) + return SuggestedStatusIcon.ReadyError; + if (this.HasWarning) + return SuggestedStatusIcon.ReadyWarning; + + return SuggestedStatusIcon.Ready; + } + else + { + if (this.ProgramState == LiveControlState.Running) + return SuggestedStatusIcon.Active; + else + return SuggestedStatusIcon.ActivePaused; + } + } + } + } +} + diff --git a/Duplicati/Server/SingleInstance.cs b/Duplicati/Server/SingleInstance.cs new file mode 100644 index 000000000..86818201f --- /dev/null +++ b/Duplicati/Server/SingleInstance.cs @@ -0,0 +1,305 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; + +namespace Duplicati.Server +{ + /// + /// The purpose of this class is to ensure that there is ever only one instance of the application running. + /// This class is based on file-locking, making it much more cross-platform portable than other versions, + /// that depend on memory communication, such as pipes or shared memory. + /// + public class SingleInstance : IDisposable + { + /// + /// The folder where control files are placed + /// + private const string CONTROL_DIR = "control_dir"; + /// + /// The file that is locked by the first process + /// + private const string CONTROL_FILE = "lock"; + /// + /// The prefix on files that communicate with the first instance + /// + private const string COMM_FILE_PREFIX = "other_invocation_"; + + /// + /// The delegate that is used to inform the first instance of the second invocation + /// + /// The commandlinearguments for the second invocation + public delegate void SecondInstanceDelegate(string[] commandlineargs); + + /// + /// When the user tries to launch the application the second time, this event is raised + /// + public event SecondInstanceDelegate SecondInstanceDetected; + + /// + /// The file that is locked to prevent other access + /// + private IDisposable m_file; + + /// + /// The folder where control files are placed + /// + private string m_controldir; + + /// + /// The full path to the locking file + /// + private string m_lockfilename; + + /// + /// The watcher that allows interprocess communication + /// + private System.IO.FileSystemWatcher m_filewatcher; + + /// + /// Gets a value indicating if this is the first instance of the application + /// + public bool IsFirstInstance { get { return m_file != null; } } + + /// + /// Constructs a new SingleInstance object + /// + /// The application name + public SingleInstance(string appname) + : this(appname, System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), appname)) + { } + + /// + /// Constructs a new SingleInstance object + /// + /// The application name + /// The folder in which the control file structure is placed + public SingleInstance(string appname, string basefolder) + { + if (!System.IO.Directory.Exists(basefolder)) + System.IO.Directory.CreateDirectory(basefolder); + + m_controldir = System.IO.Path.Combine(basefolder, CONTROL_DIR); + if (!System.IO.Directory.Exists(m_controldir)) + System.IO.Directory.CreateDirectory(m_controldir); + + m_lockfilename = System.IO.Path.Combine(m_controldir, CONTROL_FILE); + m_file = null; + + System.IO.Stream temp_fs = null; + + try + { + if (Library.Utility.Utility.IsClientLinux) + temp_fs = UnixSupport.File.OpenExclusive(m_lockfilename, System.IO.FileAccess.Write); + else + temp_fs = System.IO.File.Open(m_lockfilename, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None); + + if (temp_fs != null) + { + System.IO.StreamWriter sw = new System.IO.StreamWriter(temp_fs); + sw.WriteLine(System.Diagnostics.Process.GetCurrentProcess().Id); + sw.Flush(); + //Do not dispose sw as that would dispose the stream + m_file = temp_fs; + } + } + catch + { + if (temp_fs != null) + try { temp_fs.Dispose(); } + catch {} + } + + //If we have write access + if (m_file != null) + { + m_filewatcher = new System.IO.FileSystemWatcher(m_controldir); + m_filewatcher.Created += new System.IO.FileSystemEventHandler(m_filewatcher_Created); + m_filewatcher.EnableRaisingEvents = true; + + DateTime startup = System.IO.File.GetLastWriteTime(m_lockfilename); + + //Clean up any files that were created before the app launched + foreach(string s in System.IO.Directory.GetFiles(m_controldir)) + if (s != m_lockfilename && System.IO.File.GetCreationTime(s) < startup) + try { System.IO.File.Delete(s); } + catch { } + } + else + { + //Wait for the initial process to signal that the filewatcher is activated + int retrycount = 5; + while (retrycount > 0 && new System.IO.FileInfo(m_lockfilename).Length == 0) + { + System.Threading.Thread.Sleep(500); + retrycount--; + } + + //HACK: the unix file lock does not allow us to read the file length when the file is locked + if (new System.IO.FileInfo(m_lockfilename).Length == 0) + if (!Library.Utility.Utility.IsClientLinux) + throw new Exception("The file was locked, but had no data"); + + //Notify the other process that we have started + string filename = System.IO.Path.Combine(m_controldir, COMM_FILE_PREFIX + Guid.NewGuid().ToString()); + + //Write out the commandline arguments + string[] cmdargs = System.Environment.GetCommandLineArgs(); + using (System.IO.StreamWriter sw = new System.IO.StreamWriter(Library.Utility.Utility.IsClientLinux ? UnixSupport.File.OpenExclusive(filename, System.IO.FileAccess.Write) : new System.IO.FileStream(filename, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write, System.IO.FileShare.None))) + for (int i = 1; i < cmdargs.Length; i++) //Skip the first, as that is the filename + sw.WriteLine(cmdargs[i]); + + //Wait for the other process to delete the file, indicating that it is processed + retrycount = 5; + while (retrycount > 0 && System.IO.File.Exists(filename)) + { + System.Threading.Thread.Sleep(500); + retrycount--; + } + + //This may happen if the other process is closing as we write the command + if (System.IO.File.Exists(filename)) + { + //Try to clean up, so the other process does not spuriously show this + try { System.IO.File.Delete(filename); } + catch { } + + throw new Exception("The lock file was locked, but the locking process did not respond to the start command"); + } + } + } + + /// + /// The event that is raised when a new file is created in the control dir + /// + /// The event sender + /// The file event arguments + private void m_filewatcher_Created(object sender, System.IO.FileSystemEventArgs e) + { + //Retry 5 times if the other process is slow on releasing the file lock + int retrycount = 5; + //Indicator and holder of arguments passed + string[] commandline = null; + + //HACK: Linux has some locking issues + //The problem is that there is no atomic open-and-lock operation, so the other process + // needs a little time to create+lock the file. This is not really a fix, but an + // ugly workaround. This functionality is only used to allow a new instance to signal + // the running instance, so errors here would only affect that functionality + if (Library.Utility.Utility.IsClientLinux) + System.Threading.Thread.Sleep(1000); + + do + { + try + { + //If the other process deleted the file, just quit + if (!System.IO.File.Exists(e.FullPath)) + return; + + List args = new List(); + using (System.IO.StreamReader sr = new System.IO.StreamReader(Duplicati.Library.Utility.Utility.IsClientLinux ? UnixSupport.File.OpenExclusive(e.FullPath, System.IO.FileAccess.ReadWrite) : new System.IO.FileStream(e.FullPath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.None))) + while(!sr.EndOfStream) + args.Add(sr.ReadLine()); + + commandline = args.ToArray(); + + //Remove the file to notify the other process that we have correctly processed the call + System.IO.File.Delete(e.FullPath); + } + catch + { + } + + //If file-reading failed, wait a little before retry + if (commandline == null) + { + System.Threading.Thread.Sleep(500); + retrycount--; + } + + } while (retrycount > 0 && commandline == null); + + //If this happens, we detected the file, but was unable to read it's contents + if (commandline == null) + { + //There is nothing we can do :( + } + else + { + //If we read the data but did not delete the file, the other end still hangs + //and waits for us to clean up, so try again. + retrycount = 5; + while (retrycount > 0 && System.IO.File.Exists(e.FullPath)) + { + try + { + System.IO.File.Delete(e.FullPath); + } + catch + { + //Wait before the retry + System.Threading.Thread.Sleep(500); + } + + retrycount--; + } + + //If this happens, the other process will give an error message + if (System.IO.File.Exists(e.FullPath)) + { + //There is nothing we can do :( + } + + //Finally inform this instance about the call + if (SecondInstanceDetected != null) + SecondInstanceDetected(commandline); + } + + } + + #region IDisposable Members + + public void Dispose() + { + if (m_filewatcher != null) + { + m_filewatcher.EnableRaisingEvents = false; + m_filewatcher.Created -= new System.IO.FileSystemEventHandler(m_filewatcher_Created); + m_filewatcher.Dispose(); + m_filewatcher = null; + } + + if (m_file != null) + { + m_file.Dispose(); + + try { System.IO.File.Delete(m_lockfilename); } + catch { } + + m_file = null; + } + } + + #endregion + } +} diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHCommonOptions.Designer.cs b/Duplicati/Server/Strings/DatabaseUpgrader.Designer.cs similarity index 56% rename from Duplicati/Library/Backend/SSH/Strings/SSHCommonOptions.Designer.cs rename to Duplicati/Server/Strings/DatabaseUpgrader.Designer.cs index e42fc42de..82b5dc24d 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SSHCommonOptions.Designer.cs +++ b/Duplicati/Server/Strings/DatabaseUpgrader.Designer.cs @@ -1,14 +1,14 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.225 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace Duplicati.Library.Backend.Strings { +namespace Duplicati.Server.Strings { using System; @@ -22,14 +22,14 @@ namespace Duplicati.Library.Backend.Strings { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class SSHCommonOptions { + internal class DatabaseUpgrader { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal SSHCommonOptions() { + internal DatabaseUpgrader() { } /// @@ -39,7 +39,7 @@ namespace Duplicati.Library.Backend.Strings { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Library.Backend.Strings.SSHCommonOptions", typeof(SSHCommonOptions).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Server.Strings.DatabaseUpgrader", typeof(DatabaseUpgrader).Assembly); resourceMan = temp; } return resourceMan; @@ -61,63 +61,53 @@ namespace Duplicati.Library.Backend.Strings { } /// - /// Looks up a localized string similar to An error occured while setting up the SSH dialog: {0}. + /// Looks up a localized string similar to backup. /// - internal static string LoadError { + internal static string BackupFilenamePrefix { get { - return ResourceManager.GetString("LoadError", resourceCulture); + return ResourceManager.GetString("BackupFilenamePrefix", resourceCulture); } } /// - /// Looks up a localized string similar to The entered SFTP program could not be found. - ///Do you want to use the path anyway?. + /// Looks up a localized string similar to Unable to determine database format: {0}. /// - internal static string LocatingSftpError { + internal static string DatabaseFormatError { get { - return ResourceManager.GetString("LocatingSftpError", resourceCulture); + return ResourceManager.GetString("DatabaseFormatError", resourceCulture); } } /// - /// Looks up a localized string similar to Leave empty to use the sftp application found: {0}. - /// - internal static string SftpAutodetectedInfo { - get { - return ResourceManager.GetString("SftpAutodetectedInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The sftp application could not be located, please enter the path manually. - /// - internal static string SftpNotAutodetectedWarning { - get { - return ResourceManager.GetString("SftpNotAutodetectedWarning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An error occured while attempting to verify the sftp program path: - ///{0} + /// Looks up a localized string similar to + ///The database has version {0} but the largest supported version is {1}. /// - ///Do you want to use the path anyway?. + ///This is likely caused by upgrading to a newer version and then downgrading. + ///If this is the case, there is likely a backup file of the previous database version in the folder {2}.. /// - internal static string SftpProgramNotFoundError { + internal static string InvalidVersionError { get { - return ResourceManager.GetString("SftpProgramNotFoundError", resourceCulture); + return ResourceManager.GetString("InvalidVersionError", resourceCulture); } } /// - /// Looks up a localized string similar to You have not entered a path to the SFTP application. - ///This will use the default path to the SFTP application. - ///The SFTP application could not be found during system probing. - ///Are you sure this is what you want?. + /// Looks up a localized string similar to Unknown table layout detected. /// - internal static string UseEmptyPathWarning { + internal static string TableLayoutError { get { - return ResourceManager.GetString("UseEmptyPathWarning", resourceCulture); + return ResourceManager.GetString("TableLayoutError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to execute SQL: {0} + ///Error: {1} + ///Database is NOT upgraded.. + /// + internal static string UpgradeFailure { + get { + return ResourceManager.GetString("UpgradeFailure", resourceCulture); } } } diff --git a/Duplicati/Server/Strings/DatabaseUpgrader.resx b/Duplicati/Server/Strings/DatabaseUpgrader.resx new file mode 100644 index 000000000..9a48a0514 --- /dev/null +++ b/Duplicati/Server/Strings/DatabaseUpgrader.resx @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + backup + A string prefixed to the name of the database when making a backup + + + Unable to determine database format: {0} + An error message that is displayed if an error occurs while determining database layout + + + +The database has version {0} but the largest supported version is {1}. + +This is likely caused by upgrading to a newer version and then downgrading. +If this is the case, there is likely a backup file of the previous database version in the folder {2}. + An error message that is displayed if the database version number does not match + + + Unknown table layout detected + An error message that is displayed when the layout of the database is unexpected + + + Failed to execute SQL: {0} +Error: {1} +Database is NOT upgraded. + An error message displayed if one of the upgrade SQL statements fail + + \ No newline at end of file diff --git a/Duplicati/Server/Strings/DuplicatiRunner.Designer.cs b/Duplicati/Server/Strings/DuplicatiRunner.Designer.cs new file mode 100644 index 000000000..5e8a1a502 --- /dev/null +++ b/Duplicati/Server/Strings/DuplicatiRunner.Designer.cs @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Duplicati.Server.Strings { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DuplicatiRunner { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DuplicatiRunner() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.GUI.Strings.DuplicatiRunner", typeof(DuplicatiRunner).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The backup was stopped because the application was closed. + /// + internal static string ApplicationExitLogMesssage { + get { + return ResourceManager.GetString("ApplicationExitLogMesssage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Removing old backups .... + /// + internal static string CleaningUpMessage { + get { + return ResourceManager.GetString("CleaningUpMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed during cleanup: {0}. + /// + internal static string CleanupError { + get { + return ResourceManager.GetString("CleanupError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cleanup output:. + /// + internal static string CleanupLogdataHeader { + get { + return ResourceManager.GetString("CleanupLogdataHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error: {0}. + /// + internal static string ErrorMessage { + get { + return ResourceManager.GetString("ErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The backup was stopped, the reason was: {0}. + /// + internal static string OtherAbortMessage { + get { + return ResourceManager.GetString("OtherAbortMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicati was terminated before the backup completed, status is unknown. + /// + internal static string ShutdownWhileBackupInprogress { + get { + return ResourceManager.GetString("ShutdownWhileBackupInprogress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The backup was stopped because the application was closed by the task manager. + /// + internal static string TaskManagerCloseMessage { + get { + return ResourceManager.GetString("TaskManagerCloseMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The backup was stopped by the user. + /// + internal static string UserClosingMessage { + get { + return ResourceManager.GetString("UserClosingMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The backup was stopped because the machine was being shut down. + /// + internal static string WindowsShutdownMessage { + get { + return ResourceManager.GetString("WindowsShutdownMessage", resourceCulture); + } + } + } +} diff --git a/Duplicati/Server/Strings/DuplicatiRunner.resx b/Duplicati/Server/Strings/DuplicatiRunner.resx new file mode 100644 index 000000000..95bc4fe72 --- /dev/null +++ b/Duplicati/Server/Strings/DuplicatiRunner.resx @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The backup was stopped because the application was closed + + + Removing old backups ... + + + Failed during cleanup: {0} + + + Cleanup output: + + + Error: {0} + + + The backup was stopped, the reason was: {0} + + + Duplicati was terminated before the backup completed, status is unknown + + + The backup was stopped because the application was closed by the task manager + + + The backup was stopped by the user + + + The backup was stopped because the machine was being shut down + + \ No newline at end of file diff --git a/Duplicati/Library/Backend/SSH/Strings/SharpExpectProcess.Designer.cs b/Duplicati/Server/Strings/DynamicSetupHelper.Designer.cs similarity index 68% rename from Duplicati/Library/Backend/SSH/Strings/SharpExpectProcess.Designer.cs rename to Duplicati/Server/Strings/DynamicSetupHelper.Designer.cs index f84d6e3c4..86687bc5c 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SharpExpectProcess.Designer.cs +++ b/Duplicati/Server/Strings/DynamicSetupHelper.Designer.cs @@ -1,14 +1,14 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.225 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace Duplicati.Library.Backend.Strings { +namespace Duplicati.Server.Strings { using System; @@ -22,14 +22,14 @@ namespace Duplicati.Library.Backend.Strings { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class SharpExpectProcess { + internal class DynamicSetupHelper { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal SharpExpectProcess() { + internal DynamicSetupHelper() { } /// @@ -39,7 +39,7 @@ namespace Duplicati.Library.Backend.Strings { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Library.Backend.Strings.SharpExpectProcess", typeof(SharpExpectProcess).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Server.Strings.DynamicSetupHelper", typeof(DynamicSetupHelper).Assembly); resourceMan = temp; } return resourceMan; @@ -61,29 +61,11 @@ namespace Duplicati.Library.Backend.Strings { } /// - /// Looks up a localized string similar to Log was disabled, no output. + /// Looks up a localized string similar to No folders are included in the backup. /// - internal static string LogDisabled { + internal static string NoFoldersInSetupError { get { - return ResourceManager.GetString("LogDisabled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to *password*. - /// - internal static string PasswordMarker { - get { - return ResourceManager.GetString("PasswordMarker", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Can't write after process has exited: {0}. - /// - internal static string WriteAfterExitError { - get { - return ResourceManager.GetString("WriteAfterExitError", resourceCulture); + return ResourceManager.GetString("NoFoldersInSetupError", resourceCulture); } } } diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHSettingsControl.resx b/Duplicati/Server/Strings/DynamicSetupHelper.resx similarity index 94% rename from Duplicati/Library/Backend/SSH/Strings/SSHSettingsControl.resx rename to Duplicati/Server/Strings/DynamicSetupHelper.resx index 16d4532fe..2bbfb81f8 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SSHSettingsControl.resx +++ b/Duplicati/Server/Strings/DynamicSetupHelper.resx @@ -117,10 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Settings for the SSH backend - - - SSH Settings + + No folders are included in the backup \ No newline at end of file diff --git a/Duplicati/Server/Strings/Program.Designer.cs b/Duplicati/Server/Strings/Program.Designer.cs new file mode 100644 index 000000000..3c3742961 --- /dev/null +++ b/Duplicati/Server/Strings/Program.Designer.cs @@ -0,0 +1,193 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.225 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Duplicati.Server.Strings { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Program { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Program() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Server.Strings.Program", typeof(Program).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Another instance is running, and was notified. + /// + internal static string AnotherInstanceDetected { + get { + return ResourceManager.GetString("AnotherInstanceDetected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to create, open or upgrade the database. + ///Error message: {0}. + /// + internal static string DatabaseOpenError { + get { + return ResourceManager.GetString("DatabaseOpenError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Displays this help. + /// + internal static string HelpCommandDescription { + get { + return ResourceManager.GetString("HelpCommandDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supported commandline arguments: + /// + ///{0}. + /// + internal static string HelpDisplayDialog { + get { + return ResourceManager.GetString("HelpDisplayDialog", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to --{0}: {1}. + /// + internal static string HelpDisplayFormat { + get { + return ResourceManager.GetString("HelpDisplayFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to set the user interface language: {0}. + /// + internal static string LanguageSelectionError { + get { + return ResourceManager.GetString("LanguageSelectionError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Outputs log information to the file given. + /// + internal static string LogfileCommandDescription { + get { + return ResourceManager.GetString("LogfileCommandDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Determines the amount of information written in the log file. + /// + internal static string LoglevelCommandDescription { + get { + return ResourceManager.GetString("LoglevelCommandDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Activates portable mode where the database is placed below the program executable. + /// + internal static string PortablemodeCommandDescription { + get { + return ResourceManager.GetString("PortablemodeCommandDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A serious error occured in Duplicati: {0}. + /// + internal static string SeriousError { + get { + return ResourceManager.GetString("SeriousError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Displays the status window. + /// + internal static string ShowstausCommandDescription { + get { + return ResourceManager.GetString("ShowstausCommandDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to start up, perhaps another process is already running? + ///Error message: {0}. + /// + internal static string StartupFailure { + get { + return ResourceManager.GetString("StartupFailure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disables database encryption. + /// + internal static string UnencrypteddatabaseCommandDescription { + get { + return ResourceManager.GetString("UnencrypteddatabaseCommandDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsupported version of SQLite detected ({0}), must be {1} or higher. + /// + internal static string WrongSQLiteVersion { + get { + return ResourceManager.GetString("WrongSQLiteVersion", resourceCulture); + } + } + } +} diff --git a/Duplicati/Server/Strings/Program.resx b/Duplicati/Server/Strings/Program.resx new file mode 100644 index 000000000..8a7522ece --- /dev/null +++ b/Duplicati/Server/Strings/Program.resx @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Another instance is running, and was notified + A message displayed in the console when activating Duplicati and it is already running + + + Failed to create, open or upgrade the database. +Error message: {0} + An error message displayed when opening the database fails + + + Displays this help + + + Supported commandline arguments: + +{0} + + + --{0}: {1} + + + Failed to set the user interface language: {0} + An error message displayed if the language cannot be selected + + + Outputs log information to the file given + + + Determines the amount of information written in the log file + + + Activates portable mode where the database is placed below the program executable + + + A serious error occured in Duplicati: {0} + Duplicati crashes + + + Displays the status window + + + Unable to start up, perhaps another process is already running? +Error message: {0} + An error message displayed when Duplicati startup fails + + + Disables database encryption + + + Unsupported version of SQLite detected ({0}), must be {1} or higher + An error message displayed when the wrong version of SQLite is detected + + \ No newline at end of file diff --git a/Duplicati/Server/Strings/Scheduler.Designer.cs b/Duplicati/Server/Strings/Scheduler.Designer.cs new file mode 100644 index 000000000..e6a02c095 --- /dev/null +++ b/Duplicati/Server/Strings/Scheduler.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.225 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Duplicati.Server.Strings { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Scheduler { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Scheduler() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Server.Strings.Scheduler", typeof(Scheduler).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Unable to find a valid date, given the start date {0}, the repetition interval {1} and the allowed days {2}. + /// + internal static string InvalidTimeSetupError { + get { + return ResourceManager.GetString("InvalidTimeSetupError", resourceCulture); + } + } + } +} diff --git a/Duplicati/Library/Backend/SSH/Strings/SharpExpectProcess.resx b/Duplicati/Server/Strings/Scheduler.resx similarity index 92% rename from Duplicati/Library/Backend/SSH/Strings/SharpExpectProcess.resx rename to Duplicati/Server/Strings/Scheduler.resx index 9f280632c..87798b26e 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SharpExpectProcess.resx +++ b/Duplicati/Server/Strings/Scheduler.resx @@ -117,13 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Log was disabled, no output - - - *password* - - - Can't write after process has exited: {0} + + Unable to find a valid date, given the start date {0}, the repetition interval {1} and the allowed days {2} \ No newline at end of file diff --git a/Duplicati/Server/Strings/TaskType.Designer.cs b/Duplicati/Server/Strings/TaskType.Designer.cs new file mode 100644 index 000000000..28c370d3c --- /dev/null +++ b/Duplicati/Server/Strings/TaskType.Designer.cs @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.225 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Duplicati.Server.Strings { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class TaskType { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TaskType() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Duplicati.Server.Strings.TaskType", typeof(TaskType).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Full backup. + /// + internal static string FullBackup { + get { + return ResourceManager.GetString("FullBackup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incremental backup. + /// + internal static string IncrementalBackup { + get { + return ResourceManager.GetString("IncrementalBackup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List actual files. + /// + internal static string ListActualFiles { + get { + return ResourceManager.GetString("ListActualFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List backup entries. + /// + internal static string ListBackupEntries { + get { + return ResourceManager.GetString("ListBackupEntries", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List backups. + /// + internal static string ListBackups { + get { + return ResourceManager.GetString("ListBackups", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List files. + /// + internal static string ListFiles { + get { + return ResourceManager.GetString("ListFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove all but n full. + /// + internal static string RemoveAllButNFull { + get { + return ResourceManager.GetString("RemoveAllButNFull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove older than. + /// + internal static string RemoveOlderThan { + get { + return ResourceManager.GetString("RemoveOlderThan", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restore. + /// + internal static string Restore { + get { + return ResourceManager.GetString("Restore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restore setup. + /// + internal static string RestoreSetup { + get { + return ResourceManager.GetString("RestoreSetup", resourceCulture); + } + } + } +} diff --git a/Duplicati/Library/Backend/SSH/Strings/SSHCommonOptions.resx b/Duplicati/Server/Strings/TaskType.resx similarity index 83% rename from Duplicati/Library/Backend/SSH/Strings/SSHCommonOptions.resx rename to Duplicati/Server/Strings/TaskType.resx index e50bcdadf..28d0ba872 100644 --- a/Duplicati/Library/Backend/SSH/Strings/SSHCommonOptions.resx +++ b/Duplicati/Server/Strings/TaskType.resx @@ -117,29 +117,34 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - An error occured while setting up the SSH dialog: {0} + + Full backup - - The entered SFTP program could not be found. -Do you want to use the path anyway? + + Incremental backup - - Leave empty to use the sftp application found: {0} + + List actual files - - The sftp application could not be located, please enter the path manually + + List backup entries - - An error occured while attempting to verify the sftp program path: -{0} - -Do you want to use the path anyway? + + List backups - - You have not entered a path to the SFTP application. -This will use the default path to the SFTP application. -The SFTP application could not be found during system probing. -Are you sure this is what you want? + + List files + + + Remove all but n full + + + Remove older than + + + Restore + + + Restore setup \ No newline at end of file diff --git a/Duplicati/Server/TaskPackages.cs b/Duplicati/Server/TaskPackages.cs new file mode 100644 index 000000000..24ffffe39 --- /dev/null +++ b/Duplicati/Server/TaskPackages.cs @@ -0,0 +1,762 @@ +#region Disclaimer / License +// Copyright (C) 2011, Kenneth Skovhede +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +#endregion +using System; +using System.Collections.Generic; +using System.Text; +using Duplicati.Datamodel; + +namespace Duplicati.Server +{ + public enum DuplicityTaskType + { + IncrementalBackup, + FullBackup, + RemoveAllButNFull, + RemoveOlderThan, + Restore, + ListBackups, + ListFiles, + ListSourceFolders, + RestoreSetup, + ListActualFiles, + ListBackupEntries + } + + + public delegate void TaskCompletedHandler(IDuplicityTask owner, string output, string shortMessage); + + public interface IDuplicityTask + { + DuplicityTaskType TaskType { get; } + event TaskCompletedHandler TaskCompleted; + void RaiseTaskCompleted(string output, string parsedMessage); + + DateTime BeginTime { get; set; } + Task Task { get; } + Schedule Schedule { get; } + + string GetConfiguration(Dictionary options); + string LocalPath { get; } + + string Result { get; set; } + + bool IsAborted { get; set; } + } + + public abstract class BackupTask : IDuplicityTask + { + protected Schedule m_schedule; + protected DateTime m_beginTime; + + protected string m_result; + protected bool m_isAborted = false; + + public bool IsAborted { get { return m_isAborted; } set { m_isAborted = value; } } + public abstract DuplicityTaskType TaskType { get; } + + public Schedule Schedule { get { return m_schedule; } } + public Task Task { get { return m_schedule.Task; } } + public event TaskCompletedHandler TaskCompleted; + public virtual DateTime BeginTime + { + get { return m_beginTime; } + set { m_beginTime = value; } + } + + public BackupTask(Schedule schedule) + { + m_schedule = schedule; + } + + public void RaiseTaskCompleted(string output, string parsedMessage) + { + if (TaskCompleted != null) + TaskCompleted(this, output, parsedMessage); + } + + public abstract string LocalPath { get; } + + public virtual string GetConfiguration(Dictionary options) + { + //Schedule settings have lowest priority, because there is currently no setup + SetupSchedule(options); + + //Now setup the environment + ApplicationSettings appSet = new ApplicationSettings(this.Task.DataParent); + + if (this.Task.ExistsInDb && appSet.SignatureCacheEnabled && !string.IsNullOrEmpty(appSet.SignatureCachePath)) + options["signature-cache-path"] = System.IO.Path.Combine(System.Environment.ExpandEnvironmentVariables(appSet.SignatureCachePath), this.Task.Schedule.ID.ToString()); + + if (!string.IsNullOrEmpty(appSet.TempPath)) + { + string tempdir = System.Environment.ExpandEnvironmentVariables(appSet.TempPath); + if (!System.IO.Directory.Exists(tempdir)) + System.IO.Directory.CreateDirectory(tempdir); + + options["tempdir"] = tempdir; + } + + Dictionary env = appSet.CreateDetachedCopy(); + + //Inject the encryption, backend and compression module names into the environment + env["encryption-module"] = this.Task.EncryptionModule; + env["compression-module"] = this.Task.CompressionModule; + env["backend-module"] = this.Task.Service; + + //If there are any control extensions, let them modify the environment + foreach (Library.Interface.ISettingsControl ic in Library.DynamicLoader.SettingsControlLoader.Modules) + ic.GetConfiguration(env, SettingExtension.GetExtensions(this.Task.Schedule.DataParent, ic.Key), options); + + //Setup encryption module + SetupEncryptionModule(env, this.Task.EncryptionSettingsLookup, options); + + //Setup compression module + SetupCompressionModule(env, this.Task.CompressionSettingsLookup, options); + + //Next is the actual backend setup + string destination = SetupBackend(env, options); + + //Setup any task options + SetupTask(options); + + //Setup any task extension options + SetupTaskExtensions(options); + + //Override everything set in the overrides, this is placed last so it cannot be overriden elsewhere + foreach (TaskOverride ov in this.Task.TaskOverrides) + options[ov.Name] = ov.Value; + + return destination; + } + + protected virtual void SetupEncryptionModule(Dictionary env, IDictionary guiOptions, Dictionary options) + { + foreach (Library.Interface.IEncryption e in Library.DynamicLoader.EncryptionLoader.Modules) + if (e.FilenameExtension == this.Task.EncryptionModule) + { + if (e is Library.Interface.IEncryptionGUI) + { + (e as Library.Interface.IEncryptionGUI).GetConfiguration(env, guiOptions, options); + } + else + { + foreach (string k in guiOptions.Keys) + if (k.StartsWith("--")) + options[k.Substring(2)] = guiOptions[k]; + } + + return; + } + } + + protected virtual void SetupCompressionModule(Dictionary env, IDictionary guiOptions, Dictionary options) + { + foreach (Library.Interface.ICompression e in Library.DynamicLoader.CompressionLoader.Modules) + if (e.FilenameExtension == this.Task.CompressionModule) + { + if (e is Library.Interface.ICompressionGUI) + { + (e as Library.Interface.ICompressionGUI).GetConfiguration(env, guiOptions, options); + } + else + { + foreach (string k in guiOptions.Keys) + if (k.StartsWith("--")) + options[k.Substring(2)] = guiOptions[k]; + } + + return; + } + } + + protected virtual void SetupSchedule(Dictionary options) + { + } + + protected virtual void SetupTask(Dictionary options) + { + if (this.Task.Filters.Count > 0) + options["filter"] = this.Task.EncodedFilter; + + if (!string.IsNullOrEmpty(this.Task.EncryptionModule)) + options["encryption-module"] = this.Task.EncryptionModule; + + if (string.IsNullOrEmpty(this.Task.Encryptionkey)) + options.Add("no-encryption", ""); + else + options.Add("passphrase", this.Task.Encryptionkey); + } + + protected virtual void SetupTaskExtensions(Dictionary options) + { + Datamodel.Task.TaskExtensionWrapper ext = this.Task.Extensions; + + if (!string.IsNullOrEmpty(ext.MaxUploadSize)) + options["totalsize"] = ext.MaxUploadSize; + if (!string.IsNullOrEmpty(ext.VolumeSize)) + options["volsize"] = ext.VolumeSize; + if (!string.IsNullOrEmpty(ext.DownloadBandwidth)) + options["max-download-pr-second"] = ext.DownloadBandwidth; + if (!string.IsNullOrEmpty(ext.UploadBandwidth)) + options["max-upload-pr-second"] = ext.UploadBandwidth; + if (!string.IsNullOrEmpty(ext.ThreadPriority)) + options["thread-priority"] = ext.ThreadPriority; + if (!ext.AsyncTransfer) + options["synchronous-upload"] = "true"; + if (ext.IgnoreTimestamps) + options["disable-filetime-check"] = ""; + + if (!string.IsNullOrEmpty(ext.FileSizeLimit)) + options["skip-files-larger-than"] = ext.FileSizeLimit; + + //Bit tricky, but we REALLY want to disallow fallback decryption for new backups + //We need some extra protection, otherwise the user will be warned about an unused option, + // if they have non-AES encryption or no encryption at all. + //This means that we have special support for the "aes" module in here, which is bad encapsulation + //Once we completely remove fallback encryption this check can be removed + if (!string.IsNullOrEmpty(this.Task.Encryptionkey) && "aes".Equals(this.Task.EncryptionModule, StringComparison.InvariantCultureIgnoreCase) && ext.DisableAESFallbackDecryption) + options["aes-encryption-dont-allow-fallback"] = "true"; + } + + protected virtual string SetupBackend(Dictionary environment, Dictionary options) + { + //TODO: Replicated from GUI/GridContainer + const string DESTINATION_EXTENSION_KEY = "Destination"; + + Library.Interface.IBackend selectedBackend = null; + foreach (Library.Interface.IBackend b in Library.DynamicLoader.BackendLoader.Backends) + if (b.ProtocolKey == this.Task.Service) + { + selectedBackend = b; + break; + } + + if (selectedBackend == null) + throw new Exception("Missing backend"); + + string destination; + if (selectedBackend is Library.Interface.IBackendGUI) + { + //Simply invoke the backends setup function + destination = ((Library.Interface.IBackendGUI)selectedBackend).GetConfiguration(environment, this.Task.BackendSettingsLookup, options); + } + else + { + //We store destination with the key "Destination" and other options with the -- prefix + if (!options.ContainsKey(DESTINATION_EXTENSION_KEY)) + throw new Exception("Invalid configuration"); + + destination = options[DESTINATION_EXTENSION_KEY]; + foreach (KeyValuePair k in this.Task.BackendSettingsLookup) + if (k.Key.StartsWith("--")) //All options are prefixed with this + options[k.Key.Substring(2)] = k.Value; + } + + return destination; + } + + public string Result + { + get { return m_result; } + set { m_result = value; } + } + + protected virtual Log WriteLogMessage(string action, string subaction, string data, string parsedMessage, bool commit, Log log) + { + lock (Program.MainLock) + { + System.Data.LightDatamodel.IDataFetcher con; + LogBlob lb; + + if (log == null) + { + con = this.Task.DataParent; + log = con.Add(); + lb = con.Add(); + + log.Blob = lb; + this.Task.Logs.Add(log); + } + else + { + con = log.DataParent; + if (log.Blob == null) + log.Blob = con.Add(); + + lb = log.Blob; + } + + lb.StringData = data; + + //Keep some of the data in an easy to read manner + DuplicatiOutputParser.ParseData(log); + + + log.Action = action; + log.SubAction = subaction; + log.BeginTime = m_beginTime; + log.EndTime = DateTime.Now; + log.ParsedMessage = parsedMessage; + + if (parsedMessage == "InProgress") + { + log.ParsedStatus = DuplicatiOutputParser.InterruptedStatus; + log.ParsedMessage = ""; + } + + + if (commit) + (con as System.Data.LightDatamodel.IDataFetcherWithRelations).CommitAllRecursive(); + } + + return log; + } + } + + public abstract class FullOrIncrementalTask : BackupTask + { + protected Log m_log = null; + public Log LogEntry { get { return m_log; } set { m_log = value; } } + + protected FullOrIncrementalTask(Schedule schedule) + : base(schedule) + { + base.TaskCompleted += new TaskCompletedHandler(DoneEvent); + } + + protected void DoneEvent(IDuplicityTask task, string output, string parsedMessage) + { + WriteLogMessage("Backup", "Primary", output, parsedMessage, true, m_log); + m_log = null; + } + + public override string GetConfiguration(Dictionary options) + { + return base.GetConfiguration(options); + } + + public override string LocalPath + { + get { return System.Environment.ExpandEnvironmentVariables(this.Task.SourcePath); } + } + + internal void WriteBackupInProgress(string message) + { + m_log = WriteLogMessage("Backup", "InProgress", message, "InProgress", true, null); + } + } + + public class FullBackupTask : FullOrIncrementalTask + { + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.FullBackup; } } + + public FullBackupTask(Schedule schedule) + : base(schedule) + { + } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + options["full"] = ""; + return destination; + } + + } + + public class IncrementalBackupTask : FullOrIncrementalTask + { + private string m_fullAfter = null; + + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.IncrementalBackup; } } + + public string FullAfter + { + get { return m_fullAfter == null ? m_schedule.Task.FullAfter : m_fullAfter; } + } + + public IncrementalBackupTask(Schedule schedule, string fullAfter) + : this(schedule) + { + m_fullAfter = fullAfter; + } + + public IncrementalBackupTask(Schedule schedule) + : base(schedule) + { + } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + + if (!string.IsNullOrEmpty(this.FullAfter)) + options.Add("full-if-older-than", this.FullAfter); + + return destination; + } + } + + public class ListBackupEntriesTask : BackupTask + { + private List m_backups = null; + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.ListBackupEntries; } } + public List Backups { get { return m_backups; } set { m_backups = value; } } + + public ListBackupEntriesTask(Schedule schedule) + : base(schedule) + { + } + + public override string LocalPath + { + get { throw new NotImplementedException(); } + } + } + + public class ListBackupsTask : BackupTask + { + private string[] m_backups = null; + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.ListBackups; } } + public string[] Backups { get { return m_backups; } set { m_backups = value; } } + + public ListBackupsTask(Schedule schedule) + : base(schedule) + { + this.TaskCompleted += new TaskCompletedHandler(ListBackupsTask_TaskCompleted); + } + + void ListBackupsTask_TaskCompleted(IDuplicityTask owner, string output, string parsedMessage) + { + if (string.IsNullOrEmpty(output)) + return; + + const string DIVIDER = "-------------------------\r\n"; + const string HEADERS = " Type of backup set: Time: Num volumes:"; + + List res = new List(); + + foreach (string part in output.Split(new string[] { DIVIDER }, StringSplitOptions.RemoveEmptyEntries)) + if (part.IndexOf(HEADERS) >= 0) + { + bool passedHeader = false; + foreach (string line in part.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) + { + if (line.StartsWith(HEADERS)) + passedHeader = true; + else if (passedHeader) + { + string tmp = line.Trim(); + if (tmp.StartsWith("Full")) + tmp = tmp.Substring("Full".Length).Trim(); + else if (tmp.StartsWith("Incremental")) + tmp = tmp.Substring("Incremental".Length).Trim(); + + string datetime = tmp.Substring(0, tmp.LastIndexOf(' ')).Trim(); + /*DateTime dt; + if (DateTime.TryParse(datetime, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dt))*/ + res.Add(datetime); + } + } + } + + + //System.Text.RegularExpressions.Regex regexp = new System.Text.RegularExpressions.Regex("---------------- + + m_backups = res.ToArray(); + } + + public override string LocalPath + { + get { throw new NotImplementedException(); } + } + } + + public class ListFilesTask : BackupTask + { + protected IList m_files = null; + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.ListFiles; } } + public IList Files { get { return m_files; } set { m_files = value; } } + protected DateTime m_when; + + public ListFilesTask(Schedule schedule, DateTime when) + : base(schedule) + { + m_when = when; + } + + public string When + { + get + { + if (m_when.Year < 10) + return null; + else + return m_when.ToString("s"); + } + } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + + if (!string.IsNullOrEmpty(When)) + options["restore-time"] = this.When; + + return destination; + } + + public override string LocalPath + { + get { throw new NotImplementedException(); } + } + } + + public class ListSourceFoldersTask : ListFilesTask + { + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.ListSourceFolders; } } + public ListSourceFoldersTask(Schedule schedule, DateTime when) + : base(schedule, when) + { + } + } + + public class ListActualFilesTask : BackupTask + { + protected List> m_files = null; + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.ListActualFiles; } } + public List> Files { get { return m_files; } set { m_files = value; } } + protected DateTime m_when; + + public ListActualFilesTask(Schedule schedule, DateTime when) + : base(schedule) + { + m_when = when; + } + + public string When + { + get + { + if (m_when.Year < 10) + return null; + else + return m_when.ToString("s"); + } + } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + + if (!string.IsNullOrEmpty(When)) + options["restore-time"] = this.When; + + return destination; + } + + public override string LocalPath + { + get { throw new NotImplementedException(); } + } + } + + + public class RestoreSetupTask : BackupTask + { + protected string m_targetdir = null; + + public RestoreSetupTask(Schedule schedule, string targetdir) + : base(schedule) + { + m_targetdir = targetdir; + } + + public override string LocalPath + { + get { return System.Environment.ExpandEnvironmentVariables(this.TargetDir); } + } + + public string TargetDir { get { return m_targetdir; } } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + if (options.ContainsKey("filter")) + options.Remove("filter"); + + return destination; + } + + public override DuplicityTaskType TaskType + { + get { return DuplicityTaskType.RestoreSetup; } + } + } + + public class RestoreTask : BackupTask + { + protected DateTime m_when; + protected string m_targetdir = null; + protected string m_sourceFiles = null; + + public string When + { + get + { + if (m_when.Year < 10) + return null; + else + return m_when.ToString("s"); + } + } + + public string TargetDir { get { return m_targetdir; } } + public string SourceFiles { get { return m_sourceFiles; } } + + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.Restore; } } + + public RestoreTask(Schedule schedule, string targetdir, string sourcefiles) + : this(schedule, targetdir, sourcefiles, new DateTime()) + { + } + + public RestoreTask(Schedule schedule, string targetdir, DateTime when) + : this(schedule, targetdir, null, when) + { + } + + public RestoreTask(Schedule schedule, string targetdir) + : this(schedule, targetdir, null, new DateTime()) + { + } + + public RestoreTask(Schedule schedule, string targetdir, string sourcefiles, DateTime when) + : base(schedule) + { + m_targetdir = targetdir; + m_sourceFiles = sourcefiles; + m_when = when; + base.TaskCompleted += new TaskCompletedHandler(DoneEvent); + } + + void DoneEvent(IDuplicityTask owner, string output, string parsedMessage) + { + WriteLogMessage("Restore", "Primary", output, parsedMessage, true, null); + } + + public override string LocalPath + { + get { return System.Environment.ExpandEnvironmentVariables(this.TargetDir); } + } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + + options.Add("force", ""); + if (!string.IsNullOrEmpty(When)) + options.Add("restore-time", this.When); + + if (!string.IsNullOrEmpty(this.SourceFiles)) + options.Add("file-to-restore", this.SourceFiles); + + return destination; + } + } + + public class RemoveAllButNFullTask : BackupTask + { + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.RemoveAllButNFull; } } + private int m_fullCount; + public int FullCount { get { return m_fullCount; } } + + public RemoveAllButNFullTask(Schedule schedule, int fullcount) + : base(schedule) + { + m_fullCount = fullcount; + base.TaskCompleted += new TaskCompletedHandler(DoneEvent); + } + + void DoneEvent(IDuplicityTask owner, string output, string parsedMessage) + { + WriteLogMessage("Backup", "Cleanup", output, parsedMessage, false, null); + } + + public override string LocalPath + { + get + { + throw new MissingMethodException(); + } + } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + + options.Add("delete-all-but-n-full", this.FullCount.ToString()); + options.Add("force", ""); + if (!options.ContainsKey("number-of-retries")) + options["number-of-retries"] = "2"; + + return destination; + } + } + + public class RemoveOlderThanTask : BackupTask + { + public override DuplicityTaskType TaskType { get { return DuplicityTaskType.RemoveOlderThan; } } + + private string m_older; + public string Older { get { return m_older; } } + + public RemoveOlderThanTask(Schedule schedule, string older) + : base(schedule) + { + m_older = older; + base.TaskCompleted += new TaskCompletedHandler(DoneEvent); + } + + void DoneEvent(IDuplicityTask owner, string output, string parsedMessage) + { + WriteLogMessage("Backup", "Cleanup", output, parsedMessage, false, null); + } + + public override string GetConfiguration(Dictionary options) + { + string destination = base.GetConfiguration(options); + + options.Add("delete-older-than", this.Older); + options.Add("force", ""); + if (!options.ContainsKey("number-of-retries")) + options["number-of-retries"] = "2"; + + return destination; + } + + public override string LocalPath + { + get { throw new NotImplementedException(); } + } + } + + +} diff --git a/Duplicati/Server/WebServer.cs b/Duplicati/Server/WebServer.cs new file mode 100644 index 000000000..38c0aabf3 --- /dev/null +++ b/Duplicati/Server/WebServer.cs @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using HttpServer.MVC; +using HttpServer.HttpModules; +using System.IO; +using Duplicati.Server.Serialization; + +namespace Duplicati.Server +{ + public class WebServer + { + /// + /// The single webserver instance + /// + private HttpServer.HttpServer m_server; + + public WebServer(int port) + { + m_server = new HttpServer.HttpServer(); + + m_server.Add(new DynamicHandler()); + + FileModule fh = new FileModule("/", System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "webroot")); + fh.AddDefaultMimeTypes(); + m_server.Add(fh); + + m_server.Start(System.Net.IPAddress.Any, port); + } + + private class BodyWriter : System.IO.StreamWriter, IDisposable + { + private HttpServer.IHttpResponse m_resp; + + // We override the format provider so all JSON output uses US format + public override IFormatProvider FormatProvider + { + get { return System.Globalization.CultureInfo.InvariantCulture; } + } + + public BodyWriter(HttpServer.IHttpResponse resp) + : base(resp.Body, resp.Encoding) + { + m_resp = resp; + } + + protected override void Dispose (bool disposing) + { + if (!m_resp.HeadersSent) + { + base.Flush(); + m_resp.ContentLength = base.BaseStream.Length; + m_resp.Send(); + } + base.Dispose(disposing); + } + } + + private class DynamicHandler : HttpModule + { + private delegate void ProcessSub(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter writer); + private Server.Serialization.Serializer m_serializer; + private readonly Dictionary SUPPORTED_METHODS; + + public DynamicHandler() + { + m_serializer = new Serializer(); + SUPPORTED_METHODS = new Dictionary(System.StringComparer.InvariantCultureIgnoreCase); + + //Make a list of all supported actions + SUPPORTED_METHODS.Add("supported-actions", ListSupportedActions); + SUPPORTED_METHODS.Add("list-schedules", ListSchedules); + SUPPORTED_METHODS.Add("get-current-state", GetCurrentState); + SUPPORTED_METHODS.Add("list-application-settings", ListApplicationSettings); + SUPPORTED_METHODS.Add("list-installed-backends", GetInstalledBackends); + SUPPORTED_METHODS.Add("list-installed-encryption-modules", ListInstalledEncryptionModules); + SUPPORTED_METHODS.Add("list-installed-compression-modules", ListInstalledCompressionModules); + SUPPORTED_METHODS.Add("list-installed-generic-modules", ListInstalledGenericModules); + SUPPORTED_METHODS.Add("list-options", ListCoreOptions); + SUPPORTED_METHODS.Add("list-recent-completed", ListRecentCompleted); + SUPPORTED_METHODS.Add("get-recent-log-details", GetLogBlob); + SUPPORTED_METHODS.Add("send-command", SendCommand); + } + + public override bool Process (HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session) + { + //We use the fake entry point /control.cgi to listen for requests + //This ensures that the rest of the webserver can just serve plain files + if (!request.Uri.AbsolutePath.Equals("/control.cgi", StringComparison.InvariantCultureIgnoreCase)) + return false; + + HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; + + string action = input["action"].Value ?? ""; + + //Lookup the actual handler method + ProcessSub method; + SUPPORTED_METHODS.TryGetValue(action, out method); + + if (method == null) { + response.Status = System.Net.HttpStatusCode.NotImplemented; + response.Reason = "Unsupported action: " + (action == null ? "" : ""); + response.Send(); + } else { + //Default setup + response.Status = System.Net.HttpStatusCode.OK; + response.Reason = "OK"; +#if DEBUG + response.ContentType = "text/plain"; +#else + response.ContentType = "text/json"; +#endif + using (BodyWriter bw = new BodyWriter(response)) + { + try + { + method(request, response, session, bw); + } + catch (Exception ex) + { + if (!response.HeadersSent) + { + response.Status = System.Net.HttpStatusCode.InternalServerError; + response.Reason = ex.Message; + response.ContentType = "text/plain"; + bw.WriteLine("Internal error"); +#if DEBUG + bw.Write("Stacktrace: " + ex.ToString()); +#endif + bw.Flush(); + } + } + } + } + + return true; + } + + private void ReportError(HttpServer.IHttpResponse response, BodyWriter bw, string message) + { + response.Status = System.Net.HttpStatusCode.InternalServerError; + response.Reason = message; + + OutputObject(bw, new { Error = message }); + } + + private void OutputObject (BodyWriter b, object o) + { + m_serializer.SerializeJson(b, o); + } + + private void ListSupportedActions(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, new { Version = 1, Methods = SUPPORTED_METHODS.Keys }); + } + + private void ListSchedules (HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, Program.DataConnection.GetObjects()); + } + + private void GetCurrentState (HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, new SerializableStatus()); + } + + private void GetInstalledBackends(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, Duplicati.Library.DynamicLoader.BackendLoader.Backends); + } + + private void ListInstalledEncryptionModules(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, Duplicati.Library.DynamicLoader.EncryptionLoader.Modules); + } + + private void ListInstalledCompressionModules(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, Duplicati.Library.DynamicLoader.CompressionLoader.Modules); + } + + private void ListInstalledGenericModules(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, Duplicati.Library.DynamicLoader.GenericLoader.Modules); + } + + private void ListCoreOptions(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, new Duplicati.Library.Main.Options(new Dictionary()).SupportedCommands); + } + + private void ListRecentCompleted(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, Program.DataConnection.GetObjects("EndTime > ? AND SubAction LIKE ? ORDER BY EndTime DESC", Library.Utility.Timeparser.ParseTimeInterval(new Datamodel.ApplicationSettings(Program.DataConnection).RecentBackupDuration, DateTime.Now, true), "Primary")); + } + + private void GetLogBlob(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; + long id; + if (!long.TryParse(input["id"].Value, out id)) + ReportError(response, bw, "Invalid or missing log id"); + else + { + Datamodel.LogBlob lb = Program.DataConnection.GetObject("LogId = ?", id); + OutputObject(bw, lb); + } + } + + private void ListApplicationSettings(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + OutputObject(bw, new Datamodel.ApplicationSettings(Program.DataConnection)); + } + + private void SendCommand(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) + { + HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; + + string command = input["command"].Value ?? ""; + long id; + + switch (command.ToLowerInvariant()) + { + case "pause": + if (input.Contains("duration")) + { + TimeSpan ts; + try { ts = Library.Utility.Timeparser.ParseTimeSpan(input["duration"].Value); } + catch (Exception ex) + { + ReportError(response, bw, ex.Message); + return; + } + Program.LiveControl.Pause(ts); + } + else + { + Program.LiveControl.Pause(); + } + + break; + case "resume": + Program.LiveControl.Resume(); + break; + + case "stop": + if (!Program.Runner.IsStopRequested) + Program.Runner.Stop(CloseReason.UserClosing); + break; + + case "abort": + if (!Program.Runner.IsStopRequested) + { + Program.Runner.Stop(CloseReason.UserClosing); + Program.Runner.Stop(CloseReason.UserClosing); + } + break; + + case "run": + case "run-backup": + { + Datamodel.Schedule schedule = null; + if (long.TryParse(input["id"].Value, out id)) + schedule = Program.DataConnection.GetObjectById(id); + + if (schedule == null) + { + ReportError(response, bw, string.Format("No backup found for id: {0}", input["id"].Value)); + return; + } + + if (Library.Utility.Utility.ParseBoolOption(input["full"].ToDictionary(x => x.Name, x => x.Value), "full")) + Program.WorkThread.AddTask(new FullBackupTask(schedule)); + else + Program.WorkThread.AddTask(new IncrementalBackupTask(schedule)); + } + break; + case "clear-warning": + Program.HasWarning = false; + break; + case "clear-error": + Program.HasError = false; + break; + + default: + ReportError(response, bw, string.Format("Unsupported command {0}", command)); + break; + } + + OutputObject(bw, new { Status = "OK" }); + } + } + } +} diff --git a/Duplicati/Server/app.config b/Duplicati/Server/app.config new file mode 100644 index 000000000..02451de7f --- /dev/null +++ b/Duplicati/Server/app.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Duplicati/Server/webroot/index.html b/Duplicati/Server/webroot/index.html new file mode 100644 index 000000000..239b3aa29 --- /dev/null +++ b/Duplicati/Server/webroot/index.html @@ -0,0 +1,25 @@ + + + + + + + Duplicati Wizard + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Duplicati/Winforms/Controls/Duplicati.Winforms.Controls.csproj b/Duplicati/Winforms/Controls/Duplicati.Winforms.Controls.csproj index 1353e3a63..f6ac65b31 100644 --- a/Duplicati/Winforms/Controls/Duplicati.Winforms.Controls.csproj +++ b/Duplicati/Winforms/Controls/Duplicati.Winforms.Controls.csproj @@ -1,4 +1,4 @@ - + Debug @@ -10,7 +10,6 @@ Properties Duplicati.Winforms.Controls Duplicati.Winforms.Controls - v2.0 512 true Duplicati.snk @@ -43,6 +42,8 @@ full x86 prompt + 4 + false bin\x86\Release\ @@ -51,6 +52,7 @@ pdbonly x86 prompt + 4 diff --git a/Duplicati/Winforms/Controls/Properties/Resources.Designer.cs b/Duplicati/Winforms/Controls/Properties/Resources.Designer.cs index db0b9a656..26dee3ac5 100644 --- a/Duplicati/Winforms/Controls/Properties/Resources.Designer.cs +++ b/Duplicati/Winforms/Controls/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Winforms/Controls/Strings/PasswordConfirmationDialog.Designer.cs b/Duplicati/Winforms/Controls/Strings/PasswordConfirmationDialog.Designer.cs index 1483d29ec..4e0063b03 100644 --- a/Duplicati/Winforms/Controls/Strings/PasswordConfirmationDialog.Designer.cs +++ b/Duplicati/Winforms/Controls/Strings/PasswordConfirmationDialog.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Duplicati/Winforms/Controls/Strings/PasswordControl.Designer.cs b/Duplicati/Winforms/Controls/Strings/PasswordControl.Designer.cs index 8e516ad3f..dc0366815 100644 --- a/Duplicati/Winforms/Controls/Strings/PasswordControl.Designer.cs +++ b/Duplicati/Winforms/Controls/Strings/PasswordControl.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/Installer/Makefile/Makefile b/Installer/Makefile/Makefile index 5528c7e04..453ec3c2c 100644 --- a/Installer/Makefile/Makefile +++ b/Installer/Makefile/Makefile @@ -8,16 +8,8 @@ LOCALIZATION_TOOL=mono $(SOURCE_DIR)/Duplicati/Localization/LocalizationTool.exe all: package -Duplicati-v1.sln: - patch -d "$(SOURCE_DIR)" -i patch-1-remove-scheduler -o Duplicati-v1.sln Duplicati.sln - -Duplicati-v2.sln: Duplicati-v1.sln - patch -d "$(SOURCE_DIR)" -i patch-2-remove-unsigned-backends -o Duplicati-v2.sln Duplicati-v1.sln - -build: Duplicati-v2.sln - $(BUILD_TOOL) $(BUILD_ARGS) "$(SOURCE_DIR)/Duplicati-v2.sln" - $(BUILD_TOOL) $(BUILD_ARGS) $(OUTPUT_ARGS) "$(SOURCE_DIR)/Duplicati/Library/Backend/SSH/Duplicati.Library.Backend.SSH.csproj" - $(BUILD_TOOL) $(BUILD_ARGS) $(OUTPUT_ARGS) "$(SOURCE_DIR)/Duplicati/Library/Backend/TahoeLAFS/Duplicati.Library.Backend.TahoeLAFS.csproj" +build: + $(BUILD_TOOL) $(BUILD_ARGS) "$(SOURCE_DIR)/Duplicati.sln" $(BUILD_TOOL) $(BUILD_ARGS) "$(SOURCE_DIR)/BuildTools/LocalizationTool/LocalizationTool.sln" translations: build @@ -91,8 +83,6 @@ package: gzip --best "duplicati.tar" clean: - rm -rf "$(SOURCE_DIR)/Duplicati-v1.sln" - rm -rf "$(SOURCE_DIR)/Duplicati-v2.sln" (find "$(SOURCE_DIR)" -type d -name bin -exec rm -rf "{}" \; &> /dev/null) || echo "bin cleaned" (find "$(SOURCE_DIR)" -type d -name obj -exec rm -rf "{}" \; &> /dev/null) || echo "obj cleaned" (find "$(SOURCE_DIR)/Duplicati/Localization/" -type d -not -name Localization -exec rm -rf "{}" \; &> /dev/null) || echo "localization cleaned" diff --git a/Installer/Makefile/patch-1-remove-scheduler b/Installer/Makefile/patch-1-remove-scheduler deleted file mode 100644 index 58a689a32..000000000 --- a/Installer/Makefile/patch-1-remove-scheduler +++ /dev/null @@ -1,21 +0,0 @@ ---- Duplicati.sln 2011-12-06 18:09:23.000000000 +0100 -+++ Duplicati-v1.sln 2011-12-06 18:21:00.000000000 +0100 -@@ -55,18 +55,6 @@ - EndProject - Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.GoogleDocs", "Duplicati\Library\Backend\GoogleDocs\Duplicati.Library.Backend.GoogleDocs.csproj", "{42C91180-11EB-4590-82EA-80FE9D4DEBEC}" - EndProject --Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Scheduler.Data", "Duplicati\Scheduler\Data\Duplicati.Scheduler.Data.csproj", "{C8F0DD19-E242-4E9C-A707-2FFADE8D7970}" --EndProject --Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Scheduler", "Duplicati\Scheduler\Duplicati.Scheduler.csproj", "{D1D503E8-5CF7-413B-81FB-403D65B43AAB}" --EndProject --Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Scheduler.Utility", "Duplicati\Scheduler\Utility\Duplicati.Scheduler.Utility.csproj", "{0E3DF716-5657-4B51-9BF2-3791880930E1}" --EndProject --Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Scheduler.RunBackup", "Duplicati\Scheduler\RunBackup\Duplicati.Scheduler.RunBackup.csproj", "{520FA2BD-1C59-40D9-BCB5-EDA11327B7F0}" --EndProject --Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Scheduler.Monitor.SQL", "Duplicati\Scheduler\Monitor.SQL\Duplicati.Scheduler.Monitor.SQL.csproj", "{ECA674A3-CC59-4C75-90E3-5AACA591EC5D}" --EndProject --Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectListView2010", "thirdparty\ObjectListView\ObjectListView\ObjectListView2010.csproj", "{18FEDA0C-D147-4286-B39A-01204808106A}" --EndProject - Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU diff --git a/Installer/Makefile/patch-2-remove-unsigned-backends b/Installer/Makefile/patch-2-remove-unsigned-backends deleted file mode 100644 index bd2aa50bf..000000000 --- a/Installer/Makefile/patch-2-remove-unsigned-backends +++ /dev/null @@ -1,20 +0,0 @@ ---- Duplicati-v1.sln 2011-12-06 18:21:00.000000000 +0100 -+++ Duplicati-v2.sln 2011-12-06 20:15:33.000000000 +0100 -@@ -11,8 +11,6 @@ - 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.Compression", "Duplicati\Library\Compression\Duplicati.Library.Compression.csproj", "{19ECCE09-B5EB-406C-8C57-BAC66997D469}" - EndProject - Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{94484FDB-2EFA-4CF0-9BE6-A561157B4F87}" -@@ -49,8 +47,6 @@ - EndProject - Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Modules.Builtin", "Duplicati\Library\Modules\Builtin\Duplicati.Library.Modules.Builtin.csproj", "{52826615-7964-47FE-B4B3-1B2DBDF605B9}" - EndProject --Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.TahoeLAFS", "Duplicati\Library\Backend\TahoeLAFS\Duplicati.Library.Backend.TahoeLAFS.csproj", "{C0270709-2A40-43B5-8CF1-69581B9FA2A1}" --EndProject - Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.SkyDrive", "Duplicati\Library\Backend\SkyDrive\Duplicati.Library.Backend.SkyDrive.csproj", "{CCD76347-7DC7-4B42-B7E1-E500E624CAC3}" - EndProject - Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.GoogleDocs", "Duplicati\Library\Backend\GoogleDocs\Duplicati.Library.Backend.GoogleDocs.csproj", "{42C91180-11EB-4590-82EA-80FE9D4DEBEC}" diff --git a/Installer/WixProjBuilder.exe b/Installer/WixProjBuilder.exe index af99ffc71..2553f54bc 100644 Binary files a/Installer/WixProjBuilder.exe and b/Installer/WixProjBuilder.exe differ diff --git a/WizardForm/Strings/Dialog.Designer.cs b/WizardForm/Strings/Dialog.Designer.cs index fe2655e94..771082fa2 100644 --- a/WizardForm/Strings/Dialog.Designer.cs +++ b/WizardForm/Strings/Dialog.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.237 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/WizardForm/WizardForm.csproj b/WizardForm/WizardForm.csproj index 6e4314c3c..39a2e2331 100644 --- a/WizardForm/WizardForm.csproj +++ b/WizardForm/WizardForm.csproj @@ -15,7 +15,6 @@ 3.5 - v2.0 diff --git a/WizardForm/app.config b/WizardForm/app.config index 73859b00e..89dc7d426 100644 --- a/WizardForm/app.config +++ b/WizardForm/app.config @@ -1,3 +1,3 @@ - + diff --git a/thirdparty/HttpServer/Fadd.dll b/thirdparty/HttpServer/Fadd.dll new file mode 100644 index 000000000..2cfbbc563 Binary files /dev/null and b/thirdparty/HttpServer/Fadd.dll differ diff --git a/thirdparty/HttpServer/Fadd.xml b/thirdparty/HttpServer/Fadd.xml new file mode 100644 index 000000000..6524878e5 --- /dev/null +++ b/thirdparty/HttpServer/Fadd.xml @@ -0,0 +1,9136 @@ + + + + Fadd + + + + + Checks if a primitive type is between min and max. + + + Language file items: + + + ItemName + Language text + + + Between + '{0}' must be between {1} and {2}. + + + BetweenString + '{0}' may only have {1} up to {2} letters. + + + BetweenStringLength + '{0}' must be {1} letters long. + + + + + + + Base class for all Fadd validation attributes. + + + + Each validation class should document what their language entries should say, and their names. + This should be done in the remarks section using a table list, example: + + + ItemName + Language text + + + ValidateLetters + '{0}' may only contain letters. + + + ValidateLettersExtra + '{0}' may only contain letters and one of the following characters: {1} + + + It's recommended that the item names (or terms in the table above) equals the validation class name. + i.e. ValidateEmailAttributes adds a language item called "Email". + + + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is supported. + + + + Check's whether a value is empty or not. + + value to check. + true if the value is empty. + + + + Check's whether a value is empty or not. + + value to check. + value is considered to be not specified if the value matches this parameter + true if the value is empty. + + + + Initializes a new instance of the class. + + Value must be equal to this or larger. + Value must be equal or less than this. + Thrown if the max value is more than the min value + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + Thrown if an unsupported object value is supplied + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is supported. + + + + Min value + + + + + Max value + + + + + + + + + + + + + + + Tests it. + + My arg. + The value. + + + + + Tests it. + + My arg. + The value. + + + + + Does the this. + + + + + Modify the id of the testobject + + + + + Tests it. + + My arg. + The value. + + + + + Tests it. + + My arg. + The value. + + + + + Does the this. + + + + + Modify the id of the testobject + + + + + This class creates a command channel which can be used to send a command to a remote end point. + + + + + Try to use this port if it's not occupied. + + + + + Initializes a new instance of the class. + + Dispatcher used to invoke commands comming through the channel. + + + + Initializes a new instance of the class. + + Dispatcher used to invoke commands comming through the channel. + Socket used to transport commands. + + + + Initializes a new instance of the class. + + Dispatcher used to invoke commands comming through the channel. + Channel used to tranport commands. + + + + Releases unmanaged resources and performs other cleanup operations before the + is reclaimed by garbage collection. + + + + + Send unhandled commands through the tunnel. + + + Off by defualt. + + + + + + Handle commands tagged with the interface. + + + Off by default. + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + 2 + + + + Connect to other side. + + + + + + + Close command tunnel + + + + + Stops this instance. + + + + + Invoked when client disconnects + + + + + True if socket should auto reconnect connection is closed due to any network error. + + + + + Parse 'AND' + + + + + Tokens are used to parse a specific part of the text. + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Should be called in to let the Token parse all children. + + The containing the data to parse. + that is being built.. + true if a child have been matched and parsed. + Are called from . + + + + Should be called in to let the Token parse all children. + + The containing the data to parse. + that is being built.. + true if a child have been matched and parsed. + Are called from . + + + + Used to check if another child got a match + + used to read data. + being built. + that should not be included in the check. + true if a child matches the current position in the + + + + Add a possible child token. + + The token. + + + + Get parent token + + + + + This token is the final child in the current leaf, parse no more children + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Used to parse SQL conditions + + + + + Chars interpreted as white spaces: NewLine,LineFeed,Tab,Space + + + + + Initializes a new instance of the class. + + + + + Setup all tokens + + + + + Parse the tree + + Text to parse. + a parsed tree + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Not equal condition + + + + + Base class for all objects that are used to build a tree. + + + + + Adds the specified child. + + The child. + + + + Returns an enumerator that iterates through the collection. + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + 2 + + + + String representation of the token + + Also include all children in the output + Returns a human friendly string + + + + Used to determine the kind of this token + + + + + Gets or sets the parent. + + The parent. + + + + Last added child in the current node. + + + + + Gets the items. + + The items. + + + + Get one of the children + + zero-based index. + + + + + Depth in parse tree. + + + + + Number of children. + + + + + Used to identify conditions (used by Property to see it the text is a property or something else) + + + + + Condition string + + " != " + + + + Used to determine the kind of this token + + + + + + + + + + + Conditional representation. + + + + + + Used to determine the kind of this token + + + + + + = or == + + + + + Equal condition as string + + " == " + + + + Used to determine the kind of this token + + + + + + We are getting into a sub context (nested conditions) + + + + + Used to determine the kind of this token + + + + + + Parse a OR token + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Used to parse SQL conditions + + + + + New Line, Line Feed, Tab and Space. + + + + + Initializes a new instance of the class. + + + + + Setup all tokens + + + + + Parse the tree + + Text to parse. + a parsed tree + + + + Arguments for + + + + + Initializes a new instance of the class. + + Plugin assembly information. + + + + Evidence that the plugin should be loaded with. + + + + + Plugin assembly information + + + + + Called when an assembly is about to be loaded and we need the evidence for it. + Specify the evidence by using + + PluginManager + Assembly information + + + + This node is returned instead of null. + + + + + Contains language translations used to create multilingual applications + + + The LanguageNode provides a base class for different implementations of a hierachial language structure + + + + + Contains language translations used to create multilingual applications + + + The ILanguageNode provides a base class for different implementations of a hierachial language structure + + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + string if found; otherwise null. + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + check for paths in . + string if found; otherwise null. + + + + Gets a sub category + + The category name + if found; otherwise null. + If name is null + + + + Number of translated texts in the specified language. + + Language to get text from. + Number of text items + + + + Determine if the node contains a text item with the specified name (for the current lcid) + + True if the node contains a language element with the specified name for the current language + + + + Determine if the node contains a text item with the specified name for the specified language + + True if the node contains a language element with the specified name for the specified language + + + + Determine if a category contains a specific language. + + Language to get text from. + true if the specified language contains the text string; false if not. + + + + Checks if current language contains a child node with the specified name. + + Name of child node + true if found; otherwise false. + + + Empties all saved values in the node and its sub nodes + + + + Gets the parent language node. + + + null if no parent node exists. + + + + + Gets the mother of all nodes. + + + Root node will return itself. + + + + + Gets the number of languages (and not the number of text items). + + + + + Gets the name of this node. + + + + + Gets language to use if the requested language is not found. + + + + + Gets a localized text string in the current language. + + Phrase to find, can also contain a path. + text if found; [textName] if not. + + + lang["Name"] // => "Name" + lang["/Users/View/Name"] // => "Name" + lang["Naem"] // => "[Naem]" since it's missing + + + + + + Gets a localized text string in the specified language. + + Phrase to find, can also contain a path. + Get translation from this language. + text if found; [textName] if not. + + + lang["Name"] // => "Name" + lang["Name"] // => "Name" + lang["Naem"] // => "[Naem]" since it's missing + + + + + + An empty language node (used instead of null). + + + + + Initializes a new instance of the class. + + The default LCID. + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + string if found; otherwise null. + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + check for paths in . + string if found; otherwise null. + + + + Number of translated texts in the specified language + + + + + + + Determine if the node contains a text item with the specified name (for the current lcid) + + + + True if the node contains a language element with the specified name for the current language + + + + + Determine if the node contains a text item with the specified name for the specified language + + + + + True if the node contains a language element with the specified name for the specified language + + + + + Determine if a category contains a specific language. + + + + + + + Checks if current language contains a child node with the specified name. + + Name of child node + true if found; otherwise false. + + + Empties all saved values in the node and its sub nodes + + + + Gets a sub category + + The category name + + if found; otherwise null. + + If name is null + + + + Checks paths that are specified in text names + + For instance /user/show/firstname + language to check in + string if language contains a path, otherwise null. + + + + Add a localized text string. + + locale + Name identifying the string. Used to fetch the string later on. + Localized string + + + + Sets a localized text string. If the a string with the specified name exists it will be overwritten. + + locale + Name identifying the string. Used to fetch the string later on. + Localized string + + + + Adds a sub category + + Name of the sub category + If a category with the specified name already exists + If name is null + + + + Value that should be returned if the text is not found. + + + + + + + Set default locale + + Locale to set. + + + + All child nodes. + + + + + Parent language category + + + + + Mother of all nodes. + + + + + Get a localized text string in the current language. + + Phrase to find. + text if found; [textName] if not. + + + lang["Name"] // => "Name" + lang["Naem"] // => "[Naem]" since it's missing + + + + + + Get a localized text string + + + Phrase to find. + text if found; [textName] if not. + + + lang["Name"] // => "Name" + lang["Naem"] // => "[Naem]" since it's missing + + + + + + Number languages + + + + + Returns the name of the node + + + + + LCID to use if the specified or current LCID is not found. + + + + + Initializes a new instance of the class. + + The default LCID. + + + + Add a localized text string. + + locale + Name identifying the string. Used to fetch the string later on. + Localized string + + + + Sets a localized text string. If the a string with the specified name exists it will be overwritten. + + Name identifying the string. Used to fetch the string later on. + locale + Localized string + + + + Adds a sub category + + Name of the sub category + If a category with the specified name already exists + If name is null + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + string if found; otherwise null. + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + check for paths in . + string if found; otherwise null. + + + + Number of translated texts in the specified language + + + + + + + Determine if the node contains a text item with the specified name for the specified language + + + + + True if the node contains a language element with the specified name for the specified language + + + + + Determine if a category contains a specific language. + + + + + + Empties all saved values in the node and its sub nodes + + + + Get a localized text string in the current language. + + Phrase to find. + text if found; [textName] if not. + + + lang["Name"] // => "Name" + lang["Naem"] // => "[Naem]" since it's missing + + + + + + Get a localized text string + + + Phrase to find. + text if found; [textName] if not. + + + lang["Name"] // => "Name" + lang["Naem"] // => "[Naem]" since it's missing + + + + + + Number languages + + + + + This is a bit more advanced command pattern. The regular command pattern + ties the command to the the class that handles the command. This + pattern removes that binding and makes the commands independent of + the logic that executes it. + + The pattern also allows multiple handlers for each command, and you + can also add handlers for unhandled commands. + + + + // Map a handler to a command: + distlight.Add(typeof(MyCommand), OnMyCommand); + + // invoke my command: + if (!distlight.Invoke(new MyCommand(myUserId))) + Console.WriteLine("No one handled the command!"); + + + + + + Add a command handler. + + Must be a class, an attribute. + handler handling the command + If handler have been added to that type already. + + Program.Commands.Add(typeof(MyCommand), OnMyCommand); + + + + + Remove a command handler. + + type to remove + delegated that was mapped to the type. + + + + Invoke a command. + + command to invoke. + Handled that should not be invoked. + true if command was handled. + + + + Invoke a command. + + command to invoke. + true if command was handled. + + + + Invoke a command. + + object that is invoking the command. + command to invoke. + Handled that should not be invoked. + true if command was handled. + + + + Invoke a command. + + object that is invoking the command. + command to invoke. + true if command was handled. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + Handler that should not receive the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + object that is invoking the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + object that is invoking the command. + Handler that should not receive the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke this method when the command is complete, or if you want to wait + on the command. + + + + + + + Tells us if we have a handler for the specified type. + + Type to check + True if a handler have been registered otherwise false. + + + + Handler for unhandled commands. + + returning true will make the command look like it was handled + + + + Design by contract validator + + + + + "{0} must be specified." + + + + + "'{0}' do not equal '{1}'." + + + + + "'{0}' must be between {1} and {2}." + + + + + "'{0}' must be between {1} and {2} characters." + + + + + "'{0}' must be larger or equal to {1}." + + + + + "'{0}' must be larger or equal to {1}." + + + + + "'{0}' must evaluate as true. + + + + + "'{0}' must evaluate as false. + + + + + "'{0}' must be less or equal to {1}." + + + + + "'{0}' must be less or equal to {1}." + + + + + "'{0}' must be specified and not empty." + + + + + "'{1}' must be assignable from {0}." + + + + + Contains language information + + + + + The specified statement/parameter must be true. + + statement/parameter to evaluate. + Name of the message or param. + If statement is not true. + + + + The specified statement/parameter must be false. + + statement/parameter to evaluate. + Name of the message or param. + If statement is true. + + + + Two values can't be equal. + + value/constant to compare to. + parameter value. + parameter name, or a error message. + If contract fails. + and are both required. + + + + Value must be between (or equal) min and max + + minimum value. + maximum value. + parameter value. + parameter name, or a error message. + If contract fails. + + + + Betweens the specified min. + + minimum value. + maximum value. + parameter value to check. May not be null. + parameter name, or a error message. + If contract fails. + + + + Betweens the specified min. + + minimum value. + maximum value. + parameter value. + parameter name, or a error message. + may be null if this parameter is false. + If contract fails. + + + + Checks if the value is equal or larger. + + minimum value. + parameter value. + parameter name, or a error message. + If contract fails. + + + + Checks if the value is equal or larger. + + minimum value. + parameter value. + parameter name, or a error message. + If contract fails. + + + + Checks if the value is equal or larger. + + minimum value. + parameter value. + parameter name, or a error message. + If contract fails. + + + + Checks if the value is equal or larger. + + minimum value. + parameter value (may not be null). + parameter name, or a error message. + If contract fails. + + + + Checks if the value is equal or larger. + + minimum value. + parameter value. + parameter name, or a error message. + may be null if this parameter is false. + If contract fails. + + + + Checks if the value is less or equal. + + maximum value. + parameter value. + parameter name, or a error message. + If contract fails. + + + + Checks if the value is less or equal. + + maximum value. + parameter value. + parameter name, or a error message. + If contract fails. + + + + Checks if the value is less or equal. + + maximum value. + parameter value. + parameter name, or a error message. + may be null if this parameter is false. + If contract fails. + + + + Checks if the value is less or equal. + + max value. + parameter value. + parameter name, or a error message. + If contract fails. + + + + Parameter is required (may not be null). + + parameter value. + parameter name, or a error message. + If contract fails. + + + + The specified string may not be null or empty. + + parameter value. + parameter name, or a error message. + If contract fails. + + + + Check if the specified type can be assigned from the parameter type. + + Type that the parameter must be (or derive/implement). + instance + error message or parameter name + + + + Contains language information + + + Language may only be specified once. + + If language have been previously specified. + + + + Exception thrown when a validation fails. + + + + + Initializes a new instance of the class. + + The original error message (have not been formatted). + Formatted message. + Message arguments. + + + + Initializes a new instance of the class. + + The original error message (have not been formatted). + Formatted message. + + + + Unformatted error message, {0} have not been replaced with parameter name. + + + Can be used if you want to translate messages. + + + + + Arguments to string to format. First argument is parameter name. + + + + + Contains a value in a condition. + + + + + Used to identify conditions (used by Property to see it the text is a property or something else) + + + + + Initializes a new instance of the class. + + Property that this value belongs to. + + + + Returns SQL + + @[propertyName] + + + + Identifier for this token. + + + + + Property that this token belongs to. + + + + + Name of the parameter. + + + + + > + + + + + Returns SQL + + " > " + + + + Used to determine the kind of this token + + + + + + 'AND' SQL token + + + + + SQL 'AND' text + + " AND " + + + + Used to determine the kind of this token + + + + + + Parse >= + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + An IN token. + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parses a condition string and compares it to an item + + + This class is NOT threadsafe. + + + + + Initializes a new instance of the class. + + Conditions, should look like: "Age >= ? AND FirstName == ?". + Parameters used in the condition, for instance 17 and Jonas in the example above. + + Evaluator = new Evaluator("FirstName = ? AND Age = ?", "Adam", 10}); + + + + + Initializes a new instance of the class. + + First index is conditions, the rest is parameters. + + Evaluator = new Evaluator(new object[]{"FirstName = ?", "Adam"}); + + + + + Evaluates an instance. + + The instance. + + + + + Go through a list and find all items. + + + + + + + + Evaluate all tokens in the same level. + + + + + + + Token should be a property name. + + + + + + Will take three of the tokens (property CONDITION target) and evaluate them. + + If values can't be casted to property type + + + + + + + + + + + + + + + + + + + User used during testing. + + + + + Gets or sets the first name. + + The first name. + + + + Gets or sets the last name. + + The last name. + + + + Gets or sets the age. + + The age. + + + + Gets or sets the status. + + The status. + + + + + + + + + Class to subscribe to change events on a file and update a language hierarchy every time the file changes. + + + If the file being watched is deleted or renamed no more changes will be made to the language node. + Also be observant on the fact that the class cannot Remove entries from a , only add or modify entries. + This restriction is needed because a single YamlWatcher only watches One file and a can be connected to several + watchers and files, deleting anything could clear things for another file. + + + + + Loads and watches language files on disk. + + + Future version can watch different file types, and load them using different loaders. + + + + + Load and watch another file on disk. + + Relative or absolute path to language file. + Node that have been loaded. + + + + Parse YAML contents and add any language nodes to the watchers language + + YAML contents + The name of the node to add the contents into + Node that have been loaded. + + + + Root language node. + + + + + files that need to be reloaded + + + + A timer to use as delay if the file is currently in use + + + + Instantiates the class to listen to changes in a file, also reads the file and fills the language node with language entries + + Language node to fill + The filename to watch + Thrown if the specified file does not exist + + + + Load and watch another file on disk. + + Relative or absolute path to language file. + Node that have been loaded. + + + + Parse YAML contents and add any language nodes to the watchers language + + YAML contents + The name of the node to add the contents into + Node that have been loaded. + + + + Callback for when the file should be read again + + + + + Sets the class to watch the specified file + + + + + Callback for when the file changes + + + + + Private loading of file to wrap read failure control + + + + + Fill the root node with languages and categories from the specified file + + Full file path + The root node to fill + + + + Function to stop the watcher + + + + + Root language node. + + + + + + Provides a general view of the model needed for saving migration data in the database + + + The model and mapping must be specified explicitly by the migration user since tables most oftenly + should match a certain coding convention. Explicitly declaring the model also allows for saving + extra version data. + + + + Gets or sets the version number of the migration + + + Gets or sets the date and time for when the database was updated to the migration version + + + + Main exception in the data layer. + + + + + Initializes a new instance of the class. + + Error message. + Inner exception. + + + + Initializes a new instance of the class. + + Error message. + + + + Initializes a new instance of the class. + + SQL query that failed + Error message. + + + + Initializes a new instance of the class. + + SQL query that failed + Error message. + Inner exception. + + + + SQL query that failed. + + + + + Checks whether a field have been specified or not. + + + Language file items: + + + ItemName + Language text + + + AlphaNumeric + '{0}' may only contain alpha numeric letters. + + + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + value is considered to be not specified if the value matches this parameter. + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + + + + Determines if the validation support the specified type. + + Property/Value type. + true if type is supported. + + Used when validation objects are generated. + + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Validates that a string property represents valid Hex value (Value should not either too large or too small for an Int64). + + + + + Name of the language item for this validation. + + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + Thrown if the value supplied is not a + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + + A localized error message if the validation failed; otherwise null. + + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is supported. + + Used when validation objects are generated. + + + + + Contains all validations for a certain type of object. + + + + + true if we should save a list with all generated validators, or generate new ones each time. + + + + + Create a new validation object. + + Type of object to validate + + + + + Validate a specific property. + + Instance to validate + Name of property + A list with all validations that failed. Or an empty list of no validations failed. + + + + Validate a specific property. + + An application specific context that's passed to all validation attributes. Useful if you've implemented your own validations. + Instance to validate + Name of property + + A list with all validations that failed. Or an empty list of no validations failed. + + + + + Validate a specific property. + + Instance to validate + Name of property + List to fill with validation errors, must not be null. + + + + Validate a specific property. + + An application specific context that's passed to all validation attributes. Useful if you've implemented your own validations. + Instance to validate + Name of property + List to fill with validation errors, must not be null. + + + + Validate all properties in the object. + + Instance to validate + A list with validation errors, or an empty list of all validations succeeded. + + + + Validate all properties in the object. + + An application specific context that's passed to all validation attributes. Useful if you've implemented your own validations. + Instance to validate + A list with validation errors, or an empty list of all validations succeeded. + + + + Type that this validator is for. + + + + + Used to be able to provide context to validation classes during validation. + + + + + Initializes a new instance of the class. + + Attribute that is going to be validated. + + + + Attribute about to be used for validation. + + + + + Context that should be passed to the validation class. + + + + + Contains user information about a plugin. + + + Should be returned in the current language. + + + + + + Name of the plugin, it should be in the local language if possible. + + + + + Description of what the plugin does. Shown to the end user. + + + The description may contain links to other pages and images. + Just make sure that your controller can handle them. + Links should be opened in new windows and not leave the market place. + + + + + Author homepage + + + + + Copyright owner of this plugin. + + + + + Contains translation texts that have been loaded into memory + + + + + int is LCID, NameValueCollection contains all phrases. + + + + Instantiates a LanguageNode that holds the language data in memory + The defualt language code + The name of the node + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + string if found; otherwise null. + + + + Get a text string in the specified language. + + Language to fetch from. + name of text to find. + check for paths in . + string if found; otherwise null. + + + + Adds a text entry to the language node + + Name of the entry + Language of the entry + The text of the entry + + + + Sets a localized text string. If the a string with the specified name exists it will be overwritten. + + Name identifying the string. Used to fetch the string later on. + locale + Localized string + + + + Adds a sub category + + Name of the sub category + If a category with the specified name already exists + If name is null + + + + Returns number of text entries in the requested language + + The language to examine + + + + Determine if the node contains a text item with the specified name for the specified language + + + + + True if the node contains a language element with the specified name for the specified language + + + + + Determine if a category contains a specific language. + + + + + + Empties all saved values in the node and its subnodes + + + Returns the requested entry using the systems current language + Name of the entry to retrieve + The entry or if not found in the current language, default language or in parent nodes returns [textName] + + + + A requested text entry in the requested language + + Name of the entry to retrieve + Language code for the language to retrieve the text in + The entry or if not found in the current language, default language or in parent nodes returns [textName] + + + + Returns the number of languages in the node + + Returns the number of languages and Not text entries + + + + + + + + + A command that will be invoked + + + + + Set handled state. + + + + + + true if the command have been handled. + + Can be used to determine if another + handler already have processed the command. + + + + Initializes a new instance of the class. + + The name. + + + + Gets or sets the name. + + The name. + + + + + + + + + + + + + + + + + + + Initializes a new instance of the class. + + The name. + + + + + + + + + Initializes a new instance of the class. + + The name. + + + + + + + + + + + + + + Initializes a new instance of the class. + + The name. + + + + Gets or sets the name. + + The name. + + + + + + + + + Initializes a new instance of the class. + + The name. + + + + Gets or sets the name. + + The name. + + + + ORDER BY SQL Clause. + + + + + Returns SQL + + " ORDER BY " + + + + Used to determine the kind of this token + + + + + Class/Table name + + + + + Initializes a new instance of the class. + + Name of class. + + + + Returns SQL name + + Table name + + + + Used to determine the kind of this token + + + + + Name of class + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parse >= + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Contains a mapping between a database table and a .Net class. + + + + + Gets column name from a property name. + + + + + + + Gets current primary key. + + The primary key if exactly ONE is defined; otherwise null. + + + + Gets table name. + + + + + Gets class type + + + + + Gets all mapped properties + + + + + Gets all primary keys + + + + + SQL92 column types. + + + + + One bit + + + + + Short string + + + + + floating point + + + + + Int column + + + + + DateTime / Timestamp + + + + + Date only + + + + + Time only + + + + + Bool, int(1) + + + + + Long string + + + + + Common operations for columns + + + + + Convert a column type to a .Net type + + column type to convert + + + + + Class for building information strings about a proxy method + + + + + Instantiates a new object to parse and create readable data for a method + + + + + + + ]]> + + The method to generate the name for + The methods name + + + + Joins method name together with all parameter types creating an identifying string for the method + + The method to generate the name for + The method name + + + + Retrieves the call to Type.GetMethod needed to retrieve the method + + The method to retrieve + The correct name of the method to retrieve + + ie. GetMethod("GetUser", new Type[]{typeof(Int32)}) + + + + + Retrieves a string representation of a list putting together all the parameters passed to the method in an object array + + The method for which to generate the argument array string + ie new object[]{value, argument} + + + + Retrieves the argument list + + The method to generate the argument from + A list to which the methods needed types can be added + Ie GetData(int arg1, string arg4) => Int32 arg1, String arg4 + + + + Retrieves a string representing the call to Type.GetMethods needed to retrieve the method + + + ie. GetMethod("GetUserName", new Type[]{typeof(Int32), typeof(String)}); + + + + + Retrieves a string representation of the method return type + + + + + Retrieves the name of a method + + + + + Retrieves a signature for the method, ie _getValuesInt32String + + + + + Retrieves a string for declaring the ObjectProxy method in the form + + + + + Retrieves a list of types the method depends on + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Generic logger interface. + + + Used since we do not want to force everyone to use the Fadd.Logging framework. + Implement your own proxy to use your favorite logging mechanism + + + + + + A dictionary mapping not only values but also sub nodes of the same + type + + + + + Instances the + + + + + Instances the with a given name that can be used to identify the map later + + A unique name in a hierarchy to set for the map + + + + Adds a value + + The key to map the value to + The value to add + Thrown if a value has already been added with the given key + + + + Updates or adds a value defined by the specified key + + The key of the value to update or add + The value to set for the specified key + + + + Removes a value specified by the given key from the + + The key to remove the value for + True if a value was removed + + + + Retrieves a child using a key + + The key the child is mapped to + Null if not found + + + + Returns true if a value mapped to the given key is contained by the map + + Key the value is supposed to be mapped to + True if a key is found + + + + Returns true if a value is mapped + + The value to examine whether or not it is contained by the map + True if the value is found + + + + Returns true if a child exists that is mapped with the given key + + The key to examine child mappings for + True if a child is found + + + + Returns true if the map contains the specified child + + The map to examine whether or not it is contained + True if the map is a child of the instance + + + + Removes a child if contained. Since the name set for the map will be used to remove the map a name must have been specified. + + The map to remove + True if the child was removed + If removed the will be set to null for the map + + + + Removes a child if contained + + The key or name of the child to remove + True if the child was removed + If removed the will be set to null for the map removed + + + + Adds a child to the using the name specified in + + The map to add as a child, must have a defined se + + Thrown if the map is already a child of another instance, + it must be removed from there manually before being added to avoid hiding logical errors in code + + + + + Adds a child to the using the name supplied + + The map to add as a child + The name to set for the map, will override any previous name + + Thrown if the map is already a child of another instance, + it must be removed from there manually before being added to avoid hiding logical errors in code + + + + + Returns an enumerator that can iterate through all the values contained by the map + + + + + Returns an enumerator that can iterate through all the values contained by the map + + + + + Sets or gets a value held by the map + + Key mapping the value + Null if the value wasn't found + + + + Retrieves all values held by the map + + + + + Retrieves all child held by the map + + + + + Retrieves the name identifying the map, is null if no name is set + + + + + Retrieves any parent to the map, is null if no parent is set + + + + + Validates that a string property is an correct email. + + + Language file items: + + + ItemName + Language text + + + Email + '{0}' must be a valid email address. + + + + + + + Localize the error. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation support the specified type. + + Property/Value type. + true if type is supported. + + Used when validation objects are generated. + + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + Thrown if the object isn't a string + + + + Tag commands with this interface to be able to transport them through the + CommandChannel. + + + + + Copy all stuff that have been modified by the command handler. + + Command to copy reply from. + + + + This is a bit more advanced command pattern. The regular command pattern + ties the command to the the class that handles the command. This + pattern removes that binding and makes the commands independent of + the logic that executes it. + + The pattern also allows multiple handlers for each command, and you + can also add handlers for unhandled commands. + + + + + // Map a handler to a command: + dispatcher.Add(typeof(MyCommand), OnMyCommand); + + // invoke my command: + if (!dispatcher.Invoke(new MyCommand(myUserId))) + Console.WriteLine("No one handled the command!"); + + + + + + When asynchronous invokes should be removed (if have not been called.) + + + + + Attributes list used for commands that have not been mapped. + + + + + Classes/interfaces list used for command that have not been mapped. + + + + + Each command is mapped to different handlers + when the command is invoked. This is done once, + then this list is used to invoke the commands (= speed increase) + + + + + Classes/interfaces list used for command that have not been mapped. + + + + + Initializes a new instance of the class. + + + + + Create a new command manager + + Parent manager, it will receive all commands that are + invoked in this instance, or just all unhandled ones. + true if all commands should be sent to the parent; otherwise just all unhandled will be sent. + + + + Add a command handler. + + Must be a class, an attribute. + handler handling the command + If handler have been added to that type already. + + Program.Commands.Add(typeof(MyCommand), OnMyCommand); + + + + + Remove a command handler. + + type to remove + delegated that was mapped to the type. + + + + Invoke a command. + + object that is invoking the command. + command to invoke. + Handled that should not be invoked. + true if command was handled. + + + + Invoke a command. + + command to invoke. + true if command was handled. + + + + Invoke a command. + + command to invoke. + Handled that should not be invoked. + true if command was handled. + + + + Invoke a command. + + object that is invoking the command. + command to invoke. + true if command was handled. + + + + Invoke a command asynchronously + + object that is invoking the command. + Command to invoke + Handler that should not receive the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + IAsyncResult if command was invoked successfully; otherwise null. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + object that is invoking the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + Handler that should not receive the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke this method when the command is complete, or if you want to wait + on the command. + + + + + + + Tells us if we have a handler for the specified type. + + Type to check + True if a handler have been registered otherwise false. + + + + + + + + + + A command handler have canceled a command, this event gives you a chance to + ignore that cancellation. + + command that was canceled. + handler that canceled the command. + true means that we can cancel; false that we should continue + + + + Map a command that have never been mapped. + + + + + + + Map a command to all handlers. + this is done to speed up the invoke process. + + + + + + + Event that can override the property. + + + + + + Handler for unhandled commands. + + returning true will make the command look like it was handled + + + + Used to keep track of all asynchronous commands. + + + + + Gets an indication whether the asynchronous operation has completed. + + + + true if the operation is complete; otherwise, false. + + 2 + + + + Gets a that is used to wait for an asynchronous operation to complete. + + + + A that is used to wait for an asynchronous operation to complete. + + 2 + + + + Gets a user-defined object that qualifies or contains information about an asynchronous operation. + + + + A user-defined object that qualifies or contains information about an asynchronous operation. + + 2 + + + + Gets an indication of whether the asynchronous operation completed synchronously. + + + + true if the asynchronous operation completed synchronously; otherwise, false. + + 2 + + + + Corresponds to "IS NOT NULL". + + + + + " IS NOT NULL" + + + + + + Used to determine the kind of this token + + + + + = + + + + + Returns SQL + + " = " + + + + Used to determine the kind of this token + + + + + + Represents a comma + + + + + Conditional string + + ", " + + + + Used to determine the kind of this token + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + A property name + + + + + Initializes a new instance of the class. + + Name of property. + + + + Returns name of property + + name of property + + + + Used to determine the kind of this token + + + + + + Name of property + + + + Specific exception for errors occured during xml handling + + + Describes an exception occured during migration of database + + + Initializes the exception with a message + The exception message describing the error occured + + + Initializes the exception with a message + The exception message describing the error occured + The exception triggering the throwing of this exception + + + Initializes the + Error message to display + + + Initializes the + Error message to display + Exception that brought the throwing of the xml exception + + + Initializes the + Error messages to display + + + Retrieves the error messages not prepended with readable error string + + + + Connection helper for SQL Server. + + + + + Used + + + + + Create a new connection to the database. + + An open database connection. + If connection fails. + + + + Try to get next sequence value. + + A command to perform actions with, we pass commands since they might be in a transaction. + Name of the sequence/generator. + A generated value. + If something failed + + + + Gets the latest auto incremented value from the database. + + A command to perform actions with, we pass commands since they might be in a transaction.. + Name of the column that the auto incremented value is for. + auto incremented value + If something failed + + + + Modifies the SQL statement to include paging. + + Database mapping + SQL statement generated by the DAL. + 0 based index + number of entries per page + SQL statement with paging + + + helper.Paging("SELECT * FROM users", 5, 10); // --> "SELECT * FROM users LIMIT 10 OFFSET 50" + + + + + + Connection string + + + + + Gets the paramter prefix + + + For SQL @ + For MySQL ? + For Oracle : + + + + + Initializes a new instance of the class. + + The connection string. + + + + Create a new connection to the database. + + An open database connection. + If connection fails. + + + + Try to get + + A command to perform actions with, we pass commands since they might be in a transaction. + Name of the sequence/generator. + A generated value. + If something failed + SQL Server do not support sequences. + + + + Gets the latest auto incremented value from the database. + + A command to perform actions with, we pass commands since they might be in a transaction.. + Name of the column that the auto incremented value is for. + auto incremented value + If something failed + + + + Modifies the SQL statement to include paging. + + Used to generate SQL Servers wierd paging method. + SQL statement generated by the DAL. + 0 based index + number of entries per page + SQL statement with paging + + + helper.Paging("SELECT * FROM users", 5, 10); // --> "SELECT * FROM users LIMIT 10 OFFSET 50" + + + + Only SQL Server 2005 and above is supported. + + + + + Connection string + + + + + + Gets the paramter prefix + + Will return a @ + + + + Validates that a string only contains letter, digits and any of: |-_.:,;'^~¨'*!#¤%&/()=?`´+}][{€$££@§½ ]]> + + + + Language file items: + + + ItemName + Language text + + + AlphaNumeric + '{0}' may only contain alpha numeric letters. + + + + + + + + Input may only be letters and digits, foreign chars (for instance åäö) etc are included. + + + Language file items: + + + ItemName + Language text + + + LettersAndDigits + '{0}' may only contain letters and digits. + + + LettersAndDigitsExtra + '{0}' may only contain letters. digits and '{1}'. + + + + + + + Foreign characters (letters used for instance in the Swedish language) + + + + + Foreign characters (letters used for instance in the Swedish language) and space, dot and comma. + + + + + Space, dot and comma. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The extra characters. + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + Only strings are valid for this type. + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation support the specified type. + + Property/Value type. + true if type is supported. + + Used when validation objects are generated. + + + + + Gets the extra characters. + + The extra characters. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The extra characters. + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Does validation using the current language. + + + This class is thread safe, meaning that you can have multiple threads using it at the same time. + + + + + Initializes a new instance of the class. + + Node with the validation language. + + + + Localize all error messages. + + A list with validation errors. + containing language strings for the validated model. + A list with localized errors; or an empty list if no validations failed. + + + + Validates a model and returns errors in the current language. + + + Current language are decided by . + + An application specific context that's passed to all validation attributes. Useful if you've implemented your own validations. + Model being validated. + Language node for the model. + A collection with all validations that failed; an empty collection if not validations failed. + + + + + This dispatcher is used to be able to specify which commands that can be invoked. + + + + + A typical usage would be if you have a plugin system and you do not want to allow + all plugins to be able to execute all commands. Then you create a proxy dispatcher + and expose it to the plugins, instead of the core dispatcher which contains all command + mappings. In this way, your core system it still protected while plugins can access + a subset of it. + + + + + + Initializes a new instance of the class. + + Parent dispatcher (that is used by the rest of the system). + Commands that may be invoked. + Commands that may be handled. + + + + Initializes a new instance of the class. + + Parent dispatcher (that is used by the rest of the system). + No commands are allowed unless you use the event. + + + + Add a command handler. + + Must be a class, an attribute. + handler handling the command + If handler have been added to that type already. + The specified command may not be handled. + + Program.Commands.Add(typeof(MyCommand), OnMyCommand); + + + + + Remove a command handler. + + type to remove + delegated that was mapped to the type. + + + + Invoke a command. + + object that is invoking the command. + command to invoke. + Handled that should not be invoked. + true if command was handled. + + + + Invoke a command. + + command to invoke. + true if command was handled. + If you may not invoke the specified command. + + + + Invoke a command. + + command to invoke. + Handled that should not be invoked. + true if command was handled. + + + + Invoke a command. + + object that is invoking the command. + command to invoke. + true if command was handled. + + + + Invoke a command asynchronously + + object that is invoking the command. + Command to invoke + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + IAsyncResult if command was invoked successfully; otherwise null. + If you may not invoke the specified command. + + + + Invoke a command asynchronously + + Command to invoke + object that is invoking the command. + Handler that should not receive the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + IAsyncResult if command was invoked successfully; otherwise null. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke a command asynchronously + + Command to invoke + IAsyncResult if command was invoked successfully; otherwise null. + Handler that should not receive the command. + Callback that is invoked then the command completes. + object that you can use to identify the command in the -method. + + + + Invoke this method when the command is complete, or if you want to wait + on the command. + + + + + + + Tells us if we have a handler for the specified type. + + Type to check + True if a handler have been registered otherwise false. + + + + Occurs when an unhandled exception have been caught. + + + + + This event is invoked when someone tries to add a handler for a command. + + + + + This event is invoked when someone tries to invoke a command. + + + + + Handler for unhandled commands. + + returning true will make the command look like it was handled + + + + A mapped command have been assigned all subscribers that + have been mapped to an interface, subclass, or attribute + that the command contains. + + + + + All delegates that will be invoked for this command. + + + + + Dirty means that a new handler have been added since + the command was last invoked. + This means that we need to recheck whether we got all + handlers. + + + + + Generates Object proxies. + + + Name the proxies with generic types to allow multiple generic types ie ListInt32Proxy and ListStringProxy. + Pretty up the use of properties a bit. + Add tests for interface hierarchies and properties. + Add support for events in interfaces. + + + + + Initializes a new instance of the class. + + Dispatcher used to send events. + + + + Generate an object proxy + + Type to generate a proxy for + + + + + Compiles the proxy type declaration string that has been build + + The type from which to create a proxy + The code to compile + The compiled object or null if it couldn't be created + + + + Add all properties + + + + + + Build code for a proxy class. + + Type to build code for. + a complete class (in text). + + + + + + + + + Returns SQL + + " < " + + + + Used to determine the kind of this token + + + + + + Contains a property value + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parse a OR token + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Contains a property value + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Database transaction. + + + Makes it possible to save a lot of changes only + if all changes can succeed. + + + + + Base interface. + + + + + Creates a new transaction. + + On of the s. + An object representing the new transaction. + + Once the transaction has completed, you must explicitly commit or roll back the transaction by using the Commit or Rollback methods. + + + + + Creates a new transaction. + + An object representing the new transaction. + + Once the transaction has completed, you must explicitly commit or roll back the transaction by using the Commit or Rollback methods. + + + + + Get a single object. + + Type of object to fetch + complete SQL statement with class/properties instead of table/columns. + parameters used in the statement. + object if found; otherwise null + If the class/table mapping is incorrect. + If data layer fails. + + + + Executes an statement in the database. + + Type of object to fetch + complete SQL statement with class/properties instead of table/columns. + parameters used in the statement. + object if found; otherwise null + If the class/table mapping is incorrect. + If data layer fails. + + + + Get a single object. + + Type of object to fetch. + Conditions (corresponds to statements in the WHERE clause). + Parameters used in the where clause. + object if found; otherwise null. + If the class/table mapping is incorrect. + If data layer fails. + + + + Get a single object using its primary key + + Type of object to fetch, must have a defined mapping. + Primary key value + object if found; otherwise null. + If the class/table mapping is incorrect. + If data layer fails. + + + + Retrieves a collection of items. + + Type of item to get. + Conditions to limit. + Parameters used in the condition. + A list of objects; or an empty list + + + + Retrieves all items of a certain type. + + Type of item to get. + A list of objects; or an empty list + + + + Gets all items defined by the . + + type of items to get. + Statement specifying which elements to get. + A list of elements. + + + + Execute a custom SQL query (Object SQL can not be used yet). + + type of items to get. + SQL query. + Parameters used in the query. + A list of elements. + + + + Save or update an object in the database (1. Checks if the PK is empty. 2. Checks if the object exists in the db). + + Value to create/update. + If the class/table mapping is incorrect. + If data layer fails. + + + + Create a new object in the database. + + Object to create. + If the class/table mapping is incorrect. + If data layer fails. + + + + Update an existing object. + + Object to update + If the class/table mapping is incorrect. + If data layer fails. + + + + Checks if an object exists by using it's primary key. + + Type of object to check + Primary key + true if object was found; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Checks if an object exists by providing it. + + object to check + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Checks if an object exists in the database + + Type of object to check. + Conditions. + Arguments used in the conditions. + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Count number of items. + + Type of object to check. + Conditions. + Arguments used in the conditions. + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Count number of items. + + Type of object to check. + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Counts by SQL. Write the whole statement yourself. + + The statement. + Number of returned rows + If data layer fails. + + + + Removes an object by using it's id property. + + Type of object to remove. + Primary key value + RemoveById requires exactly one primary key. + + + + Removes an object by using it's id property. + + Type of object to remove. + + + + Gets class that takes care of all providers. + + + + + Gets logger instance. + + + + + Cancel all changes made with the transaction. + + + Cancel will be called by Dispose if commit have + not been called before the transaction is disposed. + + + + + Write all changes to the database. + + + + + Contains a compiled SQL statements. + + + "Compiled" means that we've converted the statement from ObjectSQL + to real SQL with table and column names. + + + + + Initializes a new instance of the class. + + Mapping between columns and properties + SQL statement. + Properties that have been selected. null if no properties have been specified. + Arguments used in the SQL statement.. + + + + Create a new statement with specialized SQL. + + SQL string + New statement. + + + + Gets properties that are used as arguments to the statement. + + + + + Gets SQL statement. + + + + + Gets mappings between columns and properties. + + + + + Gets properties that have been selected. + + + null if no properties have been specified. + + + + + A found plugin and which of the requested types that it implements. + + + + + Initializes a new instance of the class. + + assembly that the plugin resides in. + plugin type. + assemblies public key. should be used to decide the amount of access for the module. + + + + Add another type + + + + + + Assemblies public key if signed. + + + + + Assembly full path + + + + + Location of DLL. + + + + + Requested types that exists in the assembly + + + + + Assembly public key in byte format. + + + + + Arguments for + + + + + Initializes a new instance of the class. + + Why we were disconnected. + SocketError.Success means that we disconnected due to invalid data/packet from the remote end. + + + + Why we were disconnected. + + + + + Called when a connection have been closed. + + Socket/Tunnel/Channel + Why it was disconnected. + + + + DELETE FROM clause + + + + + FROM SQL clause + + + + + Returns SQL + + " FROM " + + + + Used to determine the kind of this token + + + + + Returns SQL + + "DELETE FROM " + + + + Used to determine the kind of this token + + + + + + Parse >= + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + An IN token. + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Enumeration specifying a detail level in which to display a duration + + + + + Display duration in months and years + + + + + Display duration in days, months and years + + + + + Display duration in hours, days, months and years + + + + + Display duration in hours, days, months and years + + + + + Display duration down to the second + + + + + Interface for all localized date/time helpers. + + + + + Will try to parse the date/time using the local parser. + Will also try the default DateTime.Parse method if date is in an unknown format. + + date/time string + A + If the date/time format is invalid. + If month/day/hour/minute/second are not within their ranges. + + + + Will try to parse the date/time using the local parser. + Will also try the default DateTime.Parse method if date is in an unknown format. + + date string (may be null/empty). + time string (may be null/empty). + A + If the date/time format is invalid. + If month/day/hour/minute/second are not within their ranges. + + + + Tries to parse a date string. + + Can contain a complete date or parts of it (which parts depends on the country). + A + If the date format is invalid. + If month/day are not within their ranges. + + + + Tries to parse a date string. + + Can contain a complete date or parts of it (which parts depends on the country). + array that parsed values are written to. + where in array to start write values + If the date format is invalid. + If month/day are not within their ranges. + + + + Tries to parse a time string. + + Can contain a complete time or parts of it (which parts depends on the country). + A + If the time format is invalid. + If hour/minute/second are not within their ranges. + + + + Tries to parse a time string. + + Can contain a complete time or parts of it (which parts depends on the country). + array that will be filled. Must contain three slots. + Where in the array to start write values + If the time format is invalid. + If hour/minute/second are not within their ranges. + + + + Validate a date. + + year value + month value + day value + true if exceptions should be thrown on incorrect values. + true if specified values are a correct date; otherwise false. + + + + Validate + + hour + minute + second + true if exceptions should be thrown on incorrect values. + true if specified values are a correct date; otherwise false. + + + + Format a date as a string. + + A date/time + Examples: "Yesterday", "On monday" + + + + Format a date/time as a string. + + A date/time + Examples: "Yesterday at 12:20pm", "On monday at 11:38am" + + + + Get duration as a human friendly string. + + Start time of duration + End time of duration + The detail in which to display the duration + Number of time units to display + + A string like: 1 month, 2 weeks, and 3 days. + + + + + Get duration as a human friendly string. Displays all time unit durations. + + Start time of duration + End time of duration + + + + Get duration as a human friendly string. + + The timespan for which to output the duration + The detail in which to display the duration + /// Number of time measures to display + + A string like: 1 month, 2 weeks, and 3 days. + + cannot be used when using this method since counting of months + requires both to and from datetime information, in such a case user + + + + Get duration as a human friendly string. Displays all time unit durations. + + The timespan for which to output the duration + + + + Create a a transaction + + + + + Base class for database handling + + + + + Initializes a new instance of the class. + + The connection helper. + + + + Initializes a new instance of the class. + + Another implementation. + + + + Initializes a new instance of the class. + + The connection helper. + Logger interface. + + + + Get a single object. + + Type of object to fetch + complete SQL statement with class/properties instead of table/columns. + parameters used in the statement. + object if found; otherwise null + If the class/table mapping is incorrect. + If data layer fails. + + + + Gets a list of items. + + Statements to get + parameters used in the statement. + A list of items. + + + + Executes an statement in the database. + + Type of object to fetch + complete SQL statement with class/properties instead of table/columns. + parameters used in the statement. + object if found; otherwise null + If the class/table mapping is incorrect. + If data layer fails. + + + + Get a single object. + + Type of object to fetch. + Conditions (corresponds to statements in the WHERE clause). + Parameters used in the where clause. + object if found; otherwise null. + If the class/table mapping is incorrect. + If data layer fails. + + + + Get a single object using its primary key + + Type of object to fetch, must have a defined mapping. + Primary key value + object if found; otherwise null. + If the class/table mapping is incorrect. + If data layer fails. + + + + Used to assign a property with a value + + Object containing the property. + Name of the property. + Value to set. + + + + Save or update an object in the database (1. Checks if the PK is empty. 2. Checks if the object exists in the db). + + Value to create/update. + If the class/table mapping is incorrect. + If data layer fails. + + + + Create a new object in the database. + + Object to create. + If the class/table mapping is incorrect. + If data layer fails. + + + + Update an existing object. + + Object to update + If the class/table mapping is incorrect. + If data layer fails. + + + + Update an existing object + + + + If SQL query fails. + + + + Checks if an object exists by using it's primary key. + + Type of object to check + Primary key + true if object was found; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Checks if an object exists by providing it. + + object to check + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Checks if an object exists in the database + + Type of object to check. + Conditions. + parameters used in the conditions. + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + DataLayerException. + + + DataLayerException. + + + DataLayerException. + + + + Creates a complete database command with the help of the compiled statement. + + Used to create the command + Statement to execute + parameters used in the statement + A filled command + Parameter count doesn't match argument count in the statement. + + + + Creates a complete database command with the help of the compiled statement. + + Used to create the command + Custom sql to execute + parameters used in the statement + A filled command + Parameter count doesn't match argument count in the statement. + + + + Retrieves a collection of items. + + Type of item to get. + Conditions to limit. + Parameters used in the condition. + A list of objects; or an empty list + + + + Retrieves all items of a certain type. + + Type of item to get. + A list of objects; or an empty list + + + + Execute a custom SQL query (Object SQL can not be used yet). + + type of items to get. + SQL query. + Parameters used in the query. + A list of elements. + + + + Gets all items defined by the . + + type of items to get. + Statement specifying which elements to get. + A list of elements. + + + + Removes an object by using it's id property. + + Type of object to remove. + Primary key value + RemoveById requires exactly one primary key. + + + + Removes an object by using it's id property. + + Type of object to remove. + Data layer failed. + + + + Count number of items. + + Type of object to check. + Conditions. + Arguments used in the conditions. + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Count number of items. + + Type of object to check. + true if object is stored in the database; otherwise false. + If the class/table mapping is incorrect. + If data layer fails. + + + + Counts by SQL. Write the whole statement yourself. + + The statement. + Number of returned rows + If data layer fails. + + + + Creates a connection, we use this method instead of the one in + the connection helper to be able to use transactions (with a single connection). + + A database connection. + + + + Create a command. + + Connection used to create the command + A + + + + Gets or sets logger instance. + + + + + Determines if we are a transaction. + + + + + Gets class that takes care of all providers. + + + + + + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + + + Cancels all changes. + + + + + Makes all changes permanent in the database. + + + + + We are not creating a connection, but returning the one used + in the transaction. + + A database connection. + + + + Creates the command. + + + + + + Creates a new transaction. + + On of the s. + An object representing the new transaction. + + Once the transaction has completed, you must explicitly commit or roll back the transaction by using the Commit or Rollback methods. + + + + + Creates a new transaction. + + An object representing the new transaction. + + Once the transaction has completed, you must explicitly commit or roll back the transaction by using the Commit or Rollback methods. + + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + 2 + + + + Determines if we are a transaction. + + + + + Transaction is about to be committed. + + + + + Transaction have been committed. + + + + + Transaction is about to be canceled. + + + + + Transaction have been canceled. + + + + + Don't do anything since someone have already created a transaction. + Let the initial creator commit. + + + + + Don't do anything since someone have already created a transaction. + Let the initial creator commit or rollback. + + To abort the transaction. + + + + Compiles and caches SQL statements to speed up processing. + + + + + Initializes a new instance of the class. + + The providers. + if set to true [cache statements]. + + + + Compiles an object SQL statement + + + + FormatException. + If mapping could not be found. + + + + Recursive scanning after properties. + + + + + + + Compile an Object SQL statement and return the corresponding SQL statement. + + Key used to identify this statement (if caching is turned on) + SQL statement with classes/properties instead of tables/columns. + + + If SQL is malformed. + Failed to find mapping file. + key or objectSql is null. + + + + Goes through a token and all it's children to convert properties/classes to columns/tables. + + Token to process + Mapping file used during conversion. + + + + Get a previously compiled statement. + + Key used when storing the statement. + Compiled statement if found; otherwise null. + If statements are not cached. + + + + Create a new compiled statement. + + Key used to store statement for future use. + ObjectSQL that should be compiled into plain sql. + Compiled statement. + + + + Checks whether a column is empty or not. + + + + + Add a class that determines if a certain type is empty or not. + + Type to check + Object checking values. + Type have already been mapped. + + + + Determines if an object is empty. + + object to check + true if empty; otherwise false. + + + + Implementation checking if a value is empty. + + + + + Determines if an object is empty. + + object to check + true if empty; otherwise false. + + + + Specifies a max length. + + + On a string it specified maximum number of letters, while on int it specifies the max number. + + + Language file items: + + + ItemName + Language text + + + AlphaNumeric + '{0}' may only contain alpha numeric letters. + + + + + + + Initializes a new instance of the class. + + minimum length. should match the type being validated, with the exception of string + where the type should be int. + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is supported. + + + + Minimum length + + + + + thrown when the specified property is not found in the specified class. + + + + + Initializes a new instance of the class. + + Name of the class. + Name of the property. + + + + Specifies a max length. + + + On a string it specified maximum number of letters, while on int it specifies the max number. + + + Language file items: + + + ItemName + Language text + + + Max + '{0}' must be less or equal to {1}. + + + MaxString + '{0}' must be contain {1} or less characters. + + + + + + + Initializes a new instance of the class. + + max length. should match the type being validated, with the exception of string + where the type should be int. + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + If the specified object is not supported. + + Supports basetypes and all types that implements IComparable (passing an int) + + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is supported. + + + + Max length + + + + + This class is used to receive and handle + + + + + Initializes a new instance of the class. + + Dispatcher that the proxy commands will arrive on. + + + + Called when a proxy command arrives. + + + + + + + + Event used to retrieve an instace for a specific type. + + + The instance is used to invoke the requested method. + + + + + Type to instance mappings + + + Maps all types to object instances that are used to invoke the method calls. + + + + + Arguments for + + + + + Initializes a new instance of the class. + + Type of instance wanted. + + + + Type of instance wanted. + + + + + Instance that will be used to invoke method. + + + + + Delegate used by to find instance to invoke on. + + + Arguments + + + + SELECT clause + + + + + Returns SQL + + "SELECT " + + + + Used to determine the kind of this token + + + + + Name of the property to sort by, and if the sort is ascending/descending. + + + + + Initializes a new instance of the class. + + Name of the property. + if set to true sort is ascending. + + + + Returns SQL + + Order by property + + + + Used to determine the kind of this token + + + + + Property that the order by is for + + + + + Ascending or Descending sort + + + + + Name of the property. + + + + + Parse ), which is end of sub context / nesting. + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parse ), which is end of subcontext / nesting. + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parse (, which is start of one level nesting. + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Provides a mapping between properties and columns in a table. + + + + + Get a mapping between a table and a class. + + Name of class, may not be full name + if found; otherwise null. + + + + Arguments for + + Implementation of IPlugin + + + + Called when a plugin have been loaded. + + Plugin being loaded. + Assembly that the plugin exist in. + information about the plugin + Plugin have not been started yet. + + + + Plugin being loaded + + + + + Assembly that the plugin exist in + + + + + information about the plugin + + + + + Invoked when a plugin have been loaded into the system. + + PluginManager that loaded the plugin. + Plugin information. + + + + This delegate is invoked when a command is about to behing invoked or added to the dispatcher. + + Throw if Type may not be handled/invoked. + + + dispatcher that is being used. + arguments with info + + + + Arguments for . + + + + + Initializes a new instance of the class. + + Type of command (if Invoke is called) or Type to handle (if Add is called). + + + + Type of command (if Invoke is called) or Type to handle (if Add is called). + + + + + Receives commands from the client and invoked them in the server dispatcher. + + + + + Initializes a new instance of the class. + + Manager that the incoming commands are invoked in. + + + + Starts the server. + + IP address that we accept incoming connections on. + Port that the server accepts new connections on. + + + + Stops the server. + + + + + Converts a string to a token tree. + + + + + Parses the specified stream. + + Stream with text content. + Tree that will be filled with tokens. + + + + Parses the specified text. + + Text to parse. + Tree that will be filled with tokens. + When we find unsupported tokens. + + + + Prototypes are used to + + The type. + + + + + Get a string for the next chars. + + Number of chars to peek. + A string with characters. + + + + Get a string for the next chars. + + Number of chars to peek + Ignore these characters before starting to count + A string with characters. + + + + Get previous character + + A + Will not change the current position + + + + Scroll back and get that char. + + Number of chars back + A + Will not change the current position + + + + Peek forward on a char + + Which char index (from current position) to get. + A . + + + + Peek forward on a char + + Ignore these characters before string to peek + Which char index (from current position) to get. + A . + + + + Get next position that do not contain the specified chars. + + + + + + + Peek on the next character + + The next character + If trying to peek beyond last character in the string. + + + + Peek forward + + true if extensions should be thrown + if no extensions should be thrown and we are peeking beyond end of text. + + + + Peek at the next character. + + Characters that are ignored (not counted). + the next character, or if no more characters exist. + + + + Read X number of characters + + number of characters to read + A string + Moves current position forward. + + + + Read a character + + A char + Moves current position forward. + + + + Ignore all specified chars + + chars to ignore + true if end of text is not hit. + Moves current position forward. + + + + Returns index of current position as a string. + + + + + All prototype tokens. + + + + + We are at end of text + + + + + Next character is the last one. + + + + + End of context. + + + + + Returns SQL + + ( + + + + Used to determine the kind of this token + + + + + + A IN clause. + + + + + Used to determine the kind of this token + + + + + Provides mappings between tables and classes. + + + + Mappings are loaded using one or more different loaders. In this way + you can provide your own special way of loading mappings, or use one of + our built in loaders such as mirror loader and XmlLoader. + + + You can also add mappings manually by using the method. + + + + + + Add a mapping. + + Mapping file. + + + + Get a mapping file. + + + Will go through all different loaders until of of them return a mapping. + All found mappings are cached. + + class to fetch a mapping for. + if found; otherwise null. + + + + Add a . + + + + + + General usage validator class. + + + Check all FieldXXXXX constants to see what you need to translate. + + + + + '{0}' is not a double. + + + + + '{0}' is not a valid amount. + + Uses Double validation internally + + + + '{0}' is not a number. + + + + + '{0}' must be {1} or {2}. + + + + + + + + '{0}' is not a valid email address. + + + + + + + '{0}' can only contain letters or digits. + + + + + + '{0}' can only contain letters, digits and the following characters: '{1}'. + + + + + + '{0}' can only contain letters, digits and any of the following characters: {1} + + + + + + + '{0}' is required. + + + + + + Used to check if a field is false. + + + + + + Text: True + + + + + + '{0}' may only contain writable characters. + + ascii 32-126, 128-167, 224-238. + + + + Name of the string to display when testing hex fail + + + + + Name of string to display when testing letters fail + + + + + Name of string to display when testing letters fail + + + + + '{0}' may only contain digits + + + + + '{0}' may only contain digits and the following characters: '{1}' + + + + + '{0}' must be between '{1}' and '{2}' + + + + + '{0}' must be '{1}' or more + + + + + '{0}' must be '{1}' or less + + + + + Characters that are valid in alphanumeric fields. + + + + + SQL Safe characters (not including '") + + + + + Space character. + + + + + Regex used to validate emails. + + + + + + Language manager used if no one else have been specified. + + + + + Extra characters that are allowed in passwords. + + + + + + A list with all errors found. + + + + + Language translation for all "name" parameters. + + + + + Language for the validator (i.e. "Field {0} is required.") + + + + + Create a new validator. + + collection to be filled with errors + + + + Create a new validator. + + collection to be filled with errors + language translation for all "name" parameters + + + + Create a new validator. + + language translation for all "name" parameters + + + + Create a new validator + + + + + Check whether the specified field is within the limit. + + field name. + max value. + value to check. + if set to true the field is required. + true if the field is within the limit + + number are check to be less or equal to max. Strings are checked on the number of characters. + + + + + Check whether the specified field is within the limit. + + field name. + min value. + value to check. + if set to true the field is required. + true if the field is within the limit + + number are check to be less or equal to min. Strings are checked on the number of characters. + + + + + Check whether the specified field is within the limit. + + field name. + min value. + max value + value to check. + if set to true the field is required. + true if the field is within the limit + + number are check to be less or equal to min. Strings are checked on the number of characters. + + + + + Validate that a string only contains letters or digits. + + Form parameter name. + value to validate + may not be null or empty if true. + value if valid; otherwise string.Empty + + + + + Validate that a string only contains letters, digits or the specified characters + + Form parameter name. + value to validate + may not be null or empty if true. + any other allowed characters. + value if valid; otherwise string.Empty + + + + + Validate that a string only contains letters or digits or any of the . + + Form parameter name. + value to validate + field may not be empty or null if true + vaue if valid; otherwise string.Empty + + + + + Check's weather a parameter is null or not. + + Parameter in form + true if value is not null/empty; otherwise false. + value that cannot be null or empty. + + + + Validate a string parameter in the form + + Form parameter name. + value to validate as string. + field may not be empty or null if true. + vaue if valid; otherwise string.Empty + + + + + + add default language phrases (only english) + + + + + Checks wether a string contains a specific character. + + source + character to find. + true if found; otherwise false. + + + + format an error message + + string containing the formatting + name of the field that the error ocurred for. + formatted string + + + + format an error message + + string containing the formatting + name of the field that the error ocurred for. + extra value in the formatting + formatted string + + + + format an error message + + string containing the formatting + name of the field that the error ocurred for. + extra value in the formatting + third parameter in the formatting + formatted string + + + + Check whether the specified value is an character. + + Name of the parameter + Parameter value to validate + Paramater is required (adds an error if it's not specified) + value if parameter is an int; char.MinValue if not. + + + + Check whether the specified value is an character. + + Name of the parameter + Parameter value to validate + value if parameter is an int; char.MinValue if not. + + + + Check whether the specified value is an integer. + + Name of the parameter + Parameter value to validate + Paramater is required (adds an error if it's not specified) + value if parameter is an int; 0 if not. + + + + Check whether the specified value is a double. + + Name of the parameter + Parameter value to validate + Paramater is required (adds an error if it's not specified) + value if parameter is a double; 0 if not. + + + + Check whether the specified value is a currency amount. + + Name of the parameter + Parameter value to validate + Paramater is required (adds an error if it's not specified) + value if parameter is a currency amount; 0 if not. + + + + Check whether the specified value is a double. + + Name of the parameter + Parameter value to validate + Paramater is required (adds an error if it's not specified) + value if parameter is a double; 0 if not. + error string + + + + Check whether the specified value is an integer. + + Name of the parameter + Parameter value to validate + Paramater is required (adds an error if it's not specified) + value if parameter contains valid characters; string.Empty if not. + ascii 32-126, 128-167, 224-238. + + + + Check if a value is digits only + + Field name. + value to validate + true if field is required (may not be empty) + string if validated, otherwise string.Empty + + + + Check if a value is digits only + + Field name. + value to validate + extra characters that is allowed. + true if field is required (may not be empty) + string if validated, otherwise string.Empty + + + + Validates a string to consist of letters. + + field name + value to validate + field is required + string if valid, string.Empty if not. + + + + Validates a string to consist of letters plus specified characters. + + The name of the parameter + The value to validate + If the value is required + A string consisting of extra allowed characters + The validated string or string.Empty if validation failed + + + + Validates a string to be in trimmed hex letters + + The name of the parameter + The value to test + If the parameter is required + The value or string.Empty if validation failed + + + + validates email address using a regexp. + + field name + value to validate + field is required (may not be null or empty). + value if validation is ok; otherwise string.Empty. + + + + validates that a certain number is within the specified interval + + field name + value to validate + The maximum value that value is allowed to be + The minimum value that value is allowed to be + field is required (may not be null or empty). + value if validation is ok; otherwise 0. + + + + validates that a certain number is within the specified interval + + field name + value to validate + The maximum value that value is allowed to be + The minimum value that value is allowed to be + field is required (may not be null or empty). + value if validation is ok; otherwise 0. + + + + Checks whether a field contains true (can also be in native language). + + field name + value to validate + field is required (may not be null or empty). + true if value is true; false if value is false or if validation failed. + + Check validation errors to see if error ocurred. + + + + Set default language. + + + + + true of validation generated errors. + + + + + Collection of validation errors. + + + + + General purpose plugin finder class. + + + This class scans a directory after all classes that contains the specified types. + All assembly checking is done in a separate app domain, which means that no dlls are loaded + during the process. + + + + + Initializes a new instance of the class. + + + + + Finds the specified full path. + + Path and wildcards. + Types wanted. + + + List>Type< types = new List>Type<(); + types.Add(typeof(IPlayer)); + types.Add(typeof(IViewer)); + finder.Find("C:\\myapp\\plugins\\plugin*.dll", types); + + + + + + + + Finds the specified full path. + + Path and wildcards. + Types wanted. + + + List>Type< types = new List>Type<(); + types.Add(typeof(IPlayer)); + types.Add(typeof(IViewer)); + finder.Find("C:\\myapp\\plugins\\plugin*.dll", types); + + + + + + + Adds the specified plugin. + + The plugin. + Assembly that the plugin resides in. + Plugin interface type. + Public key, should be used to decide the amount of access for the module. + + + + All found plugins + + + + + Packet sent through the channel. Keeps track of a command and its reply. + + + + + Initializes a new instance of the class. + + The sequence number. + Command . + + + + LIKE keyword + + + + + Conditional string + + " LIKE " + + + + Used to determine the kind of this token + + + + + Operator used for bitwise operations + + + + + Initializes a new instance of the class. + + Bitwise char. + + + + Returns SQL + + The token wrapped with spaces + + + + Used to determine the kind of this token + + + + + + Parse + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + > + + + + + Used to determine the kind of this token + + + + + + Parse == or = + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + An implementation of YAML that only support those things that is needed by the Language loader. + + + + + Parses a line + + row in text file + where to start the parsing (initial white spaces are excluded) + line number in file + used if more lines should be read + + + + + Loads mappings from embedded XML files. + + + + En mapping file can look like this: + + + + + + + + + + + + ]]> + + + + class 'name' is a assembly qualified name. + + + generator can be 'autoincrement' (works with databases such as SQL Server or MySQL), 'sequence' (works with PostgreSQL, looks for a sequence + with the name '[tablename]_id_seq'), 'guid' (Fadd.Data generates the guid), any other values are interpreted as + a sequence name (and will be used as such depending on the database type). + + + Property/Id 'type' attribute is not yet used. + Will be used along with the 'length' attribute when creating/upgrading tables/columns. + + + + + + Initializes a new instance of the class. + + An assembly to look in. + + You can add more assemblies by using + + + + + Add another assembly that mappings can be found in. + + Assembly to add. + + + + Scan after types that match the name. + + name of class to scan for + A list with matches. + + + + Get an mapping. + + Name of class. + if found; otherwise null. + Failed to find any attributes in the class element. + + + + Throw during mapping exceptions. + + + + + Initializes a new instance of the class. + + Error message. + Type that the mapping failed for. + + + + Initializes a new instance of the class. + + Error message. + Inner exception. + Type that the mapping failed for. + + + + Type that the mapping failed for. + + + + + Let's you validate a string field using regular expressions. + + + + + Initializes a new instance of the class. + + The value. + Name in language text to use, or a error message. + + + // will use the supplied text as error message if validation fails. + [ValidateRegularExpression("^[a-zA-Z0-9_\.\-]+@[a-zA-Z0-9_\.\-]+\.[a-zA-Z]{2,4}$", "Incorrect email")] + public string Email{get;set;} + + //will use the InvalidEmail text from the language file + [ValidateRegularExpression("^[a-zA-Z0-9_\.\-]+@[a-zA-Z0-9_\.\-]+\.[a-zA-Z]{2,4}$", "InvalidEmail")] + public string Email{get;set;} + + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation or if value is empty; otherwise false. + + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is supported. + + + + Regular Expression + + + + + Arguments for + + A plugin implementation + + + + Initializes a new instance of the class. + + Plugin that have been started. + + + + Plugin that have been started. + + + + + Triggered when a plugin have been started. + + Plugin manager + Plugin instance + A plugin implementation + + + + This class is responsible of loading all modules in a system. + + + + + Application interface exposed to the clients. + + + + + Initializes a new instance of the class. + + Interface exposed to plugins. + + + + Initializes a new instance of the class. + + + + + Called when a plugin assembly is about to be loaded. + + Information about the assembly. + Evidence that the should be loaded with. + + The assembly is used to determine which security settings a plugin should be loaded with. + + + + + Loads modules with the correct and correct protection level. + + + + + Loads all modules that can be loaded (their dependencies have been loaded.) + + + + + Call this method as long as it returns true. + + + + Called when a plugin have been loaded. + + Module being loaded. + Assembly that the plugin exist in. + information about the plugin + Plugin have not been started yet. + + + + Manually add a plugin + + Plugin to add + + Used if you want to inject a known plugin to let it be loaded when all dependencies have been met. + + + + + Loads all modules + + + + + Calls start in a . + + plugin to start + + + + + Triggered when all plugins have been loaded, but not started yet. + + + + + Access a loaded plugin. + + plugin if found; otherwise null. + + + + Folder in which plugins resides. + + Can also contain wildcards + + + pluginMgr.Path = "plugins\\plugin*.dll"; + + + + + + All plugins that should not be loaded. + + + + + A list with all loaded plugins. + + + + + A plugin have been loaded into the system + + + + + A plugin have been started. + + + + + An plugin assembly is about to be loaded and the system needs to know which the plugin assembly should be loaded with. + + + Plugins are loaded into the Internet zone per default. + + + + + Called when a plugin is about to be started and we need to get the application interface + that should be used when starting the plugin. + + + + + All plugins must derive this interface. + + + + + Start the module + + Application interface exposed towards the plugins. + + + + This name is used to determine dependencies, should always be in english. + Should not be confused with the human friendly name in . + + + + + Information about the plugin. + + + + + Other plugins that this one depends on. The list should contain s. + + should never be null. + + + + Calculates the number of months and days between two dates (and add the overlapping days). + + + + + Creates a monthspan between two dates + + from which date to calculate. + end date. + Number of months and days between the two dates. + + + + Number of months + + + + + Number of days + + + + + A general code compiler used to compile objects at runtime. + + + This class is NOT thread safe. + + + + + Initializes a new instance of the class. + + + + + Add a type that is used in the generated object + + the type + + + + Fills name spaces and assemblies with info from the added types. + + + + + Compile the code. + + C# code. + An assembly containing the compiled objects + If compilation fails. + + + + Create a new object of the specified type + + Constructor parameters + Type of object to create + object if we could create it; otherwise null + + + + Used to get correct names for generics. + + Type to generate a strig name for. + Type as a code string + + + ).Name; // will become: List`1 + typeName = Compiler.GetTypeName(typeof(List)); // will become: System.Collections.Generic.List + ]]> + + + + + + Used to get correct names for generics. + + Type to generate a strig name for. + true if FullName should be used (including namespace in typename) + Type as a code string + + + ).Name; // will become: List`1 + typeName = Compiler.GetTypeName(typeof(List)); // will become: System.Collections.Generic.List + ]]> + + + + + + Thrown if code compilation fails. + + + contains an item called code which contains the compiled code. + + + + + Initializes a new instance of the class. + + The error message. + A list with all compiler generated errors. + + + + Creates and returns a string representation of the current exception. + + + A string representation of the current exception. + + + + + + + + A list with all compiler generated errors. + + + + + The purpose of this class is to maintain all delegates for + one of following types: attributes, interfaces and classes. + + + These lists are used to map all commands to their subscribers. + + + + + Add a handler + + + + If handler have been added already. + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + + An object that can be used to iterate through the collection. + + 2 + + + + Test object using generic types + + The first value to use the type of + The second value to use the type of + + + + Tests it. + + String format + A value to use in the string + string.Format(myArg, value, typeof(T), typeof(K)) + + + + Tests it. + + String format + A value to use in the string + string.Format(myArg, value, typeof(T), typeof(K)) + + + + Retrieves a dictionary using the two specified types with some random arguments in + + + + + Does the this. + + + + + Modify the id of the testobject + + + + + Delegate used to handle commands. + + that the command was invoked in. + true if command was handled. + command arguments. + + + + Event arguments for . + + + + + Initializes a new instance of the class. + + command that was invoked. + + + + Initializes a new instance of the class. + + command that was invoked. + Command is being invoked asynchrounosly. + + + + Command that was invoked. + + + + + Abort the handled of this command. + + + The event can override this property. + + + + + Command is being invoked asynchrounosly. + + + + + Not equal condition + + + + + Returns SQL + + " <> " + + + + Used to determine the kind of this token + + + + + + >= + + + + + Returns SQL + + " >= " + + + + Used to determine the kind of this token + + + + + + Parsing a property + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + All tokens used during parsing + + + + + AND + + + + + OR + + + + + IN ( XXX ) + + + + + ( + + + + + ) + + + + + A generated tree with all tokens from the text + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + Number of items to keep in the . + + + + Add a new token to the tree. + + + + + + Adds a child node. + + Token to be added as a child. + this method will also set the token as . + todo: remove this one and fix haml + + + + Returns an enumerator that iterates through the collection. + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + 2 + + + + Next item is a child to the current node. + + + + + Go out of one level of nesting. + + + + + Back to root node. + + + + + Displays parse tree as a tree. + + + + + + Current node + + + + + Get one of the children + + + + + + + Number of root items. + + + + + The node that was inserted last. + + + + + A trail with the number of previously added nodes, independent of the tree/depth. + + + + + Number of items to keep in the trail. + + + Specified in the constructor, default value is 3. + + + + + Root in the tree. + + + + + OR condition ("||" in C#) + + + + + OR condition as string + + " || " + + + + Used to determine the kind of this token + + + + + + As the name implies the migrator uses sql specified for a certain driver to update the database. + The sql is loaded using manifest resource xml files named xxxxx.fdmig.xml (fadd data migration) + + + The fdmig file should look like the following + + + + + An optional description of the migration. + + + + alter table myTable rename column myColumn to myColumnValues; + + + + alter table myTable rename column myColumn to myColumnValues; + + + + + + + INSERT INTO myTable (myId, myValue) VALUES (13, '23123'); + + + + + ]]> + + + + + Inteface for a handler of database updating + + + + If changes has been made the migrator should update the database accordingly + Used for saving migration data to the database + + + Initializes the + Assemblies to look for fdmig.xml files in + + + Reads all manifest files ending with fdmig.xml and creates migrations from them + + + Parses the current 'migration' element in the reader and adds its value to the collection of migrations + The reader to parse the migration from + If is null + Throw if parsing failes due to invalid data in xml + + + Holds information about a specific migration + + + Sql for migration keyed by driver name (or default) + + + + Class to provide transparent retrieval of LCID for silverlight and normal .Net projects + + + + + Gets the LCID for the current thread + + + + + Makes sure that only digits have been entered. + + + Language file items: + + + ItemName + Language text + + + Digits + '{0}' may only contain digits. + + + DigitsExtra + '{0}' may only contain digits and "{1}". + + + + + + + Name used in language files. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + extra characters that are valid. + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is supported. + + + + General purpose arguments class for events containing only one item. + + Type of item + + + + Initializes a new instance of the class. + + The item. + + + + Gets or sets the item. + + The item. + + + + Class to generate readable names from arbitrary types + + + + + Removes any `X from generic type names, ie Dictionary`2. + If the name lacks any `X entry ie Int32 it will simply be left untouched + + The name to strip from generic parameter count + Number of arguments held + + + + Retrieves the name of the class, ie for => Dictionary + + The type to generate a name for + + + + + ]]> + + The type to generate a name for + + + + Retrieves a name of the type with any generic type arguments joined with name. + + The type to generate a name for + + => DictionaryInt32String. + Useful for naming ie arbitrary methods when building objects using reflection. + ]]> + + + + + Retrieves the full name of a class, ie System.Collections.Generic.Dictionary + + The type to return the full name for + + + + A object binding. Used to copy properties from one object to another. + + + + + Copies the specified from. + + object to copy from. + objec to copy to. + If either from or to are null + If "to" is not assignabled from "from". + + + + WHERE clause + + + + + Returns SQL + + " WHERE " + + + + Used to determine the kind of this token + + + + + Parse + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + >= + + + + + Used to determine the kind of this token + + + + + + Parsing a property + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parse >= + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parse 'AND' + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Used to parse and format date/times using the current language. + + todo: Copy latest documentation from IDateTimeHelper. + + + + Add a parser. + + + + + + + + Add default language parsers. + + + + + Will try to parse the date/time using the local parser. + Will also try the default DateTime.Parse method if date is in an unknown format. + + date/time string + A + If the date/time format is invalid. + If month/day/hour/minute/second are not within their ranges. + + + + Will try to parse the date/time using the local parser. + Will also try the default DateTime.Parse method if date is in an unknown format. + + date string (may be null/empty). + time string (may be null/empty). + A + If the date/time format is invalid. + If month/day/hour/minute/second are not within their ranges. + + + + Tries to parse a date string. + + Can contain a complete date or parts of it (which parts depends on the country). + A containing the date (time is 0) if parsed successfully; otherwise . + + + + Tries to parse a date string. + + Can contain a complete date or parts of it (which parts depends on the country). + array that parsed values are written to. + where in array to start write values + true if parsed successfully; otherwise false + + + + Tries to parse a time string. + + Can contain a complete time or parts of it (which parts depends on the country). + A containing the time (date is 0) if parsed successfully; otherwise . + + + + Tries to parse a time string. + + Can contain a complete time or parts of it (which parts depends on the country). + array that will be filled. Must contain three slots. + Where in the array to start write values + true if parsed successfully; otherwise false. + + + + Validate a date. + + year value + month value + day value + true if exceptions should be thrown on incorrect values. + true if specified values are a correct date; otherwise false. + + + + Validate + + hour + minute + second + true if exceptions should be thrown on incorrect values. + true if specified values are a correct date; otherwise false. + + + + Format a date as a string. + + A date/time + Examples: "Yesterday", "On monday" + + + + Format a date/time as a string. + + A date/time + Examples: "Yesterday at 12:20pm", "On monday at 11:38am" + + + + Get duration as a human friendly string. + + Start time of duration + End time of duration + The detail in which to display the duration + Number of time units to display + + A string like: 1 month, 2 weeks, and 3 days. + + + + + Get duration as a human friendly string. Displays all time unit durations. + + Start time of duration + End time of duration + + + + Get duration as a human friendly string. + + The timespan for which to output the duration + The detail in which to display the duration + /// Number of time measures to display + + A string like: 1 month, 2 weeks, and 3 days. + + cannot be used when using this method since counting of months + requires both to and from datetime information, in such a case user + + + + Get duration as a human friendly string. Displays all time unit durations. + + The timespan for which to output the duration + + + + Event arguments for transaction events. + + + + + Initializes a new instance of the class. + + Transaction. + The connection. + + + + Transaction that the event is for. + + + + + Connection that the transaction is being invoked in. + + + + + Main class in the data access layer. + + + + + Initializes a new instance of the class. + + The connection helper. + + + + Initializes a new instance of the class. + + The connection helper. + Assign logger to everything. + + + + Creates a connection, we use this method instead of the one in + the connection helper to be able to use transactions (with a single connection). + + A database connection. + Failed to create the connection. + + + + Create a command. + + + + + + Creates a new transaction. + + On of the s. + An object representing the new transaction. + + Once the transaction has completed, you must explicitly commit or roll back the transaction by using the Commit or Rollback methods. + + + + + Creates a new transaction. + + An object representing the new transaction. + + Once the transaction has completed, you must explicitly commit or roll back the transaction by using the Commit or Rollback methods. + + + + + Determines if we are a transaction. + + + + + An validation error. + + + + + + + Property/Argument that validation failed for. + Kind of validation that failed. + + + + Property/Argument that validation failed for + + + + + Kind of validation that failed + + + + + Delegate used to determine if the command propagation can be cancelled. + + Distlight that the command was going through. + Event information. + true of command can be cancelled. + + + + + Event arguments for . + + + + + Initializes a new instance of the class. + + Command that was cancelled. + Handler that cancelled the propagation + + + + Command that was cancelled by a handler + + + + + Handler that which to cancel the command. + + + + + Used to test inner classes. + + + + + Copy all stuff that have been modified by the command handler. + + Command to copy reply from. + + + + Transports commands in binary format. + + + + + Invoked when we have recieved a packet from the remote end. + + + + + Initializes a new instance of the class. + + The socket. + + + + Initializes a new instance of the class. + + + + + Releases unmanaged resources and performs other cleanup operations before the + is reclaimed by garbage collection. + + + + + Sends the specified value. + + object to serialize and send. + + + + Send a packet to the client. + + + + + + Goes through all incoming bytes and creates a packet. + One or more calls might be required to get a complete packet. + + buffer to process + where to start processing + total number of bytes in buffer. + number of bytes that are handled + + + + Called when a object buffer have been received completely. + + The buffer. + + + + Closes this instance. + + + + + Connect to an endpoint. + + Where to connect + if connection fails. + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + 2 + + + + true if we should reconnect when getting disconnected. + + + + + Invoked when channel is disconnected (except when are called). + + + + + Event args for + + + + + Initializes a new instance of the class. + + object received from remote end. + + + + Bytes received from remote end. + + + + + Invoked when an object have been received from the remote end. + + Client that received the object. + object received. + + + + A IN clause. + + + + + Returns SQL + + " IN" + + + + Used to determine the kind of this token + + + + + COUNT(*) SQL clause + + + + + Returns SQL + + " COUNT(*) " + + + + Used to determine the kind of this token + + + + + We are getting into a sub context (nested conditions) + + + + + Returns SQL + + ( + + + + Used to determine the kind of this token + + + + + + or != + ]]> + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + + + + + + Less tan condition + + + + + + + + Used to determine the kind of this token + + + + + + Parse + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Reads a mapping definition from an XML reader. + + + + + Initializes a new instance of the class. + + Reader containing mapping XML. + Type that the mapping is for. + + + + Read mapping. + + Mapping if found + If mapping is incorrect (syntax). + + + + Go through property attributes + + MappingException. + + + MappingException. + + + MappingException. + + + + Default mapping implementation + + + + + Initializes a new instance of the class. + + Type that the mapping is for. + Name of the database table. + + + + Get a column name from a property name. + + + + + + + Gets current primary key. + + The primary key if exactly ONE is defined; otherwise null. + + + + Add a property mapping + + Class property + Table column + + + + Add a property mapping + + Class property + Table column + True if property is a primary key. + Type of generator used. + + + + Name of database table. + + + + + Type that the mapping is for. + + + + + Gets all properties + + + + + Gets all primary keys + + + + + Attribute to specify that a property might only contain the specified letters (does not fail if the string is null or empty) + + + + + The language node item name for the attribute, should be in the form 'Parameter {0} has invalid characters, allowed characters are {1}' + + + + + Initializes a new instance of the class. + + The allowed letters + + + + Validate value + + Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. + value to validate + + true if value passed the validation; otherwise false. + + If a value not of type was supplied + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + + + + Determines if the validation class support the specified type. + + Property/Value type. + true if the type is a string + + + + General purpose class to get/set properties by specifying their name as text. + + + + User user = new User(); + Property.Set(user, "FirstName", "Jonas"); // same as user.FirstName = "Jonas"; + Property.Set(user, "CreatedAt", "2008-12-30 14:00"); // the CreatedAt property is of type DateTime, the Property class automatically converts it from string. + Property.Set(user, "Age", "20"); // age is int, which means that it will automatically be converted. + + + todo: Add the ability to extend the object with own additions. + + + + Assign a property value to the specified object. + + Object that has the property + Name of the property + Value, this method will try to cast incorrect types (and even try to parse certain types). + If value is not correct (i.e. null when null is not accepted etc) + If assigner object could not be generated. + If assigner object can't cast to the correct type. + values is null and it's not allowed for the specified type. + + + + Get a property value from the specified object. + + Object that has the property + Name of the property + property value + If value is not correct (i.e. null when null is not accepted etc) + If assigner object could not be generated. + If assigner object can't cast to the correct type. + + + + Base object for property assigner. + + + + + + + + + + + + + + + + + + + + + Used to determine + + + + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + or != + ]]> + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Tables and Classes are identical. + + + Since they are identical, we create a mapping which simply returns the + same values for the mapping. + + + + + Initializes a new instance of the class. + + Assembly that will be scanned after mappings. + "sequence" or "autoincrement" + + Specifying "sequence" means that the system will look for generators/sequences called "[tablename]_id_seq". + + + + + Gets the mapping. + + Name of the class to get mapping for. + if found; otherwise null. + + + Multiple classes found. + + + + Add an assembly in which we can look for mappings. + + An assembly + + + + Delegate used when a unhandled exception have been caught. + + + + + Arguments for + + + + + Initializes a new instance of the class. + + caught exception. + + + + Caught exception + + + + + Copies get/set properties from one object to another. + + + + + Releases unmanaged resources and performs other cleanup operations before the + is reclaimed by garbage collection. + + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + 2 + + + + Copies all get/set properties from one object to another. + + From. + To. + + + + Star * + + + + + SQL Star + + " * " + + + + Used to determine the kind of this token + + + + + A property name + + + + + Initializes a new instance of the class. + + Name of property. + + + + Returns SQL + + column name + + + + Used to determine the kind of this token + + + + + + Name of property + + + + + OR condition ("||" in C#) + + + + + Returns SQL + + " OR " + + + + Used to determine the kind of this token + + + + + + Parse (, which is start of one level nesting. + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Parse == or = + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Check if the current position is the start of this token + + + + true if our token is the next one. + + + + + + + + + Returns &&. + + " && " + + + + Used to determine the kind of this token + + + + + + Parse + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Arguments for + + Plugin type + + + + Initializes a new instance of the class. + + Plugin being started. + + + + Plugin being started + + + + + Application that the plugin should be started with. + + + + + A plugin is about to be started and it needs a interface to be passed to it. + + A type that implements . + + Arguments. + + + + INSERT INTO clause + + + + + returns SQL + + "INSERT INTO " + + + + Used to determine the kind of this token + + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + All tokens used during parsing + + + + + AND + + + + + OR + + + + + IN ( XXX ) + + + + + ( + + + + + ) + + + + + Query statement + + + + + Initializes a new instance of the class. + + Conditions (corresponds to the WHERE clause) using property names. + Argument values used in the condition. + + + + Initializes a new instance of the class. + + + + + Properties to sort by. + + properties separated by comma, with or without ASC/DESC. + the current statement + new Statement("FirstName = ?", firstName).OrderBy("LastName DESC, FirstName") + + + + Properties to group the statement by + + Properties to group by, separated by comma. + + new Statement(string.Empty).GroupBy("LastName, FirstName") + + + + Get a certain amount of entries. + + 0 based index. 0 = first page, 1 = second page.. + Number of entries per page. + + + + + Calculates hash code by concatenating the strings for all statement parts. + + + + + + Arguments being used in . + + + + + Conditions (corresponds to the WHERE clause) using property names + + + + + Property names + ASC/DESC + + + + + Property names to group by + + + + + 0 based index. 0 = first page, 1 = second page.. + + + + + Number of entries per page + + + + + Database connection configuration + + + + + Assembly qualified name of the .Net driver to use. + + + + + Connection string + + + + + Connection type. + + + + + Validates a string as http(s)/ftp url. + + + + + Name of the language item for this validation. + + + + + Localize error if validation failed. + + Localized field name. + Language node with all validation translations. + A localized error message if the validation failed; otherwise null. + + + attribute.Localize("FirstName", "'{0}' is required"); + // will return "'{0}' is required" if the validation failed, else null. + + + + + + Determines if the validation support the specified type. + + Property/Value type. + true if type is supported. + + Used when validation objects are generated. + + + + + Validate the URL + + Object instance (of the object being tested). + Value to test + true if value is an URL; otherwise false. + If type is not supported. + + + + Wrapper for the class adding support for tabbing easily + + "); + sb.AppendLine(@"); + sb.AppendLineDec(""); + + Would give the result: +
+ +
+ + Without having you worry about what tab indentation your on, neat! + ]]>
+
+ + + Instances a blank + + + + + Instance the with a and a start intendation + + A to append text to + + Can be used to start writing with tabs using a set intendation when data has previously been written to the + with intendation. + + + + + The built string + + Generated string + + + + Changes the to contain the specified number of tab characters + + Number of tab characters to set + + + + Appends tabs followed by the specified value and a line termination to the string + + The value to add + + + + Appends tabs followed by the specified value and a line termination to the string + + The value to add + + + + Appends a empty line without tabs + + + + + Just appends plain string to the underlying without any tabs + + The value to add + + + + Appends tabs and value to the string + + The value to add + + + + Adds a new line and increases the tab indendation + + "); + sb.AppendLineInc(); + sb.AppendLine(""); + sb.AppendLineDec(""); + + => +
+ +
+ ]]>
+
+ + + Appends tabs and value to the string after which the intendation is increased + + The value to append + The tab intendation is increased after adding the value + "); + sb.AppendLine("Example"); + + Would give: +
+ Example + ]]> + + + + Appends tabs and value to the string after which the intendation is increased + + The value to append + The tab intendation is increased after adding the value + "); + sb.AppendLine("Example"); + + Would give: +
+ Example + ]]> + + + + Decreases the intendation and appends tabs and value to the string + + The value to append + The tab intendation is decreased before adding the value + "); + sb.AppendLine("Example"); + sb.AppendLineDec("
"); + + Would give: +
+ Example +
+ ]]> + + + + Decreases the intendation and appends tabs and value to the string + + The value to append + The tab intendation is decreased before adding the value + "); + sb.AppendLine("Example"); + sb.AppendLineDec("
"); + + Would give: +
+ Example +
+ ]]> + + + + Retrieves the used by the class + + + + + Command sent by a TypeProxy + + + + + Initializes a new instance of the class. + + Type of object to execute method on. + Method to call. + Argumens that should be passed to the method. + + + + Return value from method call. + + + + + Type of object to execute method on. + + + + + Method to call. + + + + + Argumens that should be passed to the method call. + + + + + Exception thrown during method call + + + + + Class for building information strings about a proxy property + + + + + Instantiates and builds information about a property + + + + + + Builds a declaration string for the property proxy + + The property to build a proxy declaration for + + + + Retrieves a signature for the property, ie _propertyName + + + + + Retrieves the property name + + + + + Retrieves the return type for the property + + + + + Retrieves the property declaration string + + + + + Retrieves the property type + + + + + Returns true if the property has a get field + + + + + Returns true if the property has a set field + + + + + Invokes all handlers for a command. + + + + + Initializes a new instance of the class. + + used as "source" in the command event delegate. + command being executed. + Used to check if propagation may be cancelled. + callback when command have been executed.. + async result. + + + + + + + + + Returns SQL + + " <= " + + + + Used to determine the kind of this token + + + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Check if the current position is the start of this token + + true if our token is the next one. + + + + Parses the data in the specified . + + The containing the data to parse. + that is being built.. + true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children). + + + + Contains a value in a condition. + + + + + Initializes a new instance of the class. + + Property that this value belongs to. + + + + Question mark as string + + "?" + + + + Identifier for this token. + + + + + Property that this token belongs to. + + + + + Used to parse Swedish dates and times. + + + + + Static constructor setting up helping maps + + + + + Will try to parse the date/time using the local parser. + Will also try the default method if date is in an unknown format. + + date/time string + A if parsed successfully; otherwise . + If the date/time format is invalid. + If month/day/hour/minute/second are not within their ranges. + + Can parse one of the following formats:
+ YYYY-MM-DD HH:MM:SS
+ YYYY-MM-DD HH:MM
+ YYYYMMDDHHMM
+ YYYYMMDDHHMMSS
+ YYMMDDHHMM +
+
+ + + Will try to parse the date/time using the local parser. + Will also try the default method if date is in an unknown format. + + date string. Date will be set to current if value is not specified. + time string. Time will be set to current if not specified. + A + If the date/time format is invalid. + If month/day/hour/minute/second are not within their ranges. + + + + Tries to parse a date string. + + Can contain a complete date or parts of it (which parts depends on the country). + + A containing the date (time is 0) if parsed successfully; otherwise . + + + YYYY-MM-DD + YYYYMMDD + YYMMDD + MMDD + + + + + Can parse one of the following formats: + YYYY-MM-DD + YYYYMMDD + YYMMDD + MMDD + + contains a date string. + array that parsed values are written to. + where in array to start write values + If value contains an unknown format. + If month/day are not within their ranges. + offset is out of range. + + + + Tries to parse a time string. + + Can contain a complete time or parts of it (which parts depends on the country). + A containing the time (date is 0) if parsed successfully; otherwise . + + + + Can parse one of the following formats: + HH:MM:SS + HH:MM + HHMM + HHMMSS + + a time value + array that will be filled. Must contain three slots. + Where in the array to start write values + true if parsed successfully; otherwise false. + offset is out of range. + Invalid time format. + + + + Validate a date + + year value + month value + day value + throw exceptions + true if specified values are a correct date; otherwise false. + If any of the values are not in the allowed ranges. + Month must be between 1 and 12. + + + + Validate a time value. + + hour + minute + throw exceptions + If any of the values are not in the allowed ranges. + + + + + + + + + + If any of the values are not in the allowed ranges. + + + + Format a date as a string. + + A date/time + Examples: "Yesterday", "On monday" + + + + Format a date as a string. + + A date/time + Examples: "Yesterday at 12:20pm", "On Monday at 11:38am" + + + + Get duration as a human friendly string. + + Start time of duration + End time of duration + The detail in which to display the duration + Number of time units to display + + A string like: 1 month, 2 weeks, and 3 days. + + + + + Get duration as a human friendly string. Displays all time unit durations. + + Start time of duration + End time of duration + + + + + Get duration as a human friendly string. + + The timespan for which to output the duration + The detail in which to display the duration + Number of time measures to display + + A string like: 1 month, 2 weeks, and 3 days. + + cannot be used when using this method since counting of months + requires both to and from date time information, in such a case user + Since is given as duration months cannot be calculated, try GetDuration(DateTime, DateTime, DurationDetail) instead. + + + + Get duration as a human friendly string. Displays all time unit durations. + + The timespan for which to output the duration + + + + + Displays a value defined by the either as a part or as a total + + Number of months and days + TimeSpan containing detailed times + The time unit to retrieve + True if the total amount of the time unit should be returned or just a portion of it + A string in the form 'amount timunitname, ' + + + + Retrieves a singular or plural name with amount and comma padded, ie '3 månader, ' or '1 månad, ' + + The name in plural (used to map for singular name) + The amount to display + + + + Mappings for properties. + + + + + Initializes a new instance of the class. + + Name of the class property. + Name of the database column. + + + + Gets or sets name in table + + + + + Gets or sets name in class. + + + + + Gets or sets whether this property is part of the primary key + + + + + Gets or sets how the value is generated. + + + + + sequence + Looks for a sequence named "[tableName]_id_seq" + + + autoincrement + Uses database specific code to fetch value after insert. + ONLY VALID FOR PRIMARY KEYS + + + + guid + generate a globally unique identifier. + + + any other value + specifies a sequence/generator name + + + + + + + Connection helper specialized for PostgreSQL database. + + + + + Initializes a new instance of the class. + + The connection string. + + + + Create a new connection to the database. + + An open database connection. + If connection fails. + + + + Try to get next sequence value. + + A command to perform actions with, we pass commands since they might be in a transaction. + Name of the sequence/generator. + A generated value. + If something failed + + + + Gets the latest auto incremented value from the database. + + A command to perform actions with, we pass commands since they might be in a transaction.. + Name of the column that the auto incremented value is for. + auto incremented value + If something failed + Not supported by PostgreSQL + + + + Modifies the SQL statement to include paging. + + Mapping used by certain implementations (but not by this one). + SQL statement generated by the DAL. + 0 based index + number of entries per page + SQL statement with paging + + + helper.Paging("SELECT * FROM users", 5, 10); // --> "SELECT * FROM users LIMIT 10 OFFSET 50" + + + + + + Connection string + + + + + diff --git a/thirdparty/HttpServer/Homepage.txt b/thirdparty/HttpServer/Homepage.txt new file mode 100644 index 000000000..c0d8fec2b --- /dev/null +++ b/thirdparty/HttpServer/Homepage.txt @@ -0,0 +1 @@ +http://webserver.codeplex.com/ \ No newline at end of file diff --git a/thirdparty/HttpServer/HttpServer.MVC.dll b/thirdparty/HttpServer/HttpServer.MVC.dll new file mode 100644 index 000000000..0446bf955 Binary files /dev/null and b/thirdparty/HttpServer/HttpServer.MVC.dll differ diff --git a/thirdparty/HttpServer/HttpServer.dll b/thirdparty/HttpServer/HttpServer.dll new file mode 100644 index 000000000..593f8fa7c Binary files /dev/null and b/thirdparty/HttpServer/HttpServer.dll differ diff --git a/thirdparty/HttpServer/HttpServer.xml b/thirdparty/HttpServer/HttpServer.xml new file mode 100644 index 000000000..4815d2d3b --- /dev/null +++ b/thirdparty/HttpServer/HttpServer.xml @@ -0,0 +1,5778 @@ + + + + HttpServer + + + + + Session store using memory for each session. + + + + + A session store is used to store and load sessions on a media. + The default implementation () saves/retrieves sessions from memory. + + + + + Creates a new http session with a generated id. + + A object + + + + Creates a new http session with a specific id + + Id used to identify the new cookie.. + A object. + + Id should be generated by the store implementation if it's null or . + + + + + Load an existing session. + + Session id (usually retrieved from a client side cookie). + A session if found; otherwise null. + + + + Save an updated session to the store. + + Session id (usually retrieved from a client side cookie). + If Id property have not been specified. + + + + We use the flyweight pattern which reuses small objects + instead of creating new each time. + + Unused session that should be reused next time Create is called. + + + + Remove expired sessions + + + + + Remove a session + + id of the session. + + + + Load a session from the store + + + null if session is not found. + + + + Number of minutes before a session expires. + + Default time is 20 minutes. + + + + Initializes the class setting the expirationtimer to clean the session every minute + + + + + Delegate for the cleanup timer + + + + + Creates a new http session + + + + + + Creates a new http session with a specific id + + Id used to identify the new cookie.. + A object. + + Id should be generated by the store implementation if it's null or . + + + + + Load an existing session. + + + + + + + Save an updated session to the store. + + + + + + We use the flyweight pattern which reuses small objects + instead of creating new each time. + + EmptyLanguageNode (unused) session that should be reused next time Create is called. + + + + Remove expired sessions + + + + + Remove a session + + id of the session. + + + + Load a session from the store + + + null if session is not found. + + + + Number of minutes before a session expires. + Default is 20 minutes. + + + + + Priority for log entries + + + + + + Very detailed logs to be able to follow the flow of the program. + + + + + Logs to help debug errors in the application + + + + + Information to be able to keep track of state changes etc. + + + + + Something did not go as we expected, but it's no problem. + + + + + Something that should not fail failed, but we can still keep + on going. + + + + + Something failed, and we cannot handle it properly. + + + + + Interface used to write to log files. + + + + + Write an entry to the log file. + + object that is writing to the log + importance of the log message + the message + + + + This class writes to the console. It colors the output depending on the logprio and includes a 3-level stacktrace (in debug mode) + + + + + + The actual instance of this class. + + + + + Logwriters the specified source. + + object that wrote the logentry. + Importance of the log message + The message. + + + + Get color for the specified logprio + + prio for the log entry + A for the prio + + + + Default log writer, writes everything to null (nowhere). + + + + + + The logging instance. + + + + + Writes everything to null + + object that wrote the log entry. + Importance of the log message + The message. + + + + PrototypeJS implementation of the javascript functions. + + + + + Purpose of this class is to create a javascript toolkit independent javascript helper. + + + + + Generates a list with JS options. + + StringBuilder that the options should be added to. + the javascript options. name, value pairs. each string value should be escaped by YOU! + true if we should start with a comma. + + + + Removes any javascript parameters from an array of parameters + + The array of parameters to remove javascript params from + An array of html parameters + + + + javascript action that should be added to the "onsubmit" event in the form tag. + + + All javascript option names should end with colon. + + + JSHelper.AjaxRequest("/user/show/1", "onsuccess:", "$('userInfo').update(result);"); + + + + + + Requests a url through ajax + + url to fetch + optional options in format "key, value, key, value", used in JS request object. + a link tag + All javascript option names should end with colon. + + + JSHelper.AjaxRequest("/user/show/1", "onsuccess:", "$('userInfo').update(result);"); + + + + + + Ajax requests that updates an element with + the fetched content + + Url to fetch content from + element to update + optional options in format "key, value, key, value", used in JS updater object. + A link tag. + All javascript option names should end with colon. + + + JSHelper.AjaxUpdater("/user/show/1", "userInfo", "onsuccess:", "alert('Successful!');"); + + + + + + A link that pop ups a Dialog (overlay div) + + url to contents of dialog + link title + A "a"-tag that popups a dialog when clicked + name/value of html attributes + + WebHelper.DialogLink("/user/show/1", "show user", "onmouseover", "alert('booh!');"); + + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + Creates a new modal dialog window + + url to open in window. + window title (may not be supported by all js implementations) + + + + + + Requests a url through ajax + + url to fetch. Url is NOT enclosed in quotes by the implementation. You need to do that yourself. + optional options in format "key, value, key, value", used in JS request object. All keys should end with colon. + a link tag + onclick attribute is used by this method. + + + // plain text + JSHelper.AjaxRequest("'/user/show/1'"); + + // ajax request using this.href + string link = "<a href=\"/user/call/1\" onclick=\"" + JSHelper.AjaxRequest("this.href") + "/<call user</a>"; + + + + + + Determins if a list of strings contains a specific value + + options to check in + value to find + true if value was found + case insensitive + + + + Ajax requests that updates an element with + the fetched content + + URL to fetch. URL is NOT enclosed in quotes by the implementation. You need to do that yourself. + element to update + options in format "key, value, key, value". All keys should end with colon. + A link tag. + + + JSHelper.AjaxUpdater("'/user/show/1'", "user", "onsuccess:", "alert('hello');", "asynchronous:", "true"); + + + + + + A link that pop ups a Dialog (overlay div) + + URL to contents of dialog + link title + name, value, name, value + + A "a"-tag that popups a dialog when clicked + + Requires Control.Modal found here: http://livepipe.net/projects/control_modal/ + And the following JavaScript (load it in application.js): + + Event.observe(window, 'load', + function() { + document.getElementsByClassName('modal').each(function(link){ new Control.Modal(link); }); + } + ); + + + + WebHelper.DialogLink("/user/show/1", "show user", "onmouseover", "alert('booh!');"); + + + + + create a modal dialog (usually using DIVs) + + url to fetch + dialog title + javascript/html attributes. javascript options ends with colon ':'. + + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + javascript action that should be added to the "onsubmit" event in the form tag. + + remember to encapsulate strings in '' + + All javascript option names should end with colon. + + + JSHelper.AjaxRequest("/user/show/1", "onsuccess:", "$('userInfo').update(result);"); + + + + + + Can handle application/x-www-form-urlencoded + + + + + Interface for form content decoders. + + + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream enconding + A http form, or null if content could not be parsed. + If contents in the stream is not valid input data. + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream encoding + + A HTTP form, or null if content could not be parsed. + + If contents in the stream is not valid input data. + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + redirects from one URL to another. + + + + + Rules are used to perform operations before a request is being handled. + Rules can be used to create routing etc. + + + + + Process the incoming request. + + incoming HTTP request + outgoing HTTP response + true if response should be sent to the browser directly (no other rules or modules will be processed). + + returning true means that no modules will get the request. Returning true is typically being done + for redirects. + + If request or response is null. + + + + Initializes a new instance of the class. + + Absolute path (no server name) + Absolute path (no server name) + + server.Add(new RedirectRule("/", "/user/index")); + + + + + Initializes a new instance of the class. + + Absolute path (no server name) + Absolute path (no server name) + true if request should be redirected, false if the request URI should be replaced. + + server.Add(new RedirectRule("/", "/user/index")); + + + + + Process the incoming request. + + incoming HTTP request + outgoing HTTP response + true if response should be sent to the browser directly (no other rules or modules will be processed). + + returning true means that no modules will get the request. Returning true is typically being done + for redirects. + + + + + Gets string to match request URI with. + + Is compared to request.Uri.AbsolutePath + + + + Gets where to redirect. + + + + + Gets whether server should redirect client. + + + false means that the rule will replace + the current request URI with the new one from this class. + true means that a redirect response is sent to the client. + + + + + Contains a listener that doesn't do anything with the connections. + + + + + Listen for regular HTTP connections + + IP Address to accept connections on + TCP Port to listen on, default HTTP port is 80. + Factory used to create es. + address is null. + Port must be a positive number. + + + + Initializes a new instance of the class. + + IP Address to accept connections on + TCP Port to listen on, default HTTPS port is 443 + Factory used to create es. + Certificate to use + which HTTPS protocol to use, default is TLS. + + + Exception. + + + + Will try to accept connections one more time. + + If any exceptions is thrown. + + + + Can be used to create filtering of new connections. + + Accepted socket + true if connection can be accepted; otherwise false. + + + + Start listen for new connections + + Number of connections that can stand in a queue to be accepted. + Listener have already been started. + + + + Stop the listener + + + + + + Gives you a change to receive log entries for all internals of the HTTP library. + + + You may not switch log writer after starting the listener. + + + + + True if we should turn on trace logs. + + + + + Gets currently used port. + + + + + Catch exceptions not handled by the listener. + + + Exceptions will be thrown during debug mode if this event is not used, + exceptions will be printed to console and suppressed during release mode. + + + + + A request have been received from a . + + + + Class to handle loading of resource files + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + logger. + + + + Loads resources from a namespace in the given assembly to an URI + + The URI to map the resources to + The assembly in which the resources reside + The namespace from which to load the resources + + + resourceLoader.LoadResources("/user/", typeof(User).Assembly, "MyLib.Models.User.Views"); + + Will make the resource MyLib.Models.User.Views.list.Haml accessible via /user/list.haml or /user/list/ + + The amount of loaded files, giving you the possibility of making sure the resources needed gets loaded + If a resource has already been mapped to an uri + + + + Retrieves a stream for the specified resource path if loaded otherwise null + + Path to the resource to retrieve a stream for + A stream or null if the resource couldn't be found + + + + Fetch all files from the resource that matches the specified arguments. + + The path to the resource to extract + + a list of files if found; or an empty array if no files are found. + + Search path must end with an asterisk for finding arbitrary files + + + + Fetch all files from the resource that matches the specified arguments. + + Where the file should reside. + Files to check + + a list of files if found; or an empty array if no files are found. + + + + + Returns whether or not the loader has an instance of the file requested + + The name of the template/file + True if the loader can provide the file + + + + Helpers making it easier to work with forms. + + + + + + Used to let the website use different JavaScript libraries. + Default is + + + + + Create a <form> tag. + + name of form + action to invoke on submit + form should be posted as Ajax + HTML code + + + // without options + WebHelper.FormStart("frmLogin", "/user/login", Request.IsAjax); + + // with options + WebHelper.FormStart("frmLogin", "/user/login", Request.IsAjax, "style", "display:inline", "class", "greenForm"); + + + HTML attributes or JavaScript options. + Method will ALWAYS be POST. + options must consist of name, value, name, value + + + + Creates a select list with the values in a collection. + + Name of the SELECT-tag + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + string containing a SELECT-tag. + + + + + Creates a select list with the values in a collection. + + Name of the SELECT-tag + Id of the SELECT-tag + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + string containing a SELECT-tag. + + + + // Class that is going to be used in a SELECT-tag. + public class User + { + private readonly string _realName; + private readonly int _id; + public User(int id, string realName) + { + _id = id; + _realName = realName; + } + public string RealName + { + get { return _realName; } + } + + public int Id + { + get { return _id; } + } + } + + // Using an inline delegate to generate the select list + public void UserInlineDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, delegate(object o, out object id, out object value) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + }, 2, true); + } + + // Using an method as delegate to generate the select list. + public void UseExternalDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, UserOptions, 1, true); + } + + // delegate returning id and title + public static void UserOptions(object o, out object id, out object title) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + } + + + name, id, collection or getIdTitle is null. + + + + Creates a select list with the values in a collection. + + Name of the SELECT-tag + Id of the SELECT-tag + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + name, value collection of extra HTML attributes. + string containing a SELECT-tag. + + name, id, collection or getIdTitle is null. + Invalid HTML attribute list. + + + + Generate a list of HTML options + + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + + collection or getIdTitle is null. + + + sb is null. + + + + Creates a check box. + + element name + element value + determines if the check box is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + + + Creates a check box. + + element name + element id + element value + determines if the check box is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + value in your business object. (check box will be selected if it matches the element value) + + + + + Creates a check box. + + element name + element id + determines if the check box is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + will set value to "1". + + + + Creates a RadioButton. + + element name + element value + determines if the radio button is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + + + Creates a RadioButton. + + element name + element id + element value + determines if the radio button is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + + + form close tag + + + + + + Implements HTTP Digest authentication. It's more secure than Basic auth since password is + encrypted with a "key" from the server. + + + Keep in mind that the password is encrypted with MD5. Use a combination of SSL and digest auth to be secure. + + + + + Authentication modules are used to implement different + kind of HTTP authentication. + + + + + Tag used for authentication. + + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + Delegate used to determine if authentication is required (may be null). + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + + + + Create a response that can be sent in the WWW-Authenticate header. + + Realm that the user should authenticate in + Array with optional options. + A correct authentication request. + If realm is empty or null. + + + + An authentication response have been received from the web browser. + Check if it's correct + + Contents from the Authorization header + Realm that should be authenticated + GET/POST/PUT/DELETE etc. + options to specific implementations + Authentication object that is stored for the request. A user class or something like that. + if is invalid + If any of the parameters is empty or null. + + + + Used to invoke the authentication delegate that is used to lookup the user name/realm. + + Realm (domain) that user want to authenticate in + User name + Password used for validation. Some implementations got password in clear text, they are then sent to client. + object that will be stored in the request to help you identify the user if authentication was successful. + true if authentication was successful + + + + Determines if authentication is required. + + HTTP request from browser + true if user should be authenticated. + throw from your delegate if no more attempts are allowed. + If no more attempts are allowed + + + + name used in HTTP request. + + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + Delegate used to determine if authentication is required (may be null). + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + + + + Used by test classes to be able to use hardcoded values + + + + + An authentication response have been received from the web browser. + Check if it's correct + + Contents from the Authorization header + Realm that should be authenticated + GET/POST/PUT/DELETE etc. + First option: true if username/password is correct but not cnonce + + Authentication object that is stored for the request. A user class or something like that. + + if authenticationHeader is invalid + If any of the paramters is empty or null. + + + + Encrypts parameters into a Digest string + + Realm that the user want to log into. + User logging in + Users password. + HTTP method. + Uri/domain that generated the login prompt. + Quality of Protection. + "Number used ONCE" + Hexadecimal request counter. + "Client Number used ONCE" + Digest encrypted string + + + + + + Md5 hex encoded "userName:realm:password", without the quotes. + Md5 hex encoded "method:uri", without the quotes + Quality of Protection + "Number used ONCE" + Hexadecimal request counter. + Client number used once + + + + + Create a response that can be sent in the WWW-Authenticate header. + + Realm that the user should authenticate in + First options specifies if true if username/password is correct but not cnonce. + A correct auth request. + If realm is empty or null. + + + + Decodes authorization header value + + header value + Encoding that the buffer is in + All headers and their values if successful; otherwise null + + NameValueCollection header = DigestAuthentication.Decode("response=\"6629fae49393a05397450978507c4ef1\",\r\nc=00001", Encoding.ASCII); + + Can handle lots of whitespaces and new lines without failing. + + + + Gets the current nonce. + + + + + + Gets the Md5 hash bin hex2. + + To be hashed. + + + + + determines if the nonce is valid or has expired. + + nonce value (check wikipedia for info) + true if the nonce has not expired. + + + + name used in http request. + + + + + Gets or sets whether the token supplied in is a + HA1 generated string. + + + + + cookie sent by the client/browser + + + + + + Constructor. + + cookie identifier + cookie content + id or content is null + id is empty + + + + Gets the cookie HTML representation. + + cookie string + + + + Gets the cookie identifier. + + + + + Cookie value. Set to null to remove cookie. + + + + + Contains server side HTTP request information. + + + + + Contains server side HTTP request information. + + + + + Called during parsing of a . + + Name of the header, should not be URL encoded + Value of the header, should not be URL encoded + If a header is incorrect. + + + + Add bytes to the body + + buffer to read bytes from + where to start read + number of bytes to read + Number of bytes actually read (same as length unless we got all body bytes). + If body is not writable + bytes is null. + offset is out of range. + + + + Clear everything in the request + + + + + Decode body into a form. + + A list with form decoders. + If body contents is not valid for the chosen decoder. + If body is still being transferred. + + + + Create a byte array from the body contents. + + Byte array containing the body contents + If body is still being transferred or request is + not using the POST or PUT verbs + + + + Sets the cookies. + + The cookies. + + + + Create a response object. + + Context for the connected client. + A new . + + + + Gets kind of types accepted by the client. + + + + + Gets or sets body stream. + + + + + Gets whether the body is complete. + + + + + Gets or sets kind of connection used for the session. + + + + + Gets or sets number of bytes in the body. + + + + + Gets cookies that was sent with the request. + + + + + Gets form parameters. + + + + + Gets headers sent by the client. + + + + + Gets or sets version of HTTP protocol that's used. + + + Probably or . + + + + + + Gets whether the request was made by Ajax (Asynchronous JavaScript) + + + + + Gets or sets requested method. + + + Will always be in upper case. + + + + + + Gets parameter from or . + + + + + Gets variables sent in the query string + + + + + Gets a value indicating whether this is secure. + + + + + Remote client's IP address and port + + + + + Gets or sets requested URI. + + + + + Gets URI absolute path divided into parts. + + + // URI is: http://gauffin.com/code/tiny/ + Console.WriteLine(request.UriParts[0]); // result: code + Console.WriteLine(request.UriParts[1]); // result: tiny + + + If you're using controllers than the first part is controller name, + the second part is method name and the third part is Id property. + + + + + + Gets or sets path and query. + + + + Are only used during request parsing. Cannot be set after "Host" header have been + added. + + + + + Chars used to split an URL path into multiple parts. + + + + + Assign a form. + + + + + + Creates a new object that is a copy of the current instance. + + + + A new object that is a copy of this instance. + + 2 + + + + Decode body into a form. + + A list with form decoders. + If body contents is not valid for the chosen decoder. + If body is still being transferred. + + + + Create a byte array from the body contents. + + Byte array containing the body contents + If body is still being transferred or request is + not using the POST or PUT verbs + + + + Cookies + + the cookies + + + + Create a response object. + + A new . + + + + Called during parsing of a . + + Name of the header, should not be URL encoded + Value of the header, should not be URL encoded + If a header is incorrect. + + + + Add bytes to the body + + buffer to read bytes from + where to start read + number of bytes to read + Number of bytes actually read (same as length unless we got all body bytes). + If body is not writable + bytes is null. + offset is out of range. + + + + Clear everything in the request + + + + + Gets or sets a value indicating whether this is secure. + + + + + Path and query (will be merged with the host header) and put in Uri + + + + + + Gets whether the body is complete. + + + + + Gets kind of types accepted by the client. + + + + + Gets or sets body stream. + + + + + Gets or sets kind of connection used for the session. + + + + + Gets or sets number of bytes in the body. + + + + + Gets headers sent by the client. + + + + + Gets or sets version of HTTP protocol that's used. + + + Probably or . + + + + + + Gets or sets requested method. + + + + Will always be in upper case. + + + + + + Gets variables sent in the query string + + + + + Remote client's IP address and port + + + + + Gets or sets requested URI. + + + + + Uri absolute path splitted into parts. + + + // uri is: http://gauffin.com/code/tiny/ + Console.WriteLine(request.UriParts[0]); // result: code + Console.WriteLine(request.UriParts[1]); // result: tiny + + + If you're using controllers than the first part is controller name, + the second part is method name and the third part is Id property. + + + + + + Gets parameter from or . + + + + + Gets form parameters. + + + + + Gets whether the request was made by Ajax (Asynchronous JavaScript) + + + + + Gets cookies that was sent with the request. + + + + + Creates request parsers when needed. + + + + + Creates request parsers when needed. + + + + + Create a new request parser. + + Used when logging should be enabled. + A new request parser. + + + + Create a new request parser. + + Used when logging should be enabled. + A new request parser. + + + + Class that receives Requests from a . + + + + + Client have been disconnected. + + Client that was disconnected. + Reason + + + + + Invoked when a client context have received a new HTTP request + + Client that received the request. + Request that was received. + + + + + Used to create and reuse contexts. + + + + + Used to create es. + + + + + Creates a that handles a connected client. + + Client socket (accepted by the ). + A creates . + + + + Create a secure . + + Client socket (accepted by the ). + HTTPS certificate to use. + Kind of HTTPS protocol. Usually TLS or SSL. + Require an SSL client certificate + A created . + + + + A request have been received from one of the contexts. + + + + + Initializes a new instance of the class. + + The writer. + Amount of bytes to read from the incoming socket stream. + Used to create a request parser. + + + + Create a new context. + + true if socket is running HTTPS. + Client that connected + Client security certificate + Network/SSL stream. + Client socket + A context. + + + + Create a new context. + + true if HTTPS is used. + Remote client + Client security certificate + Network stream, uses . + Client socket + A new context (always). + + + + Create a secure . + + Client socket (accepted by the ). + HTTPS certificate to use. + Kind of HTTPS protocol. Usually TLS or SSL. + Require an SSL client certificate + + A created . + + + + + Creates a that handles a connected client. + + Client socket (accepted by the ). + + A creates . + + + + + True if detailed trace logs should be written. + + + + + A request have been received from one of the contexts. + + + + + Custom network stream to mark sockets as reusable when disposing the stream. + + + + + Creates a new instance of the class for the specified . + + + The that the will use to send and receive data. + + + The parameter is null. + + + The parameter is not connected. + -or- + The property of the parameter is not . + -or- + The parameter is in a nonblocking state. + + + + + Initializes a new instance of the class for the specified with the specified ownership. + + + The that the will use to send and receive data. + + + Set to true to indicate that the will take ownership of the ; otherwise, false. + + + The parameter is null. + + + The parameter is not connected. + -or- + the value of the property of the parameter is not . + -or- + the parameter is in a nonblocking state. + + + + + Creates a new instance of the class for the specified with the specified access rights. + + + The that the will use to send and receive data. + + + A bitwise combination of the values that specify the type of access given to the over the provided . + + + The parameter is null. + + + The parameter is not connected. + -or- + the property of the parameter is not . + -or- + the parameter is in a nonblocking state. + + + + + Creates a new instance of the class for the specified with the specified access rights and the specified ownership. + + + The that the will use to send and receive data. + + + A bitwise combination of the values that specifies the type of access given to the over the provided . + + + Set to true to indicate that the will take ownership of the ; otherwise, false. + + + The parameter is null. + + + The parameter is not connected. + -or- + The property of the parameter is not . + -or- + The parameter is in a nonblocking state. + + + + + Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. + + + + + Releases the unmanaged resources used by the and optionally releases the managed resources. + + true to release both managed and unmanaged resources; false to release only unmanaged resources. + + + + Webhelper provides helpers for common tasks in HTML. + + + + + Used to let the website use different javascript libraries. + Default is + + + + + Creates a link that invokes through ajax. + + url to fetch + link title + + optional options in format "key, value, key, value". + Javascript options starts with ':'. + + a link tag + + WebHelper.AjaxRequest("/users/add/", "Add user", "method:", "post", "onclick", "validate('this');"); + + + + + Builds a link that updates an element with the fetched ajax content. + + Url to fetch content from + link title + html element to update with the results of the ajax request. + optional options in format "key, value, key, value" + A link tag. + + + + A link that pop ups a Dialog (overlay div) + + url to contents of dialog + link title + name/value of html attributes. + A "a"-tag that popups a dialog when clicked + + WebHelper.DialogLink("/user/show/1", "show user", "onmouseover", "alert('booh!');"); + + + + + Create/Open a dialog box using ajax + + + + + + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + Create a <form> tag. + + name of form + action to invoke on submit + form should be posted as ajax + html code + + WebHelper.FormStart("frmLogin", "/user/login", Request.IsAjax); + + + + + Create a link tag. + + url to go to + link title (text that is displayed) + html attributes, name, value, name, value + html code + + WebHelper.Link("/user/show/1", "Show user", "id", "showUser", "onclick", "return confirm('Are you shure?');"); + + + + + Build a link + + url to go to. + title of link (displayed text) + extra html attributes. + a complete link + + + + Build a link + + url to go to. + title of link (displayed text) + extra html attributes. + a complete link + more options + + + + Obsolete + + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + + + + Obsolete + + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + + + + Render errors into a UL with class "errors" + + class used by UL-tag. + items to list + an unordered html list. + + + + Render errors into a UL with class "errors" + + class used by UL-tag. + items to list + an unordered html list. + + + + Render errors into a UL with class "errors" + + + + + + + Generates a list with html attributes. + + StringBuilder that the options should be added to. + attributes set by user. + attributes set by any of the helper classes. + + + + Generates a list with html attributes. + + StringBuilder that the options should be added to. + + + + + Container to bind resource names to assemblies + + + + + Instantiates an instance of + + The dot seperated uri the resource maps to + The full resource name + The assembly the resource exists in + + + + Retrieves a stream to the resource + + Null if the resource couldn't be located somehow + + + + Retrieves the assembly the resource resides in + + + + + Retrieves the full name/path of the assembly + + + + + Retrieves the extension of the resource + + + + Returns the Uri without extension + + + Retrieves the full path name to the resource file + + + + All HTTP based exceptions will derive this class. + + + + + Create a new HttpException + + http status code (sent in the response) + error description + + + + Create a new HttpException + + http status code (sent in the response) + error description + inner exception + + + + status code to use in the response. + + + + + We dont want to let the server to die due to exceptions thrown in worker threads. + therefore we use this delegate to give you a change to handle uncaught exceptions. + + Class that the exception was thrown in. + Exception + + Server will throw a InternalServerException in release version if you dont + handle this delegate. + + + + + Delegate used to let authentication modules authenticate the user name and password. + + Realm that the user want to authenticate in + User name specified by client + Can either be user password or implementation specific token. + object that will be stored in a session variable called if authentication was successful. + throw forbidden exception if too many attempts have been made. + + + Use to specify that the token is a HA1 token. (MD5 generated + string from realm, user name and password); Md5String(userName + ":" + realm + ":" + password); + + + + + + Let's you decide on a system level if authentication is required. + + HTTP request from client + true if user should be authenticated. + throw if no more attempts are allowed. + If no more attempts are allowed + + + + New implementation of the HTTP listener. + + + Use the Create methods to create a default listener. + + + + + Initializes a new instance of the class. + + IP Address to accept connections on + TCP Port to listen on, default HTTP port is 80. + Factory used to create es. + address is null. + Port must be a positive number. + + + + Initializes a new instance of the class. + + The address. + The port. + The factory. + The certificate. + + + + Initializes a new instance of the class. + + The address. + The port. + The factory. + The certificate. + Callback to validate SSL client certificates. + The protocol. + True if client SSL certificates are required, otherwise false. + + + + Creates a new instance with default factories. + + Address that the listener should accept connections on. + Port that listener should accept connections on. + Created HTTP listener. + + + + Creates a new instance with default factories. + + Address that the listener should accept connections on. + Port that listener should accept connections on. + Certificate to use + Created HTTP listener. + + + + Creates a new instance with default factories. + + Address that the listener should accept connections on. + Port that listener should accept connections on. + Certificate to use + Callback to validate SSL client certificates. + which HTTPS protocol to use, default is TLS. + True if client SSL certificates are required, otherwise false. + Created HTTP listener. + + + + Can be used to create filtering of new connections. + + Accepted socket + + true if connection can be accepted; otherwise false. + + + + + A client have been accepted, but not handled, by the listener. + + + + + Contains some kind of input from the browser/client. + can be QueryString, form data or any other request body content. + + + All names are converted into lower case. + + + + + Base class for request data containers + + + + + Adds a parameter mapped to the presented name + + The name to map the parameter to + The parameter value + + + + Returns true if the container contains the requested parameter + + Parameter id + True if parameter exists + + + + Returns a request parameter + + The name associated with the parameter + + + + Representation of a non-initialized class instance + + + Variable telling the class that it is non-initialized + + + + Initializes a new instance of the class. + + form name. + + + + Initializes a new instance of the class. + + form name. + if set to true all changes will be ignored. + this constructor should only be used by Empty + + + Creates a deep copy of the HttpInput class + The object to copy + The function makes a deep copy of quite a lot which can be slow + + + + Add a new element. Form array elements are parsed + and added in a correct hierarchy. + + Name is converted to lower case. + + name is null. + Cannot add stuff to . + + + + Returns true if the class contains a with the corresponding name. + + The field/query string name + True if the value exists + + + + Parses an item and returns it. + This function is primarily used to parse array items as in user[name]. + + + + + + + Outputs the instance representing all its values joined together + + + + Returns all items as an unescaped query string. + + + + + Extracts one parameter from an array + + Containing the string array + All but the first value + + string test1 = ExtractOne("system[user][extension][id]"); + string test2 = ExtractOne(test1); + string test3 = ExtractOne(test2); + // test1 = user[extension][id] + // test2 = extension[id] + // test3 = id + + + + Resets all data contained by class + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + + An object that can be used to iterate through the collection. + + 2 + + + + Form name as lower case + + + + + Get a form item. + + + Returns if item was not found. + + + + Interface for sessions + + + + + Remove everything from the session + + + + + Remove everything from the session + + True if the session is cleared due to expiration + + + + Session id + + + + + Should + + Name of the session variable + null if it's not set + If the object cant be serialized. + + + + When the session was last accessed. + This property is touched by the HTTP server each time the + session is requested. + + + + + Number of session variables. + + + + + Event triggered upon clearing the session + + + + + Gets keys for all values. + + + + + Arguments sent when a is cleared + + + + + Instantiates the arguments for the event + + True if the session is cleared due to expiration + + + + Gets if the session have expired. + + + + + Delegate for when a is cleared + + this is being cleared. + Arguments for the clearing + + + + Class to make dynamic binding of redirects. Instead of having to specify a number of similar redirect rules + a regular expression can be used to identify redirect URLs and their targets. + + + [a-z0-9]+)", "/users/${target}?find=true", RegexOptions.IgnoreCase) + ]]> + + + + + Initializes a new instance of the class. + + Expression to match URL + Expression to generate URL + + [a-zA-Z0-9]+)", "/user/${first}")); + Result of ie. /employee1 will then be /user/employee1 + ]]> + + + + + Initializes a new instance of the class. + + Expression to match URL + Expression to generate URL + Regular expression options to use, can be null + + [a-zA-Z0-9]+)", "/user/{first}", RegexOptions.IgnoreCase)); + Result of ie. /employee1 will then be /user/employee1 + ]]> + + + + + Initializes a new instance of the class. + + Expression to match URL + Expression to generate URL + Regular expression options to apply + true if request should be redirected, false if the request URI should be replaced. + + [a-zA-Z0-9]+)", "/user/${first}", RegexOptions.None)); + Result of ie. /employee1 will then be /user/employee1 + ]]> + + Argument is null. + + + + + Process the incoming request. + + incoming HTTP request + outgoing HTTP response + true if response should be sent to the browser directly (no other rules or modules will be processed). + + returning true means that no modules will get the request. Returning true is typically being done + for redirects. + + If request or response is null + + + + Contains all HTTP Methods (according to the HTTP 1.1 specification) + + See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + + + + + + The DELETE method requests that the origin server delete the resource identified by the Request-URI. + + + + This method MAY be overridden by human intervention (or other means) on the origin server. + The client cannot be guaranteed that the operation has been carried out, even if the status code + returned from the origin server indicates that the action has been completed successfully. + + + However, the server SHOULD NOT indicate success unless, at the time the response is given, + it intends to delete the resource or move it to an inaccessible location. + + + A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, + 202 (Accepted) if the action has not yet been enacted, + or 204 (No Content) if the action has been enacted but the response does not include an entity. + + + If the request passes through a cache and the Request-URI identifies one or more currently cached entities, + those entries SHOULD be treated as stale. Responses to this method are not cacheable. + + + + + + The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. + + + + If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the + entity in the response and not the source text of the process, unless that text happens to be the output of the process. + + + The semantics of the GET method change to a "conditional GET" if the request message includes an + If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. + A conditional GET method requests that the entity be transferred only under the circumstances described + by the conditional header field(s). The conditional GET method is intended to reduce unnecessary network + usage by allowing cached entities to be refreshed without requiring multiple requests or transferring + data already held by the client. + + + + + + The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. + + + The meta information contained in the HTTP headers in response to a HEAD request SHOULD be identical to the + information sent in response to a GET request. This method can be used for obtaining meta information about + the entity implied by the request without transferring the entity-body itself. + + This method is often used for testing hypertext links for validity, accessibility, and recent modification. + + + + + The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. + + + This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval. + + + + + The POST method is used to request that the origin server accept the entity enclosed + in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. + + + POST is designed to allow a uniform method to cover the following functions: + + + Annotation of existing resources; + + Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles; + + Providing a block of data, such as the result of submitting a form, to a data-handling process; + + Extending a database through an append operation. + + + + If a resource has been created on the origin server, the response SHOULD be 201 (Created) and + contain an entity which describes the status of the request and refers to the new resource, and a + Location header (see section 14.30). + + + The action performed by the POST method might not result in a resource that can be identified by a URI. + In this case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on + whether or not the response includes an entity that describes the result. + + Responses to this method are not cacheable, unless the response includes appropriate Cache-Control + or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent + to retrieve a cacheable resource. + + + + + + The PUT method requests that the enclosed entity be stored under the supplied Request-URI. + + + + + If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a + modified version of the one residing on the origin server. + + If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new + resource by the requesting user agent, the origin server can create the resource with that URI. + + If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. + + If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to + indicate successful completion of the request. + + If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be + given that reflects the nature of the problem. + + + + The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not + understand or implement and MUST return a 501 (Not Implemented) response in such cases. + + + + + + The TRACE method is used to invoke a remote, application-layer loop- back of the request message. + + + + + Contains all HTTP Methods (according to the HTTP 1.1 specification) + + See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + + + + + + The DELETE method requests that the origin server delete the resource identified by the Request-URI. + + + + This method MAY be overridden by human intervention (or other means) on the origin server. + The client cannot be guaranteed that the operation has been carried out, even if the status code + returned from the origin server indicates that the action has been completed successfully. + + + However, the server SHOULD NOT indicate success unless, at the time the response is given, + it intends to delete the resource or move it to an inaccessible location. + + + A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, + 202 (Accepted) if the action has not yet been enacted, + or 204 (No Content) if the action has been enacted but the response does not include an entity. + + + If the request passes through a cache and the Request-URI identifies one or more currently cached entities, + those entries SHOULD be treated as stale. Responses to this method are not cacheable. + + + + + + The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. + + + + If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the + entity in the response and not the source text of the process, unless that text happens to be the output of the process. + + + The semantics of the GET method change to a "conditional GET" if the request message includes an + If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. + A conditional GET method requests that the entity be transferred only under the circumstances described + by the conditional header field(s). The conditional GET method is intended to reduce unnecessary network + usage by allowing cached entities to be refreshed without requiring multiple requests or transferring + data already held by the client. + + + + + + The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. + + + The meta information contained in the HTTP headers in response to a HEAD request SHOULD be identical to the + information sent in response to a GET request. This method can be used for obtaining meta information about + the entity implied by the request without transferring the entity-body itself. + + This method is often used for testing hypertext links for validity, accessibility, and recent modification. + + + + + The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. + + + This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval. + + + + + The POST method is used to request that the origin server accept the entity enclosed + in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. + + + POST is designed to allow a uniform method to cover the following functions: + + + Annotation of existing resources; + + Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles; + + Providing a block of data, such as the result of submitting a form, to a data-handling process; + + Extending a database through an append operation. + + + + If a resource has been created on the origin server, the response SHOULD be 201 (Created) and + contain an entity which describes the status of the request and refers to the new resource, and a + Location header (see section 14.30). + + + The action performed by the POST method might not result in a resource that can be identified by a URI. + In this case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on + whether or not the response includes an entity that describes the result. + + Responses to this method are not cacheable, unless the response includes appropriate Cache-Control + or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent + to retrieve a cacheable resource. + + + + + + The PUT method requests that the enclosed entity be stored under the supplied Request-URI. + + + + + If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a + modified version of the one residing on the origin server. + + If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new + resource by the requesting user agent, the origin server can create the resource with that URI. + + If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. + + If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to + indicate successful completion of the request. + + If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be + given that reflects the nature of the problem. + + + + The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not + understand or implement and MUST return a 501 (Not Implemented) response in such cases. + + + + + + The TRACE method is used to invoke a remote, application-layer loop- back of the request message. + + + + + Returns item either from a form or a query string (checks them in that order) + + + + Representation of a non-initialized HttpParam + + + Initialises the class to hold a value either from a post request or a querystring request + + + + The add method is not availible for HttpParam + since HttpParam checks both Request.Form and Request.QueryString + + name identifying the value + value to add + + + + + Checks whether the form or querystring has the specified value + + Name, case sensitive + true if found; otherwise false. + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + + An object that can be used to iterate through the collection. + + 2 + + + + Fetch an item from the form or querystring (in that order). + + + Item if found; otherwise HttpInputItem.EmptyLanguageNode + + + + Serves files that are stored in embedded resources. + + + + + A HttpModule can be used to serve Uri's. The module itself + decides if it should serve a Uri or not. In this way, you can + get a very flexible http application since you can let multiple modules + serve almost similar urls. + + + Throw if you are using a and want to prompt for user name/password. + + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + true if this module handled the request. + + + + Set the log writer to use. + + logwriter to use. + + + + Log something. + + importance of log message + message + + + + If true specifies that the module doesn't consume the processing of a request so that subsequent modules + can continue processing afterwards. Default is false. + + + + + Initializes a new instance of the class. + Runs to make sure the basic mime types are available, they can be cleared later + through the use of if desired. + + + + + Initializes a new instance of the class. + Runs to make sure the basic mime types are available, they can be cleared later + through the use of if desired. + + The log writer to use when logging events + + + + Mimtypes that this class can handle per default + + + + + Loads resources from a namespace in the given assembly to an uri + + The uri to map the resources to + The assembly in which the resources reside + The namespace from which to load the resources + + resourceLoader.LoadResources("/user/", typeof(User).Assembly, "MyLib.Models.User.Views"); + + will make ie the resource MyLib.Models.User.Views.stylesheet.css accessible via /user/stylesheet.css + + The amount of loaded files, giving you the possibility of making sure the resources needed gets loaded + + + + Returns true if the module can handle the request + + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + true if this module handled the request. + + + + List with all mime-type that are allowed. + + All other mime types will result in a Forbidden http status code. + + + + The request requires user authentication. The response MUST include a + WWW-Authenticate header field (section 14.47) containing a challenge + applicable to the requested resource. + + The client MAY repeat the request with a suitable Authorization header + field (section 14.8). If the request already included Authorization + credentials, then the 401 response indicates that authorization has been + refused for those credentials. If the 401 response contains the same challenge + as the prior response, and the user agent has already attempted authentication + at least once, then the user SHOULD be presented the entity that was given in the response, + since that entity might include relevant diagnostic information. + + HTTP access authentication is explained in rfc2617: + http://www.ietf.org/rfc/rfc2617.txt + + (description is taken from + http://www.submissionchamber.com/help-guides/error-codes.php#sec10.4.2) + + + + + Create a new unauhtorized exception. + + + + + + Create a new unauhtorized exception. + + reason to why the request was unauthorized. + inner exception + + + + Create a new unauhtorized exception. + + reason to why the request was unauthorized. + + + + Delegate used to find a realm/domain. + + + + + Realms are used during HTTP Authentication + + + + + + + A complete HTTP server, you need to add a module to it to be able to handle incoming requests. + + + + // this small example will add two web site modules, thus handling + // two different sites. In reality you should add Controller modules or something + // two the website modules to be able to handle different requests. + HttpServer server = new HttpServer(); + server.Add(new WebSiteModule("www.gauffin.com", "Gauffin Telecom AB")); + server.Add(new WebSiteModule("www.vapadi.se", "Remote PBX")); + + // start regular http + server.Start(IPAddress.Any, 80); + + // start https + server.Start(IPAddress.Any, 443, myCertificate); + + + + + + + + + Initializes a new instance of the class. + + Used to get all components used in the server.. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + Form decoders are used to convert different types of posted data to the object types. + + + + + + Initializes a new instance of the class. + + A session store is used to save and retrieve sessions + + + + + Initializes a new instance of the class. + + The log writer. + + + + + Initializes a new instance of the class. + + Form decoders are used to convert different types of posted data to the object types. + The log writer. + + + + + + + Initializes a new instance of the class. + + Form decoders are used to convert different types of posted data to the object types. + A session store is used to save and retrieve sessions + The log writer. + + + + + + + + Adds the specified rule. + + The rule. + + + + Add a to the server. + + module to add + + + + Removes a from the server. + + Module to remove. + + + + Decodes the request body. + + The request. + Failed to decode form data. + + + + Generate a HTTP error page (that will be added to the response body). + response status code is also set. + + Response that the page will be generated in. + . + response body contents. + + + + Generate a HTTP error page (that will be added to the response body). + response status code is also set. + + Response that the page will be generated in. + exception. + + + + Realms are used by the s. + + HTTP request + domain/realm. + + + + Process an incoming request. + + connection to client + request information + response that should be filled + session information + + + + Can be overloaded to implement stuff when a client have been connected. + + + Default implementation does nothing. + + client that disconnected + disconnect reason + + + + Handle authentication + + + + + true if request can be handled; false if not. + Invalid authorization header + + + + Will request authentication. + + + Sends respond to client, nothing else can be done with the response after this. + + + + + + + + Received from a when a request have been parsed successfully. + + that received the request. + The request. + + + + To be able to track request count. + + + + + + + Start the web server using regular HTTP. + + IP Address to listen on, use IpAddress.Any to accept connections on all IP addresses/network cards. + Port to listen on. 80 can be a good idea =) + address is null. + Port must be a positive number. + + + + Accept secure connections. + + IP Address to listen on, use to accept connections on all IP Addresses / network cards. + Port to listen on. 80 can be a good idea =) + Certificate to use + address is null. + Port must be a positive number. + + + + shut down the server and listeners + + + + + write an entry to the log file + + importance of the message + log message + + + + write an entry to the log file + + object that wrote the message + importance of the message + log message + + + + Server that is handling the current request. + + + Will be set as soon as a request arrives to the object. + + + + + Modules used for authentication. The module that is is added first is used as + the default authentication module. + + Use the corresponding property + in the if you are using multiple websites. + + + + Form decoder providers are used to decode request body (which normally contains form data). + + + + + Server name sent in HTTP responses. + + + Do NOT include version in name, since it makes it + easier for hackers. + + + + + Name of cookie where session id is stored. + + + + + Specified where logging should go. + + + + + + + + Number of connections that can wait to be accepted by the server. + + Default is 10. + + + + Gets or sets maximum number of allowed simultaneous requests. + + + + This property is useful in busy systems. The HTTP server + will start queuing new requests if this limit is hit, instead + of trying to process all incoming requests directly. + + + The default number if allowed simultaneous requests are 10. + + + + + + Gets or sets maximum number of requests queuing to be handled. + + + + The WebServer will start turning requests away if response code + to indicate that the server + is too busy to be able to handle the request. + + + + + + Realms are used during HTTP authentication. + Default realm is same as server name. + + + + + Let's to receive unhandled exceptions from the threads. + + + Exceptions will be thrown during debug mode if this event is not used, + exceptions will be printed to console and suppressed during release mode. + + + + Container for posted form data + + + Instance to help mark a non-initialized form + + + Initializes a form container with the specified name + + + + Makes a deep copy of the input + + The input to copy + + + + Adds a file to the collection of posted files + + The file to add + If the file is already added + If file is null + If the instance is HttpForm.EmptyForm which cannot be modified + + + + Checks if the form contains a specified file + + Field name of the file parameter + True if the file exists + If the instance is HttpForm.EmptyForm which cannot be modified + + + + Retrieves a file held by by the form + + The identifier of the file + The requested file or null if the file was not found + If name is null or empty + If the instance is HttpForm.EmptyForm which cannot be modified + + + Disposes all held HttpFile's and resets values + + + + Retrieves the number of files added to the + + 0 if no files are added + + + + The requested resource was not found in the web server. + + + + + Create a new exception + + message describing the error + inner exception + + + + Create a new exception + + message describing the error + + + + This class is created as a wrapper, since there are two different cookie types in .Net (Cookie and HttpCookie). + The framework might switch class in the future and we dont want to have to replace all instances + + + + + Let's copy all the cookies. + + value from cookie header. + + + + Adds a cookie in the collection. + + cookie to add + cookie is null + + + + Gets a collection enumerator on the cookie list. + + collection enumerator + + + + Remove all cookies. + + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Remove a cookie from the collection. + + Name of cookie. + + + + Gets the count of cookies in the collection. + + + + + Gets the cookie of a given identifier (null if not existing). + + + + + The request could not be understood by the server due to malformed syntax. + The client SHOULD NOT repeat the request without modifications. + + Text taken from: http://www.submissionchamber.com/help-guides/error-codes.php + + + + + Create a new bad request exception. + + reason to why the request was bad. + + + + Create a new bad request exception. + + reason to why the request was bad. + inner exception + + + + A session stored in memory. + + + + + + + A unique id used by the sessions store to identify the session + + + + Id + + + + + + Remove everything from the session + + + + + Clears the specified expire. + + True if the session is cleared due to expiration + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + 2 + + + + Session id + + + + + Should + + Name of the session variable + null if it's not set + + + + when the session was last accessed. + + + Used to determine when the session should be removed. + + + + + Number of values in the session + + + + + Flag to indicate that the session have been changed + and should be saved into the session store. + + + + + Event triggered upon clearing the session + + + + + Gets keys for all values. + + + + + Small design by contract implementation. + + + + + Check whether a parameter is empty. + + Parameter value + Parameter name, or error description. + value is empty. + + + + Checks whether a parameter is null. + + Parameter value + Parameter name, or error description. + value is null. + + + + Checks whether a parameter is null. + + + Parameter value + Parameter name, or error description. + value is null. + + + + The server encountered an unexpected condition which prevented it from fulfilling the request. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + error message. + + + + Initializes a new instance of the class. + + error message. + inner exception. + + + + cookie being sent back to the browser. + + + + + + Constructor. + + cookie identifier + cookie content + cookie expiration date. Use DateTime.MinValue for session cookie. + id or content is null + id is empty + + + + Create a new cookie + + name identifying the cookie + cookie value + when the cookie expires. Setting DateTime.MinValue will delete the cookie when the session is closed. + Path to where the cookie is valid + Domain that the cookie is valid for. + + + + Create a new cookie + + Name and value will be used + when the cookie expires. + + + + Gets the cookie HTML representation. + + cookie string + + + + When the cookie expires. + DateTime.MinValue means that the cookie expires when the session do so. + + + + + Cookie is only valid under this path. + + + + + Event driven parser used to parse incoming HTTP requests. + + + The parser supports partial messages and keeps the states between + each parsed buffer. It's therefore important that the parser gets + ed if a client disconnects. + + + + + Parse partial or complete message. + + buffer containing incoming bytes + where in buffer that parsing should start + number of bytes to parse + Unparsed bytes left in buffer. + BadRequestException. + + + + Clear parser state. + + + + + Current state in parser. + + + + + A request have been successfully parsed. + + + + + More body bytes have been received. + + + + + Request line have been received. + + + + + A header have been received. + + + + + Gets or sets the log writer. + + + + + Current state in the parsing. + + + + + Should parse the request line + + + + + Searching for a complete header name + + + + + Searching for colon after header name (ignoring white spaces) + + + + + Searching for start of header value (ignoring white spaces) + + + + + Searching for a complete header value (can span over multiple lines, as long as they are prefixed with one/more whitespaces) + + + + + Adding bytes to body + + + + + The website module let's you handle multiple websites in the same server. + It uses the "Host" header to check which site you want. + + It's recommended that you do not + add any other modules to HttpServer if you are using the website module. Instead, + add all wanted modules to each website. + + + + + + domain name that should be handled. + + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + + + + Name of site. + + + + + This decoder converts XML documents to form items. + Each element becomes a subitem in the form, and each attribute becomes an item. + + + // xml: somethingdata + // result: + // form["hello"].Value = "something" + // form["hello"]["id"].Value = 1 + // form["hello"]["world]["id"].Value = 1 + // form["hello"]["world"].Value = "data" + + + The original xml document is stored in form["__xml__"].Value. + + + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream encoding + Note: contentType and encoding are not used? + A http form, or null if content could not be parsed. + + + + + Recursive function that will go through an xml element and store it's content + to the form item. + + (parent) Item in form that content should be added to. + Node that should be parsed. + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + Used to handle INI files. + + + + + Add a new section. + + Name of section. + + + iniFile.Add("users"); + iniFile["users"].Add("Jonas"); + + + + + + Checks if ini file contains the specified section. + + Name of section + true if section exists; otherwise false. + + + + Load a INI file from a stream reader. + + StreamReader containing INI file data. + if sucessfully parsed. + If stream do not contain a valid INI file. + + + + Read ini file data from a stream. + + Stream to read from + if sucessfully parsed. + If stream do not contain a valid INI file. + + + + Read a ini file. + + Path and filename + if sucessfully parsed. + If stream do not contain a valid INI file. + + + + Load INI file from a string variable. + + Contains ini file contents. + A . + If stream do not contain a valid INI file. + + + + Write a INI file to disk. + + Path and filename + If file is not writable or path do not exist. + + + + Returns a that represents the current . + + + A that represents the current . + + + + + Gets a section. + + Name of section. + if found; otherwise null. + + + + A section in a INI file. + + + + + Initializes a new instance of the class. + + The name. + + + + Add a new parameter + + Name + Value + + + + Checks if section contains a parameter + + Name of parameter + true if found; otherwise false. + + + + Remove a parameter + + Name of parameter. + true if found and removed; otherwise false. + + + + Returns an enumerator that iterates through the collection. + + + A that can be used to iterate through the collection. + + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + + + + Gets or sets a parameter value. + + Name of parameter. + Value or null if parameter was not found. + + + + Gets or sets name of section. + + + + + Simple template engine. + This engine turns templates into C# code and compiles them at runtime + to .NET assemblies. This makes the templates blazing fast. The templates + are only recompiled if they have been changed on disk. + + + + + Interface for template rendering engines + + + + + Render the template + + Filename of the template to render + A list of key/value pairs to pass to the template + The rendered output + If template is not found. + + + + Used to get correct names for generics. + + + + + + + Response that is sent back to the web browser / client. + + + + A response can be sent if different ways. The easiest one is + to just fill the Body stream with content, everything else + will then be taken care of by the framework. The default content-type + is text/html, you should change it if you send anything else. + + The second and slightly more complex way is to send the response + as parts. Start with sending the header using the SendHeaders method and + then you can send the body using SendBody method, but do not forget + to set and before doing so. + + + + + // Example using response body. + class MyModule : HttpModule + { + public override bool Process(IHttpRequest request, IHttpResponse response, IHttpSession session) + { + StreamWriter writer = new StreamWriter(response.Body); + writer.WriteLine("Hello dear World!"); + writer.Flush(); + + // return true to tell webserver that we've handled the url + return true; + } + } + + + todo: add two examples, using SendHeaders/SendBody and just the Body stream. + + + + Response that is sent back to the web browser / client. + + A response can be sent if different ways. The easiest one is + to just fill the Body stream with content, everything else + will then be taken care of by the framework. The default content-type + is text/html, you should change it if you send anything else. + + The second and slighty more complex way is to send the response + as parts. Start with sending the header using the SendHeaders method and + then you can send the body using SendBody method, but do not forget + to set ContentType and ContentLength before doing so. + + + public void MyHandler(IHttpRequest request, IHttpResponse response) + { + + } + + + + + Add another header to the document. + + Name of the header, case sensitive, use lower cases. + Header values can span over multiple lines as long as each line starts with a white space. New line chars should be \r\n + If headers already been sent. + If value conditions have not been met. + Adding any header will override the default ones and those specified by properties. + + + + Send headers and body to the browser. + + If content have already been sent. + + + + Make sure that you have specified ContentLength and sent the headers first. + + + If headers have not been sent. + + offest of first byte to send + number of bytes to send. + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Make sure that you have specified ContentLength and sent the headers first. + + + If headers have not been sent. + + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Send headers to the client. + + If headers already been sent. + + + + + + + Redirect client to somewhere else using the 302 status code. + + Destination of the redirect + If headers already been sent. + You can not do anything more with the request when a redirect have been done. This should be your last + action. + + + + redirect to somewhere + + where the redirect should go + + No body are allowed when doing redirects. + + + + + The body stream is used to cache the body contents + before sending everything to the client. It's the simplest + way to serve documents. + + + + + Defines the version of the HTTP Response for applications where it's required + for this to be forced. + + + + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing entity-header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + + + + Kind of connection + + + + + Encoding to use when sending stuff to the client. + + Default is UTF8 + + + + Number of seconds to keep connection alive + + Only used if Connection property is set to ConnectionType.KeepAlive + + + + Status code that is sent to the client. + + Default is HttpStatusCode.Ok + + + + Information about why a specific status code was used. + + + + + Size of the body. MUST be specified before sending the header, + unless property Chunked is set to true. + + + + + Kind of content in the body + + Default is text/html + + + + Headers have been sent to the client- + + You can not send any additional headers if they have already been sent. + + + + The whole response have been sent. + + + + + Cookies that should be created/changed. + + + + + Initializes a new instance of the class. + + Client that send the . + Contains information of what the client want to receive. + cannot be empty. + + + + Initializes a new instance of the class. + + Client that send the . + Version of HTTP protocol that the client uses. + Type of HTTP connection used. + + + + Add another header to the document. + + Name of the header, case sensitive, use lower cases. + Header values can span over multiple lines as long as each line starts with a white space. New line chars should be \r\n + If headers already been sent. + If value conditions have not been met. + Adding any header will override the default ones and those specified by properties. + + + + Send headers and body to the browser. + + If content have already been sent. + + + + Make sure that you have specified and sent the headers first. + + + If headers have not been sent. + + offset of first byte to send + number of bytes to send. + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Make sure that you have specified and sent the headers first. + + + If headers have not been sent. + + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Send headers to the client. + + If headers already been sent. + + + + + + + Redirect client to somewhere else using the 302 status code. + + Destination of the redirect + If headers already been sent. + You can not do anything more with the request when a redirect have been done. This should be your last + action. + + + + redirect to somewhere + + where the redirect should go + + No body are allowed when doing redirects. + + + + + The body stream is used to cache the body contents + before sending everything to the client. It's the simplest + way to serve documents. + + + + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing entity-header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + + + + Defines the version of the HTTP Response for applications where it's required + for this to be forced. + + + + + Kind of connection + + + + + Encoding to use when sending stuff to the client. + + Default is UTF8 + + + + Number of seconds to keep connection alive + + Only used if Connection property is set to . + + + + Status code that is sent to the client. + + Default is + + + + Information about why a specific status code was used. + + + + + Size of the body. MUST be specified before sending the header, + unless property Chunked is set to true. + + + + + Kind of content in the body + + Default type is "text/html" + + + + Headers have been sent to the client- + + You can not send any additional headers if they have already been sent. + + + + The whole response have been sent. + + + + + Cookies that should be created/changed. + + + + + Will contain helper functions for javascript. + + + + + Requests a url through ajax + + url to fetch. Url is NOT enclosed in quotes by the implementation. You need to do that yourself. + optional options in format "key, value, key, value", used in JS request object. All keys should end with colon. + a link tag + onclick attribute is used by this method. + + + // plain text + JSHelper.AjaxRequest("'/user/show/1'"); + + // ajax request using this.href + string link = "<a href=\"/user/call/1\" onclick=\"" + JSHelper.AjaxRequest("this.href") + "/<call user</a>"; + + + + + + Ajax requests that updates an element with + the fetched content + + url to fetch. Url is NOT enclosed in quotes by the implementation. You need to do that yourself. + element to update + options in format "key, value, key, value". All keys should end with colon. + A link tag. + + + JSHelper.AjaxUpdater("'/user/show/1'", "user", "onsuccess:", "alert('hello');", "asynchronous:", "true"); + + + + + + Opens contents in a dialog window. + + url to contents of dialog + link title + name, value, name, value, all parameter names should end with colon. + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + Used to queue incoming requests. + + + + + Initializes a new instance of the class. + + Called when a request should be processed. + + + + Increment the by 1 in a thread safe manor. + + + + + Decrement the by 1 in a thread safe manor. + + + + + Used to process queued requests. + + + + + Gets or sets maximum number of allowed simultaneous requests. + + + + + Gets or sets maximum number of requests queuing to be handled. + + + + + Specifies how many requests the HTTP server is currently processing. + + + + + Used two queue incoming requests to avoid + thread starvation. + + + + + Method used to process a queued request + + Context that the request was received from. + Request to process. + + + + Event arguments used when a new header have been parsed. + + + + + Initializes a new instance of the class. + + Name of header. + Header value. + + + + Initializes a new instance of the class. + + + + + Gets or sets header name. + + + + + Gets or sets header value. + + + + + A reverse proxy are used to act as a bridge between local (protected/hidden) websites + and public clients. + + A typical usage is to allow web servers on non standard ports to still be available + to the public clients, or allow web servers on private ips to be available. + + + + + + + Base url requested from browser + Base url on private web server + + // this will return contents from http://192.168.1.128/view/jonas when client requests http://www.gauffin.com/user/view/jonas + _server.Add(new ReverseProxyModule("http://www.gauffin.com/user/", "http://192.168.1.128/"); + + + + + Method that determines if an url should be handled or not by the module + + Url requested by the client. + true if module should handle the url. + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + + + + Generic helper functions for HTTP + + + + + Version string for HTTP v1.0 + + + + + Version string for HTTP v1.1 + + + + + Parses a query string. + + Query string (URI encoded) + A object if successful; otherwise + queryString is null. + If string cannot be parsed. + + + + If you string contains %2d instead of =, you have encoded your string + incorrectly. It's not a bug in the http server that it isn't decoded. + + + + + + + + + + + + + + + + + Represents a field in a multipart form + + + + Cookies that should be set. + + + + + Adds a cookie in the collection. + + cookie to add + cookie is null + + + + Copy a request cookie + + + When the cookie should expire + + + + Gets a collection enumerator on the cookie list. + + collection enumerator + + + + Remove all cookies + + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Gets the count of cookies in the collection. + + + + + Gets the cookie of a given identifier (null if not existing). + + + + + Helpers to make XML handling easier + + + + + Serializes object to XML. + + object to serialize. + XML + + Removes name spaces and adds indentation + + + + + Create an object from a XML string + + Type of object + XML string + object + + + + Delegate used by to populate select options. + + current object (for instance a User). + Text that should be displayed in the value part of a <optiongt;-tag. + Text shown in the select list. + + // Class that is going to be used in a SELECT-tag. + public class User + { + private readonly string _realName; + private readonly int _id; + public User(int id, string realName) + { + _id = id; + _realName = realName; + } + public string RealName + { + get { return _realName; } + } + + public int Id + { + get { return _id; } + } + } + + // Using an inline delegate to generate the select list + public void UserInlineDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, delegate(object o, out object id, out object value) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + }, 2, true); + } + + // Using an method as delegate to generate the select list. + public void UseExternalDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, UserOptions, 1, true); + } + + // delegate returning id and title + public static void UserOptions(object o, out object id, out object title) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + } /// + + + + Parses a HTTP request directly from a stream + + + + + Create a new request parser + + delegate receiving log entries. + + + + Add a number of bytes to the body + + buffer containing more body bytes. + starting offset in buffer + number of bytes, from offset, to read. + offset to continue from. + + + + Remove all state information for the request. + + + + + Parse request line + + + If line is incorrect + Expects the following format: "Method SP Request-URI SP HTTP-Version CRLF" + + + + We've parsed a new header. + + Name in lower case + Value, unmodified. + If content length cannot be parsed. + + + + Parse a message + + bytes to parse. + where in buffer that parsing should start + number of bytes to parse, starting on . + offset (where to start parsing next). + BadRequestException. + + + + Gets or sets the log writer. + + + + + Current state in parser. + + + + + A request have been successfully parsed. + + + + + More body bytes have been received. + + + + + Request line have been received. + + + + + A header have been received. + + + + + Arguments used when more body bytes have come. + + + + + Initializes a new instance of the class. + + buffer that contains the received bytes. + offset in buffer where to start processing. + number of bytes from that should be parsed. + + + + Initializes a new instance of the class. + + + + + Gets or sets buffer that contains the received bytes. + + + + + Gets or sets number of bytes from that should be parsed. + + + + + Gets or sets offset in buffer where to start processing. + + + + + Contains a connection to a browser/client. + + + + + Disconnect from client + + error to report in the event. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + HTML body contents, can be null or empty. + A content type to return the body as, i.e. 'text/html' or 'text/plain', defaults to 'text/html' if null or empty + If is invalid. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + + + + Send a response. + + + + + + send a whole buffer + + buffer to send + + + + + Send data using the stream + + Contains data to send + Start position in buffer + number of bytes to send + + + + + + Closes the streams and disposes of the unmanaged resources + + + + + Using SSL or other encryption method. + + + + + Using SSL or other encryption method. + + + + + Gets the client's security certificate. + + + + + Gets or sets whether the context should not be reused when disconnected. + + + + + The context have been disconnected. + + + Event can be used to clean up a context, or to reuse it. + + + + + A request have been received in the context. + + + + + A have been disconnected. + + + + + Initializes a new instance of the class. + + Reason to disconnection. + + + + Gets reason to why client disconnected. + + + + + + + + + + Initializes a new instance of the class. + + The request. + + + + Gets received request. + + + + + Client X.509 certificate, X.509 chain, and any SSL policy errors encountered + during the SSL stream creation + + + + + Client security certificate + + + + + Client security certificate chain + + + + + Any SSL policy errors encountered during the SSL stream creation + + + + + Used to inform http server that + + + + + Eventarguments used when an exception is thrown by a module + + the exception + + + + Exception thrown in a module + + + + + Container class for posted files + + + + + Creates a container for a posted file + + The identifier of the post field + The file path + The content type of the file + The name of the file uploaded + If any parameter is null or empty + + + + Creates a container for a posted file + + If any parameter is null or empty + + + Destructor disposing the file + + + + Deletes the temporary file + + True if manual dispose + + + + Disposing interface, cleans up managed resources (the temporary file) and suppresses finalization + + + + + The name/id of the file + + + + + The full file path + + + + + The name of the uploaded file + + + + + The type of file + + + + + Contains a connection to a browser/client. + + + Remember to after you have hooked the event. + + TODO: Maybe this class should be broken up into HttpClientChannel and HttpClientContext? + + + + Initializes a new instance of the class. + + true if the connection is secured (SSL/TLS) + client that connected. + Stream used for communication + Client security certificate + Used to create a . + Size of buffer to use when reading data. Must be at least 4096 bytes. + Client socket + If fails + Stream must be writable and readable. + + + + Process incoming body bytes. + + + Bytes + + + + + + + + + + + Start reading content. + + + Make sure to call base.Start() if you override this method. + + + + + Clean up context. + + + Make sure to call base.Cleanup() if you override the method. + + + + + Disconnect from client + + error to report in the event. + + + BadRequestException. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + HTML body contents, can be null or empty. + A content type to return the body as, i.e. 'text/html' or 'text/plain', defaults to 'text/html' if null or empty + If is invalid. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + + + + Send a response. + + + + + + send a whole buffer + + buffer to send + + + + + Send data using the stream + + Contains data to send + Start position in buffer + number of bytes to send + + + + + + Gets or sets whether the context is available for re use. + + + + + This context have been cleaned, which means that it can be reused. + + + + + Context have been started (a new client have connected) + + + + + Gets or sets if + + + + + Overload to specify own type. + + + Must be specified before the context is being used. + + + + + Using SSL or other encryption method. + + + + + Using SSL or other encryption method. + + + + + Specify which logger to use. + + + + + Gets or sets the network stream. + + + + + Gets the client's security certificate. + + + + + Gets or sets IP address that the client connected from. + + + + + Gets or sets port that the client connected from. + + + + + The context have been disconnected. + + + Event can be used to clean up a context, or to reuse it. + + + + + A request have been received in the context. + + + + + Lists content type mime types. + + + + + text/plain + + + + + text/haml + + + + + content type for javascript documents = application/javascript + + + + RFC 4329 states that text/javascript have been superseeded by + application/javascript. You might still want to check browser versions + since older ones do not support application/javascript. + + Browser support: http://krijnhoetmer.nl/stuff/javascript/mime-types/ + + + + + text/xml + + + + + A list of content types + + + + + + + Semicolon separated content types. + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + + + + Searches for the specified type + + Can also be a part of a type (searching for "xml" would return true for "application/xml"). + true if type was found. + + + + Get this first content type. + + + + + Fetch a content type + + Part of type ("xml" would return "application/xml") + + All content types are in lower case. + + + + The "basic" authentication scheme is based on the model that the + client must authenticate itself with a user-ID and a password for + each realm. The realm value should be considered an opaque string + which can only be compared for equality with other realms on that + server. The server will service the request only if it can validate + the user-ID and password for the protection space of the Request-URI. + There are no optional authentication parameters. + + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + Delegate used to determine if authentication is required (may be null). + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + + + + Create a response that can be sent in the WWW-Authenticate header. + + Realm that the user should authenticate in + Not used in basic auth + A correct auth request. + + + + An authentication response have been received from the web browser. + Check if it's correct + + Contents from the Authorization header + Realm that should be authenticated + GET/POST/PUT/DELETE etc. + Not used in basic auth + Authentication object that is stored for the request. A user class or something like that. + if authenticationHeader is invalid + If any of the paramters is empty or null. + + + + name used in http request. + + + + + Type of HTTP connection + + + + + Connection is closed after each request-response + + + + + Connection is kept alive for X seconds (unless another request have been made) + + + + + Requests message-body be sent with an encoding to be specified in the "Transfer-Encoding" header. + + + + + The purpose of this module is to serve files. + + + + + Initializes a new instance of the class. + + Uri to serve, for instance "/files/" + Path on hard drive where we should start looking for files + If true a Last-Modifed header will be sent upon requests urging web browser to cache files + + + + Initializes a new instance of the class. + + Uri to serve, for instance "/files/" + Path on hard drive where we should start looking for files + + + + Mimtypes that this class can handle per default + + + + + Determines if the request should be handled by this module. + Invoked by the + + + true if this module should handle it. + + + Illegal path + + + + check if source contains any of the chars. + + + + + + + + Method that process the Uri. + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + Failed to find file extension + File type is forbidden. + + + + return a file extension from an absolute Uri path (or plain filename) + + + + + + + List with all mime-type that are allowed. + + All other mime types will result in a Forbidden http status code. + + + + characters that may not exist in a path. + + + fileMod.ForbiddenChars = new string[]{ "\\", "..", ":" }; + + + + + represents a HTTP input item. Each item can have multiple sub items, a sub item + is made in a HTML form by using square brackets + + + // becomes: + Console.WriteLine("Value: {0}", form["user"]["FirstName"].Value); + + + All names in a form SHOULD be in lowercase. + + + + Representation of a non-initialized . + + + + Initializes an input item setting its name/identifier and value + + Parameter name/id + Parameter value + + + Creates a deep copy of the item specified + The item to copy + The function makes a deep copy of quite a lot which can be slow + + + + Add another value to this item + + Value to add. + Cannot add stuff to . + + + + checks if a sub-item exists (and has a value). + + name in lower case + true if the sub-item exists and has a value; otherwise false. + + + Returns a formatted representation of the instance with the values of all contained parameters + + + + Outputs the string in a formatted manner + + A prefix to append, used internally + produce a query string + + + + Add a sub item. + + Can contain array formatting, the item is then parsed and added in multiple levels + Value to add. + Argument is null. + Cannot add stuff to . + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + + An object that can be used to iterate through the collection. + + 2 + + + + Outputs the string in a formatted manner + + A prefix to append, used internally + + + + + Number of values + + + + + Get a sub item + + name in lower case. + if no item was found. + + + + Name of item (in lower case). + + + + + Returns the first value, or null if no value exist. + + + + + Returns the last value, or null if no value exist. + + + + + Returns the list with values. + + + + + + + name in lower case + + + + + The object form class takes an object and creates form items for it. + + + + + Initializes a new instance of the class. + + + form name *and* id. + action to do when form is posted. + + + + + Initializes a new instance of the class. + + form name *and* id. + action to do when form is posted. + object to get values from + + + + Initializes a new instance of the class. + + form action. + object to get values from. + + + + write out the FORM-tag. + + generated html code + + + + Writeout the form tag + + form should be posted through ajax. + generated html code + + + + Generates a text box. + + + + generated html code + + + + password box + + + + generated html code + + + + Hiddens the specified property name. + + Name of the property. + The options. + generated html code + + + + Labels the specified property name. + + property in object. + caption + generated html code + + + + Generate a checkbox + + property in object + checkbox value + additional html attributes. + generated html code + + + + Write a html select tag + + object property. + id column + The title column. + The options. + + + + + Selects the specified property name. + + Name of the property. + The items. + The id column. + The title column. + The options. + + + + + Write a submit tag. + + button caption + html submit tag + + + + html end form tag + + html + + + + The server understood the request, but is refusing to fulfill it. + Authorization will not help and the request SHOULD NOT be repeated. + If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, + it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information + available to the client, the status code 404 (Not Found) can be used instead. + + Text taken from: http://www.submissionchamber.com/help-guides/error-codes.php + + + + + Initializes a new instance of the class. + + error message + + + + Used when the request line have been successfully parsed. + + + + + Initializes a new instance of the class. + + The HTTP method. + The URI path. + The HTTP version. + + + + Initializes a new instance of the class. + + + + + Gets or sets http method. + + + Should be one of the methods declared in . + + + + + Gets or sets the version of the HTTP protocol that the client want to use. + + + + + Gets or sets requested URI path. + + + + + Inversion of control interface. + + + + + Add a component instance + + Interface type + Instance to add + + + + Get a component. + + Interface type + Component if registered, otherwise null. + + Component will get created if needed. + + + + + Checks if the specified component interface have been added. + + + true if found; otherwise false. + + + + Add a component. + + Type being requested. + Type being created. + + + + Implementation of the SCGI protocol. + + + The implementation reads configuration from an INI file called "scgi.ini", it should + be placed in the same folder as the server. The file should contain file mappings to + socket severs: + + [main] + php: localhost:4082 + py: 192.168.1.20:8037 + + + + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + true if this module handled the request. + + + + Start module, will try to read the config file. + + + + + + + + http://www.faqs.org/rfcs/rfc1867.html + + + + + multipart/form-data + + + + + form-data + + + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream enconding + A http form, or null if content could not be parsed. + If contents in the stream is not valid input data. + If any parameter is null + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + This provider is used to let us implement any type of form decoding we want without + having to rewrite anything else in the server. + + + + + + + Should contain boundary and type, as in: multipart/form-data; boundary=---------------------------230051238959 + Stream containing form data. + Encoding used when decoding the stream + if no parser was found. + If stream is null or not readable. + If stream contents cannot be decoded properly. + + + + Add a decoder. + + + + + + + Number of added decoders. + + + + + Use with care. + + + + + Decoder used for unknown content types. + + + + + Add a component instance + + Interface type + Instance to add + + + + Get a component. + + Interface type + Component if registered, otherwise null. + + Component will get created if needed. + + + + If instance cannot be created. + + + + Checks if the specified component interface have been added. + + + true if found; otherwise false. + + + + Add a component. + + Type being requested. + Type being created. + Type have already been mapped. + + + + Invoked when a client have been accepted by the + + + Can be used to revoke incoming connections + + + + + Initializes a new instance of the class. + + The socket. + + + + Client may not be handled. + + + + + Accepted socket. + + + + + Client should be revoked. + + + + diff --git a/thirdparty/HttpServer/license.txt b/thirdparty/HttpServer/license.txt new file mode 100644 index 000000000..3668016fc --- /dev/null +++ b/thirdparty/HttpServer/license.txt @@ -0,0 +1,69 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of this License; and + +2. You must cause any modified files to carry prominent notices stating that You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/thirdparty/HttpServer/xunit.dll b/thirdparty/HttpServer/xunit.dll new file mode 100644 index 000000000..c02db484c Binary files /dev/null and b/thirdparty/HttpServer/xunit.dll differ diff --git a/thirdparty/Json.NET/Homepage.txt b/thirdparty/Json.NET/Homepage.txt new file mode 100644 index 000000000..b066e0b1c --- /dev/null +++ b/thirdparty/Json.NET/Homepage.txt @@ -0,0 +1 @@ +http://json.codeplex.com/ \ No newline at end of file diff --git a/thirdparty/Json.NET/License.txt b/thirdparty/Json.NET/License.txt new file mode 100644 index 000000000..8f532d54b --- /dev/null +++ b/thirdparty/Json.NET/License.txt @@ -0,0 +1,7 @@ +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/thirdparty/Json.NET/Newtonsoft.Json.dll b/thirdparty/Json.NET/Newtonsoft.Json.dll new file mode 100755 index 000000000..4bfe4fffd Binary files /dev/null and b/thirdparty/Json.NET/Newtonsoft.Json.dll differ diff --git a/thirdparty/LitJSON/LitJson.dll b/thirdparty/LitJSON/LitJson.dll index d83411a7a..84e53a2be 100644 Binary files a/thirdparty/LitJSON/LitJson.dll and b/thirdparty/LitJSON/LitJson.dll differ diff --git a/thirdparty/LitJSON/Upgrade-readme.txt b/thirdparty/LitJSON/Upgrade-readme.txt new file mode 100644 index 000000000..9718560d4 --- /dev/null +++ b/thirdparty/LitJSON/Upgrade-readme.txt @@ -0,0 +1,4 @@ +The file LitJson has been signed with Signer.exe to give it a strong name. +If you want to upgrade LitJson to a new version, please run: + +Signer.exe -k Duplicati.snk -a LitJson.dll -outdir out \ No newline at end of file diff --git a/thirdparty/MonoMac/MonoMac.dll b/thirdparty/MonoMac/MonoMac.dll new file mode 100755 index 000000000..9bc3d8906 Binary files /dev/null and b/thirdparty/MonoMac/MonoMac.dll differ diff --git a/thirdparty/MonoMac/download.txt b/thirdparty/MonoMac/download.txt new file mode 100644 index 000000000..aefbe60b6 --- /dev/null +++ b/thirdparty/MonoMac/download.txt @@ -0,0 +1 @@ +http://www.monodevelop.com/ \ No newline at end of file diff --git a/thirdparty/MonoMac/license.txt b/thirdparty/MonoMac/license.txt new file mode 100644 index 000000000..c69cf76be --- /dev/null +++ b/thirdparty/MonoMac/license.txt @@ -0,0 +1,217 @@ +Dual licensed under either the Apache 2 license or the MIT X11 license + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the +copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other +entities that control, are controlled by, or are under common control +with that entity. For the purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or +management of such entity, whether by contract or otherwise, or (ii) +ownership of fifty percent (50%) or more of the outstanding shares, or +(iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but not +limited to compiled object code, generated documentation, and +conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object +form, made available under the License, as indicated by a copyright +notice that is included in or attached to the work (an example is +provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the +purposes of this License, Derivative Works shall not include works +that remain separable from, or merely link (or bind by name) to the +interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the +original version of the Work and any modifications or additions to +that Work or Derivative Works thereof, that is intentionally submitted +to Licensor for inclusion in the Work by the copyright owner or by an +individual or Legal Entity authorized to submit on behalf of the +copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent to +the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control +systems, and issue tracking systems that are managed by, or on behalf +of, the Licensor for the purpose of discussing and improving the Work, +but excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor +hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, +and distribute the Work and such Derivative Works in Source or Object +form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor +hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and +otherwise transfer the Work, where such license applies only to those +patent claims licensable by such Contributor that are necessarily +infringed by their Contribution(s) alone or by combination of their +Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that +the Work or a Contribution incorporated within the Work constitutes +direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of +the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative +Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following +conditions: + +You must give any other recipients of the Work or Derivative Works a +copy of this License; and You must cause any modified files to carry +prominent notices stating that You changed the files; and You must +retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices +from the Source form of the Work, excluding those notices that do not +pertain to any part of the Derivative Works; and If the Work includes +a "NOTICE" text file as part of its distribution, then any Derivative +Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at +least one of the following places: within a NOTICE text file +distributed as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, within +a display generated by the Derivative Works, if and wherever such +third-party notices normally appear. The contents of the NOTICE file +are for informational purposes only and do not modify the License. You +may add Your own attribution notices within Derivative Works that You +distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be +construed as modifying the License. You may add Your own copyright +statement to Your modifications and may provide additional or +different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works +as a whole, provided Your use, reproduction, and distribution of the +Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally +submitted for inclusion in the Work by You to the Licensor shall be +under the terms and conditions of this License, without any additional +terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as +required for reasonable and customary use in describing the origin of +the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor +provides the Work (and each Contributor provides its Contributions) on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied, including, without limitation, any warranties or +conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including +negligence), contract, or otherwise, unless required by applicable law +(such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of +any character arising as a result of this License or out of the use or +inability to use the Work (including but not limited to damages for +loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such +Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" replaced +with your own identifying information. (Don't include the brackets!) +The text should be enclosed in the appropriate comment syntax for the +file format. We also recommend that a file or class name and +description of purpose be included on the same "printed page" as the +copyright notice for easier identification within third-party +archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/thirdparty/SSH.NET/Homepage.txt b/thirdparty/SSH.NET/Homepage.txt new file mode 100644 index 000000000..dbd753874 --- /dev/null +++ b/thirdparty/SSH.NET/Homepage.txt @@ -0,0 +1 @@ +http://sshnet.codeplex.com \ No newline at end of file diff --git a/thirdparty/SSH.NET/License.txt b/thirdparty/SSH.NET/License.txt new file mode 100644 index 000000000..a8822f145 --- /dev/null +++ b/thirdparty/SSH.NET/License.txt @@ -0,0 +1,13 @@ +License: New BSD License (BSD) +Copyright (c) 2010, RENCI +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of RENCI nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/SSH.NET/Renci.SshNet.dll b/thirdparty/SSH.NET/Renci.SshNet.dll new file mode 100644 index 000000000..1e767f3ce Binary files /dev/null and b/thirdparty/SSH.NET/Renci.SshNet.dll differ diff --git a/thirdparty/SSH.NET/Upgrade-readme.txt b/thirdparty/SSH.NET/Upgrade-readme.txt new file mode 100644 index 000000000..2b86aaa8f --- /dev/null +++ b/thirdparty/SSH.NET/Upgrade-readme.txt @@ -0,0 +1,4 @@ +The file Renci.SshNet has been signed with Signer.exe to give it a strong name. +If you want to upgrade Renci.SshNet to a new version, please run: + +Signer.exe -k Duplicati.snk -a Renci.SshNet -outdir out \ No newline at end of file diff --git a/thirdparty/SharpSSH/DiffieHellman.dll b/thirdparty/SharpSSH/DiffieHellman.dll deleted file mode 100644 index aa3105110..000000000 Binary files a/thirdparty/SharpSSH/DiffieHellman.dll and /dev/null differ diff --git a/thirdparty/SharpSSH/Org.Mentalis.Security.dll b/thirdparty/SharpSSH/Org.Mentalis.Security.dll deleted file mode 100644 index bac39dd9c..000000000 Binary files a/thirdparty/SharpSSH/Org.Mentalis.Security.dll and /dev/null differ diff --git a/thirdparty/SharpSSH/Tamir.SharpSSH.dll b/thirdparty/SharpSSH/Tamir.SharpSSH.dll deleted file mode 100644 index c2f67a826..000000000 Binary files a/thirdparty/SharpSSH/Tamir.SharpSSH.dll and /dev/null differ diff --git a/thirdparty/SharpSSH/download.txt b/thirdparty/SharpSSH/download.txt deleted file mode 100644 index f8caa3674..000000000 --- a/thirdparty/SharpSSH/download.txt +++ /dev/null @@ -1 +0,0 @@ -http://sourceforge.net/projects/sharpssh/ \ No newline at end of file diff --git a/thirdparty/SharpSSH/license.txt b/thirdparty/SharpSSH/license.txt deleted file mode 100644 index 6aea36766..000000000 --- a/thirdparty/SharpSSH/license.txt +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2006 Tamir Gal, http://www.tamirgal.com, All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR - *OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/thirdparty/Signer/Signer.exe b/thirdparty/Signer/Signer.exe new file mode 100755 index 000000000..eae02bc39 Binary files /dev/null and b/thirdparty/Signer/Signer.exe differ diff --git a/thirdparty/Signer/download.txt b/thirdparty/Signer/download.txt new file mode 100755 index 000000000..a2b9854da --- /dev/null +++ b/thirdparty/Signer/download.txt @@ -0,0 +1 @@ +http://signer.codeplex.com/ \ No newline at end of file diff --git a/thirdparty/Signer/license.txt b/thirdparty/Signer/license.txt new file mode 100755 index 000000000..5f2d130b1 --- /dev/null +++ b/thirdparty/Signer/license.txt @@ -0,0 +1,458 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] + +Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + +Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + +When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + +We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + +Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +GNU LESSER GENERAL PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + +You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + +2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices +stating that you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no +charge to all third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a +table of data to be supplied by an application program that uses +the facility, other than as an argument passed when the facility +is invoked, then you must make a good faith effort to ensure that, +in the event an application does not supply such function or +table, the facility still operates, and performs whatever part of +its purpose remains meaningful. + +(For example, a function in a library to compute square roots has +a purpose that is entirely well-defined independent of the +application. Therefore, Subsection 2d requires that any +application-supplied function or table used by this function must +be optional: if the application does not supply it, the square +root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + +Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +a) Accompany the work with the complete corresponding +machine-readable source code for the Library including whatever +changes were used in the work (which must be distributed under +Sections 1 and 2 above); and, if the work is an executable linked +with the Library, with the complete machine-readable "work that +uses the Library", as object code and/or source code, so that the +user can modify the Library and then relink to produce a modified +executable containing the modified Library. (It is understood +that the user who changes the contents of definitions files in the +Library will not necessarily be able to recompile the application +to use the modified definitions.) + +b) Use a suitable shared library mechanism for linking with the +Library. A suitable mechanism is one that (1) uses at run time a +copy of the library already present on the user's computer system, +rather than copying library functions into the executable, and (2) +will operate properly with a modified version of the library, if +the user installs one, as long as the modified version is +interface-compatible with the version that the work was made with. + +c) Accompany the work with a written offer, valid for at +least three years, to give the same user the materials +specified in Subsection 6a, above, for a charge no more +than the cost of performing this distribution. + +d) If distribution of the work is made by offering access to copy +from a designated place, offer equivalent access to copy the above +specified materials from the same place. + +e) Verify that the user has already received a copy of these +materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work +based on the Library, uncombined with any other library +facilities. This must be distributed under the terms of the +Sections above. + +b) Give prominent notice with the combined library of the fact +that part of it is a work based on the Library, and explaining +where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS \ No newline at end of file