From b8fe9b9ce31872e64f3658881c730e14469034eb Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 9 Oct 2024 19:52:42 +0200 Subject: [PATCH 01/14] First .net8.0 try Only for windows, because it uses Windows.Forms --- SparkleShare.sln | 102 ++-- SparkleShare/Common/AboutController.cs | 29 +- SparkleShare/Common/SparkleShare.shproj | 9 +- .../Windows/SparkleShare.Windows.csproj | 115 +--- .../SparkleShareInviteOpener/app.config | 3 +- .../Windows/UserInterface/EventLog.cs | 2 +- .../Windows/UserInterface/NotifyIcon.cs | 3 +- SparkleShare/Windows/app.config | 4 +- SparkleShare/Windows/postBuild.cmd | 2 +- Sparkles/BaseFetcher.cs | 211 ++++---- Sparkles/BaseListener.cs | 167 +++--- Sparkles/BaseRepository.cs | 498 ++++++++++-------- Sparkles/ChangeSet.cs | 72 +-- Sparkles/Command.cs | 88 ++-- Sparkles/Configuration.cs | 351 ++++++------ Sparkles/Extensions.cs | 197 +++---- Sparkles/Git/Git.Command.cs | 12 +- Sparkles/Git/Git.Fetcher.cs | 6 +- Sparkles/Git/Git.Repository.cs | 36 +- Sparkles/Git/Sparkles.Git.csproj | 21 +- Sparkles/InstallationInfo.Directory.cs | 6 +- Sparkles/InstallationInfo.Directory.cs.in | 7 +- Sparkles/InstallationInfo.cs | 131 +++-- Sparkles/Invite.cs | 110 ++-- Sparkles/ListenerFactory.cs | 36 +- Sparkles/Logger.cs | 71 +-- Sparkles/OpenSSLCommand.cs | 18 +- Sparkles/Preset.cs | 116 ++-- Sparkles/SSHAuthenticationInfo.cs | 103 ++-- Sparkles/SSHCommand.cs | 25 +- Sparkles/SSHFetcher.cs | 111 ++-- Sparkles/Sparkles.csproj | 45 +- Sparkles/TcpListener.cs | 210 +++++--- Sparkles/Tests/Sparkles.Tests.csproj | 55 +- Sparkles/Tests/Test.cs | 81 +-- Sparkles/User.cs | 12 +- Sparkles/Watcher.cs | 24 +- 37 files changed, 1648 insertions(+), 1441 deletions(-) diff --git a/SparkleShare.sln b/SparkleShare.sln index b4acb8036..93553db29 100755 --- a/SparkleShare.sln +++ b/SparkleShare.sln @@ -1,73 +1,57 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sparkles", "Sparkles\Sparkles.csproj", "{2C914413-B31C-4362-93C7-1AE34F09112A}" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35303.130 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sparkles", "Sparkles\Sparkles.csproj", "{2C914413-B31C-4362-93C7-1AE34F09112A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sparkles.Git", "Sparkles\Git\Sparkles.Git.csproj", "{009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sparkles.Git", "Sparkles\Git\Sparkles.Git.csproj", "{009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SparkleShare.Windows", "SparkleShare\Windows\SparkleShare.Windows.csproj", "{728483AA-E34B-4441-BF2C-C8BC2901E4E0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SparkleShare.Linux", "SparkleShare\Linux\SparkleShare.Linux.csproj", "{5714D3CA-88A6-4330-A29D-4CA90D1D193C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SparkleShare.Windows", "SparkleShare\Windows\SparkleShare.Windows.csproj", "{728483AA-E34B-4441-BF2C-C8BC2901E4E0}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SparkleShare", "SparkleShare\Common\SparkleShare.shproj", "{F16E3683-B622-4654-B799-99C8D68AA963}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SparkleShare.Mac", "SparkleShare\Mac\SparkleShare.Mac.csproj", "{8FCDF699-E2C3-4CB3-AF98-44198972AFC0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sparkles.Tests", "Sparkles\Tests\Sparkles.Tests.csproj", "{8AB2969A-951F-4146-A0DD-C46D7526AC20}" EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - SparkleShare\Common\SparkleShare.projitems*{5714d3ca-88a6-4330-a29d-4ca90d1d193c}*SharedItemsImports = 4 - SparkleShare\Common\SparkleShare.projitems*{728483aa-e34b-4441-bf2c-c8bc2901e4e0}*SharedItemsImports = 4 - SparkleShare\Common\SparkleShare.projitems*{f16e3683-b622-4654-b799-99c8d68aa963}*SharedItemsImports = 13 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution - Release|Any CPU = Release|Any CPU Debug|Any CPU = Debug|Any CPU + DebugMac|Any CPU = DebugMac|Any CPU + DebugWindows|Any CPU = DebugWindows|Any CPU + Release|Any CPU = Release|Any CPU ReleaseDist|Any CPU = ReleaseDist|Any CPU ReleaseMac|Any CPU = ReleaseMac|Any CPU - DebugMac|Any CPU = DebugMac|Any CPU ReleaseWindows|Any CPU = ReleaseWindows|Any CPU - DebugWindows|Any CPU = DebugWindows|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|Any CPU.Build.0 = Release|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|Any CPU.Build.0 = ReleaseMac|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|Any CPU.Build.0 = DebugMac|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|Any CPU.Build.0 = Debug|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|Any CPU.Build.0 = DebugMac|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU - {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU - {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|Any CPU.ActiveCfg = Release|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|Any CPU.Build.0 = Release|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|Any CPU.Build.0 = ReleaseMac|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.Release|Any CPU.Build.0 = Release|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.DebugWindows|Any CPU.ActiveCfg = DebugMac|Any CPU - {5714D3CA-88A6-4330-A29D-4CA90D1D193C}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|Any CPU.Build.0 = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|Any CPU.Build.0 = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|Any CPU.Build.0 = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU @@ -78,21 +62,22 @@ Global {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU - {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.Release|Any CPU.Build.0 = Release|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.ReleaseDist|Any CPU.ActiveCfg = ReleaseDist|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.ReleaseDist|Any CPU.Build.0 = ReleaseDist|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.ReleaseMac|Any CPU.Build.0 = ReleaseMac|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.DebugMac|Any CPU.Build.0 = DebugMac|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.DebugWindows|Any CPU.ActiveCfg = DebugMac|Any CPU - {8FCDF699-E2C3-4CB3-AF98-44198972AFC0}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseMac|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|Any CPU.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|Any CPU.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|Any CPU.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|Any CPU.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|Any CPU.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -100,6 +85,10 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {36A991A8-DF23-4A30-9490-252CDA05BE90} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + SparkleShare\Common\SparkleShare.projitems*{728483aa-e34b-4441-bf2c-c8bc2901e4e0}*SharedItemsImports = 5 + SparkleShare\Common\SparkleShare.projitems*{f16e3683-b622-4654-b799-99c8d68aa963}*SharedItemsImports = 13 + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.DotNetNamingPolicy = $1 @@ -116,4 +105,3 @@ Global $3.scope = text/x-csharp EndGlobalSection EndGlobal - diff --git a/SparkleShare/Common/AboutController.cs b/SparkleShare/Common/AboutController.cs index 5095cb458..b16b00f38 100644 --- a/SparkleShare/Common/AboutController.cs +++ b/SparkleShare/Common/AboutController.cs @@ -17,6 +17,8 @@ using System; using System.Net; +using System.Net.Http.Headers; +using System.Net.Http; using System.Threading; using Sparkles; @@ -63,21 +65,24 @@ void CheckForNewVersion () ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; - var web_client = new WebClient (); + HttpClient web_client = new(); var uri = new Uri ("https://www.sparkleshare.org/version"); + HttpClient client = new(); + HttpResponseMessage response = null!; - try { - string latest_version = web_client.DownloadString (uri); - latest_version = latest_version.Trim (); - - if (new Version (latest_version) > new Version (RunningVersion)) - UpdateLabelEvent ("An update (version " + latest_version + ") is available!"); - else - UpdateLabelEvent ("✓ You are running the latest version"); + try + { + string latest_version = client.GetStringAsync(uri).GetAwaiter().GetResult(); - } catch (Exception e) { - Logger.LogInfo ("UI", "Failed to download " + uri , e); - UpdateLabelEvent ("Couldn’t check for updates\t"); + if (new Version(latest_version) > new Version(RunningVersion)) + UpdateLabelEvent("An update (version " + latest_version + ") is available!"); + else + UpdateLabelEvent("✓ You are running the latest version"); + } + catch (Exception e) + { + Logger.LogInfo("UI", "Failed to download " + uri, e); + UpdateLabelEvent("Couldn’t check for updates\t"); } } } diff --git a/SparkleShare/Common/SparkleShare.shproj b/SparkleShare/Common/SparkleShare.shproj index 0495fd247..36fe2e92c 100644 --- a/SparkleShare/Common/SparkleShare.shproj +++ b/SparkleShare/Common/SparkleShare.shproj @@ -1,14 +1,13 @@ - - - 8.0.30703 - 2.0 + + {F16E3683-B622-4654-B799-99C8D68AA963} - + 14.0 + diff --git a/SparkleShare/Windows/SparkleShare.Windows.csproj b/SparkleShare/Windows/SparkleShare.Windows.csproj index b0b03e5c9..4dce1dbfc 100755 --- a/SparkleShare/Windows/SparkleShare.Windows.csproj +++ b/SparkleShare/Windows/SparkleShare.Windows.csproj @@ -1,19 +1,7 @@ - - + - Debug - AnyCPU - 8.0.30703 - {728483AA-E34B-4441-BF2C-C8BC2901E4E0} + net8.0-windows WinExe - SparkleShare.Windows - 2.0 - SparkleShare.Windows - - - 3.5 - - false publish\ true @@ -32,93 +20,35 @@ Images\sparkleshare-app.ico - v4.5 + false + true + true + true - pdbonly - true .\bin - prompt - 4 AllRules.ruleset - AnyCPU - TRACE true - false .\bin\ - TRACE;DEBUG - prompt - full - true - 4 - false - false true .\bin - TRACE true - pdbonly - x86 - prompt AllRules.ruleset ..\..\bin\ - TRACE;DEBUG - prompt - full true - 4 false true .\bin - TRACE;DEBUG - full - AnyCPU - prompt MinimumRecommendedRules.ruleset - true - - - - - - - - - - - - - - - - - - - - - - - Note.xaml - - - - - - - - - - - @@ -223,7 +153,6 @@ Presets\bitbucket.png Always - @@ -268,22 +197,22 @@ - - Designer - MSBuild:Compile - + + - - {2C914413-B31C-4362-93C7-1AE34F09112A} - Sparkles - - - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE} - Sparkles.Git - + + - - "$(ProjectDir)\postBuild.cmd" "$(TargetDir)git_scm" - - + + + + + + + + + + + + \ No newline at end of file diff --git a/SparkleShare/Windows/SparkleShareInviteOpener/app.config b/SparkleShare/Windows/SparkleShareInviteOpener/app.config index e36560333..3957049c3 100644 --- a/SparkleShare/Windows/SparkleShareInviteOpener/app.config +++ b/SparkleShare/Windows/SparkleShareInviteOpener/app.config @@ -1,3 +1,4 @@ - + + diff --git a/SparkleShare/Windows/UserInterface/EventLog.cs b/SparkleShare/Windows/UserInterface/EventLog.cs index bf8bc6a97..346931895 100644 --- a/SparkleShare/Windows/UserInterface/EventLog.cs +++ b/SparkleShare/Windows/UserInterface/EventLog.cs @@ -337,7 +337,7 @@ private void WriteOutImages() } } - [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + // TODO: find replacement [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] [ComVisible(true)] public class SparkleScriptingObject { diff --git a/SparkleShare/Windows/UserInterface/NotifyIcon.cs b/SparkleShare/Windows/UserInterface/NotifyIcon.cs index f12351119..7ff3fc47f 100644 --- a/SparkleShare/Windows/UserInterface/NotifyIcon.cs +++ b/SparkleShare/Windows/UserInterface/NotifyIcon.cs @@ -82,10 +82,11 @@ private Forms.NotifyIcon Notification { public NotifyIcon() { VisibilityProperty.OverrideMetadata(typeof(NotifyIcon), new PropertyMetadata(OnVisibilityChanged)); + // TODO ContextMenu wird nicht mehr untersttzt. Verwenden Sie stattdessen ContextMenuStrip. Weitere Informationen finden Sie unter: https://docs.microsoft.com/en-us/dotnet/core/compatibility/winforms#removed-controls Notification = new Forms.NotifyIcon { Text = Text, Visible = true, - ContextMenu = new Forms.ContextMenu() + ContextMenuStrip = new Forms.ContextMenuStrip() }; Notification.MouseDown += OnMouseDown; Notification.MouseUp += OnMouseUp; diff --git a/SparkleShare/Windows/app.config b/SparkleShare/Windows/app.config index 51278a456..0a618fa6f 100644 --- a/SparkleShare/Windows/app.config +++ b/SparkleShare/Windows/app.config @@ -1,3 +1,3 @@ - + - + diff --git a/SparkleShare/Windows/postBuild.cmd b/SparkleShare/Windows/postBuild.cmd index c00dacb2d..d8ee6bc30 100755 --- a/SparkleShare/Windows/postBuild.cmd +++ b/SparkleShare/Windows/postBuild.cmd @@ -9,7 +9,7 @@ REM download git FOR /F "usebackq tokens=1" %%i IN ("%~dp0git.download") DO SET url=%%i FOR /F "usebackq tokens=2" %%i IN ("%~dp0git.download") DO SET md5hash=%%i CALL :downloadandverify %url% "%~dp0PortableGit.7z.exe" %md5hash% -"%~dp0PortableGit.7z.exe" -o %OUTDIR% -y +"%~dp0PortableGit.7z.exe" -o "%OUTDIR%" -y DEL "%~dp0PortableGit.7z.exe" DEL /s /q "%~dp0OpenSSH-Win32.zip" diff --git a/Sparkles/BaseFetcher.cs b/Sparkles/BaseFetcher.cs index 0cb1b90bb..928835b4b 100644 --- a/Sparkles/BaseFetcher.cs +++ b/Sparkles/BaseFetcher.cs @@ -20,36 +20,39 @@ using System.IO; using System.Threading; -namespace Sparkles { +namespace Sparkles +{ - public class SparkleFetcherInfo { - public string Address; // TODO: Uri object - public string RemotePath; + public class SparkleFetcherInfo + { + public string Address = null!; // TODO: Uri object + public string RemotePath = null!; - public string Fingerprint; + public string Fingerprint = null!; - public string Backend; - public string TargetDirectory; + public string Backend = null!; + public string TargetDirectory = null!; public bool FetchPriorHistory; - public string AnnouncementsUrl; // TODO: Uri object + public string AnnouncementsUrl = null!; // TODO: Uri object } - public abstract class BaseFetcher { + public abstract class BaseFetcher + { public event Action Started = delegate { }; public event Action Failed = delegate { }; public event FinishedEventHandler Finished = delegate { }; - public delegate void FinishedEventHandler (StorageType storage_type, string [] warnings); + public delegate void FinishedEventHandler(StorageType storage_type, string[] warnings); public event ProgressChangedEventHandler ProgressChanged = delegate { }; - public delegate void ProgressChangedEventHandler (double percentage, double speed, string information); + public delegate void ProgressChangedEventHandler(double percentage, double speed, string information); - public abstract bool Fetch (); - public abstract void Stop (); + public abstract bool Fetch(); + public abstract void Stop(); public bool IsActive { get; protected set; } public double ProgressPercentage { get; private set; } public double ProgressSpeed { get; private set; } @@ -57,10 +60,10 @@ public abstract class BaseFetcher { protected abstract bool IsFetchedRepoEmpty { get; } public StorageType FetchedRepoStorageType { get; protected set; } - public abstract bool IsFetchedRepoPasswordCorrect (string password); - public abstract void EnableFetchedRepoCrypto (string password); + public abstract bool IsFetchedRepoPasswordCorrect(string password); + public abstract void EnableFetchedRepoCrypto(string password); - public readonly List AvailableStorageTypes = new List (); + public readonly List AvailableStorageTypes = new List(); public Uri RemoteUrl { get; protected set; } @@ -70,132 +73,154 @@ public abstract class BaseFetcher { public SparkleFetcherInfo OriginalFetcherInfo; - protected List warnings = new List (); - protected List errors = new List (); + protected List warnings = new List(); + protected List errors = new List(); - public string [] Warnings { - get { - return warnings.ToArray (); + public string[] Warnings + { + get + { + return warnings.ToArray(); } } - public string [] Errors { - get { - return errors.ToArray (); + public string[] Errors + { + get + { + return errors.ToArray(); } } - - protected BaseFetcher (SparkleFetcherInfo info) + + protected BaseFetcher(SparkleFetcherInfo info) { FetchedRepoStorageType = StorageType.Unknown; - AvailableStorageTypes.Add ( - new StorageTypeInfo (StorageType.Plain, "Plain Storage", "Nothing fancy;\nmaximum compatibility")); + AvailableStorageTypes.Add( + new StorageTypeInfo(StorageType.Plain, "Plain Storage", "Nothing fancy;\nmaximum compatibility")); OriginalFetcherInfo = info; RequiredFingerprint = info.Fingerprint; - FetchPriorHistory = info.FetchPriorHistory; - string remote_path = info.RemotePath.Trim ("/".ToCharArray ()); - string address = info.Address; + FetchPriorHistory = info.FetchPriorHistory; + string remote_path = info.RemotePath.Trim("/".ToCharArray()); + string address = info.Address; - if (address.EndsWith ("/", StringComparison.InvariantCulture)) - address = address.Substring (0, address.Length - 1); + if (address.EndsWith("/", StringComparison.InvariantCulture)) + address = address.Substring(0, address.Length - 1); - if (!remote_path.StartsWith ("/", StringComparison.InvariantCulture)) + if (!remote_path.StartsWith("/", StringComparison.InvariantCulture)) remote_path = "/" + remote_path; - if (!address.Contains ("://")) + if (!address.Contains("://")) address = "ssh://" + address; TargetFolder = info.TargetDirectory; - - RemoteUrl = new Uri (address + remote_path); - IsActive = false; + // TODO: fix this hack + RemoteUrl = new UriBuilder + { + Scheme = "ssh", + Host = "github.com", + Path = remote_path, + UserName = "git" + }.Uri; + IsActive = false; } - Thread thread; + Thread thread = null!; - public void Start () + public void Start() { IsActive = true; - Started (); + Started(); + + Logger.LogInfo("Fetcher", TargetFolder + " | Fetching folder: " + RemoteUrl); - Logger.LogInfo ("Fetcher", TargetFolder + " | Fetching folder: " + RemoteUrl); + try + { + if (Directory.Exists(TargetFolder)) + Directory.Delete(TargetFolder, recursive: true); - try { - if (Directory.Exists (TargetFolder)) - Directory.Delete (TargetFolder, recursive: true); - - } catch (IOException) { - errors.Add ("\"" + TargetFolder + "\" is read-only."); - Failed (); + } + catch (IOException) + { + errors.Add("\"" + TargetFolder + "\" is read-only."); + Failed(); return; } - thread = new Thread (() => { - if (Fetch ()) { - Thread.Sleep (500); - Logger.LogInfo ("Fetcher", "Finished"); + thread = new Thread(() => + { + if (Fetch()) + { + Thread.Sleep(500); + Logger.LogInfo("Fetcher", "Finished"); IsActive = false; - Finished (FetchedRepoStorageType, Warnings); + Finished(FetchedRepoStorageType, Warnings); - } else { - Thread.Sleep (500); + } + else + { + Thread.Sleep(500); + + if (IsActive) + { + Logger.LogInfo("Fetcher", "Failed"); + Failed(); - if (IsActive) { - Logger.LogInfo ("Fetcher", "Failed"); - Failed (); - - } else { - Logger.LogInfo ("Fetcher", "Failed: cancelled by user"); + } + else + { + Logger.LogInfo("Fetcher", "Failed: cancelled by user"); } IsActive = false; } }); - thread.Start (); + thread.Start(); } - public void Complete () + public void Complete() { - if (FetchedRepoStorageType == StorageType.Unknown) { - Complete (StorageType.Plain); + if (FetchedRepoStorageType == StorageType.Unknown) + { + Complete(StorageType.Plain); return; } - this.Complete (FetchedRepoStorageType); + this.Complete(FetchedRepoStorageType); } - public virtual string Complete (StorageType storage_type) + public virtual string Complete(StorageType storage_type) { FetchedRepoStorageType = storage_type; if (IsFetchedRepoEmpty) - CreateInitialChangeSet (); - - return Path.GetRandomFileName ().SHA256 (); + CreateInitialChangeSet(); + + return Path.GetRandomFileName().SHA256(); } // Create an initial change set when the // user has fetched an empty remote folder - void CreateInitialChangeSet () + void CreateInitialChangeSet() { - string n = Environment.NewLine; - string file_path = Path.Combine (TargetFolder, "SparkleShare.txt"); + string n = Environment.NewLine; + string file_path = Path.Combine(TargetFolder, "SparkleShare.txt"); - var uri_builder = new UriBuilder (RemoteUrl); + var uri_builder = new UriBuilder(RemoteUrl); // Don't expose possible username or password - if (RemoteUrl.Scheme.StartsWith ("http", StringComparison.InvariantCultureIgnoreCase)) { + if (RemoteUrl.Scheme.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) + { uri_builder.UserName = ""; uri_builder.Password = ""; } @@ -211,50 +236,52 @@ void CreateInitialChangeSet () "Have fun! :)" + n; if (FetchedRepoStorageType == StorageType.Encrypted) - text = text.Replace ("a SparkleShare repository", "an encrypted SparkleShare repository"); + text = text.Replace("a SparkleShare repository", "an encrypted SparkleShare repository"); - File.WriteAllText (file_path, text); + File.WriteAllText(file_path, text); } DateTime progress_last_change = DateTime.Now; - protected void OnProgressChanged (double percentage, double speed, string information) { + protected void OnProgressChanged(double percentage, double speed, string information) + { // Only trigger the ProgressChanged event once per second - if (DateTime.Compare (this.progress_last_change, DateTime.Now.Subtract (new TimeSpan (0, 0, 0, 1))) >= 0) + if (DateTime.Compare(this.progress_last_change, DateTime.Now.Subtract(new TimeSpan(0, 0, 0, 1))) >= 0) return; - ProgressChanged (percentage, speed, information); + ProgressChanged(percentage, speed, information); } - public static string GetBackend (string address) + public static string GetBackend(string address) { - if (address.StartsWith ("ssh+", StringComparison.InvariantCultureIgnoreCase)) { - string backend = address.Substring (0, address.IndexOf ("://", StringComparison.InvariantCulture)); - backend = backend.Substring (4); + if (address.StartsWith("ssh+", StringComparison.InvariantCultureIgnoreCase)) + { + string backend = address.Substring(0, address.IndexOf("://", StringComparison.InvariantCulture)); + backend = backend.Substring(4); - return char.ToUpper (backend [0]) + backend.Substring (1); + return char.ToUpper(backend[0]) + backend.Substring(1); } return "Git"; } - public virtual string FormatName () + public virtual string FormatName() { - return Path.GetFileName (RemoteUrl.AbsolutePath); + return Path.GetFileName(RemoteUrl.AbsolutePath); } - public void Dispose () + public void Dispose() { - if (thread != null) - thread.Abort (); + if (thread != null) + thread.Interrupt(); } - protected string [] ExcludeRules = { + protected string[] ExcludeRules = { "*.autosave", // Various autosaving apps "*~", // gedit and emacs ".~lock.*", // LibreOffice diff --git a/Sparkles/BaseListener.cs b/Sparkles/BaseListener.cs index bd1a3f95e..f19f9636a 100644 --- a/Sparkles/BaseListener.cs +++ b/Sparkles/BaseListener.cs @@ -17,11 +17,13 @@ using System; using System.Collections.Generic; -using System.Timers; -namespace Sparkles { - public enum DisconnectReason { +namespace Sparkles +{ + + public enum DisconnectReason + { None, TimeOut, SystemSleep @@ -30,160 +32,177 @@ public enum DisconnectReason { // A persistent connection to the server that // listens for change notifications - public abstract class BaseListener { + public abstract class BaseListener + { public event Action Connected = delegate { }; public event DisconnectedEventHandler Disconnected = delegate { }; - public delegate void DisconnectedEventHandler (DisconnectReason reason); + public delegate void DisconnectedEventHandler(DisconnectReason reason); public event AnnouncementReceivedEventHandler AnnouncementReceived = delegate { }; - public delegate void AnnouncementReceivedEventHandler (Announcement announcement); + public delegate void AnnouncementReceivedEventHandler(Announcement announcement); public readonly Uri Server; - public abstract void Connect (); + public abstract void Connect(); public abstract bool IsConnected { get; } public abstract bool IsConnecting { get; } - protected abstract void AnnounceInternal (Announcement announcent); - protected abstract void AlsoListenToInternal (string folder_identifier); + protected abstract void AnnounceInternal(Announcement announcent); + protected abstract void AlsoListenToInternal(string folder_identifier); - protected List channels = new List (); + protected List channels = new List(); private int max_recent_announcements = 10; private Dictionary> recent_announcements = - new Dictionary> (); + new Dictionary>(); - private Dictionary queue_up = new Dictionary (); + private Dictionary queue_up = new Dictionary(); - private Timer reconnect_timer = new Timer { + private System.Timers.Timer? reconnect_timer = new System.Timers.Timer + { Interval = 60 * 1000, Enabled = true }; - public BaseListener (Uri server, string folder_identifier) + public BaseListener(Uri server, string folder_identifier) { Server = server; - this.channels.Add (folder_identifier); + this.channels.Add(folder_identifier); this.reconnect_timer.Elapsed += OnTimerElapsed; - this.reconnect_timer.Start (); + this.reconnect_timer.Start(); } - private void OnTimerElapsed(object sender, EventArgs args) + private void OnTimerElapsed(object? sender, EventArgs? args) { if (!IsConnected && !IsConnecting) - Reconnect (); + Reconnect(); } - public void Announce (Announcement announcement) + public void Announce(Announcement announcement) { - if (!IsRecentAnnouncement (announcement)) { - if (IsConnected) { - Logger.LogInfo ("Listener", "Announcing message " + announcement.Message + + if (!IsRecentAnnouncement(announcement)) + { + if (IsConnected) + { + Logger.LogInfo("Listener", "Announcing message " + announcement.Message + " to " + announcement.FolderIdentifier + " on " + Server); - AnnounceInternal (announcement); - AddRecentAnnouncement (announcement); + AnnounceInternal(announcement); + AddRecentAnnouncement(announcement); - } else { - Logger.LogInfo ("Listener", "Can't send message to " + Server + ". Queuing message"); - this.queue_up [announcement.FolderIdentifier] = announcement; + } + else + { + Logger.LogInfo("Listener", "Can't send message to " + Server + ". Queuing message"); + this.queue_up[announcement.FolderIdentifier] = announcement; } - } else { - Logger.LogInfo ("Listener", "Already processed message " + announcement.Message + + } + else + { + Logger.LogInfo("Listener", "Already processed message " + announcement.Message + " to " + announcement.FolderIdentifier + " from " + Server); } } - public void AlsoListenTo (string channel) + public void AlsoListenTo(string channel) { - if (!this.channels.Contains (channel)) - this.channels.Add (channel); + if (!this.channels.Contains(channel)) + this.channels.Add(channel); - if (IsConnected) { - Logger.LogInfo ("Listener", "Subscribing to channel " + channel + " on " + Server); - AlsoListenToInternal (channel); + if (IsConnected) + { + Logger.LogInfo("Listener", "Subscribing to channel " + channel + " on " + Server); + AlsoListenToInternal(channel); } } - public void Reconnect () + public void Reconnect() { - Logger.LogInfo ("Listener", "Trying to reconnect to " + Server); - Connect (); + Logger.LogInfo("Listener", "Trying to reconnect to " + Server); + Connect(); } - public void OnConnected () + public void OnConnected() { - foreach (string channel in this.channels.GetRange (0, this.channels.Count)) { - Logger.LogInfo ("Listener", "Subscribing to channel " + channel + " on " + Server); - AlsoListenToInternal (channel); + foreach (string channel in this.channels.GetRange(0, this.channels.Count)) + { + Logger.LogInfo("Listener", "Subscribing to channel " + channel + " on " + Server); + AlsoListenToInternal(channel); } - Logger.LogInfo ("Listener", "Listening for announcements on " + Server); - Connected (); + Logger.LogInfo("Listener", "Listening for announcements on " + Server); + Connected(); - if (this.queue_up.Count > 0) { - Logger.LogInfo ("Listener", "Delivering " + this.queue_up.Count + " queued messages..."); + if (this.queue_up.Count > 0) + { + Logger.LogInfo("Listener", "Delivering " + this.queue_up.Count + " queued messages..."); - foreach (KeyValuePair item in this.queue_up) { + foreach (KeyValuePair item in this.queue_up) + { Announcement announcement = item.Value; - Announce (announcement); + Announce(announcement); } } } - public void OnDisconnected (DisconnectReason reason, string message) + public void OnDisconnected(DisconnectReason reason, string message) { - Logger.LogInfo ("Listener", "Disconnected from " + Server + ": " + message); - Disconnected (reason); + Logger.LogInfo("Listener", "Disconnected from " + Server + ": " + message); + Disconnected(reason); } - public void OnAnnouncement (Announcement announcement) + public void OnAnnouncement(Announcement announcement) { - Logger.LogInfo ("Listener", "Got message " + announcement.Message + " from " + + Logger.LogInfo("Listener", "Got message " + announcement.Message + " from " + announcement.FolderIdentifier + " on " + Server); - if (IsRecentAnnouncement (announcement)) + if (IsRecentAnnouncement(announcement)) return; - AddRecentAnnouncement (announcement); - AnnouncementReceived (announcement); + AddRecentAnnouncement(announcement); + AnnouncementReceived(announcement); } - public virtual void Dispose () + public virtual void Dispose() { - if (this.reconnect_timer != null) { - this.reconnect_timer.Stop (); + if (this.reconnect_timer != null) + { + this.reconnect_timer.Stop(); this.reconnect_timer.Elapsed -= OnTimerElapsed; - this.reconnect_timer.Dispose (); + this.reconnect_timer.Dispose(); this.reconnect_timer = null; } } - private bool IsRecentAnnouncement (Announcement announcement) + private bool IsRecentAnnouncement(Announcement announcement) { - if (!this.recent_announcements.ContainsKey (announcement.FolderIdentifier)) { + if (!this.recent_announcements.ContainsKey(announcement.FolderIdentifier)) + { return false; - } else { - foreach (Announcement recent_announcement in GetRecentAnnouncements (announcement.FolderIdentifier)) { - if (recent_announcement.Message.Equals (announcement.Message)) + } + else + { + foreach (Announcement recent_announcement in GetRecentAnnouncements(announcement.FolderIdentifier)) + { + if (recent_announcement.Message.Equals(announcement.Message)) return true; } @@ -192,25 +211,25 @@ private bool IsRecentAnnouncement (Announcement announcement) } - private List GetRecentAnnouncements (string folder_identifier) + private List GetRecentAnnouncements(string folder_identifier) { - if (!this.recent_announcements.ContainsKey (folder_identifier)) - this.recent_announcements [folder_identifier] = new List (); + if (!this.recent_announcements.ContainsKey(folder_identifier)) + this.recent_announcements[folder_identifier] = new List(); - return this.recent_announcements [folder_identifier]; + return this.recent_announcements[folder_identifier]; } - private void AddRecentAnnouncement (Announcement announcement) + private void AddRecentAnnouncement(Announcement announcement) { List recent_announcements = - GetRecentAnnouncements (announcement.FolderIdentifier); + GetRecentAnnouncements(announcement.FolderIdentifier); - if (!IsRecentAnnouncement (announcement)) - recent_announcements.Add (announcement); + if (!IsRecentAnnouncement(announcement)) + recent_announcements.Add(announcement); if (recent_announcements.Count > this.max_recent_announcements) - recent_announcements.RemoveRange (0, recent_announcements.Count - this.max_recent_announcements); + recent_announcements.RemoveRange(0, recent_announcements.Count - this.max_recent_announcements); } } } diff --git a/Sparkles/BaseRepository.cs b/Sparkles/BaseRepository.cs index 088329227..ec78df09b 100644 --- a/Sparkles/BaseRepository.cs +++ b/Sparkles/BaseRepository.cs @@ -22,9 +22,11 @@ using Timers = System.Timers; -namespace Sparkles { +namespace Sparkles +{ - public enum StorageType { + public enum StorageType + { Unknown, Plain, LargeFiles, @@ -32,7 +34,8 @@ public enum StorageType { } - public class StorageTypeInfo { + public class StorageTypeInfo + { public readonly StorageType Type; @@ -40,7 +43,7 @@ public class StorageTypeInfo { public readonly string Description; - public StorageTypeInfo (StorageType storage_type, string name, string description) + public StorageTypeInfo(StorageType storage_type, string name, string description) { Type = storage_type; @@ -49,7 +52,8 @@ public StorageTypeInfo (StorageType storage_type, string name, string descriptio } } - public enum SyncStatus { + public enum SyncStatus + { Idle, Paused, SyncUp, @@ -57,7 +61,8 @@ public enum SyncStatus { Error } - public enum ErrorStatus { + public enum ErrorStatus + { None, HostUnreachable, HostIdentityChanged, @@ -70,11 +75,12 @@ public enum ErrorStatus { } - public abstract class BaseRepository { + public abstract class BaseRepository + { - public abstract bool SyncUp (); - public abstract bool SyncDown (); - public abstract void RestoreFile (string path, string revision, string target_file_path); + public abstract bool SyncUp(); + public abstract bool SyncDown(); + public abstract void RestoreFile(string path, string revision, string target_file_path); public abstract bool HasUnsyncedChanges { get; set; } public abstract bool HasLocalChanges { get; } public abstract bool HasRemoteChanges { get; } @@ -85,8 +91,8 @@ public abstract class BaseRepository { public abstract List ExcludePaths { get; } public abstract List UnsyncedChanges { get; } - public abstract List GetChangeSets (); - public abstract List GetChangeSets (string path); + public abstract List GetChangeSets(); + public abstract List GetChangeSets(string path); protected StorageType StorageType = StorageType.Plain; @@ -94,13 +100,13 @@ public abstract class BaseRepository { public event SyncStatusChangedEventHandler SyncStatusChanged = delegate { }; - public delegate void SyncStatusChangedEventHandler (SyncStatus new_status); + public delegate void SyncStatusChangedEventHandler(SyncStatus new_status); public event ProgressChangedEventHandler ProgressChanged = delegate { }; - public delegate void ProgressChangedEventHandler (); + public delegate void ProgressChangedEventHandler(); public event NewChangeSetEventHandler NewChangeSet = delegate { }; - public delegate void NewChangeSetEventHandler (ChangeSet change_set); + public delegate void NewChangeSetEventHandler(ChangeSet change_set); public event Action ConflictResolved = delegate { }; public event Action ChangesDetected = delegate { }; @@ -109,51 +115,59 @@ public abstract class BaseRepository { public readonly string LocalPath; public readonly string Name; public readonly Uri RemoteUrl; - public List ChangeSets { get; set; } + public List ChangeSets { get; set; } = null!; public SyncStatus Status { get; set; } public ErrorStatus Error { get; protected set; } public bool IsBuffering { get; set; } public double ProgressPercentage { get; private set; } public double ProgressSpeed { get; private set; } - public string ProgressInformation { get; private set; } + public string ProgressInformation { get; private set; } = null!; - public DateTime LastSync { - get { + public DateTime LastSync + { + get + { if (ChangeSets != null && ChangeSets.Count > 0) - return ChangeSets [0].Timestamp; + return ChangeSets[0].Timestamp; else return DateTime.MinValue; } } - public virtual string Identifier { - get { + public virtual string Identifier + { + get + { if (this.identifier != null) return this.identifier; - string id_path = Path.Combine (LocalPath, ".sparkleshare"); + string id_path = Path.Combine(LocalPath, ".sparkleshare"); - if (File.Exists (id_path)) { - File.SetAttributes (id_path, FileAttributes.Hidden); - this.identifier = File.ReadAllText (id_path).Trim (); + if (File.Exists(id_path)) + { + File.SetAttributes(id_path, FileAttributes.Hidden); + this.identifier = File.ReadAllText(id_path).Trim(); } - if (!string.IsNullOrEmpty (this.identifier)) { + if (!string.IsNullOrEmpty(this.identifier)) + { return this.identifier; - } else { - string config_identifier = this.local_config.IdentifierByName (Name); + } + else + { + string? config_identifier = this.local_config.IdentifierByName(Name); - if (!string.IsNullOrEmpty (config_identifier)) + if (!string.IsNullOrEmpty(config_identifier)) this.identifier = config_identifier; else - this.identifier = Path.GetRandomFileName ().SHA256 (); + this.identifier = Path.GetRandomFileName().SHA256(); - File.WriteAllText (id_path, this.identifier); - File.SetAttributes (id_path, FileAttributes.Hidden); + File.WriteAllText(id_path, this.identifier); + File.SetAttributes(id_path, FileAttributes.Hidden); - Logger.LogInfo ("Local", Name + " | Assigned identifier: " + this.identifier); + Logger.LogInfo("Local", Name + " | Assigned identifier: " + this.identifier); return this.identifier; } @@ -164,174 +178,190 @@ public virtual string Identifier { protected Configuration local_config; string identifier; - BaseListener listener = null; - Watcher watcher; - TimeSpan poll_interval = PollInterval.Short; - DateTime last_poll = DateTime.Now; - Timers.Timer remote_timer = new Timers.Timer () { Interval = 5000 }; + BaseListener listener = null!; + Watcher watcher = null!; + TimeSpan poll_interval = PollInterval.Short; + DateTime last_poll = DateTime.Now; + Timers.Timer remote_timer = new Timers.Timer() { Interval = 5000 }; DisconnectReason last_disconnect_reason = DisconnectReason.None; - bool is_syncing { + bool is_syncing + { get { return (Status == SyncStatus.SyncUp || Status == SyncStatus.SyncDown || IsBuffering); } } - static class PollInterval { - public static readonly TimeSpan Short = new TimeSpan (0, 0, 5, 0); - public static readonly TimeSpan Long = new TimeSpan (0, 0, 15, 0); + static class PollInterval + { + public static readonly TimeSpan Short = new TimeSpan(0, 0, 5, 0); + public static readonly TimeSpan Long = new TimeSpan(0, 0, 15, 0); } - public BaseRepository (string path, Configuration config) + public BaseRepository(string path, Configuration config) { - Logger.LogInfo (path, "Initializing..."); + Logger.LogInfo(path, "Initializing..."); - Status = SyncStatus.Idle; - Error = ErrorStatus.None; + Status = SyncStatus.Idle; + Error = ErrorStatus.None; this.local_config = config; - LocalPath = path; - Name = Path.GetFileName (LocalPath); - RemoteUrl = new Uri (this.local_config.UrlByName (Name)); - IsBuffering = false; - this.identifier = Identifier; + LocalPath = path; + Name = Path.GetFileName(LocalPath); + RemoteUrl = new Uri(this.local_config.UrlByName(Name)!); + IsBuffering = false; + this.identifier = Identifier; - string storage_type = this.local_config.GetFolderOptionalAttribute (Name, "storage_type"); + string storage_type = this.local_config.GetFolderOptionalAttribute(Name, "storage_type")!; - if (!string.IsNullOrEmpty (storage_type)) - StorageType = (StorageType) Enum.Parse (typeof (StorageType), storage_type); + if (!string.IsNullOrEmpty(storage_type)) + StorageType = (StorageType)Enum.Parse(typeof(StorageType), storage_type); - string is_paused = this.local_config.GetFolderOptionalAttribute (Name, "paused"); - if (is_paused != null && is_paused.Equals (bool.TrueString)) + string is_paused = this.local_config.GetFolderOptionalAttribute(Name, "paused")!; + if (is_paused != null && is_paused.Equals(bool.TrueString)) Status = SyncStatus.Paused; - string identifier_file_path = Path.Combine (LocalPath, ".sparkleshare"); - File.SetAttributes (identifier_file_path, FileAttributes.Hidden); + string identifier_file_path = Path.Combine(LocalPath, ".sparkleshare"); + File.SetAttributes(identifier_file_path, FileAttributes.Hidden); if (!UseCustomWatcher) - this.watcher = new Watcher (LocalPath); + this.watcher = new Watcher(LocalPath); - new Thread (() => CreateListener ()).Start (); + new Thread(() => CreateListener()).Start(); this.remote_timer.Elapsed += RemoteTimerElapsedDelegate; } - void RemoteTimerElapsedDelegate (object sender, EventArgs args) + void RemoteTimerElapsedDelegate(Object? sender, System.Timers.ElapsedEventArgs? args) { if (this.is_syncing || IsBuffering || Status == SyncStatus.Paused) return; - - int time_comparison = DateTime.Compare (this.last_poll, DateTime.Now.Subtract (this.poll_interval)); - - if (time_comparison < 0) { + + int time_comparison = DateTime.Compare(this.last_poll, DateTime.Now.Subtract(this.poll_interval)); + + if (time_comparison < 0) + { if (HasUnsyncedChanges && !this.is_syncing) - SyncUpBase (); - + SyncUpBase(); + this.last_poll = DateTime.Now; - + if (HasRemoteChanges && !this.is_syncing) - SyncDownBase (); - + SyncDownBase(); + // if (this.listener.IsConnected) // this.poll_interval = PollInterval.Long; } - + // In the unlikely case that we haven't synced up our // changes or the server was down, sync up again if (HasUnsyncedChanges && !this.is_syncing && Error == ErrorStatus.None) - SyncUpBase (); - - if (Status != SyncStatus.Idle && Status != SyncStatus.Error) { + SyncUpBase(); + + if (Status != SyncStatus.Idle && Status != SyncStatus.Error) + { Status = SyncStatus.Idle; - SyncStatusChanged (Status); + SyncStatusChanged(Status); } } - public void Initialize () + public void Initialize() { - ChangeSets = GetChangeSets (); + ChangeSets = GetChangeSets(); // Sync up everything that changed since we've been offline - new Thread (() => { - if (Status != SyncStatus.Paused) { + new Thread(() => + { + if (Status != SyncStatus.Paused) + { if (HasRemoteChanges) - SyncDownBase (); + SyncDownBase(); - if (HasUnsyncedChanges || HasLocalChanges) { - do { - SyncUpBase (); + if (HasUnsyncedChanges || HasLocalChanges) + { + do + { + SyncUpBase(); } while (HasLocalChanges); } } - + if (!UseCustomWatcher) this.watcher.ChangeEvent += OnFileActivity; - this.remote_timer.Start (); - - }).Start (); + this.remote_timer.Start(); + + }).Start(); } - Object buffer_lock = new Object (); + Object buffer_lock = new Object(); - public void OnFileActivity (FileSystemEventArgs args) + public void OnFileActivity(FileSystemEventArgs args) { if (IsBuffering || this.is_syncing) return; - if (args != null) { - foreach (string exclude_path in ExcludePaths) { - if (args.FullPath.Contains (Path.DirectorySeparatorChar + exclude_path)) + if (args != null) + { + foreach (string exclude_path in ExcludePaths) + { + if (args.FullPath.Contains(Path.DirectorySeparatorChar + exclude_path)) return; } } - - if (Status == SyncStatus.Paused) { - ChangesDetected (); + + if (Status == SyncStatus.Paused) + { + ChangesDetected(); return; } - lock (this.buffer_lock) { + lock (this.buffer_lock) + { if (IsBuffering || this.is_syncing || !HasLocalChanges) return; IsBuffering = true; } - ChangesDetected (); + ChangesDetected(); if (!UseCustomWatcher) - this.watcher.Disable (); + this.watcher.Disable(); - Logger.LogInfo ("Local", Name + " | Activity detected, waiting for it to settle..."); + Logger.LogInfo("Local", Name + " | Activity detected, waiting for it to settle..."); - List size_buffer = new List (); - DirectoryInfo info = new DirectoryInfo (LocalPath); + List size_buffer = new List(); + DirectoryInfo info = new DirectoryInfo(LocalPath); - do { + do + { if (size_buffer.Count >= 4) - size_buffer.RemoveAt (0); + size_buffer.RemoveAt(0); - size_buffer.Add (CalculateSize (info)); + size_buffer.Add(CalculateSize(info)); if (size_buffer.Count >= 4 && - size_buffer [0].Equals (size_buffer [1]) && - size_buffer [1].Equals (size_buffer [2]) && - size_buffer [2].Equals (size_buffer [3])) { + size_buffer[0].Equals(size_buffer[1]) && + size_buffer[1].Equals(size_buffer[2]) && + size_buffer[2].Equals(size_buffer[3])) + { - Logger.LogInfo ("Local", Name + " | Activity has settled"); + Logger.LogInfo("Local", Name + " | Activity has settled"); IsBuffering = false; bool first_sync = true; - if (HasLocalChanges && Status == SyncStatus.Idle) { - do { + if (HasLocalChanges && Status == SyncStatus.Idle) + { + do + { if (!first_sync) - Logger.LogInfo ("Local", Name + " | More changes found"); + Logger.LogInfo("Local", Name + " | More changes found"); - SyncUpBase (); + SyncUpBase(); if (Error == ErrorStatus.UnreadableFiles) return; @@ -339,75 +369,79 @@ size_buffer [2].Equals (size_buffer [3])) { first_sync = false; } while (HasLocalChanges); - } + } - if (Status != SyncStatus.Idle && Status != SyncStatus.Error) { + if (Status != SyncStatus.Idle && Status != SyncStatus.Error) + { Status = SyncStatus.Idle; - SyncStatusChanged (Status); + SyncStatusChanged(Status); } - } else { - Thread.Sleep (500); + } + else + { + Thread.Sleep(500); } } while (IsBuffering); if (!UseCustomWatcher) - this.watcher.Enable (); + this.watcher.Enable(); } - public void ForceRetry () + public void ForceRetry() { if (Error != ErrorStatus.None && !this.is_syncing) - SyncUpBase (); + SyncUpBase(); } - protected void OnConflictResolved () + protected void OnConflictResolved() { - ConflictResolved (); + ConflictResolved(); } DateTime progress_last_change = DateTime.Now; - protected void OnProgressChanged (double percentage, double speed, string information) + protected void OnProgressChanged(double percentage, double speed, string information) { if (percentage < 1) return; // Only trigger the ProgressChanged event once per second - if (DateTime.Compare (this.progress_last_change, DateTime.Now.Subtract (new TimeSpan (0, 0, 0, 1))) >= 0) + if (DateTime.Compare(this.progress_last_change, DateTime.Now.Subtract(new TimeSpan(0, 0, 0, 1))) >= 0) return; if (percentage == 100.0) percentage = 99.0; - progress_last_change = DateTime.Now; + progress_last_change = DateTime.Now; ProgressPercentage = percentage; ProgressSpeed = speed; ProgressInformation = information; - ProgressChanged (); + ProgressChanged(); } - void SyncUpBase () + void SyncUpBase() { if (!UseCustomWatcher) - this.watcher.Disable (); + this.watcher.Disable(); - Logger.LogInfo ("SyncUp", Name + " | Initiated"); + Logger.LogInfo("SyncUp", Name + " | Initiated"); HasUnsyncedChanges = true; Status = SyncStatus.SyncUp; - SyncStatusChanged (Status); + SyncStatusChanged(Status); - if (SyncUp ()) { - Logger.LogInfo ("SyncUp", Name + " | Done"); - ChangeSets = GetChangeSets (); + if (SyncUp()) + { + Logger.LogInfo("SyncUp", Name + " | Done"); + ChangeSets = GetChangeSets(); HasUnsyncedChanges = false; this.poll_interval = PollInterval.Long; @@ -415,115 +449,127 @@ void SyncUpBase () // this.listener.Announce (new Announcement (Identifier, CurrentRevision)); Status = SyncStatus.Idle; - SyncStatusChanged (Status); + SyncStatusChanged(Status); - } else { - Logger.LogInfo ("SyncUp", Name + " | Error"); - SyncDownBase (); + } + else + { + Logger.LogInfo("SyncUp", Name + " | Error"); + SyncDownBase(); if (!UseCustomWatcher) - this.watcher.Disable (); + this.watcher.Disable(); - if (Error == ErrorStatus.None && SyncUp ()) { + if (Error == ErrorStatus.None && SyncUp()) + { HasUnsyncedChanges = false; // this.listener.Announce (new Announcement (Identifier, CurrentRevision)); Status = SyncStatus.Idle; - SyncStatusChanged (Status); + SyncStatusChanged(Status); - } else { + } + else + { this.poll_interval = PollInterval.Short; Status = SyncStatus.Error; - SyncStatusChanged (Status); + SyncStatusChanged(Status); } } ProgressPercentage = 0.0; - ProgressSpeed = 0.0; + ProgressSpeed = 0.0; if (!UseCustomWatcher) - this.watcher.Enable (); + this.watcher.Enable(); this.status_message = ""; } - void SyncDownBase () + void SyncDownBase() { if (!UseCustomWatcher) - this.watcher.Disable (); + this.watcher.Disable(); - Logger.LogInfo ("SyncDown", Name + " | Initiated"); + Logger.LogInfo("SyncDown", Name + " | Initiated"); Status = SyncStatus.SyncDown; - SyncStatusChanged (Status); + SyncStatusChanged(Status); string pre_sync_revision = CurrentRevision; - if (SyncDown ()) { + if (SyncDown()) + { Error = ErrorStatus.None; - string identifier_file_path = Path.Combine (LocalPath, ".sparkleshare"); - File.SetAttributes (identifier_file_path, FileAttributes.Hidden); + string identifier_file_path = Path.Combine(LocalPath, ".sparkleshare"); + File.SetAttributes(identifier_file_path, FileAttributes.Hidden); - ChangeSets = GetChangeSets (); + ChangeSets = GetChangeSets(); - if (!pre_sync_revision.Equals (CurrentRevision) && + if (!pre_sync_revision.Equals(CurrentRevision) && ChangeSets != null && ChangeSets.Count > 0 && - !ChangeSets [0].User.Name.Equals (this.local_config.User.Name)) { + !ChangeSets[0].User.Name.Equals(this.local_config.User.Name)) + { bool emit_change_event = true; - foreach (Change change in ChangeSets [0].Changes) { - if (change.Path.EndsWith (".sparkleshare")) { + foreach (Change change in ChangeSets[0].Changes) + { + if (change.Path.EndsWith(".sparkleshare")) + { emit_change_event = false; break; } } - + if (emit_change_event) - NewChangeSet (ChangeSets [0]); + NewChangeSet(ChangeSets[0]); } - Logger.LogInfo ("SyncDown", Name + " | Done"); + Logger.LogInfo("SyncDown", Name + " | Done"); // There could be changes from a resolved // conflict. Tries only once, then lets // the timer try again periodically - if (HasUnsyncedChanges) { + if (HasUnsyncedChanges) + { Status = SyncStatus.SyncUp; - SyncStatusChanged (Status); - - if (SyncUp ()) + SyncStatusChanged(Status); + + if (SyncUp()) HasUnsyncedChanges = false; } Status = SyncStatus.Idle; - SyncStatusChanged (Status); + SyncStatusChanged(Status); - } else { - Logger.LogInfo ("SyncDown", Name + " | Error"); + } + else + { + Logger.LogInfo("SyncDown", Name + " | Error"); - ChangeSets = GetChangeSets (); + ChangeSets = GetChangeSets(); Status = SyncStatus.Error; - SyncStatusChanged (Status); + SyncStatusChanged(Status); } ProgressPercentage = 0.0; - ProgressSpeed = 0.0; + ProgressSpeed = 0.0; Status = SyncStatus.Idle; - SyncStatusChanged (Status); + SyncStatusChanged(Status); if (!UseCustomWatcher) - this.watcher.Enable (); + this.watcher.Enable(); } - void CreateListener () + void CreateListener() { // this.listener = ListenerFactory.CreateListener (Name, Identifier); @@ -538,93 +584,101 @@ void CreateListener () // this.listener.Connect (); } - - void ListenerConnectedDelegate () + + void ListenerConnectedDelegate() { - if (this.last_disconnect_reason == DisconnectReason.SystemSleep) { + if (this.last_disconnect_reason == DisconnectReason.SystemSleep) + { this.last_disconnect_reason = DisconnectReason.None; if (HasRemoteChanges && !this.is_syncing) - SyncDownBase (); + SyncDownBase(); } this.poll_interval = PollInterval.Long; } - void ListenerDisconnectedDelegate (DisconnectReason reason) + void ListenerDisconnectedDelegate(DisconnectReason reason) { - Logger.LogInfo (Name, "Falling back to regular polling"); + Logger.LogInfo(Name, "Falling back to regular polling"); this.poll_interval = PollInterval.Short; this.last_disconnect_reason = reason; - if (reason == DisconnectReason.SystemSleep) { - this.remote_timer.Stop (); + if (reason == DisconnectReason.SystemSleep) + { + this.remote_timer.Stop(); int backoff_time = 2; - do { - Logger.LogInfo (Name, "Next reconnect attempt in " + backoff_time + " seconds"); - Thread.Sleep (backoff_time * 1000); - this.listener.Connect (); + do + { + Logger.LogInfo(Name, "Next reconnect attempt in " + backoff_time + " seconds"); + Thread.Sleep(backoff_time * 1000); + this.listener.Connect(); backoff_time *= 2; - + } while (backoff_time < 64 && !this.listener.IsConnected); - this.remote_timer.Start (); + this.remote_timer.Start(); } } - void ListenerAnnouncementReceivedDelegate (Announcement announcement) + void ListenerAnnouncementReceivedDelegate(Announcement announcement) { string identifier = Identifier; - if (!announcement.FolderIdentifier.Equals (identifier)) + if (!announcement.FolderIdentifier.Equals(identifier)) return; - - if (!announcement.Message.Equals (CurrentRevision)) { + + if (!announcement.Message.Equals(CurrentRevision)) + { while (this.is_syncing) - Thread.Sleep (100); + Thread.Sleep(100); - Logger.LogInfo (Name, "Syncing due to announcement"); + Logger.LogInfo(Name, "Syncing due to announcement"); if (Status == SyncStatus.Paused) - Logger.LogInfo (Name, "We're paused, skipping sync"); + Logger.LogInfo(Name, "We're paused, skipping sync"); else - SyncDownBase (); + SyncDownBase(); } } // Recursively gets a folder's size in bytes - long CalculateSize (DirectoryInfo parent) + long CalculateSize(DirectoryInfo parent) { - if (ExcludePaths.Contains (parent.Name)) + if (ExcludePaths.Contains(parent.Name)) return 0; long size = 0; - try { - foreach (DirectoryInfo directory in parent.GetDirectories ()) - size += CalculateSize (directory); + try + { + foreach (DirectoryInfo directory in parent.GetDirectories()) + size += CalculateSize(directory); - foreach (FileInfo file in parent.GetFiles ()) + foreach (FileInfo file in parent.GetFiles()) size += file.Length; - } catch (Exception e) { - Logger.LogInfo ("Local", "Error calculating directory size", e); + } + catch (Exception e) + { + Logger.LogInfo("Local", "Error calculating directory size", e); } return size; } - public void Pause () + public void Pause() { - if (Status == SyncStatus.Idle) { - this.local_config.SetFolderOptionalAttribute (Name, "paused", bool.TrueString); + if (Status == SyncStatus.Idle) + { + this.local_config.SetFolderOptionalAttribute(Name, "paused", bool.TrueString); Status = SyncStatus.Paused; } } @@ -632,31 +686,35 @@ public void Pause () protected string status_message = ""; - public void Resume (string message) + public void Resume(string message) { this.status_message = message; - if (Status == SyncStatus.Paused) { - this.local_config.SetFolderOptionalAttribute (Name, "paused", bool.FalseString); + if (Status == SyncStatus.Paused) + { + this.local_config.SetFolderOptionalAttribute(Name, "paused", bool.FalseString); Status = SyncStatus.Idle; - if (HasUnsyncedChanges || HasLocalChanges) { - do { - SyncUpBase (); - + if (HasUnsyncedChanges || HasLocalChanges) + { + do + { + SyncUpBase(); + } while (HasLocalChanges); } } } - public void Dispose () + public void Dispose() { - if (remote_timer != null) { + if (remote_timer != null) + { this.remote_timer.Elapsed -= RemoteTimerElapsedDelegate; - this.remote_timer.Stop (); - this.remote_timer.Dispose (); - this.remote_timer = null; + this.remote_timer.Stop(); + this.remote_timer.Dispose(); + this.remote_timer = null!; } // this.listener.Disconnected -= ListenerDisconnectedDelegate; @@ -665,7 +723,7 @@ public void Dispose () // this.listener.Dispose (); if (!UseCustomWatcher && this.watcher != null) - this.watcher.Dispose (); + this.watcher.Dispose(); } } -} +} \ No newline at end of file diff --git a/Sparkles/ChangeSet.cs b/Sparkles/ChangeSet.cs index 8358012f8..a6a4485e8 100644 --- a/Sparkles/ChangeSet.cs +++ b/Sparkles/ChangeSet.cs @@ -19,9 +19,11 @@ using System.IO; using System.Collections.Generic; -namespace Sparkles { +namespace Sparkles +{ - public enum ChangeType { + public enum ChangeType + { Added, Edited, Deleted, @@ -29,83 +31,91 @@ public enum ChangeType { } - public class ChangeSet { + public class ChangeSet + { - public User User = new User ("Unknown", "Unknown"); + public User User = new User("Unknown", "Unknown"); - public SparkleFolder Folder; - public string Revision; + public SparkleFolder Folder = null!; + public string Revision = null!; public DateTime Timestamp; public DateTime FirstTimestamp; - public Uri RemoteUrl; + public Uri RemoteUrl = null!; - public List Changes = new List (); + public List Changes = new List(); - public string ToMessage () + public string ToMessage() { string message = "added: {0}"; - switch (Changes [0].Type) { - case ChangeType.Edited: message = "edited: {0}"; break; - case ChangeType.Deleted: message = "deleted: {0}"; break; - case ChangeType.Moved: message = "moved: {0}"; break; + switch (Changes[0].Type) + { + case ChangeType.Edited: message = "edited: {0}"; break; + case ChangeType.Deleted: message = "deleted: {0}"; break; + case ChangeType.Moved: message = "moved: {0}"; break; } if (Changes.Count > 0) - return string.Format (message, Changes [0].Path); + return string.Format(message, Changes[0].Path); else return "did something magical"; } } - public class Change { + public class Change + { public ChangeType Type; public DateTime Timestamp; public bool IsFolder; - public string Path; - public string MovedToPath; + public string Path = null!; + public string MovedToPath = null!; } - public class SparkleFolder { + public class SparkleFolder + { - public string Name; - public Uri RemoteAddress; + public string Name = null!; + public Uri RemoteAddress = null!; - public string FullPath { - get { - string custom_path = Configuration.DefaultConfiguration.GetFolderOptionalAttribute (Name, "path"); + public string FullPath + { + get + { + + string? custom_path = Configuration.DefaultConfiguration.GetFolderOptionalAttribute(Name, "path"); if (custom_path != null) - return Path.Combine (custom_path, Name); + return Path.Combine(custom_path, Name); - return Path.Combine (Configuration.DefaultConfiguration.FoldersPath, - new Uri (Configuration.DefaultConfiguration.UrlByName (Name)).Host, - Name); + return Path.Combine(Configuration.DefaultConfiguration.FoldersPath, + new Uri(Configuration.DefaultConfiguration.UrlByName(Name)!).Host, + Name); } } - public SparkleFolder (string name) + public SparkleFolder(string name) { Name = name; } } - public class Announcement { + public class Announcement + { public readonly string FolderIdentifier; public readonly string Message; - public Announcement (string folder_identifier, string message) + public Announcement(string folder_identifier, string message) { FolderIdentifier = folder_identifier; - Message = message; + Message = message; } } } diff --git a/Sparkles/Command.cs b/Sparkles/Command.cs index 99fcdb79b..3d034bc2b 100644 --- a/Sparkles/Command.cs +++ b/Sparkles/Command.cs @@ -21,12 +21,14 @@ using System.Reflection; using System.Collections.Generic; -namespace Sparkles { +namespace Sparkles +{ - public class Command : Process { + public class Command : Process + { bool write_output; - static string[] extended_search_path; + static string[] extended_search_path = null!; public static void SetSearchPath(string[] pathes) { @@ -35,22 +37,22 @@ public static void SetSearchPath(string[] pathes) public static void SetSearchPath(string path) { - SetSearchPath(new string[] { path}); + SetSearchPath(new string[] { path }); } - public Command (string path, string args) : this (path, args, write_output: true) + public Command(string path, string args) : this(path, args, write_output: true) { } - public Command (string path, string args, bool write_output) + public Command(string path, string args, bool write_output) { this.write_output = write_output; StartInfo.FileName = path; StartInfo.Arguments = args; - StartInfo.WorkingDirectory = Path.GetTempPath (); + StartInfo.WorkingDirectory = Path.GetTempPath(); StartInfo.CreateNoWindow = true; StartInfo.RedirectStandardOutput = true; StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; @@ -61,90 +63,94 @@ public Command (string path, string args, bool write_output) } - new public void Start () + new public void Start() { string folder = ""; - if (StartInfo.WorkingDirectory != Path.GetTempPath ()) - folder = Path.GetFileName (StartInfo.WorkingDirectory) + " | "; - + if (StartInfo.WorkingDirectory != Path.GetTempPath()) + folder = Path.GetFileName(StartInfo.WorkingDirectory) + " | "; + if (write_output) - Logger.LogInfo ("Cmd", folder + Path.GetFileName (StartInfo.FileName) + " " + StartInfo.Arguments); + Logger.LogInfo("Cmd", folder + Path.GetFileName(StartInfo.FileName) + " " + StartInfo.Arguments); - try { - base.Start (); + try + { + base.Start(); - } catch (Exception e) { - Logger.LogInfo ("Cmd", "Couldn't execute command: " - +StartInfo.FileName+","+ e.Message); - Environment.Exit (-1); + } + catch (Exception e) + { + Logger.LogInfo("Cmd", "Couldn't execute command: " + + StartInfo.FileName + "," + e.Message); + Environment.Exit(-1); } } - public void StartAndWaitForExit () + public void StartAndWaitForExit() { - Start (); - WaitForExit (); + Start(); + WaitForExit(); } - public string StartAndReadStandardOutput () + public string StartAndReadStandardOutput() { - Start (); + Start(); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes - string output = StandardOutput.ReadToEnd (); - WaitForExit (); + string output = StandardOutput.ReadToEnd(); + WaitForExit(); - return output.TrimEnd (); + return output.TrimEnd(); } - public string StartAndReadStandardError () + public string StartAndReadStandardError() { StartInfo.RedirectStandardError = true; - Start (); + Start(); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes - string output = StandardError.ReadToEnd (); - WaitForExit (); + string output = StandardError.ReadToEnd(); + WaitForExit(); StartInfo.RedirectStandardError = false; - return output.TrimEnd (); + return output.TrimEnd(); } - public void SetEnvironmentVariable (string variable, string content) + public void SetEnvironmentVariable(string variable, string content) { - if (StartInfo.EnvironmentVariables.ContainsKey (variable)) - StartInfo.EnvironmentVariables [variable] = content; + if (StartInfo.EnvironmentVariables.ContainsKey(variable)) + StartInfo.EnvironmentVariables[variable] = content; else - StartInfo.EnvironmentVariables.Add (variable, content); + StartInfo.EnvironmentVariables.Add(variable, content); } - protected static string LocateCommand (string name) + protected static string LocateCommand(string name) { string[] possible_command_paths = { - Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.Personal), "bin"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "bin"), Path.Combine(InstallationInfo.Directory, "bin"), "/usr/local/bin/", "/usr/bin/", "/opt/local/bin/" }; - List command_paths = new List(); + List command_paths = new List(); command_paths.AddRange(extended_search_path); command_paths.AddRange(possible_command_paths); - foreach (string path in command_paths) { - if (File.Exists(Path.Combine(path,name))) + foreach (string path in command_paths) + { + if (File.Exists(Path.Combine(path, name))) { - return Path.Combine (path, name); + return Path.Combine(path, name); } else if (File.Exists(Path.Combine(path, name + ".exe"))) { diff --git a/Sparkles/Configuration.cs b/Sparkles/Configuration.cs index 037b9331c..cc49e8fcf 100644 --- a/Sparkles/Configuration.cs +++ b/Sparkles/Configuration.cs @@ -21,284 +21,328 @@ using System.Xml; using System.Xml.Linq; -namespace Sparkles { +namespace Sparkles +{ - public class Configuration : XmlDocument { + public class Configuration : XmlDocument + { + private static readonly Lazy lazy = new(() => + { + string app_data_path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - private static Lazy ConfigLazy = new Lazy (() => { - string app_data_path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); + if (InstallationInfo.OperatingSystem != OS.Windows && InstallationInfo.OperatingSystem != OS.macOS) + app_data_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".config"); - if (InstallationInfo.OperatingSystem != OS.Windows && InstallationInfo.OperatingSystem != OS.macOS) - app_data_path = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".config"); + string config_path = Path.Combine(app_data_path, "org.sparkleshare.SparkleShare"); - string config_path = Path.Combine (app_data_path, "org.sparkleshare.SparkleShare"); - - return new Configuration (config_path, "projects.xml"); - }); + return new Configuration(config_path, "projects.xml"); + }); + private static readonly Lazy ConfigLazy = lazy; public static Configuration DefaultConfiguration { get { return ConfigLazy.Value; } } +#pragma warning disable CA2211 // Nicht konstante Felder drfen nicht sichtbar sein public static bool DebugMode = true; +#pragma warning restore CA2211 // Nicht konstante Felder drfen nicht sichtbar sein public readonly string DirectoryPath; public readonly string FilePath; public readonly string TmpPath; - public string AvatarProvider; + public string AvatarProvider = null!; public readonly string LogFilePath; - public string HomePath { - get { + public static string HomePath + { + get + { if (InstallationInfo.OperatingSystem == OS.Windows) - return Environment.GetFolderPath (Environment.SpecialFolder.UserProfile); + return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - return Environment.GetFolderPath (Environment.SpecialFolder.Personal); + return Environment.GetFolderPath(Environment.SpecialFolder.Personal); } } - public string FoldersPath { - get { - if (GetConfigOption ("folders_path") != null) - return GetConfigOption ("folders_path"); + public string FoldersPath + { + get + { + if (GetConfigOption("folders_path") != null) + return GetConfigOption("folders_path")!; - return Path.Combine (HomePath, "SparkleShare"); + return Path.Combine(HomePath, "SparkleShare"); } } - public Configuration (string config_path, string config_file_name) + public Configuration(string config_path, string config_file_name) { - FilePath = Path.Combine (config_path, config_file_name); + FilePath = Path.Combine(config_path, config_file_name); DirectoryPath = config_path; - string logs_path = Path.Combine (config_path, "logs"); + string logs_path = Path.Combine(config_path, "logs"); int i = 1; - do { - LogFilePath = Path.Combine ( - logs_path, "log_" + DateTime.Now.ToString ("yyyy-MM-dd") + "." + i + ".txt"); + do + { + LogFilePath = Path.Combine( + logs_path, "log_" + DateTime.Now.ToString("yyyy-MM-dd") + "." + i + ".txt"); i++; - } while (File.Exists (LogFilePath)); + } while (File.Exists(LogFilePath)); - if (!Directory.Exists (logs_path)) - Directory.CreateDirectory (logs_path); + if (!Directory.Exists(logs_path)) + Directory.CreateDirectory(logs_path); // Delete logs older than a week - foreach (FileInfo file in new DirectoryInfo (logs_path).GetFiles ("log*.txt")) { - if (file.LastWriteTime < DateTime.Now.AddDays (-7)) - file.Delete (); + foreach (FileInfo file in new DirectoryInfo(logs_path).GetFiles("log*.txt")) + { + if (file.LastWriteTime < DateTime.Now.AddDays(-7)) + file.Delete(); } - if (!Directory.Exists (config_path)) - Directory.CreateDirectory (config_path); + if (!Directory.Exists(config_path)) + Directory.CreateDirectory(config_path); - try { - Load (FilePath); + try + { + Load(FilePath); - } catch (TypeInitializationException) { - CreateInitialConfig (); + } + catch (TypeInitializationException) + { + CreateInitialConfig(); - } catch (FileNotFoundException) { - CreateInitialConfig (); + } + catch (FileNotFoundException) + { + CreateInitialConfig(); - } catch (XmlException) { - var file = new FileInfo (FilePath); + } + catch (XmlException) + { + var file = new FileInfo(FilePath); - if (file.Length == 0) { - File.Delete (FilePath); - CreateInitialConfig (); + if (file.Length == 0) + { + File.Delete(FilePath); + CreateInitialConfig(); - } else { + } + else + { throw; } - } finally { - TmpPath = Path.Combine (DirectoryPath, "tmp"); - Directory.CreateDirectory (TmpPath); + } + finally + { + TmpPath = Path.Combine(DirectoryPath, "tmp"); + Directory.CreateDirectory(TmpPath); } } - void CreateInitialConfig () + void CreateInitialConfig() { string user_name = Environment.UserName; - if (InstallationInfo.OperatingSystem != OS.Windows) { - if (string.IsNullOrEmpty (user_name)) + if (InstallationInfo.OperatingSystem != OS.Windows) + { + if (string.IsNullOrEmpty(user_name)) user_name = "Unknown"; else // On Unix systems the user name may have commas appended - user_name = user_name.TrimEnd (','); + user_name = user_name.TrimEnd(','); } XElement xml = - new XElement ("sparkleshare", - new XElement ("user", - new XElement ("name", user_name), - new XElement ("email", "Unknown") + new ("sparkleshare", + new XElement("user", + new XElement("name", user_name), + new XElement("email", "Unknown") ), - new XElement ("notifications", bool.TrueString) + new XElement("notifications", bool.TrueString) ); - LoadXml (xml.ToString ()); + LoadXml(xml.ToString()); } - public User User { - get { - string name = SelectSingleNode ("/sparkleshare/user/name/text()").Value; - string email = SelectSingleNode ("/sparkleshare/user/email/text()").Value; + public User User + { + get + { + string? name = SelectSingleNode("/sparkleshare/user/name/text()")!.Value; + string? email = SelectSingleNode("/sparkleshare/user/email/text()")!.Value; - return new User (name, email); + return new User(name!, email!); } - set { - SelectSingleNode ("/sparkleshare/user/name/text()").InnerText = value.Name; - SelectSingleNode ("/sparkleshare/user/email/text()").InnerText = value.Email; + set + { + SelectSingleNode("/sparkleshare/user/name/text()")!.InnerText = value.Name; + SelectSingleNode("/sparkleshare/user/email/text()")!.InnerText = value.Email; - Save (); + Save(); } } - public List Folders { - get { - var folders = new List (); - - foreach (XmlNode node_folder in SelectNodes ("/sparkleshare/folder")) - folders.Add (node_folder ["name"].InnerText); - - folders.Sort (); + public List Folders + { + get + { + var folders = new List(); + + foreach (XmlNode? node_folder in SelectNodes("/sparkleshare/folder")!) + if (node_folder != null) + { + folders.Add(node_folder["name"]!.InnerText); + } + folders.Sort(); return folders; } } - public void AddFolder (string name, string identifier, string url, string backend) + public void AddFolder(string name, string identifier, string url, string backend) { - XmlNode node_name = CreateElement ("name"); - XmlNode node_identifier = CreateElement ("identifier"); - XmlNode node_url = CreateElement ("url"); - XmlNode node_backend = CreateElement ("backend"); + XmlNode node_name = CreateElement("name"); + XmlNode node_identifier = CreateElement("identifier"); + XmlNode node_url = CreateElement("url"); + XmlNode node_backend = CreateElement("backend"); - node_name.InnerText = name; + node_name.InnerText = name; node_identifier.InnerText = identifier; - node_url.InnerText = url; - node_backend.InnerText = backend; + node_url.InnerText = url; + node_backend.InnerText = backend; - XmlNode node_folder = CreateNode (XmlNodeType.Element, "folder", null); + XmlNode node_folder = CreateNode(XmlNodeType.Element, "folder", null); - node_folder.AppendChild (node_name); - node_folder.AppendChild (node_identifier); - node_folder.AppendChild (node_url); - node_folder.AppendChild (node_backend); + node_folder.AppendChild(node_name); + node_folder.AppendChild(node_identifier); + node_folder.AppendChild(node_url); + node_folder.AppendChild(node_backend); - XmlNode node_root = SelectSingleNode ("/sparkleshare"); - node_root.AppendChild (node_folder); - - Save (); + XmlNode? node_root = SelectSingleNode("/sparkleshare"); + node_root?.AppendChild(node_folder); + Save(); } - public void RemoveFolder (string name) + public void RemoveFolder(string name) { - foreach (XmlNode node_folder in SelectNodes ("/sparkleshare/folder")) { - if (node_folder ["name"].InnerText.Equals (name)) - SelectSingleNode ("/sparkleshare").RemoveChild (node_folder); + foreach (XmlNode? node_folder in SelectNodes("/sparkleshare/folder")!) + { + if (node_folder != null) + { + if (node_folder["name"]!.InnerText.Equals(name)) + SelectSingleNode("/sparkleshare")!.RemoveChild(node_folder); + } } - - Save (); + Save(); } - public void RenameFolder (string identifier, string new_name) + public void RenameFolder(string identifier, string new_name) { - XmlNode node_folder = SelectSingleNode ( - string.Format ("/sparkleshare/folder[identifier=\"{0}\"]", identifier)); - - node_folder ["name"].InnerText = new_name; - Save (); + XmlNode? node_folder = SelectSingleNode( + string.Format("/sparkleshare/folder[identifier=\"{0}\"]", identifier)); + if (node_folder != null) + { + node_folder["name"]!.InnerText = new_name; + } + Save(); } - public string BackendByName (string name) + public string BackendByName(string name) { - return FolderValueByKey (name, "backend"); + return FolderValueByKey(name, "backend")!; } - public string IdentifierByName (string name) + public string IdentifierByName(string name) { - return FolderValueByKey (name, "identifier"); + return FolderValueByKey(name, "identifier")!; } - public string UrlByName (string name) + public string UrlByName(string name) { - return FolderValueByKey (name, "url"); + return FolderValueByKey(name, "url")!; } - public bool IdentifierExists (string identifier) + public bool IdentifierExists(string identifier) { - if (identifier == null) - throw new ArgumentNullException (); + ArgumentNullException.ThrowIfNull(identifier); - foreach (XmlNode node_folder in SelectNodes ("/sparkleshare/folder")) { - XmlElement folder_id = node_folder ["identifier"]; + foreach (XmlNode? node_folder in SelectNodes("/sparkleshare/folder")!) + { + if (node_folder != null) + { + XmlElement? folder_id = node_folder["identifier"]; - if (folder_id != null && identifier.Equals (folder_id.InnerText)) - return true; + if (identifier.Equals(folder_id!.InnerText)) + return true; + } } return false; } - public bool SetFolderOptionalAttribute (string folder_name, string key, string value) + public bool SetFolderOptionalAttribute(string folder_name, string key, string value) { - XmlNode folder = FolderByName (folder_name); + XmlNode? folder = FolderByName(folder_name); if (folder == null) return false; - if (folder [key] != null) { - folder [key].InnerText = value; + if (folder[key] != null) + { + folder[key]!.InnerText = value; - } else { - XmlNode new_node = CreateElement (key); + } + else + { + XmlNode new_node = CreateElement(key); new_node.InnerText = value; - folder.AppendChild (new_node); + folder.AppendChild(new_node); } - Save (); + Save(); return true; } - public string GetFolderOptionalAttribute (string folder_name, string key) + public string? GetFolderOptionalAttribute(string folder_name, string key) { - XmlNode folder = FolderByName (folder_name); + XmlNode? folder = FolderByName(folder_name); - if (folder != null) { - if (folder [key] != null) - return folder [key].InnerText; + if (folder != null) + { + if (folder[key] != null) + return folder[key]!.InnerText; else return null; - } else { + } + else + { return null; } } - public string GetConfigOption (string name) + public string? GetConfigOption(string name) { - XmlNode node = SelectSingleNode ("/sparkleshare/" + name); + XmlNode? node = SelectSingleNode("/sparkleshare/" + name); if (node != null) return node.InnerText; @@ -307,47 +351,50 @@ public string GetConfigOption (string name) } - public void SetConfigOption (string name, string content) + public void SetConfigOption(string name, string content) { - XmlNode node = SelectSingleNode ("/sparkleshare/" + name); + XmlNode? node = SelectSingleNode("/sparkleshare/" + name); - if (node != null) { + if (node != null) + { node.InnerText = content; - } else { - node = CreateElement (name); + } + else + { + node = CreateElement(name); node.InnerText = content; - XmlNode node_root = SelectSingleNode ("/sparkleshare"); - node_root.AppendChild (node); + XmlNode? node_root = SelectSingleNode("/sparkleshare"); + node_root!.AppendChild(node); } - Save (); - Logger.LogInfo ("Config", "Updated option " + name + ":" + content); + Save(); + Logger.LogInfo("Config", "Updated option " + name + ":" + content); } - XmlNode FolderByName (string name) + XmlNode? FolderByName(string name) { - return SelectSingleNode (string.Format ("/sparkleshare/folder[name=\"{0}\"]", name)); + return SelectSingleNode(string.Format("/sparkleshare/folder[name=\"{0}\"]", name)); } - string FolderValueByKey (string name, string key) + string? FolderValueByKey(string name, string key) { - XmlNode folder = FolderByName(name); + XmlNode? folder = FolderByName(name); - if ((folder != null) && (folder [key] != null)) - return folder [key].InnerText; + if ((folder != null) && (folder[key] != null)) + return folder[key]!.InnerText; return null; } - void Save () + void Save() { - Save (FilePath); - Logger.LogInfo ("Config", "Wrote to '" + FilePath + "'"); + Save(FilePath); + Logger.LogInfo("Config", "Wrote to '" + FilePath + "'"); } } } diff --git a/Sparkles/Extensions.cs b/Sparkles/Extensions.cs index b870e7365..27dba451e 100644 --- a/Sparkles/Extensions.cs +++ b/Sparkles/Extensions.cs @@ -17,147 +17,162 @@ using System; using System.IO; +using System.Numerics; +using System.Runtime.Intrinsics; using System.Security.Cryptography; using System.Text; -namespace Sparkles { +namespace Sparkles +{ - public static class Extensions { + public static class Extensions + { - public static string SHA256 (this string s) + public static string SHA256(this string s) { - SHA256 sha256 = new SHA256CryptoServiceProvider (); - byte [] bytes = ASCIIEncoding.Default.GetBytes (s); - byte [] sha256_bytes = sha256.ComputeHash (bytes); - - return BitConverter.ToString (sha256_bytes).ToLower ().Replace ("-", ""); + byte[] bytes = ASCIIEncoding.Default.GetBytes(s); + return SHA256(bytes); } + public static string SHA256(this byte[] bytes) + { + SHA256 sha256 = System.Security.Cryptography.SHA256.Create(); + byte[] sha256_bytes = sha256.ComputeHash(bytes); + return BitConverter.ToString(sha256_bytes).ToLower().Replace("-", ""); + } - public static string SHA256 (this string s, string salt) + public static string SHA256(this string s, string salt) { - SHA256 sha256 = new SHA256CryptoServiceProvider (); - byte [] bytes = ASCIIEncoding.Default.GetBytes (s + salt); - byte [] sha256_bytes = sha256.ComputeHash (bytes); + SHA256 sha256 = System.Security.Cryptography.SHA256.Create(); + byte[] bytes = ASCIIEncoding.Default.GetBytes(s + salt); + byte[] sha256_bytes = sha256.ComputeHash(bytes); - return BitConverter.ToString (sha256_bytes).ToLower ().Replace ("-", ""); + return BitConverter.ToString(sha256_bytes).ToLower().Replace("-", ""); } - public static string MD5 (this string s) + public static string MD5(this string s) { - MD5 md5 = new MD5CryptoServiceProvider (); - byte [] bytes = ASCIIEncoding.Default.GetBytes (s); - byte [] md5_bytes = md5.ComputeHash (bytes); + MD5 md5 = System.Security.Cryptography.MD5.Create(); + byte[] bytes = ASCIIEncoding.Default.GetBytes(s); + byte[] md5_bytes = md5.ComputeHash(bytes); - return BitConverter.ToString (md5_bytes).ToLower ().Replace ("-", ""); + return BitConverter.ToString(md5_bytes).ToLower().Replace("-", ""); } - - public static string AESEncrypt (this string plain_text, string password) + public static string AESEncrypt(this string plain_text, string password) { - Random random = new Random (); - byte [] salt_bytes = new Byte [16]; - random.NextBytes (salt_bytes); - - string salt = Convert.ToBase64String (salt_bytes); - password = (password + salt).SHA256 ().Substring (0, 32); - - RijndaelManaged aes = new RijndaelManaged () { - KeySize = 256, - BlockSize = 128, - Mode = CipherMode.CBC, - Padding = PaddingMode.PKCS7, - Key = Encoding.UTF8.GetBytes (password), - IV = Encoding.UTF8.GetBytes (password.ToCharArray (), 0, 16) - }; - - byte [] buffer = Encoding.UTF8.GetBytes (plain_text); - ICryptoTransform crypto = aes.CreateEncryptor (aes.Key, aes.IV); - byte [] encrypted_bytes = crypto.TransformFinalBlock (buffer, 0, buffer.Length); - - return salt + "_" + Convert.ToBase64String (encrypted_bytes); - } + Random random = new Random(); + byte[] salt_bytes = new Byte[16]; + random.NextBytes(salt_bytes); + string salt = Convert.ToBase64String(salt_bytes); + password = (password + salt).SHA256().Substring(0, 32); + + var aes = Aes.Create(); + aes.KeySize = 256; + aes.BlockSize = 128; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + aes.Key = Encoding.UTF8.GetBytes(password); + aes.IV = Encoding.UTF8.GetBytes(password.ToCharArray(), 0, 16); + + + byte[] buffer = Encoding.UTF8.GetBytes(plain_text); + ICryptoTransform crypto = aes.CreateEncryptor(aes.Key, aes.IV); + byte[] encrypted_bytes = crypto.TransformFinalBlock(buffer, 0, buffer.Length); + + return salt + "_" + Convert.ToBase64String(encrypted_bytes); + } - public static string AESDecrypt (this string s, string password) + public static string AESDecrypt(this string s, string password) { - string salt = s.Substring (0, s.IndexOf ("_")); - password = (password + salt).SHA256 ().Substring (0, 32); - - RijndaelManaged aes = new RijndaelManaged () { - KeySize = 256, - BlockSize = 128, - Mode = CipherMode.CBC, - Padding = PaddingMode.PKCS7, - Key = Encoding.UTF8.GetBytes (password), - IV = Encoding.UTF8.GetBytes (password.ToCharArray (), 0, 16) - }; - - string encrypted_text = s.Substring (s.IndexOf ("_") + 1); - byte [] buffer = Convert.FromBase64String (encrypted_text); - ICryptoTransform crypto = aes.CreateDecryptor (aes.Key, aes.IV); - byte [] decrypted_bytes = crypto.TransformFinalBlock (buffer, 0, buffer.Length); - - return Encoding.UTF8.GetString (decrypted_bytes); + string salt = s.Substring(0, s.IndexOf("_")); + password = (password + salt).SHA256().Substring(0, 32); + + var aes = Aes.Create(); + aes.KeySize = 256; + aes.BlockSize = 128; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + aes.Key = Encoding.UTF8.GetBytes(password); + aes.IV = Encoding.UTF8.GetBytes(password.ToCharArray(), 0, 16); + + string encrypted_text = s.Substring(s.IndexOf("_") + 1); + byte[] buffer = Convert.FromBase64String(encrypted_text); + ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV); + byte[] decrypted_bytes = crypto.TransformFinalBlock(buffer, 0, buffer.Length); + + return Encoding.UTF8.GetString(decrypted_bytes); } // Format a file size nicely with small caps. // Example: 1048576 becomes "1 ᴍʙ" - public static string ToSize (this double byte_count) + public static string ToSize(this double byte_count) { if (byte_count >= 1099511627776) - return string.Format ("{0:##.##} ᴛʙ", Math.Round (byte_count / 1099511627776, 2)); + return string.Format("{0:##.##} ᴛʙ", Math.Round(byte_count / 1099511627776, 2)); else if (byte_count >= 1073741824) - return string.Format ("{0:##.##} ɢʙ", Math.Round (byte_count / 1073741824, 1)); + return string.Format("{0:##.##} ɢʙ", Math.Round(byte_count / 1073741824, 1)); else if (byte_count >= 1048576) - return string.Format ("{0:##.##} ᴍʙ", Math.Round (byte_count / 1048576, 1)); + return string.Format("{0:##.##} ᴍʙ", Math.Round(byte_count / 1048576, 1)); else if (byte_count >= 1024) - return string.Format ("{0:##.##} ᴋʙ", Math.Round (byte_count / 1024, 0)); + return string.Format("{0:##.##} ᴋʙ", Math.Round(byte_count / 1024, 0)); else return byte_count + " ʙ"; } - public static bool IsSymlink (this string path) + public static bool IsSymlink(this string path) { - var file = new FileInfo (path); + var file = new FileInfo(path); return ((file.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint); } - public static string ToPrettyDate (this DateTime timestamp) + public static string ToPrettyDate(this DateTime timestamp) { - TimeSpan time_diff = DateTime.Now.Subtract (timestamp); - var day_diff = (int) time_diff.TotalDays; - DateTime yesterday = DateTime.Today.AddDays (-1); - - if (timestamp >= yesterday && timestamp < DateTime.Today) { - return "yesterday at " + timestamp.ToString ("HH:mm"); - - } else if (day_diff == 0) { - return "today at " + timestamp.ToString ("HH:mm"); - - } else if (day_diff < 7) { - return timestamp.ToString ("dddd"); - - } else if (day_diff < 31) { + TimeSpan time_diff = DateTime.Now.Subtract(timestamp); + var day_diff = (int)time_diff.TotalDays; + DateTime yesterday = DateTime.Today.AddDays(-1); + + if (timestamp >= yesterday && timestamp < DateTime.Today) + { + return "yesterday at " + timestamp.ToString("HH:mm"); + + } + else if (day_diff == 0) + { + return "today at " + timestamp.ToString("HH:mm"); + + } + else if (day_diff < 7) + { + return timestamp.ToString("dddd"); + + } + else if (day_diff < 31) + { if (day_diff < 14) return "a week ago"; else - return string.Format ("{0} weeks ago", Math.Ceiling ((double) day_diff / 7)); + return string.Format("{0} weeks ago", Math.Ceiling((double)day_diff / 7)); - } else if (day_diff < 62) { + } + else if (day_diff < 62) + { return "a month ago"; - } else { - return string.Format ("{0} months ago", Math.Ceiling ((double) day_diff / 31)); + } + else + { + return string.Format("{0} months ago", Math.Ceiling((double)day_diff / 31)); } } - public static string ReplaceUnderscoreWithSpace (this string s) + public static string ReplaceUnderscoreWithSpace(this string s) { int len = s.Length, lead = 0, trail = 0; for (int i = 0; i < len && s[i] == '_'; i++, lead++) @@ -167,9 +182,9 @@ public static string ReplaceUnderscoreWithSpace (this string s) if (lead == 0 && trail == 0) return s.Replace("_", " "); // fast code path else - return s.Substring (0, lead) + - s.Substring (lead, len - lead - trail).Replace ("_", " ") + - s.Substring (len - trail, trail); + return s.Substring(0, lead) + + s.Substring(lead, len - lead - trail).Replace("_", " ") + + s.Substring(len - trail, trail); } } } diff --git a/Sparkles/Git/Git.Command.cs b/Sparkles/Git/Git.Command.cs index 5bc1ac315..6de246543 100644 --- a/Sparkles/Git/Git.Command.cs +++ b/Sparkles/Git/Git.Command.cs @@ -23,16 +23,16 @@ namespace Sparkles.Git { public class GitCommand : SSHCommand { - public static string ExecPath; + public static string ExecPath = null!; - static string git_path; - static string git_lfs_path; + static string git_path = null!; + static string git_lfs_path = null!; public static string GitPath { get { if (git_path == null) - git_path = LocateCommand ("git").Replace("\\", "/"); + git_path = LocateCommand ("git")!.Replace("\\", "/"); return git_path; } @@ -45,7 +45,7 @@ public static string GitPath { public static string GitLfsPath { get { if (git_lfs_path == null) - git_lfs_path = LocateCommand ("git-lfs").Replace("\\","/"); + git_lfs_path = LocateCommand ("git-lfs")!.Replace("\\","/"); return git_lfs_path; } @@ -81,7 +81,7 @@ public static string GitLFSVersion { } - public GitCommand (string working_dir, string args) : this (working_dir, args, null) + public GitCommand (string working_dir, string args) : this (working_dir, args, null!) { } diff --git a/Sparkles/Git/Git.Fetcher.cs b/Sparkles/Git/Git.Fetcher.cs index dc67ad56d..f6f671a74 100644 --- a/Sparkles/Git/Git.Fetcher.cs +++ b/Sparkles/Git/Git.Fetcher.cs @@ -23,8 +23,8 @@ namespace Sparkles.Git { public class GitFetcher : SSHFetcher { - GitCommand git_clone; - SSHAuthenticationInfo auth_info; + GitCommand git_clone=null!; + SSHAuthenticationInfo auth_info = null!; string password_salt = Path.GetRandomFileName ().SHA256 ().Substring (0, 16); @@ -109,7 +109,7 @@ public override bool Fetch () string information = ""; while (!output_stream.EndOfStream) { - string line = output_stream.ReadLine (); + string line = output_stream.ReadLine ()!; ErrorStatus error = GitCommand.ParseProgress (line, out percentage, out speed, out information); diff --git a/Sparkles/Git/Git.Repository.cs b/Sparkles/Git/Git.Repository.cs index 40485b380..6b83ea72b 100644 --- a/Sparkles/Git/Git.Repository.cs +++ b/Sparkles/Git/Git.Repository.cs @@ -30,7 +30,7 @@ public class GitRepository : BaseRepository { bool user_is_set; - string cached_branch; + string cached_branch = null!; string branch { get { @@ -157,7 +157,7 @@ public override string CurrentRevision { if (git.ExitCode == 0) return output; - return null; + return null!; } } @@ -210,7 +210,7 @@ public override bool SyncUp () string message = base.status_message; if (string.IsNullOrEmpty (message)) - message = FormatCommitMessage (); + message = FormatCommitMessage ()!; if (message != null) Commit (message); @@ -293,7 +293,7 @@ bool ReadStream (GitCommand command) string information = ""; while (!output_stream.EndOfStream) { - string line = output_stream.ReadLine (); + string line = output_stream.ReadLine ()!; ErrorStatus error = GitCommand.ParseProgress (line, out percentage, out speed, out information); if (error != ErrorStatus.None) { @@ -412,7 +412,7 @@ void Commit (string message) // Merges the fetched changes bool Merge () { - string message = FormatCommitMessage (); + string message = FormatCommitMessage ()!; if (message != null) { Add (); @@ -554,8 +554,8 @@ void ResolveConflict () string abs_conflicting_file_path = Path.Combine (LocalPath, conflicting_file_path); - string abs_file_path_A = Path.Combine (Path.GetDirectoryName (abs_conflicting_file_path), file_name_A); - string abs_file_path_B = Path.Combine (Path.GetDirectoryName (abs_conflicting_file_path), file_name_B); + string abs_file_path_A = Path.Combine (Path.GetDirectoryName (abs_conflicting_file_path)!, file_name_A); + string abs_file_path_B = Path.Combine (Path.GetDirectoryName (abs_conflicting_file_path)!, file_name_B); // Recover local version @@ -658,7 +658,7 @@ public override void RestoreFile (string path, string revision, string target_fi git.StartAndWaitForExit (); if (target_file_path.StartsWith (LocalPath)) - new Thread (() => OnFileActivity (null)).Start (); + new Thread (() => OnFileActivity (null!)).Start (); } @@ -671,7 +671,7 @@ public override List UnsyncedChanges { public override List GetChangeSets () { - return GetChangeSetsInternal (null); + return GetChangeSetsInternal (null!); } public override List GetChangeSets (string path) @@ -786,7 +786,7 @@ ChangeSet ParseChangeSet (Match match) // Set the name and email if (match.Groups ["name"].Value == "SparkleShare") - return null; + return null!; change_set.Folder = new SparkleFolder (Name); change_set.Revision = match.Groups ["commit"].Value; @@ -813,7 +813,7 @@ ChangeSet ParseChangeSet (Match match) int.Parse (match.Groups ["hour"].Value), int.Parse (match.Groups ["minute"].Value), int.Parse (match.Groups ["second"].Value)); string time_zone = match.Groups ["timezone"].Value; - int our_offset = TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now).Hours; + int our_offset = TimeZoneInfo.Local.GetUtcOffset (DateTime.Now).Hours; int their_offset = int.Parse (time_zone.Substring (0, 3)); change_set.Timestamp = change_set.Timestamp.AddHours (their_offset * -1); @@ -827,7 +827,7 @@ Change ParseChange (string line) { // Skip lines containing backspace characters or the .sparkleshare file if (line.Contains ("\\177") || line.Contains (".sparkleshare")) - return null; + return null!; // File lines start with a change type letter and then a tab character if (!line.StartsWith ("A\t") && @@ -835,10 +835,10 @@ Change ParseChange (string line) !line.StartsWith ("D\t") && !line.StartsWith ("R100\t")) { - return null; + return null!; } - Change change = new Change () { Type = ChangeType.Added }; + Change change = new Change () { Type = ChangeType.Added }!; string file_path; int first_tab_pos = line.IndexOf ('\t'); @@ -905,7 +905,7 @@ void PrepareGitLFS () chmod.StartAndWaitForExit (); } - Directory.CreateDirectory (Path.GetDirectoryName (pre_push_hook_path)); + Directory.CreateDirectory (Path.GetDirectoryName (pre_push_hook_path)!); File.WriteAllText (pre_push_hook_path, pre_push_hook_content); } @@ -970,11 +970,11 @@ List ParseStatus () { List changes = new List (); - var git_status = new GitCommand (LocalPath, "status --porcelain"); + var git_status = new GitCommand (LocalPath, "status --porcelain")!; git_status.Start (); while (!git_status.StandardOutput.EndOfStream) { - string line = git_status.StandardOutput.ReadLine (); + string line = git_status.StandardOutput.ReadLine ()!; line = line.Trim (); if (line.EndsWith (".empty") || line.EndsWith (".empty\"")) @@ -1016,7 +1016,7 @@ List ParseStatus () // Creates a pretty commit message based on what has changed - string FormatCommitMessage () + string? FormatCommitMessage () { string message = ""; diff --git a/Sparkles/Git/Sparkles.Git.csproj b/Sparkles/Git/Sparkles.Git.csproj index 755b133da..9db0e26ef 100644 --- a/Sparkles/Git/Sparkles.Git.csproj +++ b/Sparkles/Git/Sparkles.Git.csproj @@ -1,5 +1,12 @@ - - + + + net8.0 + enable + enable + + + false + Debug AnyCPU @@ -13,10 +20,9 @@ 512 - v4.5 - True + False ..\..\bin\ prompt 4 @@ -58,14 +64,9 @@ - - - - - + + false + + enable + enable + + + false + Debug AnyCPU @@ -8,14 +36,13 @@ {2C914413-B31C-4362-93C7-1AE34F09112A} Library Sparkles - Sparkles + - v4.5 none - true + False ..\bin prompt 4 @@ -57,11 +84,6 @@ prompt MinimumRecommendedRules.ruleset - - - - - @@ -97,5 +119,4 @@ - - \ No newline at end of file + diff --git a/Sparkles/TcpListener.cs b/Sparkles/TcpListener.cs index 1450305d6..1934f56db 100644 --- a/Sparkles/TcpListener.cs +++ b/Sparkles/TcpListener.cs @@ -20,240 +20,276 @@ using System.Text; using System.Threading; -namespace Sparkles { +namespace Sparkles +{ - public class TcpListener : BaseListener { + public class TcpListener : BaseListener + { - private Socket socket; - private Thread thread; - private bool is_connected = false; + private Socket socket = null!; + private Thread thread = null!; + private bool is_connected = false; private bool is_connecting = false; private DateTime last_ping = DateTime.Now; - public TcpListener (Uri server, string folder_identifier) : base (server, folder_identifier) + public TcpListener(Uri server, string folder_identifier) : base(server, folder_identifier) { } - public override bool IsConnected { - get { + public override bool IsConnected + { + get + { return this.is_connected; } } - public override bool IsConnecting { - get { + public override bool IsConnecting + { + get + { return this.is_connecting; } } // Starts a new thread and listens to the channel - public override void Connect () + public override void Connect() { this.is_connecting = true; - this.thread = new Thread (() => { + this.thread = new Thread(() => + { int port = Server.Port; if (port < 0) port = 443; - try { - this.socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { + try + { + this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) + { ReceiveTimeout = 5 * 1000, - SendTimeout = 5 * 1000 + SendTimeout = 5 * 1000 }; // Try to connect to the server - this.socket.Connect (Server.Host, port); + this.socket.Connect(Server.Host, port); this.is_connecting = false; - this.is_connected = true; + this.is_connected = true; - OnConnected (); + OnConnected(); - } catch (Exception e) { - this.is_connected = false; + } + catch (Exception e) + { + this.is_connected = false; this.is_connecting = false; if (this.socket != null) - this.socket.Close (); + this.socket.Close(); - OnDisconnected (Sparkles.DisconnectReason.TimeOut, e.Message); + OnDisconnected(Sparkles.DisconnectReason.TimeOut, e.Message); return; } - byte [] bytes = new byte [4096]; + byte[] bytes = new byte[4096]; int bytes_read = 0; this.last_ping = DateTime.Now; // Wait for messages - while (this.is_connected) { - try { + while (this.is_connected) + { + try + { int i = 0; int timeout = 300; DisconnectReason reason = DisconnectReason.TimeOut; // This blocks the thread - while (this.socket.Available < 1) { - try { + while (this.socket.Available < 1) + { + try + { // We've timed out, let's ping the server to // see if the connection is still up - if (i == timeout) { - Logger.LogInfo ("ListenerTcp", "Pinging " + Server); + if (i == timeout) + { + Logger.LogInfo("ListenerTcp", "Pinging " + Server); - byte [] ping_bytes = Encoding.UTF8.GetBytes ("ping\n"); - byte [] pong_bytes = new byte [4096]; + byte[] ping_bytes = Encoding.UTF8.GetBytes("ping\n"); + byte[] pong_bytes = new byte[4096]; - this.socket.Send (ping_bytes); + this.socket.Send(ping_bytes); - if (this.socket.Receive (pong_bytes) < 1) + if (this.socket.Receive(pong_bytes) < 1) // 10057 means "Socket is not connected" - throw new SocketException (10057); + throw new SocketException(10057); - Logger.LogInfo ("ListenerTcp", "Received pong from " + Server); + Logger.LogInfo("ListenerTcp", "Received pong from " + Server); i = 0; this.last_ping = DateTime.Now; - } else { + } + else + { // Check when the last ping occured. If it's // significantly longer than our regular interval the // system likely woke up from sleep and we want to // simulate a disconnect - int sleepiness = DateTime.Compare ( - this.last_ping.AddMilliseconds (timeout * 1000 * 1.2), + int sleepiness = DateTime.Compare( + this.last_ping.AddMilliseconds(timeout * 1000 * 1.2), DateTime.Now ); - if (sleepiness <= 0) { - Logger.LogInfo ("ListenerTcp", "System woke up from sleep"); + if (sleepiness <= 0) + { + Logger.LogInfo("ListenerTcp", "System woke up from sleep"); reason = DisconnectReason.SystemSleep; // 10057 means "Socket is not connected" - throw new SocketException (10057); + throw new SocketException(10057); } } - // The ping failed: disconnect completely - } catch (SocketException e) { + // The ping failed: disconnect completely + } + catch (SocketException e) + { Disconnect(reason, "Ping timeout: " + e.Message); return; } - Thread.Sleep (1000); + Thread.Sleep(1000); i++; } - } catch (Exception) { + } + catch (Exception) + { return; } - try { + try + { if (this.socket.Available > 0) - bytes_read = this.socket.Receive (bytes); + bytes_read = this.socket.Receive(bytes); // Parse the received message - if (bytes_read > 0) { - string received = Encoding.UTF8.GetString (bytes); - string line = received.Substring (0, received.IndexOf ("\n")); + if (bytes_read > 0) + { + string received = Encoding.UTF8.GetString(bytes); + string line = received.Substring(0, received.IndexOf("\n")); - if (!line.Contains ("!")) + if (!line.Contains("!")) continue; - string folder_identifier = line.Substring (0, line.IndexOf ("!")); - string message = CleanMessage (line.Substring (line.IndexOf ("!") + 1)); + string folder_identifier = line.Substring(0, line.IndexOf("!")); + string message = CleanMessage(line.Substring(line.IndexOf("!") + 1)); // We have a message! - if (!folder_identifier.Equals ("debug") && !string.IsNullOrEmpty (message)) - OnAnnouncement (new Announcement (folder_identifier, message)); + if (!folder_identifier.Equals("debug") && !string.IsNullOrEmpty(message)) + OnAnnouncement(new Announcement(folder_identifier, message)); } - } catch (SocketException e) { - Disconnect (DisconnectReason.TimeOut, "Timeout during receiving: " + e.Message); + } + catch (SocketException e) + { + Disconnect(DisconnectReason.TimeOut, "Timeout during receiving: " + e.Message); return; } } }); - this.thread.Start (); + this.thread.Start(); } - private void Disconnect (DisconnectReason reason, string message) + private void Disconnect(DisconnectReason reason, string message) { - this.is_connected = false; + this.is_connected = false; this.is_connecting = false; - if (this.socket != null) { - this.socket.Close (); - this.socket = null; + if (this.socket != null) + { + this.socket.Close(); + //this.socket = null; } - OnDisconnected (reason, message); + OnDisconnected(reason, message); } - protected override void AlsoListenToInternal (string folder_identifier) + protected override void AlsoListenToInternal(string folder_identifier) { string to_send = "subscribe " + folder_identifier + "\n"; - try { - this.socket.Send (Encoding.UTF8.GetBytes (to_send)); + try + { + this.socket.Send(Encoding.UTF8.GetBytes(to_send)); this.last_ping = DateTime.Now; - } catch (Exception e) { - this.is_connected = false; + } + catch (Exception e) + { + this.is_connected = false; this.is_connecting = false; - OnDisconnected (DisconnectReason.TimeOut, e.Message); + OnDisconnected(DisconnectReason.TimeOut, e.Message); } } - protected override void AnnounceInternal (Announcement announcement) + protected override void AnnounceInternal(Announcement announcement) { string to_send = "announce " + announcement.FolderIdentifier + " " + announcement.Message + "\n"; - try { + try + { if (this.socket != null) - this.socket.Send (Encoding.UTF8.GetBytes (to_send)); + this.socket.Send(Encoding.UTF8.GetBytes(to_send)); this.last_ping = DateTime.Now; - } catch (Exception e) { - this.is_connected = false; + } + catch (Exception e) + { + this.is_connected = false; this.is_connecting = false; - OnDisconnected (DisconnectReason.TimeOut, e.Message); + OnDisconnected(DisconnectReason.TimeOut, e.Message); } } - public override void Dispose () + public override void Dispose() { - if (this.socket != null) { - this.socket.Close (); - this.socket = null; + if (this.socket != null) + { + this.socket.Close(); + //this.socket = null; } - this.thread.Abort (); - this.thread.Join (); - base.Dispose (); + this.thread.Interrupt(); + this.thread.Join(); + + base.Dispose(); } - private string CleanMessage (string message) + private string CleanMessage(string message) { - message = message.Replace ("\n", ""); - message = message.Replace ("\0", ""); - return message.Trim (); + message = message.Replace("\n", ""); + message = message.Replace("\0", ""); + return message.Trim(); } } } diff --git a/Sparkles/Tests/Sparkles.Tests.csproj b/Sparkles/Tests/Sparkles.Tests.csproj index 11aa20976..7c9a55570 100644 --- a/Sparkles/Tests/Sparkles.Tests.csproj +++ b/Sparkles/Tests/Sparkles.Tests.csproj @@ -1,51 +1,18 @@ - - - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {8AB2969A-951F-4146-A0DD-C46D7526AC20} - Library - Sparkles.Tests - Sparkles.Tests - v4.6.1 - + net8.0 + false - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - - - true - bin\Release - prompt - 4 - - - - - ..\..\packages\NUnit.3.10.1\lib\net45\nunit.framework.dll - - - - - + + - + + + + + - - {2C914413-B31C-4362-93C7-1AE34F09112A} - Sparkles - + - \ No newline at end of file diff --git a/Sparkles/Tests/Test.cs b/Sparkles/Tests/Test.cs index 3a22f7df1..2bcf7ae0e 100644 --- a/Sparkles/Tests/Test.cs +++ b/Sparkles/Tests/Test.cs @@ -25,34 +25,36 @@ using System; using Sparkles; -namespace Sparkles.Tests { +namespace Sparkles.Tests +{ - [TestFixture ()] - public class TestExtensions { + [TestFixture()] + public class TestExtensions + { - [Test ()] - public void ReturnSHA256 () + [Test()] + public void ReturnSHA256() { - string result = "hello".SHA256 (); - Assert.IsTrue (result == "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); + string result = "hello".SHA256(); + Assert.That(result == "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); } - [Test ()] - public void ReturnSHA256WithSalt () + [Test()] + public void ReturnSHA256WithSalt() { string salt = "salt"; - string result = "hello".SHA256 (salt); + string result = "hello".SHA256(salt); - Assert.IsTrue (result == "87daba3fe263b34c335a0ee3b28ffec4d159aad6542502eaf551dc7b9128c267"); + Assert.That(result == "87daba3fe263b34c335a0ee3b28ffec4d159aad6542502eaf551dc7b9128c267"); } - [Test ()] - public void ReturnMD5 () + [Test()] + public void ReturnMD5() { - string result = "hello".MD5 (); - Assert.IsTrue (result == "5d41402abc4b2a76b9719d911017c592"); + string result = "hello".MD5(); + Assert.That(result == "5d41402abc4b2a76b9719d911017c592"); } @@ -60,52 +62,51 @@ public void ReturnMD5 () string plain_text = "secret"; string password = "password"; - [Test (), Order (1)] - public void ReturnAESEncrypt () + [Test(), Order(1)] + public void ReturnAESEncrypt() { - string result = plain_text.AESEncrypt (password); + string result = plain_text.AESEncrypt(password); cipher_text = result; - Assert.That (result, Is.Not.Null.And.Not.Empty); - } + Assert.That(result, Is.Not.Null.And.Not.Empty); + } - [Test (), Order (2)] - public void ReturnAESDecrypt () + [Test(), Order(2)] + public void ReturnAESDecrypt() { - string result = cipher_text.AESDecrypt (password); - Assert.IsTrue (result == plain_text); + string result = cipher_text.AESDecrypt(password); + Assert.That(result == plain_text); } - - [Test ()] - public void ReturnReplaceUnderScoreWithSpace () + [Test()] + public void ReturnReplaceUnderScoreWithSpace() { - string result = "good_morning_to_you".ReplaceUnderscoreWithSpace (); - Assert.IsTrue (result == "good morning to you"); + string result = "good_morning_to_you".ReplaceUnderscoreWithSpace(); + Assert.That(result == "good morning to you"); } - [Test ()] - public void ReturnToSize () + [Test()] + public void ReturnToSize() { - Assert.IsTrue (1099511627776.0.ToSize () == "1 ᴛʙ"); - Assert.IsTrue (1073741824.0.ToSize () == "1 ɢʙ"); - Assert.IsTrue (1048576.0.ToSize () == "1 ᴍʙ"); - Assert.IsTrue (1024.0.ToSize () == "1 ᴋʙ"); - Assert.IsTrue (0.0.ToSize () == "0 ʙ"); + Assert.That(1099511627776.0.ToSize() == "1 ᴛʙ"); + Assert.That(1073741824.0.ToSize() == "1 ɢʙ"); + Assert.That(1048576.0.ToSize() == "1 ᴍʙ"); + Assert.That(1024.0.ToSize() == "1 ᴋʙ"); + Assert.That(0.0.ToSize() == "0 ʙ"); } - [Test ()] - public void ReturnToPrettyDate () + [Test()] + public void ReturnToPrettyDate() { // TODO } - [Test ()] - public void ReturnIsSymlink () + [Test()] + public void ReturnIsSymlink() { // TODO } diff --git a/Sparkles/User.cs b/Sparkles/User.cs index 643445f34..775cc1199 100644 --- a/Sparkles/User.cs +++ b/Sparkles/User.cs @@ -15,19 +15,21 @@ // along with this program. If not, see . -namespace Sparkles { +namespace Sparkles +{ - public class User { + public class User + { public readonly string Name; public readonly string Email; - public string AvatarFilePath; + public string AvatarFilePath = null!; - public User (string name, string email) + public User(string name, string email) { - Name = name; + Name = name; Email = email; } } diff --git a/Sparkles/Watcher.cs b/Sparkles/Watcher.cs index f0bb178af..ea4898235 100644 --- a/Sparkles/Watcher.cs +++ b/Sparkles/Watcher.cs @@ -17,21 +17,23 @@ using System.IO; -namespace Sparkles { +namespace Sparkles +{ - public class Watcher : FileSystemWatcher { + public class Watcher : FileSystemWatcher + { public event ChangeEventEventHandler ChangeEvent = delegate { }; - public delegate void ChangeEventEventHandler (FileSystemEventArgs args); + public delegate void ChangeEventEventHandler(FileSystemEventArgs args); - object thread_lock = new object (); + object thread_lock = new object(); - public Watcher (string path) : base (path) + public Watcher(string path) : base(path) { IncludeSubdirectories = true; - EnableRaisingEvents = true; - Filter = "*"; + EnableRaisingEvents = true; + Filter = "*"; Changed += OnChanged; Created += OnChanged; @@ -40,20 +42,20 @@ public Watcher (string path) : base (path) } - void OnChanged (object sender, FileSystemEventArgs args) + void OnChanged(object sender, FileSystemEventArgs args) { - ChangeEvent (args); + ChangeEvent(args); } - public void Enable () + public void Enable() { lock (this.thread_lock) EnableRaisingEvents = true; } - public void Disable () + public void Disable() { lock (this.thread_lock) EnableRaisingEvents = false; From 5726adabaf303298fa157d6a7558915a6ba55cac Mon Sep 17 00:00:00 2001 From: uenz Date: Tue, 15 Oct 2024 20:26:58 +0200 Subject: [PATCH 02/14] Open chrashlog when unhandled exception occurs - open crashlog on unhandled exception - dont locally catch exception when execution local commands, this results in a chrsahlog file - git_scm update to latest --- SparkleShare/Common/SparkleShare.cs | 7 +++++- .../Windows/UserInterface/Controller.cs | 10 ++++++++- .../Windows/UserInterface/UserInterface.cs | 1 + SparkleShare/Windows/git.download | 2 +- SparkleShare/Windows/postBuild.cmd | 3 ++- Sparkles/BaseFetcher.cs | 12 +++------- Sparkles/Command.cs | 12 +--------- Sparkles/Configuration.cs | 19 +++++++++++----- Sparkles/Logger.cs | 22 +++++++------------ todo.md | 6 +++++ 10 files changed, 51 insertions(+), 43 deletions(-) create mode 100644 todo.md diff --git a/SparkleShare/Common/SparkleShare.cs b/SparkleShare/Common/SparkleShare.cs index 8c7aa846d..ccab842d0 100644 --- a/SparkleShare/Common/SparkleShare.cs +++ b/SparkleShare/Common/SparkleShare.cs @@ -16,6 +16,7 @@ using System; +using System.DirectoryServices.ActiveDirectory; using System.Threading; using Sparkles; @@ -42,7 +43,7 @@ public static void Main (string [] args) } AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; - + AppDomain.CurrentDomain.ProcessExit += OnProcessExit; Controller = new Controller (Configuration.DefaultConfiguration); Controller.Initialize (); @@ -61,6 +62,10 @@ static void OnUnhandledException (object sender, UnhandledExceptionEventArgs exc { var exception = (Exception) exception_args.ExceptionObject; Logger.WriteCrashReport (exception); + Controller.OpenFile(Configuration.DefaultConfiguration.CrashReportFilePath); + } + static void OnProcessExit(object sender, EventArgs e) + { } } } diff --git a/SparkleShare/Windows/UserInterface/Controller.cs b/SparkleShare/Windows/UserInterface/Controller.cs index d7bf0f3c9..1301eb93e 100644 --- a/SparkleShare/Windows/UserInterface/Controller.cs +++ b/SparkleShare/Windows/UserInterface/Controller.cs @@ -163,12 +163,20 @@ public override void CreateSparkleShareFolder () public override void OpenFile (string path) { - Process.Start (path); + var psi = new ProcessStartInfo(path) + { + UseShellExecute = true + }; + Process.Start (psi); } public override void OpenFolder (string path) { + var psi = new ProcessStartInfo(path) + { + UseShellExecute = true + }; Process.Start (path); } diff --git a/SparkleShare/Windows/UserInterface/UserInterface.cs b/SparkleShare/Windows/UserInterface/UserInterface.cs index a897e6039..9107f5174 100644 --- a/SparkleShare/Windows/UserInterface/UserInterface.cs +++ b/SparkleShare/Windows/UserInterface/UserInterface.cs @@ -66,6 +66,7 @@ private static void OnUnhandledException (object sender, ThreadExceptionEventArg try { Logger.WriteCrashReport (exception_args.Exception); } finally { + // TODO: open log file here Environment.Exit (-1); } } diff --git a/SparkleShare/Windows/git.download b/SparkleShare/Windows/git.download index b033dfca4..e39ba4ceb 100644 --- a/SparkleShare/Windows/git.download +++ b/SparkleShare/Windows/git.download @@ -1 +1 @@ -https://github.com/git-for-windows/git/releases/download/v2.26.1.windows.1/PortableGit-2.26.1-32-bit.7z.exe 862fa87cb4c00872055efbdbf3ed0d5e87838605bb0c3024bed3b32acf43cc0c +https://github.com/git-for-windows/git/releases/download/v2.47.0.windows.1/PortableGit-2.47.0-64-bit.7z.exe 0b7fcd76902ebde5b4c00ebae597d7f65dff8c3dd0ae59f5059e1aaa3adace87 \ No newline at end of file diff --git a/SparkleShare/Windows/postBuild.cmd b/SparkleShare/Windows/postBuild.cmd index d8ee6bc30..635f60be2 100755 --- a/SparkleShare/Windows/postBuild.cmd +++ b/SparkleShare/Windows/postBuild.cmd @@ -5,7 +5,8 @@ IF [%1]==[] (SET "OUTDIR=%~dp0bin\git_scm") ELSE (SET OUTDIR="%~1") IF EXIST %OUTDIR% GOTO skipgitdownload ECHO installing git -REM download git + +ECHO download git FOR /F "usebackq tokens=1" %%i IN ("%~dp0git.download") DO SET url=%%i FOR /F "usebackq tokens=2" %%i IN ("%~dp0git.download") DO SET md5hash=%%i CALL :downloadandverify %url% "%~dp0PortableGit.7z.exe" %md5hash% diff --git a/Sparkles/BaseFetcher.cs b/Sparkles/BaseFetcher.cs index 928835b4b..20d50dd91 100644 --- a/Sparkles/BaseFetcher.cs +++ b/Sparkles/BaseFetcher.cs @@ -117,15 +117,9 @@ protected BaseFetcher(SparkleFetcherInfo info) address = "ssh://" + address; TargetFolder = info.TargetDirectory; - // TODO: fix this hack - RemoteUrl = new UriBuilder - { - Scheme = "ssh", - Host = "github.com", - Path = remote_path, - UserName = "git" - }.Uri; - IsActive = false; + + RemoteUrl = new Uri (address + remote_path); + IsActive = false; } diff --git a/Sparkles/Command.cs b/Sparkles/Command.cs index 3d034bc2b..4451ac581 100644 --- a/Sparkles/Command.cs +++ b/Sparkles/Command.cs @@ -73,17 +73,7 @@ public Command(string path, string args, bool write_output) if (write_output) Logger.LogInfo("Cmd", folder + Path.GetFileName(StartInfo.FileName) + " " + StartInfo.Arguments); - try - { - base.Start(); - - } - catch (Exception e) - { - Logger.LogInfo("Cmd", "Couldn't execute command: " - + StartInfo.FileName + "," + e.Message); - Environment.Exit(-1); - } + base.Start(); } diff --git a/Sparkles/Configuration.cs b/Sparkles/Configuration.cs index cc49e8fcf..92c59995b 100644 --- a/Sparkles/Configuration.cs +++ b/Sparkles/Configuration.cs @@ -32,8 +32,9 @@ public class Configuration : XmlDocument if (InstallationInfo.OperatingSystem != OS.Windows && InstallationInfo.OperatingSystem != OS.macOS) app_data_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".config"); - - string config_path = Path.Combine(app_data_path, "org.sparkleshare.SparkleShare"); + // TODO: Compiler switch + //string config_path = Path.Combine(app_data_path, "org.sparkleshare.SparkleShare"); + string config_path = Path.Combine(app_data_path, "org.debug.sparkleshare.SparkleShare"); return new Configuration(config_path, "projects.xml"); }); @@ -50,7 +51,7 @@ public class Configuration : XmlDocument public string AvatarProvider = null!; public readonly string LogFilePath; - + public readonly string CrashReportFilePath; public static string HomePath { @@ -70,14 +71,22 @@ public string FoldersPath { if (GetConfigOption("folders_path") != null) return GetConfigOption("folders_path")!; - - return Path.Combine(HomePath, "SparkleShare"); + // TODO: Compiler switch + //return Path.Combine(HomePath, "SparkleShare"); + return Path.Combine(HomePath, "SparkleShareDebug"); } } public Configuration(string config_path, string config_file_name) { + string home_path = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + + if (InstallationInfo.OperatingSystem == OS.Windows) + home_path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + + CrashReportFilePath = Path.Combine(home_path, "SparkleShare", "crash_report.txt"); + FilePath = Path.Combine(config_path, config_file_name); DirectoryPath = config_path; diff --git a/Sparkles/Logger.cs b/Sparkles/Logger.cs index 483251bd8..961d0bf22 100644 --- a/Sparkles/Logger.cs +++ b/Sparkles/Logger.cs @@ -24,8 +24,8 @@ namespace Sparkles public static class Logger { - static StreamWriter log_writer = File.CreateText(Configuration.DefaultConfiguration.LogFilePath); - static object log_writer_lock = new object(); + //static StreamWriter log_writer = File.CreateText(Configuration.DefaultConfiguration.LogFilePath); + //static object log_writer_lock = new object(); public static void LogInfo(string type, string message) @@ -49,13 +49,14 @@ public static void LogInfo(string type, string message, Exception? exception) if (Configuration.DebugMode) Console.WriteLine(line); - - lock (log_writer_lock) + StreamWriter log_writer = File.CreateText(Configuration.DefaultConfiguration.LogFilePath); + //lock (log_writer_lock) { try { log_writer.WriteLine(line); log_writer.Flush(); + log_writer.Close(); } catch (Exception e) @@ -69,16 +70,9 @@ public static void LogInfo(string type, string message, Exception? exception) public static void WriteCrashReport(Exception e) { - if (log_writer != null) - log_writer.Close(); - - string home_path = Environment.GetFolderPath(Environment.SpecialFolder.Personal); - - if (InstallationInfo.OperatingSystem == OS.Windows) - home_path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - - string crash_report_file_path = Path.Combine(home_path, "SparkleShare", "crash_report.txt"); - + /*if (log_writer != null) + log_writer.Close();*/ + string crash_report_file_path= Configuration.DefaultConfiguration.CrashReportFilePath; string n = Environment.NewLine; string crash_report = "Oops! SparkleShare has crashed... :(" + n + diff --git a/todo.md b/todo.md new file mode 100644 index 000000000..5508ecbde --- /dev/null +++ b/todo.md @@ -0,0 +1,6 @@ +# todolist +- [x] open crashlog on unhandled exception +- [x] dont locally catch exception when execution local commands, this results in a chrsahlog file +- [x] git_scm update to latest +- [ ] git url syntax +o \ No newline at end of file From 991c6e35910f30a8cb3e299a3ebd5ee37a024f61 Mon Sep 17 00:00:00 2001 From: uenz Date: Fri, 18 Oct 2024 16:57:38 +0200 Subject: [PATCH 03/14] Added scp like uri handling --- .github/workflows/github-actions-demo.yml | 2 +- SparkleShare/Common/AboutController.cs | 9 +- SparkleShare/Common/EventLogController.cs | 2 +- SparkleShare/Common/Presets/bitbucket.xml | 2 +- SparkleShare/Common/Presets/github.xml | 4 +- SparkleShare/Common/Presets/gitlab.xml | 4 +- SparkleShare/Common/SetupController.cs | 2 +- .../Windows/SparkleShare.Windows.csproj | 7 +- .../Windows/UserInterface/Controller.cs | 15 ++- .../Windows/UserInterface/EventLog.cs | 6 +- .../Windows/UserInterface/NotifyIcon.cs | 1 - SparkleShare/Windows/git.download | 2 +- Sparkles/BaseFetcher.cs | 10 +- Sparkles/BaseRepository.cs | 4 +- Sparkles/ChangeSet.cs | 4 +- Sparkles/Configuration.cs | 15 ++- Sparkles/Git/Git.Fetcher.cs | 12 +- Sparkles/Logger.cs | 14 +-- Sparkles/ScpUri.cs | 113 ++++++++++++++++++ Sparkles/Sparkles.csproj | 1 + todo.md | 10 +- 21 files changed, 184 insertions(+), 55 deletions(-) create mode 100644 Sparkles/ScpUri.cs diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 9e0d980da..0891648db 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -10,7 +10,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ macos-12, + os: [ # macos-12, # ubuntu-20.04, windows-latest ] diff --git a/SparkleShare/Common/AboutController.cs b/SparkleShare/Common/AboutController.cs index b16b00f38..6cef8404e 100644 --- a/SparkleShare/Common/AboutController.cs +++ b/SparkleShare/Common/AboutController.cs @@ -32,10 +32,10 @@ public class AboutController { public event UpdateLabelEventDelegate UpdateLabelEvent = delegate { }; public delegate void UpdateLabelEventDelegate (string text); - - public readonly string WebsiteLinkAddress = "https://www.sparkleshare.org/"; - public readonly string CreditsLinkAddress = "https://github.com/hbons/SparkleShare/blob/master/.github/AUTHORS.md"; - public readonly string ReportProblemLinkAddress = "https://www.github.com/hbons/SparkleShare/issues"; + // TODO: get link to issues from static configuration + public readonly string WebsiteLinkAddress = "https://github.com/uenz/SparkleShare/wiki"; + public readonly string CreditsLinkAddress = "https://github.com/uenz/SparkleShare/blob/master/.github/AUTHORS.md"; + public readonly string ReportProblemLinkAddress = "https://www.github.com/uenz/SparkleShare/issues"; public readonly string DebugLogLinkAddress = "file://" + SparkleShare.Controller.Config.LogFilePath; public string RunningVersion; @@ -68,7 +68,6 @@ void CheckForNewVersion () HttpClient web_client = new(); var uri = new Uri ("https://www.sparkleshare.org/version"); HttpClient client = new(); - HttpResponseMessage response = null!; try { diff --git a/SparkleShare/Common/EventLogController.cs b/SparkleShare/Common/EventLogController.cs index b5c6b37bb..fdcbf9643 100644 --- a/SparkleShare/Common/EventLogController.cs +++ b/SparkleShare/Common/EventLogController.cs @@ -407,7 +407,7 @@ public string GetHTMLLog (List change_sets) { if (change_sets == null || change_sets.Count == 0) return SparkleShare.Controller.EventLogHTML.Replace ("", - "
This project does not keep a history.
"); + "
This project does not keep a history.
").Replace("", "100000000"); List activity_days = new List (); diff --git a/SparkleShare/Common/Presets/bitbucket.xml b/SparkleShare/Common/Presets/bitbucket.xml index d8c118ceb..a0a5392cc 100644 --- a/SparkleShare/Common/Presets/bitbucket.xml +++ b/SparkleShare/Common/Presets/bitbucket.xml @@ -14,7 +14,7 @@ - /username/project + :username/project True diff --git a/SparkleShare/Common/Presets/github.xml b/SparkleShare/Common/Presets/github.xml index c7f1bb9a7..620c5ce69 100644 --- a/SparkleShare/Common/Presets/github.xml +++ b/SparkleShare/Common/Presets/github.xml @@ -10,12 +10,12 @@ b8:d8:95:ce:d9:2c:0a:c0:e1:71:cd:2e:f5:ef:01:ba:34:17:55:4a:4a:64:80:d3:31:cc:c2:be:3d:ed:0f:6b
- ssh://git@github.com/ + git@github.com/
- /username/project + :username/project diff --git a/SparkleShare/Common/Presets/gitlab.xml b/SparkleShare/Common/Presets/gitlab.xml index b6db2ed78..22f098514 100644 --- a/SparkleShare/Common/Presets/gitlab.xml +++ b/SparkleShare/Common/Presets/gitlab.xml @@ -9,12 +9,12 @@ 44:e4:05:bc:f4:e1:1a:b5:b8:46:e5:8b:a0:bf:6d:ab:d2:3d:cc:9e:36:7c:ae:17:cb:0c:91:b5:b3:b3:fc:44
- ssh://git@gitlab.com/ + git@gitlab.com/
- /username/project + :username/project diff --git a/SparkleShare/Common/SetupController.cs b/SparkleShare/Common/SetupController.cs index 00e0b0aa5..f8fb0b2c1 100644 --- a/SparkleShare/Common/SetupController.cs +++ b/SparkleShare/Common/SetupController.cs @@ -335,7 +335,7 @@ private void AddPageFetchedDelegate (string remote_url, string [] warnings) // so the user can easily use the same host again if (SelectedPresetIndex == 0) { Preset new_preset; - Uri uri = new Uri (remote_url); + ScpUri uri = new ScpUri (remote_url); try { string address = remote_url.Replace (uri.AbsolutePath, ""); diff --git a/SparkleShare/Windows/SparkleShare.Windows.csproj b/SparkleShare/Windows/SparkleShare.Windows.csproj index 4dce1dbfc..d5f3fb6c8 100755 --- a/SparkleShare/Windows/SparkleShare.Windows.csproj +++ b/SparkleShare/Windows/SparkleShare.Windows.csproj @@ -204,13 +204,12 @@ - + + Always + - - - diff --git a/SparkleShare/Windows/UserInterface/Controller.cs b/SparkleShare/Windows/UserInterface/Controller.cs index 1301eb93e..015c79ba3 100644 --- a/SparkleShare/Windows/UserInterface/Controller.cs +++ b/SparkleShare/Windows/UserInterface/Controller.cs @@ -173,17 +173,20 @@ public override void OpenFile (string path) public override void OpenFolder (string path) { - var psi = new ProcessStartInfo(path) - { - UseShellExecute = true - }; - Process.Start (path); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.FileName =path; + startInfo.WorkingDirectory =path; + startInfo.UseShellExecute = true; + Process.Start(startInfo); } public override void OpenWebsite (string url) { - Process.Start (new ProcessStartInfo (url)); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.FileName = url; + startInfo.UseShellExecute = true; + Process.Start(startInfo); } diff --git a/SparkleShare/Windows/UserInterface/EventLog.cs b/SparkleShare/Windows/UserInterface/EventLog.cs index 346931895..b4d2e1d99 100644 --- a/SparkleShare/Windows/UserInterface/EventLog.cs +++ b/SparkleShare/Windows/UserInterface/EventLog.cs @@ -254,7 +254,11 @@ private void UpdateContent(string html) html = html.Replace("", pixmaps_path + "/document-edited-12.png"); html = html.Replace("", pixmaps_path + "/document-deleted-12.png"); html = html.Replace("", pixmaps_path + "/document-moved-12.png"); - + html.Replace("", "100000000"); + using (StreamWriter outputFile = new StreamWriter(System.IO.Path.Combine(@"c:\temp", "page.html"))) + { + outputFile.WriteLine(html); + } this.spinner.Stop(); this.webbrowser.ObjectForScripting = new SparkleScriptingObject(); diff --git a/SparkleShare/Windows/UserInterface/NotifyIcon.cs b/SparkleShare/Windows/UserInterface/NotifyIcon.cs index 7ff3fc47f..92269592d 100644 --- a/SparkleShare/Windows/UserInterface/NotifyIcon.cs +++ b/SparkleShare/Windows/UserInterface/NotifyIcon.cs @@ -82,7 +82,6 @@ private Forms.NotifyIcon Notification { public NotifyIcon() { VisibilityProperty.OverrideMetadata(typeof(NotifyIcon), new PropertyMetadata(OnVisibilityChanged)); - // TODO ContextMenu wird nicht mehr untersttzt. Verwenden Sie stattdessen ContextMenuStrip. Weitere Informationen finden Sie unter: https://docs.microsoft.com/en-us/dotnet/core/compatibility/winforms#removed-controls Notification = new Forms.NotifyIcon { Text = Text, Visible = true, diff --git a/SparkleShare/Windows/git.download b/SparkleShare/Windows/git.download index e39ba4ceb..b033dfca4 100644 --- a/SparkleShare/Windows/git.download +++ b/SparkleShare/Windows/git.download @@ -1 +1 @@ -https://github.com/git-for-windows/git/releases/download/v2.47.0.windows.1/PortableGit-2.47.0-64-bit.7z.exe 0b7fcd76902ebde5b4c00ebae597d7f65dff8c3dd0ae59f5059e1aaa3adace87 \ No newline at end of file +https://github.com/git-for-windows/git/releases/download/v2.26.1.windows.1/PortableGit-2.26.1-32-bit.7z.exe 862fa87cb4c00872055efbdbf3ed0d5e87838605bb0c3024bed3b32acf43cc0c diff --git a/Sparkles/BaseFetcher.cs b/Sparkles/BaseFetcher.cs index 20d50dd91..8a8bc5c5f 100644 --- a/Sparkles/BaseFetcher.cs +++ b/Sparkles/BaseFetcher.cs @@ -66,7 +66,7 @@ public abstract class BaseFetcher public readonly List AvailableStorageTypes = new List(); - public Uri RemoteUrl { get; protected set; } + public ScpUri RemoteUrl { get; protected set; } public string RequiredFingerprint { get; protected set; } public readonly bool FetchPriorHistory; public string TargetFolder { get; protected set; } @@ -109,16 +109,16 @@ protected BaseFetcher(SparkleFetcherInfo info) if (address.EndsWith("/", StringComparison.InvariantCulture)) address = address.Substring(0, address.Length - 1); - + /* if (!remote_path.StartsWith("/", StringComparison.InvariantCulture)) - remote_path = "/" + remote_path; + remote_path = "/" + remote_path;*/ if (!address.Contains("://")) address = "ssh://" + address; TargetFolder = info.TargetDirectory; - RemoteUrl = new Uri (address + remote_path); + RemoteUrl = new ScpUri (address + remote_path); IsActive = false; } @@ -210,7 +210,7 @@ void CreateInitialChangeSet() string n = Environment.NewLine; string file_path = Path.Combine(TargetFolder, "SparkleShare.txt"); - var uri_builder = new UriBuilder(RemoteUrl); + var uri_builder = new UriBuilder(RemoteUrl.ToString()); // Don't expose possible username or password if (RemoteUrl.Scheme.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) diff --git a/Sparkles/BaseRepository.cs b/Sparkles/BaseRepository.cs index ec78df09b..e4d719eda 100644 --- a/Sparkles/BaseRepository.cs +++ b/Sparkles/BaseRepository.cs @@ -114,7 +114,7 @@ public abstract class BaseRepository public readonly string LocalPath; public readonly string Name; - public readonly Uri RemoteUrl; + public readonly ScpUri RemoteUrl; public List ChangeSets { get; set; } = null!; public SyncStatus Status { get; set; } public ErrorStatus Error { get; protected set; } @@ -206,7 +206,7 @@ public BaseRepository(string path, Configuration config) this.local_config = config; LocalPath = path; Name = Path.GetFileName(LocalPath); - RemoteUrl = new Uri(this.local_config.UrlByName(Name)!); + RemoteUrl = new ScpUri(this.local_config.UrlByName(Name)!); IsBuffering = false; this.identifier = Identifier; diff --git a/Sparkles/ChangeSet.cs b/Sparkles/ChangeSet.cs index a6a4485e8..97013fac0 100644 --- a/Sparkles/ChangeSet.cs +++ b/Sparkles/ChangeSet.cs @@ -40,7 +40,7 @@ public class ChangeSet public string Revision = null!; public DateTime Timestamp; public DateTime FirstTimestamp; - public Uri RemoteUrl = null!; + public ScpUri RemoteUrl = null!; public List Changes = new List(); @@ -92,7 +92,7 @@ public string FullPath return Path.Combine(custom_path, Name); return Path.Combine(Configuration.DefaultConfiguration.FoldersPath, - new Uri(Configuration.DefaultConfiguration.UrlByName(Name)!).Host, + new ScpUri(Configuration.DefaultConfiguration.UrlByName(Name)!).Host, Name); } } diff --git a/Sparkles/Configuration.cs b/Sparkles/Configuration.cs index 92c59995b..afbed728f 100644 --- a/Sparkles/Configuration.cs +++ b/Sparkles/Configuration.cs @@ -32,10 +32,12 @@ public class Configuration : XmlDocument if (InstallationInfo.OperatingSystem != OS.Windows && InstallationInfo.OperatingSystem != OS.macOS) app_data_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".config"); - // TODO: Compiler switch - //string config_path = Path.Combine(app_data_path, "org.sparkleshare.SparkleShare"); + // TODO: rename Compiler switch +#if DEBUG string config_path = Path.Combine(app_data_path, "org.debug.sparkleshare.SparkleShare"); - +#else + string config_path = Path.Combine(app_data_path, "org.sparkleshare.SparkleShare"); +#endif return new Configuration(config_path, "projects.xml"); }); private static readonly Lazy ConfigLazy = lazy; @@ -71,9 +73,12 @@ public string FoldersPath { if (GetConfigOption("folders_path") != null) return GetConfigOption("folders_path")!; - // TODO: Compiler switch - //return Path.Combine(HomePath, "SparkleShare"); + +#if DEBUG // TODO: rename compiler switch return Path.Combine(HomePath, "SparkleShareDebug"); +#else + return Path.Combine(HomePath, "SparkleShare"); +#endif } } diff --git a/Sparkles/Git/Git.Fetcher.cs b/Sparkles/Git/Git.Fetcher.cs index f6f671a74..55ea48406 100644 --- a/Sparkles/Git/Git.Fetcher.cs +++ b/Sparkles/Git/Git.Fetcher.cs @@ -42,8 +42,8 @@ protected override bool IsFetchedRepoEmpty { public GitFetcher (SparkleFetcherInfo fetcher_info, SSHAuthenticationInfo auth_info) : base (fetcher_info) { this.auth_info = auth_info; - var uri_builder = new UriBuilder (RemoteUrl); - + var uri_builder = new UriBuilder (RemoteUrl.ToUriString()); + // TODO debug this section if (!RemoteUrl.Scheme.Equals ("ssh") && !RemoteUrl.Scheme.Equals ("git")) uri_builder.Scheme = "ssh"; @@ -63,8 +63,9 @@ public GitFetcher (SparkleFetcherInfo fetcher_info, SSHAuthenticationInfo auth_i } else if (string.IsNullOrEmpty (RemoteUrl.UserInfo)) { uri_builder.UserName = "storage"; } + bool tmp = RemoteUrl.scp_style; - RemoteUrl = uri_builder.Uri; + RemoteUrl = new ScpUri(uri_builder.Uri.ToString(),tmp); AvailableStorageTypes.Add ( new StorageTypeInfo (StorageType.Encrypted, "Encrypted Storage", @@ -307,11 +308,12 @@ public override string FormatName () StorageType? DetermineStorageType () { + // TODO bad hack, because ls-remote cant handle scp_like syntax var git_ls_remote = new GitCommand (Configuration.DefaultConfiguration.TmpPath, - string.Format ("ls-remote --heads \"{0}\"", RemoteUrl), auth_info); + string.Format ("ls-remote --heads \"{0}\"", RemoteUrl.ToString()), auth_info); string output = git_ls_remote.StartAndReadStandardOutput (); - + // TODO handle exit codes 128,129,130 related to keys https://mazack.org/unix/errno.php if (git_ls_remote.ExitCode != 0) return null; diff --git a/Sparkles/Logger.cs b/Sparkles/Logger.cs index 961d0bf22..cbfd00c21 100644 --- a/Sparkles/Logger.cs +++ b/Sparkles/Logger.cs @@ -17,6 +17,7 @@ using System; using System.IO; +using System.Xml.Schema; namespace Sparkles { @@ -49,20 +50,17 @@ public static void LogInfo(string type, string message, Exception? exception) if (Configuration.DebugMode) Console.WriteLine(line); - StreamWriter log_writer = File.CreateText(Configuration.DefaultConfiguration.LogFilePath); //lock (log_writer_lock) { try { - log_writer.WriteLine(line); - log_writer.Flush(); - log_writer.Close(); - + File.AppendAllLines(Configuration.DefaultConfiguration.LogFilePath, new List([line])); } + catch (Exception e) { Console.WriteLine(string.Format("Could not write to log {0}: {1} {2}", - (log_writer.BaseStream as FileStream)!.Name, e.Message, e.StackTrace)); + Configuration.DefaultConfiguration.LogFilePath, e.Message, e.StackTrace)); } } } @@ -72,13 +70,15 @@ public static void WriteCrashReport(Exception e) { /*if (log_writer != null) log_writer.Close();*/ + + // TODO: get link to issues from static configuration string crash_report_file_path= Configuration.DefaultConfiguration.CrashReportFilePath; string n = Environment.NewLine; string crash_report = "Oops! SparkleShare has crashed... :(" + n + n + "If you want to help fix this crash, please report it at " + n + - "https://github.com/hbons/SparkleShare/issues and include the lines below." + n + + "https://github.com/uenz/SparkleShare/issues and include the lines below." + n + n + "Remove any sensitive information like file names, IP addresses, domain names, etc. if needed." + n + n + diff --git a/Sparkles/ScpUri.cs b/Sparkles/ScpUri.cs new file mode 100644 index 000000000..9663992b2 --- /dev/null +++ b/Sparkles/ScpUri.cs @@ -0,0 +1,113 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Sparkles +{ + public class ScpUri + + { + public bool scp_style = false; + protected Uri uri = null!; + + public ScpUri([StringSyntax("Uri")] string uriString) + + { + if (Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri) && uriString.Contains("://")) + { + scp_style = false; + } + else + { + if (!uriString.Contains("://")) + { + uriString = "ssh://" + uriString; + } + int place = uriString.LastIndexOf(':'); + if (place != -1) + + { + uriString = uriString.Remove(place, 1).Insert(place, "/"); + } + + Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri); + scp_style = true; + } + } + + public ScpUri([StringSyntax("Uri")] string uriString, bool use_scp_style) + + { + Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri); + scp_style = use_scp_style; + } + + public string AbsolutePath + + { + get + + { + if (scp_style == false) + { + return uri.AbsolutePath; + } + else + { + string tmp = this.ToString(); + return tmp.Substring(tmp.LastIndexOf(':')); + } + } + } + + public string Host + { + get + { + return uri.Host; + } + } + + public int Port + + { + get + { + return uri.Port; + } + } + + public string Scheme + + { + get + + { + return uri.Scheme; + } + } + public string UserInfo + { + get + { + return uri.UserInfo; + } + } + override public string ToString() + + { + if (scp_style == false) + { + return uri.ToString(); + + } + else + { + return uri.ToString().Replace(uri.Host + "/", uri.Host + ":").Replace(uri.Scheme+"://", ""); // for SCP string scheme needs to be empty + } + } + public string ToUriString() + + { + return uri.ToString(); + } + } +} \ No newline at end of file diff --git a/Sparkles/Sparkles.csproj b/Sparkles/Sparkles.csproj index 36743d4bf..cc5336279 100755 --- a/Sparkles/Sparkles.csproj +++ b/Sparkles/Sparkles.csproj @@ -101,6 +101,7 @@ Component + Component diff --git a/todo.md b/todo.md index 5508ecbde..71513b741 100644 --- a/todo.md +++ b/todo.md @@ -1,6 +1,10 @@ # todolist +## windows dotnet8 - [x] open crashlog on unhandled exception -- [x] dont locally catch exception when execution local commands, this results in a chrsahlog file +- [x] dont locally catch exception when execution local commands, this results in a chrashlog file - [x] git_scm update to latest -- [ ] git url syntax -o \ No newline at end of file +- [x] git url syntax +- [ ] deactivate/replace notification service -> was not active, so not so important + + +## maui or avaloniagui dotnet8 \ No newline at end of file From 7e376497343bc15e87acf4a808d6828909723a0a Mon Sep 17 00:00:00 2001 From: uenz Date: Fri, 18 Oct 2024 21:25:09 +0200 Subject: [PATCH 04/14] Added ScpUri tests --- SparkleShare/Windows/build.cmd | 12 +++--- Sparkles/Tests/TestScpUri.cs | 70 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 Sparkles/Tests/TestScpUri.cs diff --git a/SparkleShare/Windows/build.cmd b/SparkleShare/Windows/build.cmd index ea483431e..ae1cecdb9 100755 --- a/SparkleShare/Windows/build.cmd +++ b/SparkleShare/Windows/build.cmd @@ -1,14 +1,14 @@ @echo off -set WinDirNet=%WinDir%\Microsoft.NET\Framework -set msbuild="%WinDirNet%\v4.0\msbuild.exe" -if not exist %msbuild% set msbuild="%WinDirNet%\v4.0.30319\msbuild.exe" +REM set WinDirNet=%WinDir%\Microsoft.NET\Framework +REM set msbuild="%WinDirNet%\v4.0\msbuild.exe" +REM if not exist %msbuild% set msbuild="%WinDirNet%\v4.0.30319\msbuild.exe" set WIX=C:\Program Files (x86)\WiX Toolset v3.14 set wixBinDir=%WIX%\bin set OutputDir=%~dp0bin if not exist "%OutputDir%" mkdir "%OutputDir%" -%msbuild% "%~dp0..\..\SparkleShare.sln" /target:SparkleShare_Windows:Rebuild /p:Configuration=ReleaseWindows /p:Platform="Any CPU" -m +dotnet build "%~dp0..\..\SparkleShare.sln" /target:SparkleShare_Windows:Rebuild /p:Configuration=ReleaseWindows /p:Platform="Any CPU" -m if "%1"=="installer" ( if exist "%wixBinDir%" ( @@ -16,11 +16,13 @@ if "%1"=="installer" ( "%wixBinDir%\heat.exe" dir "%OutputDir%\git_scm" -cg gitScmComponentGroup -gg -scom -sreg -sfrag -srd -dr GITSCM_DIR -var wix.gitscmpath -o "%~dp0\git_scm.wxs" "%wixBinDir%\heat.exe" dir "%OutputDir%\Images" -cg ImagesComponentGroup -gg -scom -sreg -sfrag -srd -dr IMAGES_DIR -var wix.imagespath -o "%~dp0\images.wxs" "%wixBinDir%\heat.exe" dir "%OutputDir%\Presets" -cg PresetsComponentGroup -gg -scom -sreg -sfrag -srd -dr PRESETS_DIR -var wix.presetspath -o "%~dp0\presets.wxs" + "%wixBinDir%\heat.exe" dir "%OutputDir%\runtimes" -cg runtimesComponentGroup -gg -scom -sreg -sfrag -srd -dr PRESETS_DIR -var wix.runtimespath -o "%~dp0\runtimes.wxs" "%wixBinDir%\candle" "%~dp0\SparkleShare.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" "%wixBinDir%\candle" "%~dp0\git_scm.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" "%wixBinDir%\candle" "%~dp0\images.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" "%wixBinDir%\candle" "%~dp0\presets.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" - "%wixBinDir%\light" -ext WixUIExtension -ext WixUtilExtension "%~dp0Sparkleshare.wixobj" "%~dp0git_scm.wixobj" "%~dp0images.wixobj" "%~dp0presets.wixobj" -droot="%~dp0." -dgitscmpath="%OutputDir%\git_scm" -dimagespath="%OutputDir%\Images" -dpresetspath="%OutputDir%\Presets" -o "%~dp0SparkleShare.msi" + "%wixBinDir%\candle" "%~dp0\runtimes.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" + "%wixBinDir%\light" -ext WixUIExtension -ext WixUtilExtension "%~dp0Sparkleshare.wixobj" "%~dp0git_scm.wixobj" "%~dp0images.wixobj" "%~dp0presets.wixobj" "%~dp0runtimes.wixobj" -droot="%~dp0." -dgitscmpath="%OutputDir%\git_scm" -dimagespath="%OutputDir%\Images" -dpresetspath="%OutputDir%\Presets" -druntimespath="%OutputDir%\runtimes" -o "%~dp0SparkleShare.msi" if exist "%~dp0\SparkleShare.msi" echo SparkleShare.msi created. ) else ( echo Not building installer ^(could not find wix, Windows Installer XML toolset^) diff --git a/Sparkles/Tests/TestScpUri.cs b/Sparkles/Tests/TestScpUri.cs new file mode 100644 index 000000000..5df7c552f --- /dev/null +++ b/Sparkles/Tests/TestScpUri.cs @@ -0,0 +1,70 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sparkles.Tests +{ + [TestFixture()] + internal class TestScpUri + { + static string urlString = "git@github.com:uenz/SparkleShare.git"; + static ScpUri scp_url = new(urlString); + static ScpUri url = new(urlString,false); + + [Test()] + public void ScpUriToString() + { + //string urlString = "git@github.com:uenz/SparkleShare.git"; + //ScpUri scp_url= new(urlString); + //ScpUri url = new(urlString,false); + + Assert.That(TestScpUri.scp_url.ToString()== TestScpUri.urlString); + Assert.That(TestScpUri.url.ToString() == TestScpUri.url.Scheme+"://"+ TestScpUri.urlString.Replace(':','/')); + } + [Test()] + public void SshUriToString() + { + string urlString = "ssh://git@github.com/uenz/SparkleShare.git"; + ScpUri scp_url = new(urlString); + ScpUri url = new(urlString, false); + + Assert.That(scp_url.ToString() == urlString); + Assert.That(url.ToString() == urlString); + } + [Test()] + public void HttpToString() + { + string urlString = "http://git@github.com/uenz/SparkleShare.git"; + ScpUri scp_url = new(urlString); + ScpUri url = new(urlString, false); + + Assert.That(scp_url.ToString() == urlString); + Assert.That(url.ToString() == urlString); + } + [Test()] + public void AbsolutePath() + { + Assert.That(TestScpUri.url.AbsolutePath == "/uenz/SparkleShare.git"); + Assert.That(TestScpUri.scp_url.AbsolutePath == ":uenz/SparkleShare.git"); + + } + [Test()] + public void Scheme() + { + Assert.That(TestScpUri.url.Scheme == TestScpUri.scp_url.Scheme); + } + [Test()] + public void Host() + { + Assert.That(TestScpUri.url.Host == TestScpUri.scp_url.Host); + } + [Test()] + public void Userinfo() + { + Assert.That(TestScpUri.url.UserInfo == TestScpUri.scp_url.UserInfo); + } + } +} From f5f0a6e7230d62a3b003b991b8012e46d2ed1f49 Mon Sep 17 00:00:00 2001 From: uenz Date: Fri, 18 Oct 2024 21:26:39 +0200 Subject: [PATCH 05/14] Fixed build script for installer --- SparkleShare/Windows/SparkleShare.Windows.csproj | 1 + SparkleShare/Windows/SparkleShare.wxs | 7 +++++++ Sparkles/Configuration.cs | 4 ++-- Sparkles/ScpUri.cs | 9 +++++---- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/SparkleShare/Windows/SparkleShare.Windows.csproj b/SparkleShare/Windows/SparkleShare.Windows.csproj index d5f3fb6c8..36fd3abf3 100755 --- a/SparkleShare/Windows/SparkleShare.Windows.csproj +++ b/SparkleShare/Windows/SparkleShare.Windows.csproj @@ -17,6 +17,7 @@ 1.0.0.%2a false true + false Images\sparkleshare-app.ico diff --git a/SparkleShare/Windows/SparkleShare.wxs b/SparkleShare/Windows/SparkleShare.wxs index 1c7c66aa5..d3c355287 100644 --- a/SparkleShare/Windows/SparkleShare.wxs +++ b/SparkleShare/Windows/SparkleShare.wxs @@ -1,6 +1,9 @@ + + + @@ -19,10 +22,13 @@ + + + @@ -64,6 +70,7 @@ + diff --git a/Sparkles/Configuration.cs b/Sparkles/Configuration.cs index afbed728f..0311f14f0 100644 --- a/Sparkles/Configuration.cs +++ b/Sparkles/Configuration.cs @@ -33,7 +33,7 @@ public class Configuration : XmlDocument if (InstallationInfo.OperatingSystem != OS.Windows && InstallationInfo.OperatingSystem != OS.macOS) app_data_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".config"); // TODO: rename Compiler switch -#if DEBUG +#if DEBUG_DATASET string config_path = Path.Combine(app_data_path, "org.debug.sparkleshare.SparkleShare"); #else string config_path = Path.Combine(app_data_path, "org.sparkleshare.SparkleShare"); @@ -74,7 +74,7 @@ public string FoldersPath if (GetConfigOption("folders_path") != null) return GetConfigOption("folders_path")!; -#if DEBUG // TODO: rename compiler switch +#if DEBUG_DATASET // TODO: rename compiler switch return Path.Combine(HomePath, "SparkleShareDebug"); #else return Path.Combine(HomePath, "SparkleShare"); diff --git a/Sparkles/ScpUri.cs b/Sparkles/ScpUri.cs index 9663992b2..a98ac1eea 100644 --- a/Sparkles/ScpUri.cs +++ b/Sparkles/ScpUri.cs @@ -33,10 +33,8 @@ public ScpUri([StringSyntax("Uri")] string uriString) } } - public ScpUri([StringSyntax("Uri")] string uriString, bool use_scp_style) - + public ScpUri([StringSyntax("Uri")] string uriString, bool use_scp_style) : this(uriString) { - Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri); scp_style = use_scp_style; } @@ -71,7 +69,10 @@ public int Port { get { - return uri.Port; + if (scp_style == false) + return uri.Port; + else + return -1; } } From e2ddea6b1160342b9160cc232ea485f005f51f73 Mon Sep 17 00:00:00 2001 From: uenz Date: Thu, 31 Oct 2024 21:11:22 +0100 Subject: [PATCH 06/14] Upgraded installer to wix v4 --- SparkleShare.sln | 262 +++++++++++++++++- SparkleShare/Common/Images/Left-Dialog.png | Bin 0 -> 59383 bytes .../Common/Images/Sources/Left-Dialog.xcf | Bin 0 -> 156106 bytes .../Common/Images/Sources/Top-Banner.xcf | Bin 0 -> 4276 bytes SparkleShare/Common/Images/Top-Banner.png | Bin 0 -> 2958 bytes .../Windows/Installer/LaunchAppOnExit.wxi | 74 +++++ .../Windows/Installer/Package.en-us.wxl | 18 ++ SparkleShare/Windows/Installer/Predefines.wxi | 98 +++++++ .../SparkleShare.Windows.Installer.wixproj | 67 +++++ .../Windows/Installer/productVersion.wxi | 4 + .../Windows/SparkleShare.Windows.csproj | 12 + SparkleShare/Windows/build.cmd | 42 +-- Sparkles/Git/Sparkles.Git.csproj | 3 + Sparkles/Sparkles.csproj | 5 +- Sparkles/Tests/Sparkles.Tests.csproj | 1 + scripts/bump-version.ps1 | 21 ++ todo.md | 1 + 17 files changed, 582 insertions(+), 26 deletions(-) create mode 100644 SparkleShare/Common/Images/Left-Dialog.png create mode 100644 SparkleShare/Common/Images/Sources/Left-Dialog.xcf create mode 100644 SparkleShare/Common/Images/Sources/Top-Banner.xcf create mode 100644 SparkleShare/Common/Images/Top-Banner.png create mode 100644 SparkleShare/Windows/Installer/LaunchAppOnExit.wxi create mode 100644 SparkleShare/Windows/Installer/Package.en-us.wxl create mode 100644 SparkleShare/Windows/Installer/Predefines.wxi create mode 100644 SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj create mode 100644 SparkleShare/Windows/Installer/productVersion.wxi create mode 100644 scripts/bump-version.ps1 diff --git a/SparkleShare.sln b/SparkleShare.sln index 93553db29..9367d5400 100755 --- a/SparkleShare.sln +++ b/SparkleShare.sln @@ -13,71 +13,321 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SparkleShare", "SparkleShar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sparkles.Tests", "Sparkles\Tests\Sparkles.Tests.csproj", "{8AB2969A-951F-4146-A0DD-C46D7526AC20}" EndProject +Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "SparkleShare.Windows.Installer", "SparkleShare\Windows\Installer\SparkleShare.Windows.Installer.wixproj", "{4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}" + ProjectSection(ProjectDependencies) = postProject + {728483AA-E34B-4441-BF2C-C8BC2901E4E0} = {728483AA-E34B-4441-BF2C-C8BC2901E4E0} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 DebugMac|Any CPU = DebugMac|Any CPU + DebugMac|ARM64 = DebugMac|ARM64 + DebugMac|x64 = DebugMac|x64 + DebugMac|x86 = DebugMac|x86 DebugWindows|Any CPU = DebugWindows|Any CPU + DebugWindows|ARM64 = DebugWindows|ARM64 + DebugWindows|x64 = DebugWindows|x64 + DebugWindows|x86 = DebugWindows|x86 Release|Any CPU = Release|Any CPU + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 ReleaseDist|Any CPU = ReleaseDist|Any CPU + ReleaseDist|ARM64 = ReleaseDist|ARM64 + ReleaseDist|x64 = ReleaseDist|x64 + ReleaseDist|x86 = ReleaseDist|x86 ReleaseMac|Any CPU = ReleaseMac|Any CPU + ReleaseMac|ARM64 = ReleaseMac|ARM64 + ReleaseMac|x64 = ReleaseMac|x64 + ReleaseMac|x86 = ReleaseMac|x86 ReleaseWindows|Any CPU = ReleaseWindows|Any CPU + ReleaseWindows|ARM64 = ReleaseWindows|ARM64 + ReleaseWindows|x64 = ReleaseWindows|x64 + ReleaseWindows|x86 = ReleaseWindows|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|ARM64.Build.0 = Debug|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|x64.Build.0 = Debug|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|x86.ActiveCfg = Debug|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Debug|x86.Build.0 = Debug|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|Any CPU.Build.0 = DebugMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|ARM64.ActiveCfg = DebugMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|ARM64.Build.0 = DebugMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|x64.ActiveCfg = DebugMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|x64.Build.0 = DebugMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|x86.ActiveCfg = DebugMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugMac|x86.Build.0 = DebugMac|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|ARM64.ActiveCfg = DebugWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|ARM64.Build.0 = DebugWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|x64.ActiveCfg = DebugWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|x64.Build.0 = DebugWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|x86.ActiveCfg = DebugWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.DebugWindows|x86.Build.0 = DebugWindows|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|Any CPU.ActiveCfg = Release|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|Any CPU.Build.0 = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|ARM64.ActiveCfg = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|ARM64.Build.0 = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|x64.ActiveCfg = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|x64.Build.0 = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|x86.ActiveCfg = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.Release|x86.Build.0 = Release|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|ARM64.ActiveCfg = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|ARM64.Build.0 = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|x64.ActiveCfg = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|x64.Build.0 = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|x86.ActiveCfg = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseDist|x86.Build.0 = ReleaseWindows|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|Any CPU.Build.0 = ReleaseMac|Any CPU - {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU - {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|ARM64.ActiveCfg = ReleaseMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|ARM64.Build.0 = ReleaseMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|x64.ActiveCfg = ReleaseMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|x64.Build.0 = ReleaseMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|x86.ActiveCfg = ReleaseMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseMac|x86.Build.0 = ReleaseMac|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|ARM64.ActiveCfg = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|ARM64.Build.0 = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|x64.Build.0 = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|x86.ActiveCfg = ReleaseWindows|Any CPU + {2C914413-B31C-4362-93C7-1AE34F09112A}.ReleaseWindows|x86.Build.0 = ReleaseWindows|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|ARM64.Build.0 = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|x64.ActiveCfg = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|x64.Build.0 = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|x86.ActiveCfg = Debug|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Debug|x86.Build.0 = Debug|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|Any CPU.Build.0 = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|ARM64.ActiveCfg = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|ARM64.Build.0 = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|x64.ActiveCfg = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|x64.Build.0 = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|x86.ActiveCfg = DebugMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugMac|x86.Build.0 = DebugMac|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|ARM64.ActiveCfg = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|ARM64.Build.0 = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|x64.ActiveCfg = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|x64.Build.0 = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|x86.ActiveCfg = DebugWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.DebugWindows|x86.Build.0 = DebugWindows|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|Any CPU.Build.0 = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|ARM64.ActiveCfg = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|ARM64.Build.0 = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|x64.ActiveCfg = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|x64.Build.0 = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|x86.ActiveCfg = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.Release|x86.Build.0 = Release|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|ARM64.ActiveCfg = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|ARM64.Build.0 = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|x64.ActiveCfg = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|x64.Build.0 = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|x86.ActiveCfg = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseDist|x86.Build.0 = ReleaseWindows|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|Any CPU.Build.0 = ReleaseMac|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU - {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|ARM64.ActiveCfg = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|ARM64.Build.0 = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|x64.ActiveCfg = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|x64.Build.0 = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|x86.ActiveCfg = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseMac|x86.Build.0 = ReleaseMac|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|ARM64.ActiveCfg = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|ARM64.Build.0 = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|x64.Build.0 = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|x86.ActiveCfg = ReleaseWindows|Any CPU + {009FDCD7-1D57-4202-BB6D-8477D8C6B8EE}.ReleaseWindows|x86.Build.0 = ReleaseWindows|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|ARM64.Build.0 = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|x64.ActiveCfg = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|x64.Build.0 = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|x86.ActiveCfg = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|x86.Build.0 = Debug|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|ARM64.ActiveCfg = DebugMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|ARM64.Build.0 = DebugMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|x64.ActiveCfg = DebugMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|x64.Build.0 = DebugMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|x86.ActiveCfg = DebugMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugMac|x86.Build.0 = DebugMac|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|ARM64.ActiveCfg = DebugWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|ARM64.Build.0 = DebugWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|x64.ActiveCfg = DebugWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|x64.Build.0 = DebugWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|x86.ActiveCfg = DebugWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.DebugWindows|x86.Build.0 = DebugWindows|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|Any CPU.Build.0 = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|ARM64.ActiveCfg = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|ARM64.Build.0 = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|x64.ActiveCfg = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|x64.Build.0 = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|x86.ActiveCfg = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|x86.Build.0 = Release|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|ARM64.ActiveCfg = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|ARM64.Build.0 = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|x64.ActiveCfg = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|x64.Build.0 = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|x86.ActiveCfg = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseDist|x86.Build.0 = ReleaseWindows|Any CPU {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU - {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU - {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|ARM64.ActiveCfg = ReleaseMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|ARM64.Build.0 = ReleaseMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|x64.ActiveCfg = ReleaseMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|x64.Build.0 = ReleaseMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|x86.ActiveCfg = ReleaseMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseMac|x86.Build.0 = ReleaseMac|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|ARM64.ActiveCfg = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|ARM64.Build.0 = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|x64.Build.0 = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|x86.ActiveCfg = ReleaseWindows|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.ReleaseWindows|x86.Build.0 = ReleaseWindows|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|ARM64.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|x64.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|x64.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|x86.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Debug|x86.Build.0 = Debug|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|Any CPU.ActiveCfg = Debug|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|Any CPU.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|ARM64.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|ARM64.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|x64.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|x64.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|x86.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugMac|x86.Build.0 = Debug|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|Any CPU.ActiveCfg = Debug|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|ARM64.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|ARM64.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|x64.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|x64.Build.0 = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|x86.ActiveCfg = Debug|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.DebugWindows|x86.Build.0 = Debug|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|Any CPU.ActiveCfg = Release|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|Any CPU.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|ARM64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|ARM64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|x64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|x64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|x86.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.Release|x86.Build.0 = Release|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|Any CPU.ActiveCfg = Release|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|Any CPU.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|ARM64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|ARM64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|x64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|x64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|x86.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseDist|x86.Build.0 = Release|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|Any CPU.ActiveCfg = Release|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|ARM64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|ARM64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|x64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|x64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|x86.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseMac|x86.Build.0 = Release|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|ARM64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|ARM64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|x64.Build.0 = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|x86.ActiveCfg = Release|Any CPU + {8AB2969A-951F-4146-A0DD-C46D7526AC20}.ReleaseWindows|x86.Build.0 = Release|Any CPU + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|Any CPU.ActiveCfg = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|Any CPU.Build.0 = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|ARM64.Build.0 = Debug|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|x64.ActiveCfg = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|x64.Build.0 = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|x86.ActiveCfg = Debug|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Debug|x86.Build.0 = Debug|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|Any CPU.ActiveCfg = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|Any CPU.Build.0 = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|ARM64.ActiveCfg = Debug|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|ARM64.Build.0 = Debug|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|x64.ActiveCfg = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|x64.Build.0 = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|x86.ActiveCfg = Debug|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugMac|x86.Build.0 = Debug|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|Any CPU.ActiveCfg = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|Any CPU.Build.0 = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|ARM64.ActiveCfg = Debug|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|ARM64.Build.0 = Debug|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|x64.ActiveCfg = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|x64.Build.0 = Debug|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|x86.ActiveCfg = Debug|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.DebugWindows|x86.Build.0 = Debug|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|Any CPU.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|Any CPU.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|ARM64.ActiveCfg = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|ARM64.Build.0 = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|x64.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|x64.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|x86.ActiveCfg = Release|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.Release|x86.Build.0 = Release|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|Any CPU.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|Any CPU.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|ARM64.ActiveCfg = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|ARM64.Build.0 = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|x64.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|x64.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|x86.ActiveCfg = Release|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseDist|x86.Build.0 = Release|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|Any CPU.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|Any CPU.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|ARM64.ActiveCfg = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|ARM64.Build.0 = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|x64.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|x64.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|x86.ActiveCfg = Release|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseMac|x86.Build.0 = Release|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|Any CPU.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|Any CPU.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|ARM64.ActiveCfg = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|ARM64.Build.0 = Release|ARM64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|x64.ActiveCfg = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|x64.Build.0 = Release|x64 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|x86.ActiveCfg = Release|x86 + {4DF0FBCD-0059-4FE7-AD90-91FE1C4F6900}.ReleaseWindows|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SparkleShare/Common/Images/Left-Dialog.png b/SparkleShare/Common/Images/Left-Dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..a3c3c08da1563155dfa2f0c94ec70e27f3f75528 GIT binary patch literal 59383 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 F{s;RMs>lEU literal 0 HcmV?d00001 diff --git a/SparkleShare/Common/Images/Sources/Left-Dialog.xcf b/SparkleShare/Common/Images/Sources/Left-Dialog.xcf new file mode 100644 index 0000000000000000000000000000000000000000..66e52f16d634665573b962f79eff98167f8d0109 GIT binary patch literal 156106 zcmce<2Y438wLhHQeY?8AE%E=x>CR1@dsFUBxi`s;6E`rXNJyxmjvA8ay@;j)l2Grx z_ueHWArOde8!*NNu#IgDLKNvO5=i^~&g{Nw{+{Q%&y)Z2Iac1C-<~t4%D4o$qk>@%IN@&_8MTv%??9;r|xkPxE&`^oM^I;!lA;0e{cR z*F^UW{zma<6@aQ=@eL0R^$**G@^|Q_{ognEdvEghdueOXroflpcx%Zrx?{^ogF?Ln z{9g&!5adS~QP;e@ants-{x59|UmN82bKT2p0(`^QhHrT3W%l#Rh5#S$m;Rmo@7F)a zZJ+;_+rNH^{f};M+qll#Hz+LNrC%B4XSL?z82?sR&APUjb!|E8+G^Id4X!n!C>rqR ziEFidkLv%4e2t&){FtsmL%RNG*7fXe0@(Zyzu$a?U$Z}`E8!o$P5N=)A^%3oze(&@ z`ad%dN`iF%J6876t?!+bet-Fn^!xU(^cy1o?(&k#6=BlvDY66qe6UaYy%)oNIU5h@ z_xr)p@1Mq{-*@VzUynV~ZzTBwt27bL5Xo+hpKkWw9 zdNEjwYg<3n2JL!rtqRw6ew?qc!E(JACa^n7UvY!Y`l)rSpz*a{&#$uqk%h0tdgVGX zzyjCSzMA!#bz+bP*LJ?j^%m>I5G5+L_vL-~_15deP@a{PK6bb!T;pTCP6)Tcm4%PR zI#sy1MvH4}AI&;#xEQF!wVjXXtz2grE(Y_gBzSY{Y{SJ6x});84d=qdwKllcdRv7n z!z_JmeXP87;hHefUk%jOTNQ2@CI%>QZSO653E?(jlR>_$BzTG8_F-a>t&g3z+RHYK z4}~K^rSq~3Q-^ACW#y#}(}jwDN?hA|slu#6#Wg&;<~@Zl+fXsk&fCsQ=_%rxUn>S$ zqonb)4ppudgDr4v;b{@7U8}*BwWlW3a;@mAKt1-J%21oNQ~rRT@#I47*G`4_+ju?i zX}4Ag5d$o}zUOJZRuv)!X}$i%(_*bIMCtWIPxV^skVzjeuOEAgs}*bQLPXymc?zpJ zT=Bu8-}gP$t8GJ+!Qz@1J$0)sL$twS;0vBstF<9k!6Jm%u2zNE28-VIo_4D_4)T3sMD&J~~`$+^vJOfubL@^00Kb2(k{qk0(7>P34Jl8HCk2$BX;d}n zhP0=}7NXTPsE2f?xl>}Bik0|!1rsO3*t4605} zb~Va~s#Z6sJvnuvM}r!*b=q1rioyx8PYH?II$bSaqX42hAr9~|qO9S7=uU`@T1KI% z(V)Z+h^;DC*42pB3LvThu|tp%p_&7t9T2;zPKz2F9Dj1%F2GRO+by>YF!mqsQ{wspXy`;x+-NQ z2SC?9)z__I6q-s6ib9{*t7K(erC6Z^qVAiltC10*f&*gFCpKzOr>;^}q3zf83B6*A z0%eOzZG{SDRqtebHM?(BA)u`76+5+{wyh9Zi9aUxs8C)|p}{@XF|me6X<-Fdu0UCL zOsuDpqMTzTzDI0Qp{yy_;GVKaY!y(_m5XIclr=q59jqvn(YxSZyM#`95B#)CO}}S; zgy$N+=U?iw>a@KicE8wVOTWD@bS>z#y2M`;k3ZA(T&M1m@}hXszU#Z4s!Q67;*brl z_zvNcr`DZ&8wp(6Mofo_9I-YOW;>95LS$BN5U2IccusSaeSm0V{<1g5r7Y8*R zFSM)M7MvH)C~>80TX>$sT`M3{=enM6w{5dMFPsyPJ=4CR&FZ}BoOs->{kb;XdF?sz zq;>lXZRn@xt!)>e9h3BTu_CMEV z+iG`CI4ho5*tVe6`kd;lc*?fzxmJsF+Oy&rtG4g9s?S-UMf>B5Z&94HJBzk&d!bd; z!kt@)w~>HrZOgOZ|8A>g%fhqV8FBErR+|>Pvx+m~(85;x7VEQ`Gh&BL>$5EuXD!Z% z-IlE{w5ZS8oDqAqt>4AFQ*qY*j5weIs%+-YJ_CLM*P7<%!2jJAi{@v}@Izwzvn|%m z_GgqsV%LHeyJnj+nj!I+P0KUQmUu;p$1Pi)Z`Pc#84^!wTfW;YHYv~84~eH$EiW`H zn)ox%4vB3%uGLL142hjDH0zq49pX=mJnZw33O)%~v&M(kjhhQ{1pIR^h9kS7>5*pe{Ue)_Y{K2Kz?e5r2tgJ?5~BC#{6v(kR*QZ@;3R|*@&klNQ~rSh zl%?XoAcUIo@K?yi?;(yt+_lDBevU<6O5ElAC;fbV7ox275LYV$U<;2R+zx=Hhv-T5 zaSWl*&BjCYQp#lu7MgMHGl&`#b{-0M#LhB}f+1>m(T5Pu?}@LEk0--)?xL@9&6K|% z@5}kvA!M_57yWpqIw2I?*}1F0LM00C?A9 zMP&1eK&)K_KLOfP*%DIhTtzpvT-Ks&@5;H%AP8lFC|pE$qJjR@)fuLC z5k1ttQ$F4pJ9rY2&IR!}Q=bqkAl5FEp1!_Huo0pIV&@`ys^zkpmHAboml;toM7av_ zE@qJ${ig;`4jk|A>pjK-LJ9#jO@@KNlP4sU z;zDzrXEcs}Y%v!)w%&DyxoA~2q zutN$xb@g1G*et^!?cfosQjn>r6I%&pdUbYC&}vbyt{ZDV-fyn9!%mJJxmvM8|U zIy>4aDy5iFQ7d*am{jX%w~%3V?O4xD9qk$!*42u~_&$>?k435!h*IDw^ce_79qs0D zRadL55$godOO>~4<)BnkGgaTm_nOF^U}!_!Er*^{)eVA#18x(haiLmlq8k!@nozfa zaRZ^eIi{^sNHEZ5VH)Nu#Ck=yu}i9_c^X$%h>d(V*JY9=G)u9vu2NGWHY?010BWkg$s9M^R3H-XGMX!ClA>q(im497t0q}!qmsqU3oFEOr35xNH#PDy z$d@A=2Mjw2)*{+oSfMNztAtLj!$js2OkwrH3T^pRO{YRen;IJghU&_>GOwKYolhPGI&Q!%){uCBI*pN9D&u>m)z9=f4HH5Mh>Vr3B?n-;!#`i7F# zuPxRVi7hG_uC1wlh7AD>gXhkk89I&k_z8Oe>UP7aX@CX{x^}~vX}}WW1M7C~oXO0h z0flY5zT=#kWIzdILA$<7LgD}^Jff0)D(c)0X^5so#BQ|taSiAhtUh3;2B^c8_r-5KLgnT z!dy?+EXbM-Zp5BhkOd&i7O{60qz0o+3kLV*y88PRfb3hu0b$4>d9S~(4bU?!;z@qU zXx6Ilc?@*KQ^IMJta;xv(uimVum>PCi>-nueaGG1Y)#23P=qZxT<=f^0Fq zQZ*@OXsiHen#7h_5RJ_&nlS90p;7^|Zo+(&4B>+-0J3WmyOgJl<~RmbJ=26{z>E=v zJS;SMuKD?edI zFE(NjJpbW7j<`Yah`{+ zhOqf4^qPZSTjTHROWMvt*(h%=x_s#p8tQSrkEUszRyxc_-CSM1g+hgSsN{v&s2}I^ zXg-P&(p(e?F$*UhWN~Knoh6IEjiNE;qN`-L&!Di)Wj^ZR@h#NL({mn*f#v8 zH`G;EmX{P2jAo}L5DE>T?mTq28Lh4?gAsW|8@XRXn;L3m zG%MANLNOW5&y~>*jEERofg0wa^))a}LXT3tumLK_XkJcc3Pam5Ad=A1;zBdJOGe?{ z`DhdKIYTp&<5BM}8c~^181XHXnFjRmp?wl9`J7rUFFQRcPC_wekx>{Sqim4H+NY$@ zj7IE|Q24yejH2NQZDQV?gR(IewM1DlT8-)@S{WtNWE4%jlTh-mj7kl^vz3NGGMby6 zDWhzJMJRb!Lig|8DWj6lfl|X0im^{kHA8czQ8uz7lzc9udv-`@RR#0zY_z_v+KgtP zJD@}DU_&Yi&CSW0hmv=v(cRmLR#KOtDMt@S?FWi6nHkN_OqWp_sWG&$0H)1FNnHtz zKa38wovM|dAD|g&XktRC0)`f#)y(KN38g2+j2_yzo1r!ExrC;&PO^I&RUo4|nd$S< zLbRHUMo6^eT{B8e479d}`JB*6c8Qm(OCB^yZWDKQ-C$tI=wu~lA-C-+LAfsf&e3X1Hp?h|0VYH>itdkK6 z_cFAq5?#cM9+gn4mZ4A~Ej4)#T7*tE52XiCLQ|4t+A35ap_%D$*<2J4>omH1`xd-{ zX>f3?yQ7U>9@K)l?C~-xeW|6->QLn z%K39q^XGRqifcS)&DKJR~={axmlIghf^Uql|#&UFGa4=Y?7?AK`g#nqIP<5o>LIjl&#VX|3c;V*hpQjT*mu5xx< zzWY6ce#CN(wg05I8#o0QJ%-9F0{R~sM(&SjvFdWl&&L+YLc=O&CkN+>uT2l{^CQYN zcF1drzE-X(7ZZlH%M(5}K30zKfYpAYo7PoL^eb0vxuDmN+*gj+`Rn|UFLl*0*ukf3 z#P|U4)SUurtj`oz^R5ailS^9z_8(IF z+hZ0$^w!a>l`GZ_jT+P=TkaVjs79XNE%V;WM{|)J zZfkr+(i*b_4!NI^G(&GlcyvrmbaYgdt)Hc@=q@m09hdLFY#6-{?kl?j4)KS@7$Mrq z&&GGu1EWW0j49Wic&ta|#xGKRB4T5Y96rp)Ohw1&urhSiXZ0$K_#9m8zk$1yBTt8G zeYf#(Td^QYy*%9mdLJ6dm&UKMefB3Mf=m@pqHKJ%bl=0P z=4*(64)>e=xA93_q9S4PX#7z=eljjL*3wr8L0+rfQa(1p!Nzl&!V^+bl9Q5zL?MAc z%EfbWTC7|(VE(~<*M%`j(5Hv}52vPrOqDbVc@mitt3pf=7u1G%}`*;!e9CZEBjbBtjE`ZCL=;^Q=!6(e;Z;|F^~)AIAd#OLtYW+pCm zG$mOQg!YJ!<9(FgMyR&&&rgaM=&CGBY#a(lq9KGKf4c#;KTk zJ`H!|u2%1VKq%z$`8+O{1KU$#vgdUQ?WM~6Z(N=u51i}*sWfXly5p2-0x+6k>s zeJK&HJP7;Hb7>ySBdqU%fn5J}u77-4De>}P9){FBa7wJq$Yk`HrwQ@vqlGx~rdRb> ztRw61w+8MnD=#Z6Eh*uPA3|l)80~>ZgoF6BQCN!hqXs|fgU4=swOi6h#>Z%gCl@wv zE~?SMhbZBpAH1&gA0#u$E6@eNJjq zF7Bz{pQ*3C-xIvQ3PYf3$gL=6%+MKQB=Rz>&CJ-B43(3az0#A&$B8Q;^ZlKhO8MGP zh?PbOa8@aqOHPKTh%-GQf;A6w^3kJ_Jf3wfRxzUgJSi}nl86`%@m05pnYAzRvZf^` zr>E_NSRsl0WI_v}ZDV8kk*D`6{p0xtuAZyCPGg~}N>Un*&Mhc_$|w$4E{* zl0f!8jz1c*kN8JM20|lh`Nk_OhgnPPBpf5B!t502osvR|Q@2Vz5`h*z6wRK|2k2Tm z-M7ZYU)#67oHE6YeEl_y9BF(7!yz5c&PmA0IY|A38aX93Wm9re+!lz3{A=KkkK5?J zDj*w)ZQ{q!w~jfT*h`2VZi`FY!e>5VeS}YWvLPWU=qS&gz#*_7O-MY{(#jI!jGLra zR8V6Vqq&&Vw`^m=sf#5iAKA2h9r_79z}Mp96B3h>Q_Oj88h10@ne2S#b~M?Zm`r-p za4BLkpESP0fjT$iPd`fBscG#H5AET*+8SwOm=U{w=emvC_r;{5#WzV0Bnijc_)h_h zAMYJ#o&@gnj5bUwHN$o$y`ms3AvPhsps*l2DKgN{f5YDO^kf;R$DoU^_JZ=<(Jv3i zG1bAHnZ@RLh`q8nH!UfPFZrUl2#!nJuqJfFu6^shSNX2r6U)TMEZa&C$+dlZ7`{w3OdpACdeqX>()rX= zlItCQ_4bj3qt1SbMzIc;h7n05={O$R)j(n3S~trT_jPCu(mJ&6tZzO3N## z&qz-?dr6Sy^hun6-_c{~6Urxj1@^?iAN*yv)L_hNFqf5AR`FP`tm~QT?y|?C1XY># zm_?7GTdcBXOB7VNrRf%Hb!>eDUn-!i#mW=0K`q@c6&a%F!om}Vs7tvL4QSL|lWi3Y z(Eyn$%vS=`b%`BXwqRi#b1FG$Jq%NHVi5^LO?cu9a?{gKXh5p&oUATqFkiwIj~C`; zrL^2MDRD>FDb~{R1_8JcbI++2*ANKv><-_775=!VEEz*5&P~rb$D(0k!h-1*9=BAC zEiXN__YS^F{D`W{qEpi$w$t(ruGmzVmzmsn!-SYvVEZqGb}Z^H)#8?+Fh4u3(%%U;x;NyY5150%IAVtZQjl^0!5qHq`(ppzGyr@C%x!B7A@a6xMKaz9Xm8w zilJ$nY%M8KO7*8_4GxoRL&N>-bKp^l^`-U)>Y)6eQ0E%*Oy5vHb+Gg;Q1a_atKP-rc)s(V+&UrpEf}vhJJIVusTJUWeEqBa%NP9^?;jL<{+} zX=V(Ilj>_Lny%_ugl+nGuhZTmM-IpE(Nj?%Hyk_wtH_PJXhRE@@HLqlYAdVGvQT^M zPUea&adAW+6U|4BMjkqN5MJ0vUf)Ga7x`wc$<$a^T|V$7#oLcsT-MSTk47xuh~gvl zFp~tJ{+&83svz?EnzGJMs4p2Vto4d!V~<$=$WtaGQhx~aBmj-CZo!tGrg7-g!1~tB zn~^K`)B6=7AcJWIS4t-A*|l@$4h2>WK}16xEEqN+LK?kW=(rW}0GJRs@v*~TipF4W zmXz&S_Rg%RD)0ZCtT1-34oPI;C7*By(Iu>*5gZAUE!nYco4Q$#&v|`KMf*prJAQO| z!|GTmDn(F=XqI`6M3H*TVLP^O->Pip8;zJGt$**4;XYdV$ro`e58(4BeSE)4B-0=- zI+|2Gun!FbKkV4Hbqfzq81N0Osyr zFc9QEJrfw1$>+Z-h4A=V%B;}&aLj=yY8obX$Byl!@>0hzhvq9|Y~=mvKDR{rUgh(- zS6OU`Zx!r4dX$Fa(c2^O97w%d^3#U(%YNs{o|&O2KWQZx;FiV7)`r!YP(fvFuSvEvmG zLOprghAmsrx{&RN?H0koBO|?Dn@Y{mI3@v+cp-L3kB^4K4jm%t>_ONXx^u_&@O6^E zu)BTr8?HgYS2z3S@zoLBze(}DTtPNffX0eHa3pp+IyGKe`>2Qb?%1}`mwMNF*uHjc zaIn*Be|Jfw$jls?QAms?lh`3YV&6K}OC-;EZ`tay3GCnw!yc93;E=VxwOGrD@dS+r znE7zM#E!=iJtcG$9xCwwV`1Ylgr`}59H;c;UGORi9J^_ z(HnL!-RXhcwr%U1i#MRRuy&KU!^5SZnni|S&&|%th(EMzYuLI?J9bCHcR_glM(o|a zo8CQJHvh-U^k2w?-i}&T>gFF3we1bOx?ZZot z-p2G)zw-{%hxUFD9~uhob?fn{)>K!roF>x9hZCsnsIy0fc&`awx53}d!OcH(GhRen zw}kz}6TS-%^Y&z>heP;=4I7b^tt_X}47yHs7J4l`+_4)&_DcOOV((@@=YK5r*|<6M zHy-Hkp`jtmA)KiW?oFGK#)I!EYlxnMY!!9ey_+NHg>8N_?AR9Y`d^$~{<0pzLqnDZ zOWfOWs!ybkT|^;Zws@Qo0@W$RX$ zPA!*{gI>>GOuKjE>u3I2z;WBR(#vJT-+%c=aA?S@i`Rz9hJ$;{)@|FNJ4}~!PeQxz zjD~R1o%LDC?;Fu)!+-g^MZvH5FvCgp%@DqA+x8uEb&rpVJ{-ezXRjybclOMLhkL*H zhd;bZEiN0r6~cGy*vb4(Zwm-NN-swG(y?zg)1BT>8`iH63kwZ>=lg&3q84X{lj`8! zwcBdQ;#EE0+iw^xPTQOkyERSLz>IY@6$N#d^`pb;(+f^vM`n|4;~3J?mBnReALxhg z4J%GR3yFM_9V4zPE$siw_~0Ht%$mg~mH>ftBvNA5^Wu{>onhz*U{(=lFKSyFuwVe9btt*p&)c!o5m77U6f$Wf-c zs?yTIyT%dKu<=g)rYJUUQl5Ga>M0~~c!U%Dspqjk5<4%LRCu82 zssU2RJ~*-?MIJS#Kacg2Q=O9R(yH^1AY1==!R7=u`Gb*N#xsNJlVd#?`Bs;goVa5o zJD=Wd-4cU&Mtq1lOi|in->&{dFZ^EIb;J1Zo@)5%nTUN^e2$o{&Jwe226ZR76Q+S< zm8Fen7U=g;;`TI}05oLNd>sCAvMmNJPx2>>$9p@f&`S3q#Q0_D=0uv<$Q5$<+$TA? z_@8Fb8dVvO_jR?M{t}G_Rql0di{T5nyvGz9fSMTauX50a3GM4?J&vh4^|0yUfrwn1 z$y4Twc|yLBC+6~lwxBT#^dIZ!zAUw?>C?1rnfPiKDhkAWzF;c9K#@O{mv0B%OvigW zJI)Oo;D6Jds?CY?ozoPm3WR*B#Wc{@-FE78a=)o>XG{rw231AILexd`X@vsnBi&5{ z{XMPy*A0)J_GM8n-54cA#8QrYN0Y8pyQTliUDY3kA_X&^)G7dt%CIs!>@k zlwr6GGNEV^(p6|jYzpc-*3oy3ddl5v@q3GDnyj({pSE(mVMz^aSm42;C=#FmKVa-X z*4h2;Lp)k&wA)2nb7(%XN>xeW0AKdB6q8hZiJ_QgL-B9XNd;UEW!le3oyl;wZCet) zzF<;R@s-#n&6L7uyci?GsiI;eRdswVb|hev3@?k}FFlJ}Dvag{PJXD+twa`*nLfqTkeQ|_!JkwX@ zTXSf?1Yb8*TZg^vHBXoeh?9qFpOzHw%@#^bq|+I^I?&zk^=wZR8YgI4u?}opEmy;1 z>p58?!kQ^^*=R{y zJXoF}(xI8xsmeIMVuCd)<}W5U{pSNbn#Xr1wqRESn`o4j6Y584s+QUm{UE-KhZraa zCt_QT4~)MObEtvGNfDD^qVWlK{oox(0TFn3?4F-0j1^#!2!_eBoH7M=<0ys=pXKc= zM{*PE$^>a=35_wx>65k9%n^L0DT-#LG0iEIKdmS~PzY~U@_73+ZO!4kxK0u}LYhj7 zR@TwvUNLw{s6Ga~l@`k3E%GM`czB1t(wsq^>qIFI+&+=PIAIQ#nSA#h(zdOoGAkt^y%-HznIGrw=o)s= zUs2srg+7ERGbosTtE=*>n6fRcZS9@J4ShR1T53vj(~BFKgBt2fxA=$dh>YE``W@HM z13A7nzow>Yw`6Z?dq-z?&oPot;#td5pJ-@o$UIz*$8rMfU=XS&5|;kL$@2}qhOfP{ zw}u(r*3sDo-afYIf^}wEHArn+cDMlDksg9tp@yrz3Jq#@|KP`qp-gRtZ(3s$v%0Gr z%>4t{EyzT-p))rjSbCh4LYX-&~ItV)phN>+3&$;v_9vm2{Tm zCRNZoNC}q9kX-$n=U%IRH6J`?EBlWRoE$vGdK9&-q-%K<9y`426r?J>yK1?*TiFX2 z{J@p8go&i(@qrU3Pn{l;jcmb#K8ZIMkIu+ApTMq~8dWV{cXy}7f_Q4uuI?T(?!?K# z(?e&@*Wih#PODbg z0_L5i8@%V(Y7ZAD$CZZ*{T-ZFS-9A*QaX$7jAympW96~~R}2%MuXk|P;Eu)_dyc&) zv94NJJ?0TI_}DNqY&f@cF?JerxcS(FCRIj89*)MYaCX5>Yv(CXoNPq* zCOsbASByLv``FRJ*=CiU^OTdPhTXubgOQ2*#^R+boh^V1PFlb`_N%)S{`f2J6;2x9 zN+;2o?qDf`2NWH7V(Na!(OH2z8YgiT$>4G4f&0kFnN3$7jyxE7_+Z;IvfA0wNpw+o zDcF6J`zO&}J99@z`G-#omlm&ZvUav{nsoK@QnT9+-90yB0_Oba=);GP^a<}cS~%M~ ziLP3??gKOpcl6=dqel<*HyxL+R69T8gtZiO*L}q8&_PT|?C%-F?Z>9dw;h}W=jWY7 zcihH7MN@8=f8-A6qrcIQJ$^KH*LT^<7oA`_tC@Gd?{&@(mmH0%uxzaljjWve(Z@c{p}Si7dip3h*=;oP`X}R!i&m_(a?-AZ)0qg~?XD}$BGSZTO!VQe zjRu3>a6e#)gU-ovrRXB8R?#_4Zs?{*xWhsW7p<>0fyOvK^u}^WrIYQ-DOa2}KqN-j zBXUJFt$JNBnhg5=Z#%4Xa;yd5eI!-kjO+;BJ>`M$}*UYMuX}4qNNUYD+Nc< zP3M7A2;6wAxQRUix~aoZeJtvX$z(K{#9Fui#hOI8Lqf&@n25%ibuMN$%pKC-y}}m6=S!_|x;REnQ*jIOVX?0#ux<(PdTK zhJ)l)K4v_6AI+P7AdF8K4UumybFgq!IEYRb?vrk=uDr|RRdKOfB3J`NNAJY^@>H6E zALmR@Z!CIyg~n0sfaeB&<3VZy>5N~*Wb7jTRTj-V#yQ9zXwVv>b>r8%pvzpJNLyLiH$pYTQE%+M>oH@bcKZjw?cHa z0I9Hwiyb*~c%$oLPp>t*qlpd~+$vBS#;?Bi_HvDbVuhs}?;@;v5_@R1=WgLJ7c+MF z2oWLIdevm&jZaLeuP!XF-`8l~iB;TWjLOkVLBmMP<)%7A%h|6wRe^aD-_E`Cse*laQ^zgUS%pIMutf_M3#UoF6-3uhH?G) zjYV%QU8Y_xI&1KyVDyncTTM<&Tsk}BSXx>0v0#|ckC*;!@luelQer_Ck$)1m^Q9f| zgj?`TUbVQiqPni*F+KLCyRNS2IvSllI7oEg|WOaVV50>p;lUK$#5aBhqf#vwg(`PR|VoWzT zp;eYGbF>17=)|wQiX3xc@{fMwURGWK87vNMBU4VCI&=Qw%`pfX{if`1Z!CFd=}N0r zqB9Sk&nPRs{^xHnLx=+b6l< z@nlFXaWqTzgXO}z?_aqw`uO4Sm5#stbtEbME51@4tWf{flQVp3G=y{1sGNZVmhK+&X>$ zGnW6Bf_+kof;3ov>dd(dm)?Ev(uLFgotLU|TVsCx>LR?toCRX2t|@u>ZxrHF#Eh7i zdF$NyigAk7_cS*QH>z&lQQqw5wlNq$MCnv9kdKwit+nk{#Q(@moi??D(1mh4+$^vh!^7wRuyyRhqgnSyb6}yarrc}kFJa;Ng@G_{|@|D;$cX06$*_m)!z#FctuV{ArVKIn9qcNFt6PO13ks}rgI2z_2qr|+5R4JuE8l_;eKPoLp0ZMhwR1wBppV6W$ z2rs3D4V3HHvUw|g=#X_!j?c^mp*CkKyI50mi`*wsA~OKlG{2rJ#g*?KNY2VpgHp)G zlT{;B-;-%eB$Ggk+}RbuIAPEi9!}2@K&sBh(g++f^BIw1Y73<1S~skZl48so`x7&D zIXX~kt8c+@=vh+2)Z9whIHZL7D0noEHy%pOQh|`q!jp%|l$$bfF-ui$*@Wa3h_^mq zvF6v2aT(b>sFhiYYGu_OqQ%4-XeFbNu|T$^0x{>r#EJdMSy@_43QuNNS8J*sB3}g^ zii@G@md%?tZQ$2EK|(I<7H=9injR*`q-CkHby;GL2D2ky!CQYg$<_+PW_(g9kC6frqD|ca)C^6g zGD9p8a4h6wqNmwlBzD)W`yYPoy$}~vzh+H9V4)P2p4lCrmLX(nGp0&$Y~<~7S_KGt zYL>92u4{wWyyfEIwI(nyDCARxX<{_(P*O&w4r`CaGBxJmZliw^eF0@KJ>L53+ix%R z3Y}!f3)*RO1e5jk*>rH@pU2xb;0_Ox8L@K3&~Uvv)tbhdJG08)e#A4 ziVRh{n91X;$R0=sQ_?fE>0*`^Gl`F-tT8x3 zL)Ql5T)DfX43Q&G)yg8)C;fZkQ#G`FI$MPq?h%FozGN>{@(h;Jmm4>3E|mh<%R3JzYtzI$U74QxCS&-I zHA2vBPzO4MOlR%3?fh5@VGH-hr)bi|d^KikZ^|5bul%0*bFJSBbE*xAx*dBmr7-rh z?U9M8>NK%XQKpi3XqkZj_unF?uKn}uTrBgTGpq6UT+w!+!TWqj;SdbYNQE1 zme_K0;0E7+`NQuiDZ1S&aOW;?NwU(jYe!j_`^ARcaY=lNn58ID7oP_o63)3flnh<; zlfQ4>vTQL%cHr8x_W+ZYnOk^S3Uyny96Xwwl9E$mQG9`V5wTI~c-_Ce>h<)?W{y?Z0}LE6!zG)OD0 zILjj5yEWna7RCzhQzgh{~;zWC%>qys_qU&z4u!EcE%^4k)Vm|UnU;tDUa zRV$E3$=#p7Dj5c@0|yTsj!Q_*$jL7*!;q%wQ;L31xNnR}NYoT5WHu}u*}h}vmY2L` z4Z##i($cc>ib^1@p|SJJTg}xcmu-wrNKzn&cAl1-)0%#mu_9RYG!tR zad}mBEfxqIKbhIs?zlNRA*HBDi@@+ATlWk`*!AaEB)dph6gn4-%PTA?#}~e|AopB# zUR#RW_QMIO8f5lvNo-k}yZ3DW@qbEYMMg$pm>v&ta3x>*s3FK7a`nsw5hMzW0wc(`W z5i{O^-GLPqa9_iZ8mAiC)iZaH;Vo?Gt|+T*_luly@ z@pV7Crx<=R`f+Y~on51CgLqtxB&T$!O}A=z;-0Z5zq-M)abbfJS-x3!#_v7)vaqzS zLDTqb1Cl)_lruLq!-#HkD(f2*jW0Ba&6$LOQxV~2wNhTbYgV$!NZ59zU+#6s|M?Ou}6dS zsFXnHN>L<_rGGQ__~FCH-{cn6)N2~->v2F34wcqRu0bWKc*#w+kB#z=CQRqj%4+Ks z4bRl$5UK&iaf6vC6>r_q8)Ku7ACEpNhq4U~&)0JU>gf{`yA>(hhSg)EhA&bJ ztLtp*RXBlh7D2WGDR@3>@}!<7P{)s_mDO0`EJ3kV7DNdIZT491T?&2m!#TNCb=rE% zIFM9>oSTAy8=I)e zLRzkVK^;!RIO4iR_`U)YVJ)d; zH4Bhs=+*W=FiXlqFrP=yv5+@e^4KI8pL~^+TUl#^e64P#4pqK^h(J0>b7~V4gNY^8 zwH6pph;_RDQFCSa1sDLS@}K7B7k)#to=+ZRWmVK_Fq{w@b^QiLhOAaX7LP2xvfycY zK|wnLtMSWmh1Io+I*VGImEQlgbZBNrN8#Qyr19~_#sYurt|+)6m=KGUqo35-)Z)nY zX}KURicG=?Igu}%EUGiGu=i$cZbc2~+Eh4C{H~l6>%eC!G9K##iVBmmii-GR@jZp< z$#^@g0cnTOuj+d|O^OuuJ6$dDL(DXKxI5w zjPW*B>7V8i&>6Z7SRfN5cI$8`rj(fLP!vB5-CbOqwKcn_P1;McL&8!^ zN|5fxLSkS$>sE6IW2D;!v$Qq1W3#fQQh4r?4WaxJpwawpsLEH8cG6k2GslS6{dn z(>HR)vVUOkJv@ziz3FCjW_h&&IbE^CqW7kmr|5^?G|yGPLY^ODNxYLoXTMbF$HzMl z7eHfWwb*6Rds*gat4gtt!h}-5MwY!}smT+Vx4ZEED6|?g-HlHvs;mM}k45hd;-PGI zZJo#eDQ>R#VKb7jEGdZuBWA}gz4wJMhOqVA{;V>vSoXdz9f{iBcC?}Hr!U7XTg@`3 z{VX5J=&xLR$lha*vSJG=t8k8Lt*)0l_9;sWLyq&mdhc9lt*e@qizF#mKKSr+#n{C7 z^@!AxN-)){dlkn%HZwKX_`Uedg6~lt63J4Qhm?3e{&Zwi7@K%pdnmUIiEI&vxoVER zXJ*6vSpKsMUIx>^$;|ShGiNcYMv^}I^ycl+(XmlXmhL@TRLNIQHT7B?yI^LcWbj6- z-yHATw=Jc6=qydG$y~R-xKC_jkJ=AplvY(&wOGz@QFgV>>c7)=96L=sG=F!QiTdpG zFTZBeMsFOB$w&8XwLJEbSr(bpVDW4KQmbdrUx1+ZNxx5S64RX#Vbu8e>#E&prIl6f zmdCC$rna`$UKn)1>My6sAY!?E<=XX+KfU?c7vQ-&{Fom#KKQ0_LwsRHWhYaRQp;_v z$ovjE*_JSSpxc$JvzhKcc>L(W-S^tUqVvnEx-HDE2OIhO7k`357vF_)S3md=qHcZm z#aCZ{b9a~@(LWfud$XcH@K9cPrS_Q6^Qn}4Z)+VqHPmMrC#iS!gKHmt1g_7&`0~y- z_rNuF|C_IGHk9{!fUR0}Ox1JK%m$m1txjHg7dBnJ22r2fxb^vM;^OZ=9=`kK&1=U~ z>RNoDWG#=qKo`wyFv{^o$*2#nfAqQ*5?+xD{{^rXYSI@s!p4FTd99M`) zVTS4ag%b;ZOD0{r{t*P-y!FMG#B*=>?pHUjp6UPQSav~M$j0QNibg9Ouq$QuTRCJK z>69zlw>@)m6+8RN^#O9+b5KeZ&PU(y_%^zD(R_6I(xpr0p^kKJ+AQ=sHkae1u34x% z&J2VKb5Tmb-A zfT1{w?OW(FI-qS13KuM&hoZ^npx0z!vr*DOQeg&t{dFEmp9idMWOVVOx8|eEmcBC& zg|^G)p^_J7q1R^YokmFmsUw(ccvLBbxL4fm7s$d=pd*`Cp(d)>D8B{WN z9{LX+X*6!-qgk|XzVZ5ebjjO`zlEZg&qJB8IjG~u4w7YN6z1ZA#94B4X`un<_-F=Q z^ycf(VGc^|J{MiKbm=?uQ0fYE&|z~|n1v#3G$MD9Y4o*!2y?+jZ@u~2TyP0G^jr|! z^Ferf4k$YCE4XFcQo}Um-?8vuF8Ic4|CkTH_2yhqT*ALSF&lh>9)ROIXFwx$crpi| zNuqRgIlWmxf#VueFXhC&4y-Mk@OLM0Ul z-M4!?Lovfg=g$!>Hnm77RlrcDf{cbqC>|d(iU!(1DD#bkM&cPHlx7^+bB!L2U3K$O zrWivvt!K2dZ;1B5-d)=XW!to-6_ZhR1_h~0zKJ>{p`j8g`v&Nqo!d68Cp06&?6Js0 z`z4eeBS9z{4cnh2bghI+jV7UJctX?hT(hQx5&KwoSj%>INGLY)%tx7GG8!(?nmtA+ zb%(XId5=BU)a-}MD0Z4kD0x9bH-v>qD4uH>rDh8Y!HIyhIJIr=nO5xZ)H!GljtQKL z;!sKKB%gz_BRl7!&C>CnvuW|4j`;rp#nH0T5wJ7T@R6B^R$@EZJk)$_=qy?|XFf_} zuK&AeY69syyIveQfW!FaqO^H#E{cO;=@h%!DBL?ArPas(6uRfzHN@ftHV4he;j}nB za5h>>4L=X1^DgJ2?0m_&D4h&TXHm{UVZ=Xy;xN8F->QKQ)18gd?m+2?-5HcldYg;Z z42qZN{2HW_R%fHwN|`wi#i^>_Lh01WxhM|aWoJ>&LP_0!294PL zts3I!3qBXkro;H=pm+vxvg;gFdNSvr4eW5)IkZss+b9|FzeIPjfb~@n5oL$A|iX9h|h7IYdDJcS&xx^1?wP?!E|I=4KE2fy{d)aIy zM6+OyB};M2PSyn5^^b$Bc%)hQqz{r)Xw~M{AOAIimh;h4JI079U0MRc637&mNQbB4 zl$q;E2iLE7<4yPQDAHIy*XJMqz9@_FPOlzpyT_Z-3$jKr7GwMwxnx`zgzP1D~>zZ znLdTVKl;zNC8g!Hd#x<+W3J}2p5iN-E`XIwnMwxt3241}Q$}jwpML!7*H;Ea6_xz{ zdoR4n78lCv__E+DW-{7|wG`B5zD_3otFU=#dNxES1ibdspZ?n)-(L2;XVlBs`bTM5 zUzps?bhgg8)Xs1>hvRkLi`}++@2*|1EjBk(Nz|%We){jf|JN6v{k_Zf>?XD-u*-=w zm$IB09IRF_UM}uEw!C)n>~^X;wDc!0|Hp4%e$hs4`HPh$nEm~$#7cUT$N1E#6n3)D zr4$_K16OPPfSxn?)$0empHx%tdNO0)M_TNMOrx7*+moG z*d=xFFF*UQKYrgvfnBiaQ*?S2b5`mXX&G5q$csah%8*1LPKmFptEUGWqW)j4@so?KoDq2R*?9L&c<(1{Z#PpVGwr-h;MGiSYj{8v)-36^)z zu}im8VJKBJ!R`rtose3r+57+HO8{LIo<=%qp4`5g9eDM%uU_FV3-6oWd-vjn3+K<((b-g7%1!DABp05ktj5yij_zZ$K$up@ zvYFWH>W@aQe|YWM2OnI$@;-Pkf|tBXtknDR3b6VB3OBcP9izp_7cRYjg=UvO`Sj*Z z;TC^$gZzv1o-ef`y!UwnS+#wT(G7cW4xWHIw}T?1Bw_n#a(f9diyq#-`XEH&oQ zhwtAPM!5S!!}soea|acCcJq^ut}_KL(%X>^;3|Z_$k$!H1E(PS>UB(~fBE${_x>N& z-a0y}>kAuZ?)bz5lzQ<(@!|@#N893#0UZ6LE{PDmX_jBXra)e#pCYD zeV=`2($fCk@2~G&t6huiyZ1T!$h~v6J*Tz~F>t1l*Tgj5M8LeJ`u$syfAZ(U2bjM* zv@Ph>!M;Eydd1m`KVG?kuK#@Y*K33d>gpSrW(IlVyq2iurbd#ldiRFwp2UAiJ3y)F z*<7YPhW*`-S8sAXzj|9$Q{RaE7OE9M2##tI5by6C`daGz`O`li-b=Op`{gMrqY1lq>mHPT z`L?RIz8OV39mt^TWV!^Mm%2J3PI;^KHC1n4|Ml$8{6)GG6kGH7siLyV^WR^-cKhD% zkDtGKS6$cG(%#Y8-9`D4T*6zIu=74tN18xg&HJ}6E7$8!895w3LU{MMt*PvnWNRB+ zk)hbv-XU&h@ef}2FDOGH$=d46J58pI89ww&L|%__sr48sr!VAc>YG~IJE_NZrh~E& znf9KJ&aQ5z>z9u9*5=0Bz815_jTrXj=bs+KH<42L&tZvf-F@)qGo^3`mAjy5s7uh@ z*~R_4lizixv!m^PnEjlIBZqzU#it(+zN0JyxOnNxjl1{%c>3}!21^}703+_{?PdCS zy+8MG4S?F!^(0}T;kToQ5B=h^Pd*x?)lz<{xU}-@#UCzTzw_Ipr+>Yv;=~o*b@%r5 z30PiVcW*ZcOvl}4X)8@#b*x=SDkZ@IXy9bLVBs%%y5v0Y1yr;Wm* z(D1Z1)MToTFY1`ixA!jma9Oc7*n|yD(6S3^cJ}nCu+<&sJl9&z983CrGGwr}rn-uB z>Sd;m*ZHQa<;Ned-2CM}%?#}#8l0jpDO=rpGkCMZ+zHB+7^tPLD*G}+Q1=>>-2Lq5 z>)3pM(ne@#ZpCyc#H4J^ll?A>46)mk3jhtZTto_?&fD~sDHegcA`Lsbd&O*ZP0sEW z=F>lzc@-7Qr=q&rhK{c8?w;z~^kTn!SB+P*4O_Im7g9pDx~{YxU+zSOkK1Oyuc$<^ngfE72Q$I)xN`9IM%-))&&OdiI zbdqSxgFm1siE`UqF+7H?{v*M4iSYVUHmpQd)}Gat(^Lfm+IbQ!Y!X%Z@1nW~_3e%2vxkqLzTiz4Np?Q@ zM-nP?Z}bJPO?J8y)Pw^%d5l1+GPX=q_iJrS`;CP|znQ9QV4l(01;L*8&lLJ{GfPwZ zEY^8}!IV+MzmW0=bnzI5FY^%9HB>itKJ^{?_2k+5a#I_NKf3rmE!_ypVx-*iGhNj! zWjmM1r+>|DXKnESND2&I3y~XD-%)jB?8u2T^^8o-%pENJTe|r@&l+C5rVkJ{0p9+n z;cM{PE4TC@Ep;(}*pH&RYqf37=M9IApEk$P#LV2nVxF!1DwegUyZaSBxU~&UP`RyW zi`|@wijB?fQPt5WMRhGT&Fyzre>L`79Rs65V^I_wr%`u=Z#SrXtv@_k`(b2_pzN>ou?*3-I$)qU0Yg~ULvl{B_ z`RK5@_CN*c~bvsxY--i^hqw3WOi>ad(Zxc<6#t=6YR@5`x0AI(A z{$`pk)HE|wffRZ(+0%rdkB;gGv%VQcpD}tZv^2FGUnqo{>)RU&XN;brYiuSrHItb$ zW<1mccG_F%e~&LY)84d_UUu%wqNy?c64St|=Rv>LwyO>!CeAW6v$8SAxI|`r$1Vq3 z(?wMz()M5!zC|<>H$TQm8y4hXcqAB7&YZ2mw39=d3*ZEvcW zGitJqkvS@;P)KH@0d}54x3xhSDsfxeTcWhJzCGWh07u0TU>Wd_6!2_R3<0z=!dTm}gK_3< z|9QvuVN>n?0Z-!cX?^|RN+rK-f9D)v7>9QT)GR8xpqc~5*`KGu<%};LrnCB=jAliS ztP=Z)9IOPkM%@D=5#8_EveZ;>uGPYg08mmOKkeEzZM=~y@^XJ?*VxcpVoRO})$N8(NCT{)GimX}qjLFjv=WM7FrVuqF z9Pkur5A>#OQ~^9ix&uy+>Nqg*9AuaC_CVuHflO(}|FR;B)-|So;uVm3G;%6!*^NR5lxqMeOy6}EG~Z@7 zW7vrC-|FdoI!LU``1=ny)Srdxh|*&6SSz6mF?Q4-QQ?H^j!jEetX#fq`rNf$w5XQa-s?)Xu+}7?rd-iVJtPtkL8_a}U3=IqQH2-S+^l?K! zk}^~5goOA7gipdi#HORpY3{J%yLY?o_3+%{)k7@&mBKME?G>6YQW=}Y1p~%x zxqEv1`UM1shDSzIxw68VPXQRe!lS~2PoplWHx&2rBjK3%{VA|mVZgzb2NVYfYeQ}^ z0-HnBjpB(Zi4zo>yShO2*E_LLp5A`Q=#QfG^@E49a*^#_NEj)Cig!wfCB-v1-!H@i zQ2Vrt@A_hg3%5*|=O7eSkm!+|d^+000|18u|GS?7u!5w!3II_97!y$vE_|!_FmZ@% zm@+Tl0H__eKLx@CCt$~*!WY&&bA@-6cjaeXCk%8ax}ynhe7J3?0V;UnApB^?Y04t@ z#U~#)oLz9Txcm%kY|cEt!~cbM=jU5DuV4H5r%M;loh~arc`Tokr_HU{p6*^g{#3@0 zn07EL|3op_jIcP7k%dGl{==%@$uR%r_KmAQ{czz-MF}u&A)n6dc#5wXx^{==!KC?t z!#T$a%g(~izH#T?eaioODtLDJ=@YVSf4zI_`p-XJJWJxZ!YzY(-i6o7!yCgUz0))E zPLxz$xP(LZZ)7KvmCb(>{QA|azn(vPj0S$WdF4mIhe7?ktizaaZj+*nB_w=bLdwCc zf>UK@zrRe@0W95DZ(y9)05Yzoy6XMgH?Lq_Ke~VS)>Sl6UUZ@$CzHOycr&orArUbN zDH+)Xh2`fj!B|xsr*XJ#YHZ>+UWJ7U`x++dlSdCo{^FUk!sGde=z~CS2u*oHYDV_4 zqRI|8o8E_h(~lbtb#HdtkF+=+W^IKU!3I z@$wDWVSl}?ZEgib1a~$j6S51YTN{k@`kMD|{(AE0*V|V!m(2Zk>}YpS3_TE~Jec>a zqlFb0ApY>lU+?N#+8Kq#+Y7^m>A2Gl{gF@i;_-vBEe6xaeLZqGz9v|1l!YXvAIU#e zegS$veDd;LT{EZc-zM)h7%?!;S{m!B-@UxF*L3E@F*q20;qQajD-=5|^>E(FvU8WN z|MI({TQZjwCht#5lNSf|wwA`)TfsIuQ^t(OneDTW&)_477mS{Iv(*)lF`X=j> zvw5#^hAZ@WdSIpT^zmPh8V(~0Mz2Qu?GWxAIh1>XG{5`s>8q-SR#ZIx$L9SjbFZ<5WM7$}ThbrtR1 z9i1;xl5~KZsvmFMd-Uvebpx!4e{9~9dzTta`{rw$P022Y9ja>moNV3-+Equ3PJegh z_5<1#O>H08ycc{oTFjV$<-mCxCuf*=lP)Q2Ufd&cSTVSJ51+lRs%s{v3E9_V^WF;E z?l^a{awT9>s;PXQs5PAVcjcOz3(&Tm|P}nuDAF4neQNs-BtyKQBlhoy}2IiET*fI(JLJUT~+n&b4G8| zSu6c{Ys24hMsM3W5~N=1nrK?lZO3g=t6PbZ#c)V)3sUY>>g68 zuz4SFa?dbB@PLtD=6QnaLW8N_d_7dg*}QzdY$t5qn#RuOp|dTPY;p4qi1U96oA+_| zkC$)Wd-(JvTskD#QsK7Lc*Z#H?Hw%PY~DGq6gF>luAcFN4ZFN>Oh^tt(hA{cFY!6R z8%-a<){du9>+s%lTbkZJO{w6^@5Oc3+gmO=>N%|5;o%pAlCQKl-@6Kv_xiOv_y2tU z=6!8rLy@zM?quw1cka=xw3K#KDu%D#}#wF#kA6gjdNHHPv?wY z*jOD8J@o9CZE?eoGWE!W@DP7Xj&^^fFnq5k%BPK^SBkz%=y1q(g$>&X%l>Vufz9e2 zJH1F!5GsUZM`}t!U=qI0Fn({&nn;S$+W|XU+xnT(Vy^FKD7P|S=Ik9ps`5i6Axv6F zYErb@wXSxuWEbMCSGdRqYE8NDkMVnBwb6pj?!i%UAt6kt4A}Mjw64_nK;Ih9{Jl4( z-=ZHHuP|mg^Vff_{kq+OsF~a!0U3TUFO8R4m68;-r&M79Z&Db|A8)Gt+XAjSY+$w0 z)h`@py2Slak`Pg_AhjkrK4|BQPR;@ziQx=1d#JE!n_vN-vo~F`%_|hul!+)9jS+-M z0l_aw?Mz9Ia!Xg3!9jxuPO9iPgInA0ZZVv{X>Sn9R1*dJdodtsFh7W&%1fzFiVNC& zPhkh^M|@xhN9x6`G>uQ*>HOZ4+jId$rAfa9kl zZT*WBP$d^ihG^^mfYN|}Di^nNo?P zq9#L8!cfjF3BRxxRc#Q(!Z3yWqCW|S(5{>UZYTGe3{lniRNq=EW>#=D7K$PUZ(0=* zbnQY{=$Mb*9_8)ZpPZOrGM5&G1K2yf@=H|>ZFQ+=_U5=V_qjTTC(9uyDC__rApR@l z`nz9z>}VN5>^a6Oi*yjJyLLU@agdsprkYg1Q=Pj0$0tvp&A}iAg*_xHf||VI-?=dF z*>jt@(iX({6!UrKdW623rk1))#P9^}&mX8O#T07t3hxzT`@nud)b#v?tgK6y2np~S z%?lN)ApG{@=0Q+fMa=N*{h1;)ECR3iH;T|w@Ddv?57>dqwWD>v|A0y-{@46pc?5@-he z8+7v>3>~VW!RDee5EPG4ea4fgsL-H5-Rtx(G_*Cv!ot2HOa?Z5=`R@4nX~jlP;C(2 z1}H~z5c~59=I{B77cU|A=Iz_}kC&?r)}T#Z#B##7snCm%5uqujk?RwpToDPXNYLS5 zFJHcf=sU#eP)_2kG6*QI=#?qCsTd@}tUxOwz@EFmDwi4oMd}beqRV&hsz?w}HMI-X zF;o(L4Dd%zVGNWcp)HDWs00Ou&MSq~d#0)bCJW@qkJLv?YHimIHNWc##nQ z3`vQeB3O%WYrG}~H)xj^>z(Bh1RuLcFJ|W37=eRiURYlbmie)%KL@-IJ7B|%J z>)8eY_qD~pIVrrnw3gT|h|^M4lcLy2QD6i88XEYG{3d>Luks7Zt0O(1Aa+d~?K{X_ z#UL>|oF#{dCpxMFv|$rUm4q!kL?3z0ye3{F4=&3`b=W=JPM`%<;4R%neG}9~thB1S zwjOdQr(#+KZP&QyBMM%rJ{M?+_{l6$t|#6@Y&!@sQ~hck$qD=CuPg4B<|63Q=x$DAW=nIE;8bkdR%Yva| ziV)m_IIPs356n$!uD<~?%381PwA39p20tFkeXE!$WiXfmvlM&c0<28ut*)NF9!2M! z9l!e9&7Fi}2VU=M+>Y;;#L{q4HCUGzVSj|R8^@|144&8XYj@Yf2q$BO&2g2!DM&N; zK$sgd>83O{VHwfC$+BR2pY}YCUt%_sJ9&Qo$#eYLDTFEVM4=ok62FT({Qr-&*#+%i zq^`2o8P6S+aWZSUi}~W-0PMy7KoFLf>#pD0{5!(>*P)FL`egrcwu`oA+xriNxlkf) zjsLYav$idib2cEExCr&Bw6{M-uXuZ2D?&)H;JSLq82`}P%ynI6F^fC3DlNy40^2(p zACeeFp5Vhk7!v1lnjJ-i^>*z^_tmy@U^qfWoOEH9sV{j^1Ec*7Cq!{F3e2#%O%bCN&vjjDG z6}y^i>8qfKjZr|7wl9gn+I*Jkw$e&RVXG(%$3bqQ8U*jVc<*X3C{91xp*^IaBEVXk z@44Pye-ceR3~w^m)f}D(YhG5@V%oa9TdHVSq#!1v7t_JXQHb@d@7DQ7-;##h#KT~p z`h%#ZySk+nVa@KU9st2|lSqO+y-GncHg86_E;XAze3ZffC-e5pR8h_I>c)URK{*S0lOI@zpu&v*mD*WDio_9&f5-}`T0B%h|V zHC0q6R*Gwyo7-+~HD9qiB;oLxPLgX^_DA!>P3-f;UCS+JjT} zqf_9KEzLU7f>nRojT-I0huQNydb5+!6r~k5WU!W0F!E<{O`Ly z>bW(0fyq>*zAzfKG>vZ5Lbj>8rRCB}t2J(@@6RNS1-Xw=yjqk|3K=*JkhJq^uf^=~ zir0ypKAL0m5w_;nG*mZr-1oL#?HUwMW6jOO{d#~&FE5CHNdCX~?QP#Jm^nuO=K`uKQa0i7OKK#9Hc#H=?jGrOtbou z`HNh`(sDA8bEBHeoas7Uek}HC7eNL**~Wc$6;|}z%k?A!2X0I2)lH6D0+O;%6ldqC z=km||cDndb+CyOp^LfmXdWArBL&w7)tF>NnM~)(6tU!YkC_fr|nllw!ykIoL zO8;zct%4u3{_e2#GPkIM`KL;eU3fwxS8(Q0W$}^FXB~9Rcs~yFJZSzOI4_Vs)oqPu zmMqv2l9F=*XF;ZtEmT8PLU88K(-p_!a=3FzbK>BEv%jMQ1_?B`-*&Oza7JaI6)ee2o}r;nDk+&p^lXyvHcrq(v|#0y0n0OW(hqFK{ixPw0> zJ>uBFojY&c`1wL{=B3U%rFm(E1>ejux3--pSSaO0ek(2*_DvoKNU5-DfCg~==8bDV zo;#KCsQcFoMcEmr+$R}W*Z}?-*w%bhS>B&l>Ni{uXFkfS!i+AU^4R_6o32WE^ZWL$^fO{>Wv%Me*WRiiQ>-g=XZaq#8`?) z&N8vGwgK8RK#oy(`1|V7pBF1J3Q(N>^R?^OuU+}!%&BAD-S7Xr{R75w^3d2h2*TT{ zKrK$<>oNA6N(5pKljr)i>sNm|Uy@tj+xYt7jf+Lu2TyF8q;FwuV=DpVG$%1(WC{&| zP}Z+pC6fokIDI0cuf6WC`&ZANJd#yBZkn;BwT+z+7}O-uHe;e<1ecMZ`H~aBYNU3w z)I7WQQ{}OYg2GW4hK-H=f(2?2XhB8l_+d0+bRf9OUSn>&zk2D+(UgZR?;qd(p)5b` zXu`O;7=(?z#(Wk`ReSKo0E1h;Se zRGAfW?)AOP&^rJ4XdP24#)fqi&6jYjya9b*T#;OT$2lGD+`e)?YwMb4ccAcroWSvV z=GNey$DdEw&^*T;ScGvLFK_^UxpO0L*HV)^P&X(2gy|Hh%h>ce%~w~JjSl7y82__K zp)U-$%W=EcFEB~IbMDlU%(CG#|Wd&INXsHug@7mM&kubjgX#BV{9I$}NfCPT-^h{x%1DpV5Y+B?E91 z&eeC9W9L~K%gwCp=Pg{aeC5g&F^BRFj^$MEwRdt-$Mi7{Jo_g%bf(W8$Q$s<*{dg4 zn#|SFH#D`dah$(+#p*TA#}1UNov2R(=(DodfB+u?Pj5zktv?32&{Mq)W_~+;)?5Q) zb8GwgicRmswB&!31Y)%;lz{mmRxORzhN z=E*qrs@u1I8mc>Wuvnlra?H3%Q)lSv%a@!-#%X&~&87Q#Q;=yQu;R~CCuX~<+dp`X z8#-yYrsfyJM~#^KwZ(vU%6|}DQ_0?IEkyHbAODj#R6Q9^;eM3J%_`Un&i(#Y2+Fj$!&zAbS z+Lvc_$IUi2x3uD0ves%qu_pEgMLyq00|yTI?5k80N!tEtOYiI(&*7sW@EvK*;eGJ!ZEiZ3y9LFRFI{^Q08tu z23V{!V!{kPV>1g2ktJ)#cT~p+pQGejktQh{TV4+%yRoqz(9_qE&NfXK8DbU^%RYNY zEeF0G&$ffhi~K0Oj)BGBSYM0eHSe#!o-*4&jwZopVW*DHiSrF2JZj6{l2@Uj9zNo# z!(&HHnx$`ygfp=v--5MQaS(BgJt|V&JIG3|r%bQra|X!0n`>lhjqz+Q>^w`)A zKPs+kZfU=>l2YtQCkwu%)Pl7XIjDl6&(=nS46G(fG3!@`u~fx#zJmtlm}kqg7SuH$ ztEo0`A|=cjqkT(D4GY#v+n%*!YVFkG68UXN<1;r`w-B4NHd1@3U7f8BD*Ub6 z>rYJ`L793GRnQ5S%ZW~(+fy&vTyqUQe5BO%RxvHJmw$BFiZVPHe z2(rkGwUF3JIjwnCJ-`Dn7ocq!$U-}rZ5tXwOF)M(XTYsy##%CV>b4yqTY+dX&|K4u zwbHU}Cnh3TYM5&wcZ;=Br+Rv8o@I}PxrL)D*o3C6wTi94rp+3>mekP#33w$SOKgQU zebyLC7jIT9cE@)rs^iWmNqu3=&#KZ>Y7sZF|GbWn@_r;|#nX)6d`wOr9113zKN2xoMxf2-`1u)B0!c>dN5*t0yCV3e`~~M$b_Uitav$ zi-%Ndazb=awc>zPoRe{U@1nh7`(oooz@A25=?AE^dF-(57!#=nnfL_UlAf9r8}8G~ z9kR}y%FHe`UF97S6Pv&Q@tK!V#}za|ZpzmRsn|Gv{Ai@LXrrUn<;1`T2M#31hq~XzVe8H%4C>ecJJ(=5-!D2O&S<1! zGbAM?e<}?j)r9e{685L0ftVQX8P)ja5glO;9^15dZx~XjlKH@cZsz6=A`)g#Y@9g$ zCFLq%0aNz*Y<~X|r!EYp++dAQ1SS<&-x0dC!d{e>c;r2VtU$p(mgh4JqwD=TT&+SMzTFIzg_ z<}8Mh>o;##2n{<{a8R1w011W2-2aLdizr{W|;J>1Ec- zJ;E@QzIez84m9=)4V^w+lNn&ikAIn%6us8T!PdcP;Zhv7mMuM*m04-D!I#DoogfC5 zbpvF$3cckc=8O;zU|bpWwA04I44Jby11?*+YSqe^!};0P+XHcPM>Lx#08I6LZi? z9r5g4AJLiZ@tGt$mw<>!eiW+&yZ(z?AV?iNknq`zNrOZJjjz9%IA!|mxdtYSPb-gZ zzt3CZ795F=fR8OqRfqtz^HActF<*`ws-ZP()Ymxe&D1rG>p#FfDKg#Y6BbD~;Y5kk zB&n~l&(KLi!gi#r3>^H?kk7vyHhRqXDKodd>OaK2y>7c`XJCXNlHDf&?)583C`7n) z6>^Mz_SJ~5zp?uHpM%^5d%G<@xILX8*&CAv8Ggz;rBHmoTW9dcpAVgSkdAZBbey~E zqwBEABPbjqeG##eRB_4!P6806{plYAKN`CIpF`b?gL6%nZ1o5X!}pFK!Nv(wWhu4X zG($w08>FUf_E2%OYeg*G+Q4c3F5loVaRi&dfSXDFL&+aUj#K2Nj4Jy3c-MYw{T#bh zJ3IqI!onetCUw1>RAn=6(ut3UT%VQ; z4hHkqxN?f{BP8KGunLpfh!xqzF+;QI;MZ}_8F?n#J^X^h!kGwJI2$2JmLzp3?-Gxp zl#-^-zxK^Gr<4-v7CgA^CsLR!OKJyKLL6PBO~nKa9ks`eDSyN#Ff>dB7fG>E44C-) zJJ277vM(AH0QK#yKP}XASmWa9kKa*gxFn3-Crc9Te@iWaB?>;4=8oU@%(hi#lV?;@^{qJ#cJbH_c#F_9-@*K0poAw}cBrHq_9$_dO%LfmW$cwM#?p}}R zXeco?a$4`|MSTgtBMI$`1B(zW^@K|j8FpGpS|Z-)jW%_Jl= zRDx&H5H?JjC`xF;WzqoN0U${rCCrHNNEX5iX2V2@(u8JC5Lx5Ffx)4nLhhL$n2q2k z%243Vi)%+ZKCY1t@(UGk&&0ufk$A+9XX4uE5l}g5P7K8l# zr07W+EC^zw1qmwgZIoipso^8y9*Bc_V-gZ%XsZ?bgD_Kq0({;1+%s7a8zYSu#%}pJTqycQS49zuiw~OH!8caG7 zz30L0J09w`zi}9eiV!ftI3j}k{^cLHFMQu}JufxC+`%g)r=YYSyb!q_zufy)(?^k6 zK=rGyI6TJc>bEZ*Tq(cad8;TZ>EyAcLFst~Wtj8BuY>#1Tmc+TnX- z#RBc{x1WlibpLYp#No7)q3fd#=N+w3J%8&Qhj<97`w1#@5P(N?nxgNVZq@H!Kl$~? zvih#y$whahc(F@-R>9HJ()0XtxBJEJjg^L=DwmX_o~mkUYu>$j^6O7Obag!?H(hF8 zv7IMIb@Z(8y!hOmezDD}n!j=*LNJ>PxkJIHzg@Z5)AbeyucGXv<5^3C4x%QTe_r-a zXX>g8X-Jp`MRd8Q=G|WpZ=P@Jt;gvLj>oiPo*Sc%psw@0+PRwkuGCeF5O2qb5RhhS z-_+GszkT-G<&wU(n&cOoGFn5G!>JZ1C4T+kWM^~LWB9=G z(sBzOyi;@X3r?w@t5-;rm8uV-usjx2NCGwQp8xj4@h8o1AKj$E{fX@1<`Ocg7(PgM5>gSpX3Wkz?_yfMHs*w;k z>UZD#d95g+=J~Ji-W@ztY_)rT7C>!FRn9SITl@J33Q!foTToA5X$XT_`v&4A_wm4b89f7p2g1{{+h zi-4)Gt9^Ucca7zpYjEME99y+D=14wM(0k_GIn}d&t1jXVm{u)D>r8!B!;386ZA;Bk zZk;}skx^>7Hz}Locs2fE))4Xrgb*{Y{(apK!ERetF0ymHQJ$ZceRPrk0n!}YO-j8> z1|peKdW$Bpy8iM0U7OdeSg~w@&Dr9dSFBt*Kc?(Ra*@-HxJ)R|p42$oaYixeC5(0twD%O$zN`B^a5c`m^A{~! zv2xY2WydoPmf3nFXOlpo8ZNkPP_9!+oa!J|{s6%lruOao+!czYeK#j>-4Gxz(2>ZWTA6b%^g<>T60JM4^6GspQD>1gSCBBju61725Ls9PdB zEBQd?h}s}EoDt_HS?SNxr86Q9u{c;d7yb1h`JAX{Mkrzf$9$Uh3;!BNZ13MjB(sP5O3*&Rj#&l|KMny}7aayL-!> zu}2E{`I=`Qa9tpCuKsz#WSNGFP;EF)hm&!nG*2h@T61&VpN|Tid{VM-sW4l>2`EzS z4m-}Bs3}n${KZ$pM~|H_Wv1I}j=!;{`sI~HTOu=ZNZ{B98WkHM1)dAre*V?SufLgp ziyUo0r?07bes=LnfPm+b#0k|i{L{ZHm%dmTi5~LN$6pMcRiHS$)m7Kk-1IkF^VuTd8QJMa{Q}Cs zzuLfge<%)c@WR!XI@xaUj7mM6lPAgtWd9kp(|`2yBW;w@#3!AhOr!ex+Un-E8(Yj* z?FmiH$jr$T=d-1nr|bHexuoV#V{0hgr=hm0q2qCweDRLJ`1B(=d1#uK$ChcHexl$m zE<{XPW8E91BGuN_BGC6P$70?VUkdc)VVuG|wnFoC3wTRQaj%$~iqSoO4X?Jbx~8q^ z+(Mgm?vY6uS-3bG7q@7gZo~JAAZ`jrpA^(IAfDIU{)?;Gid~_J)H9PW%VYD^PcxMr zO1@KEq`320tjQvZ6q9!OYI*EY*=Z5SgX?_@(NYsa-qpvfDJYzJC_7gxk3B9vEhP@5 zQ5Ky#$*)0iX7dj#tPuQ#Hrct_dF)BSY1PWN=%=I@cX}bR*wpcxyXmr>A&I!CA~$zn zUT@*)(;AiU2^Fij@Z|BT`p#F$W(%DCVleRB+`)Nlkyd3ji5HzZcC@{&$liXVS7h=* z1ZxN8@pIW?t;$+@dg{cnf-CDSSMR|MSBS=HT#Y={T$M^;MI(kps8@N}5lPgSdY*bNn=h#pSMbVP=+QCM zv!ox%&Q;CR&SeXPl`=eN1+W(iWwJ?eS>E7WTxngYR?&)1sHxnnj9h8n$GPkY4e+*e z#QdBi2gRT$zk}Jt!i-_6=HK2O-;dHYh zxhgqqi7JRKwC;t_JL^#7%t5*8IY7{_;N!+{UTHh#s312pW4VE5u6B;NLIi3n4dpnr z$k4Y`%N>-%<_RjK<;~pY;oA0{?Ig<`lGB@CQK15U3|-l}li3P!?kBi)zCcYuFDg7y zke78}w&OYh==^N?u{^ z#}v|BWyjB2=xjJRM>U%*M8~B~wEh5W%E?-7wr!P;R*q&iVCc&kjEf#&;}m3P8Z6#x zzlef(-5h2v;hT0SF;9Wl`VWx6_ELkC1Ux(HlW!An$QqkmJ|2wwKTRLg%+d(i@8&HR@LblHCs5drx($(~{z|B&Ry zhK3)K2(9)55}fW9?f<4({hP*6EC8eaH@2@}Qgj zL+rBQy!woJ`v=56YdFMNIN;mnqW&Vd~XsG{RqA^DK53v<9PE+1bg!3NV zFlL=t9AiTRFe3J1(`SY0;F;aPUNka-I6^AcxXXIYviWvU-_+E^2)ufT2v}J=ZSnNn z<-BI;d~0)48mJzoyv}ijSN5UAaNoTyo7XI|GdG3;*8T%3tQKvFNB;TIoP%*8o-UhL zFLAK4M(}sZ-rVBiA{1QWa}yWrv3=c2v^#HYSYcU734K_y52q&X3k%qb=*L2r^b-0s zod8B$W_nUAu;eT)ZI*hUDC-9Y%sQAH9~By4VL5+eEYh2~FEMyQjSdU6uw15A1WSFK*Ta@X#q)`t4}=9oH56NLNi>~MwkB9HA(SU>%_%$yoMq$8MN zF%Wd~-Dqb-RFHqy)zN`h5lgD)vDl2`ovS-%4pC>#oH=Wz&UQy51CEzJho{5N=Fj5I z>|76V&;)bYISgnFXub30hKvEL&*(988QmtG*(C0W4MmKM9uGuDryVp)V?%5n#(<{} z+8my47jCCkX#~U*aru}uRxfk zoXc22g$b^tZt16zp|7O6ZYOHeemG7P)Ib0`?*Wx_HvWH7Ie$Q~g4em9H>RKJwHJ`E zpu%ehCl5!3TR}nPJhDWEx*-axi}Mx*RSCQbs;Ap-1(gE=;CKW50q9Cp&Iig3n2-sS z)XhpNIZ>3#<|YdN|R-yb{WASxyv&1s*wn@%*_nayxi2!f)^-a!BNdy=OKkCswKx6_dJ}k&9cef@oXys${&Nx{2zxNKds z9v4JzcXip#ck>_6vU^u-tcUC7b!%}C=Z1AFmakd6cHO2eTel{M@7}s;-Rc#~mM&Vd zV$IrBOBc^yw0LP+@Gel-tXRHu(IQ-300+Y2#S85BySr@Jv~KOH<+#gZ;R4+5K{rs? zMC{(aWz+h#s}Z`w)gud>9361XungR@ee33p>(?NDwqyxzqHw|)&)RD5&h6W_f_csA zl?bb$O(#ct8|%G0cQ|j`47Jv-S%of_ESis|ZLr&RY^S?DH>_K;YSoHmOBNEDgIGZ$ zU|+v(&FWQ4Ir5&}1Rvsz-y&QEisn}=TfER|ojn|1TBS3jCLP6d1w(p%$Lv zkb^&@!jAa=MfLUkZws*m!u)S4Fx3A`XVn1 zs=xBkQQVQu!BH#m&9jd+ zA*2<+q#VF^a~~!xW2O0Sgbab~%Iq&n+@BDOA^ZEJ>)ZNlU$tuW7FW-}u<%F>IM5FO zJad+AU$S7{JSV3Gi=5jM;Pa4UCM8jm^xEDQ3FBNY8lI*b$@0ji31K%$YiK z^$q26Q#-qvTfN9Ya^6J&!6t;=S8QYt()jWw;JE#MGNdK41?&}4`SZ5aoxJL z_=+xGFyCk`VmuyuL3YM<#2fIHU%q7Ff?1&m(0OB&z*WI_bmQ7JEAh3o!!KeWP7m&M z-gicyYgesUx_A$rVQ>!f@&rT^HG`x6ilzIaBX9nTNsDdvRt6hzS1qc;7-cP8Ye#kVgfUa+7y~U#X2&AOOl&H0=prnyicSE3a%`>j zZ7mn%qbAP=Mi@hE7JM_Asn`KBNKQmmedH*D1EWl8%A!N^8T#YdSJ_z^&VJYqo`#OP0#g;`zo7hBV-)L)LFm3a)i4$fJJerNI82NF%Fhf=FtQn0P>LT^Qm^v2PY1^{q zY9l#$JUo4?&UGu+}0I zkzC78%T~>XHDyesay8I3ZCEo^xeWi(LINWFTg?4iV9i>n$^{&U#G18KmGdB}g}gS_ zN>z@WM*LgDnl(|8Lk9oW=KkGhY9+xRTF8lGO;zN5rshKQplvO(V$G#;o-xm~&zfVF zTCo-)xxl#B)Wlo^461NJ@a1A-#st}Z_*>ITh5n8FIxTB0@*Buy_`A>4nvaKCR>)8_ zks0$%`YgCVC6=t2#F%FU4!MaYo~u~0=0f8>BSXeWAjd$^o`z+wg|V@~h-U~gh$a&7 zX@ie3>N7NA410}j=L?Z`C*a6@i(X?RP`i!n9G9*W%6m<%mGmY+9paIpk%hyG4VyNL z(7XoPky!MZn=po*mUgSjA_m!1g(IT_ixzWJBSW)!>vu&*?sgNS6EPmC!AH?*W?*2m za@T>JtZ*ODO$DH9fX*{(Gce%M_#bDr6%Zn(C001O)D)txj#-U?=v>l z1f6f*X9jmitEthvg}bxQR1_qq@#R8eRZA@kDVl0AHMV!!o?KaZ;NTIt#28bB2NLso zGk8tl_K=&{u8qz;o|Ba)HD={PJOk{Y8DrXNVyw4lfk*bKvZDnu<6bjM^rUWHhxAgo zl;CHYYqDrX@TrP(g(}7x78>SVisl%TR=Kg>Tsy~A@ulZ4i7?@;nF>-s89?pvJ&7RWYLHRGFhLK7oHnAbYJ25Xk6V0l?H4Rdwq&tm^KW2H!Hw$5xjLqm~~ zz>t-znroPKnVOkk5Dd0t4>2%0yxG0`%nVcvrAQZLO#4jmg7EOP%Lt~sjt(;$b$V~k zyTO-afHq8oNKBP*EZyiqAM(uXx)v(6Yk6{frJlY4EPo?&G$k|TqZcsgD>~t|@R9S~ z)?Nq9U_E_(xdt*(@j>CXsxA*&FfAftXnXr(u#w!v)Kr4hQJx7OGiPL^OGD9_Eol}s zwc!TeXB9h%g8>J(@%i!|FwN!b_UX(PHH!cN)!y7v{~&jnvD^e-HZ;Ouw0d*qFuJS` zzggBKXl!plvGBXI4ssJIK0JI3nAPW#9?hzF%5UgI9`~iiaub26nn}By*2vh%V2(gn z(j;jVHngKgsIK9niHWJENw3^k&NuEgQnW5=QiXIo;yTrJ%@0lGY9<0~Rxt4A^y$tK zG|3vp4Q+%i^|rO`g{`sF1bf^V>albAbGmg1N?6<|MR|2ALOAbUz3FLx?O-f4Q3Xe* zTyGA|1OH_Y0rv{PhCFj{y?oNv+wv#o%tQ;r=+m3S*A=F;CMQL^2XQxi{{1u4+)+~? zw_NGs6NUs@0A}zL*$_dnG)R=vl@uSm`NH2nHBI=b`F@ef;w>IP2}wxg?-wQZh6e|$ z1_=N&-;uOG%47Rm#Sad^FfQeM;4QhG{XaUd z_8Zu(*-3aHDoCDTqt%1NXe=?xbM+tXK*etEs4up*T)y3#06-EX$f03lkaiL%1TJ{> z{DcJtTm@(Tk>T>_=sh~n8$KIMZTg{dhaHq3L!7Ux|)AyGij zWAIoSkK~d-egGfYOT5IEI2<^Eq6K`sL_CxsRfmlfQ7#Gcjd%%NaMr}e#jE0hAdZdZ zBa=jeix&h5Y`hdSaa>Vf!9;4+aatMhcXV1rbbm zt3R6Dy?Q5kAVhwBL3pGBNkK$kIG(n^@$I&I!|uS;l;BXIAJ3PK6mu<0kQLYn7e`3Y z-aVUL19A&bCGBJU1ik>k;hxJP_+d>1cDdJWqelunFNc#PzI{>s&%$|OywK*5;K04^ zTLbgXpQ+3`EcWg536`L1S%fIOo@+WVz}?+Bw81c5NF#a2 z+gWMBFL`1!sPpS}?8u?&yvLPO9y`7vEL;rfcxrA6g9Bi_U#td0OZr@Mc5!9ET{ZmRbnpymHPvT=%A&z4Y@%#z*Yk=f^Qf!fG%+bU`O< z59iIhoZT~jxOz*9oxp~Q(T6mwDl~-C0&Vzs?seX@Vb6BooF8uqu`5_VeuN5G_@UJb zoF}iJifI)QZ(H@< zY>xndQw2*xWZ)D8HqwY-AGwhw1-BN{Dr^zZ&v(n)YX|)T0^yqjrvPo%ajNP^7z=2@AaSq+S%EVT8bkK$i7HVr6TAus+%kb^*^9K))c)SS= zdFxiDS-nZv*h+U1yu*1V&@VuUuXv9?=7hnAvW;AI&RYpeUL9u%SW3u@!!LPNh+kl! z3fC6jw+n_M8dLn3Bgt8%5fyIL?_ND^>uvqZ!%q?*q%JWVzCLb1{N!yHo^GowKb8<% zPjDuvM&p2~Yf)sMZ@+YR#DP4jgO*gXNNJGeh>*ZhlI8y$etH$?T1XUbYr42{{`R2c zoD;w#l2x)t)xp+YS$=%K-|JQdS0mCq?z!7<^rgCIG$%!d1AAOOM~t%PlB}Sk9emVB z*EMwhm1MidV_ybW>@2AiRIsOrr=zm`M6%n%HcW4GdtKq8WxK;ktMUplk{;M%^&H9R z>dKOwkmTn2+Sb;~n-=W|O3prk8&E{ZePGMfa@cI~X&#a>P8{$!+tT`b5K52~1=tn* zN{tFOSDM4m7MvDT)>o7kB)UiWtlI3Ythi<>wJX>HQI05^cUn@}P+pv$5*ZX0dob@L zLH|?^#whs65RhR!ttBUNvvQ8&o{NggkD<9*whE{nNB|)$r1FoE0>KukWvf*(6-@b% z3PCwrN`HM?At`5(Od!L*RLa?W)ogL)Clwmy>@jIJlO@2vwaeL)qHJLn3y#5PgP$$Q zV$s6K<$a~uDp_pdpo&kBZotyN^E4|yEf)Wf2o(Th0-iuCjLIkpbhOZB#~ta@ONK!$;ag%p-Co7l*t}pkTNm2{1Y@Q%oO0S zzML{G{0YFY0)D3G2r?>8sN-*8Y47n&{LMSeCl+b>r)82-_M{LcP`pEeY>WaFl~Q1B z9pQt*%K$Bx51LvjucR*%Y+WdMim@LfL~6w+Wg4aYlGbeUIc206fBwZVel`yS{HRPD zcwsH5mBM{eJ4Spybi@cz7F(oUHVEXl;-Y+17Ht~x#c+nm1sY`^q3uTU_7)sG;PvSj zW39|ajTC3G#VTc=l-3iy@YK-*X=6SezQTEy7R9yBcWRP4{)KrMXjN2M~w(^K#O=A~~N^p%OPr-_M`0F%xhA6TjiII}t~qLH6_Fm%u-GZwFQ zG`G!?WU@J$r5X^$LrOKt%{~~dH+bZN^;_rJ$}-uLs-@b>hZG#o%!u+DGGy}F?K>CZ zVXm}va7hgYiZmpI$d4S14qY*Di2l~y-YS`Fp`i5Rl4^QLNlL)cJrErnV5R-BzKfSi zW^Z0;=_hCx5MZYo$Pb!z=s--6pO@L-A(OVLWcD2?98w}Et_Ct0Nh5ZCC@l^y<-JQk zA3Ri&$v?v84=j=VBaO$2klwp%(}aOS?2^9BB8?IiJg-Jt4gB+&hYlpueW|-#wmTj= za#(hRJ*rxwj^}T==TI&^IaCqd-FE&UV4x2lrbdb-C0by5MH7QWro-tehz;T5OBd(% z_L>L9xP$4?VZjmhn6P9J*dB9j(Hy14kSlM`PM2oFJ!*Y(Ge3>HPb;gWECkhJ* z=?pB`Z%{E6zC*O1NHfb(bJ@l(Xo#I=&2dnf^c>4-406ovKewKX-h4G#-W z7OEH3kwgDD>6E=6p|t48#zvI}Q9V=FR$Ei`zOMNdxKse)$3N9VY)A8xlPL79+8{wK zNo#F&)!R3?ngBNtiHn3_YJ^6|j~+nOJ0b2BZmJQ1j}JcF!SmPap7!b!Cj><@VDhz* zTm2Bw{FC<658AuxIw{~gTVUnBdGQ#=>6_e>>S(SVY*|!VmLx-^CtOZeGAcKPldIK& z@@Q;U1SG70>r)d(F?p%PR9=cY;w}f*qyPswA3|`tnFzTDTvAKBxy3@?L#{T)c%%+5 z5@Ri)QgX(!g=^4m?Y^X?a=8W!WB5k!1dgN*Ge?gj{*H3Fs>vWwWmdiLYYQye%~6*c zmcPSNE;ad>qw|n7(`OEeZU^V|#D$DpX!4nyH7BLudZmmSB>6Z69Sg9BZ-6oR67Dd8 zl>jB&{c@ewE8~xEHZ>kWUM!hqGgP8vAu~t!ytw&{@h}+vDEaOtGDH}&t_5KwzP83g z;Vh6Un&H8D)MmRb{n%Rg8003O%0l#FwO;rwM?4%>2$EQU`(Tdn~|q@|_Mf;H!(O&&6boFXg2 z?D0%Pt_?vki3#wa!^CPuT(Pqmo~YspW5k+DA<<_Fr%{LFuKa}g@R6wCi4cA-k)ksO zh0V<-P7%faHsnCT|9mh=;o<^=nYj>;J8ae_<*b2!g>THmGgUm}Q|Y)FpL4}EJ9*}X zI+F8+#5IjIjClWtw6_kg>dM~7=j?ONl_Y3&IzF8WRpKrLN{bYNOG4b;Jt4RyBv{;C zkb0-3Mq5geLV*gj(^87W6-{pLJ>Pfjn^5|h`Q!IIzo#(6U3+a?d!KXGUVE)~VKh2e z++IPsFni>c)mxiV=dhVN&M;x-WU^_h$Sf#fV0S!1i}tOestnDWpj4cpxXZkG;bpP~ z>y~F#Wq8Ptp=#z7chwAKrfh5;wS|{P=J;$ai<}MX5e;7khZc4;^(zu;4lju-+P(}+ zKTC0k8EY0ze1_UxKf~SK3`~e`pDpr6Zq8bjU7WCh&jFkY{9~jUw_r!bpBXThxrs9b zHyO4y@ns!sezRfQ6J8o#;J-EB-`!oDNv-$Hf>mwC>{sS5^jPeHH17)+%$qZtxxwUz ziUzlr7`GP6d_GD0vX?M-8S}mQ$^wsN%T{@kgS;Q}8N%H&?yncme+5Q5vgZk~pke*w z*>_+H^;%mHIoDl{YNM;YeEr3Mp2)569Hv)TL(#UhkTt6jmie-XN`{wFPnfNTF_g_3 zUKF}{qZf^{+qg=>!69K`38n7>sq)~KH*@ZI^-CyzB}d#_w* zw3&{0rfvyeFhg>eAShk-+MJwvc1rn5JSCpIdy2!(UHFmx)bZnmZ_k|u63v@C0#`4g ztv^3FC(CQb3^+^i^}OLumu@@WKY8-R$rC4}<46{8d;~xy0McaVyUv~Y>clai?!U3l zcjc?@?n~k}#4j_B7Pdw>T%F#v2dU~x-lWRMzeJXSW5>QY`uS)0?PX(Pn|r0+Nku|XP(3gEqwL0A+ps)2|2s=<`pHP{ofvWFK^(-aGKu zg$mP#Z(?2J?mgM5;fwk9hM-0Fj+t$*zxm#K@54*v-S<8@apKD_K0ETsM<0A}m?2W( zy?4pWne;JyBx}UVk%EX-v)u4#(3l1HGcylS210;EetP8N!|#7|^!OK_eL~3@-bJ4X zDBup#P@E{Yd~xB{0v`|NMsvo+i~H4??(PRE8Q>9uQ9X9_(~p42c;wTMk(S}@w*YGU zT8B|}p{wGOqP_WXDU0X2aYHuM=T|9r-XF^*_$52>pD&M6LIr|nrOXh}XfR=DM)j0g z7&q0xxS2CUbR&t4(lai)A3gfnJFinZL4r&D@)#Be2^9_@k6d#Tu7+D$nxQIIm*duW z`?}Qlh?R3)dFO6;;@$D4ID=GiU%dYYrVjuqtkfqTe*o7Va`L7e_N8UWDN($4SMF9y zMV=VEV3w;Z9#ZrwVA)R2XgYH2{enAlBC9U0wkd5;!OrcQ*QJImnm5atxfoor*td-@ z(>aU1Lzvy!#er-5B2u=Mx4!xI+ZZ-{HE44nfd~#B&{N>IvOXnZjmMH1&b)UKmm6p2 zEe^`uV{d0?Yd71^-^(u~Hf3}EzKWU}MB~F_w7deU%f3B3x2#W#3-Ma8#Dh8MQCD=8 z-G*nr>XTGhY%kmWX#3I{+$^v1i;7Q7%g)F~fG4h!H>9T}Muzx$F7#OK0UN#(a~53S zap`u8zy9Yf4c}B&CfW;j%=Q<=m(TMI2!boZGF(q1;3&vvwddkxo?hNw9!?Ih{yQm{ zu;8l4heLF`yKs5T&dN%Ad$pa)w%^I!7?$qm@4dof{tO3u(Lv{kI_MAnnb;P0AYPBf zk=tu)8EOc&ik)by*f3k!#$anFIVf}+||TPQBU_DP7=f?@ot&R zu+xlX%cALuNd*Ba7^P%8YG><+_9RS$*aX*`ZmurQ&Q6ZAy;h>WgBFgjimhyS7xQs; zLV*em3a$n>EG2d^d|pS;&kQd|Ejl-|8@0zC5uJ5tf|eU>%uVF3v7=zKm(ZfY(Lrsm zx3ecNTl-Nv2jZ6@9_A*yN-m@5+EK7Kg}I5$|4N>Er(}#Sg;(W<_1SC7TeDI$yj04O zcdDrjevLRMtjowhP?nr5^DEQWfRNOK0k@-L0&v`@`K%;8g{5lHgGsiSrQ)?|0wf;zwr+a!gY{`( zkU4{#rDTgcWDHT*IzYyBgfUlr-heqm1EBj76S8^d?9goQp&{@lajekKt77BiGh zGC>J!VViGpmh80JPs`I}j6=jQwV5i|%^74hum#dEwz4*R!@>79vQ%h1scGrF9TS+v zRT$|Y=YSVWdhy}vj1;ra#Lu;laR%X`bn#xQOF%I4Q<63(i^RB`1m}~;xGL01N(9ChaZ2q@bzbJmSsgm zBDvT)ww~V+6y9J`_fU9PaFMaWCGx0$1(Pu^sVsSFckm)pgOxcbmcN7e>(s5 zS8wi!P0C=|cQC-T?0q%c(lO?gA;^sG9y4%VyXW=u;x_{Rb^ycv`->N8@V{W-7r#4q z=G2#mi<9G7#&CLC=Eky;OqSF=md*~lGJ|q9mmmG+{P}Oh^U68tZ09+SwSVCPvSNP= zCI0x)Bk$)Y#AmS7_MH{^=}4qUS2@@wxFX6#uqU*?PmqKA{OH+pXV0>)g)_ow_LX$H z9+|X{AA@@PUUg<_W@^^H;ti=-9Fi7YE@8?@tNqqHZ@=>)mI}F}k^TD2X`Z|L1nNQM zI{NwNpMLTow8S?LRP5QjqhxnRA|=kkgP0Ck3+X!2B|m1Loc{PLdCLz={(Hxi{od{e|_c@jsj!H z5F93WWeVwOs;eoiNTiHokQ@n)WS)`!;isRY{+V;{xmHZrcY;>cOj|$$aIIp55-KC!fkWgrqsYl zs!fRi>FK^i@6dmr#(JMW{uu{_Kh95n$S#L-9y&e1+B#v*tFJ06*|$4y`^Na-aJ)zG z)TZEtbr-dhGYUUE$G#o@=FFF$;us-*E_`|idjv;4tTNj#w00vrw4$VNcft0|kjSuz zs2Gx3iikJfos{^%?2Prr|31Uc4r8@2>5s*ae*W+9B;UYfS8mO%a%rFrDt!>t2}fVxhk+v5UhY*hPYt_Az-T^{;Pc7jhIhOoIVBDsf1~~1bfyIlw6W7AyGHJCYQNx!=!7I> zD~%2d^jfxj#mbedftls!A7~stGVQUXUSx+1%PBo@$QWTjQb2es5ML4Exk&BNxp?^s zs)vdgcL3zZJ5CgnIuqg({W9~aOZ|8d!m5=BW}x2L5_z$-=rn~upceY`^$)~+AukL# z3Hdh@k&H4VetTI}fS<3Aw-?5ZnXgz5gkQE)wM1FTJO!_B$RNe*8T-TAEeW5@n5d-i zj6E$ufxPM(PgF&}Y=y8~vrJjd)(il)!OuS+FeH@rC*BKUl91SqP>0Cy`0(`=A&7yX znmzz2W1ei4yi#0YSn17t`%x(%FgPSM+*mCU^Sh35BBCZNCVE2{A~WD6jPMCw6?L_% z^{B!EzX=M4qhmxQGN~G8NArt|hCgs%SaLkPh(mav{s{Q<@e#d+HHsJGe_4rMM~qr-GCge&YMl^AFdPMRwEvI5aAIxtmB$yxksA+OM5i+kJcF)-Zr_v zsLH`-(~^kYa{V9ay05|Xt={qWn(7nXqSIY=?WwPStWK&`nyB8uSd&e)^Hy!~U%Yqk zAM5B2Z>$K{aJa)MvJc<2Fl+C=hwDsgl{PJE^&4x!m=rv3ZK@maz8&5;TOY-lwwugA8VBw4GMejHJ$33 zv*+c#MXKMCHe7WnA!$71A|SQ$vYubj_-DtjJJY@(5iV zHmXt8c>RY+g1XQ-+qcdt2FhYd>4UYn0BjyBCi58XrQD-y!~LkZv}|(iq#C7_iic>> z6o!!J5MRD1pB3w&olK~mT7x`RZwjxY_z*W!)p;%{NSVuuM^Rj1Qu|Pia!|ybEa%`d ztFLj0DD#gajgJXhKu1gAu2uMq91a-`62bhVILAmirMmHCHpGT7_#zT!b6vWgPZSOW(9P z1*~MGtQ-y2sH^2FrP{1YsMc3iH_>JBAYGAi39pdCf}5pspV9D~~_Uo@h5;m9}p4b^wBIN${VKmF5axx3%H^5pjdKwa$C?rA1b|im_WNu)cM5^^H$F@#GVQ(7f1t*8#5o=@%!zK2Il) z|NQXn*;8MB`f)>si}~{1to0XMsUq|53ai;JGqBrubqu|s@yREjdTQTmui?t~%hzEY z{pj=KXU>2B<4;IA`}N7ApB#RB=d$N#CDu1K)YVp3S~|?Jh{e-LJCQ0kG(A1-nQ4Jy zGi&;~xe0(qtq0$H?}JZ|Asx~AuTLNU90_L+mIPT@`d8OhSEWDW)u|LG3oTz5WgoTtSOH{O>mpG}jd`f6gYQvSQYYPUf%DlT=Y{?7Xqj zk&KouapmO%ph-ZG`wGQ<{{cp4MGcUiTF4xYi~N1jme0@I_?)HP)9XSMd`&=TWDG(SBO7Bm$V<*FK(B?tc!(ZA)<(29G_{ zguyq3Ene*(9-qE$Yi@qtwhdXyQNh0cArX=Au48LNZ{{U<3afe%<7qwZnZNBU;8?2w zlxYBL&QFaE>oa|qtqF=s%2>B~+x9K!I6By8#lm@jWI$vUMP9Kb!lF)(`RImlPB3kLawIT17cfHp)yOnzPnnVRWjZT{l(FG$bteeT(3 zpLu55(@*{N$!)m>z*(-qbx}Q@gNFM0+J~kG=TX3Oc7*3LBh;#w=ZZzIzT)m|XZgbO z&p(Gh#_H3a{QD!zx1xSw33{i;tpRU5+kZav=yU#n{DaeEVyMrG#mwX8!ufMwp6Tph zWBKCq&x_9q&nnNbr-z>W$D`9WZp+O#!aTG3iiY|+-@iWv_UyE0o__kNC;#>M-;j`K z>R;xkWv}0ayp%l1xwgKxw&Wj=+k}K-%fn+bIBDK%s5hAp|sD~`vmwifT8g@1l}Zde$URv3w(bz--9%rO7w z!^jBw-~$gl{Ln){pnq`6)JOjOFUy4yaf!)kS?e|-=Q1BYtNpyD%H>Z_Ee?;sb3#MF zUWc+wyWD5hGAy5ytpy^r=B$DSNy=_Qa#|+3-;qyX#8s>Y+1o4TJod!P5m5+QPr!3a z$}1_qENB$11gZkgg&4I}JhExX?!6faHqkYGVkivN`@zSbo8y8Ye=>}b47VYF}I!+(2z5sc_?7EXf&B_Sa$E|x_rQDUSRAw-^t=cXLW$h`qM z@ADxm{)(d!mlp2c@$AF@aM-pPMs{*E#VHiZJG#e9Mvgfin+iKjRyJH$kz5+j*jUxN z+S)3-c6a4v|NYUw=MpplEVooWJr&k2903yLgfA1b*20x_1F%T9oQZ|0fFmnxH5>YLoO6MWN(j~ zZ`{p;D7oEnadyJH2_)_-fm>a0-sVI{2k?or^k5|--M{F;s+z^zZ_))Sa7b|V;^-j5 zM+7&rGj4N&qXTa8QETQbP88g9;Bs`3X7!U}0si3jbc|7&)dLzj0gYfyi0>r~1Ytje z?!VFOIc;Q?JJn|Hx6v^;ZQ&as8e?>D!$|NcAle~d=p`L*Q*^(<8SQP+0i1Lwl!&BliKCq@q7&gcLD%uZ49*B36=GB%oAC)cgT^OlBoUjVy{#>TGG5IE z9JUnBM^eGfo~w(Kkz#m>m>|hvR~JVjjkV21KJ4mjZ)Brk64%feg^P`Z$yde|%ZI9% zlX0X(G`MiY7~%dURGbJ{h9LlpSluNzBhrjO)1$w*q%=5l@EQ0^a4~R_;~0sYTkv-V zbGw5carJ6zVJR@h;T0{5Dk@Svf^bRMViC@UJ~$nR-lw zHrDQA`xF*uZSFn}+zFEZwa9>^zphlvGViNC`vCjL@$u@big)F1^7RW02=p(1|6RCFu-8xSZ?3B-+O;DBCX&$bf0IQK zCk@yJkY8a>UOu2CVxkYc&26%HUsRPB0cfcxTTJ+NI}B)a{;92m3j*}O^0jH{ndxao zgy=*b1{{!SZFx@4hK+3fp`9&o?x?OP1<=#R?DSl`I&(?w%&UR6N^l`tH)N%5-HsFp zgde^;zoZt>bbEJh#k-|o=Wd+rO3O-003x)ntfDX<2G$gKA?R_wVwFARaQfH-TmFuX z*(vx|!z^Ec6IXXdd1+z(){W~j({>}kzpjiQmss_87{bmiIjISe1vv0ERPb{YGCTlP zYCD=;;=8^T{uTA5(0WpOu7l1__HSbQAx)3FeA1g7J=fNukR zTcEi{01w397w{vv^1**(79%K+NCc(>Qe+iqxCw$?Z-}^|j06+@2$^YFav$-c>4;!- z=LKtfYMPiTBnt_bDTF*d#u2*8q!yq(qTN-n%;PEr_bdD(32H=0pwCJe-04>6{sw~9a4O6AoLZn} zsf9q#F<$r-tyjo=n_rL0O=Iff^G z92uj=h66ysdkv^GVZQD(-C!eig^^0r1r^40%mMQSm8JKOQ30HTI3}Xfb8Zc6q z67>jyAtc081IC!ArRWQy;d=~WK(wDBo23~U*pr+}4UAK9U7YqAs0s0rU?tNfq_C7n z?D5-GJX5^0F)D5ez#9Xd*54R8wR|b|Z)!hv76t_swp|PtEihb2tmnc-I24)~r(?;` zZeo0dk%$F?xqb=V9wa5ihWo=7jSGO~OK}6RcoBvb24VYEz`vPJp zG%RLYHSQ#gut<4Z{6iuU3=~_1yAA>jp6jFy)5< za!lOyIZ)*|8J~(2V8Vq#R(tmXf@&8O@$ERTuFXtIh>iqpA0?eICNaRIveI#9&-R@w z;^{^3&4SDQwv9Q6o`@v}P0G`PTqS#V<>vvzkoqHH87u7tObt}+{2f~miI5UchTL*i zmLtp}q#7Wu0i*~~5ApoFR9;pNzq3L#4nVGT*=ez$R#al)$bFAKlZ1?u7EQz|*c!_? z^KLBq`s~yw7+qjJ#Fq_lFokgSCu%|zu|kBE6`-O&vcF`9sc>zpzrd-GF{oG! z@Dgi7T@8(ws5=PcFE*-91S<`J6Ss^JiS-_J7sbL5E35zhH)`G(6^}_n13ELd008TdwUOIJIJIyuT&|2%22)LM9Udr5Y}Y+%Bg{WX zAatVU-bck4jMRNN@{P9(HEoQ#>wYS0=r!^JoNk<#%u3&LY9YCWp#^~NL^a)Op@67R zp@>Rj$oa#?T6ln#Zl}XB+(v=p7}*5tPy^vF?d2GI<5aXkUa_S+2?z(_ISAy5jsUT) z7OX||VBv6{jwdcU0<_v1uT!9My-19Fw_-(F5Jwh4Xx*;102cLib1&fSE_}VtZ zkX%hLqZ^6#!i#HRS%y8YrM0;ZSI-68&7Ws4_L*ZMHC0s=RW&r6qTM@v*^76ln-^f4 z6%~qz^3VChkzECOJHp&voNhjSIUYeg-grp>LJvMk;Z7EoR+bhacn5FW0$(Wr4kabT z1>4%#J32WzFWrc^9snaI$EFV9vZnD?EJz$mc#JFfb}UCM*D# zuB#R-SnBB$6cz{ggBRcmko|%qV-r|%Yg|N-_sS)Ie+fFLKayv91%yS# z!>&f;RS*B!GAb!HDrDu7m22C5{>#5*A=Br-yryLJ24t?n#$1eB=BMc+wrRD-kg8t3fp<9tX0 zaTXz2gMA&ZxNBya&J>*V5HiJ8jZ!47c%_FTkpPj=-SNw*0f0yJc6{VEQ<^n#=3Qqv zWIAAMrv0P6UF~4)M@sYVjw@f>xB-~;j*c+*SySNo4MY*fht{h>43)m86C)kE*8aoE zTQ~aA!`1Iy+$Z8N3FlfzeRl^9?izBO%gB2m_F)Luu3kTL>CRAp_w`>dT}^d!H<_t& zSKNRy(ys$<2XzbibZh#A-hu9pU%%+TbECiO+Alv|`ql+;7>JUYAv&sfuM#o#sQUVQ zuK)D$o#C7PT~~j;c=2+A8&1y?-9<-}K4bTtSKB**{@&er&sd8QkpYi1B9yP`vXeSO;AcFg(e zl`GfUfBNFfJEOxl`a6EPaQ5`~MJ_W;;UbO`w4;H9GIACCy?vVAuAZ)r&g(y&YAtL2 zu6v;S>ZNZ_esj(VVKJH+ID$K}UVT@)aS^+atfIH4{g?BfzE-n+L(#D-?UzoUz2xX7 zxtk)Yg*nQ-qX;vDjC3T^Ua1G`bM@j^A04XRy<@}H_b*>L_oJ`t4EISh1V^0%T0+(i zeRt<|>B@DiI(~K|UCOt|KRDD-S-5jc;pg99*yHRriCT6rboNNd`aOz$fuVF#Ui!}V zD?gt5^5b_7HddFHl)Zo9V@Ckuz-R@~nJZTy)NZj?-J|Ej>AiaG`lXBKPJZ^`yRWr2 zH6A*3*1=UXL+z$>a&Wj#OM0!n%e1ExLxJ3`Ub%Gfn=>bHo%GJz|2}%u-c_Dq;&#`? z!J!9ox`t)N+{7NSTkOGLI<8;6^q&j3eS&k$XQ$q3IJi!9gVVOX z(xvVZx&^SKn2)(r>=wF)I@_;Zxl92!KU_|9bTx(d^00$FmcOH0jm97VjJX#R?1sdy zU%h(em!E(B=~p{PSB;y-Rqtp|t3^F`%RLft5o@SkR+_aNBYyE;2N zz}+2a?=l5`+lrl<3xIDhbSrhMyM)fW9i14rxr56@xMC}I5(cO1rq1YHf_yh(x2lVE z>e`Q4I=Yy;s$E9xvE93R(9JzQJ@~{frBmt}>Fg@BadJ_+n!3Pg{mLkM>Cr%PBpUIG z;cBPYC3fmMdKcI^%dQh$bdGqNpa2bYYB6pdNuooAX4-$kh?wgX7Z}jScRF5zojqy{ zNA4VL|G>)W0YEaV$96UC6cwM3uA^&%jniZo*;(wPQZ5&aLq+! z8lgtp{CVd)Lh2U5a^BqDS?glvOzYm!O^smJkT?w2Iwj0S*LlFo(OKm($r&$4zDRhu zsj0izwAZm4uv)sVo=9sa$z=+Vq{V*Yn{kA?>;s?BqhOaZ>{8u}*0`yh>?}D6c(>DQ z8N$E=-QCxx>Q(iOba$S7&eCy`v)YMbUHQA1L5kfB#mG+4JzDvKm4lhHiIX0m2NhU! zy`9%FN)+G46W+gKy0xv=d4dzYr8~O&=~JWXlW3q^qGo%B7%H*;rCzl2y= zRiD^^v1f*29E}*KK?qclC;BL#BAy-nuLS3ziyd+0o3w(0`NN zQlk+r6W!I${vjY;==!^+y=bMe*4sHcPI3^g7&>o=01y#wj1EF9nD79@>~;OerGCxb)0p&#h&lZSKz{YB4NsH{aR#m)@IROz-FrSc7NNH=(6;R}|`T5Y9R zYZ2<9zOCXV!qDiz)u*PLp@K|t9e1ceMit$_N7JTTnpz>e#lg&8zN6xF2HX+-#%IkZ zfPbRBa#wvv23^7sbwf8CpSRFjiI$4B8ChuWs>aH6gFig=qNU18Z3)@fOLnTeGD>8i zsOW}Xn`Uk)TA5fXHZld&43C%H1-N7Ua~4)s6D$=gW^ZE0mmCk=^{&1NK>FzEymkTF zQ2)`490{=v0&qh{(qPexi58Iw>z>@jL1}<6zB;d*Cxxh|Ix7d<;#$CmWN>cvAe17m zJ^^e*1SrT*e{bjIbEFmZcV%X+RRi-$$R5dxrE75$zVYw?_FTiW04NWzer42)PNii_ z2!CMN`mF2(F_xY-X;3wQ2bRCXuOlZ|*R^9~%F(V&leKCz3fElRr{Rr{$Z&M78dK$l zu66w9)R=TQUdR6^U^Dcezo07CIElb4_sKi)m z$M~x1pfQ89`|7#-6s5CCnId8$Sf+woK7B_|pNJ&g_g-7wkXqN}6ZdIMzb2(;YO_?C zqY02N<2ssYe>-V*^vV4B%j3UkOcxF&XQ~h(4K>`*@fJ9$S-&N<9Abv+dRFh%>(?*;@Xe{CAHVm;{+8x9PJI`hss>V$E+IOKzkB(BMP%t3 z6X3PWKYVxg)UhKUyz}<^$4*71NQm3Gla?42)d$=i=z03<9Z-oS0PS^k0%zdoi{GFB z>iFkJKL6^&DBRAPAaG+OnV9hv98=CoNtJpPd;p+7bU-O5AOF**PJi=8REjoTm8MUP zijw=-Z=Xz31F%y`S-1@5@4x--$A;(>fH7%dT+~NJLU$+bF-->F(%u1lmB4-FmrFnW z_@67gVpAD-rPMnykw|&h+1-o7frxK*2gct^zex|ge*N07mw&l*Eif)si(6iOTqK@R zt_#RWat3y;GLs^Ow%fsd_3AhN@u{jblhon(2m)i^>)qZ(UtYneOt32>*GMiM?E2{2 zA&IFXm|4nbVniQCY}At>zit&&%Ryifc0jxC0(;N;$YirrImM71L4we8<%LuSXxD0d z)uF+3wtGh>Yf?>83=tqQ9A55e7^ElnDEO`m-MY>*{&6WX*jTbIiWErG-vreTombVZ z>uC;2fRDIIikz&(aAgdKpmQM*O*VnkG0u5CeT11Ywz>I`L*H3K_W z1>{> z`RNAEEc6bNg2f;uSc)}`(M3bm>X*UDZbFiMz-qfYG|;fr7Y^>~AVXxVNsJUNLU|&6 zi;*%EzS3VCWC#q_#HgcHT&q&u9Jw*rKG!oq6(j{Jky4B{+7NYDGp1ST29GZE4itmb zf%=db6p2wHv@6oO7_=+>zNNlqfrfxcG!~^AQ@e(5-nhAHgX1_=I2h&oD+RE?=!0;HhdwA9xm zK=MadVw5J5KVJMf?2++Q+yoHYuiugx>g%QhlMaaohYvLJ4DvX_j6GsFfvNEH43Yid zm$RgGb-mYea1uan^aomCx;GT#BRqDH89VTZ;701xuemL%tD)`S#6!YC;lM~+d0AO$ zDSZI%y^I*KwqH8QwXXJW8xEKpQi0V_3G+XhcudD%Oa0eyYQ28>=n%I@wZGPKP<6=U zpy5D`SdLe_@t~!p4+%En(ADb~PmC#CmuvTnhbA60G$3<5z8n0Ei?5hTFTR<0Szi6k z9`#o9)PqVBt5C}C|Acihyy*tI+AklyXN|g4-8$tUGSOA&%X{(pG=9AB@#s?ZU}yg4 zqkF2>FRwQpn0!z+t+R3Mya~G{gVzT`&qfRn?9dLn*}Tuoqf=hj=g`d zzG7eArhTwS9j4Jo`aOvv|O4zpkyUtgDM* ztfNrQunqZIz_2lZUliF1ZSCy2i*J{;OxdrmDC;og78Dp%Xo{>;=oo?}>X%FZ`SId~ zi@!8gfNQuK0w9?~lx&Q<7x{`i+R0o*R;&N~cx8K4E843qFVZS@M zoEG$Yhbc`2|KCo0S31i|n%hkG%Wa0%QcV|i$d|oC>eP0yb{vp4RJLgLo3!CsgB3{) znT&ONtzGI=5zF;&a%)@V{S(@Bm6Yz8!HP8A-7w!2ot<*W$o0;5_cpY#{gd02n(>XL z>eP|`i``?m+Aem8?IYKF3QC*zKhUPDH|}Z}XR)qbYDiUwey_HxI!4;B{ko~L`N1}& zNi)o45n~t_awRa`$Y7*NXXjR?! z2JV#5j@&iU@ojp3Q`^I>LchlN$|cnZg(}#;gwD}_=j^I_q!q{DUWU)cFitSAIzh3O zFoura9h>)-v^>ztTKGnxgQOY;DVgh#o$U!b3QKAyx5_PeQ)1d9q$>79=aRu8bPso) zNzaE5rCF=Cg$}vKcQju8;2@jVo$j9f>vkhhp-$6k*2190{B}XoON|VZ-D~ZZeXK+& zms=;b7;QH=htW2ME(;c2*X6|ALa{{O(Aqk+#n9EO=Jp*72eWL)5mAJ8_+@56k%%@P zY~d!S?miWa4Hyp$(oiv>Aof*l*drG!4G*9Nt`p(lPO2A~wWv60%U%>$PHth%u<#5S zFJ}muT!c}IG;CeZ4@tTERK-BcY>}JgdwL)=MaEfly&q-f7lNTm)1qxwZmQ7?H-6yp z>+T=Kco@uM1A8{?E>aa6N}8n>vt~oz4FZbSA@T3nsQSS-0=(9^t$Q`a`kI!ONzJ4d z4T)fbdB|vb(xW+22+t?y7HZH|)8uAueKJ~?V14S503O@l|7LbUkz9J*_SkLvOpBDd3GiT$xRFUhosq%*Qz^MrP-fCB zHK}1QB7j_xY=n*31%+ls2w-Z~G%=`1#v&-CgF`7BcT-UXYc_2no0Y_C=>~p@-L`iE zqQx2}G#QOn5{y>j4c*|Usd?yx6)F`Inv5nZDj5buZ0Xv9LQRoeXeeqlYf=%iy*;q+ z@z(@@WkBY*$be%Jf!qQYL>CTcqN7_ed(lztfYgBG%jh86>l{(&q9Rlj*E__@T^1#m zVSa^X?=-LJz}nqKytcS?A$I()@sFd4gWkmrs4#=$4c{9EbLNA#A?SX|@vgCkBj9O9 z@J~la%u&bdsBrC}1IiEpT#&rbU~i*BCIAV4sp4Jg#~zsyL}1V8F>D#v83q?u(Pe~Y zX=g2hQH{T{<1Hjq;D6An#%;nli>=KRvb;iKBPfT6WL#X_gy4+xx3{&P=xB!5xC&4Co$P1twLEz2dBLor+l+oa{lendo3@PvAdOT~uVM2gSr;l07Cj6_*5< zJ_u@uDfWup6x;}KJRoZczeT%=E)&s)tq4b02ZzbP)luwCVD2|K*-7wJodoC}$OHt@ z35I8wOwI6{?gL~e$O#m!{UkfRqYEe+CxGpMqOzZAr@+{6irZY-MzCi1js6|YoF_VA z!`VpIQ-NZnIBA`ypoZFdlARX6RIt$-9PKsMW_D(_5{j^Dwi;`#-9%f19m>>>T5H*E zimk!McCwS!5ePum%!+|P7WD~EW{!%DXf0b!u~p%>2`uae2Wzd>L|d&5Sjn93U~6J! zVmkpQdJ#WG2gTY1)?t2Q#w0Xgk%$VC^u;QR4uS!A^{dQ_-~IU4Sv_!GhT52q?#I*W0 zDho5B*{Li|L6fls6id-!k~v;vR?KpO1%BC0vQ;dmnk&;m!+;e_+6z{b&Bf`I;M7Q( zNV~yeg1Krs=FOQ*`Ia;_*Am4B)vPqO8XLt@V{S$i$U?D_Y|U(B^GVYcbBRk!kNcpB zHs~0%QJeY6O@?S3P47g6QzSQnSy-?xhyuE$fT6%(I87i98yR8|z|9g3NiaSM$v2YV z`4P$UMy-jCW>G?<5HS)S5rzg>s3AB+9Xu2UQ#~vIgqe|$n8=dlltJz?$vtHO$sQde zMhmDYMkwJdOobS5B@zajguBE}uEZ=wNflFu;8r~5pc~-p6)VOtI+8~i!Xw0RIZTOB zB?^d0mjVC6r~@7rL{RRYz{?O}FF=$|lnSF`;R^na#FLjYuV4TmRO$fGJTNMXp@&~^ zP{1lFR)~=?Au&=yXpJEP12@1=IYkGzN+3+36Ct^uoONU1_!8{D8aD@!jU1yyYokUG z%@Y}!%u=NkG%ls8!FwBV_OZx!80zZwHxkGCiMSoee6s5$$-5ShdstRHrbJB*7HQZrTF`DQRj1(^UQlu`9 zI#$DWDJ0*b_TZ2k2A5MY#snioxA9aaVN>w6ijO5nRbD1VDhU$Gv?K>qgA;9_zXp}m zk^00`ULe2Xx>cbOJU>eV_pHMOizhi%fB9l-hoD7w!I6P^= zERY2#{w84)LSe}RRU4;-QZqzVhfNLz52z+_hS+Ekq357{AMnr)&7PdCD4u0RLiJ_VhLoBD_vL{3W@`K{f z{0zP^rlBU|=0^!n6)be9K_#&ol!(D9G^CEvhhq(l1wQdosA-5C49gpeOk$KEnF@SI zeG)=ZLmRAwYGFAPqLoM$VqpoHB0fwLVuIyWLRF|BM`PjvB0Aw^5i-i8ASFyhnI>8Z zRG~!iWj==ZD25`K86!|aM#C_*zuwQ+S42^aNidliBQ)G(h%xvJXbvTDu_~01p;1PW z9Hm5x{t}9qkKQ{jPDB|jjfh{>VuvfC8mi5p6N=(KWHL5h99UgA61aq$*MgVty(Vifd3U$}~_NUEs#NKJYa^15H)X2T!Uo^5>X_|7 zFA`!5(IM&p6MxJgQX8QJnHpnSdr1Ha#LXi}%gqTq! zF99Ns)c9-tOd>SlN&tv!L@zN)!I(_^1YdQ8NjQ1|UI|;nycAC{N)NAj6UamHlTgOO zlpvLl%Gd`=l^`v|q4$qK8NM8TqK_7xFi%J^MCGUTRRXl(>M(tX59rhrF9~BP5q*?E z84sP}ukn^qq6eT0YDH)v4+t5L79K|*$y>VyxGA2(YQ1M9y{r6W2t^4t36n#?4rD8F(Wkv4iS(bLEEuSY-9JPZRDQ4k9$~ihycVkHnGO*@HT}{7rfvR zqnOHbi1S3(@DOKtqi6#te0}+ZLjfOb9Za!T%l(`#^8n1t0O`a$ox0qlOpigc5~0*k-Z}wBE&2cbgjLwk|1c3@Wi27Z{Kv|a=0_n%)NN`Y z$Nz#yFfOI#p*9&co2sx=cJF!sGk>5>gIdiL2C#eg=4-Zl|j6@}u)B@K_XiY<4l$4L*CN+n9ugRQETJMd^W)|^tR zE>W7Nv}#&z*Yhr^xrwDFC6iiBTW(k0Bbk<(l_+JVtrJ^rH3P$UthT0f5)4V2)+sHw zYJo>VR6Ju+=>sK7rQG^p%Z+lP(!;ZRcWLRvB}i4$`f$t5Mjka`BtKfB)IQo`Xs%*K zcW6k&TJ|9JmQwpri`a}^_8YxoN(l^4^;2O~f&hM_S5GX_6f2FBS~Sf{#eMWT(-Kn{ zp-fv$n+;8{?cD32QByLpSlcqO8P?Qs>aJbQQpuEJrBu~2rCBMJD3RVi{?NCUmONOj zl#4A7G-Hntee7+}QvOh}p`zvCW_@K59=O~0=v5Ek(NHQMX;zBm`^dGE@n9;|55i!C zHNCH(VFFp0YNo(oq?FxX&}33By4p=Q{ON`6;T^#D+bZr?@P+GXhNx))Fd`4^(McoRH-U5Ed(A zZK1I5E)9-~#3rR^GK^wPQyP^9b)odTlG4eAO3~CtrAoqU^ZqIoC@7iQs4pojH2GaY z6$;AGRNaK%k!wLNpNL=yrTq5=4JfEIZIm07!utxOCXlN%jp_!adLnPAU|o>oln-oe ztQUGOTNAqr!7KE3-@Y*`Eji8xM{CX^^8aH*J#&2eM*TT{gO&WjR z!VmgGQ=spI9;mT?HE55vpYzs<-0&Co5o_e&o8H-mA5s3I&V#(ya=4AH;H zKqO-%%5?e{vyk||H29ay|0Sao1`4Z7BGSFB0YXc)}yq0v=;bbWn4AmFH0<_o|h=*Bs}AYHT+*924b*; zOh-Y+r1cioWZ8l!AOMJ6GztaF$*>$;R2Ganq7A_?Zi58`Jia(22g~ub(qpu2x?)M4 z(=NtxJ9|V-pKhVD8Un%6!f|0t4#F#^Pgh&ru(X>OoxdC3^1K~er^{B?Y+ep2Cy|I_2uX=a-dJ_;(1GTL+6u&$19xNTB98?N zgOg*UQ}W7c8xfv~kjr%$OBXNpSTKL?>iGDuh=g^>NLsLC^ZJbCOL_W_xpU|HM8*V% z0I(%CDLw|ls*5}pzB+&2+&M45vdlj?Bw)=d3Rxw{$_1QEDUWBp?7^1i{NW}=%`REI zaN&aa^N^C}<(FT2Y359KH$>x7Gm93^r)H3;28a;uGrWMoh-NTCV>{pmGo@5t2{=e} z%6p~E$g^ewco8_4gtWPM;euCRoj;ezGXVoh+-MuEcr17oeS!@5DDO3^S0OPwjgiKJ zq0jdBMPxF79|>&|L-AP1XXNjXa9e7770AZMff|IcVB)4OS1!kd(DwYfb8%?*^Yivz zvj*G*xrzDDpFcM+koosgk6r{viAK;Prv@Auqk?(G(#0f03IXQh@WR|76Po<1^I=UP zDsN&HpAA;$RkRx*`06k?uhneTofXTL{YLE{gTEv}QNuI_!57r=u`OEyDp>*qoVo#( z$yVQ{7-edKs3W{HP=6V3fzQndQc2L5G&{85kK>`>)6XTrM|K}oSo0%^ZH#vTcu8g$ z!8mmZ_2!4;vjF(YK`u+$1)$D1Qu~P&66Slu<5kXk5q*tOX*+tn>cJ5($EiU3BTgmtS7*)r|#g-2vx}0Rmc|CvR@>;%v z!qbUr6xWy(S5rVgQN=ZVUf8HKua#KPaboAVv}l#Fxgkji+sl)!7FOO|fh-Pal~Y9= zy*Y8U_x~jhty~RRjBzgVFmev9^yWl*zOW(3h->&B;SU`r_F|Wtz5cf>6(BLLdx*; zqo?7#=Za;^y@I2Ylau0N5T@u2POs%FeS%{%2o5PF$`7Mhv}U!ZPk7?Gt!&%ihSUf@ z9|#uf9h$f?pA`)5T$>ac;Dh#IH-Kbvgfj^Ef8}qF{CV24Bt-NF2l%X5f@ghxbQs_= zWBHGU$vt1qRQeiQtMgjoq zK#&47v<0ss!5v~hf&38wAz}Z}7GJm^I|E=_uz{0R9_&C{z?;v517Rg1suR{ry#Ks8 zPQjv$>j0pal1P1m9q1jjAD3*|v>vH+DSIAS(u0tfgxcOlm{wWo)MpH(MMbAxt5@Vw z;BF2e^-=)v6@}Kp?X?;~@gP$Qywqeuk>xiRo(N}0@GnyR5+)Z~kK`~SI4bX;FxYii zA&eZ+yJ5f_K?FTT$5PlUhJy73d|9ABFb8+$?bwEBV{{2<4WkS>_|y($(%XRPV1x>2mL5>)ZH-@p5O}PXSw;zfo@{QaSjhju;{AhzrHu$Pz zbeKAN1T`kYNClW3r-B!Piix~OCGaldy`Orog)EH1$eVARHRm-_!)U<&zo?=ApIR`E zEc!lPK4Thhblez~2XvDhK#fb}Q~(;>PlYIUL1MqBrlcmuksN`_ z$2J8_f8Yt*`E2>m%jVlX`OuUH07UrMV}G@nO);jn^RedVsq3ncE``}I>VTA1OaqF5sF4&usg8EZ+m1=OmYHr+yt zvx{P*wG(XNQg*Ft;5I{T=?)lfj(9$6Zf%Dnp9!k-L_l|M^uTpVcfDum&ai~e+3njm zyXS9v>;1#;Zks*PLT_Vdr?F)^-N>E6Za7elw)ft+Bce6o_RX8$mw)v2H(#Ip?BDBb z%+Q{Rt)yckqqq7yxqo11-|f3#9J+P$$UEO(Yj6MMhclnO_9_@{aBV#*>%K;~=-@blg z@9Mw!bThwdY|uOgcY~cgL3(#NY!~+4s!^6E>D+3lvyq=rUUK>HZ)0VGORl5mm8yD| zg2;#|jZV%W-+A(WZ9S)vLs3_6sjsc6slIo+==AR8?-;vDW+Uj}`_=}dN5eYGX?NJ` zH(ai+ud6qbs#tHStF5cWbtM-L{t}}xpEkYXs;|6TRt|wtCve+KwiVYHEe+VmXf4xO zRNuRK_2lm5pLBbn8%qv<9k{Tu0Yf$DQVElK+ld2r2my-~C_A~1rh3U!yIXwe$nKxB zCZ@0ZZRh@zrpFu%&Kie(%jxnuOk=I*h+2z9t5@K`YHF$;+__zH@$AVHM>$yHTFL)W zxa^PCU#y_xCX1<_%*FaTz;)yUR+N)jeyjA>E%JtnYwW7Qq$^=?R@jTS7eB1UNI03F zn)+Jw21Zvw(#8Y90N*aVNz(T9>)l_-9YINOmIvfWM-K`TnAgY7SL(&5uJp|Ec zsHi2q>CP>VM7e(L>ea$(V#HHkUGFp!v=5yQ`^K{swe=8*f$M5&)WgQ=Dh1-0v?kJ^ zZr{9dZ%xH?)widE@&@1?G-;=t;P)GD%RHMN3N5EBw6s~Ws+Ea9gOgm zi=47?>bQgsEUU7I963{5gq(8z%E{xL zD{{cs?Q&Tk-oJZagO0HH{m5PDu*prbuaHx$LUvbC(bX%QrgHYwvEzr29_Dnw->trG zGfs9mY-`WmtEsEis;^hxC-0r+V2z6|U*_zUvnNh)3d+GfyMO?6nF33CL!$VWsZ|P!@-9~4;|dQYbRy)w*I(rb^fgrM-FY>cTUEs zkMp?w#P7wG4*_|KXg4{;rs(pObDVH;^vKbJoXfIv+pk-G+VDgE(t@9MxLuZqB?opK zJ$9h7#_YW4ux&YXs{)2tg50+2*Dh0w`oz(rhxX|OrMCXGY2AvYt2X_*@ATE%3@V$W z?$YT!N3PwqA@3zy?dsEHjFDgll={XMNKfgGBO*cb_P{^`M3Ui2K~O zn#u~Ra-A3Z6#aO)T-2m~0l4ln91AHYB>uK#)5euc^Ve+NdE{aV_cGi^k=T9C!xEIwn|F|}P>FQ0}51zYmx4PbDW!w^4RzH{7d4v1W+5;u` zByI0{;rY`?5AEKyZR@7h`KvbmviHo@vWi-pjXQ%!D3m1t9;FUj)yk8l_jDlph0{k5 z?ArO$suk^gq==7Sn?YxG->$5Lml)2x9wS~+gpE^NAR6NGrt6$tkK@zcun+YVnS zE`KBgs+Mu+sF%^pC{b4BG`Iq|P2RF?-+uYltzv{ad1T-3>k9t3bi0Z}rEM0@mS!l) z{jJf0Otw0k)#h@$oDP@2v)Ey`E#H3~(E8O&r;qOWea-r#w`=V-Zp@5_I&MBXk0!a1 zVh7vSeZ_TrN|}-IzU|J66U8MQiONYXyMA1A^gebKsg&HhxZ6Fz(DIO5W#OrV+kZK7 z%K}ro$??#3;>UBPB^-!)^1vUzE!$aULl5q$wB;sNuT$9ktPJ?uH!hyo`}1$7i47+o zUWK-wwp}i{j!_Qp{%ym$vo(;^TI%%Mg|LPd<`kXlmG!IzFfPevlEv`*<{cL+>`q(V zii6imu3kKQjO5S!ZMSOCvZhX4tKV{z)nIG1B8$ONB)V|#VMLQ;di%zu)BCsmwEN2G zU(Xg5UAcJb$gW>Eldlu7Cn{|>ta8)hr0WVY)b8Cay?){F{)1V@~BR@@Lj@UvtTv}0%$ejOgGu1M;xVHRY6jtUcjS_yTY{_l7)I` z_ir1Q{C1nzH$&X?xD$iTL}yMb(pFcH8Db#+l42M4Zp#q5dg;`Wy*qwhxBN&I#wFNn z%G#t8$bU^Iv^=b)I*qEjN3{WRFvvZ)eY5mh;W-jIe%rWgKp*=tR1a2ovNmSP&K%{B`aWcgyc^ z&h6#1#}DrQ?Z?&mN2_FL5avXy&QU^FD6Z)pVy6mn2_#z-T1kE&@j@!@A4S)--)*>3 z!-o|?(4b4%WCf*cv!i}3Vq2_QWn;wyCAY)!yNrCYKrRr(zyGpn_52-o`P8yljUpW; z_V5J^Q4P`&gP_rNUp*+gM+(Q?yQQ}zf#m9yOJ`0HjXxFSFF9UK?8?o;YC~Ji=GK{+ z_(rHdvnWy)n#$Fk%3CERr6rPja`xomJv+9nUpar>l^SKU6Ag@5B3YO-iu67tG?k6Q z1IVh~c~Ewfn?4_bmpOFm@V=c}e_XL--p;#q3Q~T=IQ8^_=Dbigk4U818tZK84%xk= z_Yz9pPo-nH=ohMv&M!FoNL18A5Axc2#*b#T>wVQhuUSiMe8897D862F?TVbU#aYCM zcJH79-txt>ek+sqNZbTdr;VG9jo`L5!L?8~h1F(v6c^tpx^lJf(xvlf&Yn7Y_|U#x zJGO3Gvusgz!5RGyh`r_ej%IB@2Qjw?bJO2-i_KvzD&zxEK7daimk&fpf7-Za<##zN z58RWnZncVqz3vhjlifJ|EB2L16Zy7;n09$) zsk7PmT27=OiOr_$<({)=IF$>I`}a`XkR|nXEQrsUv+?L%;pLG}1Y(R~uk27*eUMt& zZo51jnRe*FE)5Unt~FhJ@wvhrI@B;DrnWf#ZN4b;;z)MFW|XDVxjk<7a&b@xEzb z({OFHd6akVVE4Yy0GJr)K+WxO=#wiKlPiZ4c8U-`Gcx_V)k|kj9y(xvd7u#tkp^I}$>ZaGJi(HA z4xd=eS*%;Le^8rUo7T?%CZ@kO(02gYydxFgTMf*xEHcAtxKoD!23uYF%#@kaCMV4L zVO3&G|9*nVY8vbVp2^LZ54S!ib2^x0)jMT2WGnAEW->>s8K$`AtX(j6Kp$;@sej{u z5ekr#KF_ML%U4_@AGy`GLw(0hoiT0N^eNM)B(C})aaiBJzF?btM|dY$q}tay+$RZ$z<`MI$*cCEojZ(iXz+I!rTsknOjjA<0;oq}Z0sVNnd5tuaL zn+dTQxhXTgp?y5(h3?{q93GzsZrDK;Fdra6E}!;C{u1&TYtB}a#T!w7AZ`M8_8 zmWy$rxyqf6m>614pCPUJ5nMAxl;vo#iPd10DIz>>)EL0%!~QeZ^2l6EgVyRO>p6N# zEMA>TDG0-qdy~JN0z7=u_-_G3b65&hqepWf!?2-)O&$*m2TZg)cWWhx${f4;eS>Fs z0A`-T`DdI3IS~Qnm=(}^w$VU)sU<#maDSPrJH1ma2rW)W&X9@Ir{W(po2(`pCaCe1 za`=NB-oR4-pdo`mHM6j74Rrgss+`u|BPXDp9CRr<{pYX-HL{Yjxv z*?-j3)Ww^RUA%F-?97(Av9U&Xkn4)mzP$Hnntwyx&QZgM4-@J7^x~MnJ~0zHiSgjY zqFZ+_?^u!$Cx`O-xcyz%xOa^nFjh!}LUuYR65X@!kZc?=-3&VOShfAQhN9Hv5mR03~&e4vS-ZXD2hOt z{Mq+*ACAr0eUM`3efy7{KI^CBmy7S-JO67QhsegxjE|i;lSf<}=e`D`{7r{#ZZD40 zq|SYe>`M%tl6&M@*@J7lmL*Q-hBJe5Igi+Nh=U|yX1vS8B+KRWbscBcW9vI$6v+Ki z6w&P4XVBE;Mfb05OpBA+-3*2}_7gb8V`h8;hY4|~Ub<#Sd zRQ8mGx5}4uZ$miY5v#=+W_seG)Mgsu>qib9*n4RH`8v0gxiD*6_N_gJkts03GPHl6 zuSX|bE6#?AimQvo1k_}bF4)ELnYkdITLfn}_U|!%W06f(038l{La&kfX3fCfU-g>4 zzHAmd8`KoXAZUhZVrR;M9tmuw@F!j(n1MJtW?-MM{MYs}tYWc}BE(AQbJT@`N;194<=Jm*q(|<#6DjY)QOhzK$H*2UTWdYP7&NnV7A#eQU0g)_gk+`bYe=*vCCiamNr{lC zNuG%)ZJcn64X)9cjQd)z?w?Iqa&aa!P{wg=OL9VDB1ax4)+EU(E=kF9n<7caP6|Ox?FdG@hvv9hgn1612o>=u$Y2D?#W}&{o(_crcd3 zGuepb7@uJ=gZlUG`Q>K=;%{bQD3w&Hv*YL(e4LP!lFD8jFSL})>Etm@;7EgS#*H66 zhTA`%7sI7C{8!yS`}mus=MkCEN=QtOPfqkn_9Q~8QXHvr&P{4+nwofRJj8Nep_(k? zp8F-9?*7H6Uryb(8DZna)i6y^$%d49Jb^4FMbu79&q&P}{|$LQ`hj7i0KDkubxNT0 z`1r$tiKSVb9gU-PBPDVo50-*3h0Voej_2W+owOX}9m^ROa`KU6YH_27yyusnfAry$ zAI_-co8l@gm4eli(|Crdlcg4Na!P7?MrKwHiA|#nBdhdNlcZ|AY{q`}(Fc8FuWdwN z&c$v@QAy4;G~idI?cs2rbWA7I*|X-5-ZWykd`1lAbCQ7~pJCnq{n0-^owDXm7EF^< zQ&Q0uuDT2y@spaOQk#-h>bbOx?74GB4jgtisTrxM>6w{1^VZ2JOQ2Nl4D2t*4)D1K!so;P{c>W?wfi|q2#KvzRoXGS zAXG$dq34m#0cAO}@~#dA`7)4wY&nQPcC7#X$w&YCc;K|HpKGkI3`jgMP51&4JS6TH22@I!*oSL4l(hTXwG*kM8 zw2X9)L&{ln=6qk-IRfMWgyd6*28^ASvGQ1HMd{)cxKTx#h)6Z2c{p+|LrZ^@3LPHK z%E-!|z4qLNUSF#ohVFO2{P)NI?lXLH^5S34T)SO))MiXoL#>QgKPR5}l~jMFky zx;sNM5VTBZR?h4>ht6M=xt5VN9*t5tHRDs~FEj#IZCW0Jc zC}?5h;x+zjooi$&=F~T+qyZDTozrsj!a&2C*J^8IBE~kCOtEB(%weg|iJv_;dp?lZ z@3gh%H6HIK7c;S1yV4sOm~~eF?=w+R&0D+^mlp(d)_)EAQTAFXTjZ8KE{8^6_15}> zuY5Le+|bXS343Q&Al><|G4P@Oe^aoBR*k7)t+jsDfC=(-bJACBTTjRXrn|~-jqIAZ zosP$QTs0a~QiHXw-z%M?e2vks4IA@%_+MtB{P%uqT=!(fpXSVV^i3|fKeSnHYDW1n z8GYZL(6d#`Ni_b!ca78&WQ$90ZKPqfWwXJc8TnN{#@2(zy%zqr*+7Vw`mSkUL*Vfw zRnavwKt!k>sZ8{$8I|T^@_FZ*FI%>rO!ERGSX?KQFYhK7wzBCYvZB^P8BLm|G^0^- zwi!60Tg1QS_$+K#wg!-{>_6$-7VJCNA8-e-hnxnsH%6 z-ohVz3N&bxzR@L8X^wL1GDYt^P{U+uW>7|ByFtVMC+L&8#=NFwJ_T+jV6vqo-_vE^ z?T+2n9r>l5NkMAViDwZ*f7v z@{9q`s)i=FN#9b_ocA5pY)wq1i)7HuJ|_J+9n^J>8PY82LFFf8vMcQ@BXXTs8Qo5YD~erG?(o6R5Of46GXz; zVhgN?i^LwCzT%4mAKSg&YFlG}yz`|w%k0|?^em1yDwEMD%}t`Bm>n@6wfNH;Pq*~N zHr}b2N1wd1i0`m|t4ux^%tu2^&0wg~I}mF^DGW6uZyOqvck8RF-YNSZ%IS94x`<*8 zMgz^1R)Lh_1kulEPzE0Y#K03JllVG)dztANGYHMuXfIMmkB@|rqKQ_HqCVnggIOe? zGo$7sCL+B4v+>1z%!TW$_BCP?F(l1whzA+JxWZtt;6bi3SP;zYcDS4t+4%B$d;kN( zAf^z@&{%6iZiCjyYbseFu+hhiy=AES^DX_Xy*}I|9T6C!kqKg)FRTenlc-7f8;z)` zn2_D8WfJG(Y;f34DZ@EsG(&?(nvXshy%8h!5xHRqRamG0PRU%p_@U7@AC(N}k$QM zuv_DGABajKW3y%=puy+Y>+h?vo2499+eT<`x5%dMYoRZ_+i&*Hqi3&O+wje+H1+ZI zH~E-N%GYD|_4AWGFke`C?ADQ3!7Vm~xTyWx-G4rPrDRvaKZ3apqo@f|Sjn%yub)#k z!Pr+~ZBmw0+ezqZkIY76Gu7&yac4_@AJEc=o2y1`Bww@1?Ca~}>u2_b*e|HpulM-W zQH~~PD{PlBd^2tpM7Am9o&J|Ee_%BEYCa`2Hko~dA9Q}uoBjOcrrl=J{)bGnSQyvr zmDVK^l|(%sF2-)}o<7_e4$8-3G@Bsz6Jh-|v%!}r^fhP)mGF2!|0-KzHGz7Maz%A|^W;FI zxKHy@W`j>P>iP!=rB3YcF9FQRDaYx`5Y~5Hnt}Tmqmf~vz0vnnu-QkMntU|#JqXSI zfx^-3=NsVX9~cxAD0d5-Q_tOqejp@Rxy>}8&KYCa+Y6_g5!)O9uRzS?>k|-&ivs=p z{rvp{0s;dAxR*AQTZ(>D#lcs_-Xf35_~f6@KWFyw_4TKDK!6|mn*9R&c?9qXQ~`!S zgRwyw2-aH0zODn&C7(3P;Nr>~{eMSae|LaChSL14^x_{7#GgPv6;Kl-%q4JTJc*rn z7z_>EloWwyG)26fKUw)IKMP#_0)hhI8{`jNpl@(MU~o_n-Ov*Ag0o=o$Z$e?84nK# zRMV4h6+Ru{4`+X0v%kMzpuc~J=I;s!4iwRYoPn^1x2E*lr11{pN-U1?_tOZjQWC;!e^%N|6@`K1Fy6yOQ;vrqA$TwGw!^M7PNCN!)0z-m~ z{$~S%LxY0@f`a|!e~8>_b(3LvMvdGt`AnCas`kv=S2~J&RG=*=kRC!&H%JB4K^N#B z5)u>~5)v9oI|KJBLe`+nyW~Sq?lOpDe0#)}ApAuK0YQE+qk1{R5T19lmfzRTWen5*iX56c`i|5)sinqPa5Z z-fj}@G5A>wfIy~Z>X~;7JJJXj`v(P!se?krQ#=ocbx=rnSVZ&YZD9^F1&*45E~u+2 zM&pIwTN5{kaRUQ_RPf~xadRjy129-bSP(D5_{tmI4ieq=ax<1_r#t>{Q`(bno*0OM zMCf3A6;61E1qO$QhX#j5grh)6Xhe(F9lHuWpN(>Fk@%6DG(+29i1_oA!d4+67(28n zOa+w(1S2tGhlYg)hlYj(g@uMkZ~$*t#wOn!`9M_is^)_XneD*6PiVPhDvbFBRwv2@MYmg*w;~ z6c!#1?+`825dI*lWt)!A|D=;+d%o1k_(XKW{lMqm8grrauR$G_%M1^T zjEHK{y6p?!@ikY>q+5%ai+u9+zxJ5A?b6LduZ6%>OcjKtfC0lWwfqeWjo?pM^XAZZ z{Pg?fA~T=;_&vDei|>6lcG3PbS8web85I&1B5ua&(UIW2#c zMxh!i;xxS+N~M_K7wj+nW7Nu@x9vXgIwgu;4kW#*>6KuL76rUu>GsZ`@3#N3_wcFH z=PyYm8$U_VdL;k|8s%eNsC~VA`qu3Sj-NSSc(v$C;YC`!VxqE*@2eEo?`C|V`qhuV z-LPZdF%Fw&WB2+MIr5%1uPQ3tn5Y+2{xa!Fe;c`c+wTWYoV$9Xq?B!PPVcyI?%Zk9 zYr!OcDT)Y{ztd;luYVjke)e)vaY^aT;u4w^UI4sw;+T4k0|_W~)AVA%D|K)6NZPz@ z&(Sj%uH7gty{U@pZ?J)Vncd)1?3V7MDAP*;-3%`@y=Z==`*!b?=H_lDnrp8-c=hiw%YNN?;N*oXg~d0o7vH#s1Xl_R)g_DU z7t2mF`>8)e^0Kj;?+ev0z4A_<+~0N|I&uE;HQ9qNF1ewKD%r%pAONou$3P72`F;D= zt(!Nl<(z|8x>1+s;;Wy={kVPq(X)l3V@a{DBzxHxFI+ft`q;6SW8$B{{#ffv5v2jT$JMK7@YM2)%58fNpT1BCiU@2T)YsJIgl0>4 zcH^r2Wotojfr6^i0fK-7YXGfOR-7-ylj0Xp5js-f()n|zp~d&RckJB$>n}fUED$u; zwgYF2$^_2J#=Wv0gI*Afl@+&2Zi=zM-3dHU;Ce*C!F_vnb5_ol&70P)Sh^Bu*Qvs? zibp`8EI=h82QMJ z3bF8!!;k_wz{EeUUx}t$_nf+VTZg69*B~jisaZt=USuh(7kE)+g&u*s2>4%q{!yH= zZu1|<3QO+;sf6m+Q$UIn)c49hJjH^C`U z`;galck65GsvmI<)TVVSm#)}=URQ2b02mWo8#oi8v^pQd=!}nXLLvh&3*M`*Rn@={ z^Ve_LdF)c@J+!lfGpp6btkXTHEPguVMSwv92+=I}>+67hRaPASk6IW6$cDUKEl3lH zF^C{Gge_MET%-e5bSPK7RU)#Avr+_qQZJZ0(FueKe%FCw0uB;03wlfEDIgm|y|uQE zNUp3DZz+pI_z*EV;7?}=XEdrq$a=&?RAs%R3nH@A*Vi%%A3Q*1F`$SEb_mmIHi;D; z0?M)KfT){L)yWvp?F6ZJ0D4h-;td59#}C;W1<(gp*oy@eQ7z>NQOBtu07_}E!~6tV zB@se>OPh{N6jTi|Y1X@R2J%RRL>2sxRUs2K06)qCpIgVE8IiJB7e{&m3IqiRHG#YE zT&z#v!O_T7x)Wx)>;lb_HX@j21rl#%5fTuM-i3HZ7gn@;fP)0zhg}qcHOfDESMLlY zR z#2q519-+F4yaceOq5Qy1$JueCfthA^>z5U9BO;|1AY~=X%`}tX*Ud?w2wQ=)iU|ce z3J8t4u)vvY*hrL-(B=S!DjT+Rd0FBL7#M6d^0o{T{vomezYKO1ZJ(%nL5G z+f1xQgYY7BAQnmqV1*4Oez@{jIg5a{bXGROfZAm^Ld3ulB4Uf^WjRj%>#B-D2mrd{ zs@)5RR663Vp3l#!DjT=UCT`MV0U()nhe*f!tjoigm25pY#I`&n+d)+|!B`(ZI$TQt zVK&X~@`@~oFhQk>*Xtpn8^|p1!k9V}tZj2VRzL@k$%;KUEm)$M#2Ed_E4k-9lrgLi zCocua043||Vh))x;ND@fH#)3lJ7ZC-B6PYnz1B5BQz926_GD~`u$6$X94@!V!x9he zp&+bj=#cb9~*SXYXMX5@++~kdXe=SsURuyfJ2& zFXhbYdiDIe@9;?pxeFIA`F@ntOB))}zhU?YT4*s;`s-`xQ`5U=kKTjFB+OpCWciBk zvuBQ%%4xyd?ALeT=oz_7mabU6VqW@Wx*IcQ#3+Ge z4IVmZplL+IASthA=-V{V98)s5U*AEar={gBS+Q!>g3QTMW_0xEv7-P5fkFaNGoY_= zMC0(mW{R#g4boz+(!PJp#N>tfEAm$@$(%86q#&?BOpF{coGocdItK9c)d;D^Hq_Y9 zx9^?7Lovg!Z&T(iUAD3yf7VPu4lvO1FfnSVh5cZ`5`9hX@t3CI4Ke+oxxlV;p8=z1 z&ia03LBYxeDc>R!Ho+)BkA@8zJa8}>;N-r4)xAgep1lWC+AYT1_bfT${fAG@T#ONa zSd=w=9GHYrz#DK&aGITJf-O8plsn5NJjGY#jBJe{%h<(d?fAw|mq2H$FeYbq| zn*3RD0Jx|jiV1La%rG^$asavCi~0 zy<$%CM8q67lAL6mGIYpL_V&q%CpVr#6FqwN8$LNcW8R8299X_`o)%M#4+e~l%fU%M ztXh~p3H3nEjRaH>Gh~Puk~D4AXEAB$J$v?!nK(0b-l~n8xBR+sX;$3SDMJ85j+~zH z9fzkEEXtmWIY)yE5EqcJEe6$L0m$2 zz<1rM`6-jes?iC}){Vq3K+nJlv9%AvsW+bK-MvSTzA+QybMm+RzW30PovUZZ3*JdU ziXbkh&sdw6HDw%7Dxf;R_hN=&FDVN^34pJA^^|=5q2I)1F4?^6(D74;eqNjmCK5y> zP#m2eu$3Oulu*T7!(xUH1q&f%D0;Ei-~G$pgU8NDU-Z-dqo>cG-M%6{Zu&G(U^8N4 z)l3fvtr?(nXPjU}h|oIh1TO&tlf-&zKMWW-HD$s2eJ9RcDm<`$9+0K!vD0P%A|vfN zE>6vC0^rsF{!ERyP9zdj-kJwep8(`s%%p_bKkPbw{_2g>zb;OMbfy*uv?~?_P%RiF z^p68N3tEgKsDrPI!i;?=DMmPKd|X!kwqxfDuV3D^GS%A(eZ4Gao&jUU3-DTiTtQw^ zJcP;}{Un%scJDoC^mGt=tZft*9a^6&X2w>=nT9x9EZ89^<3RF&)Vhr#0M01LfV|@0 z0V5|T&0D|!bm8^V6F)C7#x}+Y&}s(QE;JS_TP$Y;3JeHh1ESoI1H$(u#Vm$Sn3=nJ z_sL7wZ(i7upBxvb&9HM4wE*pA#G2yVfV*bSG{!fevYL61&Zf_d?cTlj;Bn|Ei)lBD z_O4C`JsW2it0E+PXM%GQ6G3Z=w*e6YMwbZ8%NSQS3U_Wfdj4AJ&EuP9&j1`78y_z! z(iYj`8fV67i0Bk-7}5$#$`ubp4E#^LA@0$JL+7p*mz~?TI03s%kBvv98ZdkCir3+D zD2sY3-jk%mts0Y(Rg!@|ZiwpfaiwKfcP&o>!3+g*5`@B6AbxZer{Z0x3RcP#Z%6>B zl$5BF8UX&N1ddf)n<=Jw8~v*p zF6;?M@OJU|5Yr?Og5r$$Mp0BtG$fP)D?@XLqz`&Z1}%mqiFAP&BC{xGm}y8bBP-0} z5l$r<6KqKW3xjJ?vX*3r27%(lDYT9C;C%QP;gh5#+6bbA1OQbDNh;BhRDqoFf^kX$ zLZf#fVTP23*n07y$S>H*Bn?I$=yar>MT8Tth`L&GV@i@rE(Z~&1I={XqJ@Ab1(hfk z^q>?_I}a}swWPA-6!B>y03)D9Fy|pS9O&Z`2mrfaF2Sq`uCP{Yk^ng-6%3gv$tI>1 z>!zfsl-gu@BFHW=c>?CKCx{$$09&V!VJ9ug`K#dS%P8%iAMhtn=@+#Rfv27XGFPV4W@?V z2KrM;_EZ2=&_bQon4GGml?$p%w>QwoghW7)A|*N`Cix^Ak{ak%C0X!E8f2mxTMBJ{?#X?B*h~You?#&X>x;eOA!cG+I@gYK(`!l9TBmg#gf-O8^Na z?`o+J=s=6{lcLg_(o9HNi(>>b zmXV4i83O-m^8RM3EJJ1k?_tW6##Ag=nFgT+gPUZ4NI{ zD9xFUSJ6<*XiC@8s--P#vNEzWGI3H)W=^Jt;1*ibQ(9(DcFt_$EJqHdDiB8JWfYU7 zT}jUbCznOdm#lOc2{)L+NWgDA(sF0z&Y45uw0UYy{j40W3Z@QDnJVJ}_&NZA*}Q;+ z(j$QUGBW^FX4lGdE>nzT7Q}gK;Ug*EIERuh83csrmubvs%0iP244mDBnQc9cSv`$vZ}gTdstC@XA5P5^XAW`3`wSFmx1xK zRCci_2`~P6^_|Ug3Wi8082tg%aysq9`}coZrYlZ4oh%@f;uJiSouzV`vehidoLO_` zQi5l$Hp?)(4*6l8m36tiniGjsjp0%K!_qbR-y^(K#~~b?cvqBEIqq4r=TXdazMA)7 zK9x(PModnw7$f~+;a#SfwY92-mFW|!mO(z38b460>_@1JoO9;R%gbAkmq#U7wa~F3 zZ^4{7vvOytY-eW1r7PF&)EGH!k~QLARxe+&a6WH|npi7aTPS52lMK|u$kn^u<@62JM^Ynqnw&T6$Lu%PLO zfIQ3Wsogubc<#NPg9Z*B%$v*P&5(koW#+t^`SJZ;4DI;V7lQ`J#Ps{i)1kb*GH{{F z3oU3`tmV}#$Qb!vc)M3V89Zd@@F5?*5CyKoOp_n{s8rgtFr=V-RbF21#1EreKlgs$ zAu%IHeDPXavkyQLAUviuJ_{-Kyg0bv?uw+Zx<)|y0ItQ}tjW)W z7K$RKO-uX>9<81;=FcG=UjG7SBM1HO^Wn6!0>WV<)}#Tvp)GngeQz$fzj*2gE!w^O z;XoP=|K#N;*;ChOL`x^n!OHlkHB>vzr+L0sa3??Ms}3!m`}+VUV!^=ASKEzWCAL zk)y_r{P(M^e0=B?L^=o$i{tyh7~b{0Uc*Uh81%o- z2Ae=rfM=7 zKJ6ARs(`{1V>c5V4E%(&STp%XKlA$EKk74)*~zpK|9S=((9-ySw`%k9$HSOJkNxtE zmR@09C@cH=ssZGVfPN$z!aKkE?niwm@==%``}NC_U}J#sG=J{>LCj3Y_y22Ww&8J@ zjwgT;ke%3A+*oDMf;)73o3fOdxpU^`j()$R*#~Hf!CdbjFnaX(iNpT&Y_L&uydsbr zzzpD5&>8^%9r?9>?u~zZH7U^4M%{1i`{q$>p?>;&^e^tSn1tZ@IM=~G4hi0&7X1kxSe*n-P^fc|!Im(cbpu?qf zuSGoZ(qBFwkyWti=dJ6ef7lizmYI=YGzwseE`XN-`JplPh0cpE5}RqNFsS|WZ+$c* zW!;wTzweyW_fLK_0ET1Iky1Ls4BQlGmLOp!a2o-GUPA{T&A-hvZ~U`w?5bb>*nfD< zH*W<4?xL+A%S@o1gt78z@Z+z4Q-CrE3{#zfDaO`}zAc`9?QcCNE}@L*k!^_|H9mde)ZT_Z{6gw@(*|P_Y!v0m1=J^EZQd z0vIP~ErEK0hLM=oegbtH)b9DWJ{+97amU^x$CrjL-a*le*`8v48BAN9)>c&*H`)94h#UWCg>xu zJ3;9`?KyJ#?D|P>1xU>J`1vT`S~Ki%k^u0q7`H-$pUpRr7y~mE7_0&f{^y|wxD*_8 z`ohkH55kF3y)7MxcYMWY@(dgKGpxY2`J%VKRp4}(JJ=s+S&$)Q&sXj7n0QtA!Bw!1 zf(Ik2b#ifXU?6`2fJ=p7hHn`R#z#a1GoYu z1w749K!cb``M89P8>(+Lec%}oX#|zOUr->gOab*l@YTUXBC-m$1_g(S!qAFez#0;T zct#0;E|5?TM1+7x;InWqzd^!2I4D#Fc>tz`s*oy)V|@$&V$;ozQ3G@&@2YEm( z0UgqUY@m)p;V&`gYX_qU!jdS$K3w627SIrgl>(dmwIDa%0V5V%1kwmYV=`e2(n>7M z-NIkck;Fc-;x777fClMDv{k`|5JRv769utlU6Wr(bd zii~Q}0v&ZjiU7PHp~4IyTJUAKzy=b6891Jfp+jwgJ0dbFib8ur9Nm(r!Fh%VExaTG z;lPBV41Nlha(v=w0pek(h{)zqsQZZjVBwm#XdczFd5cyNkrAr7YGFW_E3^p<2lXc` zql7F1m?QS$PdLO;k>OE1jiQ$*Yjlg|EwH-?pjsVl+qz|o7Evv93c>t|OZ9#t5E9-% z_aX?Ei0H`Z$jB)8H;2AubacxWty;I$I#qUT*ScM+R`eVm2^V3Cb5xi+JQ7J%b895z zq5#B58)azT2;1n>?K*Vm+V!cYpL()Om(J~5BLT)iMD!#s031d%ZvptPIY`l{$jFv0 zaaIehxgn~gWmL=PR?*R&o_O-v=U;fS+w;#o^VAbX%O)3TJ_Z@>QX^G|i{*t!*MqFU(oiqImhI5}E; zjHy&}LyM}GC>Y%WCef{0!u_?p6IZUPYY#83{Pe9?UwEc#=XP9QuKB{-V5 z6rp&uh}K%T=nk8!Rt~h^TX^MKk#_y(qN79Jd9B<3JlVN*Yp9VoJX#NExQcsl6aS)H zHgDc0I=V%xmMx>(w&ri^3s&EqSec!el35=(oB{W{3(0#Y(|L3`9pY7TO{o1u_+q(6djaz;@a`Sds zncl(gpT7U+7oY9gsVx#jtL9f)v~1O;ea9|OKlgmMmnAslP|ufNeE#|8p6Y@QZQHbM zx9-PZw*T>Csk(Eb>^2pDW`FW`>7X-RLrZ+M>d>X@Gk<#Fh1cJF<6RD9eeaz=zxC$p zuXg*>(@%D#hYoEMnk_tb;`DiNaEJDTyxh9w$F(cIpP!o=fAReJbEl3Tp`P9LU$*?T ze)W=~XZz0^Rxe#tDo34{P;T!$K+(eocW=|LQaY~ecFB!vg%<>%O54rr(TBoW zRO$boU515HZSc-bDJgcA+Jw5iXi-6}M5@|x^~`Z;ix%`N6%@-# zuU`=v9$)=o>0(X}FTd@T3~=eeJv)#rpSE{7E*%6XGJvAo^P7|iTvWk%^2ku6U)41h z>mb=(>XXTV?PpHFjkc@xeknqUywnT?8|;-~@dIi1pS0eA-SZbw7EtWNVe(Rq@DO#kM2ZLH<(L?60Fyl-di;#UizzCG z6gRHw7DkVqTQ>Z#jH~4oMw6@OK;j;?KfMBJW|5fqpA1?w83i z_ri^&8_Idx>PC1@xn>Vgpxpe;YiNm^X1xpvv zFHxm)leW6X`HLUO)mvg=ukRmQ=fOR>s$25t;r~b`(L=ZwUAcsHjv$#=2I53|5G^Rf zrCS}z$}tb+-ozI=H#8Q>==Y}BjH}0_!kB)Qj)mKm3+Di=9^Aci>qe}u%PUt!i{pB~ z1bPid*Tog#Z{3^?gqLY6CvT8#AEK*Pr+3H+1~BQ{)(-#H83sV<^ngug}-xCdVda&!%K{TKuuf<3hFV) z)!}*#dnyZkJ6VcpjHjv)00qN{;d;-M4x%~<#WJXsGZwH#jMQ!DP4%Wplc}IX^RZ(` zqlA>9=uH77&YGoc(0Dnv6-38SDopg5P8ZXp)X_H-DH%h7cns06H^q#=RM2=lSE)%M zZTs~Bbua^lQ|Ln40;iFytgTI_0+wDU1OA=$ zSgUu3`^hBdMq|Q98zV{n(?Fw)7!&R>bmL?Js++!jFT0^`a^IxxWJv z612EGGp0_Xc-aJMbRh4rK?CT3wiNJ3J2kP4lK|*oAUmiOzX1zC56CcX3g;7kxF8VJ9dUPy=k(Vv`vm3ARfRQ&=i=9Xo1FK z^``n>^2|8)@~2~9xjJ(A;6bwEi5g-y%z(gBG-g1mN`AFj}~r6QNt=p3CaSJ|19wuvgP9smy~0El}AWu&Rp@-()uK}XPqT%})j+|tw7 z?*$P+S-Gi`^|nLV11n5Rm%}9pK;j+UM20N3AXCK*#0hF}UIbcXW~%gkY5F+`-s2)T zbb_l{X^6q@Klp-J&VOLs>sMuyj4fVK4;RVR;k3=vG7h5#F2bMRaxm-gVeB??P6W!a z-_D59wcx63v%yUKi7lso%cgRV9>x1Uv4eSNI@)2gvlB}%hq4K**TcyIIgke z5yo6url&1g@L~yw6&P95lDshHG%Z`PBF0tL(O7X%A*B)0wpAOJ<9vK&jmSSh76h0^ zwrJJ)e`LW<`96?-naziVwtDKNKfm!BOOJXFTA(|!^Yd@~S-qa|()0Q+qP%;sA}x8|w}YzPq-6pLESmucIs zT}K$Pnj@=eEC;Z_(u%7cI;g;Mxv0BVOwb~_P5TZVS+!xUGDL+}v(OMJbEr1$+jR() z^-t0P!<(3t)}w7p1nb1YOfc(S*TTs~6uHort9q^^tVAdTh;G>yZk@51TrUJPx08KlgC+jYg-*T5e3jI z8Y5zbPEWAfL%2jTk0t7np)FTCcZp!_g+&{g(6?-*S|4cFp(Ab4l-W8)U@Zb^nK5_l z-1!Lv6(z7hTQ0+^eTU9Zbdg1$NV>%?tyP;)b=Qi!ycVcm)6JmAkO1C0Vdki2h=p<_%Fl&QKTt=x*9XogA>Jwe*mLA)*?a;9U zygI>(9=Q5cYgR|vwrkf(b!hC=S#>JXuReo82%%1$IiyGg=&JYdjBp~fJ9q48?4-I{ zo@SwgOC3?Ov+AfiRl@rz7EGR@QF{ha2T|w=)y2@&@MPoD&s<--a{1!CC9AgVBR{O7 z9t^lQPrwEmUa|^!4We5EaIXXTV8X8>qFS+xdC#4HxO3ZS9l@+q-4;meqG+PD-Q*MMS98c(6eRi;P3T( z8A*Zw=uaMpbVRQ@tX&hFy{(b70*|EfkOlXc&wtx^=#T6hVJZnS;OAi{IV0X06ELUL zt=@~1mXd2>v|$@;gshS>BL5XB4iNEDGD+Md5C|E4l0jyxm+TYDP}>{5={CytNW>Gp zbYlKc+E`MQnn>7Do(H^3qMa_GZWJ2G3sM0j=c67gBcV6RK~K*ii$oU%!C`81lbAp* z8wn8JU(HU2lq7gaT8VHJYWat_P%4jJ``t*Qk=T$Quey>KAcSa!VI_JzqCWkTcOYcQ zBPny_KGaLf%uU(|H64qQkssBOTW82cG8)7H9%{Es*=Qn!ex#;N4bC4K8 zDoOFs)zf1WXA6xC7K~++Ytn5;<}eU*ZH({Pdb!lGkFe*9u$lJVmU%q8c6( zBQnVv%JUF`NJP?|Eq39TJTLUOlK3RNG<%hUlohcl#uD2~mWfkQURv7gp@gzhHrq*t z5k3$Dk~5<0QgN$V&q)zldFZq*Q5Pf8o%kLD+w~+8mmWD{LJ~tBlKVoPYss9!Ch|{` zQJhU~-j?aqdV#zqNBp%ccsF_2RR7p2>{dGN~{M(D|g5;2h1 z>GBvGU9g7_ae*}DG}8N!zI5M1CO>wESx*USl)A~LMk8ZiEGzjz63t>sov~Qa?&Gj< zMSs{hnZnfI(}=GasCxXlB|8Z%Bpxkr6LK6TA~d=@<_2@4nNy9pDDQ|cyoH$`NgImP z2?HKZm)nn2B&k=<`zEPwrZ?8R6Ah)>ImXjl1~&LN{$Ff4MIB;66H7tNOU=>b4yMt4 zTFH13=j*MIK<*~+mqJp=Oue>HM1172J(5H$e)NLu;yXf1tR;S@Uz!<7<8svAyDO!= zB%x09lw34=^tLeyV`o@{y|KYr_u%$@j$m>@6En;CO^ps|?g?plYV5R4V`Y^hB}Xx8EtY%i^Hd(+F5z)Rt4vb zkYz=4C&^zBP)tXL8=^=q8zGJ+9!c6#l=H%84IWSRLtIxQJ`y%CKtSo*LrNiZb=(;A z4+-uZ5Xwky#Don^RKoK}g>XqUBaWO>I}6Ts?qND7J!oWjLFOfG@NqZ5LB9o(XC?|! zXG?5ql#=({$8C~-CTvjAp*cMcPlLzLZE-oQToc)(2(N*fRJ_lJ9m;b~7O~7b;w>`L zH1x7I1iC8_hQZ_HQ(Za~?Zskf;Zp+=`2lfbeql-(T1MhN~idCYf+%t8db*)WQ*RLwQ zRpSb$P@Ep;5{G2#@nL~pkNx2kLBt{kIxjE2k6H$##!&p_4HANFcuT&iVL`Ei&WJ2a z)OA0+dGnzw7z%@vn=?j224aP_A{j3+o=xhgaqGt{%H_7+FD|zPx(PgZHt{Pu$U{DX z^@c_Vc6B*8H4X-kZk1L!{r>-&OE5}3Xb!-E00}J>#dqq>ZXX=OcL{>So6#r*{X`_B zk#UEe>m|}1c9*NRtfZ3jt{O#L4a?wG87k705u_VJqbZ}q;ebw)A;_Bzj>=l2n~!q% z*4c?pJpiSl-7GPI;zBHA9c6Ht9Bjp{RdbVQ3Y7$wNN;d8%E z5vg1TkGp|#;e1X>$kIf-k0=JZp)4Ov7%DE*Ro>njU2d{A-82>R>hhwh(8=%-p}9AB z^`S%}T6%9V!m0s|Mo$e*aigx5+!GK+KJ8$`%|Qma1EHuiDOSOM(t=;APkO}8($ecr zzCnl?gfr}fR$|vf&*;Ku7F1#vlZWpB$OaL*(4B5-B7m+%Vo67ZoWKxr-Iy9*C#XZ| ztd>5-%%THQNJ}}c?!H{Yocdi&i1l^|!Z?P|!Ue9-Cw~Ayy5xIHjV>R&g%`0yTRB{b zhSJK85lo)zpMU&W&w*p7<^Hh$Qt|D|(t~TVS-@hA53FTcnxr-eK3rvZGRb^U8D}JG z0+2eeaB2n5KX(3Nzfn`u@^_xQcB|svi67^Zf{;o=gybBM$N;iFEmf1$14&uX(-z!m zSwO;7c2&--x${q*zqD}6>B7=`56*8}0#hxeGM#_m@yU&Vj~ARS>>IP_<2Gn=pz46l zv#EePcfq*}mx}LJUfsPC*nVm{IUXwQA;}Wd5|&g60Om*vuXIDEi^EWLHL3(~4u*Qp zyzkCkxO`|`F2a)KU`TiB9LXd|tAaII8(E?$5c$k>0P2AHRi>y>{}{SGFaO-d!gON_ zNfK~1q`I(D8mo$FZar0lECwL^O)L=_vOI$4SG=_3WX;OXoi%U8`HO`lg!rVpM9eg- zYS&dw!>(F}9R*2xVBJ!K=4Lk1&LU-De!=-mS6KEG>!wH^LkjwcCCM77lIE;3qA}}_ zWE6{UDI2qMw5-DdhUcO8kd-ai`B`KSNCH9b?0E|o{Q|kTlH3W_X303n%#{DKqACd? z*>1r2SU0;=xtd^% z2Iy3-VOHgwd5gaP<6_}8$xeWsq=t~=l1a7!j>*h5W?2M)&r^vbazhL`hS?1;RJpaY za^}om_}y-Zv5aJt93*{&^bmR=F@h^PA=rP&v$BQ1NSd1~$JgSfS#xG%?z(ME;IjIV>@1yN3vRW1_`u@f5D=q$DlW6RthmuB*yj9X3w5QiiRD>%%bgV znBJx%QYGflmn3e?pP%<#{wb&-5;~RBn9CoRZmb-Ydz>e;Br4#mpE;~6Osa;&=iE7S z7qBt6?3~c&U_0@n=$M_YLghO3#EIFnRc;wnbA>rJo9888L9krMD_BmEM@4eGKvSIhW_j!0&Sh90a z&RMb~N1VW5Oop{t7ECfr&R{UcfN_+poO8~>*|LHa9b`GlIm^m9&5We?TRrB!_n-Hp zH0n8Zs!mn)>7MFeRUzGg7-q|MeztD0*g6iOZre#j-2pA_j(6v- z>=xZ%BSbUjc;FSv)>GQQP>G;IOv7lI0rr7GLFB6hN9ERDCcUPsd-ER_Tl8B=nn9mi z_`w~v?y%U7DBDLNQ+MhDbvru(QF3zW>02$WWb|})30HA>W!u=+?H0%=a*o17*`e$- z1PUjTYDRViMwcABdPixw-P&fRlQRtU2ov-mnJltC-Hu_haBxFXao{(w9qj~$XCApg zH_H}XtFCRRokXCnZcQa7du{-Qw<=0+DYU>l?-T>JHt` zezJwc@dJYrj^9w4|8F{7=Ku{-CFh2%glsV#p0XXKZ0FZ@W#=G?MwlTmaQ|&RbmkT6 z+7*`Ae6}nT)$p~GL@P0zg7%dihQJ^pQ3J<<4o+xR|BsBrt>-H<NH5-PPtZS5qVI*d|hMa3n%S%DBJJNmZ=24XVY3d)H=RQPV3pho$W!pg2aJ?f&zng1qBC&1nmmkJE6Jta&hpc&4JGvBqydOr=_Q7 zDCuMPu6XVaD0b2Md57d%1(PSWi)5{_pndx0&YNeGw+4rXhDSt3MMlTQCnO~)$zv(0 zYH}~93F_cCYYmk2uplGEyMmNpUC3B)kUnq;g?_>o}vV zj}4U<&~c`Rgvd`=SZK|)dux>13zdb55z$exu~Ct+aj~&+F;Qq|S5OFS_}Uc$9fbOj zQAAb3Mnhtb{jEl=9WKp^jgEi zM~S?wAy`D_cW4+1kGu{EQ;|mrAN_4xfede{*_)2$kOjF>Y{)Lg1P6=Ep<#?Qg~^}r zk%;i%UpB;*-V2GYZn&Z#F&1^8PhE%_dMyO8WgG&r6~lx4N^HXKE$g@CR8OqY*XrxW zYV(v}6NW(EHpC(EE-W0Ph;Y^#84isaF%S+>WK_tnKSmvxfWFi^r5<$z--bg-Xhc{9 z)X?w`Y#1967H&c6-biG`_8&KA98qDUK%xs73J;a}QIis;3mIWF?ir;-=)z4AVWE*q zlrB;i)f*8V9T^oJ75dxyu)Q^Z(uR(Q=|gp4q}PS@iSok3BcjB?B9zEMe&gd&(Gd|A z%wP=M@M~Iys<9k}7q$q~g$rRnR4fR>$jB%)(nN=Jc$%P#QlfQ`jzmX=ZTT^zM1fdW zuM0CWJPflK;1JH(a9zYuWHcv4R8$0vg+v#viyVuNQKC-aLO5+~Oz@^n$(6Nrb@lbA z0(y}yoCfV-GDnm~8l^<`%OsJ}{2vn;sYdH!b&{zV9Sf#1W=Byi0`y{o@CeZhtHTVD zx`?r;Xce9a9ubY;%n)-=v`IW&jWfo_oji3^sncUHU8EW@4#hxJOti=w#czc*j>pJ4 z4KZ-xtc>f5<>ZQsZ#aMcY<-+&3R!Ht z5<3>xpq#&Rj&(+g*+q|7E+Qrh4WVFBX>@l?luRQWqoZT_wul>xjZ@>tjyEai$D5SM z;cym*Ibi;9hl=HwUJxOfpDE^cD{c!ScUJ3oY<$#*2m6vye;))^Be zR)`VVVwLF8*yt!FMi*;{k^L{uB7Q8cQEf7vN2B`4HWnr3zyh+ssEDXIcq%dDu_`2^ zF>x_U9Dmt<)C4tt{FJ^)#ZcX3mPX>C%*fC2uo$~dbR5gU-f=N8ak0FD%7mB+O8hXSi3tfQjryijs*a>N&-t(Z zkXhZ(bglA_m5=_n;QvWvkOTm!SgHigERp_<4gL$Fh|gC*sG$MJuEfC9YoOr1Ok-~y*f^!PjE_umh|T%NvbPwywsu#SjL zqI$^Q^@|@62!iB${Ffv^G*d8H3I`)NkbY>HNuF)rf1k<!ywT798xkwISsOdpOR{w6|xjdN%jXYm}=s}?DPt2Gb6`w{D4bR-knUGS+WNG}* zeUf@02mbp$)UDi4atY7#5g%ATPs`CDkJ103JlZ^E=Hlptbm4>O|C_g0avw<_WQqJ;ax(-g!B`{ey&u$r5z!w& z>Z=GLc>p}X2lVmybgLI*k}?2Ri13v0{-3|A_nYALkn+IzKa>Z`9s*c^rm?h^y+DQv z5EKsbWd0FA6f>+>$0lczb^>b_Z+0!13(f)bh4Mfbynt*#uX)UakCJoH{^&ywKPFG> zpO`$;=B?P|EX?#j>V3NVL1)}Q`uF=#vF`pBpa+nUGVw6nWCpMhc#86{?vdjU1C9Zz zG1F#UTuSzR=<_}h6!0WvL|ju-nNVZof8d)BK77xEx`%GSQt}rbef05ZR!*PAr{+jX z1*Z5P$xIPvAX(vozduZd1z9CVQZUH$cyItUX^9q(KJ=*iaMz=cJTcwU;hXrhockrW z1kK)GDS!R}cfnu?5QSfG;x(dcJRf=ZVX_6x=!(amm}cqpQ$kwq-({eB|H#9fLy)Tv zb&Godk9hczhe$|4P~{(k_%&+OYJRkuC$I<(3(0BGNwcxTJ<~ zV#$j~kzRSc1#?;2dqgH>zg;AfEULi$-ka;gc{YwF4IsiJ1mS2G9?{ z6SHg_d~mh`7z+*x6>Ds}B#}f`2@&dk^s$GPN3T4_(gkq=#DX`T5KPSU*)|UI;*zt< zaADGK9v&1G3$uqFQy;;~teIaVb|{YxKKhuzhJZahqCR#W)W_th5W6mmPp!HP4XbDL zV^5qla)y)K!5=Mm@(Vs_3e93QUd{EGe=gAws2!@8YC6i31}j4E5m|P z0sb*rKc#ExbgL)7Nv%5BcuK2*@X$l*!{f*}0s)L3v3P7)R4X7BB8kJoa#6j5`qMJOn#nVjS#Aj>mF-I$$~fm@FK_&`&GZ?*Bn<) z^a}N9GJ~jPj(4dKH?dvdRIn5kN%qX+NDAeoM<1Qc%gIm7Ui4+s;bY2i-3eVoyP3L) zlHph3{y5)HJgz*Zd-Mi`kH8*>cmflF{{a^B#N(5^)`skNMKx4JSoxXq(PoI9{;h1JT%{k4}0*P9HE$lg9Cg z;mK1_)|AOpKve&Ss{9dD>k`);|< z{AJIsdg)bAly7KYCICRJP!xoQKod-~bu zsfqZ~%bcMAj3z%mh0_LLTekp-P|g%Gw`PDi`sY1ITj~x(taYEsso)zhZ^4sGmMni} z<*Jp>J}bbi36peFI>nOU(I&xa+T1|Ap(~9XiQKkwwN+44+z*4Ex#-@CtKiXZD{>o&~Q)d$@ z7S5kPcdjf8cNJRTU&<}ncncOi&Gz30shG52~y>#?wZMc5R(AFPcaka9tv2#>iyF5HS zeSCbqJv}@y0@j?QpWHG9ER2_}C*rIN|^6_FDJ9~Q<7gtvoHxdio z+}&IR=Y^b8}DLFu;L-nhoc)&Y^7>|I=4oL!t8CUZ_t zna)sU>hKgai-M;q({(e3%!D)MMBPi2OtYsUo)zKFNHuNNOkN4rPE({xLGY? zuGt7iF5XNlYZ43r-;sT)%pS3_p8ePpOTN)-wBy9c2pXceVF;$ru$*bBOdpypz&_!n z&K?F}XK4wk-g?B+YU)GwtACSFB2hpBWfJS@6e2OybiUBb^jWi&nS++rkic{1OvSp> z#?s2hM!v0WZDu|3*upO~*8N)eD=-kQEOw@87WB%jKJ+TMKATxqv#n<FVU`}9YwU)e+e^iO(3ouSMeMY}5G!d1wvEp04?+}aB6kdt6(qqlCgwYHr4 zSir}`vWYS63=}s(vC3?nrEa#)s@JS1Tco#!P_epTZ6#96Swd>1y^XE4m5qb_>?dZw z_^V71h?GJOal4h}tXarv#Y(MbTcI=GTH9J#+dyt-Z)d02=MVq| z)LF^z@NWY}VM!Sr%40%OM8CZMLPI zVym+rw^XdFkQT8K*v1xGdmB5_JRR+w?Cl&JWAh6NvV$0no@d#zQiv_>L|vBFwzd{F zown9+vvGjb#+Hm!W>Fo+?G=YnC&y?df2<%!u{6qRtrg2bRwiOXtJ>(S+icJf`Xwb* zUf9?>*u!3R&^a2B+9^)WKU)A}90GIlE$g(xBrFx}+90JZsuh+t){5;g;hU3+jm~O-K{%zg4OUg{I+0XFg`O{aI6Ej#;}DKHCh7A{!c4L5u(1*% zw2F|nHrBTGcJ_L^oA#P|VdmuM#HQ%zB)f)_gT0fJd$K+s7i7HHP<|`6cbHz2)seAK zI-*2L#4Z(|~~z$n=5d z4aRDZt_3M-5>Sgg;TEh2%@Z}Uz8-40cbH70qsBp_VR+)0i3jiiBQ#W&fK&vGAWzl> z4IqTNp{{{`DW2ReM22CbVZh z8I$>11hpWzH<40%h;J~kQsz<(fNOy9P^j6=ipSv4jZ7S)g0y)FJTW)Gizn;?1Tq?- zYBx{9(xmS~N1Mnv1h`@Zw3eV-c*YJY2d$3`^2{FLj5HRs;2#uAnRYZmPs$PN|t$*P3c z=pax!ki*6y1XK;<*ch0u8A)ISt~fS2MpHi1fDuRwC9Wx(FGK^#>Xti$Hou~+hZbHU35YL zJ0l{43>m<*I{p!yg<`tbJUy^5qYS1|1lk=NPyqxPfr=RWM*#(z#1g9M9@eV~Bcly8 ze(3J%lF|JNLv=Fu(8youOR57FIB{GVQ?zk?)RZ@mLuhbI;&aCF{{J1kP43d6^!1< z==eBzGyWfkWC)UO)X~|gju`+iiV2Zr%-Bam&M}KomGR)r21kH3Qd~(J-=Uqe3g`Er zED>Qaqj6+#R4mI$C2nUh3EmBXRUsK^gQTky64Q`IA|8rc8VrM)Du+3}26_jEVPa%j zlS$F&41E;P$A1*e !te>=teL%k#T3n+4yZ4kp_V2zm`W`>qQX>J2Eoh*k1(IC-) z+538X0N7~@fkq6XV}V1a?knmTdDPToV?5NbOQ2tv*S9S}T? z2s)_$I@)FGU~@x6n2mud1jHqKy9a^R4E9TFl`%s* z+X<(SN%l!a5IpcSOhliu%_y87_l(}`@8xtE9_*w4Lq~t>h&TecMh-zFyNL`0KczPg zirM5{BV+@^T}A^N8aW2NN|$iwT>%0K!b^ih;?{8BHP}8BI&riY$R{e2eMrjqYiG%b z_ygAks@=0PK$C)x0*Y)tFH93gdrV!N&rCiv*ei`6^ae0jf@lPMhlGD(+7JMG5fJGn zj`s9w2GDp(8GPoX?dX^+NT5=(G`1mNN*bAwWE*k77q6ql`<^|$P zFp(Op%*;(|mM{T->@@*86HFCdG-!xmUOyWeI6Xm#t3cxrmA_nEj5?MzX6zArDF)zq z{k_sXLqU78Ak+g+uf=>_6FE65_%bmJd*bMsF%&5pL6w+I+n&2*-T0D7XC}}tiHddw zz#BkY;b3|^akPJ!tq>o=n%V^cL!bvP4iGX@uG=E1Oax+1t~@xMnRI9n$L_;`BcxJ@ z-4PnK$ovB1vKa0{AmkT38A`)bG&+-kGhJFb-$p${vZr=)*%L8^kH||Jf*@(RVhLJJ zF5m1WCKYi_rhXIhYMbvxF0$H1Tl4~s4A0Q!l79tRD*kKKh;_z+QO+cBOg1TH zu-B|BVJQ>}>%=peLO%kGTdtJ|jZ@Qrnrnz`MnL>HZ?U3w4$; zhA>YQZjdMLR+8Q=^>XVs7y`O(I_Mckr&W z$>w0}pD3q>@gnU{G&PEN+y(vr($PYfQ)7H+;^-CRP2IS)lUO8-zV+7WRzx01^P zO;)*Na*}g%iyLbkxdSp-;MsB)g@+Bu1;b=)gV7+cXt@P~h|ai}OMFVZ_9>%dL)^J2 zqkTAy<}BUNbXbf4cOH=&ZiC#iaUEy#n_wCrmiceX?tm&d)l8;tc1hVhn4A}?>3=al zi;*ih=_AT$w^+tJ*3c_gT?KCz3ccxnGp}|Qa^Ewej_Jm`$K*4H+@W%V3J{u$E}NL% zbf0!V!Ia$X7{|ErCBsI$@9pgxIn#9U^40w(6nenvdTw;L7NlskfwzMw1H`p;%Z{C3 zvv@chYkn-+#&gezh!C(V+rXp|)xL7=;NfO7b#wQH%w!4+g#>DNxGh@+qY@Gx#WT~y z6G_RbDJjW`i3xFW(UIX&v>Ei%+4GmKTs>4-OXeCoJ9pug=}EB>A;AK-+ID(7c(l;4 zD4ql-rlh8(r=_K&q^2Y#Ch#mZJTxc}xYfA}m#K%_vOo&okIBO@~-Bb^x%;$kDi8M002uU#syuC48m39hiqH|0>>C@Lga07QWy zVLaeYNKOMTl+S1Syj&%#B_lO0F)5xhOS^XM+(PfjYZoeO>grFb&{uLQRb-_n#Yz3) zZ99TPP+MXe%|r@{N_H2QloS`_=jLQ(rX(jN#72hf+PMwwU2m$ct*bwJ<1YQ3qO7z; zGzcOnBrGZ_DJ`9TB7628*nfzQh0;VYCo3Z@IWayeG$d%p`Ab)?pRKN|uRnUMTj=lf z_MD}RQ+#x|Kv)1CrDWyi7w+QL{J1-+CK2nJ~6Gv68!ae&-%a7F59&NnVH`pn4ZfbVtW~C*g+b}p^yKxf0M6FUY zR^OxVzYY26qTKZ4xR}_4?>P^coEZJY#aN%b2ot8HI{Cd|73WJ?LkW0;~_}DO^rB)M7 zCo3x|E6(@orLo(seT7uHO3TVFE1ml58v*G}+LsAcz9Gl-bqrER^6&3HzOrJmUp|A>QP#) zD*LY-C@npIo7GX3h~i3@c|3CE+VvZ^@3gjcbn!`RY)lz%9+Z+rQl+UhKRYEU{?gUv zo~o)N<)!R8+kCVA=6o%`Hl6r$|7>u3IRqRWOu&us+*=kY{Ji|4J%zNV(OtK zH23s&mRFXS;=M}FP(ktD(j&K^p`vk{(ozL6)a}jJFI~RYObMy0mv6U0ev5Kclqlk4 z=bBcg&Y!!8@owCt3fz?&9a@ueG-edyIcH zzc@D|F%J6MS~;|QJRv1DJ*#kUSw-FDmX6NW8y8QXri1L|%U7=Guj;PouAXe`phK!q zUoPBJn4P9k-zcXkFJ;_PlM<6svvP~~S5#Nm9cuvJt6m(rbn((fzAY{tZ)t~=+FCcR zA1U6GpOu;z3+Jm9Wry|_a$+SU#3xd#jqy@w80)zsG1R#jD2l$Dj2R~)IT zIePt;(tP+10K{9Bhs)uXm7EwG-E*Vp@Sy|fFFiRvCK`NEOnfrjcke%RxV)m`a4F4s z_wL=lkMr>Sb-Y(?uDEslP-W>p*?`!W8}kkx!qxLKWIKu!C{FgRnfFeLc#U`buXXb!7 zEh;K3$j?#pY6?m!FR}@8?lj-NQd(Pect6X9ASPn}{(V9%!V5VdG~`c6Nli=3%*tBm(4_4dN^|+mO6qwX+DECor1+?)h&{V$8B29Q_PZDWQh?DwJtZV0CM2e$ zq@{8RPD{_s&M!Hwv2RxH+&EfWS5r>yFf0|zg}Jb>01+t~2zgYLK0+5c47>?kQ(}^m z*uX-#ARXt@apRV3LT74@*NW<7mjfmhmYbiGD}J7cBBH_pESg}iA+mG{y@?6j17ze^ zpXY45)vUB!xl+?`6wU3%)st9kR2cABE>CP(G2qFYt>yTslSivKwernA4la!Qlca>0_!wlz@Dsvyk!2{4e`9c=w7io|m##v}hI6Lz z^zk}Ml@)SYCMU!pdU!lIuz0|0Vly~{0pu65N2*bmV&an0vJRZPK>ui=zI5_zBkSH* zltXdFcu;PbO#y!v8x626T8$is?t~!fB0#os^OKyBTgM*5rq|wa^W^z6Cs6sGBB|k+ z074E(Auu^`eq+oMsf!v54+Sz9f#M@k{7L$UW#u(!tlK+UPF-kfJXUk~KoPeINysZ& zM?29niwLbm8G$TD@%NYpdISWVl9^q2>XMoJor@RFo~WzjOvAwvp*NpjvEcmB65Jzh zVQvAvp*QBiaT3z9@``IOUuWe?N8632)5oh1b9QB=v9BP%EI^GO5u_myM8P`hqD`P8 zLCy8W0i4dtFFAO{Oq|zxq48K1=4OXUN(9F!AVJm+b3xmQzQoo{#2cJ+_)BtHcK&YQ z^P+b4_3I5sj~py1;M|hiP$jm@Jb{4okQ#_3>(<2p@Ql>~1iXyq)3XZp96oeaRL{QN ze72S|g*`Eq8(Be8f(GW61^2ufYhJLh6=a`qkBv)C74f;;H*>igXlcG)PdUD#+{{!Z z=?-J!kQ$JqHd@vos7HW|?HWihUhvx~nRz7#$}2A3zN5(X`sTSKhxYHu&(28ZvJ87x zSp;}cjnl;p3XoJFh0)RUz=-by`<<4VQ?&Q+k*b@VYH~%tew?y@#bUYSFE^aIyNaNw;6t>Hf{f8@$T>kS$n&nYE%>*lqim2~=seRgKL+`re?rzRtXnI6=oATQ$* z5);9!16fZ=&&nw%*?YLE{tP!K+Li5M4eiaDKyG2#3*n!VnjDu1b5W!cuS*zD)F+K6 zD=8;a(=xft?mbjlUw2*P=jw9(6t`{%_G)*)X=zFI_3??Q5N-+aN`jI&%%vHwDXB`z zX>Fs<$=iLPtmfD;ZTWINKUU4vbT7B>*<8o#>SC}!B7}poFEdSYO0qsBI~|~ZYI;Uy zcEO&5wA4Fx{*K({vHFX3!k!x#?t#Y|d8*r<~5VXO@2)YP<;jI{JDKHg-e^C2g% zU^o2-j-EJnOKoZAx_skw?Gd_`?=Hy8$w;rOiy$k&$S#?ftR(4@b!o%-M@~1LJy~8X zSC`tH++4`Zj?^A+JSBID)OW;lC^&iu?d9jD*Vl$)xn$t|EP++0W|lWyp@6UP(%GYj z3Y6Tw?EIp=a{GR)vGFQye@E1Rk;<7;pq5_dCu>8Y;%cHMj$nhd8X;D%ox6JeRQ3M+ z!lDw0D{786o;%HMXTBFZL*>{*`*#=S=A5eyu}B<*G2WAMNa_YF*YwwGuADnjxo_`* zL*-Sq$4~K*jJrulTW()JS$BkMd0}DB`D39XEs9G?N=ZyDxp3vmRcI$(@1fS_iR!ZQ zBXuCOu3T+ZyKc}R`qr7F!m^|=x9M1T3UnBkYfnn{S%up)-8J>P{>G*2Hz-iq)L4J4 z@!aLBH?PrKr{8ECXt{K}PP?Y%Hl2u|wMw!@>R84xRU=cbPq@)_^VY4~x4F^0c;T{K z#(Ia<@j+8F#eTU}E6yKD%PHJ*;O8{uRtLAES1+G@`>A=}uC_CQn}8!!9!{7tZML<8 zle?F%f55!C^XAW+8{qHb?dj@dZ#`@J6si=Z=kkH?=S>aNdgWg5>gAFbm(KHcw3z`^ zMsS6brp=_#wY#_9`~?daFJ8KM@skS|&h__Y0tcJf)0D~Ubu}4zbfNrtbNJ0>sTa$~ zh|MdP_`BL$QnwlG6evDxdnb2q|9Oj+E_-Ulil>$@Tef)N{JFkf6u!2aJ#F&T%!1tq z_W!zd`$6r-j0qaverA!coBizR0{EIb)6UMt!#iNX(iJP8d-0{`1wpi8`O-!6{CwSA zSj3FU*@b%#?cKa1F!;h9oEHhMyb$5AS(PR;+sYwbgG>Zj%63H4UF>aUv1OO#ZzqK$JnGt?6J)Jf zwZz}U!OF_o&dGc3GOBdG|M8c!mXlt1Uw^u8?dn%wcy`6oc>z8yb~aqG%5#FWT#iE7 z07Qbz8&^NO*w2IK=Ps^um#%vK-|Ig8=BHmJGxd)xK|8j5_uju=d*Qj|PcHCtceG>Q zF3$`k??lk)B0Edom#;nl?9v7P?%1v(VaLvGe@K?{?|W{NYj=kYwDyyKzx>=&Pd)e2TN|PZ z4_DP5<%7s|i}7n+RkE#QfAHE1PcNS9Q_LrfxNY11_)VJ0ZQN3MQAK^XZ>D_o?^j=b ze`73ud6-WiC+SCZgOBK4HAfEbFDVFp)e9Blwo*dSGM`J_}^f4uR`xr>)?$w%~-`a^{|druzYE^>cK z_`7RfdV10BGdHhn-u%n=pS=I?)vvF4>w~X;-d}&BQ8~T-FQqaE@hOZ2`%71@wU3W? zRLTdWqpaWvSEXI=zwy%33u~|6y!6|qpT7Ng?dn%vcxm<9|M@nkS~Pv`?CDbtpeli| zp1;u4Kq21h<6O)R?yWi9hz7VH`t5^%y|`jNpTEy-+Vta>AHMm@iz`>Y`0CrA{g_$L zhuo&~O-*M7ka^}<<$-;N$|@vuQ&zgaQmR$6(H`9M#RqS`xLoKPH*Ngki}%(%zjDR$ zXI}W{2VZSWKh}8mymF!V{JG;5dx;wyI8-X19Lx9wSjwa#fBC`qb?>fue#t!OkNx`d z_n*J>`l{tmE?TmD)xSRa{N@pItJS7);H^4c~nF&a2NXo*%Ga$+NG#{pp5nVW7+lOLmtO7ZsboOC$pB(tGLB-ino{lxL3bv0$b{qV&)F}%O`wd)1n|M2C9|9)k~LO)Mu zCnq=G1y8;BuXSJk@GH^dZJU4F_#-V!fB2bBg}Z1JboAJZ@>EFy)`c^XZHc?=;Y?TVA%_+-}%oM-~907kKa*4RnCczKL7g1yz`m+Rhq`RPRgbKG-2`TDC**S+=X%B6EXT@<@)M;DJd^Omi8b6bzS3LK^ zE3d7l67~PIIyiwfo#Q`u!NP^}LA)+r`pnC3B^T~3t*og(cKlf4uI;~m z`=5WmwBpGCFRuJjoBHMFpIQumCp%kUd6t&8z-?XKJ-xhry}f<><}F&h^5rnDD^iI6 z#EF8C;6Hx+{JnoZw{)J5r>nF5ORqft{Ikmz&YR=zY;Pw(M$6fP+j4Mna(40X@bdEU zU$Fe8-*Sr&lvmfEXlOVb5fS*yHy^(F6883Rb#}IY_L*mvJ-J}s98YHuX~5KGmjTIR z1|VLJt}b3a0gG0Co?W=F4C<4Ob@8zwTYmU_?dz+aobT=7=Hl$Qbm^1xX^a4Kftp!c z0W};4#uy4j&CcGz#m#5o@_(o1?czPjTe!cXuHnqN#{B$@n4n)id;hiP7W;d- zyE-~4jvbsWKo8M2cNS<*mMs9#%>X)W9h}{K7p-_Th4x(BqBfl@%1sX2^22{bb9kMr z69Rz4gTDX~nE^;B7)92=LYNKcvd7$I&;2ua&*7RAr_Y^lD$k1F^~)D%ZpmCPHzzk% z4-n5@0EC#yMpN2Z?r#vo4&~d~I=XuLEq(rt@Aj3~%0_dRDGPduq9UHlC_iR*_2Yl1at|g*t_ww_uhDA zSHFfL z0ap%gI(B~h(b_dHuUzreVs6AK>Er7q3jqJ>;HWz3Ty*X_H=TQpr?;oK z|NO-(UV8IC-){~qXY1q2RaKt!UYmIXltkMRBY4 z^7Qof@%Hla_4S`8DtP~^joahvNvNT<#F4{+KYjVpd;gL<_GOFa?%cW1(-UYv#&C8~ zofBO>y*z!qy?uQH{QUjr_;D-n-0F8f`*F*z-A9i%G>8I?K{VJ!Q6mF^X3Q43z)xP{_^Kuf9I1Q{@4{&dt7M{h1(yu>! zzHaRsFTe22^5r{rczd{evYR*~fvdX*WvZY1DLFSY=G)hvT)gPXCzmXH`uTsp{mFN~ z?u^Jh)yU#zQUBL6tE!(->jh06qu5Ns}NQT7R?}&dZrR94w13!6=4|hwS zd11}FAAh%Tdqiy6$}F+o+ymW|(jzV5BnFF&^;ZkrRQ0^GUY$OXsmj{^q}m8yr& zmX#JIZ+hpMRa`jV`{bLA+ryKxPMtj`-8t&2lK$B6^(XK1k!5A>4qL^wgAX-ucX#vj z{$wxIhYy#P>B}n*WQVVRd(GP)eDTAk9nmSdWla|@oIiK!L~U8{Z|gt%fR9Wo^FwXO ztw40v?dk6IN-=kM5SN!vsHiS0$qM`V(=UJcEhHhUaNp@mmoC#f<#=^EqJQ-Mo3Fo+ zA8GCG;^yM+>EXg9Zb^oSEwoC7zOsi~=JGw+QGaX;Ps%GPtyHddQChb3#J(MJ*Yo!3 z7xUw66}NGB)kEji7Z85nPuhwKi^|R;Rq{ZWjv%?2TsEpt-Qcm>;BbBDmJPV=o3E@_ z2Y61S^}#qx#|wI9l+wjQx{qi_r60&i>B4eoFWo%oUt;bZa`7xpacby}!BM(c@Q~<^ zJQdaYVEoykL;7Icp*sexXSDl%X2>-6!D!Z+vk14UBZoPf9243u;Y1#C!Ho|Y94(Ns z?GkPxG{IIWjd@D%9%WuSX`DZEn!X)c&kSY=<1tywZ5ey2Q9HWZ+;yXkqcpEML#rI~ z4AP53o=kC+b}2INU2e$0M>}a#LqnSS>LZ8t7GxwwhIOEaTeQ`YmLhZsG0PCzN%WPY z7td-ln2$ztlzBNy*BEKIBCAM@3hQKI8B1#)B-2)$5E;hUW*JMX9Fa_0aa3rB9HkYE zaO2d%7SbgI?eHWGc~5C$H5sDAI#9c&UuknwUBS_erB`eki|E@)N42gxIqWy~O2=cqiJ zrL~GQh%p}xM>|BjwAMA!YKNog$uZ#)vBt)<7ed||BIo&Xw6mR%9x_NX8SSXq^6jmy zB3aW}IjU(AZj7Y^4`a_Z9;=lZG=LOZ-*UEeWgjwG4l*^v{Ms88qbI( zHCrUdqDj=QHB{n5pr)M+(F2m*lBPP1T7x3dnYLnintWGYvp?X!9jzRdN8G2y@>o;z zb@ZUwt?4u}RCBa2lddr=O}JfRZPMyVjtaN7meyN0%{r?`XZy^yXydsOwlJ^lNVy!1 z4HwI64TCskr5(*aW{{(8=5{}4fc5&=$mo8{Fswy=fC10TJj_498Xasg>&!1!E zsQAfof&iRbB5`}01UIi;7ENFy>6G)Q#Iy{Oc^P|`o7hvfDb3Sr@hS7_D13x9qzw>b zt2M2OmD^j1X-dN%+Co*A%O1l~)P^SLyv3~#&8fgy$Wcu{nn#?F%kkZ#NOt>{W+Q24 z#Jt(bap9eCU1~^(dHKI{J z|GfSO<=Yz^l@2k4-nK|LjEx-q{@ZWArYX(3_uf+e&~473$Qtc;w4=1hpveY9DLN*V z>S%`nyDzl{BGg?He!p&%qu+l=j|@7ltldE^sjb>ktw{#GN$8RB4rP|Mj>1m)S+{<@ z^6>ZH5_tS%-TP~IO2sj4^v3nll;ev}nd6yVR`#P_`YjBHE7|)_*5zdp9aPID}U5RBq!a>-g?#QE{Xc)&cy5 z+7ROB_1}K|IrByUNekMsol11t*dM?B3I)F_NH?j;#p*WwLbUdKnyHwNGFB%3brYuk z{%abfypJA&gU~S5)_xNQ`2K5R!ym{ZkYNjB#aYZp*F}U=fJe+hX*nG3$M3%p1&4-F zgGgX$cne2=`azDqPa&V+;GNic>mO3xal`kx>qpEhGa#EN^Os*X5VmA-YUr5|sYbQs z52-hK_h48!MSEoI)<3lFD-5NzPH31CS{}?;jw0Dl>(TGW>lh1mOq52AJh3v<%Gj`A z47YQ~7Ucau^D1E(!J$FgQ5MIEAY(t|se34<$Enceri~kk;Bb^uGt?!bU>2}JbZI`i zE|M}b_>^cubh+^-%_-H0Qfxu|nS12DFCaIVy@2OaLCtQE`c1H=#(zs!^FZ+AgML=x=w2@-P-KgJ_bX zXB_=mRQ3@MwIid<_LONi$uK7<1Jw9f;1!{voVHB8X@mJF zpp9t!k}2@O-_4p3tYSQ;9Gqi2#KfUtyQmyDvW4ZLOLda&iSa4Q)cWZ&$bBZ8m2ay_G}0jHoP6aO)25o6 zVa&3$lH_&L?Ma}WHe>2EdUVWIEQhU0h@L@yx;jm|S#(J@tc|s#VNW*aQ>LuA#o!&>5Gnt!2(g=mj zQfG`XOmd;sS)H>j`Iv8ODGfj%oj!f`4E?N=!iI>2B}v3&FH5?smVQa~B z1SMq`-o#hPZyew$51}UVG)Xrmh4v1?3u{{sCkI;zUy+MU$}6D|f)Tb|zHZK>Z(3T; zBC^Io;yZRa+hacT5GB?&dp4=glDkVHDtVoLUM`OI)?|K~^FJjHLM*|LsG_5dLKf>W zNePvs2 z$)|jFcO+)PjF4MN7)Z76;}IGuu-cpi!)(Xw98~*3;*unM+M{;G#vm!34vva_g_j#) z8-}7f`M#S|>h8uvJX?Zy=!B$N2?`L1abvxVVx5Ha2<|8jeSF+^m!Jy)R~uW!zMY{A zCU_u$K!imsVj+@M+i6dn$;C-hUm@kN1L?NC1ilE{Fsn?=fl*>Os-wY)oKrhn#jaOk zK!otfrk604YTu@XXC$?ltW`((#LJA#t~lx(u9Ngj)PkeLJ(!r69FQQ4ymV5WO{~sA zPp+(q=^Pb~^vQZ5lkk^=lj30EJjA~oV+9IBTP3taJeDXH@f4YlH=LbR=TR6d4ui5z z3DY<_Sva31b|Rb*REsJJB_GI%q{#acW|Cl;#F z{Nw8C#7A)=8uH574N@6Lh``yMf$nZ@#2PpRGidqnP4vmtO>r4PCsOJ_an`wwyDBa^ z=MkxUfEaq$VF^yaPhJu$;Pbj0<4n-Gsje+&3J_J`}6)Zujw8 z^~};mbLSAo=iAGB4izXApTBsKQ>GdwO45bolaRV22{)2#N9AoY04#?4B@aVVI;h`H z&Vz-C#AGe^K}vcLn{$9Doo-HIq0+dP2PIihQuLl_WL6RfNSKgsb);8vWSCPD8u%kM z*|l^Em`N@ZT;&b&5Dl<06G?8fI?}1-lR%3s43>NkNixxr;(8%7r#?VQBMT^XkXVq% zLkis88tQ}Op_mg^JA_y=AIPg9*@l-xaFf&#Etk)n4kGDTWY~y279%Z4CsS)wq8QRK zgFk_9cfQbr?EYN?tdS}vcP#I>(1D#r8ARm zsAU#wDObqLo&;AJE}9(G(xmWaNnyi{d5agwAR09GXoe9l54|ej#QsY4wuQh6v(+oVtva^HYq^ zoQ&wxQu0h@7RU!RtG)Kr%jS1tdVVwwzYj$v#r4-`MEk{k1SSp zVqt)k6AagRM?{cc92OU0R^%}sQB3q?)5dBv@{V>AYZ}oUns>?kMMqlxploZR3A}Kk z(6LZ7LAERJ3;Y-eEgy`Om~Qz43KkT#z*FUrQ2?6pDzMaUaGF4l0fEckgfvQJj*^w8 z#Psn&N$9{)^P|LF5VV6Ja4B0V#6Ya$VgXvl^9^8=pTxurs5`+NOHEI~?3($;s&OVz zv;wG&y#*#U5fX|RCrT+~K|2Duiq50%KLzhCrQ~Py97zutfZUFo&)m0&kq3kG>U_z5%EXYD^>92=XU5lAwJ zuoC6KdkFdDD;_YaAt{*&QY?~(fjjagq=q>1{^DZwaV99@)OcN74{AX=z{w1TQcda; zEU563YSBq*@>sz+Oat0Pkjdi$kOf`aFPcaKvHPctqa*{UNk%?jQXD&Z1nq*KEwpG- zlLk8mnQEQfihl6yo_T>B~}Re)=Z7GtSTufjz0*;=mCDG zC8wp4M3Jhdjiv6hXj0GKun) zWP{ie^(UX^-{d42%k(f50q_3D0b!p(#dG=2%+@yzYSM{Xk}Q%>iRH}};J2tL4Sqty z3&7DC8T>Ce=!~rRBWjaTu)9f0N;kZc839MpPkNe?I+m8APwf?gR8bAs>x^_YLzk}0 zFlJ_DMU*Q|V<;$PSYX{`u0o+p8OQAU)K)S;0l}w|1t7fAQ&YiQXJta1HJq89nH`jQ zrb$T|LSRv$X2UdHYC9Z6wPuNdX^X^ax{{%0Dp})X_82m<>-=?w05}4;kb8^LsA~?eFhq38U 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 V)&~g~Tfxjt3d7vMqyI#)e*z^94h8@K literal 0 HcmV?d00001 diff --git a/SparkleShare/Common/Images/Top-Banner.png b/SparkleShare/Common/Images/Top-Banner.png new file mode 100644 index 0000000000000000000000000000000000000000..6694b2fefc9baff0da13e7e8611fa347379bac97 GIT binary patch literal 2958 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 F{{g+Uhjjn| literal 0 HcmV?d00001 diff --git a/SparkleShare/Windows/Installer/LaunchAppOnExit.wxi b/SparkleShare/Windows/Installer/LaunchAppOnExit.wxi new file mode 100644 index 000000000..fa43ac6b5 --- /dev/null +++ b/SparkleShare/Windows/Installer/LaunchAppOnExit.wxi @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/Installer/Package.en-us.wxl b/SparkleShare/Windows/Installer/Package.en-us.wxl new file mode 100644 index 000000000..8c51edc36 --- /dev/null +++ b/SparkleShare/Windows/Installer/Package.en-us.wxl @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/Installer/Predefines.wxi b/SparkleShare/Windows/Installer/Predefines.wxi new file mode 100644 index 000000000..e103e3ad1 --- /dev/null +++ b/SparkleShare/Windows/Installer/Predefines.wxi @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj b/SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj new file mode 100644 index 000000000..619a55b8c --- /dev/null +++ b/SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj @@ -0,0 +1,67 @@ + + + ..\build\setup\x64\Debug\ + x86;x64 + + + build\setup\x64\Release\ + + + ..\build\setup\x86\Release\ + + + + + ..\build\setup\x86\Debug\ + + + + + + + + + + + + + GIT_SCM_DIR + INSTALLFOLDER + false + true + true + git_scm + + + + + + RUNTIMES_DIR + INSTALLFOLDER + false + runtimes + + + + + + IMAGES_DIR + INSTALLFOLDER + false + Images + + + + + + PRESETS_DIR + INSTALLFOLDER + false + Presets + + + + + + + \ No newline at end of file diff --git a/SparkleShare/Windows/Installer/productVersion.wxi b/SparkleShare/Windows/Installer/productVersion.wxi new file mode 100644 index 000000000..f1b4fde70 --- /dev/null +++ b/SparkleShare/Windows/Installer/productVersion.wxi @@ -0,0 +1,4 @@ + + + + diff --git a/SparkleShare/Windows/SparkleShare.Windows.csproj b/SparkleShare/Windows/SparkleShare.Windows.csproj index 36fd3abf3..a6f8863bd 100755 --- a/SparkleShare/Windows/SparkleShare.Windows.csproj +++ b/SparkleShare/Windows/SparkleShare.Windows.csproj @@ -1,6 +1,7 @@  net8.0-windows + true WinExe false publish\ @@ -16,6 +17,7 @@ 0 1.0.0.%2a false + true false Images\sparkleshare-app.ico @@ -26,13 +28,21 @@ true true + + false + false + .\bin AllRules.ruleset true + full + 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 .\bin\ + full + 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 true @@ -207,6 +217,8 @@ Always + True + latest-minimum diff --git a/SparkleShare/Windows/build.cmd b/SparkleShare/Windows/build.cmd index ae1cecdb9..52972d1b8 100755 --- a/SparkleShare/Windows/build.cmd +++ b/SparkleShare/Windows/build.cmd @@ -1,4 +1,6 @@ @echo off +REM delete wix in (user).dotnet/tools +REM dotnet tool install wix --create-manifest-if-needed REM set WinDirNet=%WinDir%\Microsoft.NET\Framework REM set msbuild="%WinDirNet%\v4.0\msbuild.exe" @@ -11,23 +13,27 @@ if not exist "%OutputDir%" mkdir "%OutputDir%" dotnet build "%~dp0..\..\SparkleShare.sln" /target:SparkleShare_Windows:Rebuild /p:Configuration=ReleaseWindows /p:Platform="Any CPU" -m if "%1"=="installer" ( - if exist "%wixBinDir%" ( - if exist "%~dp0\SparkleShare.msi" del "%~dp0\SparkleShare.msi" - "%wixBinDir%\heat.exe" dir "%OutputDir%\git_scm" -cg gitScmComponentGroup -gg -scom -sreg -sfrag -srd -dr GITSCM_DIR -var wix.gitscmpath -o "%~dp0\git_scm.wxs" - "%wixBinDir%\heat.exe" dir "%OutputDir%\Images" -cg ImagesComponentGroup -gg -scom -sreg -sfrag -srd -dr IMAGES_DIR -var wix.imagespath -o "%~dp0\images.wxs" - "%wixBinDir%\heat.exe" dir "%OutputDir%\Presets" -cg PresetsComponentGroup -gg -scom -sreg -sfrag -srd -dr PRESETS_DIR -var wix.presetspath -o "%~dp0\presets.wxs" - "%wixBinDir%\heat.exe" dir "%OutputDir%\runtimes" -cg runtimesComponentGroup -gg -scom -sreg -sfrag -srd -dr PRESETS_DIR -var wix.runtimespath -o "%~dp0\runtimes.wxs" - "%wixBinDir%\candle" "%~dp0\SparkleShare.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" - "%wixBinDir%\candle" "%~dp0\git_scm.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" - "%wixBinDir%\candle" "%~dp0\images.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" - "%wixBinDir%\candle" "%~dp0\presets.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" - "%wixBinDir%\candle" "%~dp0\runtimes.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" - "%wixBinDir%\light" -ext WixUIExtension -ext WixUtilExtension "%~dp0Sparkleshare.wixobj" "%~dp0git_scm.wixobj" "%~dp0images.wixobj" "%~dp0presets.wixobj" "%~dp0runtimes.wixobj" -droot="%~dp0." -dgitscmpath="%OutputDir%\git_scm" -dimagespath="%OutputDir%\Images" -dpresetspath="%OutputDir%\Presets" -druntimespath="%OutputDir%\runtimes" -o "%~dp0SparkleShare.msi" - if exist "%~dp0\SparkleShare.msi" echo SparkleShare.msi created. - ) else ( - echo Not building installer ^(could not find wix, Windows Installer XML toolset^) - echo wix is available at http://wix.sourceforge.net/ - SET ERRORLEVEL=2 - ) + rem dotnet tool install --global wix + dotnet restore + rem dotnet tool install wix --create-manifest-if-needed + dotnet build "%~dp0..\..\SparkleShare.sln" /target:SparkleShare_Windows_Installer:Rebuild /p:Configuration=ReleaseWindows /p:Platform="Any CPU" -m + REM if exist "%wixBinDir%" ( + REM if exist "%~dp0\SparkleShare.msi" del "%~dp0\SparkleShare.msi" + REM "%wixBinDir%\heat.exe" dir "%OutputDir%\git_scm" -cg gitScmComponentGroup -gg -scom -sreg -sfrag -srd -dr GITSCM_DIR -var wix.gitscmpath -o "%~dp0\git_scm.wxs" + REM "%wixBinDir%\heat.exe" dir "%OutputDir%\Images" -cg ImagesComponentGroup -gg -scom -sreg -sfrag -srd -dr IMAGES_DIR -var wix.imagespath -o "%~dp0\images.wxs" + REM "%wixBinDir%\heat.exe" dir "%OutputDir%\Presets" -cg PresetsComponentGroup -gg -scom -sreg -sfrag -srd -dr PRESETS_DIR -var wix.presetspath -o "%~dp0\presets.wxs" + REM "%wixBinDir%\heat.exe" dir "%OutputDir%\runtimes" -cg runtimesComponentGroup -gg -scom -sreg -sfrag -srd -dr PRESETS_DIR -var wix.runtimespath -o "%~dp0\runtimes.wxs" + REM "%wixBinDir%\candle" "%~dp0\SparkleShare.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" + REM "%wixBinDir%\candle" "%~dp0\git_scm.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" + REM "%wixBinDir%\candle" "%~dp0\images.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" + REM "%wixBinDir%\candle" "%~dp0\presets.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" + REM "%wixBinDir%\candle" "%~dp0\runtimes.wxs" -ext WixUIExtension -ext WixUtilExtension -o "%~dp0\" + REM "%wixBinDir%\light" -ext WixUIExtension -ext WixUtilExtension "%~dp0Sparkleshare.wixobj" "%~dp0git_scm.wixobj" "%~dp0images.wixobj" "%~dp0presets.wixobj" "%~dp0runtimes.wixobj" -droot="%~dp0." -dgitscmpath="%OutputDir%\git_scm" -dimagespath="%OutputDir%\Images" -dpresetspath="%OutputDir%\Presets" -druntimespath="%OutputDir%\runtimes" -o "%~dp0SparkleShare.msi" + REM if exist "%~dp0\SparkleShare.msi" echo SparkleShare.msi created. + REM ) else ( + REM echo Not building installer ^(could not find wix, Windows Installer XML toolset^) + REM echo wix is available at http://wix.sourceforge.net/ + REM SET ERRORLEVEL=2 + REM ) ) else echo Not building installer, as it was not requested. ^(Issue "build.cmd installer" to build installer ^) diff --git a/Sparkles/Git/Sparkles.Git.csproj b/Sparkles/Git/Sparkles.Git.csproj index 9db0e26ef..58995c5c5 100644 --- a/Sparkles/Git/Sparkles.Git.csproj +++ b/Sparkles/Git/Sparkles.Git.csproj @@ -20,12 +20,14 @@ 512 + latest-minimum False ..\..\bin\ prompt 4 + 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 False @@ -33,6 +35,7 @@ 4 TRACE DEBUG true + 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 True diff --git a/Sparkles/Sparkles.csproj b/Sparkles/Sparkles.csproj index cc5336279..010168868 100755 --- a/Sparkles/Sparkles.csproj +++ b/Sparkles/Sparkles.csproj @@ -1,7 +1,6 @@  net8.0 - + latest-minimum none @@ -47,6 +47,7 @@ prompt 4 False + 1701;1702;SPELL False @@ -54,6 +55,7 @@ 4 true DEBUG + 1701;1702;SPELL none @@ -116,7 +118,6 @@ Component - diff --git a/Sparkles/Tests/Sparkles.Tests.csproj b/Sparkles/Tests/Sparkles.Tests.csproj index 7c9a55570..37b14644f 100644 --- a/Sparkles/Tests/Sparkles.Tests.csproj +++ b/Sparkles/Tests/Sparkles.Tests.csproj @@ -2,6 +2,7 @@ net8.0 false + latest-minimum diff --git a/scripts/bump-version.ps1 b/scripts/bump-version.ps1 new file mode 100644 index 000000000..ae9bc3870 --- /dev/null +++ b/scripts/bump-version.ps1 @@ -0,0 +1,21 @@ +param ( + [string]$version +) + +if (-not $version) { + Write-Output "No version number specified. Usage: .\bump-version.ps1 -version VERSION_NUMBER" +} else { + (Get-Content ../SparkleShare/Windows/SparkleShare.wxs) -replace " Version='[^']*'", " Version='$version'" | Set-Content ../SparkleShare/Windows/SparkleShare.wxs + (Get-Content ../Sparkles/InstallationInfo.Directory.cs) -replace 'assembly:AssemblyVersion *\("[^"]*"\)', "assembly:AssemblyVersion (\"$version\")" | Set-Content ../Sparkles/InstallationInfo.Directory.cs + (Get-Content ../meson.build) -replace "configuration.set('VERSION', '[^\"]*')", "configuration.set('VERSION', '$version')" | Set-Content ../meson.build + + $infoPlist = Get-Content ../SparkleShare/Mac/Info.plist + $infoPlist = $infoPlist -replace '(?s)(CFBundleShortVersionString<\/key>\s*)[^<]*(<\/string>)', "`$1$version`$2" + $infoPlist = $infoPlist -replace '(?s)(CFBundleVersion<\/key>\s*)[^<]*(<\/string>)', "`$1$version`$2" + Set-Content ../SparkleShare/Mac/Info.plist -Value $infoPlist + + Remove-Item ../meson.build.bak -ErrorAction SilentlyContinue + Remove-Item ../SparkleShare/Mac/Info.plist.tmp -ErrorAction SilentlyContinue + Remove-Item ../SparkleShare/Windows/SparkleShare.wxs.bak -ErrorAction SilentlyContinue + Remove-Item ../Sparkles/InstallationInfo.Directory.cs.bak -ErrorAction SilentlyContinue +} \ No newline at end of file diff --git a/todo.md b/todo.md index 71513b741..7f5e7753f 100644 --- a/todo.md +++ b/todo.md @@ -3,6 +3,7 @@ - [x] open crashlog on unhandled exception - [x] dont locally catch exception when execution local commands, this results in a chrashlog file - [x] git_scm update to latest +- [x] add debug switch to switch directories/config to not compromise productive installation - [x] git url syntax - [ ] deactivate/replace notification service -> was not active, so not so important From 1ea4e1fc97791c65c5ed4cd8f8df7704cf307bcf Mon Sep 17 00:00:00 2001 From: uenz Date: Fri, 1 Nov 2024 21:03:34 +0100 Subject: [PATCH 07/14] Suppress build warnings with editorconfig --- .editorconfig | 310 ++++++++++++++++++ SparkleShare/Common/SparkleShare.shproj | 3 + .../Windows/SparkleShare.Windows.csproj | 16 +- .../SparkleShareInviteOpener.csproj | 4 +- Sparkles/Git/Sparkles.Git.csproj | 8 +- Sparkles/Sparkles.csproj | 5 +- 6 files changed, 332 insertions(+), 14 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..f40fee429 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,310 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Code Actions #### + +# Type members +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# Symbol search +dotnet_search_reference_assemblies = true + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_anonymous_function = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent + +dotnet_diagnostic.IDE0008.severity = none +dotnet_diagnostic.IDE0011.severity = none +dotnet_diagnostic.IDE0036.severity = none +dotnet_diagnostic.IDE0040.severity = none +dotnet_diagnostic.IDE0044.severity = none +dotnet_diagnostic.IDE0051.severity = none +dotnet_diagnostic.IDE0055.severity = none +dotnet_diagnostic.IDE0060.severity = none +dotnet_diagnostic.CA1416.severity = none +dotnet_diagnostic.CA1507.severity = none +dotnet_diagnostic.CA1510.severity = none +dotnet_diagnostic.CA1806.severity = none +dotnet_diagnostic.CA1816.severity = none +dotnet_diagnostic.CA1822.severity = none +dotnet_diagnostic.CA1825.severity = none +dotnet_diagnostic.CA1872.severity = none +dotnet_diagnostic.CA1845.severity = none +dotnet_diagnostic.CA1847.severity = none +dotnet_diagnostic.CA1850.severity = none +dotnet_diagnostic.CA1859.severity = none +dotnet_diagnostic.CA1861.severity = none +dotnet_diagnostic.CA1865.severity = none +dotnet_diagnostic.CA1866.severity = none +dotnet_diagnostic.CA2101.severity = none +dotnet_diagnostic.CA2208.severity = none +dotnet_diagnostic.CA2211.severity = none +dotnet_diagnostic.CA2241.severity = none +dotnet_diagnostic.CA2263.severity = none diff --git a/SparkleShare/Common/SparkleShare.shproj b/SparkleShare/Common/SparkleShare.shproj index 36fe2e92c..0dfe81bb2 100644 --- a/SparkleShare/Common/SparkleShare.shproj +++ b/SparkleShare/Common/SparkleShare.shproj @@ -10,4 +10,7 @@ + + + diff --git a/SparkleShare/Windows/SparkleShare.Windows.csproj b/SparkleShare/Windows/SparkleShare.Windows.csproj index a6f8863bd..408fdffe9 100755 --- a/SparkleShare/Windows/SparkleShare.Windows.csproj +++ b/SparkleShare/Windows/SparkleShare.Windows.csproj @@ -34,21 +34,21 @@ .\bin - AllRules.ruleset + true full - 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 + 1701;1702 .\bin\ full - 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 + 1701;1702 true .\bin true - AllRules.ruleset + ..\..\bin\ @@ -58,7 +58,7 @@ true .\bin - MinimumRecommendedRules.ruleset + @@ -178,6 +178,7 @@ Presets\own-server.xml Always + @@ -223,8 +224,11 @@ - + + + + \ No newline at end of file diff --git a/SparkleShare/Windows/SparkleShareInviteOpener/SparkleShareInviteOpener.csproj b/SparkleShare/Windows/SparkleShareInviteOpener/SparkleShareInviteOpener.csproj index bbccc7926..8ee9a8397 100644 --- a/SparkleShare/Windows/SparkleShareInviteOpener/SparkleShareInviteOpener.csproj +++ b/SparkleShare/Windows/SparkleShareInviteOpener/SparkleShareInviteOpener.csproj @@ -36,7 +36,7 @@ full AnyCPU prompt - MinimumRecommendedRules.ruleset + ..\..\..\bin\ @@ -45,7 +45,7 @@ pdbonly AnyCPU prompt - MinimumRecommendedRules.ruleset + diff --git a/Sparkles/Git/Sparkles.Git.csproj b/Sparkles/Git/Sparkles.Git.csproj index 58995c5c5..fb5472460 100644 --- a/Sparkles/Git/Sparkles.Git.csproj +++ b/Sparkles/Git/Sparkles.Git.csproj @@ -27,7 +27,7 @@ ..\..\bin\ prompt 4 - 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 + 1701;1702 False @@ -35,7 +35,7 @@ 4 TRACE DEBUG true - 1701;1702;CA1416;IDE0055;IDE0040;CA1822;IDE0044;IDE0011;IDE0060;IDE0008;CA1822;CA1510;CA2211;IDE0036;CA2101;CA1816;CA1825;CA1866;CA1845;CA1861;CA1847;CA2208;CA1806;CA1859;IDE0051 + 1701;1702 True @@ -55,14 +55,14 @@ ..\..\bin\ TRACE DEBUG AnyCPU - MinimumRecommendedRules.ruleset + ..\..\bin\ true AnyCPU prompt - MinimumRecommendedRules.ruleset + diff --git a/Sparkles/Sparkles.csproj b/Sparkles/Sparkles.csproj index 010168868..b5a5d66f7 100755 --- a/Sparkles/Sparkles.csproj +++ b/Sparkles/Sparkles.csproj @@ -77,14 +77,14 @@ .\Windows\bin\ DEBUG AnyCPU - MinimumRecommendedRules.ruleset + .\Windows\bin\ true AnyCPU prompt - MinimumRecommendedRules.ruleset + @@ -119,6 +119,7 @@ + From 0c4f2530a01526b8f077e9d91f43cf5b1ffb3a4f Mon Sep 17 00:00:00 2001 From: uenz Date: Fri, 1 Nov 2024 21:04:08 +0100 Subject: [PATCH 08/14] Fixed build command --- .github/workflows/github-actions-demo.yml | 4 ++-- SparkleShare/Windows/README.md | 8 +++----- SparkleShare/Windows/build.cmd | 12 ++++++------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 0891648db..102ace540 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -49,7 +49,7 @@ jobs: - name: Install Dependencies (Windows) run: | echo "Windows {{matrix.config.os}}" - choco install -y wixtoolset --version=3.14.1 + # choco install -y wixtoolset --version=3.14.1 choco install tartool choco install unzip if: matrix.os == 'windows-latest' @@ -78,7 +78,7 @@ jobs: - name: Compile (Windows) run: | ./SparkleShare/Windows/build.cmd installer - cp ./SparkleShare/Windows/SparkleShare.msi SparkleShare-Windows-${GITHUB_REF##*/}.msi + cp ./SparkleShare/Windows/Installer/build/setup/x64/Release/en-US/SparkleShare.Windows.Installer.msi SparkleShare-Windows-${GITHUB_REF##*/}.msi if: matrix.os == 'windows-latest' - name: Publish uses: softprops/action-gh-release@v1 diff --git a/SparkleShare/Windows/README.md b/SparkleShare/Windows/README.md index 70c5ea70f..d6f49b53c 100644 --- a/SparkleShare/Windows/README.md +++ b/SparkleShare/Windows/README.md @@ -5,7 +5,7 @@ You can choose to build SparkleShare from source or to run the Windows installer ### Installing build requirements Install [VisualStudioCommunity](https://visualstudio.microsoft.com/de/vs/community/) -or install version 4.0 of the [.NET Framework](http://www.microsoft.com/download/en/details.aspx?id=17851) if you haven't already. +or install [.NET 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) if you haven't already. Open a command prompt and execute the following: @@ -14,19 +14,17 @@ cd C:\path\to\SparkleShare-sources cd SparkleShare\Windows build ``` -The build command ends with 2 errors. But that´s all right. `C:\path\to\SparkleShare-sources\bin` should now contain `SparkleShare.exe`, which you can run. ### Creating a Windows installer -To create an installer package, install [WiX 3.11.2](https://github.com/wixtoolset/wix3/releases/tag/wix3112rtm), restart Windows and run: ``` -cd C:\path\to\SparkleShare-sources\SparkleShare\Windows\ +cd C:\path\to\SparkleShare-sources\SparkleShare\Windows build installer ``` -This will create `SparkleShare.msi` in the same directory. +This will create `SparkleShare.msi` in the directory .\Installer\build\setup\x64\Release\en-US. ### Resetting SparkleShare settings diff --git a/SparkleShare/Windows/build.cmd b/SparkleShare/Windows/build.cmd index 52972d1b8..4cb75ffbe 100755 --- a/SparkleShare/Windows/build.cmd +++ b/SparkleShare/Windows/build.cmd @@ -5,17 +5,17 @@ REM dotnet tool install wix --create-manifest-if-needed REM set WinDirNet=%WinDir%\Microsoft.NET\Framework REM set msbuild="%WinDirNet%\v4.0\msbuild.exe" REM if not exist %msbuild% set msbuild="%WinDirNet%\v4.0.30319\msbuild.exe" -set WIX=C:\Program Files (x86)\WiX Toolset v3.14 -set wixBinDir=%WIX%\bin -set OutputDir=%~dp0bin -if not exist "%OutputDir%" mkdir "%OutputDir%" +REM set WIX=C:\Program Files (x86)\WiX Toolset v3.14 +REM set wixBinDir=%WIX%\bin +REM set OutputDir=%~dp0bin +REM if not exist "%OutputDir%" mkdir "%OutputDir%" dotnet build "%~dp0..\..\SparkleShare.sln" /target:SparkleShare_Windows:Rebuild /p:Configuration=ReleaseWindows /p:Platform="Any CPU" -m if "%1"=="installer" ( - rem dotnet tool install --global wix + REM dotnet tool install --global wix dotnet restore - rem dotnet tool install wix --create-manifest-if-needed + REM dotnet tool install wix --create-manifest-if-needed dotnet build "%~dp0..\..\SparkleShare.sln" /target:SparkleShare_Windows_Installer:Rebuild /p:Configuration=ReleaseWindows /p:Platform="Any CPU" -m REM if exist "%wixBinDir%" ( REM if exist "%~dp0\SparkleShare.msi" del "%~dp0\SparkleShare.msi" From 8841d221b4509c7a539e3940fc39bb516139fe18 Mon Sep 17 00:00:00 2001 From: uenz Date: Fri, 1 Nov 2024 21:04:33 +0100 Subject: [PATCH 09/14] Fixed bump-version script --- scripts/bump-version.ps1 | 58 ++++++++++++++++++++++++++++++++++------ scripts/bump-version.sh | 4 +-- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/scripts/bump-version.ps1 b/scripts/bump-version.ps1 index ae9bc3870..fb455f9e2 100644 --- a/scripts/bump-version.ps1 +++ b/scripts/bump-version.ps1 @@ -1,3 +1,15 @@ +<# +.DESCRIPTION + This PowerShell script bumps the version in severas source files. +.PARAMETER version + String with the new version. +.EXAMPLE + PS> ./bump-version.ps1 3.8.2 + > pofershell -f bump-version.ps1 3.8.2 +.NOTES + Author: uenz +#> + param ( [string]$version ) @@ -5,17 +17,47 @@ param ( if (-not $version) { Write-Output "No version number specified. Usage: .\bump-version.ps1 -version VERSION_NUMBER" } else { - (Get-Content ../SparkleShare/Windows/SparkleShare.wxs) -replace " Version='[^']*'", " Version='$version'" | Set-Content ../SparkleShare/Windows/SparkleShare.wxs - (Get-Content ../Sparkles/InstallationInfo.Directory.cs) -replace 'assembly:AssemblyVersion *\("[^"]*"\)', "assembly:AssemblyVersion (\"$version\")" | Set-Content ../Sparkles/InstallationInfo.Directory.cs - (Get-Content ../meson.build) -replace "configuration.set('VERSION', '[^\"]*')", "configuration.set('VERSION', '$version')" | Set-Content ../meson.build + # for debugging regex got to https://regex101.com/ + + # replace version in installer script + (Get-Content $PSScriptRoot/../SparkleShare/Windows/Installer/productVersion.wxi) -replace " ProductVersion=`"[^']*`"", " ProductVersion=`"$version`"" | Set-Content $PSScriptRoot/../SparkleShare/Windows/Installer/productVersion.wxi + # replace version in assembly info + (Get-Content $PSScriptRoot/../Sparkles/InstallationInfo.Directory.cs) -replace "assembly:AssemblyVersion *\(`"[^`']*`"\)", "assembly:AssemblyVersion (`"$version`")" | Set-Content $PSScriptRoot/../Sparkles/InstallationInfo.Directory.cs + # replace version in meson.build + (Get-Content $PSScriptRoot/../meson.build) -replace "configuration.set\('VERSION', ('[^`"]*')\)", "configuration.set('VERSION', '$version')" | Set-Content $PSScriptRoot/../meson.build + $plistPath="$PSScriptRoot/../SparkleShare/Mac/Info.plist" + # Read the content of the plist file + $content = Get-Content -Path $plistPath -Raw + # Define the new values for the keys + $newValues = @{ + "CFBundleVersion" = $version + "CFBundleShortVersionString" = $version + } + # Function to replace the value of a key in a plits file + function Replace-KeyValue-InPlist { + param ( + [string]$content, + [string]$key, + [string]$newValue + ) + $pattern = "($key<\/key>\s*)([^<]*)(<\/string>)" + return [regex]::Replace($content, $pattern, "`$1 $newValue `$3") + } + # Replace the values for each key + foreach ($key in $newValues.Keys) { + $content = Replace-KeyValue-InPlist -content $content -key $key -newValue $newValues[$key] + } - $infoPlist = Get-Content ../SparkleShare/Mac/Info.plist - $infoPlist = $infoPlist -replace '(?s)(CFBundleShortVersionString<\/key>\s*)[^<]*(<\/string>)', "`$1$version`$2" - $infoPlist = $infoPlist -replace '(?s)(CFBundleVersion<\/key>\s*)[^<]*(<\/string>)', "`$1$version`$2" - Set-Content ../SparkleShare/Mac/Info.plist -Value $infoPlist + # Write the updated content back to the plist file + Set-Content -Path $plistPath -Value $content Remove-Item ../meson.build.bak -ErrorAction SilentlyContinue Remove-Item ../SparkleShare/Mac/Info.plist.tmp -ErrorAction SilentlyContinue Remove-Item ../SparkleShare/Windows/SparkleShare.wxs.bak -ErrorAction SilentlyContinue Remove-Item ../Sparkles/InstallationInfo.Directory.cs.bak -ErrorAction SilentlyContinue -} \ No newline at end of file +} + + + + + diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh index ae4e9eb36..853fa85ac 100755 --- a/scripts/bump-version.sh +++ b/scripts/bump-version.sh @@ -3,14 +3,14 @@ if [ "$1" = "" ]; then echo "No version number specified. Usage: ./bump-version.sh VERSION_NUMBER" else - sed -i.bak "s/ Version='[^']*'/ Version='$1'/" ../SparkleShare/Windows/SparkleShare.wxs + sed -i.bak "s/ ProductVersion=\"[^']*\"/ ProductVersion=\"$1\"/" ../SparkleShare/Windows/Installer/productVersion.wxi sed -i.bak "s/assembly:AssemblyVersion *(\"[^\"]*\")/assembly:AssemblyVersion (\"$1\")/" ../Sparkles/InstallationInfo.Directory.cs sed -i.bak "s/configuration.set('VERSION', '[^\"]*')/configuration.set('VERSION', '$1')/" ../meson.build cat ../SparkleShare/Mac/Info.plist | eval "sed -e '/CFBundleShortVersionString<\/key>/{N;s#.*<\/string>#$1<\/string>#;}'" > ../SparkleShare/Mac/Info.plist.tmp cat ../SparkleShare/Mac/Info.plist.tmp | eval "sed -e '/CFBundleVersion<\/key>/{N;s#.*<\/string>#$1<\/string>#;}'" > ../SparkleShare/Mac/Info.plist rm ../meson.build.bak rm ../SparkleShare/Mac/Info.plist.tmp - rm ../SparkleShare/Windows/SparkleShare.wxs.bak + rm ../SparkleShare/Windows/Installer/productVersion.wxi.bak rm ../Sparkles/InstallationInfo.Directory.cs.bak fi From e182ef6d05ca9236c608cbc1a24c31c63c7fcd3e Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 6 Nov 2024 19:23:03 +0100 Subject: [PATCH 10/14] Added missing windows installer files --- .gitignore | 3 +- SparkleShare/Windows/Installer/Components.wxs | 115 ++++++++++++++++++ SparkleShare/Windows/Installer/Folders.wxs | 53 ++++++++ SparkleShare/Windows/Installer/Package.wxs | 114 +++++++++++++++++ .../SparkleShare.Windows.Installer.wixproj | 5 + .../Windows/Installer/VerifyReadyDlg.wxs | 112 +++++++++++++++++ .../Windows/Installer/WixUI_InstallDir.wxs | 94 ++++++++++++++ SparkleShare/Windows/README.md | 5 +- .../Windows/SparkleShare.Windows.csproj | 10 +- version-latest | 1 + 10 files changed, 502 insertions(+), 10 deletions(-) create mode 100644 SparkleShare/Windows/Installer/Components.wxs create mode 100644 SparkleShare/Windows/Installer/Folders.wxs create mode 100644 SparkleShare/Windows/Installer/Package.wxs create mode 100644 SparkleShare/Windows/Installer/VerifyReadyDlg.wxs create mode 100644 SparkleShare/Windows/Installer/WixUI_InstallDir.wxs create mode 100644 version-latest diff --git a/.gitignore b/.gitignore index a864264b2..7f1d57c3b 100644 --- a/.gitignore +++ b/.gitignore @@ -26,11 +26,10 @@ _ReSharper.* *.msi *.wixobj *.wixpdb -*.wxs *.dotCover SparkleShare/Windows/build/ .vs/ - +SparkleShare/Windows/*.wxs # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore diff --git a/SparkleShare/Windows/Installer/Components.wxs b/SparkleShare/Windows/Installer/Components.wxs new file mode 100644 index 000000000..597b40cf5 --- /dev/null +++ b/SparkleShare/Windows/Installer/Components.wxs @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/Installer/Folders.wxs b/SparkleShare/Windows/Installer/Folders.wxs new file mode 100644 index 000000000..a2bf196e5 --- /dev/null +++ b/SparkleShare/Windows/Installer/Folders.wxs @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/Installer/Package.wxs b/SparkleShare/Windows/Installer/Package.wxs new file mode 100644 index 000000000..920dee6d1 --- /dev/null +++ b/SparkleShare/Windows/Installer/Package.wxs @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj b/SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj index 619a55b8c..2776e27b0 100644 --- a/SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj +++ b/SparkleShare/Windows/Installer/SparkleShare.Windows.Installer.wixproj @@ -5,6 +5,10 @@ build\setup\x64\Release\ + false + false + true + ICE57 ..\build\setup\x86\Release\ @@ -39,6 +43,7 @@ RUNTIMES_DIR INSTALLFOLDER false + true runtimes diff --git a/SparkleShare/Windows/Installer/VerifyReadyDlg.wxs b/SparkleShare/Windows/Installer/VerifyReadyDlg.wxs new file mode 100644 index 000000000..60af472ef --- /dev/null +++ b/SparkleShare/Windows/Installer/VerifyReadyDlg.wxs @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/Installer/WixUI_InstallDir.wxs b/SparkleShare/Windows/Installer/WixUI_InstallDir.wxs new file mode 100644 index 000000000..a1a8c9565 --- /dev/null +++ b/SparkleShare/Windows/Installer/WixUI_InstallDir.wxs @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/Windows/README.md b/SparkleShare/Windows/README.md index d6f49b53c..c6d5a93e2 100644 --- a/SparkleShare/Windows/README.md +++ b/SparkleShare/Windows/README.md @@ -7,14 +7,14 @@ You can choose to build SparkleShare from source or to run the Windows installer Install [VisualStudioCommunity](https://visualstudio.microsoft.com/de/vs/community/) or install [.NET 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) if you haven't already. -Open a command prompt and execute the following: +To build from commandline open a command prompt and execute the following: ``` cd C:\path\to\SparkleShare-sources cd SparkleShare\Windows build ``` -`C:\path\to\SparkleShare-sources\bin` should now contain `SparkleShare.exe`, which you can run. +`C:\path\to\SparkleShare-sources\Windows\bin` should now contain `SparkleShare.exe`, which you can run. ### Creating a Windows installer @@ -26,6 +26,7 @@ build installer This will create `SparkleShare.msi` in the directory .\Installer\build\setup\x64\Release\en-US. +Or from within Visual studio you need to install the [HeatWave]https://marketplace.visualstudio.com/items?itemName=FireGiant.FireGiantHeatWaveDev17 extension to build the installer package. ### Resetting SparkleShare settings diff --git a/SparkleShare/Windows/SparkleShare.Windows.csproj b/SparkleShare/Windows/SparkleShare.Windows.csproj index 408fdffe9..5e1c3d72f 100755 --- a/SparkleShare/Windows/SparkleShare.Windows.csproj +++ b/SparkleShare/Windows/SparkleShare.Windows.csproj @@ -17,19 +17,17 @@ 0 1.0.0.%2a false - true false Images\sparkleshare-app.ico - - false + true true true - - - false + + + false diff --git a/version-latest b/version-latest new file mode 100644 index 000000000..d2ba1f297 --- /dev/null +++ b/version-latest @@ -0,0 +1 @@ +2.39.0 \ No newline at end of file From b502f94fd82782280f4a3fcd57315cd3f96c6bf9 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 6 Nov 2024 19:24:12 +0100 Subject: [PATCH 11/14] Moved CheckForNewVersion to github --- SparkleShare/Common/AboutController.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/SparkleShare/Common/AboutController.cs b/SparkleShare/Common/AboutController.cs index 6cef8404e..608a7d197 100644 --- a/SparkleShare/Common/AboutController.cs +++ b/SparkleShare/Common/AboutController.cs @@ -63,15 +63,14 @@ void CheckForNewVersion () UpdateLabelEvent ("Checking for updates…"); Thread.Sleep (500); - ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault; - HttpClient web_client = new(); - var uri = new Uri ("https://www.sparkleshare.org/version"); + var uri = new Uri ("https://raw.githubusercontent.com/uenz/SparkleShare/refs/heads/master/version-latest"); HttpClient client = new(); try { - string latest_version = client.GetStringAsync(uri).GetAwaiter().GetResult(); + string latest_version = client.GetStringAsync(uri).GetAwaiter().GetResult().Split(' ')[0].Trim(); if (new Version(latest_version) > new Version(RunningVersion)) UpdateLabelEvent("An update (version " + latest_version + ") is available!"); From adc11cfbf4d4fdbf6d682d9bb7ba895cd2a7d900 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 6 Nov 2024 19:24:41 +0100 Subject: [PATCH 12/14] Removed deprecated functions --- SparkleShare/Common/Avatars.cs | 67 ++++++++++++++++---------- SparkleShare/Common/SetupController.cs | 2 +- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/SparkleShare/Common/Avatars.cs b/SparkleShare/Common/Avatars.cs index e9a5631ad..5ba148a1d 100644 --- a/SparkleShare/Common/Avatars.cs +++ b/SparkleShare/Common/Avatars.cs @@ -18,7 +18,7 @@ using System; using System.IO; using System.Collections.Generic; -using System.Net; +using System.Net.Http; using System.Net.Mime; using System.Net.Security; using System.Security.Cryptography.X509Certificates; @@ -42,9 +42,9 @@ public static string GetAvatar (string email, int size, string target_path, stri email = email.ToLower (); if (skipped_avatars.Contains (email)) - return null; + return null!; - string avatars_path = Path.Combine (Path.GetDirectoryName (target_path), "avatars", size + "x" + size); + string avatars_path = Path.Combine (Path.GetDirectoryName (target_path)!, "avatars", size + "x" + size); // Search avatars by file name, ignore extension // Delete files over a day old @@ -65,10 +65,10 @@ public static string GetAvatar (string email, int size, string target_path, stri } catch (InvalidOperationException e) { Logger.LogInfo ("Avatars", "Error fetching avatar for " + email, e); - return null; + return null!; } - var client = new WebClient (); + //HttpClient client = new(); string url = ""; if (provider == "libravatar") @@ -77,38 +77,55 @@ public static string GetAvatar (string email, int size, string target_path, stri url = "https://secure.gravatar.com/avatar/" + email.MD5 () + ".png?s=" + size + "&d=404"; try { - byte [] buffer = client.DownloadData (url); + using (HttpClient client = new HttpClient()) + { + // Send a GET request to the specified URL + HttpResponseMessage response = client.GetAsync(url).GetAwaiter().GetResult(); - if (client.ResponseHeaders ["content-type"].Equals (MediaTypeNames.Image.Jpeg, StringComparison.InvariantCultureIgnoreCase)) { - avatar_file_path += ".jpg"; + // Ensure the request was successful + response.EnsureSuccessStatusCode(); - } else if (client.ResponseHeaders ["content-type"].Equals (MediaTypeNames.Image.Gif, StringComparison.InvariantCultureIgnoreCase)) { - avatar_file_path += ".gif"; + // Check the headers for content type and other information + var contentType = response.Content.Headers.ContentType; + var contentLength = response.Content.Headers.ContentLength; - } else { - avatar_file_path += ".png"; - } - if (buffer.Length > 255) { - if (!Directory.Exists (avatars_path)) { - Directory.CreateDirectory (avatars_path); - Logger.LogInfo ("Avatars", "Created '" + avatars_path + "'"); - } + byte[] buffer = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); + + if (contentType.MediaType.Equals (MediaTypeNames.Image.Jpeg, StringComparison.InvariantCultureIgnoreCase)) { + avatar_file_path += ".jpg"; - File.WriteAllBytes (avatar_file_path, buffer); - Logger.LogInfo ("Avatars", "Fetched " + size + "x" + size + " avatar for " + email); + } else if (contentType.MediaType.Equals (MediaTypeNames.Image.Gif, StringComparison.InvariantCultureIgnoreCase)) { + avatar_file_path += ".gif"; - return avatar_file_path; + } else { + avatar_file_path += ".png"; + } - } else { - return null; - } + if (buffer.Length > 255) + { + if (!Directory.Exists(avatars_path)) + { + Directory.CreateDirectory(avatars_path); + Logger.LogInfo("Avatars", "Created '" + avatars_path + "'"); + } + + File.WriteAllBytes(avatar_file_path, buffer); + Logger.LogInfo("Avatars", "Fetched " + size + "x" + size + " avatar for " + email); + + return avatar_file_path; + } + else + { + return null!; + } + } } catch (Exception e) { Logger.LogInfo ("Avatars", "Error fetching avatar for " + email, e); skipped_avatars.Add (email); - return null; + return null!; } } diff --git a/SparkleShare/Common/SetupController.cs b/SparkleShare/Common/SetupController.cs index f8fb0b2c1..20791fa85 100644 --- a/SparkleShare/Common/SetupController.cs +++ b/SparkleShare/Common/SetupController.cs @@ -298,7 +298,7 @@ public void AddPageCompleted (string address, string remote_path) ChangePageEvent (PageType.Syncing, null); - address = Uri.EscapeUriString (address.Trim ()); + address = address.Trim (); remote_path = remote_path.Trim (); remote_path = remote_path.TrimEnd ("/".ToCharArray ()); From 9e189f2bf6146fbe10e65da8cf162b46af78cc2a Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 6 Nov 2024 19:25:36 +0100 Subject: [PATCH 13/14] Removed warinings --- .editorconfig | 1 + Sparkles/ScpUri.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index f40fee429..f8221f06e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -280,6 +280,7 @@ dotnet_style_qualification_for_property = false:silent dotnet_style_qualification_for_method = false:silent dotnet_style_qualification_for_event = false:silent +dotnet_diagnostic.IDE0005.severity = none dotnet_diagnostic.IDE0008.severity = none dotnet_diagnostic.IDE0011.severity = none dotnet_diagnostic.IDE0036.severity = none diff --git a/Sparkles/ScpUri.cs b/Sparkles/ScpUri.cs index a98ac1eea..dea1b1f80 100644 --- a/Sparkles/ScpUri.cs +++ b/Sparkles/ScpUri.cs @@ -11,7 +11,7 @@ public class ScpUri public ScpUri([StringSyntax("Uri")] string uriString) { - if (Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri) && uriString.Contains("://")) + if (Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri!) && uriString.Contains("://")) { scp_style = false; } @@ -28,7 +28,7 @@ public ScpUri([StringSyntax("Uri")] string uriString) uriString = uriString.Remove(place, 1).Insert(place, "/"); } - Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri); + Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri!); scp_style = true; } } From 908cf0da7729ff44fc6530d4de7a05b1d4a12243 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 6 Nov 2024 19:41:11 +0100 Subject: [PATCH 14/14] Bug fix in windows installer --- SparkleShare/Windows/Installer/Package.wxs | 4 ++-- SparkleShare/Windows/postBuild.cmd | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SparkleShare/Windows/Installer/Package.wxs b/SparkleShare/Windows/Installer/Package.wxs index 920dee6d1..4f6f0c238 100644 --- a/SparkleShare/Windows/Installer/Package.wxs +++ b/SparkleShare/Windows/Installer/Package.wxs @@ -95,10 +95,10 @@ --> - + diff --git a/SparkleShare/Windows/postBuild.cmd b/SparkleShare/Windows/postBuild.cmd index 635f60be2..dcc6689c1 100755 --- a/SparkleShare/Windows/postBuild.cmd +++ b/SparkleShare/Windows/postBuild.cmd @@ -12,7 +12,7 @@ FOR /F "usebackq tokens=2" %%i IN ("%~dp0git.download") DO SET md5hash=%%i CALL :downloadandverify %url% "%~dp0PortableGit.7z.exe" %md5hash% "%~dp0PortableGit.7z.exe" -o "%OUTDIR%" -y DEL "%~dp0PortableGit.7z.exe" -DEL /s /q "%~dp0OpenSSH-Win32.zip" +REM DEL /s /q "%~dp0OpenSSH-Win32.zip" :skipgitdownload SET ERRORLEVEL=0