From 6b7987fdd671384d9f4e3c89301a5f17fcd8ee6b Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sat, 8 Aug 2020 12:41:23 -0700 Subject: [PATCH 01/32] Remove __MonoCS__ constant from travis build. This is no longer needed (see pull request #4213). --- pipeline/stage_build/job.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pipeline/stage_build/job.sh b/pipeline/stage_build/job.sh index 70dd17549..997efca8d 100755 --- a/pipeline/stage_build/job.sh +++ b/pipeline/stage_build/job.sh @@ -22,7 +22,7 @@ function build () { msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}/BuildTools/GnupgSigningTool/GnupgSigningTool.sln" # build duplicati - msbuild -p:DefineConstants=__MonoCS__ -p:DefineConstants=ENABLE_GTK /p:Configuration=Release "${DUPLICATI_ROOT}/Duplicati.sln" + msbuild -p:DefineConstants=ENABLE_GTK /p:Configuration=Release "${DUPLICATI_ROOT}/Duplicati.sln" msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}"/Duplicati.sln cp -r "${DUPLICATI_ROOT}"/Duplicati/Server/webroot "${DUPLICATI_ROOT}"/Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/webroot @@ -30,4 +30,4 @@ function build () { travis_mark_begin "BUILDING BINARIES" build -travis_mark_end "BUILDING BINARIES" \ No newline at end of file +travis_mark_end "BUILDING BINARIES" From 26482a426b5ebb54417c03694c6e771b26de2a17 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sat, 8 Aug 2020 12:43:07 -0700 Subject: [PATCH 02/32] Fix indentation in Travis build script. --- pipeline/stage_build/job.sh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/pipeline/stage_build/job.sh b/pipeline/stage_build/job.sh index 997efca8d..6799d5d7f 100755 --- a/pipeline/stage_build/job.sh +++ b/pipeline/stage_build/job.sh @@ -3,29 +3,29 @@ . /pipeline/shared/duplicati.sh function build () { - nuget restore "${DUPLICATI_ROOT}"/Duplicati.sln + nuget restore "${DUPLICATI_ROOT}"/Duplicati.sln - if [ ! -d "${DUPLICATI_ROOT}"/packages/SharpCompress.0.23.0 ]; then - ln -s "${DUPLICATI_ROOT}"/packages/sharpcompress.0.23.0 "${DUPLICATI_ROOT}"/packages/SharpCompress.0.23.0 - fi + if [ ! -d "${DUPLICATI_ROOT}"/packages/SharpCompress.0.23.0 ]; then + ln -s "${DUPLICATI_ROOT}"/packages/sharpcompress.0.23.0 "${DUPLICATI_ROOT}"/packages/SharpCompress.0.23.0 + fi - # build version stamper - msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}/BuildTools/UpdateVersionStamp/UpdateVersionStamp.csproj" - mono "${DUPLICATI_ROOT}/BuildTools/UpdateVersionStamp/bin/Release/UpdateVersionStamp.exe" --version="${releaseversion}" + # build version stamper + msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}/BuildTools/UpdateVersionStamp/UpdateVersionStamp.csproj" + mono "${DUPLICATI_ROOT}/BuildTools/UpdateVersionStamp/bin/Release/UpdateVersionStamp.exe" --version="${releaseversion}" - # build autoupdate - nuget restore "${DUPLICATI_ROOT}/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln" - msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln" + # build autoupdate + nuget restore "${DUPLICATI_ROOT}/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln" + msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln" - # build gpg signing tool - nuget restore "${DUPLICATI_ROOT}/BuildTools/GnupgSigningTool/GnupgSigningTool.sln" - msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}/BuildTools/GnupgSigningTool/GnupgSigningTool.sln" + # build gpg signing tool + nuget restore "${DUPLICATI_ROOT}/BuildTools/GnupgSigningTool/GnupgSigningTool.sln" + msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}/BuildTools/GnupgSigningTool/GnupgSigningTool.sln" - # build duplicati - msbuild -p:DefineConstants=ENABLE_GTK /p:Configuration=Release "${DUPLICATI_ROOT}/Duplicati.sln" + # build duplicati + msbuild -p:DefineConstants=ENABLE_GTK /p:Configuration=Release "${DUPLICATI_ROOT}/Duplicati.sln" - msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}"/Duplicati.sln - cp -r "${DUPLICATI_ROOT}"/Duplicati/Server/webroot "${DUPLICATI_ROOT}"/Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/webroot + msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}"/Duplicati.sln + cp -r "${DUPLICATI_ROOT}"/Duplicati/Server/webroot "${DUPLICATI_ROOT}"/Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/webroot } travis_mark_begin "BUILDING BINARIES" From 9392e90b5b69bbc4953403cf69d0f1972495942e Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sat, 8 Aug 2020 12:43:30 -0700 Subject: [PATCH 03/32] Remove redundant build in Travis build script. --- pipeline/stage_build/job.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/pipeline/stage_build/job.sh b/pipeline/stage_build/job.sh index 6799d5d7f..b61f3dd3a 100755 --- a/pipeline/stage_build/job.sh +++ b/pipeline/stage_build/job.sh @@ -24,7 +24,6 @@ function build () { # build duplicati msbuild -p:DefineConstants=ENABLE_GTK /p:Configuration=Release "${DUPLICATI_ROOT}/Duplicati.sln" - msbuild -p:Configuration=Release -v:minimal "${DUPLICATI_ROOT}"/Duplicati.sln cp -r "${DUPLICATI_ROOT}"/Duplicati/Server/webroot "${DUPLICATI_ROOT}"/Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/webroot } From 201bb53b297d9d18416a038ec3774ad974eac543 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sat, 22 Aug 2020 15:24:25 -0700 Subject: [PATCH 04/32] Add mono-complete to Docker image. After targeting .NET 4.7.1, some users started encountering errors like: Could not find method '.ctor' due to a type load error: Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. assembly:/opt/duplicati/SharpAESCrypt.exe type:SharpAESCrypt member:(null) The TencentCOS and Tardigrade backends are built on .NET Standard 2.0, and it appears that the dependencies can be awoken even if these backends are not used. This fixes #4289. --- Installer/Docker/context/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Installer/Docker/context/Dockerfile b/Installer/Docker/context/Dockerfile index c93e0d54a..ffcd6e3cc 100644 --- a/Installer/Docker/context/Dockerfile +++ b/Installer/Docker/context/Dockerfile @@ -3,6 +3,7 @@ FROM --platform=$TARGETPLATFORM mono:5-slim RUN apt-get update && \ apt-get install -y --no-install-recommends \ curl \ + mono-complete \ libmono-sqlite4.0-cil \ libmono-system-drawing4.0-cil \ libmono-system-net-http-webrequest4.0-cil \ From 8ae5b81e164b4b88d1d3c2111efdd19a1c7b7cfe Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sat, 22 Aug 2020 16:31:37 -0700 Subject: [PATCH 05/32] Remove script used for previous Travis configuration. As of pull request #4005, our Travis builds no longer use this script. --- build.sh | 53 ----------------------------------------------------- 1 file changed, 53 deletions(-) delete mode 100755 build.sh diff --git a/build.sh b/build.sh deleted file mode 100755 index 12008517c..000000000 --- a/build.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -quit_on_error() { - echo "Error on line $1, stopping build." - exit 1 -} - -list_dir() { - echo "listing directory $1 :" - ls -al $1 -} - -trap 'quit_on_error $LINENO' ERR - -CATEGORY=$1 -TRAVIS_BUILD_DIR=${2:-.} - -if id travis &> /dev/null -then - TESTUSER=travis -else - TESTUSER=$(whoami) -fi - -echo "Build script starting with parameters TRAVIS_BUILD_DIR=$TRAVIS_BUILD_DIR and CATEGORY=$CATEGORY" - -# build duplicati - -list_dir "${TRAVIS_BUILD_DIR}"/packages/ -echo "travis_fold:start:build_duplicati" -msbuild /p:Configuration=Release Duplicati.sln -cp -r ./Duplicati/Server/webroot ./Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/webroot -echo "travis_fold:end:build_duplicati" - -rm -rf ~/duplicati_testdata && mkdir ~/duplicati_testdata -chown -R $TESTUSER ~/duplicati_testdata/ -chmod -R 755 ~/duplicati_testdata - -# run unit tests -echo "travis_fold:start:unit_test" -if [[ "$CATEGORY" != "GUI" && "$CATEGORY" != "" ]]; then - mono ./testrunner/NUnit.ConsoleRunner.3.10.0/tools/nunit3-console.exe \ - ./Duplicati/UnitTest/bin/Release/Duplicati.UnitTest.dll --where:cat==$CATEGORY --workers=1 -fi -echo "travis_fold:end:unit_test" - -# start server and run gui tests -echo "travis_fold:start:gui_unit_test" -if [[ "$CATEGORY" == "GUI" ]]; then - mono ./Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/Duplicati.Server.exe & - python guiTests/guiTest.py -fi -echo "travis_fold:end:gui_unit_test" From d4d7874aaf873075e73939a824d4a5559030eda3 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sun, 23 Aug 2020 13:58:34 -0700 Subject: [PATCH 06/32] Avoid decoding URL parameters when removing passwords. In revision c41f2c6a60 ("moved all sanitization logic into backup class and moved from regex to using internal Uri library"), we cleaned up the removal of passwords from an exported backup configuration. However, the use of Uri.QueryParameters resulted in exporting decoded parameter values, which violated some assumptions made by the decode_uri function in AppUtils.js. This caused usernames in the JSON to contain '@' instead of '%40', which led to incorrect decomposition of the target URL into its components in the UI. This concerns issue #3619. --- Duplicati/Library/Utility/Uri.cs | 13 +++++++++++-- Duplicati/Server/Database/Backup.cs | 17 +++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Duplicati/Library/Utility/Uri.cs b/Duplicati/Library/Utility/Uri.cs index 6ba4e75ee..0cb746cf3 100644 --- a/Duplicati/Library/Utility/Uri.cs +++ b/Duplicati/Library/Utility/Uri.cs @@ -444,7 +444,8 @@ namespace Duplicati.Library.Utility /// /// The parsed query string /// The query to parse - public static NameValueCollection ParseQueryString(string query) + /// Whether to the parameter values should be decoded or not. + public static NameValueCollection ParseQueryString(string query, bool decodeValues = true) { if (query == null) throw new ArgumentNullException(nameof(query)); @@ -455,7 +456,15 @@ namespace Duplicati.Library.Utility var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase); foreach (System.Text.RegularExpressions.Match m in RE_URLPARAM.Matches(query)) - result.Add(UrlDecode(m.Groups["key"].Value), UrlDecode(m.Groups["value"].Success ? m.Groups["value"].Value : "")); + { + string value = m.Groups["value"].Success ? m.Groups["value"].Value : ""; + if (decodeValues) + { + value = UrlDecode(value); + } + + result.Add(UrlDecode(m.Groups["key"].Value), value); + } return result; } diff --git a/Duplicati/Server/Database/Backup.cs b/Duplicati/Server/Database/Backup.cs index 962fb1081..117253942 100644 --- a/Duplicati/Server/Database/Backup.cs +++ b/Duplicati/Server/Database/Backup.cs @@ -18,6 +18,7 @@ using System; using Duplicati.Server.Serialization.Interface; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; namespace Duplicati.Server.Database @@ -121,11 +122,19 @@ namespace Duplicati.Server.Database public void SanitizeTargetUrl() { var url = new Duplicati.Library.Utility.Uri(this.TargetURL); - var filteredParameters = url.QueryParameters; - foreach (string field in UrlPasswords) { - filteredParameters.Remove(field); + NameValueCollection filteredParameters = new NameValueCollection(); + if (url.Query != null) + { + // We cannot use url.QueryParameters since it contains decoded parameter values, which + // breaks assumptions made by the decode_uri function in AppUtils.js. Since we are simply + // removing password parameters, we will leave the parameters as they are in the target URL. + filteredParameters = Library.Utility.Uri.ParseQueryString(url.Query, false); + foreach (string field in this.UrlPasswords) + { + filteredParameters.Remove(field); + } } - url = url.SetQuery(Duplicati.Library.Utility.Uri.BuildUriQuery(url.QueryParameters)); + url = url.SetQuery(Duplicati.Library.Utility.Uri.BuildUriQuery(filteredParameters)); this.TargetURL = url.ToString(); } From acba6351dc1fad105aeaa908e596327558bfdeb5 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sun, 23 Aug 2020 14:24:27 -0700 Subject: [PATCH 07/32] Extract local function to method. --- Duplicati/UnitTest/ImportExportTests.cs | 42 ++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Duplicati/UnitTest/ImportExportTests.cs b/Duplicati/UnitTest/ImportExportTests.cs index 5d92f2e16..9406cc43a 100644 --- a/Duplicati/UnitTest/ImportExportTests.cs +++ b/Duplicati/UnitTest/ImportExportTests.cs @@ -16,7 +16,7 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using Duplicati.Server.Database; using Duplicati.Server.Serialization.Interface; -using NUnit.Framework; +using NUnit.Framework; using System; using System.Collections.Generic; using System.IO; @@ -47,40 +47,40 @@ namespace Duplicati.UnitTest } } + IBackup CreateBackup(string name, string username, string password, Dictionary metadata) + { + return new Backup + { + Description = "Mock Backup Description", + Filters = new IFilter[0], + ID = "1", + Metadata = metadata, + Name = name, + Settings = new[] { new Setting { Name = "passphrase", Value = "12345" } }, + Sources = new[] { "Mock Backup Source" }, + Tags = new[] { "Tags" }, + TargetURL = $"file:///mock_backup_target?auth-username={username}&auth-password={password}" + }; + } + [Test] [Category("ImportExport")] public void RoundTrip() { Dictionary metadata = new Dictionary { { "SourceFilesCount", "1" } }; - IBackup CreateBackup(string name) - { - return new Backup - { - Description = "Mock Backup Description", - Filters = new IFilter[0], - ID = "1", - Metadata = metadata, - Name = name, - Settings = new[] { new Setting { Name = "passphrase", Value = "12345" } }, - Sources = new[] { "Mock Backup Source" }, - Tags = new[] { "Tags" }, - TargetURL = "file:///mock_backup_target" - }; - } - Dictionary advancedOptions = new Dictionary { { "server-datafolder", this.serverDatafolder } }; using (Duplicati.Server.Program.DataConnection = Duplicati.Server.Program.GetDatabaseConnection(advancedOptions)) { // Unencrypted file, don't import metadata. string unencryptedWithoutMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); - File.WriteAllBytes(unencryptedWithoutMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(CreateBackup("unencrypted without metadata"), null)); + File.WriteAllBytes(unencryptedWithoutMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("unencrypted without metadata", "user", "password", metadata), null)); Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(unencryptedWithoutMetadata, false, () => null, advancedOptions); Assert.AreEqual(1, Duplicati.Server.Program.DataConnection.Backups.Length); Assert.AreEqual(0, Duplicati.Server.Program.DataConnection.Backups[0].Metadata.Count); // Unencrypted file, import metadata. string unencryptedWithMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); - File.WriteAllBytes(unencryptedWithMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(CreateBackup("unencrypted with metadata"), null)); + File.WriteAllBytes(unencryptedWithMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("unencrypted with metadata", "user", "password", metadata), null)); Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(unencryptedWithMetadata, true, () => null, advancedOptions); Assert.AreEqual(2, Duplicati.Server.Program.DataConnection.Backups.Length); Assert.AreEqual(metadata.Count, Duplicati.Server.Program.DataConnection.Backups[1].Metadata.Count); @@ -88,14 +88,14 @@ namespace Duplicati.UnitTest // Encrypted file, don't import metadata. string encryptedWithoutMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); string passphrase = "abcde"; - File.WriteAllBytes(encryptedWithoutMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(CreateBackup("encrypted without metadata"), passphrase)); + File.WriteAllBytes(encryptedWithoutMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("encrypted without metadata", "user", "password", metadata), passphrase)); Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(encryptedWithoutMetadata, false, () => passphrase, advancedOptions); Assert.AreEqual(3, Duplicati.Server.Program.DataConnection.Backups.Length); Assert.AreEqual(0, Duplicati.Server.Program.DataConnection.Backups[2].Metadata.Count); // Encrypted file, import metadata. string encryptedWithMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); - File.WriteAllBytes(encryptedWithMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(CreateBackup("encrypted with metadata"), passphrase)); + File.WriteAllBytes(encryptedWithMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("encrypted with metadata", "user", "password", metadata), passphrase)); Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(encryptedWithMetadata, true, () => passphrase, advancedOptions); Assert.AreEqual(4, Duplicati.Server.Program.DataConnection.Backups.Length); Assert.AreEqual(metadata.Count, Duplicati.Server.Program.DataConnection.Backups[3].Metadata.Count); From 9dd98457f1a335ff4b44ba2d31730d7625a7e67f Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sun, 23 Aug 2020 14:25:34 -0700 Subject: [PATCH 08/32] Clean up code. --- Duplicati/UnitTest/ImportExportTests.cs | 52 +++++++++++++------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/Duplicati/UnitTest/ImportExportTests.cs b/Duplicati/UnitTest/ImportExportTests.cs index 9406cc43a..01d6f7feb 100644 --- a/Duplicati/UnitTest/ImportExportTests.cs +++ b/Duplicati/UnitTest/ImportExportTests.cs @@ -14,12 +14,16 @@ // 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 Duplicati.Server.Database; -using Duplicati.Server.Serialization.Interface; -using NUnit.Framework; + using System; using System.Collections.Generic; using System.IO; +using Duplicati.Server; +using Duplicati.Server.Database; +using Duplicati.Server.Serialization.Interface; +using Duplicati.Server.WebServer.RESTMethods; +using NUnit.Framework; +using Backup = Duplicati.Server.Database.Backup; namespace Duplicati.UnitTest { @@ -47,7 +51,7 @@ namespace Duplicati.UnitTest } } - IBackup CreateBackup(string name, string username, string password, Dictionary metadata) + private IBackup CreateBackup(string name, string username, string password, Dictionary metadata) { return new Backup { @@ -56,9 +60,9 @@ namespace Duplicati.UnitTest ID = "1", Metadata = metadata, Name = name, - Settings = new[] { new Setting { Name = "passphrase", Value = "12345" } }, - Sources = new[] { "Mock Backup Source" }, - Tags = new[] { "Tags" }, + Settings = new[] {new Setting {Name = "passphrase", Value = "12345"}}, + Sources = new[] {"Mock Backup Source"}, + Tags = new[] {"Tags"}, TargetURL = $"file:///mock_backup_target?auth-username={username}&auth-password={password}" }; } @@ -67,42 +71,42 @@ namespace Duplicati.UnitTest [Category("ImportExport")] public void RoundTrip() { - Dictionary metadata = new Dictionary { { "SourceFilesCount", "1" } }; - Dictionary advancedOptions = new Dictionary { { "server-datafolder", this.serverDatafolder } }; - using (Duplicati.Server.Program.DataConnection = Duplicati.Server.Program.GetDatabaseConnection(advancedOptions)) + Dictionary metadata = new Dictionary {{"SourceFilesCount", "1"}}; + Dictionary advancedOptions = new Dictionary {{"server-datafolder", this.serverDatafolder}}; + using (Program.DataConnection = Program.GetDatabaseConnection(advancedOptions)) { // Unencrypted file, don't import metadata. string unencryptedWithoutMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); File.WriteAllBytes(unencryptedWithoutMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("unencrypted without metadata", "user", "password", metadata), null)); - Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(unencryptedWithoutMetadata, false, () => null, advancedOptions); - Assert.AreEqual(1, Duplicati.Server.Program.DataConnection.Backups.Length); - Assert.AreEqual(0, Duplicati.Server.Program.DataConnection.Backups[0].Metadata.Count); + Backups.ImportBackup(unencryptedWithoutMetadata, false, () => null, advancedOptions); + Assert.AreEqual(1, Program.DataConnection.Backups.Length); + Assert.AreEqual(0, Program.DataConnection.Backups[0].Metadata.Count); // Unencrypted file, import metadata. string unencryptedWithMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); File.WriteAllBytes(unencryptedWithMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("unencrypted with metadata", "user", "password", metadata), null)); - Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(unencryptedWithMetadata, true, () => null, advancedOptions); - Assert.AreEqual(2, Duplicati.Server.Program.DataConnection.Backups.Length); - Assert.AreEqual(metadata.Count, Duplicati.Server.Program.DataConnection.Backups[1].Metadata.Count); + Backups.ImportBackup(unencryptedWithMetadata, true, () => null, advancedOptions); + Assert.AreEqual(2, Program.DataConnection.Backups.Length); + Assert.AreEqual(metadata.Count, Program.DataConnection.Backups[1].Metadata.Count); // Encrypted file, don't import metadata. string encryptedWithoutMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); string passphrase = "abcde"; File.WriteAllBytes(encryptedWithoutMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("encrypted without metadata", "user", "password", metadata), passphrase)); - Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(encryptedWithoutMetadata, false, () => passphrase, advancedOptions); - Assert.AreEqual(3, Duplicati.Server.Program.DataConnection.Backups.Length); - Assert.AreEqual(0, Duplicati.Server.Program.DataConnection.Backups[2].Metadata.Count); + Backups.ImportBackup(encryptedWithoutMetadata, false, () => passphrase, advancedOptions); + Assert.AreEqual(3, Program.DataConnection.Backups.Length); + Assert.AreEqual(0, Program.DataConnection.Backups[2].Metadata.Count); // Encrypted file, import metadata. string encryptedWithMetadata = Path.Combine(this.serverDatafolder, Path.GetRandomFileName()); File.WriteAllBytes(encryptedWithMetadata, Server.WebServer.RESTMethods.Backup.ExportToJSON(this.CreateBackup("encrypted with metadata", "user", "password", metadata), passphrase)); - Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(encryptedWithMetadata, true, () => passphrase, advancedOptions); - Assert.AreEqual(4, Duplicati.Server.Program.DataConnection.Backups.Length); - Assert.AreEqual(metadata.Count, Duplicati.Server.Program.DataConnection.Backups[3].Metadata.Count); + Backups.ImportBackup(encryptedWithMetadata, true, () => passphrase, advancedOptions); + Assert.AreEqual(4, Program.DataConnection.Backups.Length); + Assert.AreEqual(metadata.Count, Program.DataConnection.Backups[3].Metadata.Count); // Encrypted file, incorrect passphrase. - Assert.Throws(Is.InstanceOf(), () => Duplicati.Server.WebServer.RESTMethods.Backups.ImportBackup(encryptedWithMetadata, true, () => passphrase + " ", advancedOptions)); + Assert.Throws(Is.InstanceOf(), () => Backups.ImportBackup(encryptedWithMetadata, true, () => passphrase + " ", advancedOptions)); } } } -} +} \ No newline at end of file From e59e9a42b8eb4d570a6371d13f62af79417d0aed Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sun, 23 Aug 2020 20:05:04 -0700 Subject: [PATCH 09/32] Extract removal of passwords to method. This makes it easier to create a simple test for the behavior. --- Duplicati/Server/WebServer/RESTMethods/Backup.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Duplicati/Server/WebServer/RESTMethods/Backup.cs b/Duplicati/Server/WebServer/RESTMethods/Backup.cs index e3f2069e5..926e7ba80 100644 --- a/Duplicati/Server/WebServer/RESTMethods/Backup.cs +++ b/Duplicati/Server/WebServer/RESTMethods/Backup.cs @@ -122,6 +122,12 @@ namespace Duplicati.Server.WebServer.RESTMethods info.OutputOK(new { inuse = Library.Main.DatabaseLocator.IsDatabasePathInUse(backup.DBPath) }); } + public static void RemovePasswords(IBackup backup) + { + backup.SanitizeSettings(); + backup.SanitizeTargetUrl(); + } + private void Export(IBackup backup, RequestInfo info) { var cmdline = Library.Utility.Utility.ParseBool(info.Request.QueryString["cmdline"].Value, false); @@ -129,8 +135,7 @@ namespace Duplicati.Server.WebServer.RESTMethods var exportPasswords = Library.Utility.Utility.ParseBool(info.Request.QueryString["export-passwords"].Value, false); if (!exportPasswords) { - backup.SanitizeSettings(); - backup.SanitizeTargetUrl(); + Backup.RemovePasswords(backup); } if (cmdline) From 5915fdfba94b0a6a333fb98696d47427038fce9d Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sun, 23 Aug 2020 20:25:11 -0700 Subject: [PATCH 10/32] Add test for exporting backup configuration to JSON. This tests that we escape '@' with '%40', as the import code in escape_uri in AppUtils.js cannot handle '@'. This concerns issue #3619. --- Duplicati/UnitTest/Duplicati.UnitTest.csproj | 1 + Duplicati/UnitTest/ImportExportTests.cs | 51 ++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/Duplicati/UnitTest/Duplicati.UnitTest.csproj b/Duplicati/UnitTest/Duplicati.UnitTest.csproj index 15f3586cd..745232828 100644 --- a/Duplicati/UnitTest/Duplicati.UnitTest.csproj +++ b/Duplicati/UnitTest/Duplicati.UnitTest.csproj @@ -42,6 +42,7 @@ + diff --git a/Duplicati/UnitTest/ImportExportTests.cs b/Duplicati/UnitTest/ImportExportTests.cs index 01d6f7feb..897b04c6f 100644 --- a/Duplicati/UnitTest/ImportExportTests.cs +++ b/Duplicati/UnitTest/ImportExportTests.cs @@ -20,6 +20,8 @@ using System.Collections.Generic; using System.IO; using Duplicati.Server; using Duplicati.Server.Database; +using Duplicati.Server.Serializable; +using Duplicati.Server.Serialization; using Duplicati.Server.Serialization.Interface; using Duplicati.Server.WebServer.RESTMethods; using NUnit.Framework; @@ -67,6 +69,55 @@ namespace Duplicati.UnitTest }; } + [Test] + [Category("ImportExport")] + [TestCase(true)] + [TestCase(false)] + public void ExportToJSONEncoding(bool removePasswords) + { + Dictionary metadata = new Dictionary(); + Dictionary advancedOptions = new Dictionary {{"server-datafolder", this.serverDatafolder}}; + + string usernameKey = "auth-username"; + string passwordKey = "auth-password"; + string username = @"user%40email.com"; + string password = @"abcde12345!@#$%/\"; + + IBackup backup = this.CreateBackup("backup", username, password, new Dictionary()); + if (removePasswords) + { + Server.WebServer.RESTMethods.Backup.RemovePasswords(backup); + } + + Assert.That(backup.TargetURL, Does.Contain($"{usernameKey}={username}")); + if (removePasswords) + { + Assert.That(backup.TargetURL, Does.Not.Contain(passwordKey)); + Assert.That(backup.TargetURL, Does.Not.Contain(password)); + } + else + { + Assert.That(backup.TargetURL, Does.Contain(passwordKey)); + Assert.That(backup.TargetURL, Does.Contain(password)); + } + + byte[] jsonByteArray; + using (Program.DataConnection = Program.GetDatabaseConnection(advancedOptions)) + { + jsonByteArray = Server.WebServer.RESTMethods.Backup.ExportToJSON(backup, null); + } + + // The username should not have the '%40' converted to '@' since the import code + // cannot handle it (see issue #3619). + string json = System.Text.Encoding.Default.GetString(jsonByteArray); + Assert.That(json, Does.Not.Contain("user@email.com")); + + ImportExportStructure importedConfiguration = Serializer.Deserialize(new StreamReader(new MemoryStream(jsonByteArray))); + Assert.AreEqual(backup.Description, importedConfiguration.Backup.Description); + Assert.AreEqual(backup.Name, importedConfiguration.Backup.Name); + Assert.AreEqual(backup.TargetURL, importedConfiguration.Backup.TargetURL); + } + [Test] [Category("ImportExport")] public void RoundTrip() From c15cdcf26ae6eb4cdfaff1f547add6eafe7f8121 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Mon, 24 Aug 2020 07:49:14 -0700 Subject: [PATCH 11/32] Remove unused local variable. --- Duplicati/UnitTest/ImportExportTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Duplicati/UnitTest/ImportExportTests.cs b/Duplicati/UnitTest/ImportExportTests.cs index 897b04c6f..9d0a05c7a 100644 --- a/Duplicati/UnitTest/ImportExportTests.cs +++ b/Duplicati/UnitTest/ImportExportTests.cs @@ -75,7 +75,6 @@ namespace Duplicati.UnitTest [TestCase(false)] public void ExportToJSONEncoding(bool removePasswords) { - Dictionary metadata = new Dictionary(); Dictionary advancedOptions = new Dictionary {{"server-datafolder", this.serverDatafolder}}; string usernameKey = "auth-username"; From f28550f4b50db4f3479127eae4ba72fe00d7a877 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Mon, 24 Aug 2020 07:51:57 -0700 Subject: [PATCH 12/32] Use overload instead of optional parameters. From Codacy: The overloading mechanism should be used in place of optional parameters for several reasons: * Optional parameter values are baked into the method call site code, thus, if a default value has been changed, all referencing assemblies need to be rebuilt, otherwise the original values will be used. * The Common Language Specification (CLS) allows compilers to ignore default parameter values, and thus require the caller to explicitly specify the values. For example, if you want to consume a method with default argument from another .NET compatible language (for instance C++/CLI), you will have to provide all arguments. When using method overloads, you could achieve similar behavior as default arguments. * Optional parameters prevent muddying the definition of the function contract. Here is a simple example: if there are two optional parameters, when one is defined, is the second one still optional or mandatory? --- Duplicati/Library/Utility/Uri.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Duplicati/Library/Utility/Uri.cs b/Duplicati/Library/Utility/Uri.cs index 0cb746cf3..19be6be5b 100644 --- a/Duplicati/Library/Utility/Uri.cs +++ b/Duplicati/Library/Utility/Uri.cs @@ -19,7 +19,6 @@ using System; using System.Collections.Specialized; using System.Linq; using System.Text; -using System.Web; namespace Duplicati.Library.Utility { @@ -438,6 +437,17 @@ namespace Duplicati.Library.Utility /// private static readonly System.Text.RegularExpressions.Regex RE_URLPARAM = new System.Text.RegularExpressions.Regex(@"(?[^\=\&]+)(\=(?[^\&]*))?", System.Text.RegularExpressions.RegexOptions.Compiled); + /// + /// Parses the query string. + /// This is a duplicate of the System.Web.HttpUtility.ParseQueryString that does not work well on Mono + /// + /// The parsed query string + /// The query to parse + public static NameValueCollection ParseQueryString(string query) + { + return Library.Utility.Uri.ParseQueryString(query, true); + } + /// /// Parses the query string. /// This is a duplicate of the System.Web.HttpUtility.ParseQueryString that does not work well on Mono @@ -445,7 +455,7 @@ namespace Duplicati.Library.Utility /// The parsed query string /// The query to parse /// Whether to the parameter values should be decoded or not. - public static NameValueCollection ParseQueryString(string query, bool decodeValues = true) + public static NameValueCollection ParseQueryString(string query, bool decodeValues) { if (query == null) throw new ArgumentNullException(nameof(query)); From aa2e92ea3a48926950186853fd36f699b62b2d0b Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sat, 29 Aug 2020 09:52:45 -0700 Subject: [PATCH 13/32] Fix appearance of loading icon in file restore tree. This fixes #4294. --- Duplicati/Server/webroot/ngax/less/style.less | 2 +- Duplicati/Server/webroot/ngax/styles/style.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Duplicati/Server/webroot/ngax/less/style.less b/Duplicati/Server/webroot/ngax/less/style.less index f211d8b01..4382e63cd 100755 --- a/Duplicati/Server/webroot/ngax/less/style.less +++ b/Duplicati/Server/webroot/ngax/less/style.less @@ -2525,13 +2525,13 @@ div.modal-dialog { background-image: url(../img/loader-16.gif); background-repeat: no-repeat; background-position: 0px 0px; + background-size: 16px 16px; @media only screen and (-webkit-min-device-pixel-ratio: 1.25), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 1.25dppx) { background-image: url('../img/loader-32.gif'); - background-size: 16px 16px; } } diff --git a/Duplicati/Server/webroot/ngax/styles/style.css b/Duplicati/Server/webroot/ngax/styles/style.css index a45264d25..8caecc1f6 100644 --- a/Duplicati/Server/webroot/ngax/styles/style.css +++ b/Duplicati/Server/webroot/ngax/styles/style.css @@ -1,4 +1,4 @@ @font-face{font-family:'Clear Sans';src:url('../fonts/ClearSans-Light-webfont.eot');src:url('../fonts/ClearSans-Light-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/ClearSans-Light-webfont.woff') format('woff'),url('../fonts/ClearSans-Light-webfont.ttf') format('truetype'),url('../fonts/ClearSans-Light-webfont.svg#clear_sans_lightregular') format('svg');font-weight:300;font-style:normal}@font-face{font-family:'Clear Sans';src:url('../fonts/ClearSans-Regular-webfont.eot');src:url('../fonts/ClearSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/ClearSans-Regular-webfont.woff') format('woff'),url('../fonts/ClearSans-Regular-webfont.ttf') format('truetype'),url('../fonts/ClearSans-Regular-webfont.svg#clear_sansregular') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'Clear Sans';src:url('../fonts/ClearSans-Medium-webfont.eot');src:url('../fonts/ClearSans-Medium-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/ClearSans-Medium-webfont.woff') format('woff'),url('../fonts/ClearSans-Medium-webfont.ttf') format('truetype'),url('../fonts/ClearSans-Medium-webfont.svg#clear_sans_mediumregular') format('svg');font-weight:500;font-style:normal}@font-face{font-family:'Clear Sans';src:url('../fonts/ClearSans-Bold-webfont.eot');src:url('../fonts/ClearSans-Bold-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/ClearSans-Bold-webfont.woff') format('woff'),url('../fonts/ClearSans-Bold-webfont.ttf') format('truetype'),url('../fonts/ClearSans-Bold-webfont.svg#clear_sansbold') format('svg');font-weight:700;font-style:normal}form.styled div.leftflush input{width:auto;margin-top:10px}form.styled div.leftflush label{width:auto;min-width:190px}form.styled label{display:block;width:190px;float:left;line-height:37px}form.styled input,form.styled textarea,form.styled select{color:#8f8f8f;font-size:16px;font-weight:300;float:left;display:block;border:1px #d8d8d8 solid;border-radius:2px;width:420px}form.styled input:focus,form.styled textarea:focus,form.styled select:focus{border:1px #a5a5a5 solid}form.styled .input{padding-bottom:18px;overflow:hidden}form.styled .input.text input,form.styled .input.password input{height:35px;line-height:35px;padding:0 12px}form.styled .input.text.text-browse input{width:375px;border-top-right-radius:0;border-bottom-right-radius:0;border-right:0}form.styled .input.text.text-browse a.browse{width:45px;display:block;float:left;height:37px;border-radius:2px;border-top-left-radius:0;border-bottom-left-radius:0;color:white;background:#65b1dd;line-height:37px}form.styled .input.text.text-browse a.browse:hover{background:#2881b4}form.styled .input.textarea textarea{height:130px;padding:10px 12px}form.styled .input.select select{width:446px;padding:0 12px;-webkit-appearance:menulist-button;background:white;border-radius:2px;height:38px;line-height:38px}form.styled .buttons{overflow:hidden;float:right}form.styled .buttons input,form.styled .buttons a{display:block;background:#65b1dd;color:white !important;padding:5px 15px;float:right;margin-left:10px;cursor:pointer;width:auto;border:none;font-family:'Clear Sans',sans-serif;font-size:16px;font-weight:300;border-radius:0}form.styled .buttons input{padding:4px 15px}form.styled .buttons input:hover,form.styled .buttons a:hover{background:#23729f}@media (max-width:480px){form.styled input,form.styled textarea,form.styled select{font-size:15px}}/*! * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}*{font-family:'Clear Sans',sans-serif}html,body{margin:0;padding:0;height:100%}h1,h2{font-weight:300;color:#81c401}h1{margin:10px 0}h3{font-weight:400}a{text-decoration:none}ul{list-style:none;margin:0;padding:0}hr{border:none;border-bottom:1px #ddd solid}textarea{max-width:94%}.external-link-image{display:inline-block;margin-left:8px;margin-right:8px;height:16px;width:16px;background:url('../img/external-link-hover.png');background-size:16px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.external-link-image{background-image:url('../img/external-link-hover_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.external-link-image{background-image:url('../img/external-link-hover_3x.png')}}a .external-link-image{background:url('../img/external-link.png');background-size:16px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){a .external-link-image{background-image:url('../img/external-link_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){a .external-link-image{background-image:url('../img/external-link_3x.png')}}.header a:hover .external-link-image{background:url('../img/external-link-hover.png');background-size:16px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.header a:hover .external-link-image{background-image:url('../img/external-link-hover_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.header a:hover .external-link-image{background-image:url('../img/external-link-hover_3x.png')}}.button{display:block;background:#65b1dd;color:white !important;padding:5px 15px;float:right;margin-left:10px;cursor:pointer;width:auto;border:none;font-family:'Clear Sans',sans-serif;font-size:16px;font-weight:300;border-radius:0}.button:hover{background:#3b9cd4}.step3 source-folder-picker,#folder_path_picker,#restore_file_picker{display:block;border:1px solid lightgray;padding:2px;height:100%;overflow:auto}.not-clickable{cursor:default !important}.not-clickable>a,.not-clickable span,.not-clickable div{cursor:default !important}.ui-match{font-weight:bold;color:darkgreen}wait-area{min-width:350px;text-align:center;display:block}.prewrapped-text{white-space:pre-wrap}.exceptiontext{background-color:lightgray;color:black}.backup-result{width:90%;display:grid;grid-template-columns:50% 50%;grid-auto-rows:minmax(50px, auto);margin:0 auto}.backup-result div .horizontal-rule{width:100%;border-bottom:1px solid #d8d8d8;margin:5px 0 5px 0}.backup-result .box{margin:10px;margin-bottom:0px}.backup-result .title{color:#609101;font-weight:bold;font-size:30px}.backup-result .item{display:block}.backup-result .item .key{color:#81c401;font-weight:bold}.backup-result .item .value{color:#8f8f8f}.backup-result .item .expanded{padding:0 10px 0 18px;margin-bottom:10px}.backup-result .one{border-right:1px solid #d8d8d8;grid-column:1;grid-row:1}.backup-result .two{grid-column:2;grid-row:1}.backup-result .wide{grid-column:span 2;border-top:1px solid #d8d8d8;padding-top:10px}.backup-result .three{grid-row:2}.backup-result .four{grid-row:3;margin-bottom:10px}.success-color{color:#339900}.error-color{color:#cc0000}.warning-color{color:#ffcc00}ul.tabs{margin-bottom:10px}ul.tabs>li{display:inline;margin-right:10px;border:1px solid #65b1dd;padding:5px}ul.tabs>li.active{background-color:#65b1dd;color:white}ul.tabs>li.active>a{background-color:#65b1dd;color:white}ul.tabs>li.active.disabled{border:1px solid lightgray;background-color:lightgray;color:grey;cursor:default}ul.tabs>li.active.disabled>a{background-color:lightgray;color:grey;cursor:default}.licenses>ul{list-style:initial;margin:10px;margin-left:20px}.licenses li{margin-bottom:10px}.licenses a.itemlink{font-weight:bold}.logpage ul.entries{list-style:initial;margin:10px;margin-left:20px}.logpage .entries div.entryline.clickable{cursor:pointer}.logpage .entries.livedata li.expanded{height:auto}.logpage .button{text-align:center;margin-right:10px;border:1px solid #65b1dd;padding:5px;background-color:#65b1dd;color:white;cursor:pointer}.exportpage .checkbox input{width:auto;margin-top:10px}.exportpage .commandline div{background-color:lightgray;color:black}.themelink{margin-left:20px}ul.notification{position:fixed;bottom:0px;left:0px;right:0px;margin:auto;width:480px}.notification .title{border:1px solid #65b1dd;background-color:#65b1dd;border-top-left-radius:5px;border-top-right-radius:5px;border-bottom-left-radius:0px;border-bottom-right-radius:0px;padding:2px;padding-left:5px;padding-right:5px;font-weight:bold;color:lightgray;width:100%;text-align:center;clear:both}.notification .content{background-color:white;border:1px solid #65b1dd;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:5px;border-bottom-right-radius:5px;padding:2px;padding-left:5px;padding-right:5px;width:100%}.notification .message{width:100%;color:black}.notification .button{padding:2px 10px;margin-top:6px}.notification .clear{clear:right;height:1px}.notification .error .title{border-color:red;background-color:red}.notification .error .content{border-color:red}.notification .error .button{border-color:red;background-color:red}.notification .warning .title{background-color:orange;border-color:orange}.notification .warning .button{background-color:orange;border-color:orange}.notification .warning .content{border-color:orange}.filepicker{height:200px}.resizable{margin-bottom:6px;max-width:100%}.advanced-toggle{float:right;margin-right:25px;line-height:37px}.advancedoptions li{clear:both;margin-bottom:10px;padding:10px 0;border-top:1px lightgray solid}.advancedentry .multiple{display:inline}.advancedentry .shortname{font-weight:bold}.advancedentry input[type="text"]{width:300px}.advancedentry select{width:300px}.advancedentry input[type="checkbox"]{margin-top:13px;width:auto}.advancedentry .longdescription{margin-left:190px;clear:both;font-style:italic}.settings div.sublabel{clear:both;padding:0 31px;font-style:italic}.logo img.mainlogo{height:64px;width:64px;float:left;padding-right:8px;padding-top:2px}.logo div.logotext{float:left}.logo a{float:left;display:block;line-height:normal}.logo div.build-suffix{clear:both;display:inline;float:left;font-size:16px;line-height:16px}.logo div.powered-by{font-size:16px;margin:0px;line-height:16px;float:left;padding:0px;margin-left:5px}.fixed-width-font{font-family:monospace}.warning{margin:10px;font-style:italic;color:#f49b42}div.captcha .details{padding-top:10px;margin-left:auto;margin-right:auto;width:180px}.centered-text{text-align:center}body{color:#8f8f8f}body .container{min-height:100%;position:relative}body .container .header{line-height:70px;background:#ededed;overflow:hidden;height:70px;position:fixed;top:0px;left:0px;right:0px;z-index:100}body .container .header a{color:#65b1dd}body .container .header a:hover,body .container .header a.active{color:#4f4f4f}body .container .header .logo{font-size:30px;font-weight:700;float:left;padding-left:40px}body .container .header .statepadding{padding-right:90px;margin-left:320px}body .container .header .state{float:left;color:#609101;width:595px;padding:13px 15px;margin:10px 20px;border:1px #609101 solid;font-weight:300;font-size:18px;overflow:hidden;line-height:normal;display:inline-block;background-color:white;text-overflow:ellipsis;position:relative;height:25px}body .container .header .state strong{display:inline;margin-right:10px}body .container .header .state span{display:inline}body .container .header .state .button{position:static;margin-top:70px}body .container .header .state .content{position:relative;z-index:10;margin-right:40px;display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}body .container .header .state .buttons{position:absolute;right:0px;top:0px;bottom:0px;width:26px;margin:13px 15px}body .container .header .state .buttons .stop{display:block;width:26px;height:26px;background:url('../img/progress-stop.png');background-size:26px;cursor:pointer}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .state .buttons .stop{background-image:url('../img/progress-stop_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .state .buttons .stop{background-image:url('../img/progress-stop_3x.png')}}body .container .header .state .buttons .resume{display:block;width:26px;height:26px;background:url('../img/progress-resume.png');background-size:26px;cursor:pointer}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .state .buttons .resume{background-image:url('../img/progress-resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .state .buttons .resume{background-image:url('../img/progress-resume_3x.png')}}body .container .header .state .progress-bar{position:absolute;top:0px;bottom:0px;left:0px;background:rgba(129,196,1,0.25);z-index:5}body .container .header .state .task-name{overflow:hidden;text-overflow:ellipsis;cursor:help}body .container .header .state .task-state-info{display:flex}body .container .header .action-icons{display:inline-block;line-height:normal;margin:10px 0px;padding:13px 0px;float:left}body .container .header .action-icons-small{display:none;float:right;margin-top:21px;line-height:normal}body .container .header .action-icons>.pause,body .container .header .action-icons-small>.pause{width:26px;height:26px;display:inline-block;cursor:pointer;background:url('../img/pause.png');background-size:26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .action-icons>.pause,body .container .header .action-icons-small>.pause{background-image:url('../img/pause_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .action-icons>.pause,body .container .header .action-icons-small>.pause{background-image:url('../img/pause_3x.png')}}body .container .header .action-icons>.pause.active,body .container .header .action-icons-small>.pause.active{background:url('../img/resume.png');background-size:26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .action-icons>.pause.active,body .container .header .action-icons-small>.pause.active{background-image:url('../img/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .action-icons>.pause.active,body .container .header .action-icons-small>.pause.active{background-image:url('../img/resume_3x.png')}}body .container .header .action-icons>.throttle,body .container .header .action-icons-small>.throttle{width:26px;height:26px;display:inline-block;cursor:pointer;background:url('../img/throttle.png');background-size:26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .action-icons>.throttle,body .container .header .action-icons-small>.throttle{background-image:url('../img/throttle_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .action-icons>.throttle,body .container .header .action-icons-small>.throttle{background-image:url('../img/throttle_3x.png')}}body .container .header .action-icons>.throttle.inactive,body .container .header .action-icons-small>.throttle.inactive{opacity:.5}body .container .header .about-header{float:right;padding-right:20px;overflow:hidden}body .container .header .about-header ul{overflow:hidden;list-style:none}body .container .header .about-header ul li{float:right;padding-right:20px}body .container .header .donate{float:right}body .container .header .donate ul{overflow:hidden;float:right;padding-left:20px;padding-right:10px}body .container .header .donate ul li{float:right;margin-right:10px;padding-top:5px}body .container .header .donate img{opacity:.6}body .container .header .donate img:hover{opacity:1}body .container .body{width:100%;overflow:hidden;min-height:500px;padding-top:120px;padding-bottom:70px}body .container .body a{color:#65b1dd}body .container .body .mainmenu{width:260px;padding-left:40px;float:left;position:fixed}body .container .body .mainmenu>ul>li{position:relative}body .container .body .mainmenu>ul>li>a{font-size:22px;font-weight:300;padding:5px 10px 5px 55px;display:block}body .container .body .mainmenu>ul>li>a:hover{color:white}body .container .body .mainmenu>ul>li>a.active{color:white}body .container .body .mainmenu>ul>li>a.add{background:url('../img/mainmenu/add.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.add{background-image:url('../img/mainmenu/add_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.add{background-image:url('../img/mainmenu/add_3x.png')}}body .container .body .mainmenu>ul>li>a.restore{background:url('../img/mainmenu/restore.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.restore{background-image:url('../img/mainmenu/restore_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.restore{background-image:url('../img/mainmenu/restore_3x.png')}}body .container .body .mainmenu>ul>li>a.resume{background:url('../img/mainmenu/resume.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.resume{background-image:url('../img/mainmenu/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.resume{background-image:url('../img/mainmenu/resume_3x.png')}}body .container .body .mainmenu>ul>li>a.settings{background:url('../img/mainmenu/settings.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.settings{background-image:url('../img/mainmenu/settings_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.settings{background-image:url('../img/mainmenu/settings_3x.png')}}body .container .body .mainmenu>ul>li>a.logout{background:url('../img/mainmenu/logout.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.logout{background-image:url('../img/mainmenu/logout_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.logout{background-image:url('../img/mainmenu/logout_3x.png')}}body .container .body .mainmenu>ul>li>a.home{background:url('../img/mainmenu/home.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.home{background-image:url('../img/mainmenu/home_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.home{background-image:url('../img/mainmenu/home_3x.png')}}body .container .body .mainmenu>ul>li>a.about{background:url('../img/mainmenu/about.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.about{background-image:url('../img/mainmenu/about_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.about{background-image:url('../img/mainmenu/about_3x.png')}}body .container .body .mainmenu>ul>li>a.home.active{background:#a4d1eb url('../img/mainmenu/over/home.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.home.active{background-image:url('../img/mainmenu/over/home_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.home.active{background-image:url('../img/mainmenu/over/home_3x.png')}}body .container .body .mainmenu>ul>li>a.add.active{background:#a4d1eb url('../img/mainmenu/over/add.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.add.active{background-image:url('../img/mainmenu/over/add_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.add.active{background-image:url('../img/mainmenu/over/add_3x.png')}}body .container .body .mainmenu>ul>li>a.restore.active{background:#a4d1eb url('../img/mainmenu/over/restore.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.restore.active{background-image:url('../img/mainmenu/over/restore_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.restore.active{background-image:url('../img/mainmenu/over/restore_3x.png')}}body .container .body .mainmenu>ul>li>a.resume.active{background:#a4d1eb url('../img/mainmenu/over/resume.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.resume.active{background-image:url('../img/mainmenu/over/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.resume.active{background-image:url('../img/mainmenu/over/resume_3x.png')}}body .container .body .mainmenu>ul>li>a.settings.active{background:#a4d1eb url('../img/mainmenu/over/settings.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.settings.active{background-image:url('../img/mainmenu/over/settings_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.settings.active{background-image:url('../img/mainmenu/over/settings_3x.png')}}body .container .body .mainmenu>ul>li>a.about.active{background:#a4d1eb url('../img/mainmenu/over/about.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.about.active{background-image:url('../img/mainmenu/over/about_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.about.active{background-image:url('../img/mainmenu/over/about_3x.png')}}body .container .body .mainmenu>ul>li>a.add:hover{background:#65b1dd url('../img/mainmenu/over/add.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.add:hover{background-image:url('../img/mainmenu/over/add_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.add:hover{background-image:url('../img/mainmenu/over/add_3x.png')}}body .container .body .mainmenu>ul>li>a.restore:hover{background:#65b1dd url('../img/mainmenu/over/restore.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.restore:hover{background-image:url('../img/mainmenu/over/restore_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.restore:hover{background-image:url('../img/mainmenu/over/restore_3x.png')}}body .container .body .mainmenu>ul>li>a.resume:hover{background:#65b1dd url('../img/mainmenu/over/resume.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.resume:hover{background-image:url('../img/mainmenu/over/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.resume:hover{background-image:url('../img/mainmenu/over/resume_3x.png')}}body .container .body .mainmenu>ul>li>a.settings:hover{background:#65b1dd url('../img/mainmenu/over/settings.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.settings:hover{background-image:url('../img/mainmenu/over/settings_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.settings:hover{background-image:url('../img/mainmenu/over/settings_3x.png')}}body .container .body .mainmenu>ul>li>a.logout:hover{background:#65b1dd url('../img/mainmenu/over/logout.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.logout:hover{background-image:url('../img/mainmenu/over/logout_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.logout:hover{background-image:url('../img/mainmenu/over/logout_3x.png')}}body .container .body .mainmenu>ul>li>a.home:hover{background:#65b1dd url('../img/mainmenu/over/home.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.home:hover{background-image:url('../img/mainmenu/over/home_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.home:hover{background-image:url('../img/mainmenu/over/home_3x.png')}}body .container .body .mainmenu>ul>li>a.about:hover{background:#65b1dd url('../img/mainmenu/over/about.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.about:hover{background-image:url('../img/mainmenu/over/about_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.about:hover{background-image:url('../img/mainmenu/over/about_3x.png')}}body .container .body .mainmenu>ul li.hr-top{padding-top:25px;margin-top:25px;border-top:1px #ededed solid}body .container .body div.contextmenu_container{position:relative}body .container .body .contextmenu{display:none;position:absolute;background:white;border:1px #ededed solid;box-shadow:0 4px 8px rgba(0,0,0,0.3);z-index:200;padding:5px}body .container .body .contextmenu li a{color:#65b1dd;font-size:15px;font-weight:400;padding:0;display:block;min-width:200px;padding:4px 10px;white-space:nowrap;padding-left:45px;overflow:hidden;text-overflow:ellipsis}body .container .body .contextmenu li a:hover{background:#65b1dd;color:white}body .container .body .contextmenu.open{display:block}body .container .body .content{float:left;padding-left:350px;padding-bottom:50px;max-width:70%}body .container .body .content ul.tabs>li{display:inline-block}body .container .body .content .tasks .tasklist .task{border-top:1px solid #eee;padding-top:20px;margin-bottom:25px}body .container .body .content .tasks .tasklist .task:last-child{border-bottom:1px solid #eee;padding-bottom:20px}body .container .body .content .tasks .tasklist .task:first-child{padding-top:0px;border-top:0px none}body .container .body .content .tasks .tasklist .progress-small{text-align:center;height:18px;background:rgba(164,209,235,0.5)}body .container .body .content .tasks .tasklist .progress-small-bg{border:1px #65b1dd solid;width:200px}body .container .body .content .tasks .tasklist a{font-size:30px;font-weight:300;display:inline-block}body .container .body .content .tasks .tasklist a.action-link{font-size:14px;background:none;padding-left:0px}body .container .body .content .tasks .tasklist dl{padding-left:55px;overflow:hidden;font-size:14px}body .container .body .content .tasks .tasklist dl dt,body .container .body .content .tasks .tasklist dl dd{display:block;float:left}body .container .body .content .tasks .tasklist dl dt{clear:both;font-weight:500;margin-bottom:5px}body .container .body .content .tasks .tasklist dl dd{margin-left:10px}body .container .body .content .tasks .tasklist dl.taskmenu p{display:inline;margin-right:10px;color:#65b1dd;cursor:pointer}body .container .body .content .tasks .tasklist dl.taskmenu dt{float:left;margin-right:10px;margin-bottom:0;padding:5px 8px;color:#8f8f8f;cursor:pointer;clear:none}body .container .body .content .tasks .tasklist dl.taskmenu dd{clear:both;float:none;padding-bottom:8px;border-bottom:1px #ddd solid;margin-bottom:5px}body .container .body .content div.add .steps,body .container .body .content div.restore .steps{width:100%;overflow:hidden}body .container .body .content div.add .steps .step,body .container .body .content div.restore .steps .step{float:left;background:url('../img/steps/line-out.png') no-repeat top left;background-size:485px 24px;color:#c7e5f6}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .content div.add .steps .step,body .container .body .content div.restore .steps .step{background-image:url('../img/steps/line-out_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .content div.add .steps .step,body .container .body .content div.restore .steps .step{background-image:url('../img/steps/line-out_3x.png')}}body .container .body .content div.add .steps .step span,body .container .body .content div.restore .steps .step span{display:block;border:4px #c7e5f6 solid;background:white;border-radius:50%;width:35px;height:35px;text-align:center;font-size:22px;line-height:35px;cursor:pointer}body .container .body .content div.add .steps .step.active,body .container .body .content div.restore .steps .step.active{color:#65b1dd}body .container .body .content div.add .steps .step.active span,body .container .body .content div.restore .steps .step.active span{border:4px #65b1dd solid;background:#65b1dd;color:white}body .container .body .content div.add .steps .step.active h2,body .container .body .content div.restore .steps .step.active h2{color:#65b1dd}body .container .body .content div.add .steps .step:first-child,body .container .body .content div.restore .steps .step:first-child{padding-left:0;background:transparent}body .container .body .content div.add .steps-legend,body .container .body .content div.restore .steps-legend{overflow:hidden;padding-bottom:50px;list-style:none;margin:0}body .container .body .content div.add .steps-legend li,body .container .body .content div.restore .steps-legend li{color:#c7e5f6;font-size:18px;text-align:center;float:left;padding-top:10px;cursor:pointer}body .container .body .content div.add .steps-legend li.active,body .container .body .content div.restore .steps-legend li.active{color:#65b1dd}body .container .body .content div.add .steps-boxes,body .container .body .content div.restore .steps-boxes{padding-left:40px}body .container .body .content div.add .steps-boxes .step,body .container .body .content div.restore .steps-boxes .step{display:none}body .container .body .content div.add .steps-boxes .step.active,body .container .body .content div.restore .steps-boxes .step.active{display:block}body .container .body .content div.add .steps-boxes .box.browser .checklinks a,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a{float:left;margin-left:20px;color:#8f8f8f}body .container .body .content div.add .steps-boxes .box.browser .checklinks a i,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a i{border:2px solid;border-color:#8f8f8f;border-radius:2px;font-size:18px;height:18px;vertical-align:top;width:18px}body .container .body .content div.add .steps-boxes .box.browser .checklinks a.inactive,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a.inactive{color:#c2c2c2;cursor:default}body .container .body .content div.add .steps-boxes .box.browser .checklinks a.inactive i,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a.inactive i{border-color:#c2c2c2}body .container .body .content div.add .steps-boxes .box.browser .checklinks a:first-child,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a:first-child{margin-left:0}body .container .body .content div.add .steps-boxes .box.browser .input.overlayButton,body .container .body .content div.restore .steps-boxes .box.browser .input.overlayButton{padding-top:10px;max-width:100%}body .container .body .content div.add .steps-boxes .box.browser .input.overlayButton input#sourcePath,body .container .body .content div.restore .steps-boxes .box.browser .input.overlayButton input#sourcePath{width:100%;box-sizing:border-box;height:37px}body .container .body .content div.add .steps-boxes .box.browser .input.overlayButton a.button,body .container .body .content div.restore .steps-boxes .box.browser .input.overlayButton a.button{top:10px}body .container .body .content div.add .steps-boxes .box.filters .input.link a,body .container .body .content div.restore .steps-boxes .box.filters .input.link a{color:#8f8f8f}body .container .body .content div.add .steps-boxes .box.filters .input.link a i,body .container .body .content div.restore .steps-boxes .box.filters .input.link a i{border:2px solid;border-color:#8f8f8f;border-radius:2px;font-size:18px;height:18px;vertical-align:top;width:18px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist{overflow:hidden;padding-bottom:15px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist li,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist li{overflow:hidden;clear:both;padding-bottom:5px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist li select,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist li select{width:200px;margin-right:5px;height:36px;line-height:36px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist li input,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist li input{width:280px;padding:5px}body .container .body .content div.add .steps-boxes .step1 li.strength.score-0,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-0{color:#ff0000}body .container .body .content div.add .steps-boxes .step1 li.strength.score-1,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-1{color:#ff7700}body .container .body .content div.add .steps-boxes .step1 li.strength.score-2,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-2{color:#aaaa00}body .container .body .content div.add .steps-boxes .step1 li.strength.score-3,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-3{color:#007700}body .container .body .content div.add .steps-boxes .step1 li.strength.score-4,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-4{color:#427e27}body .container .body .content div.add .steps-boxes .step1 li.strength.score-x,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-x{color:#ff0000}body .container .body .content div.add .steps-boxes .step5 div.input.maxSize input.number,body .container .body .content div.restore .steps-boxes .step5 div.input.maxSize input.number,body .container .body .content div.add .steps-boxes .step5 div.input.keepBackups input.number,body .container .body .content div.restore .steps-boxes .step5 div.input.keepBackups input.number{width:60px}body .container .body .content div.add .steps-boxes .step5 .hint,body .container .body .content div.restore .steps-boxes .step5 .hint,body .container .body .content div.add .steps-boxes .step5 .retention-options,body .container .body .content div.restore .steps-boxes .step5 .retention-options{clear:both;margin-left:190px;margin-top:50px;font-style:italic}body .container .body .content div.add .steps-boxes .step5 .retention-options input,body .container .body .content div.restore .steps-boxes .step5 .retention-options input{margin-bottom:10px}body .container .body .content div.add .steps-boxes .step5 .advancedoptions,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions{padding-top:15px;clear:both}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li{border-top:none}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li>a,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li>a{display:block;background:#65b1dd;color:white !important;padding:5px 15px;float:right;margin-left:10px;cursor:pointer;width:auto;border:none;font-family:'Clear Sans',sans-serif;font-size:16px;font-weight:300;border-radius:0}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li.advancedentry,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li.advancedentry{border-bottom:1px solid lightgray}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li:last-child,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li:last-child{padding-top:0}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li:last-child select,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li:last-child select{max-width:400px}body .container .body .content div.add .steps-boxes .step5 .advancedoptions label,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions label{line-height:normal}body .container .body .content div.add .steps-boxes .step5 .advancedoptions input,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions input,body .container .body .content div.add .steps-boxes .step5 .advancedoptions select,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions select{width:auto;max-width:100%;box-sizing:border-box}body .container .body .content div.add .steps-boxes .step5 .advancedoptions .longdescription,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions .longdescription{margin-top:10px}body .container .body .content div.add .steps-boxes .step5 .advanced-toggle,body .container .body .content div.restore .steps-boxes .step5 .advanced-toggle{color:#8f8f8f;line-height:normal;margin-top:16px;clear:both;float:left}body .container .body .content div.add .steps-boxes .step5 .advanced-toggle i.fa,body .container .body .content div.restore .steps-boxes .step5 .advanced-toggle i.fa{border:2px solid;border-color:#8f8f8f;border-radius:2px;font-size:18px;height:18px;vertical-align:top;width:18px}body .container .body .content div.add .steps-boxes .step5 textarea,body .container .body .content div.restore .steps-boxes .step5 textarea{box-sizing:border-box;clear:both;margin-top:15px;width:100%}body .container .body .content div.add form,body .container .body .content div.restore form{padding-bottom:50px;overflow:hidden}body .container .body .content div.add form .input.password .tools,body .container .body .content div.restore form .input.password .tools{clear:both;padding-left:190px;padding-top:10px}body .container .body .content div.add form .input.password .tools ul,body .container .body .content div.restore form .input.password .tools ul{overflow:hidden}body .container .body .content div.add form .input.password .tools ul li,body .container .body .content div.restore form .input.password .tools ul li{float:left;padding-right:7px}body .container .body .content div.add form .input.password .tools ul li.strength.useless,body .container .body .content div.restore form .input.password .tools ul li.strength.useless{color:red}body .container .body .content div.add form .input.password .tools ul li.strength.average,body .container .body .content div.restore form .input.password .tools ul li.strength.average{color:yellow}body .container .body .content div.add form .input.password .tools ul li.strength.good,body .container .body .content div.restore form .input.password .tools ul li.strength.good{color:#65b1dd}body .container .body .content div.add form .input.multiple input,body .container .body .content div.restore form .input.multiple input,body .container .body .content div.add form .input.multiple select,body .container .body .content div.restore form .input.multiple select{width:auto;margin-right:5px}body .container .body .content div.add form .input.multiple select,body .container .body .content div.restore form .input.multiple select{padding:5px 12px}body .container .body .content div.add form .input.overlayButton,body .container .body .content div.restore form .input.overlayButton{overflow:hidden;position:relative;max-width:446px}body .container .body .content div.add form .input.overlayButton input,body .container .body .content div.restore form .input.overlayButton input{width:347px}body .container .body .content div.add form .input.overlayButton a.button,body .container .body .content div.restore form .input.overlayButton a.button{position:absolute;top:0;right:0;padding:7px 12px 8px}body .container .body .content div.add form .input.checkbox.multiple strong,body .container .body .content div.restore form .input.checkbox.multiple strong{display:block;padding-bottom:5px}body .container .body .content div.add form .input.checkbox.multiple label,body .container .body .content div.restore form .input.checkbox.multiple label{display:inline-block;float:none;width:auto;padding-right:10px}body .container .body .content div.add form .input.checkbox.multiple input,body .container .body .content div.restore form .input.checkbox.multiple input{width:auto;display:inline-block;float:none}body .container .body .content div.add form .buttons,body .container .body .content div.restore form .buttons{float:none;width:635px;padding-top:30px}body .container .body .content div.add .steps{margin-left:48.5px}body .container .body .content div.add .steps .step{padding-left:97px}body .container .body .content div.add .steps-legend{padding-left:0}body .container .body .content div.add .steps-legend li{width:140px}body .container .body .content div.restore .steps{margin-left:153.5px}body .container .body .content div.restore .steps .step{padding-left:307px}body .container .body .content div.restore .steps-legend{padding-left:0}body .container .body .content div.restore .steps-legend li{width:350px}body .container .body .content div.restore.restore-direct .steps{margin-left:66px}body .container .body .content div.restore.restore-direct .steps .step{padding-left:132px}body .container .body .content div.restore.restore-direct .steps-legend{padding-left:0}body .container .body .content div.restore.restore-direct .steps-legend li{width:175px}body .container .body .content div.restore.restore-direct .step:first-child{padding-left:0;background:transparent}body .container .body .content div.restore.restore-direct .steps-legend{padding-left:20px}body .container .body .content div.headerthreedotmenu{margin:20px 0 20px 0}body .container .body .content div.headerthreedotmenu h2{display:inline}body .container .body .content div.headerthreedotmenu .contextmenu_container{float:right}body .container .body .content div.headerthreedotmenu .contextmenu{left:auto;right:0;top:auto}body .container .body .content div.headerthreedotmenu .threedotmenubutton{padding:5px}body .container .body .content .expandable{margin:20px 0 20px 0}body .container .body .content .expandable h2{display:inline}body .container .body .content .expandable img{padding:0 6px}body .container .body .content div.settings .input.mixed.multiple input.checkbox,body .container .body .content div.settings .input.checkbox input.checkbox{width:auto}body .container .body .content div.settings .input.mixed.multiple select,body .container .body .content div.settings .input.checkbox select{width:auto;margin-right:5px}body .container .body .content div.settings .input.mixed.multiple label,body .container .body .content div.settings .input.checkbox label{line-height:normal;padding:0 15px;width:auto}body .container .body .content .logpage ul.tabs{padding:15px 0}body .container .body .content .logpage ul.entries li{padding:10px 0 10px 0;border-bottom:1px solid #d8d8d8}body .container .body .content .logpage ul.backuplog{list-style:none}body .container .body .content .prewrapped-text{white-space:pre-wrap;overflow-x:auto}body .container .footer{background:#ededed;min-height:70px;line-height:70px;overflow:hidden;position:absolute;bottom:0;width:100%}body .container .footer a{color:#65b1dd}body .container .footer .about-footer{float:left;overflow:hidden;padding-right:20px;display:none}body .container .footer .about-footer span{display:block;float:left;padding-left:20px}body .container .footer .about-footer ul{float:left}body .container .footer .about-footer li{float:left;padding-left:20px}body .container .footer .donate{display:none;float:right;padding-right:40px;overflow:hidden}body .container .footer .donate ul{overflow:hidden;float:right}body .container .footer .donate ul li{float:left;margin-left:10px}body .container .footer .donate ul li a img{margin-top:24px;display:inline-block;opacity:.6}body .container .footer .donate ul li a img:hover{opacity:1}body .container .footer .donate>a{float:left}body .container .footer .social{float:right}body .container .footer .social ul{overflow:hidden;float:right;padding-left:20px;padding-right:10px}body .container .footer .social ul li{float:right;margin-right:10px;padding-top:5px}body .container .footer .social ul li img{opacity:.6}body .container .footer .social ul li img:hover{opacity:1}body .container .footer .themelink{float:right;padding-right:20px}body #modal-menu{max-width:400px}body #modal-menu a{color:#65b1dd;font-size:20px;line-height:40px}.remodal{padding:30px;box-shadow:0 2px 7px rgba(0,0,0,0.3);background:white;display:none}.remodal form .buttons{float:none}.remodal-wrapper .remodal{display:block}span.info{font-size:10px;font-weight:500;display:inline-block;background:#65b1dd;border-radius:50%;width:15px;height:15px;vertical-align:super;color:white;line-height:15px;margin-left:5px;text-align:center}.hidden{display:none}.clear{clear:both}.nofloat{float:none !important}div.blocker,div.connection-lost,div.modal-dialog{position:fixed;top:0px;left:0px;right:0px;bottom:0px;margin:auto}div.blocker{z-index:5000;background-color:#000000;opacity:.65}#connection-lost-blocker{z-index:5100}#connection-lost-dialog{z-index:5200}div.connection-lost,div.modal-dialog{z-index:5001;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-webkit-box-pack:center;-moz-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-box-align:center;-moz-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}div.connection-lost div.info,div.modal-dialog div.info{min-width:310px;max-width:650px;margin:5px}div.connection-lost div.title,div.modal-dialog div.title{border:1px solid #65b1dd;background-color:#65b1dd;border-radius:5px 5px 0 0;padding:10px 20px;font-weight:bold;color:lightgray;text-align:center}div.connection-lost div.content,div.modal-dialog div.content{background-color:white;border:1px solid white;padding:20px}div.connection-lost .buttons,div.modal-dialog .buttons{border-radius:0 0 5px 5px;padding-top:10px;overflow:auto}div.connection-lost form,div.modal-dialog form{margin-top:15px}div.connection-lost form textarea,div.modal-dialog form textarea{height:130px;width:420px;padding:10px 12px;border:1px #d8d8d8 solid;border-radius:2px;color:#8f8f8f;font-size:16px;font-weight:300}div.connection-lost form input,div.modal-dialog form input{height:35px;line-height:35px;padding:0 12px}div.modal-dialog .content.buttons ul{float:right}div.modal-dialog .content.buttons .tooltipped{position:relative}div.modal-dialog .content.buttons .tooltipped:after{position:absolute;z-index:1000000;display:none;padding:5px 8px;font:normal normal 11px/1.5 Helvetica,arial,nimbussansl,liberationsans,freesans,clean,sans-serif,"Segoe UI Emoji","Segoe UI Symbol";color:#fff;text-align:center;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-wrap:break-word;white-space:pre;pointer-events:none;content:attr(aria-label);background:rgba(0,0,0,0.8);border-radius:3px;-webkit-font-smoothing:subpixel-antialiased}div.modal-dialog .content.buttons .tooltipped:before{position:absolute;z-index:1000001;display:none;width:0;height:0;color:rgba(0,0,0,0.8);pointer-events:none;content:"";border:5px solid transparent}div.modal-dialog .content.buttons .tooltipped:hover:before,div.modal-dialog .content.buttons .tooltipped:hover:after,div.modal-dialog .content.buttons .tooltipped:active:before,div.modal-dialog .content.buttons .tooltipped:active:after,div.modal-dialog .content.buttons .tooltipped:focus:before,div.modal-dialog .content.buttons .tooltipped:focus:after{display:inline-block;text-decoration:none}div.modal-dialog .content.buttons .tooltipped-w:after{right:100%;bottom:50%;margin-right:5px;-webkit-transform:translateY(50%);-ms-transform:translateY(50%);transform:translateY(50%)}div.modal-dialog .content.buttons .tooltipped-w:before{top:50%;bottom:50%;left:-5px;margin-top:-5px;border-left-color:rgba(0,0,0,0.8)}.importpage form.styled input{margin-top:11px;margin-bottom:11px}.restorewizard form.styled ul,.addwizard form.styled ul{margin:20px;margin-left:0px}.restorewizard form.styled input[type="radio"],.addwizard form.styled input[type="radio"]{width:20px;margin-left:5px;margin-right:5px}.restorewizard form.styled label,.addwizard form.styled label{width:auto;line-height:normal}.restorewizard form.styled div.subtext,.addwizard form.styled div.subtext{clear:both;margin-left:30px;padding-top:5px;color:lightgray}.pauseoptions form.styled li{line-height:normal;padding:0px}.pauseoptions form.styled li input{height:auto;margin-top:8px;margin-right:8px;width:auto}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress{position:relative;min-height:25px}.progress>span{vertical-align:middle;display:block;width:100%;height:100%;text-align:center;z-index:100;padding-top:2px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress .progress-bar{float:left;width:0;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease;height:100%;position:absolute;top:0}.progress .progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.tree-view{list-style-type:none;margin-left:10px;padding-bottom:5px}.tree-view ul{margin-left:16px}.tree-view span.nodeLabel{cursor:pointer}.tree-view span.nodeLabel.selected{border:1px solid #aaa;background-color:#ddd;padding:1px 3px}.tree-view li .node{padding-bottom:5px}.tree-view li div.selected{border-color:lightblue;background-color:lightblue}.tree-view li>ul{display:none}.tree-view li>ul.expanded{display:block}.tree-view li a.nav{cursor:pointer;display:inline-block;width:16px;height:16px;vertical-align:middle;background-image:url(../img/treeicons.png);background-repeat:no-repeat;background-position:-80px 0;background-size:112px 64px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.nav{background-image:url('../img/treeicons_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.tree-view li a.nav{background-image:url('../img/treeicons_3x.png')}}.tree-view li a.nav.leaf{background:none}.tree-view li a.nav.expanded{background-position:-80px -16px}.tree-view li a.type{cursor:auto;display:inline-block;width:16px;height:16px;vertical-align:middle;background-image:url(../img/treeicons.png);background-repeat:no-repeat;background-position:0 -16px;background-size:112px 64px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.type{background-image:url('../img/treeicons_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.tree-view li a.type{background-image:url('../img/treeicons_3x.png')}}.tree-view li a.type.invisible{background-position:0 -32px}.tree-view li a.type.loading{cursor:progress;background-image:url(../img/loader-16.gif);background-repeat:no-repeat;background-position:0px 0px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.type.loading{background-image:url('../img/loader-32.gif');background-size:16px 16px}}.tree-view li a.type.x-tree-icon-drive{background-position:-16px -16px}.tree-view li a.type.x-tree-icon-leaf{background-position:-32px -16px}.tree-view li a.type.x-tree-icon-symlink{background-position:-48px -16px}.tree-view li a.type.x-tree-icon-userdata{background-position:-16px -48px}.tree-view li a.type.x-tree-icon-locked{background-position:-64px -16px}.tree-view li a.type.x-tree-icon-broken{background-position:-64px -16px}.tree-view li a.type.x-tree-icon-computer{background-position:0 -48px}.tree-view li a.type.x-tree-icon-hyperv{background-position:-96px -16px}.tree-view li a.type.x-tree-icon-hypervmachine{background-position:-96px 0}.tree-view li a.type.x-tree-icon-mssql{background-position:-96px -32px}.tree-view li a.type.x-tree-icon-mssqldb{background-position:-80px -32px}.tree-view li a.type.x-tree-icon-mydocuments{background-position:-32px -48px}.tree-view li a.type.x-tree-icon-mymusic{background-position:-48px -48px}.tree-view li a.type.x-tree-icon-mypictures{background-position:-64px -48px}.tree-view li a.type.x-tree-icon-desktop{background-position:-80px -48px}.tree-view li a.type.x-tree-icon-home{background-position:-96px -48px}.tree-view li a.type.x-tree-icon-drive.invisible{background-position:-16px -32px}.tree-view li a.type.x-tree-icon-leaf.invisible{background-position:-32px -32px}.tree-view li a.type.x-tree-icon-symlink.invisible{cursor:auto;background-position:-48px -32px}.tree-view li a.type.x-tree-icon-locked.invisible{background-position:-64px -32px}.tree-view li a.check{height:16px;width:16px;display:inline-block;cursor:pointer;background-image:url(../img/treeicons.png);background-repeat:no-repeat;background-position:0 0;vertical-align:middle;background-size:112px 64px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.check{background-image:url('../img/treeicons_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.tree-view li a.check{background-image:url('../img/treeicons_3x.png')}}.tree-view li a.partial{background-position:-32px 0}.tree-view li a.include{background-position:-16px 0}.tree-view li a.exclude{background-position:-48px 0}.tree-view li a.root{background:none;display:none}.throttlesettings div.multiple select{width:auto;margin-right:5px}.throttlesettings div.multiple input{width:100px}.throttlesettings div.multiple input.checkbox{width:auto}.throttlesettings div.multiple label{line-height:35px;padding:0 15px;width:auto;min-width:150px}.throttlesettings .disabled{color:#cfcfcf}.throttlesettings .disabled input,.throttlesettings .disabled select{color:#cfcfcf}@media (max-width:1150px){body .container .header{height:140px}body .container .header .statepadding{padding-right:90px;margin-left:0px}body .container .header .state{width:100%;margin:10px 40px;clear:left;float:left}body .container .header .action-icons{display:none}body .container .header .action-icons-small{display:inline-block}body .container .header .menubutton{display:block;font-size:18px;padding-right:50px;margin-top:5px;margin-right:15px;background:url('../img/menu.png') no-repeat right top;background-size:39px 39px;position:relative;height:40px;line-height:40px;color:#8f8f8f;float:right;top:10px;padding-left:20px;text-transform:uppercase;text-align:right}body .container .header .menubutton.active{background-image:url('../img/menu_active.png');background-size:39px 39px;color:#65b1dd}body .container .header .donate{display:none}body .container .body{position:relative;padding-top:140px}body .container .body .mainmenu{display:none;position:fixed;background:none repeat scroll 0 0 white;box-shadow:0 4px 8px rgba(0,0,0,0.3);left:10px;padding:20px;top:60px}body .container .body .mainmenu.mobile-open{display:block;left:auto;right:0;top:0;z-index:1000}body .container .body .contextmenu{left:0;top:auto}body .container .body .content{float:none;padding:20px 20px;margin:0px auto 30px auto}body .container .body .content .state{width:auto}body .container .mobileOpen{display:block !important}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:1.25),(max-width:1150px) and (min-resolution:192dpi),(max-width:1150px) and (min-resolution:1.25dppx){body .container .header .menubutton{background-image:url('../img/menu_2x.png')}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:2.25),(max-width:1150px) and (min-resolution:288dpi),(max-width:1150px) and (min-resolution:2.25dppx){body .container .header .menubutton{background-image:url('../img/menu_3x.png')}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:1.25),(max-width:1150px) and (min-resolution:192dpi),(max-width:1150px) and (min-resolution:1.25dppx){body .container .header .menubutton.active{background-image:url('../img/menu_active_2x.png')}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:2.25),(max-width:1150px) and (min-resolution:288dpi),(max-width:1150px) and (min-resolution:2.25dppx){body .container .header .menubutton.active{background-image:url('../img/menu_active_3x.png')}}@media (max-width:768px){body .container .body .content .tasks .tasklist a{font-size:20px;background-size:24px;background-position:0 4px;padding-left:35px}body .container .body .content .tasks .tasklist dl{padding-left:35px}body .container .header .logo{padding-left:10px}body .container .header .statepadding{padding-right:50px}body .container .header .state{margin-left:10px}body .container .header .menubutton{margin-right:5px}body .container .body .content div.add .steps,body .container .body .content div.restore .steps,body .container .body .content div.settings .steps{display:none}body .container .body .content div.add .steps-legend,body .container .body .content div.restore .steps-legend,body .container .body .content div.settings .steps-legend{list-style:decimal;padding-left:20px;border-bottom:1px solid #eee;margin-bottom:30px;padding-bottom:20px}body .container .body .content div.add .steps-legend li,body .container .body .content div.restore .steps-legend li,body .container .body .content div.settings .steps-legend li{float:none;font-weight:500;width:auto !important;padding-right:0 !important}body .container .body .content div.add .steps-boxes,body .container .body .content div.restore .steps-boxes,body .container .body .content div.settings .steps-boxes{padding-left:0}body .container .body .content div.add form.styled .input input,body .container .body .content div.restore form.styled .input input,body .container .body .content div.settings form.styled .input input,body .container .body .content div.add form.styled .input select,body .container .body .content div.restore form.styled .input select,body .container .body .content div.settings form.styled .input select,body .container .body .content div.add form.styled .input textarea,body .container .body .content div.restore form.styled .input textarea,body .container .body .content div.settings form.styled .input textarea{max-width:100%;box-sizing:border-box}body .container .body .content div.add form.styled .input.select select,body .container .body .content div.restore form.styled .input.select select,body .container .body .content div.settings form.styled .input.select select{width:420px}body .container .body .content div.add form.styled .buttons,body .container .body .content div.restore form.styled .buttons,body .container .body .content div.settings form.styled .buttons{max-width:100%;width:auto}body .container .body .content div.add form.styled .tools,body .container .body .content div.restore form.styled .tools,body .container .body .content div.settings form.styled .tools{padding-left:0 !important}body .container .body .content div.add form.styled .input.checkbox.multiple,body .container .body .content div.restore form.styled .input.checkbox.multiple,body .container .body .content div.settings form.styled .input.checkbox.multiple{padding-bottom:5px}body .container .body .content div.add form.styled .input.checkbox.multiple input,body .container .body .content div.restore form.styled .input.checkbox.multiple input,body .container .body .content div.settings form.styled .input.checkbox.multiple input,body .container .body .content div.add form.styled .input.checkbox.multiple label,body .container .body .content div.restore form.styled .input.checkbox.multiple label,body .container .body .content div.settings form.styled .input.checkbox.multiple label{display:block !important;float:left !important;line-height:normal}body .container .body .content div.add form.styled .input.checkbox.multiple input,body .container .body .content div.restore form.styled .input.checkbox.multiple input,body .container .body .content div.settings form.styled .input.checkbox.multiple input{clear:both}body .container .body .content div.add form.styled .input.text.multiple input,body .container .body .content div.restore form.styled .input.text.multiple input,body .container .body .content div.settings form.styled .input.text.multiple input{max-width:48% !important}}@media (max-width:640px){body h2{font-size:20px;text-align:center}body .container .body{padding-bottom:10px}body .container .body .content{margin:0px auto}body .container .body .content div.add form .input.overlayButton,body .container .body .content div.restore form .input.overlayButton{padding-top:8px;padding-bottom:30px;margin-bottom:10px}body .container .body .content div.add form .input.overlayButton a.button,body .container .body .content div.restore form .input.overlayButton a.button{padding:7px 10px;right:1px;top:9px}body .container .body .content div.add form .input.checkbox.multiple div,body .container .body .content div.restore form .input.checkbox.multiple div{display:block}body .container .body .content div.add form .input.select.multiple input#exclude-larger-than-number,body .container .body .content div.restore form .input.select.multiple input#exclude-larger-than-number{width:75px}body .container .body .content div.add form .input.select.multiple select#exclude-larger-than-multiplier,body .container .body .content div.restore form .input.select.multiple select#exclude-larger-than-multiplier{width:140px}body .container .body .content div.add form .filters .input.textarea,body .container .body .content div.restore form .filters .input.textarea{padding-bottom:10px}body .container .body .content div.add form .filters h3,body .container .body .content div.restore form .filters h3{margin:5px 0}body .container .body .content div.add form .input.text.select.multiple.repeat label,body .container .body .content div.restore form .input.text.select.multiple.repeat label{float:none}body .container .body .content div.add form .input.text.select.multiple.repeat input#repeatRunNumber,body .container .body .content div.restore form .input.text.select.multiple.repeat input#repeatRunNumber{width:70px}body .container .body .content div.add form .input.text.select.multiple.repeat select#repeatRunMultiplier,body .container .body .content div.restore form .input.text.select.multiple.repeat select#repeatRunMultiplier{width:100px}body .container .body .content div.add form .input.multiple.text.select.maxSize input,body .container .body .content div.restore form .input.multiple.text.select.maxSize input{width:70px}body .container .body .content div.add form .input.multiple.text.select.maxSize select,body .container .body .content div.restore form .input.multiple.text.select.maxSize select{width:100px}body .container .body .content div.add form .input.multiple.text.select.keepBackups select,body .container .body .content div.restore form .input.multiple.text.select.keepBackups select{width:85px;padding:4px 6px}body .container .body .content div.add form .input.multiple.text.select.keepBackups input,body .container .body .content div.restore form .input.multiple.text.select.keepBackups input{width:60px}body .container .footer{position:static;padding:15px;line-height:normal;text-align:left;box-sizing:border-box}body .container .footer *{float:none !important;text-align:center;box-sizing:border-box}body .container .footer .about-footer{padding-right:0;display:block}body .container .footer .about-footer span{padding-left:0;padding-bottom:5px}body .container .footer .about-footer li{padding-left:0;float:none;display:inline-block;height:32px;width:32px;background-size:28px !important;border-bottom:none}body .container .footer .about-footer li:first-child{padding-bottom:0}body .container .footer .about-footer li:last-child{padding-bottom:20px}body .container .footer li,body .container .footer .donate,body .container .footer .social,body .container .footer .about-footer{padding:8px 0;border-bottom:1px #ddd solid}body .container .footer .donate{display:block}body .container .footer .donate ul li{display:inline-block;border:none;margin:0 5px}body .container .footer .donate ul li a img{margin-top:0}body .container .footer .social li{display:inline-block;border:none}body .container .footer .themelink{padding:5px 0}}@media (max-width:580px){.advancedentry .longdescription{margin-left:0}}@media (max-width:492px){ul.notification{width:auto}}@media (max-width:480px){body{font-size:15px}body .container .header .logo{padding-left:5px}body .container .header .menubutton{margin-right:5px}body .container .header .state{margin-left:5px}body .container .header .statepadding{padding-right:40px}body .container .header .menubutton{padding-left:10px}body .container .body .mainmenu{width:280px;box-sizing:border-box}body .container .body .mainmenu ul li a{font-size:22px}body .container .body .content{padding:15px}body .container .body .content div.add form .input.password .tools ul li,body .container .body .content div.restore form .input.password .tools ul li{font-size:14px}body .container .body .content div.add form .buttons a,body .container .body .content div.restore form .buttons a{float:none;text-align:center;margin-bottom:5px}body .container .body .content div.add .steps-boxes .box.browser .checklinks a,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a{float:none;margin-bottom:8px;display:block}}@media (max-width:400px){body{font-size:15px}body .container .header .menubutton{margin-right:0px;padding-left:0px;padding-right:40px}body .container .header .menubutton span{display:none}}@media (max-width:325px){body{font-size:15px}body .container .header .logo div{display:none}}@media (max-width:200px){body{font-size:15px}body .container .header .menubutton{position:static;margin-top:0px}body .container .header .action-icons-small{clear:right;margin-top:0px}} \ No newline at end of file + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}*{font-family:'Clear Sans',sans-serif}html,body{margin:0;padding:0;height:100%}h1,h2{font-weight:300;color:#81c401}h1{margin:10px 0}h3{font-weight:400}a{text-decoration:none}ul{list-style:none;margin:0;padding:0}hr{border:none;border-bottom:1px #ddd solid}textarea{max-width:94%}.external-link-image{display:inline-block;margin-left:8px;margin-right:8px;height:16px;width:16px;background:url('../img/external-link-hover.png');background-size:16px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.external-link-image{background-image:url('../img/external-link-hover_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.external-link-image{background-image:url('../img/external-link-hover_3x.png')}}a .external-link-image{background:url('../img/external-link.png');background-size:16px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){a .external-link-image{background-image:url('../img/external-link_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){a .external-link-image{background-image:url('../img/external-link_3x.png')}}.header a:hover .external-link-image{background:url('../img/external-link-hover.png');background-size:16px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.header a:hover .external-link-image{background-image:url('../img/external-link-hover_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.header a:hover .external-link-image{background-image:url('../img/external-link-hover_3x.png')}}.button{display:block;background:#65b1dd;color:white !important;padding:5px 15px;float:right;margin-left:10px;cursor:pointer;width:auto;border:none;font-family:'Clear Sans',sans-serif;font-size:16px;font-weight:300;border-radius:0}.button:hover{background:#3b9cd4}.step3 source-folder-picker,#folder_path_picker,#restore_file_picker{display:block;border:1px solid lightgray;padding:2px;height:100%;overflow:auto}.not-clickable{cursor:default !important}.not-clickable>a,.not-clickable span,.not-clickable div{cursor:default !important}.ui-match{font-weight:bold;color:darkgreen}wait-area{min-width:350px;text-align:center;display:block}.prewrapped-text{white-space:pre-wrap}.exceptiontext{background-color:lightgray;color:black}.backup-result{width:90%;display:grid;grid-template-columns:50% 50%;grid-auto-rows:minmax(50px, auto);margin:0 auto}.backup-result div .horizontal-rule{width:100%;border-bottom:1px solid #d8d8d8;margin:5px 0 5px 0}.backup-result .box{margin:10px;margin-bottom:0px}.backup-result .title{color:#609101;font-weight:bold;font-size:30px}.backup-result .item{display:block}.backup-result .item .key{color:#81c401;font-weight:bold}.backup-result .item .value{color:#8f8f8f}.backup-result .item .expanded{padding:0 10px 0 18px;margin-bottom:10px}.backup-result .one{border-right:1px solid #d8d8d8;grid-column:1;grid-row:1}.backup-result .two{grid-column:2;grid-row:1}.backup-result .wide{grid-column:span 2;border-top:1px solid #d8d8d8;padding-top:10px}.backup-result .three{grid-row:2}.backup-result .four{grid-row:3;margin-bottom:10px}.success-color{color:#339900}.error-color{color:#cc0000}.warning-color{color:#ffcc00}ul.tabs{margin-bottom:10px}ul.tabs>li{display:inline;margin-right:10px;border:1px solid #65b1dd;padding:5px}ul.tabs>li.active{background-color:#65b1dd;color:white}ul.tabs>li.active>a{background-color:#65b1dd;color:white}ul.tabs>li.active.disabled{border:1px solid lightgray;background-color:lightgray;color:grey;cursor:default}ul.tabs>li.active.disabled>a{background-color:lightgray;color:grey;cursor:default}.licenses>ul{list-style:initial;margin:10px;margin-left:20px}.licenses li{margin-bottom:10px}.licenses a.itemlink{font-weight:bold}.logpage ul.entries{list-style:initial;margin:10px;margin-left:20px}.logpage .entries div.entryline.clickable{cursor:pointer}.logpage .entries.livedata li.expanded{height:auto}.logpage .button{text-align:center;margin-right:10px;border:1px solid #65b1dd;padding:5px;background-color:#65b1dd;color:white;cursor:pointer}.exportpage .checkbox input{width:auto;margin-top:10px}.exportpage .commandline div{background-color:lightgray;color:black}.themelink{margin-left:20px}ul.notification{position:fixed;bottom:0px;left:0px;right:0px;margin:auto;width:480px}.notification .title{border:1px solid #65b1dd;background-color:#65b1dd;border-top-left-radius:5px;border-top-right-radius:5px;border-bottom-left-radius:0px;border-bottom-right-radius:0px;padding:2px;padding-left:5px;padding-right:5px;font-weight:bold;color:lightgray;width:100%;text-align:center;clear:both}.notification .content{background-color:white;border:1px solid #65b1dd;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:5px;border-bottom-right-radius:5px;padding:2px;padding-left:5px;padding-right:5px;width:100%}.notification .message{width:100%;color:black}.notification .button{padding:2px 10px;margin-top:6px}.notification .clear{clear:right;height:1px}.notification .error .title{border-color:red;background-color:red}.notification .error .content{border-color:red}.notification .error .button{border-color:red;background-color:red}.notification .warning .title{background-color:orange;border-color:orange}.notification .warning .button{background-color:orange;border-color:orange}.notification .warning .content{border-color:orange}.filepicker{height:200px}.resizable{margin-bottom:6px;max-width:100%}.advanced-toggle{float:right;margin-right:25px;line-height:37px}.advancedoptions li{clear:both;margin-bottom:10px;padding:10px 0;border-top:1px lightgray solid}.advancedentry .multiple{display:inline}.advancedentry .shortname{font-weight:bold}.advancedentry input[type="text"]{width:300px}.advancedentry select{width:300px}.advancedentry input[type="checkbox"]{margin-top:13px;width:auto}.advancedentry .longdescription{margin-left:190px;clear:both;font-style:italic}.settings div.sublabel{clear:both;padding:0 31px;font-style:italic}.logo img.mainlogo{height:64px;width:64px;float:left;padding-right:8px;padding-top:2px}.logo div.logotext{float:left}.logo a{float:left;display:block;line-height:normal}.logo div.build-suffix{clear:both;display:inline;float:left;font-size:16px;line-height:16px}.logo div.powered-by{font-size:16px;margin:0px;line-height:16px;float:left;padding:0px;margin-left:5px}.fixed-width-font{font-family:monospace}.warning{margin:10px;font-style:italic;color:#f49b42}div.captcha .details{padding-top:10px;margin-left:auto;margin-right:auto;width:180px}.centered-text{text-align:center}body{color:#8f8f8f}body .container{min-height:100%;position:relative}body .container .header{line-height:70px;background:#ededed;overflow:hidden;height:70px;position:fixed;top:0px;left:0px;right:0px;z-index:100}body .container .header a{color:#65b1dd}body .container .header a:hover,body .container .header a.active{color:#4f4f4f}body .container .header .logo{font-size:30px;font-weight:700;float:left;padding-left:40px}body .container .header .statepadding{padding-right:90px;margin-left:320px}body .container .header .state{float:left;color:#609101;width:595px;padding:13px 15px;margin:10px 20px;border:1px #609101 solid;font-weight:300;font-size:18px;overflow:hidden;line-height:normal;display:inline-block;background-color:white;text-overflow:ellipsis;position:relative;height:25px}body .container .header .state strong{display:inline;margin-right:10px}body .container .header .state span{display:inline}body .container .header .state .button{position:static;margin-top:70px}body .container .header .state .content{position:relative;z-index:10;margin-right:40px;display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}body .container .header .state .buttons{position:absolute;right:0px;top:0px;bottom:0px;width:26px;margin:13px 15px}body .container .header .state .buttons .stop{display:block;width:26px;height:26px;background:url('../img/progress-stop.png');background-size:26px;cursor:pointer}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .state .buttons .stop{background-image:url('../img/progress-stop_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .state .buttons .stop{background-image:url('../img/progress-stop_3x.png')}}body .container .header .state .buttons .resume{display:block;width:26px;height:26px;background:url('../img/progress-resume.png');background-size:26px;cursor:pointer}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .state .buttons .resume{background-image:url('../img/progress-resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .state .buttons .resume{background-image:url('../img/progress-resume_3x.png')}}body .container .header .state .progress-bar{position:absolute;top:0px;bottom:0px;left:0px;background:rgba(129,196,1,0.25);z-index:5}body .container .header .state .task-name{overflow:hidden;text-overflow:ellipsis;cursor:help}body .container .header .state .task-state-info{display:flex}body .container .header .action-icons{display:inline-block;line-height:normal;margin:10px 0px;padding:13px 0px;float:left}body .container .header .action-icons-small{display:none;float:right;margin-top:21px;line-height:normal}body .container .header .action-icons>.pause,body .container .header .action-icons-small>.pause{width:26px;height:26px;display:inline-block;cursor:pointer;background:url('../img/pause.png');background-size:26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .action-icons>.pause,body .container .header .action-icons-small>.pause{background-image:url('../img/pause_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .action-icons>.pause,body .container .header .action-icons-small>.pause{background-image:url('../img/pause_3x.png')}}body .container .header .action-icons>.pause.active,body .container .header .action-icons-small>.pause.active{background:url('../img/resume.png');background-size:26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .action-icons>.pause.active,body .container .header .action-icons-small>.pause.active{background-image:url('../img/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .action-icons>.pause.active,body .container .header .action-icons-small>.pause.active{background-image:url('../img/resume_3x.png')}}body .container .header .action-icons>.throttle,body .container .header .action-icons-small>.throttle{width:26px;height:26px;display:inline-block;cursor:pointer;background:url('../img/throttle.png');background-size:26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .header .action-icons>.throttle,body .container .header .action-icons-small>.throttle{background-image:url('../img/throttle_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .header .action-icons>.throttle,body .container .header .action-icons-small>.throttle{background-image:url('../img/throttle_3x.png')}}body .container .header .action-icons>.throttle.inactive,body .container .header .action-icons-small>.throttle.inactive{opacity:.5}body .container .header .about-header{float:right;padding-right:20px;overflow:hidden}body .container .header .about-header ul{overflow:hidden;list-style:none}body .container .header .about-header ul li{float:right;padding-right:20px}body .container .header .donate{float:right}body .container .header .donate ul{overflow:hidden;float:right;padding-left:20px;padding-right:10px}body .container .header .donate ul li{float:right;margin-right:10px;padding-top:5px}body .container .header .donate img{opacity:.6}body .container .header .donate img:hover{opacity:1}body .container .body{width:100%;overflow:hidden;min-height:500px;padding-top:120px;padding-bottom:70px}body .container .body a{color:#65b1dd}body .container .body .mainmenu{width:260px;padding-left:40px;float:left;position:fixed}body .container .body .mainmenu>ul>li{position:relative}body .container .body .mainmenu>ul>li>a{font-size:22px;font-weight:300;padding:5px 10px 5px 55px;display:block}body .container .body .mainmenu>ul>li>a:hover{color:white}body .container .body .mainmenu>ul>li>a.active{color:white}body .container .body .mainmenu>ul>li>a.add{background:url('../img/mainmenu/add.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.add{background-image:url('../img/mainmenu/add_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.add{background-image:url('../img/mainmenu/add_3x.png')}}body .container .body .mainmenu>ul>li>a.restore{background:url('../img/mainmenu/restore.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.restore{background-image:url('../img/mainmenu/restore_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.restore{background-image:url('../img/mainmenu/restore_3x.png')}}body .container .body .mainmenu>ul>li>a.resume{background:url('../img/mainmenu/resume.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.resume{background-image:url('../img/mainmenu/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.resume{background-image:url('../img/mainmenu/resume_3x.png')}}body .container .body .mainmenu>ul>li>a.settings{background:url('../img/mainmenu/settings.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.settings{background-image:url('../img/mainmenu/settings_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.settings{background-image:url('../img/mainmenu/settings_3x.png')}}body .container .body .mainmenu>ul>li>a.logout{background:url('../img/mainmenu/logout.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.logout{background-image:url('../img/mainmenu/logout_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.logout{background-image:url('../img/mainmenu/logout_3x.png')}}body .container .body .mainmenu>ul>li>a.home{background:url('../img/mainmenu/home.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.home{background-image:url('../img/mainmenu/home_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.home{background-image:url('../img/mainmenu/home_3x.png')}}body .container .body .mainmenu>ul>li>a.about{background:url('../img/mainmenu/about.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.about{background-image:url('../img/mainmenu/about_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.about{background-image:url('../img/mainmenu/about_3x.png')}}body .container .body .mainmenu>ul>li>a.home.active{background:#a4d1eb url('../img/mainmenu/over/home.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.home.active{background-image:url('../img/mainmenu/over/home_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.home.active{background-image:url('../img/mainmenu/over/home_3x.png')}}body .container .body .mainmenu>ul>li>a.add.active{background:#a4d1eb url('../img/mainmenu/over/add.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.add.active{background-image:url('../img/mainmenu/over/add_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.add.active{background-image:url('../img/mainmenu/over/add_3x.png')}}body .container .body .mainmenu>ul>li>a.restore.active{background:#a4d1eb url('../img/mainmenu/over/restore.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.restore.active{background-image:url('../img/mainmenu/over/restore_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.restore.active{background-image:url('../img/mainmenu/over/restore_3x.png')}}body .container .body .mainmenu>ul>li>a.resume.active{background:#a4d1eb url('../img/mainmenu/over/resume.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.resume.active{background-image:url('../img/mainmenu/over/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.resume.active{background-image:url('../img/mainmenu/over/resume_3x.png')}}body .container .body .mainmenu>ul>li>a.settings.active{background:#a4d1eb url('../img/mainmenu/over/settings.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.settings.active{background-image:url('../img/mainmenu/over/settings_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.settings.active{background-image:url('../img/mainmenu/over/settings_3x.png')}}body .container .body .mainmenu>ul>li>a.about.active{background:#a4d1eb url('../img/mainmenu/over/about.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.about.active{background-image:url('../img/mainmenu/over/about_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.about.active{background-image:url('../img/mainmenu/over/about_3x.png')}}body .container .body .mainmenu>ul>li>a.add:hover{background:#65b1dd url('../img/mainmenu/over/add.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.add:hover{background-image:url('../img/mainmenu/over/add_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.add:hover{background-image:url('../img/mainmenu/over/add_3x.png')}}body .container .body .mainmenu>ul>li>a.restore:hover{background:#65b1dd url('../img/mainmenu/over/restore.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.restore:hover{background-image:url('../img/mainmenu/over/restore_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.restore:hover{background-image:url('../img/mainmenu/over/restore_3x.png')}}body .container .body .mainmenu>ul>li>a.resume:hover{background:#65b1dd url('../img/mainmenu/over/resume.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.resume:hover{background-image:url('../img/mainmenu/over/resume_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.resume:hover{background-image:url('../img/mainmenu/over/resume_3x.png')}}body .container .body .mainmenu>ul>li>a.settings:hover{background:#65b1dd url('../img/mainmenu/over/settings.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.settings:hover{background-image:url('../img/mainmenu/over/settings_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.settings:hover{background-image:url('../img/mainmenu/over/settings_3x.png')}}body .container .body .mainmenu>ul>li>a.logout:hover{background:#65b1dd url('../img/mainmenu/over/logout.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.logout:hover{background-image:url('../img/mainmenu/over/logout_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.logout:hover{background-image:url('../img/mainmenu/over/logout_3x.png')}}body .container .body .mainmenu>ul>li>a.home:hover{background:#65b1dd url('../img/mainmenu/over/home.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.home:hover{background-image:url('../img/mainmenu/over/home_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.home:hover{background-image:url('../img/mainmenu/over/home_3x.png')}}body .container .body .mainmenu>ul>li>a.about:hover{background:#65b1dd url('../img/mainmenu/over/about.png') no-repeat 8px 7px;background-size:27px 26px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .mainmenu>ul>li>a.about:hover{background-image:url('../img/mainmenu/over/about_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .mainmenu>ul>li>a.about:hover{background-image:url('../img/mainmenu/over/about_3x.png')}}body .container .body .mainmenu>ul li.hr-top{padding-top:25px;margin-top:25px;border-top:1px #ededed solid}body .container .body div.contextmenu_container{position:relative}body .container .body .contextmenu{display:none;position:absolute;background:white;border:1px #ededed solid;box-shadow:0 4px 8px rgba(0,0,0,0.3);z-index:200;padding:5px}body .container .body .contextmenu li a{color:#65b1dd;font-size:15px;font-weight:400;padding:0;display:block;min-width:200px;padding:4px 10px;white-space:nowrap;padding-left:45px;overflow:hidden;text-overflow:ellipsis}body .container .body .contextmenu li a:hover{background:#65b1dd;color:white}body .container .body .contextmenu.open{display:block}body .container .body .content{float:left;padding-left:350px;padding-bottom:50px;max-width:70%}body .container .body .content ul.tabs>li{display:inline-block}body .container .body .content .tasks .tasklist .task{border-top:1px solid #eee;padding-top:20px;margin-bottom:25px}body .container .body .content .tasks .tasklist .task:last-child{border-bottom:1px solid #eee;padding-bottom:20px}body .container .body .content .tasks .tasklist .task:first-child{padding-top:0px;border-top:0px none}body .container .body .content .tasks .tasklist .progress-small{text-align:center;height:18px;background:rgba(164,209,235,0.5)}body .container .body .content .tasks .tasklist .progress-small-bg{border:1px #65b1dd solid;width:200px}body .container .body .content .tasks .tasklist a{font-size:30px;font-weight:300;display:inline-block}body .container .body .content .tasks .tasklist a.action-link{font-size:14px;background:none;padding-left:0px}body .container .body .content .tasks .tasklist dl{padding-left:55px;overflow:hidden;font-size:14px}body .container .body .content .tasks .tasklist dl dt,body .container .body .content .tasks .tasklist dl dd{display:block;float:left}body .container .body .content .tasks .tasklist dl dt{clear:both;font-weight:500;margin-bottom:5px}body .container .body .content .tasks .tasklist dl dd{margin-left:10px}body .container .body .content .tasks .tasklist dl.taskmenu p{display:inline;margin-right:10px;color:#65b1dd;cursor:pointer}body .container .body .content .tasks .tasklist dl.taskmenu dt{float:left;margin-right:10px;margin-bottom:0;padding:5px 8px;color:#8f8f8f;cursor:pointer;clear:none}body .container .body .content .tasks .tasklist dl.taskmenu dd{clear:both;float:none;padding-bottom:8px;border-bottom:1px #ddd solid;margin-bottom:5px}body .container .body .content div.add .steps,body .container .body .content div.restore .steps{width:100%;overflow:hidden}body .container .body .content div.add .steps .step,body .container .body .content div.restore .steps .step{float:left;background:url('../img/steps/line-out.png') no-repeat top left;background-size:485px 24px;color:#c7e5f6}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){body .container .body .content div.add .steps .step,body .container .body .content div.restore .steps .step{background-image:url('../img/steps/line-out_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){body .container .body .content div.add .steps .step,body .container .body .content div.restore .steps .step{background-image:url('../img/steps/line-out_3x.png')}}body .container .body .content div.add .steps .step span,body .container .body .content div.restore .steps .step span{display:block;border:4px #c7e5f6 solid;background:white;border-radius:50%;width:35px;height:35px;text-align:center;font-size:22px;line-height:35px;cursor:pointer}body .container .body .content div.add .steps .step.active,body .container .body .content div.restore .steps .step.active{color:#65b1dd}body .container .body .content div.add .steps .step.active span,body .container .body .content div.restore .steps .step.active span{border:4px #65b1dd solid;background:#65b1dd;color:white}body .container .body .content div.add .steps .step.active h2,body .container .body .content div.restore .steps .step.active h2{color:#65b1dd}body .container .body .content div.add .steps .step:first-child,body .container .body .content div.restore .steps .step:first-child{padding-left:0;background:transparent}body .container .body .content div.add .steps-legend,body .container .body .content div.restore .steps-legend{overflow:hidden;padding-bottom:50px;list-style:none;margin:0}body .container .body .content div.add .steps-legend li,body .container .body .content div.restore .steps-legend li{color:#c7e5f6;font-size:18px;text-align:center;float:left;padding-top:10px;cursor:pointer}body .container .body .content div.add .steps-legend li.active,body .container .body .content div.restore .steps-legend li.active{color:#65b1dd}body .container .body .content div.add .steps-boxes,body .container .body .content div.restore .steps-boxes{padding-left:40px}body .container .body .content div.add .steps-boxes .step,body .container .body .content div.restore .steps-boxes .step{display:none}body .container .body .content div.add .steps-boxes .step.active,body .container .body .content div.restore .steps-boxes .step.active{display:block}body .container .body .content div.add .steps-boxes .box.browser .checklinks a,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a{float:left;margin-left:20px;color:#8f8f8f}body .container .body .content div.add .steps-boxes .box.browser .checklinks a i,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a i{border:2px solid;border-color:#8f8f8f;border-radius:2px;font-size:18px;height:18px;vertical-align:top;width:18px}body .container .body .content div.add .steps-boxes .box.browser .checklinks a.inactive,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a.inactive{color:#c2c2c2;cursor:default}body .container .body .content div.add .steps-boxes .box.browser .checklinks a.inactive i,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a.inactive i{border-color:#c2c2c2}body .container .body .content div.add .steps-boxes .box.browser .checklinks a:first-child,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a:first-child{margin-left:0}body .container .body .content div.add .steps-boxes .box.browser .input.overlayButton,body .container .body .content div.restore .steps-boxes .box.browser .input.overlayButton{padding-top:10px;max-width:100%}body .container .body .content div.add .steps-boxes .box.browser .input.overlayButton input#sourcePath,body .container .body .content div.restore .steps-boxes .box.browser .input.overlayButton input#sourcePath{width:100%;box-sizing:border-box;height:37px}body .container .body .content div.add .steps-boxes .box.browser .input.overlayButton a.button,body .container .body .content div.restore .steps-boxes .box.browser .input.overlayButton a.button{top:10px}body .container .body .content div.add .steps-boxes .box.filters .input.link a,body .container .body .content div.restore .steps-boxes .box.filters .input.link a{color:#8f8f8f}body .container .body .content div.add .steps-boxes .box.filters .input.link a i,body .container .body .content div.restore .steps-boxes .box.filters .input.link a i{border:2px solid;border-color:#8f8f8f;border-radius:2px;font-size:18px;height:18px;vertical-align:top;width:18px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist{overflow:hidden;padding-bottom:15px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist li,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist li{overflow:hidden;clear:both;padding-bottom:5px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist li select,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist li select{width:200px;margin-right:5px;height:36px;line-height:36px}body .container .body .content div.add .steps-boxes .box.filters ul#simplefilterlist li input,body .container .body .content div.restore .steps-boxes .box.filters ul#simplefilterlist li input{width:280px;padding:5px}body .container .body .content div.add .steps-boxes .step1 li.strength.score-0,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-0{color:#ff0000}body .container .body .content div.add .steps-boxes .step1 li.strength.score-1,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-1{color:#ff7700}body .container .body .content div.add .steps-boxes .step1 li.strength.score-2,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-2{color:#aaaa00}body .container .body .content div.add .steps-boxes .step1 li.strength.score-3,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-3{color:#007700}body .container .body .content div.add .steps-boxes .step1 li.strength.score-4,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-4{color:#427e27}body .container .body .content div.add .steps-boxes .step1 li.strength.score-x,body .container .body .content div.restore .steps-boxes .step1 li.strength.score-x{color:#ff0000}body .container .body .content div.add .steps-boxes .step5 div.input.maxSize input.number,body .container .body .content div.restore .steps-boxes .step5 div.input.maxSize input.number,body .container .body .content div.add .steps-boxes .step5 div.input.keepBackups input.number,body .container .body .content div.restore .steps-boxes .step5 div.input.keepBackups input.number{width:60px}body .container .body .content div.add .steps-boxes .step5 .hint,body .container .body .content div.restore .steps-boxes .step5 .hint,body .container .body .content div.add .steps-boxes .step5 .retention-options,body .container .body .content div.restore .steps-boxes .step5 .retention-options{clear:both;margin-left:190px;margin-top:50px;font-style:italic}body .container .body .content div.add .steps-boxes .step5 .retention-options input,body .container .body .content div.restore .steps-boxes .step5 .retention-options input{margin-bottom:10px}body .container .body .content div.add .steps-boxes .step5 .advancedoptions,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions{padding-top:15px;clear:both}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li{border-top:none}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li>a,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li>a{display:block;background:#65b1dd;color:white !important;padding:5px 15px;float:right;margin-left:10px;cursor:pointer;width:auto;border:none;font-family:'Clear Sans',sans-serif;font-size:16px;font-weight:300;border-radius:0}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li.advancedentry,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li.advancedentry{border-bottom:1px solid lightgray}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li:last-child,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li:last-child{padding-top:0}body .container .body .content div.add .steps-boxes .step5 .advancedoptions li:last-child select,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions li:last-child select{max-width:400px}body .container .body .content div.add .steps-boxes .step5 .advancedoptions label,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions label{line-height:normal}body .container .body .content div.add .steps-boxes .step5 .advancedoptions input,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions input,body .container .body .content div.add .steps-boxes .step5 .advancedoptions select,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions select{width:auto;max-width:100%;box-sizing:border-box}body .container .body .content div.add .steps-boxes .step5 .advancedoptions .longdescription,body .container .body .content div.restore .steps-boxes .step5 .advancedoptions .longdescription{margin-top:10px}body .container .body .content div.add .steps-boxes .step5 .advanced-toggle,body .container .body .content div.restore .steps-boxes .step5 .advanced-toggle{color:#8f8f8f;line-height:normal;margin-top:16px;clear:both;float:left}body .container .body .content div.add .steps-boxes .step5 .advanced-toggle i.fa,body .container .body .content div.restore .steps-boxes .step5 .advanced-toggle i.fa{border:2px solid;border-color:#8f8f8f;border-radius:2px;font-size:18px;height:18px;vertical-align:top;width:18px}body .container .body .content div.add .steps-boxes .step5 textarea,body .container .body .content div.restore .steps-boxes .step5 textarea{box-sizing:border-box;clear:both;margin-top:15px;width:100%}body .container .body .content div.add form,body .container .body .content div.restore form{padding-bottom:50px;overflow:hidden}body .container .body .content div.add form .input.password .tools,body .container .body .content div.restore form .input.password .tools{clear:both;padding-left:190px;padding-top:10px}body .container .body .content div.add form .input.password .tools ul,body .container .body .content div.restore form .input.password .tools ul{overflow:hidden}body .container .body .content div.add form .input.password .tools ul li,body .container .body .content div.restore form .input.password .tools ul li{float:left;padding-right:7px}body .container .body .content div.add form .input.password .tools ul li.strength.useless,body .container .body .content div.restore form .input.password .tools ul li.strength.useless{color:red}body .container .body .content div.add form .input.password .tools ul li.strength.average,body .container .body .content div.restore form .input.password .tools ul li.strength.average{color:yellow}body .container .body .content div.add form .input.password .tools ul li.strength.good,body .container .body .content div.restore form .input.password .tools ul li.strength.good{color:#65b1dd}body .container .body .content div.add form .input.multiple input,body .container .body .content div.restore form .input.multiple input,body .container .body .content div.add form .input.multiple select,body .container .body .content div.restore form .input.multiple select{width:auto;margin-right:5px}body .container .body .content div.add form .input.multiple select,body .container .body .content div.restore form .input.multiple select{padding:5px 12px}body .container .body .content div.add form .input.overlayButton,body .container .body .content div.restore form .input.overlayButton{overflow:hidden;position:relative;max-width:446px}body .container .body .content div.add form .input.overlayButton input,body .container .body .content div.restore form .input.overlayButton input{width:347px}body .container .body .content div.add form .input.overlayButton a.button,body .container .body .content div.restore form .input.overlayButton a.button{position:absolute;top:0;right:0;padding:7px 12px 8px}body .container .body .content div.add form .input.checkbox.multiple strong,body .container .body .content div.restore form .input.checkbox.multiple strong{display:block;padding-bottom:5px}body .container .body .content div.add form .input.checkbox.multiple label,body .container .body .content div.restore form .input.checkbox.multiple label{display:inline-block;float:none;width:auto;padding-right:10px}body .container .body .content div.add form .input.checkbox.multiple input,body .container .body .content div.restore form .input.checkbox.multiple input{width:auto;display:inline-block;float:none}body .container .body .content div.add form .buttons,body .container .body .content div.restore form .buttons{float:none;width:635px;padding-top:30px}body .container .body .content div.add .steps{margin-left:48.5px}body .container .body .content div.add .steps .step{padding-left:97px}body .container .body .content div.add .steps-legend{padding-left:0}body .container .body .content div.add .steps-legend li{width:140px}body .container .body .content div.restore .steps{margin-left:153.5px}body .container .body .content div.restore .steps .step{padding-left:307px}body .container .body .content div.restore .steps-legend{padding-left:0}body .container .body .content div.restore .steps-legend li{width:350px}body .container .body .content div.restore.restore-direct .steps{margin-left:66px}body .container .body .content div.restore.restore-direct .steps .step{padding-left:132px}body .container .body .content div.restore.restore-direct .steps-legend{padding-left:0}body .container .body .content div.restore.restore-direct .steps-legend li{width:175px}body .container .body .content div.restore.restore-direct .step:first-child{padding-left:0;background:transparent}body .container .body .content div.restore.restore-direct .steps-legend{padding-left:20px}body .container .body .content div.headerthreedotmenu{margin:20px 0 20px 0}body .container .body .content div.headerthreedotmenu h2{display:inline}body .container .body .content div.headerthreedotmenu .contextmenu_container{float:right}body .container .body .content div.headerthreedotmenu .contextmenu{left:auto;right:0;top:auto}body .container .body .content div.headerthreedotmenu .threedotmenubutton{padding:5px}body .container .body .content .expandable{margin:20px 0 20px 0}body .container .body .content .expandable h2{display:inline}body .container .body .content .expandable img{padding:0 6px}body .container .body .content div.settings .input.mixed.multiple input.checkbox,body .container .body .content div.settings .input.checkbox input.checkbox{width:auto}body .container .body .content div.settings .input.mixed.multiple select,body .container .body .content div.settings .input.checkbox select{width:auto;margin-right:5px}body .container .body .content div.settings .input.mixed.multiple label,body .container .body .content div.settings .input.checkbox label{line-height:normal;padding:0 15px;width:auto}body .container .body .content .logpage ul.tabs{padding:15px 0}body .container .body .content .logpage ul.entries li{padding:10px 0 10px 0;border-bottom:1px solid #d8d8d8}body .container .body .content .logpage ul.backuplog{list-style:none}body .container .body .content .prewrapped-text{white-space:pre-wrap;overflow-x:auto}body .container .footer{background:#ededed;min-height:70px;line-height:70px;overflow:hidden;position:absolute;bottom:0;width:100%}body .container .footer a{color:#65b1dd}body .container .footer .about-footer{float:left;overflow:hidden;padding-right:20px;display:none}body .container .footer .about-footer span{display:block;float:left;padding-left:20px}body .container .footer .about-footer ul{float:left}body .container .footer .about-footer li{float:left;padding-left:20px}body .container .footer .donate{display:none;float:right;padding-right:40px;overflow:hidden}body .container .footer .donate ul{overflow:hidden;float:right}body .container .footer .donate ul li{float:left;margin-left:10px}body .container .footer .donate ul li a img{margin-top:24px;display:inline-block;opacity:.6}body .container .footer .donate ul li a img:hover{opacity:1}body .container .footer .donate>a{float:left}body .container .footer .social{float:right}body .container .footer .social ul{overflow:hidden;float:right;padding-left:20px;padding-right:10px}body .container .footer .social ul li{float:right;margin-right:10px;padding-top:5px}body .container .footer .social ul li img{opacity:.6}body .container .footer .social ul li img:hover{opacity:1}body .container .footer .themelink{float:right;padding-right:20px}body #modal-menu{max-width:400px}body #modal-menu a{color:#65b1dd;font-size:20px;line-height:40px}.remodal{padding:30px;box-shadow:0 2px 7px rgba(0,0,0,0.3);background:white;display:none}.remodal form .buttons{float:none}.remodal-wrapper .remodal{display:block}span.info{font-size:10px;font-weight:500;display:inline-block;background:#65b1dd;border-radius:50%;width:15px;height:15px;vertical-align:super;color:white;line-height:15px;margin-left:5px;text-align:center}.hidden{display:none}.clear{clear:both}.nofloat{float:none !important}div.blocker,div.connection-lost,div.modal-dialog{position:fixed;top:0px;left:0px;right:0px;bottom:0px;margin:auto}div.blocker{z-index:5000;background-color:#000000;opacity:.65}#connection-lost-blocker{z-index:5100}#connection-lost-dialog{z-index:5200}div.connection-lost,div.modal-dialog{z-index:5001;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-webkit-box-pack:center;-moz-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-box-align:center;-moz-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}div.connection-lost div.info,div.modal-dialog div.info{min-width:310px;max-width:650px;margin:5px}div.connection-lost div.title,div.modal-dialog div.title{border:1px solid #65b1dd;background-color:#65b1dd;border-radius:5px 5px 0 0;padding:10px 20px;font-weight:bold;color:lightgray;text-align:center}div.connection-lost div.content,div.modal-dialog div.content{background-color:white;border:1px solid white;padding:20px}div.connection-lost .buttons,div.modal-dialog .buttons{border-radius:0 0 5px 5px;padding-top:10px;overflow:auto}div.connection-lost form,div.modal-dialog form{margin-top:15px}div.connection-lost form textarea,div.modal-dialog form textarea{height:130px;width:420px;padding:10px 12px;border:1px #d8d8d8 solid;border-radius:2px;color:#8f8f8f;font-size:16px;font-weight:300}div.connection-lost form input,div.modal-dialog form input{height:35px;line-height:35px;padding:0 12px}div.modal-dialog .content.buttons ul{float:right}div.modal-dialog .content.buttons .tooltipped{position:relative}div.modal-dialog .content.buttons .tooltipped:after{position:absolute;z-index:1000000;display:none;padding:5px 8px;font:normal normal 11px/1.5 Helvetica,arial,nimbussansl,liberationsans,freesans,clean,sans-serif,"Segoe UI Emoji","Segoe UI Symbol";color:#fff;text-align:center;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-wrap:break-word;white-space:pre;pointer-events:none;content:attr(aria-label);background:rgba(0,0,0,0.8);border-radius:3px;-webkit-font-smoothing:subpixel-antialiased}div.modal-dialog .content.buttons .tooltipped:before{position:absolute;z-index:1000001;display:none;width:0;height:0;color:rgba(0,0,0,0.8);pointer-events:none;content:"";border:5px solid transparent}div.modal-dialog .content.buttons .tooltipped:hover:before,div.modal-dialog .content.buttons .tooltipped:hover:after,div.modal-dialog .content.buttons .tooltipped:active:before,div.modal-dialog .content.buttons .tooltipped:active:after,div.modal-dialog .content.buttons .tooltipped:focus:before,div.modal-dialog .content.buttons .tooltipped:focus:after{display:inline-block;text-decoration:none}div.modal-dialog .content.buttons .tooltipped-w:after{right:100%;bottom:50%;margin-right:5px;-webkit-transform:translateY(50%);-ms-transform:translateY(50%);transform:translateY(50%)}div.modal-dialog .content.buttons .tooltipped-w:before{top:50%;bottom:50%;left:-5px;margin-top:-5px;border-left-color:rgba(0,0,0,0.8)}.importpage form.styled input{margin-top:11px;margin-bottom:11px}.restorewizard form.styled ul,.addwizard form.styled ul{margin:20px;margin-left:0px}.restorewizard form.styled input[type="radio"],.addwizard form.styled input[type="radio"]{width:20px;margin-left:5px;margin-right:5px}.restorewizard form.styled label,.addwizard form.styled label{width:auto;line-height:normal}.restorewizard form.styled div.subtext,.addwizard form.styled div.subtext{clear:both;margin-left:30px;padding-top:5px;color:lightgray}.pauseoptions form.styled li{line-height:normal;padding:0px}.pauseoptions form.styled li input{height:auto;margin-top:8px;margin-right:8px;width:auto}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress{position:relative;min-height:25px}.progress>span{vertical-align:middle;display:block;width:100%;height:100%;text-align:center;z-index:100;padding-top:2px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress .progress-bar{float:left;width:0;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease;height:100%;position:absolute;top:0}.progress .progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.tree-view{list-style-type:none;margin-left:10px;padding-bottom:5px}.tree-view ul{margin-left:16px}.tree-view span.nodeLabel{cursor:pointer}.tree-view span.nodeLabel.selected{border:1px solid #aaa;background-color:#ddd;padding:1px 3px}.tree-view li .node{padding-bottom:5px}.tree-view li div.selected{border-color:lightblue;background-color:lightblue}.tree-view li>ul{display:none}.tree-view li>ul.expanded{display:block}.tree-view li a.nav{cursor:pointer;display:inline-block;width:16px;height:16px;vertical-align:middle;background-image:url(../img/treeicons.png);background-repeat:no-repeat;background-position:-80px 0;background-size:112px 64px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.nav{background-image:url('../img/treeicons_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.tree-view li a.nav{background-image:url('../img/treeicons_3x.png')}}.tree-view li a.nav.leaf{background:none}.tree-view li a.nav.expanded{background-position:-80px -16px}.tree-view li a.type{cursor:auto;display:inline-block;width:16px;height:16px;vertical-align:middle;background-image:url(../img/treeicons.png);background-repeat:no-repeat;background-position:0 -16px;background-size:112px 64px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.type{background-image:url('../img/treeicons_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.tree-view li a.type{background-image:url('../img/treeicons_3x.png')}}.tree-view li a.type.invisible{background-position:0 -32px}.tree-view li a.type.loading{cursor:progress;background-image:url(../img/loader-16.gif);background-repeat:no-repeat;background-position:0px 0px;background-size:16px 16px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.type.loading{background-image:url('../img/loader-32.gif')}}.tree-view li a.type.x-tree-icon-drive{background-position:-16px -16px}.tree-view li a.type.x-tree-icon-leaf{background-position:-32px -16px}.tree-view li a.type.x-tree-icon-symlink{background-position:-48px -16px}.tree-view li a.type.x-tree-icon-userdata{background-position:-16px -48px}.tree-view li a.type.x-tree-icon-locked{background-position:-64px -16px}.tree-view li a.type.x-tree-icon-broken{background-position:-64px -16px}.tree-view li a.type.x-tree-icon-computer{background-position:0 -48px}.tree-view li a.type.x-tree-icon-hyperv{background-position:-96px -16px}.tree-view li a.type.x-tree-icon-hypervmachine{background-position:-96px 0}.tree-view li a.type.x-tree-icon-mssql{background-position:-96px -32px}.tree-view li a.type.x-tree-icon-mssqldb{background-position:-80px -32px}.tree-view li a.type.x-tree-icon-mydocuments{background-position:-32px -48px}.tree-view li a.type.x-tree-icon-mymusic{background-position:-48px -48px}.tree-view li a.type.x-tree-icon-mypictures{background-position:-64px -48px}.tree-view li a.type.x-tree-icon-desktop{background-position:-80px -48px}.tree-view li a.type.x-tree-icon-home{background-position:-96px -48px}.tree-view li a.type.x-tree-icon-drive.invisible{background-position:-16px -32px}.tree-view li a.type.x-tree-icon-leaf.invisible{background-position:-32px -32px}.tree-view li a.type.x-tree-icon-symlink.invisible{cursor:auto;background-position:-48px -32px}.tree-view li a.type.x-tree-icon-locked.invisible{background-position:-64px -32px}.tree-view li a.check{height:16px;width:16px;display:inline-block;cursor:pointer;background-image:url(../img/treeicons.png);background-repeat:no-repeat;background-position:0 0;vertical-align:middle;background-size:112px 64px}@media only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-resolution:192dpi),only screen and (min-resolution:1.25dppx){.tree-view li a.check{background-image:url('../img/treeicons_2x.png')}}@media only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-resolution:288dpi),only screen and (min-resolution:2.25dppx){.tree-view li a.check{background-image:url('../img/treeicons_3x.png')}}.tree-view li a.partial{background-position:-32px 0}.tree-view li a.include{background-position:-16px 0}.tree-view li a.exclude{background-position:-48px 0}.tree-view li a.root{background:none;display:none}.throttlesettings div.multiple select{width:auto;margin-right:5px}.throttlesettings div.multiple input{width:100px}.throttlesettings div.multiple input.checkbox{width:auto}.throttlesettings div.multiple label{line-height:35px;padding:0 15px;width:auto;min-width:150px}.throttlesettings .disabled{color:#cfcfcf}.throttlesettings .disabled input,.throttlesettings .disabled select{color:#cfcfcf}@media (max-width:1150px){body .container .header{height:140px}body .container .header .statepadding{padding-right:90px;margin-left:0px}body .container .header .state{width:100%;margin:10px 40px;clear:left;float:left}body .container .header .action-icons{display:none}body .container .header .action-icons-small{display:inline-block}body .container .header .menubutton{display:block;font-size:18px;padding-right:50px;margin-top:5px;margin-right:15px;background:url('../img/menu.png') no-repeat right top;background-size:39px 39px;position:relative;height:40px;line-height:40px;color:#8f8f8f;float:right;top:10px;padding-left:20px;text-transform:uppercase;text-align:right}body .container .header .menubutton.active{background-image:url('../img/menu_active.png');background-size:39px 39px;color:#65b1dd}body .container .header .donate{display:none}body .container .body{position:relative;padding-top:140px}body .container .body .mainmenu{display:none;position:fixed;background:none repeat scroll 0 0 white;box-shadow:0 4px 8px rgba(0,0,0,0.3);left:10px;padding:20px;top:60px}body .container .body .mainmenu.mobile-open{display:block;left:auto;right:0;top:0;z-index:1000}body .container .body .contextmenu{left:0;top:auto}body .container .body .content{float:none;padding:20px 20px;margin:0px auto 30px auto}body .container .body .content .state{width:auto}body .container .mobileOpen{display:block !important}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:1.25),(max-width:1150px) and (min-resolution:192dpi),(max-width:1150px) and (min-resolution:1.25dppx){body .container .header .menubutton{background-image:url('../img/menu_2x.png')}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:2.25),(max-width:1150px) and (min-resolution:288dpi),(max-width:1150px) and (min-resolution:2.25dppx){body .container .header .menubutton{background-image:url('../img/menu_3x.png')}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:1.25),(max-width:1150px) and (min-resolution:192dpi),(max-width:1150px) and (min-resolution:1.25dppx){body .container .header .menubutton.active{background-image:url('../img/menu_active_2x.png')}}@media (max-width:1150px) and (-webkit-min-device-pixel-ratio:2.25),(max-width:1150px) and (min-resolution:288dpi),(max-width:1150px) and (min-resolution:2.25dppx){body .container .header .menubutton.active{background-image:url('../img/menu_active_3x.png')}}@media (max-width:768px){body .container .body .content .tasks .tasklist a{font-size:20px;background-size:24px;background-position:0 4px;padding-left:35px}body .container .body .content .tasks .tasklist dl{padding-left:35px}body .container .header .logo{padding-left:10px}body .container .header .statepadding{padding-right:50px}body .container .header .state{margin-left:10px}body .container .header .menubutton{margin-right:5px}body .container .body .content div.add .steps,body .container .body .content div.restore .steps,body .container .body .content div.settings .steps{display:none}body .container .body .content div.add .steps-legend,body .container .body .content div.restore .steps-legend,body .container .body .content div.settings .steps-legend{list-style:decimal;padding-left:20px;border-bottom:1px solid #eee;margin-bottom:30px;padding-bottom:20px}body .container .body .content div.add .steps-legend li,body .container .body .content div.restore .steps-legend li,body .container .body .content div.settings .steps-legend li{float:none;font-weight:500;width:auto !important;padding-right:0 !important}body .container .body .content div.add .steps-boxes,body .container .body .content div.restore .steps-boxes,body .container .body .content div.settings .steps-boxes{padding-left:0}body .container .body .content div.add form.styled .input input,body .container .body .content div.restore form.styled .input input,body .container .body .content div.settings form.styled .input input,body .container .body .content div.add form.styled .input select,body .container .body .content div.restore form.styled .input select,body .container .body .content div.settings form.styled .input select,body .container .body .content div.add form.styled .input textarea,body .container .body .content div.restore form.styled .input textarea,body .container .body .content div.settings form.styled .input textarea{max-width:100%;box-sizing:border-box}body .container .body .content div.add form.styled .input.select select,body .container .body .content div.restore form.styled .input.select select,body .container .body .content div.settings form.styled .input.select select{width:420px}body .container .body .content div.add form.styled .buttons,body .container .body .content div.restore form.styled .buttons,body .container .body .content div.settings form.styled .buttons{max-width:100%;width:auto}body .container .body .content div.add form.styled .tools,body .container .body .content div.restore form.styled .tools,body .container .body .content div.settings form.styled .tools{padding-left:0 !important}body .container .body .content div.add form.styled .input.checkbox.multiple,body .container .body .content div.restore form.styled .input.checkbox.multiple,body .container .body .content div.settings form.styled .input.checkbox.multiple{padding-bottom:5px}body .container .body .content div.add form.styled .input.checkbox.multiple input,body .container .body .content div.restore form.styled .input.checkbox.multiple input,body .container .body .content div.settings form.styled .input.checkbox.multiple input,body .container .body .content div.add form.styled .input.checkbox.multiple label,body .container .body .content div.restore form.styled .input.checkbox.multiple label,body .container .body .content div.settings form.styled .input.checkbox.multiple label{display:block !important;float:left !important;line-height:normal}body .container .body .content div.add form.styled .input.checkbox.multiple input,body .container .body .content div.restore form.styled .input.checkbox.multiple input,body .container .body .content div.settings form.styled .input.checkbox.multiple input{clear:both}body .container .body .content div.add form.styled .input.text.multiple input,body .container .body .content div.restore form.styled .input.text.multiple input,body .container .body .content div.settings form.styled .input.text.multiple input{max-width:48% !important}}@media (max-width:640px){body h2{font-size:20px;text-align:center}body .container .body{padding-bottom:10px}body .container .body .content{margin:0px auto}body .container .body .content div.add form .input.overlayButton,body .container .body .content div.restore form .input.overlayButton{padding-top:8px;padding-bottom:30px;margin-bottom:10px}body .container .body .content div.add form .input.overlayButton a.button,body .container .body .content div.restore form .input.overlayButton a.button{padding:7px 10px;right:1px;top:9px}body .container .body .content div.add form .input.checkbox.multiple div,body .container .body .content div.restore form .input.checkbox.multiple div{display:block}body .container .body .content div.add form .input.select.multiple input#exclude-larger-than-number,body .container .body .content div.restore form .input.select.multiple input#exclude-larger-than-number{width:75px}body .container .body .content div.add form .input.select.multiple select#exclude-larger-than-multiplier,body .container .body .content div.restore form .input.select.multiple select#exclude-larger-than-multiplier{width:140px}body .container .body .content div.add form .filters .input.textarea,body .container .body .content div.restore form .filters .input.textarea{padding-bottom:10px}body .container .body .content div.add form .filters h3,body .container .body .content div.restore form .filters h3{margin:5px 0}body .container .body .content div.add form .input.text.select.multiple.repeat label,body .container .body .content div.restore form .input.text.select.multiple.repeat label{float:none}body .container .body .content div.add form .input.text.select.multiple.repeat input#repeatRunNumber,body .container .body .content div.restore form .input.text.select.multiple.repeat input#repeatRunNumber{width:70px}body .container .body .content div.add form .input.text.select.multiple.repeat select#repeatRunMultiplier,body .container .body .content div.restore form .input.text.select.multiple.repeat select#repeatRunMultiplier{width:100px}body .container .body .content div.add form .input.multiple.text.select.maxSize input,body .container .body .content div.restore form .input.multiple.text.select.maxSize input{width:70px}body .container .body .content div.add form .input.multiple.text.select.maxSize select,body .container .body .content div.restore form .input.multiple.text.select.maxSize select{width:100px}body .container .body .content div.add form .input.multiple.text.select.keepBackups select,body .container .body .content div.restore form .input.multiple.text.select.keepBackups select{width:85px;padding:4px 6px}body .container .body .content div.add form .input.multiple.text.select.keepBackups input,body .container .body .content div.restore form .input.multiple.text.select.keepBackups input{width:60px}body .container .footer{position:static;padding:15px;line-height:normal;text-align:left;box-sizing:border-box}body .container .footer *{float:none !important;text-align:center;box-sizing:border-box}body .container .footer .about-footer{padding-right:0;display:block}body .container .footer .about-footer span{padding-left:0;padding-bottom:5px}body .container .footer .about-footer li{padding-left:0;float:none;display:inline-block;height:32px;width:32px;background-size:28px !important;border-bottom:none}body .container .footer .about-footer li:first-child{padding-bottom:0}body .container .footer .about-footer li:last-child{padding-bottom:20px}body .container .footer li,body .container .footer .donate,body .container .footer .social,body .container .footer .about-footer{padding:8px 0;border-bottom:1px #ddd solid}body .container .footer .donate{display:block}body .container .footer .donate ul li{display:inline-block;border:none;margin:0 5px}body .container .footer .donate ul li a img{margin-top:0}body .container .footer .social li{display:inline-block;border:none}body .container .footer .themelink{padding:5px 0}}@media (max-width:580px){.advancedentry .longdescription{margin-left:0}}@media (max-width:492px){ul.notification{width:auto}}@media (max-width:480px){body{font-size:15px}body .container .header .logo{padding-left:5px}body .container .header .menubutton{margin-right:5px}body .container .header .state{margin-left:5px}body .container .header .statepadding{padding-right:40px}body .container .header .menubutton{padding-left:10px}body .container .body .mainmenu{width:280px;box-sizing:border-box}body .container .body .mainmenu ul li a{font-size:22px}body .container .body .content{padding:15px}body .container .body .content div.add form .input.password .tools ul li,body .container .body .content div.restore form .input.password .tools ul li{font-size:14px}body .container .body .content div.add form .buttons a,body .container .body .content div.restore form .buttons a{float:none;text-align:center;margin-bottom:5px}body .container .body .content div.add .steps-boxes .box.browser .checklinks a,body .container .body .content div.restore .steps-boxes .box.browser .checklinks a{float:none;margin-bottom:8px;display:block}}@media (max-width:400px){body{font-size:15px}body .container .header .menubutton{margin-right:0px;padding-left:0px;padding-right:40px}body .container .header .menubutton span{display:none}}@media (max-width:325px){body{font-size:15px}body .container .header .logo div{display:none}}@media (max-width:200px){body{font-size:15px}body .container .header .menubutton{position:static;margin-top:0px}body .container .header .action-icons-small{clear:right;margin-top:0px}} \ No newline at end of file From 4008736494a84bf3482a084a9e314dc638f15826 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Mon, 31 Aug 2020 19:43:38 -0700 Subject: [PATCH 14/32] Fix typo that resulted in hidden SSH fingerprint error. This fixes #4301. --- .../Server/webroot/ngax/scripts/directives/backupEditUri.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Duplicati/Server/webroot/ngax/scripts/directives/backupEditUri.js b/Duplicati/Server/webroot/ngax/scripts/directives/backupEditUri.js index 8f44f7410..d20a8105e 100644 --- a/Duplicati/Server/webroot/ngax/scripts/directives/backupEditUri.js +++ b/Duplicati/Server/webroot/ngax/scripts/directives/backupEditUri.js @@ -209,7 +209,7 @@ backupApp.directive('backupEditUri', function(gettextCatalog) { var message = ((prev || '').trim().length == 0) ? (gettextCatalog.getString('No certificate was specified previously, please verify with the server administrator that the key is correct: {{key}} \n\nDo you want to approve the reported host key?', { key: key })) : - (gettextCatalog.getString('The host key has changed, please check with the server administrator if this is correct, otherwise you could be the victim of a MAN-IN-THE-MIDDLE attack.\n\nDo you want to REPLACE your CURRENT host key "{{prev}}" with the REPORTED host key: {{key}}?', { prev: pref, key: key })); + (gettextCatalog.getString('The host key has changed, please check with the server administrator if this is correct, otherwise you could be the victim of a MAN-IN-THE-MIDDLE attack.\n\nDo you want to REPLACE your CURRENT host key "{{prev}}" with the REPORTED host key: {{key}}?', { prev: prev, key: key })); DialogService.dialog(gettextCatalog.getString('Trust host certificate?'), message, [gettextCatalog.getString('No'), gettextCatalog.getString('Yes')], function(ix) { if (ix == 1) { From dd902ce7b24b6738aca009aa2697bcb71cc478b5 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 3 Sep 2020 19:48:47 -0700 Subject: [PATCH 15/32] Remove misleading comment. The retention policy logic was changed, and this comment was leftover from the old implementation. https://forum.duplicati.com/t/new-retention-policy-deletes-old-backups-in-a-smart-way/2195/34?u=ts678 This fixes #3306. --- Duplicati/Library/Main/Operation/DeleteHandler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Duplicati/Library/Main/Operation/DeleteHandler.cs b/Duplicati/Library/Main/Operation/DeleteHandler.cs index bb1cc8f88..c3693a1fe 100644 --- a/Duplicati/Library/Main/Operation/DeleteHandler.cs +++ b/Duplicati/Library/Main/Operation/DeleteHandler.cs @@ -281,7 +281,6 @@ namespace Duplicati.Library.Main.Operation /// /// Remove backups according to the --retention-policy option. - /// Backups that are not within any of the specified time frames will will NOT be deleted. /// Partial backups are not removed. /// public class RetentionPolicyRemover : FilesetRemover From be6ac941ea547c56bc6c46a702ce2abb78b51cad Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Mon, 7 Sep 2020 20:22:45 -0700 Subject: [PATCH 16/32] Add symlink policy tests This adds a basic symlink policy test that performs backups using each symlink policy. If you don't have the required privilege in Windows, you can get an exception trying to create a symbolic link. If an exception is thrown trying to create a symbolic link, the exception is trapped and the test is marked as "Ignored"; e.g., ``` 1) Ignored : Duplicati.UnitTest.SymLinkTests.SymLinkPolicy Client could not create a symbolic link. Error reported: (1314) A required privilege is not held by the client. | Read: [C:\td\backup-data\target] | Write: [C:\td\backup-data\symlink] ``` --- Duplicati/UnitTest/BasicSetupHelper.cs | 12 ++ Duplicati/UnitTest/Duplicati.UnitTest.csproj | 1 + Duplicati/UnitTest/ProblematicPathTests.cs | 8 - Duplicati/UnitTest/SymLinkTests.cs | 157 +++++++++++++++++++ 4 files changed, 170 insertions(+), 8 deletions(-) create mode 100755 Duplicati/UnitTest/SymLinkTests.cs diff --git a/Duplicati/UnitTest/BasicSetupHelper.cs b/Duplicati/UnitTest/BasicSetupHelper.cs index ba4b3a353..b73abdaa8 100644 --- a/Duplicati/UnitTest/BasicSetupHelper.cs +++ b/Duplicati/UnitTest/BasicSetupHelper.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.IO.Compression; using Duplicati.Library.Common; using Duplicati.Library.Common.IO; +using Duplicati.Library.Utility; namespace Duplicati.UnitTest { @@ -211,6 +212,17 @@ namespace Duplicati.UnitTest ZipFile.ExtractToDirectory(sourceArchiveFileName, destinationDirectoryName); } } + + /// + /// Write file with . + /// + protected static void WriteFile(string path, byte[] contents) + { + using (FileStream fileStream = SystemIO.IO_OS.FileOpenWrite(path)) + { + Utility.CopyStream(new MemoryStream(contents), fileStream); + } + } } } diff --git a/Duplicati/UnitTest/Duplicati.UnitTest.csproj b/Duplicati/UnitTest/Duplicati.UnitTest.csproj index 15f3586cd..86b17ef74 100644 --- a/Duplicati/UnitTest/Duplicati.UnitTest.csproj +++ b/Duplicati/UnitTest/Duplicati.UnitTest.csproj @@ -44,6 +44,7 @@ + diff --git a/Duplicati/UnitTest/ProblematicPathTests.cs b/Duplicati/UnitTest/ProblematicPathTests.cs index 7d1f05a3a..3e458a779 100644 --- a/Duplicati/UnitTest/ProblematicPathTests.cs +++ b/Duplicati/UnitTest/ProblematicPathTests.cs @@ -13,14 +13,6 @@ namespace Duplicati.UnitTest [TestFixture] public class ProblematicPathTests : BasicSetupHelper { - private static void WriteFile(string path, byte[] contents) - { - using (FileStream fileStream = SystemIO.IO_OS.FileOpenWrite(path)) - { - Utility.CopyStream(new MemoryStream(contents), fileStream); - } - } - [Test] [Category("ProblematicPath")] public void ExcludeProblematicPaths() diff --git a/Duplicati/UnitTest/SymLinkTests.cs b/Duplicati/UnitTest/SymLinkTests.cs new file mode 100755 index 000000000..1c6f7bde2 --- /dev/null +++ b/Duplicati/UnitTest/SymLinkTests.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Duplicati.Library.Interface; +using Duplicati.Library.Main; +using Duplicati.Library.Snapshots; +using NUnit.Framework; + +namespace Duplicati.UnitTest +{ + [TestFixture] + public class SymLinkTests : BasicSetupHelper + { + [Test] + [Category("SymLink")] + public void SymLinkPolicy() + { + // Create symlink target directory + const string targetDirName = "target"; + var targetDir = systemIO.PathCombine(this.DATAFOLDER, targetDirName); + systemIO.DirectoryCreate(targetDir); + // Create files in symlink target directory + var fileNames = new[] { "a.txt", "b.txt", "c.txt" }; + foreach (var file in fileNames) + { + var targetFile = systemIO.PathCombine(targetDir, file); + WriteFile(targetFile, Encoding.Default.GetBytes(file)); + } + + // Create actual symlink directory linking to the target directory + const string symlinkDirName = "symlink"; + var symlinkDir = systemIO.PathCombine(this.DATAFOLDER, symlinkDirName); + try + { + systemIO.CreateSymlink(symlinkDir, targetDir, asDir: true); + } + catch (Exception e) + { + // If client cannot create symlinks, mark test as ignored + Assert.Ignore($"Client could not create a symbolic link. Error reported: {e.Message}"); + } + + // Perform backups using all symlink policies and verify restores + var symlinkPolicies = new[] { Options.SymlinkStrategy.Store, Options.SymlinkStrategy.Follow, Options.SymlinkStrategy.Ignore }; + Dictionary restoreOptions = new Dictionary(this.TestOptions) { ["restore-path"] = this.RESTOREFOLDER }; + foreach (var symlinkPolicy in symlinkPolicies) + { + // Backup all files with given symlink policy + Dictionary backupOptions = new Dictionary(this.TestOptions) { ["symlink-policy"] = symlinkPolicy.ToString() }; + using (Controller c = new Controller("file://" + this.TARGETFOLDER, backupOptions, null)) + { + IBackupResults backupResults = c.Backup(new[] { this.DATAFOLDER }); + Assert.AreEqual(0, backupResults.Errors.Count()); + Assert.AreEqual(0, backupResults.Warnings.Count()); + } + // Restore all files + using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) + { + IRestoreResults restoreResults = c.Restore(null); + Assert.AreEqual(0, restoreResults.Errors.Count()); + Assert.AreEqual(0, restoreResults.Warnings.Count()); + + // Verify that symlink policy was followed + var restoreSymlinkDir = systemIO.PathCombine(this.RESTOREFOLDER, symlinkDirName); + switch (symlinkPolicy) + { + case Options.SymlinkStrategy.Store: + // Restore should contain an actual symlink to the original target + Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.True); + var restoredSymlinkFullPath = systemIO.PathGetFullPath(systemIO.GetSymlinkTarget(restoreSymlinkDir)); + var symlinkTargetFullPath = systemIO.PathGetFullPath(targetDir); + Assert.That(restoredSymlinkFullPath, Is.EqualTo(symlinkTargetFullPath)); + break; + case Options.SymlinkStrategy.Follow: + // Restore should contain a regular directory with copies of the files in the symlink target + Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.False); + AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, $"Symlink policy: {Options.SymlinkStrategy.Store}"); + break; + case Options.SymlinkStrategy.Ignore: + // Restore should not contain the symlink at all + Assert.That(systemIO.DirectoryExists(restoreSymlinkDir), Is.False); + break; + } + // Prepare for a fresh backup and restore cycle + foreach (var dir in systemIO.EnumerateDirectories(this.RESTOREFOLDER)) + { + systemIO.DirectoryDelete(dir, true); + } + foreach (var file in systemIO.EnumerateFiles(this.RESTOREFOLDER)) + { + systemIO.FileDelete(file); + } + c.DeleteAllRemoteFiles(); + if (systemIO.FileExists(this.DBFILE)) + { + systemIO.FileDelete(this.DBFILE); + } + if (systemIO.FileExists($"{this.DBFILE}-journal")) + { + systemIO.FileDelete($"{this.DBFILE}-journal"); + } + } + } + } + + /// + /// Asserts that the two directories are equivalent; i.e., they they contain the same subdirectories and files, recursively. + /// + private static void AssertDirectoryTreesAreEquivalent(string expectedDir, string actualDir, string contextMessage) + { + var localMessage = $"{contextMessage}, in directories {expectedDir} and {actualDir}"; + var expectedSubdirs = systemIO.EnumerateDirectories(expectedDir).OrderBy(systemIO.PathGetFileName); + var actualSubdirs = systemIO.EnumerateDirectories(actualDir).OrderBy(systemIO.PathGetFileName); + Assert.That(expectedSubdirs.Select(systemIO.PathGetFileName), Is.EquivalentTo(actualSubdirs.Select(systemIO.PathGetFileName)), localMessage); + var expectedSubdirsEnumerator = expectedSubdirs.GetEnumerator(); + var actualSubdirsEnumerator = actualSubdirs.GetEnumerator(); + while (expectedSubdirsEnumerator.MoveNext() && actualSubdirsEnumerator.MoveNext()) + { + AssertDirectoryTreesAreEquivalent(expectedSubdirsEnumerator.Current, actualSubdirsEnumerator.Current, contextMessage); + } + var expectedFiles = systemIO.EnumerateFiles(expectedDir).OrderBy(systemIO.PathGetFileName); + var actualFiles = systemIO.EnumerateFiles(actualDir).OrderBy(systemIO.PathGetFileName); + Assert.That(expectedFiles.Select(systemIO.PathGetFileName), Is.EquivalentTo(actualFiles.Select(systemIO.PathGetFileName)), localMessage); + var expectedFilesEnumerator = expectedFiles.GetEnumerator(); + var actualFilesEnumerator = actualFiles.GetEnumerator(); + while (expectedFilesEnumerator.MoveNext() && actualFilesEnumerator.MoveNext()) + { + AssertFilesAreEqual(expectedFilesEnumerator.Current, actualFilesEnumerator.Current, contextMessage); + } + } + + /// + /// Asserts that two files are the same by comparing their size and their contents. + /// + private static void AssertFilesAreEqual(string expectedFile, string actualFile, string contextMessage) + { + using (var expectedFileStream = systemIO.FileOpenRead(expectedFile)) + using (var actualFileStream = systemIO.FileOpenRead(actualFile)) + { + Assert.That(expectedFileStream.Length, Is.EqualTo(actualFileStream.Length), $"{contextMessage}, file size mismatch for {expectedFile} and {actualFile}"); + for (long i = 0; i < expectedFileStream.Length; i++) + { + var expectedByte = expectedFileStream.ReadByte(); + var actualByte = actualFileStream.ReadByte(); + // Only generate message if byte comparison fails + if (expectedByte != actualByte) + { + var message = + $"{contextMessage}, file contents mismatch at position {i} for {expectedFile} and {actualFile}"; + Assert.That(actualByte, Is.EqualTo(expectedByte), message); + } + } + } + } + } +} From b3e01e500c689720fd31c1e5e684323055b930d6 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Tue, 8 Sep 2020 07:38:40 -0700 Subject: [PATCH 17/32] Add SymLink test category to .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 83a9cd3e5..79419b3da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,7 @@ jobs: directories: - $BUILD_DIR script: - - ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories SVNDataLong,SVNData,RecoveryTool,ProblematicPath + - ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories SVNDataLong,SVNData,RecoveryTool,ProblematicPath,SymLink - stage: tests cache: directories: From 7c59b3d53106b96d4c7680ebfdef27a57364c9f2 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 10 Sep 2020 16:44:31 -0700 Subject: [PATCH 18/32] Add Storadera S3 provider. This fixes #4313. --- Duplicati/Library/Backend/S3/S3Backend.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Duplicati/Library/Backend/S3/S3Backend.cs b/Duplicati/Library/Backend/S3/S3Backend.cs index ee1b84eb1..b8dcb24c4 100644 --- a/Duplicati/Library/Backend/S3/S3Backend.cs +++ b/Duplicati/Library/Backend/S3/S3Backend.cs @@ -51,6 +51,7 @@ namespace Duplicati.Library.Backend { "dinCloud - Chicago", "d3-ord.dincloud.com" }, { "dinCloud - Los Angeles", "d3-lax.dincloud.com" }, { "IBM COS (S3) Public US", "s3-api.us-geo.objectstorage.softlayer.net" }, + { "Storadera", "eu-east-1.s3.storadera.com" }, { "Wasabi Hot Storage", "s3.wasabisys.com" }, { "Wasabi Hot Storage (US West)", "s3.us-west-1.wasabisys.com" }, { "Wasabi Hot Storage (EU Central)", "s3.eu-central-1.wasabisys.com" }, From f4195e4eb09503850b72b9cc4f71093255bc4483 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Thu, 10 Sep 2020 18:41:55 -0700 Subject: [PATCH 19/32] Address review comments from @warwickmm Replace `VerifyDir` and `CompareFiles` with `AssertDirectoryTreesAreEquivalent` and `AssertFilesAreEqual`. Move `WriteFile` to `TestUtils`. Add "Symlink" category to start.sh. --- Duplicati/UnitTest/BasicSetupHelper.cs | 11 -- Duplicati/UnitTest/BorderTests.cs | 2 +- .../UnitTest/CommandLineOperationsTests.cs | 8 +- Duplicati/UnitTest/DisruptionTests.cs | 6 +- Duplicati/UnitTest/ProblematicPathTests.cs | 22 +-- Duplicati/UnitTest/RecoveryToolTests.cs | 4 +- Duplicati/UnitTest/SVNCheckoutsTest.cs | 8 +- Duplicati/UnitTest/SymLinkTests.cs | 54 +----- Duplicati/UnitTest/TestUtils.cs | 170 +++++++----------- pipeline/start.sh | 2 +- 10 files changed, 91 insertions(+), 196 deletions(-) diff --git a/Duplicati/UnitTest/BasicSetupHelper.cs b/Duplicati/UnitTest/BasicSetupHelper.cs index b73abdaa8..accc49944 100644 --- a/Duplicati/UnitTest/BasicSetupHelper.cs +++ b/Duplicati/UnitTest/BasicSetupHelper.cs @@ -212,17 +212,6 @@ namespace Duplicati.UnitTest ZipFile.ExtractToDirectory(sourceArchiveFileName, destinationDirectoryName); } } - - /// - /// Write file with . - /// - protected static void WriteFile(string path, byte[] contents) - { - using (FileStream fileStream = SystemIO.IO_OS.FileOpenWrite(path)) - { - Utility.CopyStream(new MemoryStream(contents), fileStream); - } - } } } diff --git a/Duplicati/UnitTest/BorderTests.cs b/Duplicati/UnitTest/BorderTests.cs index b8d5a811d..1c4ceaf9e 100644 --- a/Duplicati/UnitTest/BorderTests.cs +++ b/Duplicati/UnitTest/BorderTests.cs @@ -353,7 +353,7 @@ namespace Duplicati.UnitTest Assert.AreEqual(filenames.Count * 3, r.RestoredFiles); } - TestUtils.VerifyDir(DATAFOLDER, RESTOREFOLDER, !Library.Utility.Utility.ParseBoolOption(testopts, "skip-metadata")); + TestUtils.AssertDirectoryTreesAreEquivalent(DATAFOLDER, RESTOREFOLDER, !Library.Utility.Utility.ParseBoolOption(testopts, "skip-metadata"), "Restore"); using(var tf = new Library.Utility.TempFolder()) { diff --git a/Duplicati/UnitTest/CommandLineOperationsTests.cs b/Duplicati/UnitTest/CommandLineOperationsTests.cs index ece4befa3..c92b67d9c 100644 --- a/Duplicati/UnitTest/CommandLineOperationsTests.cs +++ b/Duplicati/UnitTest/CommandLineOperationsTests.cs @@ -216,7 +216,7 @@ namespace Duplicati.UnitTest ProgressWriteLine("Verifying partial restore ..."); using (new Library.Logging.Timer(LOGTAG, "VerifiationOfPartialRestore", "Verification of partial restored files")) - TestUtils.VerifyDir(rf, RESTOREFOLDER, true); + TestUtils.AssertDirectoryTreesAreEquivalent(rf, RESTOREFOLDER, true, "VerifiationOfPartialRestore"); systemIO.DirectoryDelete(RESTOREFOLDER, true); @@ -226,7 +226,7 @@ namespace Duplicati.UnitTest ProgressWriteLine("Verifying partial restore ..."); using (new Library.Logging.Timer(LOGTAG, "VerificationOfPartialRestore", "Verification of partial restored files")) - TestUtils.VerifyDir(rf, RESTOREFOLDER, true); + TestUtils.AssertDirectoryTreesAreEquivalent(rf, RESTOREFOLDER, true, "VerificationOfPartialRestore"); systemIO.DirectoryDelete(RESTOREFOLDER, true); @@ -237,7 +237,7 @@ namespace Duplicati.UnitTest ProgressWriteLine("Verifying full restore ..."); using (new Library.Logging.Timer(LOGTAG, "VerificationOfFullRestore", "Verification of restored files")) foreach (var s in systemIO.EnumerateDirectories(DATAFOLDER)) - TestUtils.VerifyDir(s, Path.Combine(RESTOREFOLDER, Path.GetFileName(s)), true); + TestUtils.AssertDirectoryTreesAreEquivalent(s, Path.Combine(RESTOREFOLDER, Path.GetFileName(s)), true, "VerificationOfFullRestore"); systemIO.DirectoryDelete(RESTOREFOLDER, true); @@ -248,7 +248,7 @@ namespace Duplicati.UnitTest ProgressWriteLine("Verifying full restore ..."); using (new Library.Logging.Timer(LOGTAG, "VerificationOfFullRestoreWithoutDb", "Verification of restored files")) foreach (var s in systemIO.EnumerateDirectories(DATAFOLDER)) - TestUtils.VerifyDir(s, Path.Combine(RESTOREFOLDER, Path.GetFileName(s)), true); + TestUtils.AssertDirectoryTreesAreEquivalent(s, Path.Combine(RESTOREFOLDER, Path.GetFileName(s)), true, "VerificationOfFullRestoreWithoutDb"); ProgressWriteLine("Testing data ..."); using (new Library.Logging.Timer(LOGTAG, "TestRemoteData", "Test remote data")) diff --git a/Duplicati/UnitTest/DisruptionTests.cs b/Duplicati/UnitTest/DisruptionTests.cs index bf8dbeb9e..f9f0953b9 100644 --- a/Duplicati/UnitTest/DisruptionTests.cs +++ b/Duplicati/UnitTest/DisruptionTests.cs @@ -527,7 +527,7 @@ namespace Duplicati.UnitTest foreach (string filepath in partialVersionFiles) { string filename = Path.GetFileName(filepath); - Assert.IsTrue(TestUtils.CompareFiles(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), filename, false)); + TestUtils.AssertFilesAreEqual(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), false, filename); } } @@ -572,7 +572,7 @@ namespace Duplicati.UnitTest foreach (string filepath in fullVersionFiles) { string filename = Path.GetFileName(filepath); - Assert.IsTrue(TestUtils.CompareFiles(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), filename, false)); + TestUtils.AssertFilesAreEqual(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), false, filename); } } } @@ -639,7 +639,7 @@ namespace Duplicati.UnitTest foreach (string filepath in fullVersionFiles) { string filename = Path.GetFileName(filepath); - Assert.IsTrue(TestUtils.CompareFiles(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), filename, false)); + TestUtils.AssertFilesAreEqual(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), false, filename); } } } diff --git a/Duplicati/UnitTest/ProblematicPathTests.cs b/Duplicati/UnitTest/ProblematicPathTests.cs index 3a3285cf1..8917701b5 100644 --- a/Duplicati/UnitTest/ProblematicPathTests.cs +++ b/Duplicati/UnitTest/ProblematicPathTests.cs @@ -32,7 +32,7 @@ namespace Duplicati.UnitTest if (!Platform.IsClientWindows) { SystemIO.IO_OS.DirectoryCreate(dirWithAsterisk); - WriteFile(SystemIO.IO_OS.PathCombine(dirWithAsterisk, file), new byte[] {0}); + TestUtils.WriteFile(SystemIO.IO_OS.PathCombine(dirWithAsterisk, file), new byte[] {0}); directories.Add(dirWithAsterisk); questionMarkWildcardShouldMatchCount++; verbatimAsteriskShouldMatchCount++; @@ -44,7 +44,7 @@ namespace Duplicati.UnitTest if (!Platform.IsClientWindows) { SystemIO.IO_OS.DirectoryCreate(dirWithQuestionMark); - WriteFile(SystemIO.IO_OS.PathCombine(dirWithQuestionMark, file), new byte[] { 1 }); + TestUtils.WriteFile(SystemIO.IO_OS.PathCombine(dirWithQuestionMark, file), new byte[] { 1 }); directories.Add(dirWithQuestionMark); questionMarkWildcardShouldMatchCount++; } @@ -54,14 +54,14 @@ namespace Duplicati.UnitTest const string singleCharacterDir = "X"; string dirWithSingleCharacter = Path.Combine(this.DATAFOLDER, singleCharacterDir); SystemIO.IO_OS.DirectoryCreate(dirWithSingleCharacter); - WriteFile(SystemIO.IO_OS.PathCombine(dirWithSingleCharacter, file), new byte[] { 2 }); + TestUtils.WriteFile(SystemIO.IO_OS.PathCombine(dirWithSingleCharacter, file), new byte[] { 2 }); directories.Add(dirWithSingleCharacter); questionMarkWildcardShouldMatchCount++; const string dir = "dir"; string normalDir = Path.Combine(this.DATAFOLDER, dir); SystemIO.IO_OS.DirectoryCreate(normalDir); - WriteFile(SystemIO.IO_OS.PathCombine(normalDir, file), new byte[] {3}); + TestUtils.WriteFile(SystemIO.IO_OS.PathCombine(normalDir, file), new byte[] {3}); directories.Add(normalDir); // Backup all files. @@ -88,7 +88,7 @@ namespace Duplicati.UnitTest { string fileName = SystemIO.IO_OS.PathGetFileName(expectedFilePath); string restoredFilePath = SystemIO.IO_OS.PathCombine(this.RESTOREFOLDER, directoryName, fileName); - Assert.IsTrue(TestUtils.CompareFiles(expectedFilePath, restoredFilePath, expectedFilePath, false)); + TestUtils.AssertFilesAreEqual(expectedFilePath, restoredFilePath, false, expectedFilePath); } } @@ -132,7 +132,7 @@ namespace Duplicati.UnitTest string fileName = SystemIO.IO_OS.PathGetFileName(expectedFilePath); string restoredFilePath = SystemIO.IO_OS.PathCombine(this.RESTOREFOLDER, fileName); - Assert.IsTrue(TestUtils.CompareFiles(expectedFilePath, restoredFilePath, expectedFilePath, false)); + TestUtils.AssertFilesAreEqual(expectedFilePath, restoredFilePath, false, expectedFilePath); SystemIO.IO_OS.FileDelete(restoredFilePath); } @@ -171,7 +171,7 @@ namespace Duplicati.UnitTest // A long path to exclude. string longFile = SystemIO.IO_OS.PathCombine(this.DATAFOLDER, new string('y', 255)); - WriteFile(longFile, new byte[] {0, 1}); + TestUtils.WriteFile(longFile, new byte[] {0, 1}); // A folder that ends with a dot to exclude. string folderWithDot = Path.Combine(this.DATAFOLDER, "folder_with_dot."); @@ -183,11 +183,11 @@ namespace Duplicati.UnitTest // A file that ends with a dot to exclude. string fileWithDot = Path.Combine(this.DATAFOLDER, "file_with_dot."); - WriteFile(fileWithDot, new byte[] {0, 1}); + TestUtils.WriteFile(fileWithDot, new byte[] {0, 1}); // A file that ends with a space to exclude. string fileWithSpace = Path.Combine(this.DATAFOLDER, "file_with_space "); - WriteFile(fileWithSpace, new byte[] {0, 1}); + TestUtils.WriteFile(fileWithSpace, new byte[] {0, 1}); FilterExpression filter = new FilterExpression(longFile, false); filter = FilterExpression.Combine(filter, new FilterExpression(Util.AppendDirSeparator(folderWithDot), false)); @@ -226,7 +226,7 @@ namespace Duplicati.UnitTest string fileName = new string('y', 255); string filePath = SystemIO.IO_OS.PathCombine(folderPath, fileName); byte[] fileBytes = {0, 1, 2}; - WriteFile(filePath, fileBytes); + TestUtils.WriteFile(filePath, fileBytes); Dictionary options = new Dictionary(this.TestOptions); using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) @@ -269,7 +269,7 @@ namespace Duplicati.UnitTest string filePath = SystemIO.IO_OS.PathCombine(folderPath, pathComponent); byte[] fileBytes = {0, 1, 2}; - WriteFile(filePath, fileBytes); + TestUtils.WriteFile(filePath, fileBytes); Dictionary options = new Dictionary(this.TestOptions); using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) diff --git a/Duplicati/UnitTest/RecoveryToolTests.cs b/Duplicati/UnitTest/RecoveryToolTests.cs index 65c5c3dc5..dfc6cbd34 100644 --- a/Duplicati/UnitTest/RecoveryToolTests.cs +++ b/Duplicati/UnitTest/RecoveryToolTests.cs @@ -93,13 +93,13 @@ namespace Duplicati.UnitTest foreach (string filepath in Directory.EnumerateFiles(this.DATAFOLDER)) { string filename = Path.GetFileName(filepath); - Assert.IsTrue(TestUtils.CompareFiles(filepath, Path.Combine(restoreFolder, baseFolder, filename ?? String.Empty), filename, false)); + TestUtils.AssertFilesAreEqual(filepath, Path.Combine(restoreFolder, baseFolder, filename ?? String.Empty), false, filename); } foreach (string filepath in Directory.EnumerateFiles(subdirectoryPath)) { string filename = Path.GetFileName(filepath); - Assert.IsTrue(TestUtils.CompareFiles(filepath, Path.Combine(restoreFolder, baseFolder, subdirectoryName, filename ?? String.Empty), filename, false)); + TestUtils.AssertFilesAreEqual(filepath, Path.Combine(restoreFolder, baseFolder, subdirectoryName, filename ?? String.Empty), false, filename); } } } diff --git a/Duplicati/UnitTest/SVNCheckoutsTest.cs b/Duplicati/UnitTest/SVNCheckoutsTest.cs index 1a0df38c2..77480f291 100644 --- a/Duplicati/UnitTest/SVNCheckoutsTest.cs +++ b/Duplicati/UnitTest/SVNCheckoutsTest.cs @@ -421,11 +421,7 @@ namespace Duplicati.UnitTest throw new Exception("Unittest is broken"); } - if (!TestUtils.CompareFiles(sourcename, restoredname, s, verifymetadata)) - { - Log.WriteErrorMessage(LOGTAG, "PartialRestoreWrongFile", null, "Partial restore file differs: {0}", s); - BasicSetupHelper.ProgressWriteLine("Partial restore file differs: " + s); - } + TestUtils.AssertFilesAreEqual(sourcename, restoredname, verifymetadata, $"Partial restore file differs: {s}"); } } } @@ -435,7 +431,7 @@ namespace Duplicati.UnitTest using (new Timer(LOGTAG, "SourceVerification", "Verification of " + source)) { for (int j = 0; j < actualfolders.Length; j++) - TestUtils.VerifyDir(actualfolders[j], restorefoldernames[j], verifymetadata); + TestUtils.AssertDirectoryTreesAreEquivalent(actualfolders[j], restorefoldernames[j], verifymetadata, "VerifyFullRestore"); } } diff --git a/Duplicati/UnitTest/SymLinkTests.cs b/Duplicati/UnitTest/SymLinkTests.cs index 1c6f7bde2..4b52bb384 100755 --- a/Duplicati/UnitTest/SymLinkTests.cs +++ b/Duplicati/UnitTest/SymLinkTests.cs @@ -25,7 +25,7 @@ namespace Duplicati.UnitTest foreach (var file in fileNames) { var targetFile = systemIO.PathCombine(targetDir, file); - WriteFile(targetFile, Encoding.Default.GetBytes(file)); + TestUtils.WriteFile(targetFile, Encoding.Default.GetBytes(file)); } // Create actual symlink directory linking to the target directory @@ -75,7 +75,7 @@ namespace Duplicati.UnitTest case Options.SymlinkStrategy.Follow: // Restore should contain a regular directory with copies of the files in the symlink target Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.False); - AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, $"Symlink policy: {Options.SymlinkStrategy.Store}"); + TestUtils.AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, true, $"Symlink policy: {Options.SymlinkStrategy.Store}"); break; case Options.SymlinkStrategy.Ignore: // Restore should not contain the symlink at all @@ -103,55 +103,5 @@ namespace Duplicati.UnitTest } } } - - /// - /// Asserts that the two directories are equivalent; i.e., they they contain the same subdirectories and files, recursively. - /// - private static void AssertDirectoryTreesAreEquivalent(string expectedDir, string actualDir, string contextMessage) - { - var localMessage = $"{contextMessage}, in directories {expectedDir} and {actualDir}"; - var expectedSubdirs = systemIO.EnumerateDirectories(expectedDir).OrderBy(systemIO.PathGetFileName); - var actualSubdirs = systemIO.EnumerateDirectories(actualDir).OrderBy(systemIO.PathGetFileName); - Assert.That(expectedSubdirs.Select(systemIO.PathGetFileName), Is.EquivalentTo(actualSubdirs.Select(systemIO.PathGetFileName)), localMessage); - var expectedSubdirsEnumerator = expectedSubdirs.GetEnumerator(); - var actualSubdirsEnumerator = actualSubdirs.GetEnumerator(); - while (expectedSubdirsEnumerator.MoveNext() && actualSubdirsEnumerator.MoveNext()) - { - AssertDirectoryTreesAreEquivalent(expectedSubdirsEnumerator.Current, actualSubdirsEnumerator.Current, contextMessage); - } - var expectedFiles = systemIO.EnumerateFiles(expectedDir).OrderBy(systemIO.PathGetFileName); - var actualFiles = systemIO.EnumerateFiles(actualDir).OrderBy(systemIO.PathGetFileName); - Assert.That(expectedFiles.Select(systemIO.PathGetFileName), Is.EquivalentTo(actualFiles.Select(systemIO.PathGetFileName)), localMessage); - var expectedFilesEnumerator = expectedFiles.GetEnumerator(); - var actualFilesEnumerator = actualFiles.GetEnumerator(); - while (expectedFilesEnumerator.MoveNext() && actualFilesEnumerator.MoveNext()) - { - AssertFilesAreEqual(expectedFilesEnumerator.Current, actualFilesEnumerator.Current, contextMessage); - } - } - - /// - /// Asserts that two files are the same by comparing their size and their contents. - /// - private static void AssertFilesAreEqual(string expectedFile, string actualFile, string contextMessage) - { - using (var expectedFileStream = systemIO.FileOpenRead(expectedFile)) - using (var actualFileStream = systemIO.FileOpenRead(actualFile)) - { - Assert.That(expectedFileStream.Length, Is.EqualTo(actualFileStream.Length), $"{contextMessage}, file size mismatch for {expectedFile} and {actualFile}"); - for (long i = 0; i < expectedFileStream.Length; i++) - { - var expectedByte = expectedFileStream.ReadByte(); - var actualByte = actualFileStream.ReadByte(); - // Only generate message if byte comparison fails - if (expectedByte != actualByte) - { - var message = - $"{contextMessage}, file contents mismatch at position {i} for {expectedFile} and {actualFile}"; - Assert.That(actualByte, Is.EqualTo(expectedByte), message); - } - } - } - } } } diff --git a/Duplicati/UnitTest/TestUtils.cs b/Duplicati/UnitTest/TestUtils.cs index 0c8732d65..6bdb0a57b 100644 --- a/Duplicati/UnitTest/TestUtils.cs +++ b/Duplicati/UnitTest/TestUtils.cs @@ -24,7 +24,8 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using Duplicati.Library.Common.IO; - +using NUnit.Framework; + namespace Duplicati.UnitTest { public static class TestUtils @@ -169,129 +170,78 @@ namespace Duplicati.UnitTest } /// - /// Verifies the existence of all files and folders, and ensures that all - /// files are binary equal. + /// Asserts that the two directory trees are equivalent; i.e., + /// that they they contain the same directories and files, recursively. /// - /// One folder - /// Another folder - public static void VerifyDir(string f1, string f2, bool verifymetadata) + /// The expected directory tree. + /// The actual directory tree. + /// True to also compare file metadata. + /// Context information to include in an assert message. + public static void AssertDirectoryTreesAreEquivalent(string expectedDir, string actualDir, bool verifymetadata, string contextMessage) { - var anymissing = false; - f1 = Util.AppendDirSeparator(f1); - f2 = Util.AppendDirSeparator(f2); - - var folders1 = Utility.EnumerateFolders(f1); - var folders2 = Utility.EnumerateFolders(f2).ToList(); - - foreach (string s in folders1) + var localMessage = $"{contextMessage}, in directories {expectedDir} and {actualDir}"; + // Assert that expectedDir and actualDir contain the same directories + var expectedSubdirs = SystemIO.IO_OS.EnumerateDirectories(expectedDir).OrderBy(SystemIO.IO_OS.PathGetFileName); + var actualSubdirs = SystemIO.IO_OS.EnumerateDirectories(actualDir).OrderBy(SystemIO.IO_OS.PathGetFileName); + Assert.That(expectedSubdirs.Select(SystemIO.IO_OS.PathGetFileName), Is.EquivalentTo(actualSubdirs.Select(SystemIO.IO_OS.PathGetFileName)), localMessage); + // Recursively compare the contained directories + var expectedSubdirsEnumerator = expectedSubdirs.GetEnumerator(); + var actualSubdirsEnumerator = actualSubdirs.GetEnumerator(); + while (expectedSubdirsEnumerator.MoveNext() && actualSubdirsEnumerator.MoveNext()) { - string relpath = s.Substring(f1.Length); - string target = System.IO.Path.Combine(f2, relpath); - int ix = IndexOf(folders2, target); - if (ix < 0) - { - Log.WriteErrorMessage(LOGTAG, "MissingFolder", null, "Missing folder: {0}", relpath); - Console.WriteLine("Missing folder: " + relpath); - anymissing = true; - } - else - folders2.RemoveAt(ix); + AssertDirectoryTreesAreEquivalent(expectedSubdirsEnumerator.Current, actualSubdirsEnumerator.Current, verifymetadata, contextMessage); } - - foreach (string s in folders2) + // Assert that expectedDir and actualDir contain the same files + var expectedFiles = SystemIO.IO_OS.EnumerateFiles(expectedDir).OrderBy(SystemIO.IO_OS.PathGetFileName); + var actualFiles = SystemIO.IO_OS.EnumerateFiles(actualDir).OrderBy(SystemIO.IO_OS.PathGetFileName); + Assert.That(expectedFiles.Select(SystemIO.IO_OS.PathGetFileName), Is.EquivalentTo(actualFiles.Select(SystemIO.IO_OS.PathGetFileName)), localMessage); + // Assert that the files are equal + var expectedFilesEnumerator = expectedFiles.GetEnumerator(); + var actualFilesEnumerator = actualFiles.GetEnumerator(); + while (expectedFilesEnumerator.MoveNext() && actualFilesEnumerator.MoveNext()) { - Log.WriteErrorMessage(LOGTAG, "ExtraFolder", null, "Extra folder: {0}", s.Substring(f2.Length)); - Console.WriteLine("Extra folder: " + s.Substring(f2.Length)); + AssertFilesAreEqual(expectedFilesEnumerator.Current, actualFilesEnumerator.Current, verifymetadata, contextMessage); } - - var files1 = Utility.EnumerateFiles(f1); - var files2 = Utility.EnumerateFiles(f2).ToList(); - foreach (string s in files1) - { - string relpath = s.Substring(f1.Length); - string target = System.IO.Path.Combine(f2, relpath); - int ix = IndexOf(files2, target); - if (ix < 0) - { - Log.WriteErrorMessage(LOGTAG, "MissingFile", null, "Missing file: {0}", relpath); - Console.WriteLine("Missing file: " + relpath); - anymissing = true; - } - else - { - files2.RemoveAt(ix); - if (!CompareFiles(s, target, relpath, verifymetadata)) - { - Log.WriteErrorMessage(LOGTAG, "FileDiffers", null, "File differs: {0}", relpath); - Console.WriteLine("File differs: " + relpath); - } - } - } - - foreach (string s in files2) - { - Log.WriteErrorMessage(LOGTAG, "ExtraFile", null, "Extra file: {0}", s.Substring(f2.Length)); - Console.WriteLine("Extra file: " + s.Substring(f2.Length)); - } - - if (anymissing) - throw new Exception("Verify failed, some items are missing"); } /// - /// Compares two files by reading all bytes, and comparing one by one + /// Asserts that two files are equal by comparing their length, contents, and, optionally, their metadata. /// - /// One file - /// Another file - /// File display name - /// True if they are equal, false otherwise - public static bool CompareFiles(string f1, string f2, string display, bool verifymetadata) + /// The expected file. + /// The actual file. + /// True to also compare file metadata. + /// Context information to include in an assert message. + public static void AssertFilesAreEqual(string expectedFile, string actualFile, bool verifymetadata, string contextMessage) { - using (System.IO.FileStream fs1 = System.IO.File.OpenRead(f1)) - using (System.IO.FileStream fs2 = System.IO.File.OpenRead(f2)) - if (fs1.Length != fs2.Length) + using (var expectedFileStream = SystemIO.IO_OS.FileOpenRead(expectedFile)) + using (var actualFileStream = SystemIO.IO_OS.FileOpenRead(actualFile)) + { + // Compare file lengths + var expectedFileStreamLength = expectedFileStream.Length; + var actualFileStreamLength = actualFileStream.Length; + Assert.That(actualFileStreamLength, Is.EqualTo(expectedFileStreamLength), $"{contextMessage}, file size mismatch for {expectedFile} and {actualFile}"); + // Compare file contents + for (long i = 0; i < expectedFileStreamLength; i++) { - Log.WriteErrorMessage(LOGTAG, "LengthsDiffer", null, "Lengths differ: {0}, {1} vs {2}", display, fs1.Length.ToString(), fs2.Length.ToString()); - Console.WriteLine("Lengths differ: " + display + ", " + fs1.Length.ToString() + " vs. " + fs2.Length.ToString()); - return false; - } - else - { - // The byte-by-byte compare is dog-slow, so we use a fast(-er) check, and then report the first byte diff if required - if (!Library.Utility.Utility.CompareStreams(fs1, fs2, true)) + var expectedByte = expectedFileStream.ReadByte(); + var actualByte = actualFileStream.ReadByte(); + // For performance reasons, only generate message if byte comparison fails + if (expectedByte != actualByte) { - fs1.Position = 0; - fs2.Position = 0; - long len = fs1.Length; - for(long l = 0; l < len; l++) - if (fs1.ReadByte() != fs2.ReadByte()) - { - Log.WriteErrorMessage(LOGTAG, "MismatchInFile", null, "Mismatch in byte {0} in file {1}", l.ToString(), display); - Console.WriteLine("Mismatch in byte " + l.ToString() + " in file " + display); - return false; - } + var message = + $"{contextMessage}, file contents mismatch at position {i} for {expectedFile} and {actualFile}"; + Assert.That(actualByte, Is.EqualTo(expectedByte), message); } } - + } + // Compare file metadata if (verifymetadata) { - if (System.IO.File.GetLastWriteTime(f1) != System.IO.File.GetLastWriteTime(f2)) - { - Log.WriteWarningMessage(LOGTAG, "MismatchInLastModified", null, "Mismatch in lastmodified for {0}, {1} vs {2}", f2, System.IO.File.GetLastWriteTimeUtc(f1), System.IO.File.GetLastWriteTimeUtc(f2)); - Console.WriteLine("Mismatch in lastmodified for " + f2 + ", " + System.IO.File.GetLastWriteTimeUtc(f1) + " vs. " + System.IO.File.GetLastWriteTimeUtc(f2)); - } - - if (System.IO.File.GetCreationTimeUtc(f1) != System.IO.File.GetCreationTimeUtc(f2)) - { - Log.WriteWarningMessage(LOGTAG, "MismatchInCreateTime", null, "Mismatch in create-time for {0}, {1} vs {2}", f2, System.IO.File.GetCreationTimeUtc(f1), System.IO.File.GetCreationTimeUtc(f2)); - Console.WriteLine("Mismatch in create-time for " + f2 + ", " + System.IO.File.GetCreationTimeUtc(f1) + " vs. " + System.IO.File.GetCreationTimeUtc(f2)); - } + Assert.That(SystemIO.IO_OS.GetLastWriteTimeUtc(actualFile), Is.EqualTo(SystemIO.IO_OS.GetLastWriteTimeUtc(expectedFile)), $"{contextMessage}, last write time mismatch for {expectedFile} and {actualFile}"); + Assert.That(SystemIO.IO_OS.GetCreationTimeUtc(actualFile), Is.EqualTo(SystemIO.IO_OS.GetCreationTimeUtc(expectedFile)), $"{contextMessage}, creation time mismatch for {expectedFile} and {actualFile}"); } - - - return true; } - + public static Dictionary Expand(this Dictionary self, object extra) { var res = new Dictionary(self); @@ -312,6 +262,16 @@ namespace Duplicati.UnitTest return res; } + /// + /// Write file with . + /// + public static void WriteFile(string path, byte[] contents) + { + using (FileStream fileStream = SystemIO.IO_OS.FileOpenWrite(path)) + { + Utility.CopyStream(new MemoryStream(contents), fileStream); + } + } } } diff --git a/pipeline/start.sh b/pipeline/start.sh index b6aa81f89..a208fd6b3 100755 --- a/pipeline/start.sh +++ b/pipeline/start.sh @@ -6,6 +6,6 @@ SCRIPT_DIR="$( cd "$(dirname "$0")" ; pwd -P )" ${ROOT_DIR}/pipeline/jobs/build_job.sh ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories BulkNormal ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories BulkNoSize -${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories SVNDataLong,SVNData,RecoveryTool,ProblematicPath +${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories SVNDataLong,SVNData,RecoveryTool,ProblematicPath,SymLink ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories Border ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories Filter,Targeted,Purge,Serialization,WebApi,Utility,UriUtility,IO,ImportExport,Disruption,RestoreHandler,RepairHandler,DeleteHandler,Controller From 2c56634899182705fec7d27580b857e01b03f315 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Fri, 11 Sep 2020 23:25:03 -0700 Subject: [PATCH 20/32] Add tolerance to file time comparisons --- Duplicati/UnitTest/TestUtils.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Duplicati/UnitTest/TestUtils.cs b/Duplicati/UnitTest/TestUtils.cs index 6bdb0a57b..8eca8483f 100644 --- a/Duplicati/UnitTest/TestUtils.cs +++ b/Duplicati/UnitTest/TestUtils.cs @@ -237,8 +237,14 @@ namespace Duplicati.UnitTest // Compare file metadata if (verifymetadata) { - Assert.That(SystemIO.IO_OS.GetLastWriteTimeUtc(actualFile), Is.EqualTo(SystemIO.IO_OS.GetLastWriteTimeUtc(expectedFile)), $"{contextMessage}, last write time mismatch for {expectedFile} and {actualFile}"); - Assert.That(SystemIO.IO_OS.GetCreationTimeUtc(actualFile), Is.EqualTo(SystemIO.IO_OS.GetCreationTimeUtc(expectedFile)), $"{contextMessage}, creation time mismatch for {expectedFile} and {actualFile}"); + Assert.That( + SystemIO.IO_OS.GetLastWriteTimeUtc(actualFile), + Is.EqualTo(SystemIO.IO_OS.GetLastWriteTimeUtc(expectedFile)).Within(1).Milliseconds, + $"{contextMessage}, last write time mismatch for {expectedFile} and {actualFile}"); + Assert.That( + SystemIO.IO_OS.GetCreationTimeUtc(actualFile), + Is.EqualTo(SystemIO.IO_OS.GetCreationTimeUtc(expectedFile)).Within(1).Milliseconds, + $"{contextMessage}, creation time mismatch for {expectedFile} and {actualFile}"); } } From 75b688c9997cb69f4e1ab4351803caf5d7d40dd4 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sat, 12 Sep 2020 08:08:52 -0700 Subject: [PATCH 21/32] Restore use of Utility.CompareStreams in AssertFilesAreEqual For performance reasons, use `Utility.CompareStreams` in AssertFilesAreEqual. --- Duplicati/UnitTest/TestUtils.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Duplicati/UnitTest/TestUtils.cs b/Duplicati/UnitTest/TestUtils.cs index 8eca8483f..a31cd156f 100644 --- a/Duplicati/UnitTest/TestUtils.cs +++ b/Duplicati/UnitTest/TestUtils.cs @@ -221,16 +221,20 @@ namespace Duplicati.UnitTest var actualFileStreamLength = actualFileStream.Length; Assert.That(actualFileStreamLength, Is.EqualTo(expectedFileStreamLength), $"{contextMessage}, file size mismatch for {expectedFile} and {actualFile}"); // Compare file contents - for (long i = 0; i < expectedFileStreamLength; i++) + // The byte-by-byte compare is dog-slow, so we use a fast(-er) check, and then report the first byte diff if required + if (!Utility.CompareStreams(expectedFileStream, actualFileStream, true)) { - var expectedByte = expectedFileStream.ReadByte(); - var actualByte = actualFileStream.ReadByte(); - // For performance reasons, only generate message if byte comparison fails - if (expectedByte != actualByte) + for (long i = 0; i < expectedFileStreamLength; i++) { - var message = - $"{contextMessage}, file contents mismatch at position {i} for {expectedFile} and {actualFile}"; - Assert.That(actualByte, Is.EqualTo(expectedByte), message); + var expectedByte = expectedFileStream.ReadByte(); + var actualByte = actualFileStream.ReadByte(); + // For performance reasons, only exercise Assert mechanism and generate message if byte comparison fails + if (expectedByte != actualByte) + { + var message = + $"{contextMessage}, file contents mismatch at position {i} for {expectedFile} and {actualFile}"; + Assert.That(actualByte, Is.EqualTo(expectedByte), message); + } } } } From 03674189253a1c102454379c5753f7da40f52cfa Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sat, 12 Sep 2020 10:52:01 -0700 Subject: [PATCH 22/32] Fix typo in log and assert messages --- Duplicati/UnitTest/CommandLineOperationsTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Duplicati/UnitTest/CommandLineOperationsTests.cs b/Duplicati/UnitTest/CommandLineOperationsTests.cs index c92b67d9c..ef2785c1b 100644 --- a/Duplicati/UnitTest/CommandLineOperationsTests.cs +++ b/Duplicati/UnitTest/CommandLineOperationsTests.cs @@ -215,8 +215,8 @@ namespace Duplicati.UnitTest Duplicati.CommandLine.Program.RealMain((new string[] { "restore", target, rf + "*", "--restore-path=\"" + RESTOREFOLDER + "\"" }.Union(opts)).ToArray()); ProgressWriteLine("Verifying partial restore ..."); - using (new Library.Logging.Timer(LOGTAG, "VerifiationOfPartialRestore", "Verification of partial restored files")) - TestUtils.AssertDirectoryTreesAreEquivalent(rf, RESTOREFOLDER, true, "VerifiationOfPartialRestore"); + using (new Library.Logging.Timer(LOGTAG, "VerificationOfPartialRestore", "Verification of partial restored files")) + TestUtils.AssertDirectoryTreesAreEquivalent(rf, RESTOREFOLDER, true, "VerificationOfPartialRestore"); systemIO.DirectoryDelete(RESTOREFOLDER, true); From 3366071d048fcf851d9e57fa63f91ad4cde5f641 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sat, 12 Sep 2020 14:29:04 -0700 Subject: [PATCH 23/32] Add back missing stream reset to AssertFilesAreEqual --- Duplicati/UnitTest/TestUtils.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Duplicati/UnitTest/TestUtils.cs b/Duplicati/UnitTest/TestUtils.cs index a31cd156f..fc6202ad5 100644 --- a/Duplicati/UnitTest/TestUtils.cs +++ b/Duplicati/UnitTest/TestUtils.cs @@ -224,6 +224,9 @@ namespace Duplicati.UnitTest // The byte-by-byte compare is dog-slow, so we use a fast(-er) check, and then report the first byte diff if required if (!Utility.CompareStreams(expectedFileStream, actualFileStream, true)) { + // Reset stream positions + expectedFileStream.Position = 0; + actualFileStream.Position = 0; for (long i = 0; i < expectedFileStreamLength; i++) { var expectedByte = expectedFileStream.ReadByte(); From eafe7320e90996633889ea1c64d3fd94deb81691 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sat, 12 Sep 2020 14:45:55 -0700 Subject: [PATCH 24/32] Address review comments --- Duplicati/UnitTest/ProblematicPathTests.cs | 10 +++------- Duplicati/UnitTest/TestUtils.cs | 3 +-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Duplicati/UnitTest/ProblematicPathTests.cs b/Duplicati/UnitTest/ProblematicPathTests.cs index 8917701b5..ac1d075f1 100644 --- a/Duplicati/UnitTest/ProblematicPathTests.cs +++ b/Duplicati/UnitTest/ProblematicPathTests.cs @@ -82,14 +82,10 @@ namespace Duplicati.UnitTest Assert.AreEqual(0, restoreResults.Warnings.Count()); foreach (string directory in directories) - { + { string directoryName = SystemIO.IO_OS.PathGetFileName(directory); - foreach (string expectedFilePath in SystemIO.IO_OS.EnumerateFiles(directory)) - { - string fileName = SystemIO.IO_OS.PathGetFileName(expectedFilePath); - string restoredFilePath = SystemIO.IO_OS.PathCombine(this.RESTOREFOLDER, directoryName, fileName); - TestUtils.AssertFilesAreEqual(expectedFilePath, restoredFilePath, false, expectedFilePath); - } + string restoredDirectory = SystemIO.IO_OS.PathCombine(this.RESTOREFOLDER, directoryName); + TestUtils.AssertDirectoryTreesAreEquivalent(directory, restoredDirectory, true, "Restore"); } // List results using * should return a match for each directory. diff --git a/Duplicati/UnitTest/TestUtils.cs b/Duplicati/UnitTest/TestUtils.cs index fc6202ad5..36d7beaf1 100644 --- a/Duplicati/UnitTest/TestUtils.cs +++ b/Duplicati/UnitTest/TestUtils.cs @@ -234,8 +234,7 @@ namespace Duplicati.UnitTest // For performance reasons, only exercise Assert mechanism and generate message if byte comparison fails if (expectedByte != actualByte) { - var message = - $"{contextMessage}, file contents mismatch at position {i} for {expectedFile} and {actualFile}"; + var message = $"{contextMessage}, file contents mismatch at position {i} for {expectedFile} and {actualFile}"; Assert.That(actualByte, Is.EqualTo(expectedByte), message); } } From e3b8a12ec11aa19e61b722f37b4bcad97bbec41f Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sat, 12 Sep 2020 16:59:41 -0700 Subject: [PATCH 25/32] Unit test improvements per review comments --- Duplicati/UnitTest/ProblematicPathTests.cs | 7 +- Duplicati/UnitTest/SymLinkTests.cs | 94 +++++++++------------- 2 files changed, 38 insertions(+), 63 deletions(-) diff --git a/Duplicati/UnitTest/ProblematicPathTests.cs b/Duplicati/UnitTest/ProblematicPathTests.cs index ac1d075f1..eb2e815d6 100644 --- a/Duplicati/UnitTest/ProblematicPathTests.cs +++ b/Duplicati/UnitTest/ProblematicPathTests.cs @@ -81,12 +81,7 @@ namespace Duplicati.UnitTest Assert.AreEqual(0, restoreResults.Errors.Count()); Assert.AreEqual(0, restoreResults.Warnings.Count()); - foreach (string directory in directories) - { - string directoryName = SystemIO.IO_OS.PathGetFileName(directory); - string restoredDirectory = SystemIO.IO_OS.PathCombine(this.RESTOREFOLDER, directoryName); - TestUtils.AssertDirectoryTreesAreEquivalent(directory, restoredDirectory, true, "Restore"); - } + TestUtils.AssertDirectoryTreesAreEquivalent(this.DATAFOLDER, this.RESTOREFOLDER, true, "Restore"); // List results using * should return a match for each directory. IListResults listResults = c.List(SystemIO.IO_OS.PathCombine(dirWithAsterisk, file)); diff --git a/Duplicati/UnitTest/SymLinkTests.cs b/Duplicati/UnitTest/SymLinkTests.cs index 4b52bb384..b2a178f09 100755 --- a/Duplicati/UnitTest/SymLinkTests.cs +++ b/Duplicati/UnitTest/SymLinkTests.cs @@ -14,7 +14,10 @@ namespace Duplicati.UnitTest { [Test] [Category("SymLink")] - public void SymLinkPolicy() + [TestCase(Options.SymlinkStrategy.Store)] + [TestCase(Options.SymlinkStrategy.Follow)] + [TestCase(Options.SymlinkStrategy.Ignore)] + public void SymLinkPolicy(Options.SymlinkStrategy symlinkPolicy) { // Create symlink target directory const string targetDirName = "target"; @@ -41,65 +44,42 @@ namespace Duplicati.UnitTest Assert.Ignore($"Client could not create a symbolic link. Error reported: {e.Message}"); } - // Perform backups using all symlink policies and verify restores - var symlinkPolicies = new[] { Options.SymlinkStrategy.Store, Options.SymlinkStrategy.Follow, Options.SymlinkStrategy.Ignore }; + // Backup all files with given symlink policy Dictionary restoreOptions = new Dictionary(this.TestOptions) { ["restore-path"] = this.RESTOREFOLDER }; - foreach (var symlinkPolicy in symlinkPolicies) + Dictionary backupOptions = new Dictionary(this.TestOptions) { ["symlink-policy"] = symlinkPolicy.ToString() }; + using (Controller c = new Controller("file://" + this.TARGETFOLDER, backupOptions, null)) { - // Backup all files with given symlink policy - Dictionary backupOptions = new Dictionary(this.TestOptions) { ["symlink-policy"] = symlinkPolicy.ToString() }; - using (Controller c = new Controller("file://" + this.TARGETFOLDER, backupOptions, null)) - { - IBackupResults backupResults = c.Backup(new[] { this.DATAFOLDER }); - Assert.AreEqual(0, backupResults.Errors.Count()); - Assert.AreEqual(0, backupResults.Warnings.Count()); - } - // Restore all files - using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) - { - IRestoreResults restoreResults = c.Restore(null); - Assert.AreEqual(0, restoreResults.Errors.Count()); - Assert.AreEqual(0, restoreResults.Warnings.Count()); + IBackupResults backupResults = c.Backup(new[] { this.DATAFOLDER }); + Assert.AreEqual(0, backupResults.Errors.Count()); + Assert.AreEqual(0, backupResults.Warnings.Count()); + } + // Restore all files + using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) + { + IRestoreResults restoreResults = c.Restore(null); + Assert.AreEqual(0, restoreResults.Errors.Count()); + Assert.AreEqual(0, restoreResults.Warnings.Count()); - // Verify that symlink policy was followed - var restoreSymlinkDir = systemIO.PathCombine(this.RESTOREFOLDER, symlinkDirName); - switch (symlinkPolicy) - { - case Options.SymlinkStrategy.Store: - // Restore should contain an actual symlink to the original target - Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.True); - var restoredSymlinkFullPath = systemIO.PathGetFullPath(systemIO.GetSymlinkTarget(restoreSymlinkDir)); - var symlinkTargetFullPath = systemIO.PathGetFullPath(targetDir); - Assert.That(restoredSymlinkFullPath, Is.EqualTo(symlinkTargetFullPath)); - break; - case Options.SymlinkStrategy.Follow: - // Restore should contain a regular directory with copies of the files in the symlink target - Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.False); - TestUtils.AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, true, $"Symlink policy: {Options.SymlinkStrategy.Store}"); - break; - case Options.SymlinkStrategy.Ignore: - // Restore should not contain the symlink at all - Assert.That(systemIO.DirectoryExists(restoreSymlinkDir), Is.False); - break; - } - // Prepare for a fresh backup and restore cycle - foreach (var dir in systemIO.EnumerateDirectories(this.RESTOREFOLDER)) - { - systemIO.DirectoryDelete(dir, true); - } - foreach (var file in systemIO.EnumerateFiles(this.RESTOREFOLDER)) - { - systemIO.FileDelete(file); - } - c.DeleteAllRemoteFiles(); - if (systemIO.FileExists(this.DBFILE)) - { - systemIO.FileDelete(this.DBFILE); - } - if (systemIO.FileExists($"{this.DBFILE}-journal")) - { - systemIO.FileDelete($"{this.DBFILE}-journal"); - } + // Verify that symlink policy was followed + var restoreSymlinkDir = systemIO.PathCombine(this.RESTOREFOLDER, symlinkDirName); + switch (symlinkPolicy) + { + case Options.SymlinkStrategy.Store: + // Restore should contain an actual symlink to the original target + Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.True); + var restoredSymlinkFullPath = systemIO.PathGetFullPath(systemIO.GetSymlinkTarget(restoreSymlinkDir)); + var symlinkTargetFullPath = systemIO.PathGetFullPath(targetDir); + Assert.That(restoredSymlinkFullPath, Is.EqualTo(symlinkTargetFullPath)); + break; + case Options.SymlinkStrategy.Follow: + // Restore should contain a regular directory with copies of the files in the symlink target + Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.False); + TestUtils.AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, true, $"Symlink policy: {Options.SymlinkStrategy.Store}"); + break; + case Options.SymlinkStrategy.Ignore: + // Restore should not contain the symlink at all + Assert.That(systemIO.DirectoryExists(restoreSymlinkDir), Is.False); + break; } } } From 641a590d89c29585cba79f46c36f4d9153ee3461 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sat, 12 Sep 2020 20:30:28 -0700 Subject: [PATCH 26/32] For "Ignore" policy, also ensure a file doesn't exist --- Duplicati/UnitTest/SymLinkTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Duplicati/UnitTest/SymLinkTests.cs b/Duplicati/UnitTest/SymLinkTests.cs index b2a178f09..7d60e994d 100755 --- a/Duplicati/UnitTest/SymLinkTests.cs +++ b/Duplicati/UnitTest/SymLinkTests.cs @@ -77,8 +77,9 @@ namespace Duplicati.UnitTest TestUtils.AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, true, $"Symlink policy: {Options.SymlinkStrategy.Store}"); break; case Options.SymlinkStrategy.Ignore: - // Restore should not contain the symlink at all + // Restore should not contain the symlink or directory at all Assert.That(systemIO.DirectoryExists(restoreSymlinkDir), Is.False); + Assert.That(systemIO.FileExists(restoreSymlinkDir), Is.False); break; } } From a060b850399d95d3d0972e7ed97d678a687ccb64 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sun, 13 Sep 2020 07:45:30 -0700 Subject: [PATCH 27/32] Add travis_wait to "Border" tests to avoid timeout --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 79419b3da..225209504 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ jobs: directories: - $BUILD_DIR script: - - ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories Border + - travis_wait ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories Border - stage: tests cache: directories: From a3c27a2ba2e85ef6fe650c91bc1bc60557f3f007 Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Sun, 13 Sep 2020 08:12:23 -0700 Subject: [PATCH 28/32] Add `Assert.Fail` to catch unexpected symlink policies --- Duplicati/UnitTest/SymLinkTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Duplicati/UnitTest/SymLinkTests.cs b/Duplicati/UnitTest/SymLinkTests.cs index 7d60e994d..84d21a24d 100755 --- a/Duplicati/UnitTest/SymLinkTests.cs +++ b/Duplicati/UnitTest/SymLinkTests.cs @@ -74,13 +74,16 @@ namespace Duplicati.UnitTest case Options.SymlinkStrategy.Follow: // Restore should contain a regular directory with copies of the files in the symlink target Assert.That(systemIO.IsSymlink(restoreSymlinkDir), Is.False); - TestUtils.AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, true, $"Symlink policy: {Options.SymlinkStrategy.Store}"); + TestUtils.AssertDirectoryTreesAreEquivalent(targetDir, restoreSymlinkDir, true, "Restore"); break; case Options.SymlinkStrategy.Ignore: // Restore should not contain the symlink or directory at all Assert.That(systemIO.DirectoryExists(restoreSymlinkDir), Is.False); Assert.That(systemIO.FileExists(restoreSymlinkDir), Is.False); break; + default: + Assert.Fail($"Unexpected symlink policy"); + break; } } } From edb2819d6f982e3721d92bcef2f30918815a35cd Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Fri, 18 Sep 2020 19:58:18 -0700 Subject: [PATCH 29/32] Disable "legacy path handling" for executables This is a defensive measure in case legacy path handling has been enabled at the system level. Duplicati requires that legacy path handling be _disabled_ to function properly. Issue #4295 appears to be a result of legacy path handling being enabled globally. --- Duplicati/CommandLine/BackendTester/app.config | 1 + Duplicati/CommandLine/BackendTool/app.config | 1 + Duplicati/CommandLine/ConfigurationImporter/app.config | 1 + Duplicati/CommandLine/app.config | 1 + Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config | 1 + Duplicati/Library/AutoUpdater/app.config | 3 +++ Duplicati/Library/Snapshots/app.config | 1 + Duplicati/Server/app.config | 1 + Duplicati/Service/app.config | 1 + Duplicati/WindowsService/App.config | 3 +++ 10 files changed, 14 insertions(+) diff --git a/Duplicati/CommandLine/BackendTester/app.config b/Duplicati/CommandLine/BackendTester/app.config index 85bf1086d..f8e90b9d2 100644 --- a/Duplicati/CommandLine/BackendTester/app.config +++ b/Duplicati/CommandLine/BackendTester/app.config @@ -4,6 +4,7 @@ + diff --git a/Duplicati/CommandLine/BackendTool/app.config b/Duplicati/CommandLine/BackendTool/app.config index e79c0a8bf..bf21025c0 100644 --- a/Duplicati/CommandLine/BackendTool/app.config +++ b/Duplicati/CommandLine/BackendTool/app.config @@ -4,6 +4,7 @@ + diff --git a/Duplicati/CommandLine/ConfigurationImporter/app.config b/Duplicati/CommandLine/ConfigurationImporter/app.config index fb8872460..d32715a66 100644 --- a/Duplicati/CommandLine/ConfigurationImporter/app.config +++ b/Duplicati/CommandLine/ConfigurationImporter/app.config @@ -4,6 +4,7 @@ + diff --git a/Duplicati/CommandLine/app.config b/Duplicati/CommandLine/app.config index 74c0529e7..7deb7437d 100644 --- a/Duplicati/CommandLine/app.config +++ b/Duplicati/CommandLine/app.config @@ -4,6 +4,7 @@ + diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config b/Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config index 25008be6c..9eaab5dd7 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/app.config @@ -2,6 +2,7 @@ + diff --git a/Duplicati/Library/AutoUpdater/app.config b/Duplicati/Library/AutoUpdater/app.config index 72546de59..4bbc5293b 100644 --- a/Duplicati/Library/AutoUpdater/app.config +++ b/Duplicati/Library/AutoUpdater/app.config @@ -3,4 +3,7 @@ + + + diff --git a/Duplicati/Library/Snapshots/app.config b/Duplicati/Library/Snapshots/app.config index 92ad670ed..ce73c9a49 100644 --- a/Duplicati/Library/Snapshots/app.config +++ b/Duplicati/Library/Snapshots/app.config @@ -4,6 +4,7 @@ + diff --git a/Duplicati/Server/app.config b/Duplicati/Server/app.config index 25008be6c..9eaab5dd7 100644 --- a/Duplicati/Server/app.config +++ b/Duplicati/Server/app.config @@ -2,6 +2,7 @@ + diff --git a/Duplicati/Service/app.config b/Duplicati/Service/app.config index 85bf1086d..f8e90b9d2 100644 --- a/Duplicati/Service/app.config +++ b/Duplicati/Service/app.config @@ -4,6 +4,7 @@ + diff --git a/Duplicati/WindowsService/App.config b/Duplicati/WindowsService/App.config index d56293618..79ce3c6f4 100644 --- a/Duplicati/WindowsService/App.config +++ b/Duplicati/WindowsService/App.config @@ -3,4 +3,7 @@ + + + From 9b977b7ba04a940868754fd15a37b99fbd77f1bd Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Sat, 26 Sep 2020 15:23:28 +0200 Subject: [PATCH 30/32] Version bump to v2.0.5.111-2.0.5.111_canary_2020-09-26 You can download this build from: Binaries: https://updates.duplicati.com/canary/duplicati-2.0.5.111_canary_2020-09-26.zip Signature file: https://updates.duplicati.com/canary/duplicati-2.0.5.111_canary_2020-09-26.zip.sig ASCII signature file: https://updates.duplicati.com/canary/duplicati-2.0.5.111_canary_2020-09-26.zip.sig.asc MD5: 0b7325121134c8eddaeab5bd147fc9e3 SHA1: 8ee10b030ec5d62bc24fa3d89e5c3624596c26de SHA256: 242ec9be83b65bcccb59c9c3dd9bb8dd5bfb5fa85a34478c7f2740a07d57550c --- Updates/build_version.txt | 2 +- changelog.txt | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Updates/build_version.txt b/Updates/build_version.txt index bc6298e80..58c9bdf9d 100644 --- a/Updates/build_version.txt +++ b/Updates/build_version.txt @@ -1 +1 @@ -110 +111 diff --git a/changelog.txt b/changelog.txt index cbee0f275..21b797425 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,17 @@ +2020-09-26 - 2.0.5.111_canary_2020-09-26 +========== +* Support for Windows paths starting with "\\?\", thanks @dferreyra +* Improved performance of USN, thanks @dgehri +* Improved performance for restoring, thanks @pcmind +* Added support for filters with literals that would normally be treated as wildcards, thanks @dferreyra +* Improved build and tests, thanks @warwickmm and @dferreyra +* Switched to the new built-in .Net support for long paths, thanks @dferreyra +* Fixed issues with the Docker images by including mono-complete, thanks @warwickmm +* Fixed an issue with exporting passwords with url-encoding characters, thanks @warwickmm +* Fixed an issue with displaying icons in the restore tree, thanks @warwickmm +* Fixed a type that prevented a warning box from showing, thanks @warwickmm +* Added Storadera as an S3 destination, thanks @warwickmm + 2020-08-10 - 2.0.5.110_canary_2020-08-10 ========== * Fixed an issue with snapshot paths, thanks @dferreyra From a8b0df57d43cbb6ecdd312955ec50417946fbc6b Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Sun, 27 Sep 2020 10:44:25 -0700 Subject: [PATCH 31/32] Avoid clock issues when quickly running successive backups in tests. This is an attempt to avoid errors like "System.Exception : The previous backup has time 9/27/2020 5:25:56 PM, but this backup has time 9/27/2020 5:25:55 PM. Something is wrong with the clock." We are seeing these errors often recently in AppVeyor. --- Duplicati/UnitTest/ProblematicPathTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Duplicati/UnitTest/ProblematicPathTests.cs b/Duplicati/UnitTest/ProblematicPathTests.cs index eb2e815d6..e470f67c3 100644 --- a/Duplicati/UnitTest/ProblematicPathTests.cs +++ b/Duplicati/UnitTest/ProblematicPathTests.cs @@ -140,6 +140,9 @@ namespace Duplicati.UnitTest Assert.AreEqual(directories.Count, backupResults.ExaminedFiles); } + // Block for a small amount of time to avoid clock issues when quickly running successive backups. + System.Threading.Thread.Sleep(1000); + // Backup with verbatim asterisk in include filter should include // one directory in Linux and zero directories in Windows. filter = new FilterExpression("@" + SystemIO.IO_OS.PathCombine(dirWithAsterisk, file)); From 6e0611f1ae42f4e47a4c0935b02cc22b458ee94e Mon Sep 17 00:00:00 2001 From: Dean Ferreyra Date: Mon, 28 Sep 2020 15:22:46 -0700 Subject: [PATCH 32/32] Disable "legacy path handling" for executables This disables legacy path handling by changing the `AssemblyRedirects.xml` that's deployed as the app.config files in the installation image. Issue #4295 appears to be a result of legacy path handling being enabled globally. --- Installer/AssemblyRedirects.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/Installer/AssemblyRedirects.xml b/Installer/AssemblyRedirects.xml index d275e04da..ca39a1d2f 100644 --- a/Installer/AssemblyRedirects.xml +++ b/Installer/AssemblyRedirects.xml @@ -1,6 +1,7 @@ +