From fee47541502892847e05f307b4b584eb8b7b647a Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Wed, 29 Nov 2017 18:04:58 -0800 Subject: [PATCH 01/28] Re-evaluate IsLatest properties when a package is delisted (#42) Fix https://github.com/NuGet/NuGetGallery/issues/3434 --- .../Infrastructure/IServerPackageCache.cs | 4 +- .../Infrastructure/ServerPackageCache.cs | 18 ++-- .../Infrastructure/ServerPackageRepository.cs | 28 +++--- .../Infrastructure/ServerPackageStore.cs | 11 +-- .../Controllers/NuGetODataController.cs | 13 +-- .../Infrastructure/TemporaryDirectory.cs | 9 +- .../ServerPackageCacheTest.cs | 20 ++++- .../ServerPackageRepositoryTest.cs | 86 ++++++++++++++----- 8 files changed, 117 insertions(+), 72 deletions(-) diff --git a/src/NuGet.Server.Core/Infrastructure/IServerPackageCache.cs b/src/NuGet.Server.Core/Infrastructure/IServerPackageCache.cs index 6af757b..50c8d53 100644 --- a/src/NuGet.Server.Core/Infrastructure/IServerPackageCache.cs +++ b/src/NuGet.Server.Core/Infrastructure/IServerPackageCache.cs @@ -18,9 +18,9 @@ public interface IServerPackageCache IEnumerable GetAll(); - void Add(ServerPackage entity); + void Add(ServerPackage entity, bool enableDelisting); - void AddRange(IEnumerable entities); + void AddRange(IEnumerable entities, bool enableDelisting); void Remove(string id, SemanticVersion version, bool enableDelisting); diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageCache.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageCache.cs index 5e9f414..7092c09 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageCache.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageCache.cs @@ -130,7 +130,7 @@ public void Remove(string id, SemanticVersion version, bool enableDelisting) _packages.RemoveWhere(p => IsMatch(p, id, version)); } - UpdateLatestVersions(_packages.Where(p => IsMatch(p, id))); + UpdateLatestVersions(_packages.Where(p => IsMatch(p, id)), enableDelisting); _isDirty = true; } @@ -140,7 +140,7 @@ public void Remove(string id, SemanticVersion version, bool enableDelisting) } } - public void Add(ServerPackage entity) + public void Add(ServerPackage entity, bool enableDelisting) { _syncLock.EnterWriteLock(); try @@ -148,7 +148,7 @@ public void Add(ServerPackage entity) _packages.Remove(entity); _packages.Add(entity); - UpdateLatestVersions(_packages.Where(p => IsMatch(p, entity.Id))); + UpdateLatestVersions(_packages.Where(p => IsMatch(p, entity.Id)), enableDelisting); _isDirty = true; } @@ -158,7 +158,7 @@ public void Add(ServerPackage entity) } } - public void AddRange(IEnumerable entities) + public void AddRange(IEnumerable entities, bool enableDelisting) { _syncLock.EnterWriteLock(); try @@ -169,7 +169,7 @@ public void AddRange(IEnumerable entities) _packages.Add(entity); } - UpdateLatestVersions(_packages); + UpdateLatestVersions(_packages, enableDelisting); _isDirty = true; } @@ -179,7 +179,7 @@ public void AddRange(IEnumerable entities) } } - private static void UpdateLatestVersions(IEnumerable packages) + private static void UpdateLatestVersions(IEnumerable packages, bool enableDelisting) { var semVer1AbsoluteLatest = InitializePackageDictionary(); var semVer1Latest = InitializePackageDictionary(); @@ -195,6 +195,12 @@ private static void UpdateLatestVersions(IEnumerable packages) package.SemVer2IsAbsoluteLatest = false; package.SemVer2IsLatest = false; + // Unlisted packages are never considered "latest". + if (enableDelisting && !package.Listed) + { + return; + } + // Update the SemVer1 views. if (!package.IsSemVer2) { diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs index 04f936d..04d1788 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs @@ -72,17 +72,12 @@ internal ServerPackageRepository( ISettingsProvider settingsProvider = null, Logging.ILogger logger = null) { - if (fileSystem == null) - { - throw new ArgumentNullException(nameof(fileSystem)); - } - if (innerRepository == null) { throw new ArgumentNullException(nameof(innerRepository)); } - _fileSystem = fileSystem; + _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _runBackgroundTasks = runBackgroundTasks; _settingsProvider = settingsProvider ?? new DefaultSettingsProvider(); _logger = logger ?? new TraceLogger(); @@ -272,7 +267,7 @@ private void AddPackagesFromDropFolderWithoutLocking() } // Add packages to metadata store in bulk - _serverPackageCache.AddRange(serverPackages); + _serverPackageCache.AddRange(serverPackages, EnableDelisting); _serverPackageCache.PersistIfDirty(); _logger.Log(LogLevel.Info, "Finished adding packages from drop folder."); @@ -302,7 +297,7 @@ public async Task AddPackageAsync(IPackage package, CancellationToken token) EnableDelisting); // Add the package to the metadata store. - _serverPackageCache.Add(serverPackage); + _serverPackageCache.Add(serverPackage, EnableDelisting); _logger.Log(LogLevel.Info, "Finished adding package {0} {1}.", package.Id, package.Version); } @@ -428,7 +423,7 @@ private async Task RebuildPackageStoreWithoutLockingAsync(CancellationToken toke // Build cache var packages = await ReadPackagesFromDiskWithoutLockingAsync(token); _serverPackageCache.Clear(); - _serverPackageCache.AddRange(packages); + _serverPackageCache.AddRange(packages, EnableDelisting); // Add packages from drop folder AddPackagesFromDropFolderWithoutLocking(); @@ -517,9 +512,11 @@ private void RegisterFileSystemWatcher() if (EnableFileSystemMonitoring && _runBackgroundTasks && _fileSystemWatcher == null && !string.IsNullOrEmpty(Source) && Directory.Exists(Source)) { // ReSharper disable once UseObjectOrCollectionInitializer - _fileSystemWatcher = new FileSystemWatcher(Source); - _fileSystemWatcher.Filter = "*"; - _fileSystemWatcher.IncludeSubdirectories = true; + _fileSystemWatcher = new FileSystemWatcher(Source) + { + Filter = "*", + IncludeSubdirectories = true, + }; _fileSystemWatcher.Changed += FileSystemChangedAsync; _fileSystemWatcher.Created += FileSystemChangedAsync; @@ -643,12 +640,7 @@ private sealed class SuppressedFileSystemWatcher : IDisposable public SuppressedFileSystemWatcher(ServerPackageRepository repository) { - if (repository == null) - { - throw new ArgumentNullException(nameof(repository)); - } - - _repository = repository; + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); } public bool LockTaken => _lockHandle.LockTaken; diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageStore.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageStore.cs index 94eb648..533c850 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageStore.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageStore.cs @@ -45,9 +45,7 @@ public void Remove(string id, SemanticVersion version, bool enableDelisting) { if (enableDelisting) { - var physicalFileSystem = _fileSystem as PhysicalFileSystem; - - if (physicalFileSystem != null) + if (_fileSystem is PhysicalFileSystem physicalFileSystem) { var fileName = physicalFileSystem.GetFullPath( GetPackageFileName(id, version.ToNormalizedString())); @@ -150,10 +148,7 @@ private PackageDerivedData GetPackageDerivedData(IPackage package, bool enableDe var normalizedVersion = package.Version.ToNormalizedString(); var packageFileName = GetPackageFileName(package.Id, normalizedVersion); var hashFileName = GetHashFileName(package.Id, normalizedVersion); - - // File system - var physicalFileSystem = _fileSystem as PhysicalFileSystem; - + // Build package info var packageDerivedData = new PackageDerivedData(); @@ -165,7 +160,7 @@ private PackageDerivedData GetPackageDerivedData(IPackage package, bool enableDe // Read package info var localPackage = package as LocalPackage; - if (physicalFileSystem != null) + if (_fileSystem is PhysicalFileSystem physicalFileSystem) { // Read package info from file system var fullPath = _fileSystem.GetFullPath(packageFileName); diff --git a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs index 250b581..61b74ed 100644 --- a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs +++ b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs @@ -40,12 +40,7 @@ protected NuGetODataController( IServerPackageRepository repository, IPackageAuthenticationService authenticationService = null) { - if (repository == null) - { - throw new ArgumentNullException(nameof(repository)); - } - - _serverRepository = repository; + _serverRepository = repository ?? throw new ArgumentNullException(nameof(repository)); _authenticationService = authenticationService; } @@ -226,8 +221,7 @@ public virtual async Task GetUpdates( var packagesToUpdate = new List(); for (var i = 0; i < idValues.Length; i++) { - SemanticVersion semVersion; - if(SemanticVersion.TryParse(versionValues[i],out semVersion)) + if(SemanticVersion.TryParse(versionValues[i], out var semVersion)) { packagesToUpdate.Add(new PackageBuilder { Id = idValues[i], Version = semVersion }); } @@ -441,8 +435,7 @@ protected HttpResponseMessage CreateStringResponse(HttpStatusCode statusCode, st private string GetApiKeyFromHeader() { string apiKey = null; - IEnumerable values; - if (Request.Headers.TryGetValues(ApiKeyHeader, out values)) + if (Request.Headers.TryGetValues(ApiKeyHeader, out var values)) { apiKey = values.FirstOrDefault(); } diff --git a/test/NuGet.Server.Core.Tests/Infrastructure/TemporaryDirectory.cs b/test/NuGet.Server.Core.Tests/Infrastructure/TemporaryDirectory.cs index 1b18e5d..32334b7 100644 --- a/test/NuGet.Server.Core.Tests/Infrastructure/TemporaryDirectory.cs +++ b/test/NuGet.Server.Core.Tests/Infrastructure/TemporaryDirectory.cs @@ -23,7 +23,14 @@ public void Dispose() { if (Directory.Exists(Path)) { - Directory.Delete(Path, true); + try + { + Directory.Delete(Path, true); + } + catch + { + // Nothing to do here. + } } } diff --git a/test/NuGet.Server.Core.Tests/ServerPackageCacheTest.cs b/test/NuGet.Server.Core.Tests/ServerPackageCacheTest.cs index 21bf498..02b2a70 100644 --- a/test/NuGet.Server.Core.Tests/ServerPackageCacheTest.cs +++ b/test/NuGet.Server.Core.Tests/ServerPackageCacheTest.cs @@ -146,7 +146,7 @@ public void Persist_RetainsSemVer2Version() { Id = PackageId, Version = SemVer2Version - }); + }, enableDelisting: false); // Act actual.Persist(); @@ -232,7 +232,11 @@ public void Exists_IsCaseInsensitive() .Setup(x => x.FileExists(CacheFileName)) .Returns(false); var target = new ServerPackageCache(fileSystem.Object, CacheFileName); - target.Add(new ServerPackage { Id = "NuGet.Versioning", Version = new SemanticVersion("3.5.0-beta2") }); + target.Add(new ServerPackage + { + Id = "NuGet.Versioning", + Version = new SemanticVersion("3.5.0-beta2"), + }, enableDelisting: false); // Act var actual = target.Exists("nuget.versioning", new SemanticVersion("3.5.0-BETA2")); @@ -250,7 +254,11 @@ public void Exists_ReturnsFalseWhenPackageDoesNotExist() .Setup(x => x.FileExists(CacheFileName)) .Returns(false); var target = new ServerPackageCache(fileSystem.Object, CacheFileName); - target.Add(new ServerPackage { Id = "NuGet.Versioning", Version = new SemanticVersion("3.5.0-beta2") }); + target.Add(new ServerPackage + { + Id = "NuGet.Versioning", + Version = new SemanticVersion("3.5.0-beta2"), + }, enableDelisting: false); // Act var actual = target.Exists("NuGet.Frameworks", new SemanticVersion("3.5.0-beta2")); @@ -268,7 +276,11 @@ public void Exists_ReturnsTrueWhenPackageExists() .Setup(x => x.FileExists(CacheFileName)) .Returns(false); var target = new ServerPackageCache(fileSystem.Object, CacheFileName); - target.Add(new ServerPackage { Id = "NuGet.Versioning", Version = new SemanticVersion("3.5.0-beta2") }); + target.Add(new ServerPackage + { + Id = "NuGet.Versioning", + Version = new SemanticVersion("3.5.0-beta2"), + }, enableDelisting: false); // Act var actual = target.Exists("NuGet.Versioning", new SemanticVersion("3.5.0-beta2")); diff --git a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs index ded407a..6fec523 100644 --- a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs +++ b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs @@ -319,19 +319,10 @@ public async Task ServerPackageRepositorySearchUnlisted() using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - Func getSetting = (key, defaultValue) => - { - if (key == "enableDelisting") - { - return true; - } - return defaultValue; - }; - var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test1", "1.0")); - }, getSetting); + }, EnableDelisting); // Assert base setup var packages = (await serverRepository.SearchAsync( @@ -515,12 +506,15 @@ public async Task ServerPackageRepositoryMultipleIds() } } - [Fact] - public async Task ServerPackageRepositorySemVer1IsAbsoluteLatest() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ServerPackageRepositorySemVer1IsAbsoluteLatest(bool enableDelisting) { using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "2.0-alpha")); @@ -528,8 +522,14 @@ public async Task ServerPackageRepositorySemVer1IsAbsoluteLatest() repository.AddPackage(CreatePackage("test", "2.2-beta")); repository.AddPackage(CreatePackage("test", "2.3")); repository.AddPackage(CreatePackage("test", "2.4.0-prerel")); + repository.AddPackage(CreatePackage("test", "2.5.0-prerel")); repository.AddPackage(CreatePackage("test", "3.2.0+taggedOnly")); - }); + }, getSetting); + + await serverRepository.RemovePackageAsync( + "test", + new SemanticVersion("2.5.0-prerel"), + CancellationToken.None); // Act var packages = await serverRepository.GetPackagesAsync(ClientCompatibility.Default, Token); @@ -540,12 +540,15 @@ public async Task ServerPackageRepositorySemVer1IsAbsoluteLatest() } } - [Fact] - public async Task ServerPackageRepositorySemVer2IsAbsoluteLatest() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ServerPackageRepositorySemVer2IsAbsoluteLatest(bool enableDelisting) { using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "2.0-alpha")); @@ -554,7 +557,13 @@ public async Task ServerPackageRepositorySemVer2IsAbsoluteLatest() repository.AddPackage(CreatePackage("test", "2.3")); repository.AddPackage(CreatePackage("test", "2.4.0-prerel")); repository.AddPackage(CreatePackage("test", "3.2.0+taggedOnly")); - }); + repository.AddPackage(CreatePackage("test", "3.3.0+unlisted")); + }, getSetting); + + await serverRepository.RemovePackageAsync( + "test", + new SemanticVersion("3.3.0+unlisted"), + CancellationToken.None); // Act var packages = await serverRepository.GetPackagesAsync(ClientCompatibility.Max, Token); @@ -586,18 +595,27 @@ public async Task ServerPackageRepositoryIsLatestOnlyPreRel() } } - [Fact] - public async Task ServerPackageRepositorySemVer1IsLatest() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ServerPackageRepositorySemVer1IsLatest(bool enableDelisting) { using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test1", "1.0.0")); + repository.AddPackage(CreatePackage("test1", "1.1.0")); repository.AddPackage(CreatePackage("test1", "1.2.0+taggedOnly")); repository.AddPackage(CreatePackage("test1", "2.0.0-alpha")); - }); + }, getSetting); + + await serverRepository.RemovePackageAsync( + "test1", + new SemanticVersion("1.1.0"), + CancellationToken.None); // Act var packages = await serverRepository.GetPackagesAsync(ClientCompatibility.Default, Token); @@ -608,21 +626,30 @@ public async Task ServerPackageRepositorySemVer1IsLatest() } } - [Fact] - public async Task ServerPackageRepositorySemVer2IsLatest() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ServerPackageRepositorySemVer2IsLatest(bool enableDelisting) { using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "1.11")); + repository.AddPackage(CreatePackage("test", "2.0")); repository.AddPackage(CreatePackage("test", "1.9")); repository.AddPackage(CreatePackage("test", "2.0-alpha")); repository.AddPackage(CreatePackage("test1", "1.0.0")); repository.AddPackage(CreatePackage("test1", "1.2.0+taggedOnly")); repository.AddPackage(CreatePackage("test1", "2.0.0-alpha")); - }); + }, getSetting); + + await serverRepository.RemovePackageAsync( + "test", + new SemanticVersion("2.0"), + CancellationToken.None); // Act var packages = await serverRepository.GetPackagesAsync(ClientCompatibility.Max, Token); @@ -795,7 +822,10 @@ private static IPackage CreateMockPackage(string id, string version) return package.Object; } - private IPackage CreatePackage(string id, string version, PackageDependency packageDependency = null) + private IPackage CreatePackage( + string id, + string version, + PackageDependency packageDependency = null) { var parsedVersion = new SemanticVersion(version); var packageBuilder = new PackageBuilder @@ -848,5 +878,15 @@ private IPackage CreatePackage(string id, string version, PackageDependency pack return outputPackage; } + + private static bool EnableDelisting(string key, bool defaultValue) + { + if (key == "enableDelisting") + { + return true; + } + + return defaultValue; + } } } From 2011cd4e3cbb5f696b45d109772ae0a6b822cc9c Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Wed, 29 Nov 2017 21:33:57 -0800 Subject: [PATCH 02/28] When a package is unlisted, return the package's published date as 1900 (#41) Fix https://github.com/NuGet/NuGetGallery/issues/4020 --- .../DataServices/PackageExtensions.cs | 4 +- .../PackageExtensionsTest.cs | 47 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/NuGet.Server.Core/DataServices/PackageExtensions.cs b/src/NuGet.Server.Core/DataServices/PackageExtensions.cs index 9f686b1..03ea2c3 100644 --- a/src/NuGet.Server.Core/DataServices/PackageExtensions.cs +++ b/src/NuGet.Server.Core/DataServices/PackageExtensions.cs @@ -11,6 +11,8 @@ namespace NuGet.Server.Core.DataServices { public static class PackageExtensions { + private static readonly DateTime PublishedForUnlisted = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); + public static ODataPackage AsODataPackage(this IServerPackage package, ClientCompatibility compatibility) { return new ODataPackage @@ -31,7 +33,7 @@ public static ODataPackage AsODataPackage(this IServerPackage package, ClientCom Description = package.Description, Summary = package.Summary, ReleaseNotes = package.ReleaseNotes, - Published = package.Created.UtcDateTime, + Published = package.Listed ? package.Created.UtcDateTime : PublishedForUnlisted, LastUpdated = package.LastUpdated.UtcDateTime, Dependencies = string.Join("|", package.DependencySets.SelectMany(ConvertDependencySetToStrings)), PackageHash = package.PackageHash, diff --git a/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs b/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs index 0782ab7..33fb734 100644 --- a/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs +++ b/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Linq; using NuGet.Server.Core.DataServices; using NuGet.Server.Core.Infrastructure; @@ -10,6 +11,52 @@ namespace NuGet.Server.Core.Tests { public class PackageExtensionsTest { + [Fact] + public void AsODataPackage_Uses1900ForUnlistedPublished() + { + // Arrange + var package = new ServerPackage + { + Version = new SemanticVersion("0.1.0"), + Authors = Enumerable.Empty(), + Owners = Enumerable.Empty(), + + Listed = false, + Created = new DateTimeOffset(2017, 11, 29, 21, 21, 32, TimeSpan.FromHours(-8)), + }; + + // Act + var actual = package.AsODataPackage(ClientCompatibility.Max); + + // Assert + Assert.Equal( + new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc), + actual.Published); + } + + [Fact] + public void AsODataPackage_UsesCreatedForListedPublished() + { + // Arrange + var package = new ServerPackage + { + Version = new SemanticVersion("0.1.0"), + Authors = Enumerable.Empty(), + Owners = Enumerable.Empty(), + + Listed = true, + Created = new DateTimeOffset(2017, 11, 29, 21, 21, 32, TimeSpan.FromHours(-8)), + }; + + // Act + var actual = package.AsODataPackage(ClientCompatibility.Max); + + // Assert + Assert.Equal( + new DateTime(2017, 11, 30, 5, 21, 32, DateTimeKind.Utc), + actual.Published); + } + [Theory] [InlineData(true, true, false, false, 1, true, true)] [InlineData(false, false, true, true, 1, false, false)] From ffc2b360c0d4a35a57e5971b35390c6f06b213fb Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 30 Nov 2017 13:47:42 -0800 Subject: [PATCH 03/28] Use latest build tools --- build.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.ps1 b/build.ps1 index 400c31e..67d48e2 100644 --- a/build.ps1 +++ b/build.ps1 @@ -9,7 +9,7 @@ param ( [string]$SemanticVersion = '1.0.0-zlocal', [string]$Branch, [string]$CommitSHA, - [string]$BuildBranch = '1c479a7381ebbc0fe1fded765de70d513b8bd68e' + [string]$BuildBranch = '802a2329581ab88326bf1fd442595bac6dbaa848' ) # For TeamCity - If any issue occurs, this script fail the build. - By default, TeamCity returns an exit code of 0 for all powershell scripts, even if they fail @@ -69,7 +69,7 @@ Invoke-BuildStep 'Set version metadata in AssemblyInfo.cs' { Invoke-BuildStep 'Building solution' { $SolutionPath = Join-Path $PSScriptRoot "NuGet.Server.sln" - Build-Solution $Configuration $BuildNumber -MSBuildVersion "14" $SolutionPath -SkipRestore:$SkipRestore ` + Build-Solution $Configuration $BuildNumber -MSBuildVersion "15" $SolutionPath -SkipRestore:$SkipRestore ` } ` -ev +BuildErrors From f5aa2bad57de536068dd530de536117be4548d7a Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Fri, 15 Dec 2017 10:50:35 -0800 Subject: [PATCH 04/28] Support projection on the Packages() endpoint ($select) (#46) Fix https://github.com/NuGet/NuGetGallery/issues/5193 --- .../Controllers/NuGetODataController.cs | 3 -- test/NuGet.Server.Tests/IntegrationTests.cs | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs index 61b74ed..f7ce0db 100644 --- a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs +++ b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs @@ -45,10 +45,7 @@ protected NuGetODataController( } // GET /Packages - // Never seen this invoked. NuGet.Exe and Visual Studio seems to use 'Search' for all package listing. - // Probably required to be OData compliant? [HttpGet] - [EnableQuery(PageSize = 100, HandleNullPropagation = HandleNullPropagationOption.False)] public virtual async Task Get( ODataQueryOptions options, [FromUri] string semVerLevel = "", diff --git a/test/NuGet.Server.Tests/IntegrationTests.cs b/test/NuGet.Server.Tests/IntegrationTests.cs index 477ce90..5297d02 100644 --- a/test/NuGet.Server.Tests/IntegrationTests.cs +++ b/test/NuGet.Server.Tests/IntegrationTests.cs @@ -98,6 +98,40 @@ public async Task PushPackageThenReadPackages() } } + [Theory] + [MemberData(nameof(EndpointsSupportingProjection))] + public async Task CanQueryUsingProjection(string endpoint) + { + // Arrange + using (var tc = new TestContext()) + { + var packagePath = Path.Combine(tc.PackagesDirectory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, packagePath); + + // Act + using (var request = new HttpRequestMessage(HttpMethod.Get, $"/nuget/{endpoint}$select=Id,Version")) + using (var response = await tc.Client.SendAsync(request)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Contains(TestData.PackageId, content); + Assert.Contains(TestData.PackageVersionString, content); + } + } + } + + public static IEnumerable EndpointsSupportingProjection + { + get + { + yield return new object[] { "Packages()?" }; + yield return new object[] { "Search()?searchTerm=''&targetFramework=''&includePrerelease=true&includeDelisted=true&" }; + yield return new object[] { $"FindPackagesById()?id='{TestData.PackageId}'&" }; + } + } + private sealed class TestContext : IDisposable { private readonly HttpServer _server; From 135a92001b876fc4604238f5efe9305a1e5e4c91 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Sun, 21 Jan 2018 15:02:12 -0800 Subject: [PATCH 05/28] Use a null file system for the OptimizedZipPackage's expanded file system (#48) Address https://github.com/NuGet/NuGetGallery/issues/5230 --- src/NuGet.Server.Core/Core/NullFileSystem.cs | 40 +++++ src/NuGet.Server.Core/Core/PackageFactory.cs | 29 ++++ .../Infrastructure/ServerPackageRepository.cs | 2 +- .../NuGet.Server.Core.csproj | 2 + .../Controllers/NuGetODataController.cs | 4 +- .../Core/PackageFactoryTest.cs | 83 ++++++++++ .../NuGet.Server.Core.Tests.csproj | 1 + test/NuGet.Server.Tests/IntegrationTests.cs | 146 +++++++++++++++++- 8 files changed, 300 insertions(+), 7 deletions(-) create mode 100644 src/NuGet.Server.Core/Core/NullFileSystem.cs create mode 100644 src/NuGet.Server.Core/Core/PackageFactory.cs create mode 100644 test/NuGet.Server.Core.Tests/Core/PackageFactoryTest.cs diff --git a/src/NuGet.Server.Core/Core/NullFileSystem.cs b/src/NuGet.Server.Core/Core/NullFileSystem.cs new file mode 100644 index 0000000..cda19b1 --- /dev/null +++ b/src/NuGet.Server.Core/Core/NullFileSystem.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; + +namespace NuGet.Server.Core +{ + /// + /// A file system implementation that persists nothing. This is intended to be used with + /// so that package files are never actually extracted anywhere on disk. + /// + public class NullFileSystem : IFileSystem + { + public static NullFileSystem Instance { get; } = new NullFileSystem(); + + public Stream CreateFile(string path) => Stream.Null; + public bool DirectoryExists(string path) => true; + public bool FileExists(string path) => false; + public string GetFullPath(string path) => null; + public Stream OpenFile(string path) => Stream.Null; + + public ILogger Logger { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + public string Root => throw new NotSupportedException(); + public void AddFile(string path, Stream stream) => throw new NotSupportedException(); + public void AddFile(string path, Action writeToStream) => throw new NotSupportedException(); + public void AddFiles(IEnumerable files, string rootDir) => throw new NotSupportedException(); + public void DeleteDirectory(string path, bool recursive) => throw new NotSupportedException(); + public void DeleteFile(string path) => throw new NotSupportedException(); + public void DeleteFiles(IEnumerable files, string rootDir) => throw new NotSupportedException(); + public DateTimeOffset GetCreated(string path) => throw new NotSupportedException(); + public IEnumerable GetDirectories(string path) => throw new NotSupportedException(); + public IEnumerable GetFiles(string path, string filter, bool recursive) => throw new NotSupportedException(); + public DateTimeOffset GetLastAccessed(string path) => throw new NotSupportedException(); + public DateTimeOffset GetLastModified(string path) => throw new NotSupportedException(); + public void MakeFileWritable(string path) => throw new NotSupportedException(); + public void MoveFile(string source, string destination) => throw new NotSupportedException(); + } +} diff --git a/src/NuGet.Server.Core/Core/PackageFactory.cs b/src/NuGet.Server.Core/Core/PackageFactory.cs new file mode 100644 index 0000000..59a3eb0 --- /dev/null +++ b/src/NuGet.Server.Core/Core/PackageFactory.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace NuGet.Server.Core +{ + public static class PackageFactory + { + public static IPackage Open(string fullPackagePath) + { + if (string.IsNullOrEmpty(fullPackagePath)) + { + throw new ArgumentNullException(nameof(fullPackagePath)); + } + + var directoryName = Path.GetDirectoryName(fullPackagePath); + var fileName = Path.GetFileName(fullPackagePath); + + var fileSystem = new PhysicalFileSystem(directoryName); + + return new OptimizedZipPackage( + fileSystem, + fileName, + NullFileSystem.Instance); + } + } +} diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs index 04d1788..38aa495 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs @@ -236,7 +236,7 @@ private void AddPackagesFromDropFolderWithoutLocking() try { // Create package - var package = new OptimizedZipPackage(_fileSystem, packageFile); + var package = PackageFactory.Open(_fileSystem.GetFullPath(packageFile)); if (!CanPackageBeAddedWithoutLocking(package, shouldThrow: false)) { diff --git a/src/NuGet.Server.Core/NuGet.Server.Core.csproj b/src/NuGet.Server.Core/NuGet.Server.Core.csproj index 07c3a26..3f7cbb1 100644 --- a/src/NuGet.Server.Core/NuGet.Server.Core.csproj +++ b/src/NuGet.Server.Core/NuGet.Server.Core.csproj @@ -58,7 +58,9 @@ + + diff --git a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs index f7ce0db..03c7d74 100644 --- a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs +++ b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs @@ -14,6 +14,7 @@ using System.Web.Http; using System.Web.Http.OData; using System.Web.Http.OData.Query; +using NuGet.Server.Core; using NuGet.Server.Core.DataServices; using NuGet.Server.Core.Infrastructure; using NuGet.Server.V2.Model; @@ -396,8 +397,7 @@ public virtual async Task UploadPackage(CancellationToken t } } - var package = new OptimizedZipPackage(temporaryFile); - + var package = PackageFactory.Open(temporaryFile); HttpResponseMessage retValue; if (_authenticationService.IsAuthenticated(User, apiKey, package.Id)) diff --git a/test/NuGet.Server.Core.Tests/Core/PackageFactoryTest.cs b/test/NuGet.Server.Core.Tests/Core/PackageFactoryTest.cs new file mode 100644 index 0000000..22364fb --- /dev/null +++ b/test/NuGet.Server.Core.Tests/Core/PackageFactoryTest.cs @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using NuGet.Server.Core.Tests.Infrastructure; +using Xunit; + +namespace NuGet.Server.Core.Tests.Core +{ + public class PackageFactoryTest + { + public class Open : IDisposable + { + private readonly TemporaryDirectory _directory; + + public Open() + { + _directory = new TemporaryDirectory(); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void RejectsInvalidPaths(string path) + { + var ex = Assert.Throws( + () => PackageFactory.Open(path)); + Assert.Equal("fullPackagePath", ex.ParamName); + } + + [Fact] + public void InitializesPackageWithMetadata() + { + // Arrange + var path = Path.Combine(_directory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, path); + + // Act + var package = PackageFactory.Open(path); + + // Assert + Assert.Equal(TestData.PackageId, package.Id); + Assert.Equal(TestData.PackageVersion, package.Version); + } + + [Fact] + public void InitializesPackageWithSupportedFrameworks() + { + // Arrange + var path = Path.Combine(_directory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, path); + + // Act + var package = PackageFactory.Open(path); + + // Assert + var frameworks = package.GetSupportedFrameworks(); + var framework = Assert.Single(frameworks); + Assert.Equal(VersionUtility.ParseFrameworkName("net40-client"), framework); + } + + [Fact] + public void InitializesPackageWhichCanBeCheckedForSymbols() + { + // Arrange + var path = Path.Combine(_directory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, path); + + // Act + var package = PackageFactory.Open(path); + + // Assert + Assert.False(package.IsSymbolsPackage(), "The provided package is not a symbols package."); + } + + public void Dispose() + { + _directory?.Dispose(); + } + } + } +} diff --git a/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj b/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj index 1d7d70b..3bac6ab 100644 --- a/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj +++ b/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj @@ -81,6 +81,7 @@ + diff --git a/test/NuGet.Server.Tests/IntegrationTests.cs b/test/NuGet.Server.Tests/IntegrationTests.cs index 5297d02..312d500 100644 --- a/test/NuGet.Server.Tests/IntegrationTests.cs +++ b/test/NuGet.Server.Tests/IntegrationTests.cs @@ -6,13 +6,16 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading; using System.Threading.Tasks; using System.Web.Http; using System.Web.Http.Dependencies; using NuGet.Server.App_Start; +using NuGet.Server.Core.Infrastructure; using NuGet.Server.Core.Tests; using NuGet.Server.Core.Tests.Infrastructure; using Xunit; @@ -56,6 +59,72 @@ public async Task DropPackageThenReadPackages() } } + [Fact] + public async Task DownloadPackage() + { + // Arrange + using (var tc = new TestContext()) + { + // Act & Assert + // 1. Write a package to the drop folder. + var packagePath = Path.Combine(tc.PackagesDirectory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, packagePath); + var expectedBytes = File.ReadAllBytes(packagePath); + + // 2. Download the package. + using (var request = new HttpRequestMessage( + HttpMethod.Get, + $"/nuget/Packages(Id='{TestData.PackageId}',Version='{TestData.PackageVersionString}')/Download")) + using (var response = await tc.Client.SendAsync(request)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var actualBytes = await response.Content.ReadAsByteArrayAsync(); + + Assert.Equal("binary/octet-stream", response.Content.Headers.ContentType.ToString()); + Assert.Equal(expectedBytes, actualBytes); + } + } + } + + [Fact] + public async Task FilterOnFramework() + { + // Arrange + using (var tc = new TestContext()) + { + tc.Settings["enableFrameworkFiltering"] = "true"; + + // Act & Assert + // 1. Write a package to the drop folder. + var packagePath = Path.Combine(tc.PackagesDirectory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, packagePath); + + // 2. Search for all packages supporting .NET Framework 4.6 (this should match the test package) + using (var request = new HttpRequestMessage( + HttpMethod.Get, + $"/nuget/Search?targetFramework='net46'")) + using (var response = await tc.Client.SendAsync(request)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var content = await response.Content.ReadAsStringAsync(); + + Assert.Contains(TestData.PackageId, content); + } + + // 3. Search for all packages supporting .NET Framework 2.0 (this should match nothing) + using (var request = new HttpRequestMessage( + HttpMethod.Get, + $"/nuget/Search?targetFramework='net20'")) + using (var response = await tc.Client.SendAsync(request)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var content = await response.Content.ReadAsStringAsync(); + + Assert.DoesNotContain(TestData.PackageId, content); + } + } + } + [Fact] public async Task PushPackageThenReadPackages() { @@ -122,6 +191,75 @@ public async Task CanQueryUsingProjection(string endpoint) } } + [Fact] + public async Task DoesNotWriteToNuGetScratch() + { + // Arrange + OptimizedZipPackage.PurgeCache(); + var expectedTempEntries = Directory + .GetFileSystemEntries(Path.Combine(Path.GetTempPath(), "NuGetScratch")) + .OrderBy(x => x) + .ToList(); + + using (var tc = new TestContext()) + { + tc.Settings["enableFrameworkFiltering"] = "true"; + tc.Settings["allowOverrideExistingPackageOnPush"] = "true"; + + string apiKey = "foobar"; + tc.SetApiKey(apiKey); + + // Act & Assert + // 1. Write a package to the drop folder. + var packagePath = Path.Combine(tc.PackagesDirectory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, packagePath); + + // 2. Search for packages. + using (var request = new HttpRequestMessage( + HttpMethod.Get, + $"/nuget/Search?targetFramework='net46'")) + using (var response = await tc.Client.SendAsync(request)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + // 3. Push the package. + var pushPath = Path.Combine(tc.TemporaryDirectory, "package.nupkg"); + TestData.CopyResourceToPath(TestData.PackageResource, pushPath); + using (var request = new HttpRequestMessage(HttpMethod.Put, "/nuget") + { + Headers = + { + { "X-NUGET-APIKEY", apiKey } + }, + Content = tc.GetFileUploadContent(pushPath), + }) + { + using (request) + using (var response = await tc.Client.SendAsync(request)) + { + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + } + } + + // 4. Search for packages again. + using (var request = new HttpRequestMessage( + HttpMethod.Get, + $"/nuget/Search?targetFramework='net46'")) + using (var response = await tc.Client.SendAsync(request)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + // 6. Make sure we have not added more temp files. + var actualTempEntries = Directory + .GetFileSystemEntries(Path.Combine(Path.GetTempPath(), "NuGetScratch")) + .OrderBy(x => x) + .ToList(); + Assert.Equal(expectedTempEntries, actualTempEntries); + } + } + public static IEnumerable EndpointsSupportingProjection { get @@ -135,7 +273,6 @@ public static IEnumerable EndpointsSupportingProjection private sealed class TestContext : IDisposable { private readonly HttpServer _server; - private readonly DefaultServiceResolver _serviceResolver; private readonly HttpConfiguration _config; public TestContext() @@ -149,11 +286,11 @@ public TestContext() { "apiKey", string.Empty } }; - _serviceResolver = new DefaultServiceResolver(PackagesDirectory, Settings); + ServiceResolver = new DefaultServiceResolver(PackagesDirectory, Settings); _config = new HttpConfiguration(); _config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; - _config.DependencyResolver = new DependencyResolverAdapter(_serviceResolver); + _config.DependencyResolver = new DependencyResolverAdapter(ServiceResolver); NuGetODataConfig.Initialize(_config, "TestablePackagesOData"); @@ -162,6 +299,7 @@ public TestContext() Client.BaseAddress = new Uri("http://localhost/"); } + public DefaultServiceResolver ServiceResolver { get; } public TemporaryDirectory TemporaryDirectory { get; } public TemporaryDirectory PackagesDirectory { get; } public NameValueCollection Settings { get; } @@ -198,7 +336,7 @@ public void Dispose() Client.Dispose(); _server.Dispose(); _config.Dispose(); - _serviceResolver.Dispose(); + ServiceResolver.Dispose(); PackagesDirectory.Dispose(); TemporaryDirectory.Dispose(); } From 44ce5767383e048bd83605d7af3d810aa32a6d93 Mon Sep 17 00:00:00 2001 From: Tarjei Olsen Date: Sat, 3 Feb 2018 18:27:44 +0100 Subject: [PATCH 06/28] Configurable cache file name (#49) * Added support for custom cache file name. Handy when you have configured two feeds pointing to the same packages folder. * added some air after commas in type parameter * added curlies, even on one liners * renamed misleading InitializeServerPackageStore method to InitializeServerPackageCache * Updated XML comment explaining the custom cache file name option * Throwing up on invalid cache file name. Added tests to prove it. * compacting test arrange code --- .../DictionarySettingsProvider.cs | 12 ++- .../Program.cs | 2 +- .../Infrastructure/DefaultSettingsProvider.cs | 5 ++ .../Infrastructure/ISettingsProvider.cs | 1 + .../Infrastructure/ServerPackageRepository.cs | 38 +++++++- src/NuGet.Server.Core/Strings.Designer.cs | 9 ++ src/NuGet.Server.Core/Strings.resx | 3 + .../WebConfigSettingsProvider.cs | 6 ++ src/NuGet.Server/Web.config | 6 ++ .../Infrastructure/FuncSettingsProvider.cs | 11 ++- .../ServerPackageRepositoryTest.cs | 89 +++++++++++++++++-- 11 files changed, 165 insertions(+), 17 deletions(-) diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs b/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs index 8958501..1bc49c9 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs +++ b/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using NuGet.Server.Core.Infrastructure; @@ -7,9 +8,9 @@ namespace NuGet.Server.V2.Samples.OwinHost { public class DictionarySettingsProvider : ISettingsProvider { - readonly Dictionary _settings; + readonly Dictionary _settings; - public DictionarySettingsProvider(Dictionary settings) + public DictionarySettingsProvider(Dictionary settings) { _settings = settings; } @@ -18,8 +19,13 @@ public DictionarySettingsProvider(Dictionary settings) public bool GetBoolSetting(string key, bool defaultValue) { System.Diagnostics.Debug.WriteLine("getSetting: " + key); - return _settings.ContainsKey(key) ? _settings[key] : defaultValue; + return _settings.ContainsKey(key) ? Convert.ToBoolean(_settings[key]) : defaultValue; } + + public string GetStringSetting(string key, string defaultValue) + { + return _settings.ContainsKey(key) ? Convert.ToString(_settings[key]) : defaultValue; + } } } diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs b/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs index 6f952bd..46e775f 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs +++ b/samples/NuGet.Server.V2.Samples.OwinHost/Program.cs @@ -24,7 +24,7 @@ static void Main(string[] args) // Set up a common settingsProvider to be used by all repositories. // If a setting is not present in dictionary default value will be used. - var settings = new Dictionary(); + var settings = new Dictionary(); settings.Add("enableDelisting", false); //default=false settings.Add("enableFrameworkFiltering", false); //default=false settings.Add("ignoreSymbolsPackages", true); //default=false diff --git a/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs b/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs index 65f31e1..b3c5ce9 100644 --- a/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs +++ b/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs @@ -8,5 +8,10 @@ public bool GetBoolSetting(string key, bool defaultValue) { return defaultValue; } + + public string GetStringSetting(string key, string defaultValue) + { + return defaultValue; + } } } diff --git a/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs b/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs index bd20aa5..6fca200 100644 --- a/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs +++ b/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs @@ -5,5 +5,6 @@ namespace NuGet.Server.Core.Infrastructure public interface ISettingsProvider { bool GetBoolSetting(string key, bool defaultValue); + string GetStringSetting(string key, string defaultValue); } } diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs index 38aa495..6dcd9c6 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs @@ -58,7 +58,7 @@ public ServerPackageRepository( _runBackgroundTasks = true; _settingsProvider = settingsProvider ?? new DefaultSettingsProvider(); _logger = logger ?? new TraceLogger(); - _serverPackageCache = InitializeServerPackageStore(); + _serverPackageCache = InitializeServerPackageCache(); _serverPackageStore = new ServerPackageStore( _fileSystem, new ExpandedPackageRepository(_fileSystem, hashProvider), @@ -81,7 +81,7 @@ internal ServerPackageRepository( _runBackgroundTasks = runBackgroundTasks; _settingsProvider = settingsProvider ?? new DefaultSettingsProvider(); _logger = logger ?? new TraceLogger(); - _serverPackageCache = InitializeServerPackageStore(); + _serverPackageCache = InitializeServerPackageCache(); _serverPackageStore = new ServerPackageStore( _fileSystem, innerRepository, @@ -105,9 +105,39 @@ internal ServerPackageRepository( private bool EnableFileSystemMonitoring => _settingsProvider.GetBoolSetting("enableFileSystemMonitoring", true); - private ServerPackageCache InitializeServerPackageStore() + private string CacheFileName => _settingsProvider.GetStringSetting("cacheFileName", null); + + private ServerPackageCache InitializeServerPackageCache() { - return new ServerPackageCache(_fileSystem, Environment.MachineName.ToLowerInvariant() + ".cache.bin"); + return new ServerPackageCache(_fileSystem, ResolveCacheFileName()); + } + + private string ResolveCacheFileName() + { + var fileName = CacheFileName; + const string suffix = ".cache.bin"; + + if (String.IsNullOrWhiteSpace(fileName)) + { + // Default file name + return Environment.MachineName.ToLowerInvariant() + suffix; + } + + if (fileName.LastIndexOfAny(Path.GetInvalidFileNameChars()) > 0) + { + var message = string.Format(Strings.Error_InvalidCacheFileName, fileName); + + _logger.Log(LogLevel.Error, message); + + throw new InvalidOperationException(message); + } + + if (fileName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) + { + return fileName; + } + + return fileName + suffix; } /// diff --git a/src/NuGet.Server.Core/Strings.Designer.cs b/src/NuGet.Server.Core/Strings.Designer.cs index 687e05d..7aaf25b 100644 --- a/src/NuGet.Server.Core/Strings.Designer.cs +++ b/src/NuGet.Server.Core/Strings.Designer.cs @@ -60,6 +60,15 @@ internal Strings() { } } + /// + /// Looks up a localized string similar to Configured cache file name '{0}' is invalid. Keep it simple; No paths allowed.. + /// + internal static string Error_InvalidCacheFileName { + get { + return ResourceManager.GetString("Error_InvalidCacheFileName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Package {0} already exists. The server is configured to not allow overwriting packages that already exist.. /// diff --git a/src/NuGet.Server.Core/Strings.resx b/src/NuGet.Server.Core/Strings.resx index f3e2b0b..9b10159 100644 --- a/src/NuGet.Server.Core/Strings.resx +++ b/src/NuGet.Server.Core/Strings.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Configured cache file name '{0}' is invalid. Keep it simple; No paths allowed. + Package {0} already exists. The server is configured to not allow overwriting packages that already exist. diff --git a/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs b/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs index 658cf08..0e52495 100644 --- a/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs +++ b/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs @@ -28,5 +28,11 @@ public bool GetBoolSetting(string key, bool defaultValue) bool value; return !bool.TryParse(settings[key], out value) ? defaultValue : value; } + + public string GetStringSetting(string key, string defaultValue) + { + var settings = _getSettings(); + return settings[key] ?? defaultValue; + } } } \ No newline at end of file diff --git a/src/NuGet.Server/Web.config b/src/NuGet.Server/Web.config index b084c2c..6be0921 100644 --- a/src/NuGet.Server/Web.config +++ b/src/NuGet.Server/Web.config @@ -22,6 +22,12 @@ --> + + + diff --git a/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs b/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs index 87ebf8c..b18397e 100644 --- a/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs +++ b/test/NuGet.Server.Core.Tests/Infrastructure/FuncSettingsProvider.cs @@ -7,8 +7,8 @@ namespace NuGet.Server.Core.Tests.Infrastructure { class FuncSettingsProvider : ISettingsProvider { - readonly Func _getSetting; - internal FuncSettingsProvider(Func getSetting) + readonly Func _getSetting; + internal FuncSettingsProvider(Func getSetting) { if (getSetting == null) { @@ -20,7 +20,12 @@ internal FuncSettingsProvider(Func getSetting) public bool GetBoolSetting(string key, bool defaultValue) { - return _getSetting(key, defaultValue); + return Convert.ToBoolean(_getSetting(key, defaultValue)); + } + + public string GetStringSetting(string key, string defaultValue) + { + return Convert.ToString(_getSetting(key, defaultValue)); } } } diff --git a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs index 6fec523..b813d94 100644 --- a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs +++ b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs @@ -23,7 +23,7 @@ public class ServerPackageRepositoryTest public static async Task CreateServerPackageRepositoryAsync( string path, Action setupRepository = null, - Func getSetting = null) + Func getSetting = null) { var fileSystem = new PhysicalFileSystem(path); var expandedPackageRepository = new ExpandedPackageRepository(fileSystem); @@ -514,7 +514,7 @@ public async Task ServerPackageRepositorySemVer1IsAbsoluteLatest(bool enableDeli using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "2.0-alpha")); @@ -548,7 +548,7 @@ public async Task ServerPackageRepositorySemVer2IsAbsoluteLatest(bool enableDeli using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "2.0-alpha")); @@ -603,7 +603,7 @@ public async Task ServerPackageRepositorySemVer1IsLatest(bool enableDelisting) using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test1", "1.0.0")); @@ -634,7 +634,7 @@ public async Task ServerPackageRepositorySemVer2IsLatest(bool enableDelisting) using (var temporaryDirectory = new TemporaryDirectory()) { // Arrange - var getSetting = enableDelisting ? EnableDelisting : (Func)null; + var getSetting = enableDelisting ? EnableDelisting : (Func)null; var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => { repository.AddPackage(CreatePackage("test", "1.11")); @@ -811,6 +811,83 @@ public async Task ServerPackageRepositoryAddPackageRejectsDuplicatesWithSemVer2( } } + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public async Task ServerPackageRepository_CustomCacheFileNameNotConfigured_UseMachineNameAsFileName(string fileNameFromConfig) + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + ServerPackageRepository serverRepository = await CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? fileNameFromConfig : defaultValue); + + string expectedCacheFileName = Path.Combine(serverRepository.Source, Environment.MachineName.ToLowerInvariant() + ".cache.bin"); + + Assert.True(File.Exists(expectedCacheFileName)); + } + } + + [Fact] + public async Task ServerPackageRepository_CustomCacheFileNameIsConfigured_CustomCacheFileIsCreated() + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + ServerPackageRepository serverRepository = await CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? "CustomFileName.cache.bin" : defaultValue); + + string expectedCacheFileName = Path.Combine(serverRepository.Source, "CustomFileName.cache.bin"); + + Assert.True(File.Exists(expectedCacheFileName)); + } + } + + [Fact] + public async Task ServerPackageRepository_CustomCacheFileNameWithoutExtensionIsConfigured_CustomCacheFileWithExtensionIsCreated() + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + ServerPackageRepository serverRepository = await CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? "CustomFileName" : defaultValue); + + string expectedCacheFileName = Path.Combine(serverRepository.Source, "CustomFileName.cache.bin"); + + Assert.True(File.Exists(expectedCacheFileName)); + } + } + + [Theory] + [InlineData("c:\\file\\is\\a\\path\\to\\Awesome.cache.bin")] + [InlineData("random:invalidFileName.cache.bin")] + public async Task ServerPackageRepository_CustomCacheFileNameIsInvalid_ThrowUp(string invlaidCacheFileName) + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + Task Code() => CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? invlaidCacheFileName : defaultValue); + + await Assert.ThrowsAsync(Code); + } + } + + [Fact] + public async Task ServerPackageRepository_CustomCacheFileNameIsInvalid_ThrowUpWithCorrectErrorMessage() + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + Task Code() => CreateServerPackageRepositoryAsync( + temporaryDirectory.Path, + getSetting: (key, defaultValue) => key == "cacheFileName" ? "foo:bar/baz" : defaultValue); + + var expectedMessage = "Configured cache file name 'foo:bar/baz' is invalid. Keep it simple; No paths allowed."; + Assert.Equal(expectedMessage, (await Assert.ThrowsAsync(Code)).Message); + } + } + private static IPackage CreateMockPackage(string id, string version) { var package = new Mock(); @@ -879,7 +956,7 @@ private IPackage CreatePackage( return outputPackage; } - private static bool EnableDelisting(string key, bool defaultValue) + private static object EnableDelisting(string key, object defaultValue) { if (key == "enableDelisting") { From 4aad11b9d9f923b7d4dd6732480961158fcff4c4 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 6 Feb 2018 13:08:40 -0800 Subject: [PATCH 07/28] In the template C# file NuGetODataConfig.cs.pp, log exceptions to Trace (#53) --- src/NuGet.Server/App_Start/NuGetODataConfig.cs | 7 +++++++ .../App_Start/NuGetODataConfig.cs.pp | 7 +++++++ .../Infrastructure/TraceExceptionLogger.cs | 16 ++++++++++++++++ src/NuGet.Server/NuGet.Server.csproj | 1 + 4 files changed, 31 insertions(+) create mode 100644 src/NuGet.Server/Infrastructure/TraceExceptionLogger.cs diff --git a/src/NuGet.Server/App_Start/NuGetODataConfig.cs b/src/NuGet.Server/App_Start/NuGetODataConfig.cs index 56bec91..d0a7cf9 100644 --- a/src/NuGet.Server/App_Start/NuGetODataConfig.cs +++ b/src/NuGet.Server/App_Start/NuGetODataConfig.cs @@ -3,8 +3,10 @@ using System.Net.Http; using System.Web.Http; +using System.Web.Http.ExceptionHandling; using System.Web.Http.Routing; using NuGet.Server.DataServices; +using NuGet.Server.Infrastructure; using NuGet.Server.V2; // The consuming project executes this logic with its own copy of this class. This is done with a .pp file that is @@ -28,6 +30,11 @@ public static void Initialize(HttpConfiguration config, string controllerName) { NuGetV2WebApiEnabler.UseNuGetV2WebApiFeed(config, "NuGetDefault", "nuget", controllerName); + config.Services.Replace(typeof(IExceptionLogger), new TraceExceptionLogger()); + + // Trace.Listeners.Add(new TextWriterTraceListener(HostingEnvironment.MapPath("~/NuGet.Server.log"))); + // Trace.AutoFlush = true; + config.Routes.MapHttpRoute( name: "NuGetDefault_ClearCache", routeTemplate: "nuget/clear-cache", diff --git a/src/NuGet.Server/App_Start/NuGetODataConfig.cs.pp b/src/NuGet.Server/App_Start/NuGetODataConfig.cs.pp index bba5618..d4788ae 100644 --- a/src/NuGet.Server/App_Start/NuGetODataConfig.cs.pp +++ b/src/NuGet.Server/App_Start/NuGetODataConfig.cs.pp @@ -1,7 +1,9 @@ using System.Net.Http; using System.Web.Http; +using System.Web.Http.ExceptionHandling; using System.Web.Http.Routing; using NuGet.Server; +using NuGet.Server.Infrastructure; using NuGet.Server.V2; [assembly: WebActivatorEx.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.NuGetODataConfig), "Start")] @@ -18,6 +20,11 @@ NuGetV2WebApiEnabler.UseNuGetV2WebApiFeed(config, "NuGetDefault", "nuget", "PackagesOData"); + config.Services.Replace(typeof(IExceptionLogger), new TraceExceptionLogger()); + + // Trace.Listeners.Add(new TextWriterTraceListener(HostingEnvironment.MapPath("~/NuGet.Server.log"))); + // Trace.AutoFlush = true; + config.Routes.MapHttpRoute( name: "NuGetDefault_ClearCache", routeTemplate: "nuget/clear-cache", diff --git a/src/NuGet.Server/Infrastructure/TraceExceptionLogger.cs b/src/NuGet.Server/Infrastructure/TraceExceptionLogger.cs new file mode 100644 index 0000000..899e8b4 --- /dev/null +++ b/src/NuGet.Server/Infrastructure/TraceExceptionLogger.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics; +using System.Web.Http.ExceptionHandling; + +namespace NuGet.Server.Infrastructure +{ + public class TraceExceptionLogger : ExceptionLogger + { + public override void Log(ExceptionLoggerContext context) + { + Trace.TraceError(context.ExceptionContext.Exception.ToString()); + } + } +} \ No newline at end of file diff --git a/src/NuGet.Server/NuGet.Server.csproj b/src/NuGet.Server/NuGet.Server.csproj index bce6a5d..d414ada 100644 --- a/src/NuGet.Server/NuGet.Server.csproj +++ b/src/NuGet.Server/NuGet.Server.csproj @@ -109,6 +109,7 @@ + From 0f4624508e8fd00be4fa1a5d259191f1ddc1ffc7 Mon Sep 17 00:00:00 2001 From: Cosmin Popescu Date: Mon, 12 Feb 2018 18:16:46 +0200 Subject: [PATCH 08/28] Normalize directories before comparing paths in file changed event in order to avoid erroneous cache invalidation in a multi-node deploymeny (#54) * Normalize nuget server path before comparing root paths. * Normalize nuget server path before comparing root paths. PR review changes. --- .../Infrastructure/ServerPackageRepository.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs index 6dcd9c6..7b8533b 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs @@ -32,6 +32,7 @@ public class ServerPackageRepository private readonly bool _runBackgroundTasks; private FileSystemWatcher _fileSystemWatcher; + private string _watchDirectory; private bool _isFileSystemWatcherSuppressed; private bool _needsRebuild; @@ -548,6 +549,9 @@ private void RegisterFileSystemWatcher() IncludeSubdirectories = true, }; + //Keep the normalized watch path. + _watchDirectory = Path.GetFullPath(_fileSystemWatcher.Path); + _fileSystemWatcher.Changed += FileSystemChangedAsync; _fileSystemWatcher.Created += FileSystemChangedAsync; _fileSystemWatcher.Deleted += FileSystemChangedAsync; @@ -576,6 +580,8 @@ private void UnregisterFileSystemWatcher() _logger.Log(LogLevel.Verbose, "Destroyed FileSystemWatcher - no longer monitoring {0}.", Source); } + + _watchDirectory = null; } @@ -593,8 +599,16 @@ private async void FileSystemChangedAsync(object sender, FileSystemEventArgs e) _logger.Log(LogLevel.Verbose, "File system changed. File: {0} - Change: {1}", e.Name, e.ChangeType); + var changedDirectory = Path.GetDirectoryName(e.FullPath); + if (changedDirectory == null || _watchDirectory == null) + { + return; + } + + changedDirectory = Path.GetFullPath(changedDirectory); + // 1) If a .nupkg is dropped in the root, add it as a package - if (string.Equals(Path.GetDirectoryName(e.FullPath), _fileSystemWatcher.Path, StringComparison.OrdinalIgnoreCase) + if (string.Equals(changedDirectory, _watchDirectory, StringComparison.OrdinalIgnoreCase) && string.Equals(Path.GetExtension(e.Name), ".nupkg", StringComparison.OrdinalIgnoreCase)) { // When a package is dropped into the server packages root folder, add it to the repository. @@ -602,7 +616,7 @@ private async void FileSystemChangedAsync(object sender, FileSystemEventArgs e) } // 2) If a file is updated in a subdirectory, *or* a folder is deleted, invalidate the cache - if ((!string.Equals(Path.GetDirectoryName(e.FullPath), _fileSystemWatcher.Path, StringComparison.OrdinalIgnoreCase) && File.Exists(e.FullPath)) + if ((!string.Equals(changedDirectory, _watchDirectory, StringComparison.OrdinalIgnoreCase) && File.Exists(e.FullPath)) || e.ChangeType == WatcherChangeTypes.Deleted) { // TODO: invalidating *all* packages for every nupkg change under this folder seems more expensive than it should. From 5a354c6a175286f70fb6d39194e84ad4457fd390 Mon Sep 17 00:00:00 2001 From: Patrick Sadowski Date: Mon, 26 Feb 2018 17:57:28 +0100 Subject: [PATCH 09/28] Use includeDelisted query parameter (#56) * Use includeDelisted query parameter * Rename parameter to share the same paradigm with NuGetGallery. * Simplify tests --- .../IServerPackageRepository.cs | 8 ++ .../Infrastructure/ServerPackageRepository.cs | 17 ++- .../ServerPackageRepositoryExtensions.cs | 17 +++ .../Controllers/NuGetODataController.cs | 19 ++-- .../ServerPackageRepositoryTest.cs | 100 +++++++++++++++++- 5 files changed, 145 insertions(+), 16 deletions(-) diff --git a/src/NuGet.Server.Core/Infrastructure/IServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/IServerPackageRepository.cs index b584a04..9c0c4ce 100644 --- a/src/NuGet.Server.Core/Infrastructure/IServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/IServerPackageRepository.cs @@ -22,6 +22,14 @@ Task> SearchAsync( ClientCompatibility compatibility, CancellationToken token); + Task> SearchAsync( + string searchTerm, + IEnumerable targetFrameworks, + bool allowPrereleaseVersions, + bool allowUnlistedVersions, + ClientCompatibility compatibility, + CancellationToken token); + Task ClearCacheAsync(CancellationToken token); Task RemovePackageAsync(string packageId, SemanticVersion version, CancellationToken token); diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs index 7b8533b..940a8a4 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs @@ -214,6 +214,17 @@ public async Task> SearchAsync( bool allowPrereleaseVersions, ClientCompatibility compatibility, CancellationToken token) + { + return await SearchAsync(searchTerm, targetFrameworks, allowPrereleaseVersions, false, compatibility, token); + } + + public async Task> SearchAsync( + string searchTerm, + IEnumerable targetFrameworks, + bool allowPrereleaseVersions, + bool allowUnlistedVersions, + ClientCompatibility compatibility, + CancellationToken token) { var cache = await GetPackagesAsync(compatibility, token); @@ -221,7 +232,7 @@ public async Task> SearchAsync( .Find(searchTerm) .FilterByPrerelease(allowPrereleaseVersions); - if (EnableDelisting) + if (EnableDelisting && !allowUnlistedVersions) { packages = packages.Where(p => p.Listed); } @@ -496,7 +507,7 @@ private async Task> ReadPackagesFromDiskWithoutLockingAsy throw; } } - + /// /// Sets the current cache to null so it will be regenerated next time. /// @@ -605,7 +616,7 @@ private async void FileSystemChangedAsync(object sender, FileSystemEventArgs e) return; } - changedDirectory = Path.GetFullPath(changedDirectory); + changedDirectory = Path.GetFullPath(changedDirectory); // 1) If a .nupkg is dropped in the root, add it as a package if (string.Equals(changedDirectory, _watchDirectory, StringComparison.OrdinalIgnoreCase) diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepositoryExtensions.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepositoryExtensions.cs index 2d9acf2..e5d782f 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepositoryExtensions.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepositoryExtensions.cs @@ -38,6 +38,23 @@ public static async Task> SearchAsync( token); } + public static async Task> SearchAsync( + this IServerPackageRepository repository, + string searchTerm, + bool allowPrereleaseVersions, + bool allowUnlistedVersions, + ClientCompatibility compatibility, + CancellationToken token) + { + return await repository.SearchAsync( + searchTerm, + Enumerable.Empty(), + allowPrereleaseVersions, + allowUnlistedVersions, + compatibility, + token); + } + public static async Task FindPackageAsync( this IServerPackageRepository repository, string id, diff --git a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs index 03c7d74..a678f97 100644 --- a/src/NuGet.Server.V2/Controllers/NuGetODataController.cs +++ b/src/NuGet.Server.V2/Controllers/NuGetODataController.cs @@ -44,7 +44,7 @@ protected NuGetODataController( _serverRepository = repository ?? throw new ArgumentNullException(nameof(repository)); _authenticationService = authenticationService; } - + // GET /Packages [HttpGet] public virtual async Task Get( @@ -129,8 +129,8 @@ public virtual IHttpActionResult GetPropertyFromPackages(string propertyName, st [HttpPost] public virtual async Task Search( ODataQueryOptions options, - [FromODataUri] string searchTerm = "", - [FromODataUri] string targetFramework = "", + [FromODataUri] string searchTerm = "", + [FromODataUri] string targetFramework = "", [FromODataUri] bool includePrerelease = false, [FromODataUri] bool includeDelisted = false, [FromUri] string semVerLevel = "", @@ -144,6 +144,7 @@ public virtual async Task Search( searchTerm, targetFrameworks, includePrerelease, + includeDelisted, clientCompatibility, token); @@ -203,8 +204,8 @@ public virtual async Task GetUpdates( var idValues = packageIds.Trim().Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); var versionValues = versions.Trim().Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - var targetFrameworkValues = String.IsNullOrEmpty(targetFrameworks) - ? null + var targetFrameworkValues = String.IsNullOrEmpty(targetFrameworks) + ? null : targetFrameworks.Split('|').Select(VersionUtility.ParseFrameworkName).ToList(); var versionConstraintValues = (String.IsNullOrEmpty(versionConstraints) ? new string[idValues.Length] @@ -219,7 +220,7 @@ public virtual async Task GetUpdates( var packagesToUpdate = new List(); for (var i = 0; i < idValues.Length; i++) { - if(SemanticVersion.TryParse(versionValues[i], out var semVersion)) + if (SemanticVersion.TryParse(versionValues[i], out var semVersion)) { packagesToUpdate.Add(new PackageBuilder { Id = idValues[i], Version = semVersion }); } @@ -365,7 +366,7 @@ public virtual async Task DeletePackage( } else { - return CreateStringResponse(HttpStatusCode.Forbidden, string.Format("Access denied for package '{0}', version '{1}'.", requestedPackage.Id,version)); + return CreateStringResponse(HttpStatusCode.Forbidden, string.Format("Access denied for package '{0}', version '{1}'.", requestedPackage.Id, version)); } } @@ -416,7 +417,7 @@ public virtual async Task UploadPackage(CancellationToken t File.Delete(temporaryFile); } catch (Exception) - { + { retValue = CreateStringResponse(HttpStatusCode.InternalServerError, "Could not remove temporary upload file."); } @@ -436,7 +437,7 @@ private string GetApiKeyFromHeader() { apiKey = values.FirstOrDefault(); } - + return apiKey; } diff --git a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs index b813d94..e539164 100644 --- a/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs +++ b/test/NuGet.Server.Core.Tests/ServerPackageRepositoryTest.cs @@ -106,11 +106,11 @@ public async Task ServerPackageRepositoryAddsPackagesFromDropFolderOnStart(bool foreach (var packageToAddToDropFolder in packagesToAddToDropFolder) { var package = packages.FirstOrDefault( - p => p.Id == packageToAddToDropFolder.Value.Id + p => p.Id == packageToAddToDropFolder.Value.Id && p.Version == packageToAddToDropFolder.Value.Version); // check the package from drop folder has been added - Assert.NotNull(package); + Assert.NotNull(package); // check the package in the drop folder has been removed Assert.False(File.Exists(Path.Combine(temporaryDirectory.Path, packageToAddToDropFolder.Key))); @@ -356,6 +356,98 @@ public async Task ServerPackageRepositorySearchUnlisted() } } + [Fact] + public async Task ServerPackageRepositorySearchUnlistingDisabledAndExclude() + { + await ServerPackageRepositorySearchUnlistedWithOptions( + enableUnlisting: false, + allowUnlistedVersions: false, + searchable: false, + gettable: false); + } + + [Fact] + public async Task ServerPackageRepositorySearchUnlistingDisabledAndInclude() + { + await ServerPackageRepositorySearchUnlistedWithOptions( + enableUnlisting: false, + allowUnlistedVersions: true, + searchable: false, + gettable: false); + } + + [Fact] + public async Task ServerPackageRepositorySearchUnlistingEnabledAndExclude() + { + await ServerPackageRepositorySearchUnlistedWithOptions( + enableUnlisting: true, + allowUnlistedVersions: false, + searchable: false, + gettable: true); + } + + [Fact] + public async Task ServerPackageRepositorySearchUnlistingEnabledAndInclude() + { + await ServerPackageRepositorySearchUnlistedWithOptions( + enableUnlisting: true, + allowUnlistedVersions: true, + searchable: true, + gettable: true); + } + + private async Task ServerPackageRepositorySearchUnlistedWithOptions( + bool enableUnlisting, bool allowUnlistedVersions, bool searchable, bool gettable) + { + using (var temporaryDirectory = new TemporaryDirectory()) + { + // Arrange + var getSetting = enableUnlisting ? EnableDelisting : (Func)null; + var serverRepository = await CreateServerPackageRepositoryAsync(temporaryDirectory.Path, repository => + { + repository.AddPackage(CreatePackage("test1", "1.0")); + }, getSetting); + + // Remove the package + await serverRepository.RemovePackageAsync("test1", new SemanticVersion("1.0"), Token); + + // Verify that the package is not returned by search + var packages = (await serverRepository.SearchAsync( + "test1", + allowPrereleaseVersions: true, + allowUnlistedVersions: allowUnlistedVersions, + compatibility: ClientCompatibility.Max, + token: Token)).ToList(); + if (searchable) + { + Assert.Equal(1, packages.Count); + Assert.Equal("test1", packages[0].Id); + Assert.Equal("1.0", packages[0].Version.ToString()); + Assert.False(packages[0].Listed); + } + else + { + Assert.Equal(0, packages.Count); + } + + // Act: search with includeDelisted=true + packages = (await serverRepository.GetPackagesAsync(ClientCompatibility.Max, Token)).ToList(); + + // Assert + if (gettable) + { + Assert.Equal(1, packages.Count); + Assert.Equal("test1", packages[0].Id); + Assert.Equal("1.0", packages[0].Version.ToString()); + Assert.False(packages[0].Listed); + } + else + { + Assert.Equal(0, packages.Count); + } + } + } + [Fact] public async Task ServerPackageRepositoryFindPackageById() { @@ -463,7 +555,7 @@ public async Task ServerPackageRepositoryFindPackage() new SemanticVersion("1.0.0-alpha"), Token); var invalidPreRel = await serverRepository.FindPackageAsync( - "test3", + "test3", new SemanticVersion("1.0.0"), Token); var invalid = await serverRepository.FindPackageAsync("bad", new SemanticVersion("1.0"), Token); @@ -586,7 +678,7 @@ public async Task ServerPackageRepositoryIsLatestOnlyPreRel() repository.AddPackage(CreatePackage("test", "2.1-alpha")); repository.AddPackage(CreatePackage("test", "2.2-beta+tagged")); }); - + // Act var packages = await serverRepository.GetPackagesAsync(ClientCompatibility.Max, Token); From ddcb106e62a1078f06e50b96f5c4a5c78b3a0a31 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Wed, 28 Feb 2018 17:59:49 -0800 Subject: [PATCH 10/28] Enable TLS 1.2 in build.ps1 (#58) Progress on https://github.com/NuGet/NuGetGallery/issues/5551 --- build.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.ps1 b/build.ps1 index 67d48e2..5dace8e 100644 --- a/build.ps1 +++ b/build.ps1 @@ -24,6 +24,10 @@ trap { if (-not (Test-Path "$PSScriptRoot/build")) { New-Item -Path "$PSScriptRoot/build" -ItemType "directory" } + +# Enable TLS 1.2 since GitHub requires it. +[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + wget -UseBasicParsing -Uri "https://raw.githubusercontent.com/NuGet/ServerCommon/$BuildBranch/build/init.ps1" -OutFile "$PSScriptRoot/build/init.ps1" . "$PSScriptRoot/build/init.ps1" -BuildBranch "$BuildBranch" From 94ee01b54ad50562a9dfc6d308d06e3fb7b25c00 Mon Sep 17 00:00:00 2001 From: ericcoleman Date: Tue, 4 Sep 2018 15:46:49 -0400 Subject: [PATCH 11/28] Add route to match older Nuget.Server uploads (#62) --- src/NuGet.Server.V2/NuGetV2WebApiEnabler.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/NuGet.Server.V2/NuGetV2WebApiEnabler.cs b/src/NuGet.Server.V2/NuGetV2WebApiEnabler.cs index 98830c9..9915b74 100644 --- a/src/NuGet.Server.V2/NuGetV2WebApiEnabler.cs +++ b/src/NuGet.Server.V2/NuGetV2WebApiEnabler.cs @@ -53,6 +53,13 @@ public static HttpConfiguration UseNuGetV2WebApiFeed(this HttpConfiguration conf constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Delete) } ); + config.Routes.MapHttpRoute( + name: "apiv2package_upload", + routeTemplate: "api/v2/package", + defaults: new { controller = oDatacontrollerName, action = "UploadPackage" }, + constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Put) } + ); + config.Routes.MapODataServiceRoute(routeName, routeUrlRoot, oDataModel, new CountODataPathHandler(), conventions); return config; } From 09c28fdd807f9d7c3d58d185c4695b37f9b89bf3 Mon Sep 17 00:00:00 2001 From: Zverev Eugene Date: Wed, 5 Sep 2018 01:30:15 +0300 Subject: [PATCH 12/28] Appended allowRemoteCacheManagement key into web.config to allow remote cache management (clear cache). (#59) * Appended allowRemoteCacheManagement key into web.config to allow remote cache management. * Revert "Appended allowRemoteCacheManagement key into web.config to allow remote cache management." This reverts commit daf5b2de1a218829253278b4b506f23bf48743b0. * Appended allowRemoteCacheManagement key into web.config to allow remote cache management. * Store web server settings in user file. * Appended allowRemoteCacheManagement key into web.config to allow remote cache management. * Revert "Appended allowRemoteCacheManagement key into web.config to allow remote cache management." This reverts commit daf5b2de1a218829253278b4b506f23bf48743b0. * Appended allowRemoteCacheManagement key into web.config to allow remote cache management. * Store web server settings in user file. * Update Web.config --- .../Controllers/PackagesODataController.cs | 2 +- src/NuGet.Server/Core/DefaultServiceResolver.cs | 5 +++++ src/NuGet.Server/Default.aspx | 2 +- src/NuGet.Server/NuGet.Server.csproj | 11 +---------- src/NuGet.Server/Web.config | 4 +++- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/NuGet.Server/Controllers/PackagesODataController.cs b/src/NuGet.Server/Controllers/PackagesODataController.cs index f68cea5..0862d9f 100644 --- a/src/NuGet.Server/Controllers/PackagesODataController.cs +++ b/src/NuGet.Server/Controllers/PackagesODataController.cs @@ -29,7 +29,7 @@ protected PackagesODataController(IServiceResolver serviceResolver) // Exposed through ordinary Web API route. Bypasses OData pipeline. public async Task ClearCache(CancellationToken token) { - if (RequestContext.IsLocal) + if (RequestContext.IsLocal || ServiceResolver.Current.Resolve().GetBoolSetting("allowRemoteCacheManagement", false)) { await _serverRepository.ClearCacheAsync(token); return CreateStringResponse(HttpStatusCode.OK, "Server cache has been cleared."); diff --git a/src/NuGet.Server/Core/DefaultServiceResolver.cs b/src/NuGet.Server/Core/DefaultServiceResolver.cs index dae8fab..5a0bc94 100644 --- a/src/NuGet.Server/Core/DefaultServiceResolver.cs +++ b/src/NuGet.Server/Core/DefaultServiceResolver.cs @@ -53,6 +53,11 @@ public object Resolve(Type type) return _packageAuthenticationService; } + if (type == typeof(ISettingsProvider)) + { + return _settingsProvider; + } + return null; } diff --git a/src/NuGet.Server/Default.aspx b/src/NuGet.Server/Default.aspx index ff32180..feeff08 100644 --- a/src/NuGet.Server/Default.aspx +++ b/src/NuGet.Server/Default.aspx @@ -34,7 +34,7 @@ <% } %> - <% if (Request.IsLocal) { %> + <% if (Request.IsLocal || ServiceResolver.Current.Resolve().GetBoolSetting("allowRemoteCacheManagement", false)) { %>
Adding packages diff --git a/src/NuGet.Server/NuGet.Server.csproj b/src/NuGet.Server/NuGet.Server.csproj index d414ada..5f1c2c1 100644 --- a/src/NuGet.Server/NuGet.Server.csproj +++ b/src/NuGet.Server/NuGet.Server.csproj @@ -174,16 +174,7 @@ - True - True - 1425 - / - http://localhost:40221/ - False - False - - - False + True diff --git a/src/NuGet.Server/Web.config b/src/NuGet.Server/Web.config index 6be0921..a039929 100644 --- a/src/NuGet.Server/Web.config +++ b/src/NuGet.Server/Web.config @@ -69,6 +69,8 @@ on a fixed 1-hour interval. --> + + + + ..\..\build + $(BUILD_SOURCESDIRECTORY)\build + $(NuGetBuildPath) + none + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. diff --git a/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj b/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj index 3537f51..62ffc74 100644 --- a/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj +++ b/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj @@ -139,7 +139,17 @@ - + + + ..\..\build + $(BUILD_SOURCESDIRECTORY)\build + $(NuGetBuildPath) + none + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. diff --git a/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj b/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj index dd2f1a3..06ae231 100644 --- a/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj +++ b/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj @@ -137,7 +137,17 @@ - + + + ..\..\build + $(BUILD_SOURCESDIRECTORY)\build + $(NuGetBuildPath) + none + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. From c595803a3f6bd60369b34bb50b3fee02a02fdf43 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Fri, 8 Mar 2019 08:51:02 -0800 Subject: [PATCH 15/28] Enable package signing on NuGet.Server .nupkgs (#73) Address https://github.com/NuGet/Engineering/issues/1816 --- .nuget/packages.config | 1 + build.ps1 | 10 ++++++++-- build/sign.proj | 24 ++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 build/sign.proj diff --git a/.nuget/packages.config b/.nuget/packages.config index 4bbca7b..47d6c8e 100644 --- a/.nuget/packages.config +++ b/.nuget/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index 25a2623..bc70e6e 100644 --- a/build.ps1 +++ b/build.ps1 @@ -9,7 +9,7 @@ param ( [string]$SemanticVersion = '1.0.0-zlocal', [string]$Branch, [string]$CommitSHA, - [string]$BuildBranch = '795fed66b8bae2d248237ee5ec82e688e7174a42' + [string]$BuildBranch = '1c8734ee61e209f159972ab974784ba55ee2bd6d' ) $msBuildVersion = 15; @@ -30,7 +30,7 @@ if (-not (Test-Path "$PSScriptRoot/build")) { # Enable TLS 1.2 since GitHub requires it. [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 -wget -UseBasicParsing -Uri "https://raw.githubusercontent.com/NuGet/ServerCommon/$BuildBranch/build/init.ps1" -OutFile "$PSScriptRoot/build/init.ps1" +Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/NuGet/ServerCommon/$BuildBranch/build/init.ps1" -OutFile "$PSScriptRoot/build/init.ps1" . "$PSScriptRoot/build/init.ps1" -BuildBranch "$BuildBranch" Write-Host ("`r`n" * 3) @@ -91,6 +91,12 @@ Invoke-BuildStep 'Creating artifacts' { } ` -ev +BuildErrors +Invoke-BuildStep 'Signing the packages' { + $ProjectPath = Join-Path $PSScriptRoot "build\sign.proj" + Build-Solution $Configuration $BuildNumber -MSBuildVersion "$msBuildVersion" $ProjectPath ` + } ` + -ev +BuildErrors + Trace-Log ('-' * 60) ## Calculating Build time diff --git a/build/sign.proj b/build/sign.proj new file mode 100644 index 0000000..7f0a3c7 --- /dev/null +++ b/build/sign.proj @@ -0,0 +1,24 @@ + + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'README.md'))\ + $(RepositoryRootDirectory)artifacts\sign\obj\ + $(RepositoryRootDirectory) + GetOutputNupkgs + + + + + + NuGet + + + + + + + + + \ No newline at end of file From 3b9e7de440d25620b8b48db0d42958a2960a1057 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Wed, 6 Mar 2019 09:57:33 -0800 Subject: [PATCH 16/28] Filter out late file system events caused by API push (#72) Un-suppress file system events before releasing the lock. Add two configuration values which allow control of automatic cache rebuild. Add a concurrency integration test to ensure cache rebuilds don't occur from concurrent pushes. Address https://github.com/NuGet/NuGetGallery/issues/6960. --- .../DictionarySettingsProvider.cs | 8 +- .../Infrastructure/DefaultSettingsProvider.cs | 5 + .../Infrastructure/ISettingsProvider.cs | 1 + .../Infrastructure/KnownPathUtility.cs | 65 ++++++++ .../Infrastructure/ServerPackageRepository.cs | 100 +++++++++++- .../NuGet.Server.Core.csproj | 1 + .../Core/DefaultServiceResolver.cs | 20 ++- .../WebConfigSettingsProvider.cs | 7 + src/NuGet.Server/Web.config | 18 ++- .../Infrastructure/FuncSettingsProvider.cs | 5 + .../KnownPathUtilityTest.cs | 49 ++++++ .../NuGet.Server.Core.Tests.csproj | 2 + test/NuGet.Server.Core.Tests/TestData.cs | 22 +++ .../TestOutputLogger.cs | 36 +++++ test/NuGet.Server.Tests/IntegrationTests.cs | 146 +++++++++++++++--- 15 files changed, 452 insertions(+), 33 deletions(-) create mode 100644 src/NuGet.Server.Core/Infrastructure/KnownPathUtility.cs create mode 100644 test/NuGet.Server.Core.Tests/KnownPathUtilityTest.cs create mode 100644 test/NuGet.Server.Core.Tests/TestOutputLogger.cs diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs b/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs index 1bc49c9..001bf47 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs +++ b/samples/NuGet.Server.V2.Samples.OwinHost/DictionarySettingsProvider.cs @@ -8,19 +8,21 @@ namespace NuGet.Server.V2.Samples.OwinHost { public class DictionarySettingsProvider : ISettingsProvider { - readonly Dictionary _settings; + private readonly Dictionary _settings; public DictionarySettingsProvider(Dictionary settings) { _settings = settings; } - public bool GetBoolSetting(string key, bool defaultValue) { - System.Diagnostics.Debug.WriteLine("getSetting: " + key); return _settings.ContainsKey(key) ? Convert.ToBoolean(_settings[key]) : defaultValue; + } + public int GetIntSetting(string key, int defaultValue) + { + return _settings.ContainsKey(key) ? Convert.ToInt32(_settings[key]) : defaultValue; } public string GetStringSetting(string key, string defaultValue) diff --git a/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs b/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs index b3c5ce9..b8056ba 100644 --- a/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs +++ b/src/NuGet.Server.Core/Infrastructure/DefaultSettingsProvider.cs @@ -9,6 +9,11 @@ public bool GetBoolSetting(string key, bool defaultValue) return defaultValue; } + public int GetIntSetting(string key, int defaultValue) + { + return defaultValue; + } + public string GetStringSetting(string key, string defaultValue) { return defaultValue; diff --git a/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs b/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs index 6fca200..49e56c5 100644 --- a/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs +++ b/src/NuGet.Server.Core/Infrastructure/ISettingsProvider.cs @@ -6,5 +6,6 @@ public interface ISettingsProvider { bool GetBoolSetting(string key, bool defaultValue); string GetStringSetting(string key, string defaultValue); + int GetIntSetting(string key, int defaultValue); } } diff --git a/src/NuGet.Server.Core/Infrastructure/KnownPathUtility.cs b/src/NuGet.Server.Core/Infrastructure/KnownPathUtility.cs new file mode 100644 index 0000000..eabd5c6 --- /dev/null +++ b/src/NuGet.Server.Core/Infrastructure/KnownPathUtility.cs @@ -0,0 +1,65 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; + +namespace NuGet.Server.Core.Infrastructure +{ + public static class KnownPathUtility + { + /// + /// Determines if a relative file path could have been generated by . + /// The path is assumed to be relative to the base of the package directory. + /// + /// The file path to parse. + /// The package ID found. + /// The package version found. + /// True if the file name is known. + public static bool TryParseFileName(string path, out string id, out SemanticVersion version) + { + id = null; + version = null; + + if (path == null || Path.IsPathRooted(path)) + { + return false; + } + + var pathPieces = path.Split(Path.DirectorySeparatorChar); + if (pathPieces.Length != 3) // {id}\{version}\{file name} + { + return false; + } + + id = pathPieces[pathPieces.Length - 3]; + var unparsedVersion = pathPieces[pathPieces.Length - 2]; + var fileName = pathPieces[pathPieces.Length - 1]; + + if (!SemanticVersion.TryParse(unparsedVersion, out version) + || version.ToNormalizedString() != unparsedVersion) + { + return false; + } + + string expectedFileName; + if (fileName.EndsWith(NuGet.Constants.PackageExtension)) + { + expectedFileName = $"{id}.{version}{NuGet.Constants.PackageExtension}"; + } + else if (fileName.EndsWith(NuGet.Constants.HashFileExtension)) + { + expectedFileName = $"{id}.{version}{NuGet.Constants.HashFileExtension}"; + } + else if (fileName.EndsWith(NuGet.Constants.ManifestExtension)) + { + expectedFileName = $"{id}{NuGet.Constants.ManifestExtension}"; + } + else + { + return false; + } + + return expectedFileName == fileName; + } + } +} diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs index 940a8a4..2bac8d1 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackageRepository.cs @@ -108,6 +108,35 @@ internal ServerPackageRepository( private string CacheFileName => _settingsProvider.GetStringSetting("cacheFileName", null); + private TimeSpan InitialCacheRebuildAfter + { + get + { + var value = GetPositiveIntSetting("initialCacheRebuildAfterSeconds", 15); + return TimeSpan.FromSeconds(value); + } + } + + private TimeSpan CacheRebuildFrequency + { + get + { + int value = GetPositiveIntSetting("cacheRebuildFrequencyInMinutes", 60); + return TimeSpan.FromMinutes(value); + } + } + + private int GetPositiveIntSetting(string name, int defaultValue) + { + var value = _settingsProvider.GetIntSetting(name, defaultValue); + if (value <= 0) + { + value = defaultValue; + } + + return value; + } + private ServerPackageCache InitializeServerPackageCache() { return new ServerPackageCache(_fileSystem, ResolveCacheFileName()); @@ -529,18 +558,21 @@ private void SetupBackgroundJobs() _logger.Log(LogLevel.Info, "Registering background jobs..."); // Persist to package store at given interval (when dirty) + _logger.Log(LogLevel.Info, "Persisting the cache file every 1 minute."); _persistenceTimer = new Timer( callback: state => _serverPackageCache.PersistIfDirty(), state: null, dueTime: TimeSpan.FromMinutes(1), period: TimeSpan.FromMinutes(1)); - // Rebuild the package store in the background (every hour) + // Rebuild the package store in the background + _logger.Log(LogLevel.Info, "Rebuilding the cache file for the first time after {0} second(s).", InitialCacheRebuildAfter.TotalSeconds); + _logger.Log(LogLevel.Info, "Rebuilding the cache file every {0} hour(s).", CacheRebuildFrequency.TotalHours); _rebuildTimer = new Timer( callback: state => RebuildPackageStoreAsync(CancellationToken.None), state: null, - dueTime: TimeSpan.FromSeconds(15), - period: TimeSpan.FromHours(1)); + dueTime: InitialCacheRebuildAfter, + period: CacheRebuildFrequency); _logger.Log(LogLevel.Info, "Finished registering background jobs."); } @@ -595,7 +627,6 @@ private void UnregisterFileSystemWatcher() _watchDirectory = null; } - /// /// This is an event handler for background work. Therefore, it should never throw exceptions. /// @@ -608,6 +639,12 @@ private async void FileSystemChangedAsync(object sender, FileSystemEventArgs e) return; } + if (ShouldIgnoreFileSystemEvent(e)) + { + _logger.Log(LogLevel.Verbose, "File system event ignored. File: {0} - Change: {1}", e.Name, e.ChangeType); + return; + } + _logger.Log(LogLevel.Verbose, "File system changed. File: {0} - Change: {1}", e.Name, e.ChangeType); var changedDirectory = Path.GetDirectoryName(e.FullPath); @@ -642,6 +679,59 @@ private async void FileSystemChangedAsync(object sender, FileSystemEventArgs e) } } + private bool ShouldIgnoreFileSystemEvent(FileSystemEventArgs e) + { + // We can only ignore Created or Changed events. All other types are always processed. Eventually we could + // try to ignore some Deleted events in the case of API package delete, but this is harder. + if (e.ChangeType != WatcherChangeTypes.Created + && e.ChangeType != WatcherChangeTypes.Changed) + { + _logger.Log(LogLevel.Verbose, "The file system event change type is not ignorable."); + return false; + } + + /// We can only ignore events related to file paths changed by the + /// . If the file system event is representing a known file path + /// extracted during package push, we can ignore the event. File system events are supressed during package + /// push but this is still necessary since file system events can come some time after the suppression + /// window has ended. + if (!KnownPathUtility.TryParseFileName(e.Name, out var id, out var version)) + { + _logger.Log(LogLevel.Verbose, "The file system event is not related to a known package path."); + return false; + } + + /// The file path could have been generated by . Now + /// determine if the package is in the cache. + var matchingPackage = _serverPackageCache + .GetAll() + .Where(p => StringComparer.OrdinalIgnoreCase.Equals(p.Id, id)) + .Where(p => version.Equals(p.Version)) + .FirstOrDefault(); + + if (matchingPackage == null) + { + _logger.Log(LogLevel.Verbose, "The file system event is not related to a known package."); + return false; + } + + var fileInfo = new FileInfo(e.FullPath); + if (!fileInfo.Exists) + { + _logger.Log(LogLevel.Verbose, "The package file is missing."); + return false; + } + + var minimumCreationTime = DateTimeOffset.UtcNow.AddMinutes(-1); + if (fileInfo.CreationTimeUtc < minimumCreationTime) + { + _logger.Log(LogLevel.Verbose, "The package file was not created recently."); + return false; + } + + return true; + } + private async Task LockAsync(CancellationToken token) { var handle = new Lock(_syncLock); @@ -710,8 +800,8 @@ public void Dispose() { if (_lockHandle != null && _lockHandle.LockTaken) { - _lockHandle.Dispose(); _repository._isFileSystemWatcherSuppressed = false; + _lockHandle.Dispose(); } } } diff --git a/src/NuGet.Server.Core/NuGet.Server.Core.csproj b/src/NuGet.Server.Core/NuGet.Server.Core.csproj index e137025..c102835 100644 --- a/src/NuGet.Server.Core/NuGet.Server.Core.csproj +++ b/src/NuGet.Server.Core/NuGet.Server.Core.csproj @@ -75,6 +75,7 @@ + diff --git a/src/NuGet.Server/Core/DefaultServiceResolver.cs b/src/NuGet.Server/Core/DefaultServiceResolver.cs index 5a0bc94..af627b8 100644 --- a/src/NuGet.Server/Core/DefaultServiceResolver.cs +++ b/src/NuGet.Server/Core/DefaultServiceResolver.cs @@ -13,6 +13,7 @@ namespace NuGet.Server public sealed class DefaultServiceResolver : IServiceResolver, IDisposable { + private readonly Core.Logging.ILogger _logger; private readonly CryptoHashProvider _hashProvider; private readonly ServerPackageRepository _packageRepository; private readonly PackageAuthenticationService _packageAuthenticationService; @@ -24,20 +25,33 @@ public DefaultServiceResolver() : this( { } - public DefaultServiceResolver(string packagePath, NameValueCollection settings) + public DefaultServiceResolver(string packagePath, NameValueCollection settings) : this( + packagePath, + settings, + new TraceLogger()) { + } + + public DefaultServiceResolver(string packagePath, NameValueCollection settings, Core.Logging.ILogger logger) + { + _logger = logger; + _hashProvider = new CryptoHashProvider(Core.Constants.HashAlgorithm); _settingsProvider = new WebConfigSettingsProvider(settings); - _packageRepository = new ServerPackageRepository(packagePath, _hashProvider, _settingsProvider, new TraceLogger()); + _packageRepository = new ServerPackageRepository(packagePath, _hashProvider, _settingsProvider, _logger); _packageAuthenticationService = new PackageAuthenticationService(settings); - } public object Resolve(Type type) { + if (type == typeof(Core.Logging.ILogger)) + { + return _logger; + } + if (type == typeof(IHashProvider)) { return _hashProvider; diff --git a/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs b/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs index 0e52495..4113948 100644 --- a/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs +++ b/src/NuGet.Server/Infrastructure/WebConfigSettingsProvider.cs @@ -29,6 +29,13 @@ public bool GetBoolSetting(string key, bool defaultValue) return !bool.TryParse(settings[key], out value) ? defaultValue : value; } + public int GetIntSetting(string key, int defaultValue) + { + var settings = _getSettings(); + int value; + return !int.TryParse(settings[key], out value) ? defaultValue : value; + } + public string GetStringSetting(string key, string defaultValue) { var settings = _getSettings(); diff --git a/src/NuGet.Server/Web.config b/src/NuGet.Server/Web.config index a039929..f387f0c 100644 --- a/src/NuGet.Server/Web.config +++ b/src/NuGet.Server/Web.config @@ -62,6 +62,7 @@ Uncomment the following configuration entry to enable NAT support. --> + - + + + + + + + + -m2f)-D-1EyEa&4Fd4 zO}{~#G$~RswS0BO9IgfvhN@HBnS0xlz`~DV3nK$PJ+|0fi#O=~^vI0PjMhFg!eq{j z(8k%3a_7#OmJf!l44o9sW`}^5FVPDe)BD*LM8m_i4$iq(IXJ6VF^kcT_mKITBgS0x zd{~u&Hihlzd3PDnS{54i5291-LLViic_Jg!nM$*oV zNVT)CaxTX1K_-$JTP5}(D-xEt$LfqQe^V69L0_?M7}6XZ+LS7v-HIJgQalFQ1c3rB z#cJ@U7R;kSQhvt461z>*?Ct3E?TxMLI+SJt2HZv2mC*6aYqrZ(^K?m147La_TaUR^cYE`^ z;5|asRg0HxK*iY2@gsKx3yBBJUprtqK3%oPARRFzULUA(Up7sft%eMJV7r=P+wqL|RxSIE;wV=}*x_Ynfoajib zUX?PU^VhHa-@4jFSZ~`Aj`xthX~DRr`lKK8=h^md@o44l*jD5qDcJdO-QM`PTu>Sq zDK=1-rtZ{!Z1>83Ty*mEu~8IlhLf)8#m}0V56N(b7vW26k3ivZX4$cuf$ynAFWWP8 z+!11WH88d7I$1Y>Z)kBQErg`4)JL;~wdr&hrG$C`j*KcCdYynd3qiKR9xk2P%qk0k z=r}NaRTeJKy1*8ygy>d&1OA*^&mRGY3bn}QIt$k(c`|%hxy=<-H~h?9v`i#^ura}f zKsm&*_5(V2Yt@L|*qswRxpx83`(WBLw-00!)VDs@E6 z-37oIJRl0A3Ta!Bsg^MW3T2io>F*s^Dhug{zM=TDD$v))|#AEs#)R5^Q|28C4OA zm6&Zyb%Hg^lxic4s>mMdmESzMY!}!{^H|;4*y3s(-73(1C;j7dFTd7=USxP@#x(0Y zH=GUULA=>sVP@B@T~|4sze8}4gafg4`d9dFr=kGT9;S7UGt=>^=8FSQyo+8V918lj zj0iT8!AouK2`jC^=^dSfcj_9Z6hBeL336}sNJ;S7R$HjQZK=G523|AdwWAq}h4quI z)?!s0n!pY7Qh_J$ZK3b;*B&T`nwCF>2Hv+v5M5sQpzPGC>w>JJkD5=ric}~N^Uy7= zS^GhlLh>wbXMlQLu}Kh{(`(^Pk{txIUSUjV{!Lc>$sn`!DekcJsX?8sEDI)#g+aAl z+4=(2q7<+THWa5#*bACteLh>tX{1@##g4ov-s4QDF`aUle{`gr*{sRTdQebnX_@90 z3S1$`ra8+mJgu$iZPl!-61BSEAPqWFkfS|p>wx44CwDL*Z6;dY7ZjMg1 zYsU4D^fb;`;w^KBj;asCrHq>h2ax`{@|Qn&wP7oj7&vHLmro-Mo#y<~4caW>FLkOu zycQTXX=>7Zykp_w6P-359I3U5dgF$LTScH2uuE$rs310zU6Eg(Y;7_K%J_BITAB={ zQD#$-Zz!7!UK;47sFy+DpNNQX%QH2#HSa`8u|HkvD6M>yAC{_T&T5L-0>FWtYbSj{ zpZQ?y7O!i%4)oFD>6c%ARYFiwU%FZ#Ltw9KEK*3>x`i#+^V(TnjuZv!Sw|K+E3xMi z#)CaaAr*8_w#vt5jLOC+=SqXwVxzkNazQ#*>Dl^qh z)992P*5N)8=S^JkbL;vF_D{LdsG-FSsV2$rj)^o$XpsKCrR5d-z9hrw0dB>vFnohe zvZSFe$ky)LO&(!nloQ5#Ts2?TS_3K07on0Adq}igVOdI+Qg-Nhm~125Y_KTDm}JzU z)v;P}O<&FGk(;n@CF`UZ5G&hT>nI&7D?6OHGENhk8=#QMX)+DyDYmNej64|Bi4{&G z?R;AMdw+G0ohjr_1hM3M1Z#<0*%2Zh|Et+{O()0B5xX50d(Fy^4C6(suCvFK__`SqdRDlcD2x6d}1a`DdQ(k+~Fe~71Y(Tc+lj%WFlelH z5`L2Sa-iwgkCCIyNt#ksOtKCf)~tI=Vv52;x|62Cycc9cUb{4oF zWMmWO{6b1EZQES_DW#U_WjKQ?mao_AHNN!py~?M@yhN_?6GpYcc&K~4*Vd~qrf>7V zLl4|e>5W?kJ;7z09(EY}ORR>Vev4LDNaIP+^dnZ_I;n!8(st4vaNy9M`t{X5mcE-V z7z70=LHfmXkyd%*jZ{HbeF(z;h_=Ri$dG>{{a#?s?%hE(_nmX{YhU1G~0kc)BJfl6N9#aAFm_UuS1V^QEvg@2r%i9)pQ$hi3o&D zT8#7n{&s8an?dOmX1PUQ*QExErUHV_QcA5^q~Akr*!lGuk~`TDIS_kaW%;=B8KYXq`WxK*9~e3;r=oGh>X|ycVZ*?jFfdNbD4ivEH}+vu$hS^ z@G_F+di_hF`!;D@$3ux_Sk96`Bb~cott`{p25CqeTGILoKKgf=I!eb~~^@&G+V@9pL`zx2XjKdP@CYXM{J>H5ja5>dY#b%}e{|Uu9fx@!v*< zQm6d8wA~7)A|)!|H9xmfhr`vHxTwx4J498{pkw)=NSk&!@WMuz5|Y+0kdYG^GkHPl71572vy*1_1mv^nHpO6mXk=C;q|UTY3D}i z#fvsa)!U*k?jSczcMc|OT4bK$LwlO-YZv*uZ6gvN7Dt)ZveE_n;ET^!E_1CnvmRG_ zu`Q|f{m9_0Q3$J7gAc3i8!4S@OLtqEr?eogS>1ul@di6{uvI(L))J4|Lg|xSaheg| zS50w8hc|O?W+1+!R-Ny3d!^*lwe=68e=nJ_Jv}$AM7OQ9s!(-J6;XFYyR^yf&;<*c>OtqNtCq3P**+CzSA04EWT96LerN;DPt}7bqQp~*Jt8|mh@zVVLly;2m_4jISp$o4rkL=FY z=%n=Atv4*W2yQI%Kj#>0K^b>adb{>49V+vqZqGwJVMBxYH`?xxb34;|a{SY%yETVx zr_1+htHrPbw0G%V?F}(*IL`EZZe>`yJEKYy_bQobw}%Z)Pgz@D2TzGs>cAXXR-j&S zbi8D?9mFFD3k7=zI?In-#!4p>mh75E(A8Ko*A@J$l>~SE@vv?y?_9rioyN5%*Lwuz zON>SQLS;<)kBnp37B}xucQB5{;W#9Fmg!@U@>x3(FB6>lTHFQXnae}B$K>2euMOK& z-%7omB)7FPvdg?DmtanvV>MfOCn#*9B}j+#`FG_Cw^q=LxM5wB1-`4q6y`|(Xq{JM z&`M|S)skS@lLaFy(d~U$C+i^T5AM}pt371AlFLV21+T#i!}_kzjPT!Qrj-@1-2+}= z=-b0G!_iODJ+brL!P;2>b1sD>hs%yYL%p{&)TIO0!sPrionn5x*S5a2LE&|Fy!BeR zajzEMr7uBVRI!S!>e51*h%it&-4XMaL#M`vVBtne|9IF>tC?i`@;>Nqq{XX@Df^th zMT;Y}pwX{)^mXY!%?du>h3C*Wka0Nu=zVIsUD~DQd&yL=^haF|KX2UnCRm)c%WK8L zcWP)byjQnt1$3d*U!(L~w_m4%J|Dq`c>ZAJ;F`VBIH&8v!)LI_T1wlUz!B5k9@^7;Vu*$!T^x1A3i=^*_T~e*P^pE93kn1|@a>I3X z*{rc;>sI=?d~`m79;JWu+i9`iir{&FBW-Z^5AL^aUVr&HumI=3U440Zf}6kE)o{AF zsop*Znp)?c1X>uTWeL;wQwUqt$_8-SF8ahiRqH-!={TZ`b!M&2wQE=}S&E3#1R=sL zjgtCh)0}j7*{qIfDmkPF;d{2M(^DGNr`D(AjA**qa5|UwG~T0SK~P+E-nQg!F&ZD~ za&M;TRq4%{BGrS|TvA+ItnYZ@(bojiliiv3 zT6uu&_(SQzExdaFlr4C1h`OmgN6**j+4llph~2yNz-&ri9r|P2RsBKwgYn+e!$;J`hEVLEx(lhJPp0fKEE~+pKn?jo6wtV9R48v5#@hI8`smH#_#W!U&?zi zy+;1;lQ+-re*(~6On<_EKchF*n6nc7yj-)N{`-u|M?d@jQQyxqm*;`3wtpSM{bd!5 zUoco`?)@3$(NCM9)}Y_RerWP0-5=&QN?P{0?>7p5-3&&rUsT+bVdWFX(G ztk6a~)c-%qbWz;Vb-1Aa0iw)PbzlF*5l{S~7u)y3i`J*N#A^|x3g^;``;Gn<}Uv(c-~ywBE+S%`8c zHI}ZW#%Le&8AuzKUY~V2avsutwLEd#L!elW`AE`*O@^m7dSRdJjmwnTWwiP?9oL%3=JT1Qr-XIc`!uwV z(qqDGX_2>VpR`%0M{`i&JY!lhm>A7JlGD|<_Gz%WAN|-?S3PM4v;516f1un?zl!Q* zIZyR-q_O%QuH_%^+63vrYqW}P_v9>n2rJchjlWu9*65wj_vuC&si;@cHp1s!>KSFf z;1*r=RWFh^rR*_LPR>gw@+ykEi}$6++x%WXTjC5_oqt5Y;Y*CW?m|zPk@&2*Pz?{A zn-)9aMcm&}cc9 zTkoex6Hk=YxJx|Ubynk);hVm+8mDaMU-pASK^>wW#0tC3N>t#<4D&^BUeFf4rboJC z&^||c;P;`kSxU(APES^etLO%Yy+&&5_E@&KH-L<6tBaPi1P`e7(nGaoI{r3TCfh?+ zjzWR|A^-izdJGJtZ_zZfIW`Ml(z@N!xQ_ll`r}XeFFmtleYtGZNSvl$qFvv|fr=j! z1+@}>mNsQ`3IF%VnUedUTD{Kr&oe@KQs6NU{39^5>zn-(n`iKnW3?={)gN62qqRoD zckPP{*1lrdBk6gH;!z}t?9o%ScZvv0;iSrn=XjKl)?b$HD!87Gt4+yt(XZsCzO{=Y zo+ciYj=m4xz6;i+6F=sA$MD6eBR%pyDYEX}U!-xR=ll3C%<1y_MN$M&54s-VfR8JB zQ}i;D$7*_BI_`fj(7#5VPUM`D@~D^SdmpuyQ*zLrH2YbDB4a!*s=LB>^!aEvUy!uE zAMKVaBbe*+)GJ`=5)`ut1S+O!h1HJWcd!~~h!g3Syl2b3^0UlqY%IPb0EHiLBxlhd z4lxi>QesEyjFP)oR6fFq6mjKsi7DAG?vtMM zT24PTH)eo(>j@N53&vk+|8xNfGn6$^;jCT)u{q$@epR5O$N+UG!ke9v%oux+? zDVe#ncu#1;M<%2u^hV8Jko9Q0^q4$MnwjhbMPrF{hZA&&l>HuSGg0ub+5`Y=!iwc$}n@q*SZr1gB0ZX4UQAXydk`uH$cYbs8hM)IVw8(#NXm4@!RmXEO8 z>MoG6^ns4{G3%&(t25~pczt@ITkm${*6~ULX@3jdcWPUgef@hQ%xwjBJa`eF_;E@P zu27mt*W>=3sV-p+DZQ+*`6Da+rH33}K7h-oha6Xgm+8R|p}i~omBj}B9cwM#bmRgu zAw8{L+>bB$JVFsi^U<7i4w+lW_vy#v&w0k^RIQ{dXp zPW8ksgfT@&e@eQR^v4a`Z5!ktxs5)YjtYvxxxPLq{W=|YeOk7v#rQs@*3_={y!%l_ zP%BpYj%g_A1<_pfKxl+rt*c5@aXN8;e42uGSgjwRdp(b2tGeVx+E}BcB0c3v?~Uz6 zK~52}&Mo_~Vl95q!;{@Ud%1R2q4ssf*7YWuK((9tEog?~Hlbx;o7Vjiu;BP*AR|p~ z2b%zjJ|;avW$f#jZmwo3tWmyZWxIe+w>3sf9Mj6^%!_(S4+$4yx9o&y+o@3NLheav z;exrh9(vw0DwkJVSXo!;iv0#pp1y8%^0+kb7H5GinyR#HNeS~=JU+eE^wj>XrIUVo zqenNUA$-x>h)7^(*BU?H8EJY(@Nc~#yYR1Wjl_-56#@~qOocP)8ju!R{a87+0Sx`F0)@g9hLqeoX_XShNb*?r*HkFM|{VPXH^VGt^H@~ zI9`PBx4zA{8KHb8lXU0>#U{8mbS3^~I`=x+!Ug*t(px0yXu!!h6h9CR^FzllLH@sHfKqimgYjhqaG&zu!Pph!H*7oy8kD{b|qthdoSJ z&3rJ`OXSmjvfiUjtp0lPsrV1^T)Re`aXLLd9Cs;w#16k6Ih)QQrOv`}`O6pDi+%C; zfwQEXvHUZUFE2}a0NL{3X;M^n9;m|ePH5ewEedDMx_(rCs^N}wI?-y_=hVg{-4ttg zkcQ4MNMjs>v@;`V%wtfF*$&cPfp@#UB&c|gt@dQ24K3dw#Zh5dGqhh)X%{`(Q>~4X zqt91mx)5$*y`ghJN4<(1~kGzPUdFaRs(l(F(KBJbVx{dTFTIxjp&YEj_ zWXKW5{PwcMWO)}(@`C2j#_WjeN(<3>S#kEf2eQLBnaA zFBweNN&6h_yz9kAo}G9XNVPqt=ZAX6opFiZ(-#Vle^7J9v--4P{v{imq@1h@ z=|t(-q3?90RPpea9vWTarxw?hHB867b|tlk=yEywd{7Z*$c~3R4&JUdI4Oy7opE(a z9;+o-*9R3Hjo$r`v>8zxICsMJxh7`qk$ws_iPXv`F{J6WeUST<*La_F!OE>5Sw-%b zO^-@5SC7hTdV0J~@3t!JZs-x4OJ9W=k2`$So@q?%it3c4?<34o?k2O76oI!sRCm16 zBa#Ig*&Tvor1J|Zu5HVoo3+2$?utAm&9Q1f11rn+w)5V&Q+`jQJyZ8kbi2WMFzIdf zK;GxK_ChZ3{XTHL#JA|+W6{$~-8w39ali7Z%i=#TBMey8L5{RJU5zR#4Nn$Uh5aVcIWXohVy*M(AR7hV9;!iuaPY6XNh z&cC#FTF-vOXFB1rXMVcWtR{H9^DW<>1(%C&c{)p{SKBSpRia`1J8bz<={On0VPb=c7!E4y7l6~(~_L14=Dv^%n_`!7aDq|Bz zO3(Sc#RDY8L zPkM(i@B`ZDTJ}&PaUPN=8jtT^Oeu%77A|GWwT$Fa<=lNsSp9W(@A}>it(sBdr^B+R zm|^P+aZPCa3Z|hKS3#ds_UVp%F74uxp9Xd08NqdX+AO7>TLk;0Q@XA1-V3c+YgMyy zrjy-vo#|-aS4hV{wvvLr@F+bp9A`Q?YP&zFJsa%{>!PC^p`WoQJwDwJole&EIAxMl z6qBCxJ(^1A;@rij@>q+%i>!$1XpVlTkNkd3RMlf{&%6Jnb8FB1JT(TWny2ehl z>($2P4p8&$fQ0xP*OHoML!Upscc0@mHQ!6-g_ab)3Ra~v_ipQ5dKV>>{x98sospgo z&`N)$-ZwtcS+N%cUFpBVnFU+PN-KhQBjwJB?PWq?;>KF0Rf~Y^wyM6yTO{Y0dne_u zgd0Ddbvnf+EB;9V;;!W>dX|vp6YfKkFG>G#ch=Lc?__JxWtz|0?O1vJWqY{grzi`L zB9l%4rlODd?u4&TLYEO2_eelNTE0)0#_pY$)f#2d5Od3Z#|tq}EkRM|3K8l(n++4*HLK-*@&O9sGCydV`U@ zaIYHes1_K7Tg8IZP#R8w+1$PA4M%ma8qKKQkgSz;tEdXCFmFIGPQlW5B7FU(jP2Ef z)gFv+?qKzrab6hKxC(YjzE+Qw^kLa#Q${;7*XeBx#ym3D>9w1YbLLqP7JXJCQs(m`sqBNU}d4ob#pcVgYV zC0tkD@RBJoJ9m(}!x0{&Rx`pm*FF_}akoz?YagIk8kuh!bqi`cNQI7hSCMYaVC%ZOMM4I&b4}E3C0}Vzom{j}LR-q8G{SvAi1f*R5A#+IzkABHDYAh_m<&J({YHXc5Rn zywnTbKK3o!>o=FZ43-IA*{;nHw7t+lpvWSwdEQMQcE6u3?I0e_1Z)^;!R&Xl-VwPGayYcv@0fm3!3r=>&%|g z%y;z2JYUk{-BYL$r^kKwL-W%;0NR<7H%dO_bi(yN)&H0=E2YjdQix+8hlk)Tuk#kY z$p>-h*=Z$fn}13vt(tsaIofR~Rawoqt4B;XdEdLnDMm4Y<7zBbe^JyT(mSl@*pYi& zFpxbz&nSAgD_~`6Y~s(7HrlC^J)u4BVQI#BLbDKV7eDSp+obH$I&@Ngvkw?2FCEXM zEtDEW&1xO<-G7}(wj4=>*dgPDNACs%I>p$SH4#O+Q|CPTfm$yB{M^#$Cyu5yS4PA$z$Oij+ zGTRd3KskmoJ*rjJ2_eN3=ysg!arp^en72rgm8|jCeX;a>K5r#x)w=Qlw%)rxMTZgf zNN*I~77p-Ko7#n}+S6&G*!_4?5!%`tQX5)<*6)}eY1`3Q&_2VnpH~)-)p11%$jb6v ztn`$$a*fDuuhCwxR$Tk5yZEEhvywFzD6Ks_!Ad9Ix@DlXlP-}})5~bbIi0HMu_ zXEeha7c<}Rv8glt*1ev|y`EFA%W%uFbLPMOe$VE9Z9TiE*r)bpJT~vgPAz3Kk=`L0 z)j#Jxwbql4SN%I*0|UU(Q$S|PICG>urW#7CGA?qCIrg^YoZD-cU6>X*7rs4a7HoM& zqo}QS^_=MEc%IvD-tSr-yUo6or5`BWl}9v?Y7h z@@!4oGd`V~M$2kX+@bAl%_M{5O}=5ILeS^asbkP;HN2_NdQP?9XNDeYG`*bQKg?CE zfZ$Ws-=M4As2IJ`w^pN_zji=%Vr$22G)dCP{SCNIkrvNsq_qdv#%0at$g=2*_mxf& zRk4KqrZvc^sx9xj;yy}hLrJdryKWwJ42R{z(E3CgZ$x@aY88D!%^yK_jcq|?V4nIswLXljFjM% zt#VJ2AH*eX>WsZ@4@i)=G_-*x53Abb`N(*(X zHT%QT4s2Dl%pBJVGYzFS?^` z?IPiI3zvgX>4B-_v)^vN;`=Lb=BZT6E<-F@Gp zzyIN&9sCD^Vr*b`o#t}PuLlhudI0BRxCrv`-1JA$cF5V+S0vrB zFiZL+_=d!flmlIdA;m7-4;BPn1DB=BNQ{e}XZK+`k;Qir`dk9xfiy|~_uZ2-M-IZ|;nPd9-@Pqc_lfw_&-@Cs&IdkIB%-wfM&VL`P-(UVE=}#V_-jVOxzbf_c z-~Fz&^mnbwCx3^WPonxSzeCpDU(%k+TdS|d-%E#PzWTQmd&EU)bgWjwy>-6h?&F74d_*W-qzS#xvznb~-|9+_X{r8pKd>{V3UIKZy z+XB>oWF+l$RO^?e=%1>3Kb>YC-zV>UG&ij-RliiT)ADKlnFEyhAiB9%TQv2Jy1Q37 zx_#k3%Fadi2W@-iWoS}__^X+(H=2e0XU)Rq-+5v4_hBo~;BvyuH%~N8{FhA=`FCz2 ze;+;((x9fO*-8A-JHI3!pxv~6R8p{bYyI?R>@a3i^<6D?ZiJux0#8N7Y3TQ| zHPEj`L(?ie7Y*JE!Tu9#cv>rTaIXelijEG7a#yUwd$mf(2W`;U)6B~+S-S_VH1o3f z-ph^x-#o!z5asI)$qS>N0JFqkG`|DrlVgK5O{p;LOCWO*aPVkQMmv^-59p%KCU3H`$ zkiTF4%0v+&M8qSK`u7Ok`uWW%lJ7p|l8NlEP9b=p8_BxwJjv&G>(65~XXdN#AN34= zQRB%@4e_O~PVxUP7{5B@@=%Qj>EC^tF<23#XnfGj-De;0RQ?q}tcAPJ+Yi>aRCw{Q zKBPU+wht?};)xGGEZEmx7zmS$_VnZX4vo9F`TH7*#On{9JaPBjp%ZsJ{p%IPyuR?bov^N*XZGu*v2 z^NAUS^k#qjQ4`i__r>7Qsmv`{J@wGJ-L9EII}Mj80xO)IP)HSH6I zP0A-ZE84jkwLh`menz2Vhac7u1f8P@^Set7pE=%Lb-w*86om3y<<`-Nd@!GYTxw2# zW9_6;=>G$&4f#++(E7B@pRwkD_EY~g^Yy<7@Z6j^T9IPr=*&&1S7n|?Kbr}F-Gm0S z(D)a^-oNxFzwr-)rBW*@sIEXq>lqcIZHCI6p>nSqRpRUKK^rB@N|vv~51G91TGnRN zpOq7!%j zximUq$zPFs;?M&i*8^I?Kl^8V|6w%sz4X09TA}~>kpF#N3gCASD;fO!UScJKuua9K zxo?v58xgw`*F#3s{@c6!>Mt}2u;a@=|9kv^K)?KR{y!-C%Rm2)y8K^-Y5w=_FY^zP zmU3PuK_v6bsB`z-(9b`4m==}ux<>Y}>klY1cVG9PkjdS-1~=}`K^TXHkl==Ws0Wna z75>uBiom5F@4jy6EnW+4JFfqdGJ+skpsZOQcVB;4h-y7r1(<;GOtDI>C&tK$yZ;-1 zgDsbSRn10c?6@RT>@i5PeA7Q&uJ8v^ToIF4VZB~E^e_eQ{+}udIPd;nPaI~x`6u)B ztY7^dWj*nP=9_=_G7mSIhb5%iMg`Ak)a{^0WDb0GXl75dgN*kwi~E0kfxdY{<-T%E z1bOv^sq}Cti_GM!zpx2?^%syy{31=D`|2-Z$ylP_i6#5$#Rndq`TJ0vSXKV`U9A-D zin?nm$bmlDOJqJOnrmhyF@L*J2w=Ykz%t3gZoB(Ss|fMtlKhk=m_Lshjpr{i5nd(p z?}^BB-bt_68<)|3E;4iUAB3do&F8`I`s__Mm(pg)VyE5xX~64ssVyZ@3l zz_Yu*V;%gCh!Q4vS2Ll?6ElDIzeIsuL&)zXBZ?>Z_pw9z_rxJ%oNvIOepmg!;mUtRPO!~Am=ZQ4EAyNmr?%pe)-p( z7e)EkbNSc3{A~oi|Js$GLePBz&TI<+Q_1TIs9}l@^Si$cWXwabwPBowCOMJv-D0m> zF^1O`)8GK4mele!9F@~xAOS2Q{ev_*@n`>*N#~!IMJjWL#qrn4EN2@8O`;t;OvS?x z^H=|E+msC|*KmqTD1*eWu{#B$fK>2C@T|th2M~Ya9vFNXY<9_wJc+C%tguxu- zsIbud7gA06deZ+KJA4GU2mct4LDN7y|2NW99%dop*ONY>_#*jV!bRe1<_ZYgPX0uc2a;a{c>;C_`hkAK8cVuSIE+6h?~Q z%bk#$7{6Dgx zml6WMm->XTY+56yypLWLTK*qxU3^jU?+l;({XN6q?x>oo6qi0(n)2#(*#x}$CErE; zVt$zJzlcEqGo-p1NG2B?g&v6-Wo-o)l^m`(=<8+;CFHmM!$YP{{=e+K4Uk>OmEZT~ zVFvSNF!L~T0g?bU0}Zu9Ewv`Nz%F4EULXd%;F@$G?a%~wsRYUnmNO!d1&ObL1lmyY z?t5=ORHmyS2e!E}X~R`ug`0pCY?-u)6qQg3l#nTCOIsyXQDvx#N-w|gruU?WMw`@Q&HAENb&WFk)e62kj3 zBNG8pxC1Gc{G^B|P07PpW=OOyYe=*%lRw`WVFhYvT`n|nmyi&hKooYA6tHG+MY$5r z6x1(IU=y9b0{-5IT;7*>37(P!c-66r3NG+yp%Hu&b}#c6DjvS=3-N?>AZ5>pS}F#RnW8u)YY zdODDB5T?c(nyR;BPXYWbeW!rrk0=A^3pLNhyUc z-O#AIyFX&r9lgp7Rs>S7kH{TpBBVEgr(E=Ia)Jv~HfcrDyR@Lw{1aQZG=VljDO##r z^FBn2`Z*WlB;ZJV%lsv}S~wivbMbvbZ<)h@ulSSr1psBrh?3>AGb^Gg3TU3n8n1?N zl(jF$1mxInV%bmsc7}=s{`K!RaZ48`o9aq^6hKg+_gC!du*z`!4lZvkxUw{i397cVr1l4)5u-4AdmO1@$bsf5x=9p%%5o8$m{WFH zsgWV1*oAH&E8DJ6wOxV7*YS0h(s!+*g(*gugYb7n)b0x9?$X3Sup)isn@I8#z+&P|qU5eHqhe z1P5o7kcdBQnmmh9rIO-UbYXFXDcJLH2d2s^G}B5dEjmb{v4Yt_s!CAvw1xr_8-r`( z0yVNtQEq-D(SS@HbXtZ?_0(y8#6h;r)9ySLdrn)6X`VI*J#AjqNmk5}(j2OBh5X{7 zdP4*bS6OGFqcl|27n%^6HE*MCHcPnLjdM_UWS}N!r(JEP)$xZBAV$^+6Ff1`3y}rn zd$HEJ5t_*b#{%{ow{!uEk|J$JimCFwA_dUUURP(_C((!3HEXYfSj7wp5H3P~N^Gx_ z))LB)8f2Wvu^{`Dj4-%V7HJnQDJ@>%F;*gQg*X?@z0R`HU;@}W_$k$;YuT*5&e~n9 zl?P*VQ;Sajw>GMHIBxSYy>dkF&{8y{sI-e>&`u9a)QEi8!#&f}J&$ zsOQNEf+e7pqsAMi>|z#&Tufv=2Ee+McusV~hHz<3slA&lQ7i_CdP);?S%!_Q_j_?| ze5)+-F0SJpYh%D64?|WWN^oq%?2ghZfTH5cD@f2>^C30i1%jcc))z^#n93L-kk%hb zVdktU7P89Ehp~~LkL(~nFC#mAVZ?GQKQBrpFvza_d^RGvqqk((NX)%AELH6dx-o>C@DmLUz3_v%K$=?F^CX0NHO{08>W$g9bs)Z< zAWQ23+Vm(L1WOu|zaqam*rRwsCSH0rsUZ2Isojqjx(H!iDWXL`f3%*o6`X+vLbLIK z6Dp=vyAhsI4Qzi8sl^ED6b8j8%*=#}MDIvcf{FkTLrr5W!>yAvNJha>;~rFN)b|h2 z&Ai2j)g`Utp#LIOgbl{_wH~d8E^IxT?I0Q^-y|OsdJ?pQGiiFkN1XK{-T{Td zz=t*`yPH5MLS25blM#`DU#l>{44z~%Dg2F#Op&4`vE^QBsqcWaLsX`!ipqAl5FW91 z^p3T-P&@F`i3;=o5E0b1q6zLi5kTV{_!WTQVYboTFqi>pg6M2q#3}J*M+Xa#tRg_Q z9T8ygB!I!^VuLeuK;vQ<2?rSgokA|ALM}ou5u@Y^XjO$f9&oJMcLJCex&@t(x+tpS zO$BG019mqHyR>LC4bapgmptC8x4VdUsn=+9#CGUKS*f}b1nX$(R2le6UG8ZQ?U@uE zYIc}SB5~!A=0zVoU(L37djQSq4$vqlVn>k`U++pEnldQ@(OgT5lap34wbh3b+bng6 z!@VfXvUM=(WttIv%e?NO?i97_gc-PWn`w}043TH1?j1D5DN1qJDbP}(S7Zt|F74Lx7D51)&=WD7IFO}KU5+R`vImtsL zL3d*p0XiT06OO1M;v(VV+YENo$Og2I_FGKwjThPcNsgyUNIfQldsxY2$7eP-U*6m%n5Rqg`|a$?sRD(!-b%47v4SIdx+{|`qOlF?DlnqK8$;suckjxVzGLB}L5l4K0R!tOZ?wr;-x&j+ zajT7daLsapdTFywUE8G{+4|!?w&`RKjn$RACuxLP z@vxx_{SZAz(wiaUEv|Zt6(dP(9(FUXFgvb_<*QBdfa!OUK5|MqNN!7>vcGX9O2;eg z^$SC=aV6?vjrXJ0kn@QIJl;>w!r?@XE7ADm0EvJ;Dn&%z=ftdGhFPcj16qD`D&Viic?xiy-J%9`tGia2r>WY)p-SLxq-=8bS|w+@v&dH8FXEyi7h< zUaTY^#tLF!xTqfW42tvt-T@oUQ<}L)T0x$*-vh1DVi%pM?CAA}E26T(5Edg!hOiit zMtk%|Ls;|$%_@2f6znxmQBAPYJe5q5ABtUk;7YObJEF=Fuq;$GkkUl#o+Id^YRwZY zy*qL!MrdS+bnj3BNZk&sZSOdD8R)o29H-RQ-SEy)TBA3)&FZijoDd3JsE6GpPGIUX0i-={xpSC`5 z<7`Q4EX!71YtFYU^fm)q7D!7pC>m#Ip9Y!Ci@Zm8ZRHgtx7MtQmrgkn8K}m|nJxsG zdatneCbv2;!&GniuFnwYRIQH6!wZeI$-#fanlIGC*`f3IjwZhz=@1 zN8*s#HIYzK#LC2CwW}OZ^W3c*LZhI=liHG07_!XErKp|^RC>}4XIDGLoy^)39b1-D z!fqq&Ar(F28%Btmu=YY&W{Xg3*p15g$21Bu#f^E>5tN^^03NtRj*&eDLH(3S91d`b z;*)z9uu_{fDwrKrOWvf4MvcquL00o#+Mjq2{rpDgy~Z2vJ$tcIn@5fIkk9qt&A!(` zLG-F8Z_Skjm-2X0HJDxVDnD9;iIh_2lGYksoq@?ZedC)3Y!@{EPiBWUhF%Q~6|@Uh zDdh;&Y+f}vUA6i#OaTDG)Yb0m6d1?r`!JARs6ac!;Z22mBJ^kBC#nYfDg0V9UKSEr z%d*gFg4CUz+(V^elRI>xmiOI>+DkS+64FV07H&tE{oY!nNs)|0M|!fQ6T-lf_E)xr zG%`oav*rvI?UMP7h~N%*Yu}<=?OOufwykl|$52uGmJM-Zb_WOMXJrIBJ+cR&^#wiG< z*tMud;LGoT!{81}8V+{am?Vs82a{V|!tSgYKe%bm$;4U&LISc>G`$tJ88@gh)bBTK z$U)iCy|lqVpINpw`1l%($)EPxx9<$rA-#JPbHXuloh)39!qqNZONHy{!gZ=}oi1F@ z6s~6r*O|g~ws3h8>Kb@rYS;N9_NBu0O5u97aJ^QzE*7rW3)iK>_1(hty~6co;ksP7 z-YQ&g7p`{-*Sm%5y~6cD;rg&}ePoxqUIu9}3RMg1a@7jgP~jRWTx;!e55kP51Y!~p zNK;fPO&NLlvnSq|FoH~NZ}e!~?B^CgxB9u=&z&*bM&1b4wiiOS_l&mh2P~#Q*#-no zd&>H%%!JK+5jjnL%K#s?69Nt!V+jsSLcqdu^x@D-Gtdw+7ieqYM0}qN@0@a`!GcZp zYxHS{NfAEOd_;9^K0>4o?Q41{L~}CR;-j8oH{89jWyewFaGS-0wcUP8NV|}^lIWdm z-WGZdu#R;sS;8?#2V4EO!W#&|Mt?7FYAVh0VhA}%hzBzk2=KyAKB-W4fnE|$YALr6 z{gZ`_DVU|K%3!Gq`#5DDlF^DxWS04Ma=5SzP2z)rdeYHt8KeJHK*u^JZ9^^Zgt(0z zFKZ?1trfG`O*{dHVk!+LBGKz&7@gMYoP-{1PEu0WRM*%*-QhDVuVK6mHUiB~75cCi zRyw+K)Q6pXX>UNQ=*J4hOTmv~;e8T9^BZAb*&D|C8W1djA2flU#u@J4sq{7~mD7Cg zzp}~$|KE6c=jKXprP9OyzjFB4Fa66G|Mx3*{-8B~_y5rQ%!!N7y#3+XHQ)To{2M?1 z_KF`Ld~(O)mfa_B`v?F0jeq%rZ*IHuJKw+hx9a(-ul@0V{$Jhmz(1b;s#cLn@ zi(mZjH~+@}I`c^6zm7(iBHhPI2h#uXFyz zP7VmWAAjgDy>%RBKhvg7+V!t5U;NvE|Ouv06`1#pOqH=~R^` zIK;Jl0Gdufd7Q9c;b_-zSnnnd68h!JO5IfUJEih^#T{#V)(Soi!4M)^3Kj5)D@unL?3g@}&9I)S)cUA3%!}OVsp-HEbb<_6Rgy-^gvm34#cn@@Oj6#l9K70LWHLqN`5uKA)20Ika zy%7U^yciw5cSAls{OhgMK=}sYoxebGC71eQLdVzWge2jibH?2Wrf)T3a3+*Ph+9)9C4(?y6Skw8KDlf1d`f?SSiC zbGZx&A$12u9m1U)@orm67Ga>r%jSOqL>aeB@jq=7)5@#vIL6p|^%tIMPr=qHdq56qz3;jd*{G z7`uM@Q0EP)F3WR;y4NMvreGmo2t{{$Q#)7gBL30J${9*MQK<)CimZpwgsPQ~fR?Gs z%Hu{_b>=hG+0Xonjsr`)r9&Cr#4j>an*(Z>ajWjgy4XvCH&e;3y(#0aRKrr@rh z^<4hW@Hv|J)+ZxVo-~8h@wksc87HXYOyv_P^5ajt|HO*C@nQF$NRc0Z*8L|^8Y&T$sm2IGP08qCt)r=BfaZZ-L0ovZN$fe)RQ8-?nc*e z)w?5ZAP{~E&%GfdFCwPkJ2w;$A0xG{kwk-`zPbx=G6{mtX`$DOy;2PVHWHKlSuwJq z8$nYj1)2mVQ5glvatYFrI`>o4Tf4nU&fI$ao56-83Dlckp_E&7KBhWY{SDK?r5Qk$ zg2l0qTZO8pAf2iUA#`5=cMsWq6M1n>a7c}G(LuX0&R({5W zoHP2fLHsSsQwbZEC-1)fZr$?ebZOo-`O@umI+}IUwO~{9Z@LeoXz$ZBsj`Yu^(*&m zzW1I@oWIyp>6?9W<0B7q-;E{kyR{k@5O$BxZD@w*3~n)vdeqZ3X|s(o`#dUW>K+{B?T$Kc;Re&FED zv5Dh{4?TGg`dm*%s-qof-u&C3IqRP4t-pPA-{CJDdh-4gkIf#QJ8I+o-(*iM3#7x{f5i(4~{I~T~ve587wBVjc<_;V@Fvs;apX#aHS_*&g$+_9b zkM2_)xh$-wa?gMJ2NS<}d~WvWW54jq4T#^yjI;^=|L_s<=h04cLX z&wzst!c~sDfsPpK;ciHG!*3lpc6{H#hv$yZ95{4ONOF5m<(BJ~5&F~lQhx?rT3!3| zafx7rUXD-g0Nl}2`PGM?IyN`^#65pw_Sm80M<1I#cF&$ehYlXQ=i#}dhn{@=;8S~^ zIy^g*BLAwWVD@M~-KGC!P#vJZ*i-pTK*7VaPwabg?!aTQn+59E4?X#n*`uKO`gsjy zsHf6@;K|1h9-o1Zi;&q9<&cq{$|}?3&c{L*I-UAtX7=#xlQXkVKBh~!dn!YZ9XkBf zo^usJcTl&llj;+84PZ4V4Oraw{uN4|sOoyX_)A3Az0a1e8kz`_*w@bM>}*mv})K?nOmPXl39 zCty8YzkA=YxknB&Kxb$AJ24+$Pj&C%eUJU#zQ=#Q2psOI)Sj4~+edBo{aeyAACIU% z)!2Tz`vBI6uZno~Os%{B=+Q$*KRzr4j?T`AO5jiuOR+P%*pWQloCtDkK!TEf!$^ulU;X^?gL4NC zGfKsMl7v;k|7IGM_?b#fjdG8!?WwGu+jsQw*||b^#ukR*SAPDyNg%-fc|+~M`=sG_ zO8Ph9`b5O?^(tQPsSF&Q{qpQl#O2R}DgOEDhU;PM-^Q@N`H8X8iGr0TPV@OF2tR+| z*s%joK8~Wa;@DBH8SUUrvzWk!%Awct)lQC^t}>=>V&f(@ety%!o78lZnr>24Nj&{o z9}t+airJ>-9D@%Y`@->q2lxFh6OK;ZuWRk;ME@Cfp)vG%MvpaMGgvKz4B7laC)?W_go2b8g?`T^+oxr?TeQp)b#U zb>Gq1lF`3hNg48&CAkrM5D$r@@UX4h%R1fdb)sMwT%Jg1nmP2?v1Qp5{M7@$cVJmC z_Rjhf`&e#x^6}jVuum4Wne|kLfA{#~dyZ1d>`{jcCr4*l)4<|hsmMn?zjA=?s#F46 zpZqd3K8N?NE~ECkVE0NBuQKI*nvN^1-v?%oEf)`;g$F!-^w9CcpSlr@hi8+C+?Y{k zEVq*bw^F%*0o|GWC@6+uHCR{}uBp+TsYbVV*XVB~n}jPwy*pNlxVn@5xU?5MaXh=by{=NRmSe0du&|SQ2PJeR91|E4#KVz)QS9a>gsND zx9`tslRv&$*Cj7!S-yPfz;29n`)*iJXQl8@QUADM?QTZ=|B9d9?CzW0ebdIYe42W5 zRQ#D96$sk!?`LtiFN=hqyss$g=kEBpx_?YzAD8Xlblk^O^XDG*v$w4%x;sk|{Ab_s z(<6H#y-=PI!|O z-sFTgIpIxCc#{*}eNMQF<153_r|mr}R&qn)%4+Y*(bl=%m0AAu0FgSB zl4IY4Yr48RIPyzA^fTX0o~i1}I>}r6`-%G^$G67B+_o4J57ZUIL7oP~sp<0*gCT|! zSEphjQ`rWSc}GB7*m`*1E$_7Ks8I%OCi~bQt=;@ft*?nBn5PUq>yZ`x^i%6<+zXb_QkyrNKM4Wxw1O$+hMxX zEFS+|^GKzL+tly{9+#Xub=J!S?7bv+_r&P7x2zP%rP-+{O{OpG! zVoCey;uP6F4Jbx7X9I@{HO?75x|Fi<9iOn=Fs*nk}-lbnw$X6Rym2_pw z8GZ@5jWac2?2A2h0nWdfa3t4s5oByBXj3dczmx#vyAw;AuB!x)m2VR&>#xZPJ;)T&|kmZveVUbVdyUz>Jw22ci2-%Z@8T*Qgw)B~3N6Y3g}h4ZEU;Di-?s zs@k(PvB9>3`%L9H90iT(xax^?}{mH0kio#(9LnR zdAQmac^WbRrv8i=Z+%0A6TO5#D-jeTv#C@}cW8B8%GH$^c!3Z@0cTvmcuE?)z&@)2 zg0Fr{iY*6LuNzvw?Rse`N)+)DkCsMe>x6O39m@Bj0A=}WdYshb^+*BS)(arrw2|)? zUg)nrkH?ouwXn|FjbgKV?xG{fcA;EA&Zptpeu37V0|d`F`XqrA2AhnjIw1t!9;)wa zDH6!oYvSH}RTrJtu40DE67nT(Yr9N4i=tBi!PkZbe7q1xr|a&6-bnX_zmSG89vDKD81K2d27+#_Zk_hg?e!@8bPoKCT7}3eO}ub6OIN2|(-v5y6Si3TWGdBE~( z$emb+;3<4kKe!Pm*2KP0W$2{2j`~X$)J(NH>fSAC{z+&y(yXc$a$0_6#Y&MDw4FxcKbv-Ovm!!%kq7ER_*Bn)a}XiG2zS31q#EyhFYx9|->WOfgqgO%rmNHy@-&ae zRk=<_%4?pkrq0yu=!$wqD0&u_`H*}jy@Vl%An;eN_u)_@ z==;oLXpaYfMpSB1N%_BvHzxYXFzM%RZ9 zZeAv?@s=ueB>I})RnRlBL^RmMKO%+u75S{0S8;)@=u^e4C z8~|`HaU@;c_8Xz@=y1_+eQ&j({^B~{pp7vYeVIk1FwsEcT)?-Z=$9~D&7#vCwMoo$ z-BU{&7Td7`+*V8VP;$68*7cbx(rp|S zp&{PbDs-N=DyK=4Z)=U{;!4ysiYeUpwU(%6j3pG-^53I$2P#X~xP9c*;MF#IGCJVq3Vd zEx2@9T5##UG__h%SXZT~dN9*;FN)cMl(2T10dqfQz#Ni+x}^Ke?r>#F0XVzw&`PS} zvfyfwvpYHYhoS&VJ@J0o@KoHEmHM#=pkhJ%FnWCZ6?~_iX5YHgDz+RKF-y$WMwgyO z6!(O1-I;{2nNg!Ba7^Q9G=j(u9!aIfOGItQF^|;A8n5XgM$YZX#)M|+nt-nF5gQ9r zb4^^8C4W&t@9FWOcuo19RCi|i+SM`7{mUWMvE`Y}Den+uRVLrTBg|wUkn2m4LypnV z`HQi$(U9kD96-9FFY@3jQr8!Uf?k!+vPTUH%9LD|v=cF@1&QTden~~WsBp$EH!kB? zb`g}Iz9kUik!7ivZ~n?tyMLPGwE zGVM}>C9DLGJ4pirj5kXuY(ZB`k#eD-6FZ7243b@JU&KG-(Y#DjOuPm!t>LP|3MfFwdzXQpjr*V@I_G_(bN;t zbE6l?R@+*y>hW5vVRKRrsne5^5}OtGyvBZcXz1EvYtVlJiNvdn>$Zjm%PGvxVbgf(G=Ci`}zSr-iKtT^eNYIiaOQ@DJR;N3TOT-H_24_>lv9?5$0YpjzNkC>w^S=o#T`=Sa%CGXwIRIt{v%tS^2uu%r~%d5ARfeh2lmKumKm`|Gui? zRy6(&aQiDg*;GIqZxB&|gMoH2GJdYiy_8fP;|v(qWqNtuNkx0IJ3&YtWRA9;sSkh` z<~8;4F`;vdn-6++xuRPJD%I?Ptnp&@Kn>R}y60sPK(j_}vei^*h`7~sQB;d7~k?`u2O~Otn1|6 z;S4`*!HSGGf;nXXKkF(MfPPw8zYO{}k?v;&sa`Vj1M-TcE^MO#c`1Z=ItIw40DQ#? zSCJuwtR@2sJVgqZr2Y#f6{}iB#uTEAb7V~Zp$WQ;QP`e{sgN{eAEIZUiB2O!=?C#s#5GcTX5h6bhRXzh%@33H3510xw>0c|8! zrmze+!IB#1BF(*<6XxES@*Et+OLJlsJi)1WS|o%l8i^@jl5*i7D!xBd7A}cg**p+@ z7|QL!sE8dsgR=ICHHPvBT^YuFs1=TBF{$m@ zcC-8Ze)FsWLSfykTPbosch&Qn#w+2c$*P$dvIa&oUB_a#TI_N<$g^qux4MNgWpynb zibrF2Q;Jm+cG?Ojrrx?w$k3{y&<5j!_QdaYxV<}I)3DbmL?GcfK!|A+k7*@81!4zP zAG@mV!&3pjr|jts9umQ#z&${a%0P?yq@ZRYt{iyV1ja^iTuSHcqeTUZ{H|N!qmx59 zl&d=Pl72x$RCK?^#Z<)he$lPgeDXVl-u?Y}Vag6x!EtGHctSE}ie#E?c_6$wNYcie z>ht<&MGCE!6!jd<|Bg^YV~;>8Lqd(_&lg!U6+X~(^t@=SG%gxN6ZWvCN>wH9#t=!Q zc{h#m{6%TIs_H9}d*FJc{@IbLwps*v6`s-~HqdycCw_@z42QvxtR8V_v@4^f@Gw-< zSZbc7I|Bymk&fwxY1OL=GSO|a?GTaFs(_9Oa3UtC{^bS(p-9> zf=Gy}a0V441;EY}pbB|qxCYATv3IHi5I22>OT|)%>3+LH%raP54kH?esW&V!dsN5e z<}iK@wv*ySDMes1UViSR-IQ5BaJEn>mi!p_UgDUh=Z$zc#ZZNMK^JBlBY5nb4guRj zuQnpGM9c35PV}G^sVsj`iV_A(I@SCe#_Cij47*-lgC^e~7KNYIQfTy&k#(;gP5PSqN!D|W>Vl{hA-Y@8*_1r@;fsxS^p+D@C-b$b{a zA`^2Ki5e#vb(nooqM>dfcRjGjd&a`>z>z43Eh~%-_hRdVq=LcoV_4w+kQW=HQW%U= z1ChW@MLIYTKvZpa40}L~bgTj&n48XlRXhhxJ|u&d;5MLG`$bIuX^_MoC48I1x6v34 z9{h%pZ&S9MT{V8Q(MIp)ne3q?pKWA;n6y$+7xpl-+wxkku8Fmj>XJeQ6|-z;o;Qj} zzLG<{80ArpYT!{*$uUx8e2J_tf_^1Nd6AxHZ8LcYU1<(CwMtWU>N5Z&(+lqW@=6ej z(M+(LhPV@_9=0Q3wcy-%~=zuiE~n`HnK zV*|``TxMp}wc%!MDy5umh_=!W?Ho&d7j3i$;S_r1m701TOV^}_^h?BHb#+{GM6@RH z5@&YIC}|qBUQ7@zr7cY1B~{s94COU(l!xrR=;!N2_w1G05G`~0ZU>vu*6Hok^T3x1 z!fQi?K9@nzvg!!zU%EIo)D!JcqCm>wyo(Q!gv6msGH9jRC&t%&Icr{Us^$gJ3{AQK zPv*%oKTkn=NdJ@0!`egR+=wX!Nq8~aQLSPJ&Cqk?<2Mk4Zked1p!|FhYT0%^;P`yd zLt$sEl!E&PTYk=^SwcPmFbXBexNV$UH_;o=tnsd-mwhAP$T?!IEv zmvl5Um=F>;OW5v3I55PLf*%h|H+qVEA_m%%hWZ~U{pqz_PcOBEwXF0S zsgJ57y7q_-cS3-@SDaJNGYx*z(RgDz+=6 zNNj#csG#T;0WG-Xs^C`5mAaJ;_;xXZfw1Qc_+i#>3OB(n_{8*Y)z$ zijfcug+Yf~f(F6{Wl6KPiI74WiDcK8;;TD6&LX1>JUtrsC-!CN2SSwK{AxqxN3DP@2#%#Xxq$G zK}HshytOi$Y!8f097`1+5oeQrc8=M$cxQ8lU{+>k#LwEWHEWY-x=38jO_D5?`X(EuWRYm8ac+$%w>wPSF?1=KB%`&Hn7$MM@^2CU$b(vQ^uDdrz zHqIsTr<|^+Tj>4eVcGsw6YdLHVa{PsB+u(=zx~-XY@`$GrixNA_TqKbnxl=p>HaATC(XJjOO>NqFF~U#Y<*re@gu%bJ{(9wvUlAex$pjcckRtu-bn>UccETn3N&84UZ*w|o}1ZX>{SdTC)H(ik( zjP61NoMx!@WK8SUo6nrofW=_&tcArqRri3T1&s^V>STa&%BMA8+2@t!C}suuNz2jZ zi8>fET{cfEq#luN-?#)R7=9lsETiOo+2h`qz5rgJ-}XB6JWY|KkD&!}Nae8u+w=(N z`0K_B7=Y@@OMQ6>y86WG3~SRWM?m#7 zYVkVh!t;|ZE#JG&C{Dh>!@M>4)7G4>6A?3lg>2#VD7@)gb`~>~gG9%pz5))t2DWRw zC8xulC-xYtHVVdBQC1x8l=eEm9IK7Q%o1IkwiM?GQi3QKYzzy26VbFeOy8l-##`>d z%jU5g1-G@4_yABZ$l~NcMQY(mtT?zR85aXQgkcFtVb%VMhWbgVSd{j9*8HwTn_bxs z>0p3HlKcd9#ByW8^=wXzO@xtN=t@*PeV12VQ$$)q(K*eDbqdqA!vrA$fm%c%W(K*+27u^B1`AOt$}^o%l%5u1agUorRg=~id6bLq zhsej7eu5DQO)seSyzO=&zN_;#ZXg@ot)L*xQK!_&_3CX_iL|PBQB91F4N)NlZ|hlu zrWa%fvA|B!JPj~yfd`N5a}S8l0W9^4Zy?-5sBr42)}#HjXa{fi)DA`-L%1C_)O)H5 zoah}EXA4W$s;+!40S_rdHZjJ=MJmD58AE{4On1kX*jJ<&ObU%u=oJvi7BJShB%NMC zt2bP2QmlBdq86XWm3&pMduKuJ)wWRh~K&+k6oii$r7FtA1E@B!@ zz;o^B)4EoXGHF%ftIDCA5Oyzu{|I)l2q*faV1zg?)*{ITr&eDRkRSH!Cjx^z+8 zh0(Z88dLl{K>>^BA<8l1PaaERCoh6-jYPY6-V%qjjt5Rn84Oq;-KcFiG&zz zkSOpYfYY`|I*FYv?6&m4*+MLQZG@@v3f|R``Woh@1gUx(Dz@ve!W6N`20unhjOCkP zyP0WL&W)!$=>P?WSb4=OqjwlDSs7#k&y-pvz7v#|7+AzI5r?=Yu1SzNmuI925MmfW-g+Th zueMyUuR=}}kzKV3kkk5>7L!)ck>AShWFMIrgfzY^XNi#FSpAFCzX_zn-H=KQ5_YAG zF_P5Yhcr%z*c8`rDU0T5Tt*evPLo=jr8w_#;Bz}Dk|)7fDxU3_!6(q!LnA$sFxx-+Fv(3DaZo`}gS z3=d6^(hNyVQ?^x!Jx72$SiUGtF4fgM>fB%xXp|_u%_wIW`Ejgfza9(1p=E0;j~36 z$w|btEGtfo3ZtRn_+ogewqd*#F%%Y9amZ$j@G(<;L#0pq(n=7ED5mDGeoEUlUcGK? zpsD7mVEuRxJS6Lfq&*F4$FM`F8iGe$Y5F(M5j|TlNgQ~9)`ps zIy95@ERC6HWRWV!64xE&8il%@={CP$EDGarfV?74b;rfEL9+=(q=iA!ZJt@iN=Ne}F|z@W{9i+tdI`Zjy;f`cAoubNM)*Eo_ zgbkrljBfzh;zLJVwTi^2f!4jnGNdH3n-N%5*nUtm`9x5&4rIfJVomvn;`ZUC69mxj z+!UOew1TEw-+!QMo$IFq8G+|Qy@O;6PuZSBT@b}GmvwdlgkT`>0RS_!KqxaX2J!5G z2WirB>GMRX5B(S@^bZwYi&Es(=Zd_PfUZGtZ$7GR*NCJGrc1sApspfDndxh5;M8bX z2tl=Q>4a#0iJdv@q_IQW^%T&!S}5(T5eyXqJ8R$KCdd8Z#n+C=l?^7wYra`b-$OD` zXw`Dea=;cfW3ncc%%O9!r9cht=d=ibG%oBiUCT#Oy7Hv}c%-T~=v8HUBZkcQ5o8gW6?zCxWtsb!1ng@xG(--6oZw&wGn;@%^NSxR>xc zm}I=b3osFcCN-Xxzah&wrWLx4MoeeZ;H~B->m#YVcR>X#PPneN;$xks#j>vR1OOK2 z*rB=j5mrt$v)gFra&0CGG-@X>*MK3Ipcd1pm`z3~(IQ-#t{kJ@kQxv&Vp`$@Ojjn_ zAtD!RTpU&?@#1M1jQ~yyLnwek=x9vip^?&Al?k2oCVWsv3Z6kbMxo9osVJbOQD>9u zR8cuKJ*0}yv(3f_Y^^U6Esq(l6uGfsc(c;=01$`rui|({I+#RwW|M_SwMV+;FJi=q zgQcbs%Y66ZT2p&C4?qG%JYKfZf0{tj@ffjKL=&-`TCH2I zJP?wS+!C@c(=I3-!Jr8@6l^M`4v2g$4=Dnc$vTrhhgUoJJd+xJle$t?^tgerLt0!Yw`wuy7}RM+XFfR0Gr z(1VQBYLT@q=6UV!^EqIR!H5ZWO^D!|3nl>87_N`D*UOkPHZ}%AvYjim zY_&6h#delGINJWfX#0nXdyp;9lAzYw*=IGtJ=?Kv#Z)8JRW$|&OZNsa)pE+T6$o zkeZ(vDYfwxbP3->_Con9C|Ru}L%4)?0bBSOI zvPO`Y46iB_)AB%g5YT(v=8lw3yDpx@T z9ongy?_D9DQfY#!A+3nPU4yFBoYD4=KRr@K?B|~=0E_BNpQzk!M6$a|XiVK5QbbZ$ z$J}WsTEWQgxK2uf+0)RYdUk+d1)&`Ni!RlpHX4N@luB9L%lU(tY1Por`zgaV|9nq$md01&o3xA{S_eJgbw?XzenvZ8G34ZAE zXyZkBlKf;Zz?Trw}{7wK$sNPIWvlesl%VSJAgQRMEo>Yu7Mld`#E*AA~(C zFKR6xa9;vF`>^A>mb2PnPzQ<@h?-?6`h0-FhDDgDf{2|0dZ;a|Iq45=3{|utp-~6(tMu|D*h@n91 zX|Wbs$(S|&=ZzyPJi#T<{P6~rB3gX%4p-lezd=F~YZx=kDJUvv!!8gi&6;cuY5uq- z@d1&tOGH6z#cuxC=2wMj+Z(ma=5Glkw!WrBR~ZP(*8KM9{J3fJC#Xnti9}J0Z#VSn zLu_>7T77J4$y&_FNp}GlFH0+llX61n(5u+b35g6hzB+b{&$_u#Xjfz1E5f411d;2_ zU#~|JbQVVr)Oo?5Ek?yCR>{i9o2kKOb)5DE^{f83us%=s!em;vgox(zyiNOS;6}!* z3pR?;j8U8B?--re5e*aow_g#g>0%OBR1WcdU=NUf>q~6UBUkH- z7Pd!tt_2^heKhAl2ywN##RXWL)>4FY!Gbd2J*|CeYU?QmktFo!JfESN%|KqGt)KLg2&E29^*Ira zdOfT3vGk@s2qc263h0HNskHcRq~Y3zp1>D4DJ@v1c48!fHtJ)LkWNh*?;D-xdkm?- zU{F#HGW9toqfen=qUCHIeaa*8IfxzN`X^F5wK3tJIHl$%wBdPrfPrd~60aYlFGweq z6}V`&kt9()M47cT{;222F{hP?zxoJ5noZTC^IIXp5oy9OM13BQPJv#LqVg#?zXd4@o3RQ3i;WVgl7SaL9NoePN zgLIPNYUQP>p(?qpUA@E7B7h8j9M*asA7NC%gGfW`P7L>KJTo_Y8QygPyqhnYuZkOj zWpTMW6mJ5IX~kq)aMlY1LwAYavDP8p1o2Rr+6uOh2O$AWRe7|nmIpNM4CJ8$oE{1* z>+NBhDr*Xp++otOxCc_2)Jvi=Ymd^~TWTs;=w*V&sq0c6T(9@D<{!fGRonI*$blY$ zJ#DItwm(mFZKM`A0)zoSTpigLy50M%z;Ap`-`H>+{;jO@Z)|M}l2$ z{)-SR-4oM&X~kWfd~}zVr|;6CM?ACqX$4Ga(*&{g+FdjhTaQv;!0M|pYJb0%=bfae zE9n*|frfSo2q)$c*0@04T8bDL5fG*|nx%#lIm!XrAStj<`?=H~;_8CKUen;?GeNty zLS);DuuGIOC|^7xBc|nuj1%P!Xe~M4y-GPGXWno{*qqRKPdWK2R?mjx)*9BF3(+^E zf=Nm~9F49}I|Us~L7P$&IW+_L#|`2oKVMV8Lw3HNQn8m)u+(F_UFP}F_U#P4Z3OW7 zlb*RcCd*$?=H@zuu(h9%J2T}*=ciwIYo6x*X%*eyJncLu{WS43pAjqu9?w8jt$P)l zFS+QjiPp(4`#Iu-7xZjxR|v>WyK{Vug8+Bd`g^knvj?m4JXq8D3gjG6vgU^z;nw`nVf2y1=5^}a{Mb-G=ar3H z$zb7RC7*SYetws` z#Y*gMoRz2?I!ZC5y1Re%;LifZAO_To`AOuEFk#1S{i}57*EsB3r=2O8e>%rc0jhQD zBWm6HNY2LZ-`3x+wEl3R@tiDg*wJ)wnKCGpw9r1M`|+(X&KE($11tJD0|MMWkUgNT z)1^4O39I$>BXRuX)Ukf-X8rxWShs4m!T#QHWUc-y-xNv@uf`uk#K8Px0ht=W$o8L6h-#gl9gCJUjqw#kCUk(D~r#?%U#Gp}4{M+>R7y4HT zs~&Dv0G2hjXbaRa1g=6tc=)PxHIDc>_-jrMf$#ZyBac>p+`CHMitPLxD7cnHp%jz-08a3>Vv!8drwB~l!g-HQd|Q$1JG|&eW}JboWDH^ zhVnnuukms=&-VzgkTm~8rC;LFdd2nebN*?$^9M~zQ~=&9F7}2pyg}U&ilQgWK#R%U z<74z^;B2lG7^Pt%O+jj$g|wYs2Oqu{$*1w&Ff<@t?|vvDpZ-NSn9uTM6&Wc8cmUoJ zw88sJPO^t&I;piQkI5#exQmaoHFQd+X@llh!Z}eJkKy(viWDjh*7|^E6K2(6T{;_f9 zulg$-7(S3>`44}oe+Yqag%AqRM&!ww;}SB;g~=AmeZkLj5OlioXeG|9U8$##z`*MLRanFpuEiS1OaV!4oq+iwFr>gn>CAESx zouOB$2sQd~Yt|G4X>QeH`z=-z*g6$RSUYqeR}b9gPNhJv6R9KI&5zDTvezl%tN^Tn z%EhCuFesdmouBW!%;7|%`HQ~WK+S#8>{LVR5c%EDe7+A=(;BJGp#V5OaVoY<1exx`AXsVl!c29_%o5< z5cQPPB(mD(8os8{57DW7-#lT*FoY9#?Qld7GQQ(kEgt0kJjC!bl-Jed*U4q63Dsxw zmlSlVK>Rxa@#!TNGbIfZrp{YHIB|zS&VM0Luc~|Gtt@}*7L6MXRN4ofG`H~!RO7%% zK78BI-)s6Y1womd<`Xp0cPv=)yMiF%$Ut#VdFv{Qk_~>k4l`w|pXNiu^uG@P(XPAm zG^KeS?U*gDoy2bVch$=8+Ns0S=f78nMF{|<>otfs!tub+@-7R!6ff6`l^h@>l4bo- zCeskB_BcS*X}tphYrWwLlZ3P0Zge> zL3qQdw_t#(ojC27J!Ho!x8B z9Z%&^qPJ12?-ifaF+G|V*s(74tF`0M#df+S@BB(Xvk~g9uMDpD^2|<=m{<6AGMj%_ z7e2&LmHMvXyywR= zqP!N&3v1P4Srw7H*wewFb8m270jcp0aC;0#YVtsn(B%6eK# z8i}Q)!PvUY-2eb9L2_*(7yD?m;<$!RCpbH%<>^405^^ zGjv&yQ3D!U%mBCcXM85d93>c2G+Ei6Erbc4BpAvynAHXj=6TMcTof^*y9Q)`A&Y8j zdyI2zL?r?Z-PgzoOp(5DFz?c(2&E_x!csXlg60oZ!!78;bZCBMG>F8|LUqjUa9c|X zP!)E=M_>-vJ2{{puyQ=sKkP0M^8-mRLe13Vkx{ON zcPC>hb%Iq2S{vp&CPGXtOb&qBfj4P3u8L3oHJLV+RlHM?2mm}_S&T^R^Jz-fSjoo_ zq1p&hkR`{j^$%tXdpaN?x|lB(QU)$<1yrOo>ia!u7MU}vwzW}YTS=juvDA1va+Ai> zVR>-R>R zavL)@-$1BTREy$`l!2m+uhKJ?*Ou9!U3pZNiyO7Zqrlh-cq}OTN(#eO6s-^p={aZNA&;isQon^j(bA)~w+iVnl%T)YYtqR(+vzvxX^wM$XeJ>3bqwk&Xo9uvdC1huwSO~F|Ar?%G!%#MQz2p{$bDNb;K6d zeNF9GNNrfLfpIM;@tiG1M#$zZni1A+>mPC!v^ciU*65&+S~YJxOPP&lW$tVKSo@H| z#g9KGv}rNwP{~s{evIgm5`R^*aw+Em(UfSlRfdiC3$~9R0k9O=g@d>B^VzBDB;p`2 zIIPqx9A+-z2~XH=tx1LLl=9l7&W%u-ZD@N81uUGvmo?uDiye;BKy|Dfj^7}LuOMS3 znw_3>Z`A119eT1kBYY^nGm6Z?9SNNWD^96YvN5&`_zEv;K?}W^tYc; znG9~BbdP@d+rs7^9{Jm=QF+y~p7Qb7(LZ8lzTZz>bB@9~9iDs!Jw6r&#ePoI7uSN_ zbFwYT?8I2-86sKst2w-L-9+Tb{Y+(ab8O6{(u=rhOfc3uRTcN1E>hf|Ewly3ZqhHb z|2o>s!V)Q|DSILqK&XdEhoFn=5H38__Sz>Sm0Q`3Q`ItP{Y$<#?5a2vIc&#Wcv=NL zor!J4I-GJt_AC69bd-hD*3?hi{fynuti@1Xn!RQtY6uh8U?B(-SM5Yu8+)hgurb|F z#q6Rh>@RAFg1=pg3i=c0c$qoyG7fjiPrLjM>t(yd5liWfJ;-8L>I7eUraa~n&+3_P zUMCv0bthXmg9)TK!b?p)Qxn~63O4`drZt)toRzVxJK0%bgJd!c$o|LgAU};y8BxcW z7c}o+x*?P1!V6+{TiBs=m0#!zX6m?|`>=A8ta%6J=Z7&2yP2qxM<~Y`KK+T6ZwB z;`0{Q%5nnJ9i1ffXl9@0y!qZ?jbH87;IpoL?{D^xk}8-(cz;e1DtUVq(KP-)nkc9|P46P~-~vlWXBpW()at6(YcOY^#&~`m_{U%J%{lvf#*_Y~j0t z7}%_8DPsLDU}DHdaTcmrl3YM@SM z%CDMrP!Izmxfd_+Y5>{7HWV z_CagJRyBq>wI^$8fv$N%aytJ>F!V2Ak9AMHWg>eU!vm7NOU&=o)IeIPCZpGXXA6iu zv@4KPyrf0O5VgbB&V_fqkkg!Cn?Z;dqnfA*IO_34mFY^h@SbR_le+}uJ=wm?asR%i zf3ehZcz0l+Y|onn7NxiZ-D9t)Y6P#!1PaGdlYU#ylSXWj>khHB?(*q-7Z_`jhAOiO zny8E^!C&eh&KANZhyag|wiFAS?nZXD@L>QMo@c-#4H3E+6s1~P-+F|Gbluj@taX3Tbz53SYTa)J#9m|_ zpmsr`>!D06X>rJfp%LhVB$XLMRFcEiD_JcA2FTrHYBez z+uXs#n`*Pg9Z>_0uW^n@AOpB2iHhIx3qlDxVhD=49!SYag=ka42aMN8e?jtvF)n5L z+Ddupuzow6v#i-%o4{D!EqJ@P$wF~$id2)Xdh3%9ZJkDuZ+{OyTUBK^3X!JR|7$Yn z{fw~KQ0+NEe0g=Or43!JE%50?q~4B@Elx>uU!3yp1*v*W78mzqi;p_^QI8@aPS}7y zXr>sv3CUdZF7iYCI6C4I4S~Sv7-(42o=l`f+2TyLxZh*hK_zFXMjoh|WNk59eA@14K3DY9 z;y2qlt%pHxvp&n3GJgmu`73Jz-_3N>Bry1>e({vVD1BN43Cbz>y`_>^1p^zTOI3kn zywqi*l>q1tz{pU-a+*_lF_eT1NB4!MB8#W(d`8Y^v~mIeGwm;Wg12}^(TEx~>Z}xK z=6X%3xr=AD-V@I3)55(^$kxnNJCnv&&|HSa?_(4TJ5Kp%O4GUV6%*p3eN`YqfShk< z8LP$R6rx_}#0$qxi24{^Is#7>@T6<;CD&iujmA0kj`4re0k8V`+A#8kI?2w%imkRT zrDY`o_$gKEGVRvNpsUDR6VJY^eK&=)g8=xR%KM%{z9}aQ!ZdHYW*-91G?(SDEPsx; z{JDOKD9}uDzL(Jy;h(T5$>QLYD0}g;KAaJd%OdaA^UMIp$HqAM;19(3TYrF^tmbVP zDz5nsVNL60i*G5DR6u={A%*iJN~TSNHle{;_1z8M8U#eEF)}5LFvD8Q0{hw(-m_G% zGmBlT!Q_=rVaC=u1o17>pEv`Lalxz!2bAphJR9()6GT`L_SZbsO&Ya}@3C11SnvHRR<8Uaz1sRVRD~w= z!_s!K!r2``#@4qr89@zuUJ`YT{hy3u%@$qf52X*cJ_=)Xg1Rb{kDhogexzbQl5=bY}#Iey-RFdF^}b4IN7euul0RdAo!`$OTEIhlOrEJ(`;NxuDtI)krK?zKm;e_ zoID^nFo6ru=#$w9m;edekW}!2z`-EkcL^~k*{5~WrJ5@~BssKau0l|im`D)nv}=`?)i4OZ^+S(gEk`25ehANs8P!(puxkBKX{v+$D;e-V}F5wfLbjVNc7^g;nsSZLXk@vSg&R)~W1u`rKn_7wWJSWJT^0 zK{YS%`<$AAv53bV;?eQ+yK>k(U)ZVID)X9zVwauJnAD42w#@3p3fJiY75jjlyXD+% z=aigNb_z>Nd+e0%xAdrNY254@Bq)3CBr$_tJ|g+Qv{#}bG)d^NpLvwj^XAL?>BGpM zu(?O`03f#1^5##>bLoei{TJty(QcPJt#YSB$L|Na!OGzv=4K(t4zrJg%FQ7@%I5_l z`w)p*eYJan8Q0QbOv-$V89pW@d5jy!Y0^I|#o#BX00iip58Z8F>+fR=9uibcfwRjd z$HaG;-NaAWb&AwCY4x1GAg@S>l_NwJF-jHHd37|ic{R4T85X#%Vg+Vj8DE1IsS5|t z`Pr0Gj;E!KZwguM0rtv3FZx(i!_*1^Gf?RTd@)M-jw&x&D>Ot)6?8Xg4uuZW+G71@ zi;SyX0Ofxt}h%PnF!KOYUb%?q^HxGbQ)glKX{{`&`L= zzT|$XnH~a=%`3Un;r3TXKJ|S3wNG=l9I zb~%{@JsWjQjQ^DAukj)b6jt8esDox(ukaJCos!BD!jm9uZzK`lMZ+G5Ly2i`6rJ)Q z5Osmi^!r!vz1|?-%Ee+rVd=@-tO$1jJu44P~x*n|13swtvJvIE4zktMlom5olu9C zs^dZxOigwM+YggP!k)qeVIR|TmK)hjo8^2|qMhQw5{T6RPPKGafX`ANn;>nd2__iZ zqQXwGr5BXw1*f-`aPAQ;!J%zS;+Aazt~H9e-`@XFj)mFaQwQ>y*Aa)ci?E69l^A=9uu#T7 z!u7n<{g*tyY1bP(1C%#$z3s3Mim-oOxc;x`8mtVRPVm;?3VH7=T)$She!Fn(EnL4B zU4xaQxW2~U%F0p<>#uwRm;SA+Jc}#T`MBA3q$C|^bU-CV5uzkYCm|ULMF=6v2%!j}^n2g;y7#@G z4xi8e`@O#Z-*x@wx+X8LeXqk_d+oK>UeBHxJDNj_?dWSDu0akJnY+1-qfhaFNvd;& zqukq=e$coi?KkS#Ic$x4Kt3)pB|M`NN;y;kHsj+-iXuitB`beOynYejpxS zVlKLm?y;j2KwOs`dIH4#xh5?Dy^inF^fmtFD_xUznfp^6e)IP6!YKwNGPl?8Dw zIh1eIKncGL<(M$8l^yL0x*p#-bf39NMzfUAUqHT$m3H)9qc1G(dysF9e?h)AlGFWJ zqGyBZ;=3ZXz`tFDZd58uLqU8WRix1%jw?%x281+8k5;4UAii2jS^(lPwi+!r_a=zT ztw^hkP#t_H=`8%~)3yis_PZ41`)OaJ0U#a|OVbRD zqQmijrD-bu^)y#0Ny{`YNpC8ZrVSvzw@TCJ8s}S=TRcfW!TCD>4&u7x(BB~6$Ag)E ze#@b9Aa)h0I*8kmWZfeE{;;?K9BV_)gMZqxh`QWh>RC8X#YTW=f^0D~Q`Fhx&kcBuvsk zkk9#E5MP$Y<9KqukTG~8J%y`6Xf%| z4&+xYH!D@7p&);+K43IgDTkJTxEJTpn@Xq97SPZ5#&6|%+}Gt4DhaBL?<6%*SC-BK zaev97b|CIoWvP?8By|P(+V@c^O}Co6-`osyOU=Cr;xkj-%i<=1e49RH^sdo%qy0uz=7cFu zRXT+(0{ND_62v{MBHak`qtpU>b_m^?51Bk!s z)8!z4M-2e+*m(-2LEOXY(gYCS3pw-%h--Cid_IW3a%ee-+q5oi0QoDpLkVk(XTr8B z58`&_ujg;_|K(>hM*!WAkZ-se!m$dZ0&`Dw}FaOtHn3 zqLan!%`gpVgv+Izo5NJ4sFCDzjF~djOiTkam8henYHj8ix=3QWnyF6RQNLWe0cHfg zE1))gB<4Z<-)u1_&<#?b7w~_4rbKn9Sj<})<~uR(!+eTwj^QWPT>4JUY?z-U=RY)6 z1)S$UgkQf(S^dNFYDmuUnI2{4G-@QKftgm+*m0}2RzuW^P8HKV!!&W+&evq5I!$8k zu$Z=Vp2SR6(}&tp2bV_aFIlR#bfLtoG1G(kiTT7#FB&8{?^c6nbh-mgkW2rV<97|CahnhJ=)UIOxY+RSx?PLHzQ zFmo&M(kz$0%rK8h4S$F6SMXMPTug9lWafw|mtkHLQ#ZqWis}C~~QT#XL^Eg4sB~>nxuq=*obv&IpTng8E3zM2ne2*9BbG6BaXv`b*3!7V{+C zA^8-kDWWIoPKo)-ViwX&iTP8_23kmuiHQ#gd);DMAgRi!AzDH&%URU7m?gACV%lfK zydwErnh~>1On);=X;r}e`A#z{=`G3mAsF8`R?^!6_d0$ukiJ2$()xh=#!499H(sT8 zrSuJ&4xX$%p z_+GAnm=Q6)f-N&r-7Tg|h8ZcQe}=gy#y#O~HJs1A5;M_aO1eogKKI!cQ_?*oW>JQD zSZesbnNn`LSN=(^7;kj3HOC_ItGd0~CQlCa@h>mv~ zB<6gJIo@p)bB&q0ZnK#E{lc7Yas%VshC?i7h?^+pZZo6Yk~nS^DpWJXjdIH+<}r)8 zSIskO`njp@YoyAhS1sldw^LHRV`irNJ5T+_KsqP?z0lKa>$i`naH zCb>RG-|o+%RpK94TVk5Q_`dOvt1IRLjalIixKop9J_|J!umRgt^0_C&v=lSlOfqPj z<8w|09g=*eZ(B?%xIkh)Gm{;3m$Ud$4N-~Ua>+R{*w;A)(_2hAHCtWvV2qd= zYGw!d!Tn-RQ-irXm?Ea7nd5_LVlIO5?R-M;f|P!p#$Xi{yectwtJwhahL~w;7E<%z zM>((O)l{H!g9AzK&&$*daqaPgb*@G(ty5Duz+c(R;y$($#`m#~_$-94S8PbwVjTng zwq%&fFx>0P2R8)#caC$ZrJ6p}FX$;T3tNS$ZVY;f>2CQ93&v(~%lFk(6(}7%l*LzP zK!%x>#eHK`hMAqkJ?b$vM0W?zNY0Ba=I-D*Nwvz%s9=$#df!rw4wgyGcWR1gbg*3N z6JROe@1`-qTT-8+V0>>G6ReY%lQLp9N7bjKB^jm%KF7$V^=cL(rl-Vgv6uzH zO_K9&H5FnDf}16#o`mt&;`QJ){1)$ATB~M-TNAvM z;vTh4^Qk~@25(DQKdDJ!4qhi^?N>9zeHZMMn4Do@4S$B=e9D{I73`M!)Ks%E*d6>K zsZLchH~201OWLQc8lvBW0}^woniLFW^A)_#V*U)Wv-v8g)l{HAgHjSR)ndxV>PgJw z7E?A>Kbvd#oSAB|<`T2oO!e4V_#O1Q^tqabv6``S@R?RF?Sb*L@UgLT#bn(XwrZ_d zJ4sbZP5Gc*Y_P=eO4#2Q?PGUH4NpZ38=~`K!^O1CFe9Ztm#V3Nn0q9jL1r$9O_rF^ zX1d0nlbFZN^oYGEXZnJfp0RhNtc_}jE{}a6`4p=ug849;?l6D)YB*92 zQC~F=W|+UE&hyn2A?9x}Z>y<5x5cXEaNqda%#hegV)mN3BX&v-_o%XWh1X(etck=N zucipA!=^dh8mFnjS|@gy#I(1VDQe#A8eZjxV%JK{RT@))rp0a&Gt|ti*l>J0noAR5 zxP2-IkH<#ka2r0MhUoFwJreV>nhG$ZbGY|zw0s_q-6uJJaz$ABQ?XeRQ*1HM#h#Nh z{Z|dqbFl>yQ#Ku@dM>t7%&{3}mE_ze!>pFpC{Q!REr`99!}CsG7=M))tGUxsEsnh- zF%!%ziEWmgAI>maBua0Iuh?S6-%QaOy_erc| z3GQPT*w_+0vhj%I&EK&HCFXdG*%zB8 zH9X7m`8PIGa=yS!jd+omUTRjxj*YJuGt^>^jlVDX6q>0U-z+hYnK>!GRm^-djpM(F zS!Je4{12(&J1|`58o_CCSCX&b7Z%eZo-E1NVwag#@tP778yU7nn|NI@WireuVooq~ zLHrys=a}ggzfE$!*i7H}bcq>sNtkp0_;NA*EoMOc9lY?6OZTbS7z~K7mwaYe%G4Wps+gG>uPNpv zGf%|vml^QY7KXnn(A@Zm_*)IRG~UcJY92Q;Ki*JcmYR7!-c-zcW){ZJ5VPIPqWC#t z{x-8X-d;@ZJz?oD$1fCfyqTr(i^a4svn+n8n2XG;h+iS5ubEZxYsCyVvpRmGn8{{d zj}H*@jF~m@A!1gWSsTAg%*SSm;`fNzX=Yt~oS4|?@GREH3&m71^IrTRF%8XZh(9W( zotY2fPl&nP%%=F$Vs10@aeRT831&9OUlKFN%xCfCVwRiP5`Rt12WGyEza{2-Gh5^D ziouqg_JnWZABs83%=Y-FV(ObIj(;VljhP+sZ^iU9^F#bcF$2u(jPDjR*38fGKgG;8 zvn#$|%u+ME@poakXTERdH~h9$HpOOs$1iGTv)|00@$zEIj|tD~uXvTxF~po`=AU>C zF=v|D7q2O%z|8)5Eit{!xWq|f2AGK@P8L&WCM(fY%v>|siRNM!n<MO!dS?VhYUE zNOUP3!`RZtOwB}BiHYh{D{-lqsPq#Oy~NyRsp=-K5ObfIritstJZh$SqMw+>X4)if z7gJ=WU1ErsEoRP33>CA>%=w8s#Y8QCVd5?^@v**rW>e?Hy@$kfPK*^(QDZ7l&%`7# zb9=)`(4C!4u1v7vMV3C>b8#7#(SmY9wfGf_>{ z4+|5Y9TM|kVvCr|E!EV-mtt-;GcB=I%mg#fC%zXG^|6JCAH_ti`a1oCO7;{JpAfc~OZE~| z$xM*!E#^csiDX|fQLE-8`-_RLdx_+&V$QWxrIG{1TxzCj@=h`R%^Z^)F6JIH)styy z`DtcqB=44(sPz2gXfe?>t(hDnCTize$@|1aBldC0ablv@I6gT+%=1=O-Q)veUN=)e zSturI`KHNfVxsHbA~{3M$Cm2M*PE!@%zIo*fIISA^BXCd_~%@hQ)MFE*I0xOxNV=lInaj-I8yK>20Qa@@+8#&GbmF z7Zc4XmnGj96P?%P$&F&7vA1{fLos74pKFqziFs zmi$>v)W-%VcZ-Rx*B!~<#O$(E!;-&CYwS03Z}Pw)`HW2_b9rP-JrK6@_+(1V(Pr*X z=7@<}Y+ACcn5b{eNaF9=BA;lL@@TT6m}s1znXDuxYQx8pRm3#1vSuZ#iaF2Bv&rMc zTxRC^WbH%pc_CR>OjP=d$$DaL}fAbVIjOzSyvc8z8A1+CrEap}#>y>1KLt>UE zPZ1Nf#>!-4G0|Q5YO;x#=pKAM*<4I?53Wg`E@rfqwKjQ%n5ZAVlRQhzG>dsRdA68^ zW;P_x5wph3reqs2o6UTbY%3<}b)O~Mi`i*0UnD!^^4vrd!=CU}@&bv8`q;P09%75Q0&g4~MjvmO;Q*-YK6r*iK`|9RTX8Cfri zSz)H7nhj=JsrlB-l`z~7qw{(=Yw;oGk*t@+M7?)f)>1Li-84OGnV3H;=NVZm#FQ)y zui&FutHeaT?uo3mVxrRLWEJI(M14-MR8M8ClbEQ*7OII-EzbHtVxqqBQr0Fh8TVJ# z$6}(fWl7d%G0}Q#Y1U_A&b88C$=V_&T2U|0`btcc^NOsm#YE?|Dr>u#9+uA=SwD!0 zYWQZ>PBGDJwKnT#F*jMNx3Yc_gFlI?PqiOq?G+QPnm*0iFD5GMv#bMRqLKgeEXw1n z9No)bW(8s%vYfxdAM)XtXw=%8l@t@z@awFUm}rdpCM!owG^1_LDk&yeM<5Eq;L~DRps+pLpEGC&cP0a0PQmGbV zqS+xQb*7l8AC^e9%*&^X@%eg_2Z{2j52zK$(IC*dLXU#l3#|rS0Lr7ypw2=EK$i&} zk56K+k+}0gw+USb^10k+ZmzmKdQ+((eWBDjy%Tf?(l$iv5#1@d;BO*|!{^OLb&WcL z?v}V)lzjch!sXEvxJjUb^a@Q;kbc+bGo^g`3N!<8`Sd+#j-=(Yd`@T=+)|LEqwon3 z*UwP{qxMGE7>!cOr$<4rBSk*VHChOIOLATYS|_v?^ggH|eGK|gQhW>A2Fg71f8c)f z4xbD7x->RAAM}eH?E~5aDoBq2{RL`BV?h53%~Z;#Wgwr+I-{tJZRTRw!{$2U9ZV3X zXhN-xx)}{HnqoA=XpYf*qZgI(X(cE&iOX28&Smt;&F1zfHKcObr7nRKc~lEjMyLbm zSfN`%rwUC2br*UaG(qSG&_*HNe?D$9=W-gTo6wb@K0;$bgM}7>#tMA~nkj^xJz6AG z7qmv`BG4y7cYwADJr4R)XdNhf3ZLb#Am8@f;+~Gw!;<*g!P^asf2SFUVG^^DFl>SJ^V$d@t0XqC|?M!Sr1%Y`XU2KhR-H@eDbILO!K z38QyF{+|8`1b5KLuP4@XOanot+eQJ{S4 z391TmbRDRn&@G@-<>+wGX+q;cZG^&FIeHAPlcZP)x)_v4JiGJ|;<31o(5E6nY5slF*BwWgws8RiRJd z)(HKm@h4TW+RacPipwxu=fQpby6S_qyp9X^7h0CX5pwB^h#PcdUf9Brt znR~})&aNPRA5!qSbxzMv%BL4Vz7HKdz8vY^`WCt1JsaE~p8nsxw|p7@*L%z7!uOUZ zzPCIb>E1f{?%65lyACb%htSuc147A47?mI5)(Gz?N8x?x@2zX(4mfzlkti zqy9oX3k?C`=L;+2t`MpTx=-j7(1Su}f*umO5Hv&RD$s19fuJXaMuVOeng;SKfEPf% zhrMdF(T@IPu1pmlmrwah4XHlJk2K+GGoRYPy@WD+JFgHbfLkkcwb4+}266X;J{Fo| zZmGrbthQZ_u7mUIgP)Dc938s)AfHPcBOW7ukhHx1`vnw@iJ2?pOOe8l%He7xb1u6D zM`I83RXBV;Z-_tpj-SQE(S~$C;z~-~JSE=--Yti#BuC!{`4-;-;&Nm31IUkD2jAzu zRtL|a-tm3z>A&CSzaeKo`<1I2QWFqgBS)8+JJM`*7mikyI?n+4{__8gIoY2%j~kwN z-0-vm|pV8$Ye?5kReA;P7uN(ac%9qxtgkK)v=bQnm!Y>1FmcO93@9L;n#iq1czAz^am)9!tpeZIwJ0X9K8a>cT_{V1{8mU zQw#?A^POb$tWgojx7CkErD}SYN4)mta{UvoFPGN`Ws!41x-Q~4=X^Q`#O3BwCs1`9 z^=q11LYKku`Y)eu0@a7hr$M0RAU_VB3BpfCVodZk<)`ho;)Ws4yHQ3nKo`or&(mliL5?>mx`8};N;x)f_yyo}BYkp6> z=J#}@@rBpzmq@$ty1lp1Gia;6Ab$mK5#qk<`ya1&hl=AnZj=MkcpIgym7~0(*eJ9cZo3e#@BMn?N2K^!T;*f2YXHipvqAA`Oy_|r zfPCLLUWj*C&H!bu^k?B-Xe;gkqc@Ef8HMY6{4_0c_G|CKM$vOXv{_1ub5Bo6%Pn-15Vz0-Azp{g z5jq+vRtoXz>wO{K4fsij*I}{g+*aoxt}^J*d%>^AZ@^K%Qs8HMzx#GSoIlGYO1Lj^ zl%Lu2DSUd*ljoU+$T^?BM4W$S-vMd@*N}b%oi0cD?rsV4Db52Gr0KY@$5sJdC@FaD z(*=Z|#D()U=m_dA?kY_Yt%CCDHn=Ns)IYKGlUzo@@tUh4JqQ{Imrv6{KEDM@`BVhD zLvr3^?rYFp;&y}X6DoOpm^Qr9j!uH}PnFSAThxcz;OHYr>uWVri0_KdxaN7p@ew-qo3PJp2h@YSa{f48C)`I-KA3aM(J$suS{Tsyd zM?RH50Y43ev<;~Sh-NS~R?6(B=fQE#evC*U&Yw|bAs!=60u`h$)3|)P0dzK8K8*nR zXJU(9`9;gK79@HZS}KKJ_WV?_06L^(8oxdN6kTB30(pDUWoV7cozasy>OgM zL3+B;OG=qDb`c!!hIUPVqLfekT;cEdFW@}wG0LtJx*A4}LBGivwKM7i3LfRN66qwwPa>Z9NyO7gLYHYQbYO>bR(Sa5rc$y zoikF1XNYk^ybm%__GPZ(7 zNPf|b7|oK=sP`-4?w7d3=gU0e@oy52`tk31As+vVgn0b>Qix}@T|#`N_k%L;DBj8E zHt3ozQO{FBx&p}GjmH_C0`gBvZ9u8V_>3;lqxsZTDRb4#rSUp8pRT}BKTqFoDZ+8X z_k*&?&+m-o3-KzbAqbyvAjMfi++$mT@`-mk+M4SEax_JAcC^IG_#D(-QiNAEpSD@t zPoQ3s_BW7!+Sw1{e&wH(uENngDuwmhji829{v`BMP-gp8gBuRlHGRBNKGg$_g!A+2 zy+WtJG5Hyr_uxCH&xPZ5?wr0#sR<1>y3c63(R`yfKl1{zO*J(-H3nQ zmut|3+8FgTy2)rbi1z@R&{z=f8Z@D&Kz?Lc3>q(I`G(OZ5Vz!!XZC0Xumf?AA}#h_ zPYzQYZ4~a?`t_-w<9U7RiJy-=@rcQkd1cN(E>9unuIV;L9gO(8`J;T@Jay8eSiOR{ zT^#iUao=c2SAlrH7%Ml>d^z7V$bT~MAjqe94&?6?9##Ar_&vB6kOH5W=+T1o-*doUaLc7uFM!qv6{+*H%SJeVzkM#m>y2-Pc%IoU z#AC519*aHksPc~-*87Ul{YjiP)dzgRLZH%M-N%(9KR%E!UyavvvzKH7p=lf76P(gYS z9AB+`d$!M~k#OB5Z8UoG6E0uNg7h>T^m{Xiz}n1zhQ@mj zp7{C06F+}=;^z-fygv2Bv%M$Y5%$C@JWssB^Tg{qPyCsUC%!VC_{#J}{V?Vrg(to; zS4sVNec&Ch4?Mk#+WWM>8y#~>=-Pn%XMr~wO#$5~`K<(v5c<|!sYaOn#GMLyTIe#+ zt3vmHe16Y?e7PSP$+HV;?`S_9@7JNfG!Cyq70^cHf}Lm(?{no-T@a7E1?grWf3@0z zK9;!7plw2zgLVkr0@^KfC&>4v@ap@K|6e&e0Y~#@hkdAu5RXH(gm@gP5Ayx5DJUBC ze6MREaXd7xHp_RhCR-;LfWqBCzUdfJ_mOyMx?IkMMkeE6{KGUaUU;8 ze`@r*QQ}m8)Md0^Ww;I~EuW49os2eh)C|OLF*rIK)Je{*sm|>F`Migm8}C+JCB*9z zPyC#6qvRJ|Bff68NiLm{A72mOYC|QCpWf4;g7nQe>hI8@p!?+LM9?ImnV=aUN6SEb z-SEVs1E{z;Sjcx5@-0&ypT4!v&tq}esy2S+{iGa96nPeVXFf8^76 zCH$-<$j>vYj5aG3q<>Y)rxTiD-6S>O`{+)zke>rui{nw=fBwrok;zf`CIWsxAkv;M zaom2q|Kis}UBz+xT_?n|#=Vx`3{XM(e5CdDJA5YFq|ljs8{geRmzw`ZIc8h|lN?A?|hG3303aDa5T->IqK4yM8AK$$D3a@1ur7d>@?- zav5()@qBdXr@qm<5Xoj?+lPClc~l+F@7>e}wMBl>-cl1f8IIR*O{j%YTcb`!JwUvA z_2Q7|`V{2fp4e~h=;mRH z7D_swE(8zt`Wep--@-uwyQ_sM(~uY)oMA#FkW6cD#* zLAt%sHAbV19y3~Q^qJ9Kql%}8`JHOi)o8HMgGLLD-ZJ{aXs=P(7GchHjLtGDP-;Tg zfOxO82@NtDuhfv{g1ARCq!&TF$J3DBG}>ge+oj`8mZ=FOa|U27>r0)lu|G zO*C$d(>T1*2%3YOqdgwK&+xpI#`C!+{`6smIDR77B*e4i4k2E%?g15~7ii7}={Ky5 zPn7(K`LCql>*0y>^YaX^mHj-!GqIm%c&*IyOdfG*J_WCfQ*+qyy11GUw~L>Nxz3(A zzXo!Y_eH(q^|L2lEBjeIuXZ@w7o(a%lp(RESr|o_fLg{6>Rr zk)ymmNDGDQgM#$)ILcS1AuR{__Zhg(*TCfw?|u3AOStw^BrQKf_@jItzNXwRGvz3^ zi!Ybkg=?C*bNsQE=0BBLC@HvS`!nL6^YS|Z^(IYA9@dzPYvPxTgoj!TpFHrjgIsTdohmseeZ1K(o3!akDdHn7OffZCLrSY zs+OZ0K)l;s&fNvtjB?AlNuV#7=&A51Emz8?_d&c1i_Z-}-$+gOnXB0rxx!&+Tk;7nqCccj*ya)b=-9+Nl<|z}%}x za8ZlDZ%3ovxy{_KmP@j2SkowN6?0MR)>nt$5Dwz?P(+R{hVyUJM{kwf3g_qe2_W7j zNz#0ddzfh{sCX`qVXuQYKS%F_cx~(GGbQ{54A3sbCFw5^=a(dE7v{`q_ed^BN}EC) zkBLdjH97|Lm*iJlbHQ(rHsaFu%h5C8f+smGyBr~|0gqhXl@Zq-apf(}yH1v0cTlB6 z;-VDSS=<1l$c-?^H8@6c83*S-cZohLdIXMlzw)~0IUUsGzt+n4q5od1 zwwCh+Mm>zKHoC>=PLMD6KBFl{vn`I#{0ymI)SI3>!qFqSMTl!9XUf=()R!9 z+&an8|Nr!HK97r~E^nePfzSr6m9NWxZs%_h=kJ-Y|KYEI9Of$0cW^zBb4B_Ubc4`- zbE)&N+asU$Y! z*CM~J>DQF-7XxtD`Z3}ixOZ^Wk0qZ7MR)fm9QC93RuI?a$Z=7AVVpnnEy(XbkMjI3 z3I3{x<($p7+$I*MjotAvk|kquw4}U(UI|qzI4t^ZoDhrBCsM<@}=2 zn?@gjqS?=AK6XLa4@w(V1Nq)r4|Ipb zwE+3PagotA7I(A74F~ytH6E0fv=4*E3e5rW&TNvR9{UX3LpbW|%s=~Zq&oYf{1bvl zI*R^`T;@s{tBgJZb&)lujOcRoOE`YJ!{^6O2}jQFd&Dh}6uXS}fp(y;;V;Qt7}Bwz zB@$Q9=roY;yXP8R4&u_1G{|VQ(PR+6`IDr_K&vIcd7yWMmV*5C2&s|$tlK6zx&cS$ z%6-mf`Kh?5w?{qaNO57`P11iF$9;ZLTyzHHU zK>m*6_TzVKl5}t{`Ojlm7?-5($c6hA?naOwHwGHDXZ$9$-5lO0uqyI?S6F|Oo zn}WC{{dxTF)?D1WG>>zZc9!cAjsiZv|31_49uNN34qDQWFVT2)I%?%dsdgYgO7#Hw zd*%j^PjQ#UjRW~JddT7ySlnx%gw*r{kiV8(=Nxh2jN{MaVE6xPREcu=966Vh{JsZO z650pyV?@?P;RtdRh~F2$-}1uPevHKN$|E0yzpI3!#|bsp6pnZ|-si%l)t3}eojYp^ zUwc0KKdH0N)cjVVp%D5iXT4)f+*I=}jHWhTf9OXUQowd#PlLOFUVr+tvmH#&M9+|)yk4uRu08~hcVE=Nbh%`rDi z9Jj8g**H2^+>4-Rgw}u-9FjIFHyY)S)q;NJ!(r<8n_Dgl~pGqgG z3TTz2I1%)QP*lG&;NBP41{C#OU;B&Te7jr*@-s&NYfpX*?His?ttkK$@xjNi8p@}E7GdYXy%`6~+XKL2qb z|3u|WyA&x-6&KAPH^H58h>Pa#G>-Z+ic*9}{k_0BA9?-_y zb^eKO9NayK`@hv{Dsu6&?gEgn%W{w}ccUHMW=DSk`MW#olJLqL4f1ua1M+EkchJ|g z37n@^AU{%H0OHzrP4@tCZ||D!Wpu640Hd_g7^6u>j~hL2w9M!=qc@G-HTuwKi_wwF z{nn2DZuFOt>lU6z%BY-Cb)(~rPBCg>)Y|AmqaH>#8QpF4h|xTw#YS%!eW28YJ_hlX zZbDm(zBc;KXs6L{Mt>RcpBePIWEqt*DsObOQB9+IMoo>*Hagd+qtQi1U5$De^)kB3 z=vt$GMtqHYo%tGhy48*jHR9{*kB%{SztI#UzWP3HhB@wa-aTn<#iq|3X?OU;F>Q3%d<*Ns3<@)-NL7C-gg- zrE%0#70?bzQPW&)5WnG)qz0f)LZ^ZFr*-m(*V+8eNs>Ci{Um8G0r6)=Ns8j?RS4fr z;dilpipvrAtEBj*eYnqm_?pn4<$qp<_eU;%1wYhi9LVph6dKI{{VAnAYxJre{Q#8v z4A=R4P<5f-L0qnXDykzcT8l>O>{!pR9~^1L$VdH}upHU+~n(A}1^cMIU| zJ;bes^P|8Apz(4vTn)FwZVBFetTeqB-dVyCyzQG|9uYH9%-ZbPh6Ci_QC6{Ce}+~q!>7%f&)!^~1OP0g$jGdk57<~hlEjhgFVUKF!I z%&62|Fw0=#v{}rE)L59+Vz!G}lRX87&muuPd_FV}<}EQ>Y7)K*={cD9V3O40ST-YQ zoyKI*95HL$S1=zTCPnkq?A4eYny;p0Z=Z7sTBxQv%vVT-$07MzlV}E03{#30OUylV z9?UN=rD>0tLh25)7p4rAuf@p<>3SIVEdRZhF=~dwq+swnkJOBYDFaiM=BRlTrUFbk z@?UEci(zWQlt0MSfx&PnUuzSu_r^PTFcm{n>Rp&-FqO<~f#Ft-%rA$<{A)4ur9Pvr z*Hxm$8RiVhnSX*FEt4IVRhf4Am{i^sct%7_W!k4PVd+&UejKM7ovMKt?wM66S4?qs zSf8V*lEyUBe2%7C8WYyAD%I1NR+>*$Y9?kaT?}&$N+!G`f?S;8QQdJUDSgGulp=lt- zxAWB!(=x-{22+b#*5-Uhrz#^ApI2>aCuT&dCd?QyT{PxIjj2ODHKwJ;)SW zq^e8(HReW`sbYp{%sm=&B8}3R85(mUO%zk?UWA#2RP|`Pn6nYD)C+`E;aOYL15Cd^%DCH79D!`P58J3ynFSTB|uW*NWMqW+qHOG27KV19K}(7uu(0 zotg5Dxjs`o#~pehE>&6Gt*ejg=RXc8EmGHnkUQ*Q}eNz$!e0{y8(gI^US-qQ>(ft7bu**XG&3_p&G}{)tGU|DdNni5 zY*(|`%sw?mX3979HQZsQo|*$@TC1scU0COyYU-OAsOEe#%<~VP=z> zrDk@jDK-;t=IfkweORBWYK}G2SWR0q9o1ZIhJRLyug)+t!_>?)Gg-|mX6CE;(##q) z|C-sN=GYs;8tzfk!A!~KzVtz6YN?rRhJUJxYglBai<+;^^iz}Q7nU_jO*Jz!)U-3R zSk3ij)~lIpX1kh~&FoXN-Awt@d<_#fhNah2Q`byuHJ#1$RC9}&fodk18LwuEnK^1U znpvUdJ2RWql({La&rUV<&BRalb-u_3o$@@M$7xWi06HPg+sRaQPbSa3N_u$Y*KTZnVo9Jo8h1N;=A%0GgZ~RW~Q;4O=dc(`NK>fHDv~bbsnas zo|(yNE;ch?%>XlN)QmT?Ma>d3d(`}3hR*>jnZ%zkx6S9(h5uTcC@~Pb5-=Bsrd0Us z(9KL4#N3OR$Q%vBE5^w1+U8(WA2H)4RmrnBtK#e^=kj8rr;=_|Lu10Sx>Ie9X^VWO zAfN8kSYyJ{dr%9F>5Ld&XZE0W8WYy%QtG5Jyw>8FOR1;Egf;9*eZ-8WYc!vplxoR! zE~Fb^rla)BsIQnwbT`agn9FI7nCMC23R)^AdYZU`*7%sz14#8eQe8po)jR^TSj=WM zPrxjPxstZ3c@gF{F+0Ufr8i)TV6LL_XA{1n(yykf8HUgFYHFNeK18Z(s9lEn9OioJ zmtnTS+(hXNvlC_jP0ldC!wjYs8HTUUaN3b!4#3<+xvioaCZFRcp9UGGG|VXKmSHNw z+)IPSjF1&|b(nF~`y9^4ud#WrU?MFRGddOamO@$~W<)CNErnF1G3(G`+%q4f4I1+W z47bmNv_)ff!SK`QB-*YqIfL-#B}$(}yELX24CgbM_G(N+7|v%h#aqjn(wQ*zk!lK! z5mQK)!ZZ?thcbLkC4ORU4l|W{s~L!xvtS;g{%Y=2^9ZHI6w-K@wuqTdozCTa3h7ap zi(qEbU^NS1E`fQB=7^a}uflLyvuL%(Y=Y?tGn@8_DRw`=Tm|zuwQI{cd(#)@398qQ z&D7K$#M}fkhdQdE+q`*_x~VCxW*!YwQ$@`)G+E6_YUa~oH7(RUPwT}*dol~DWqU4t zG+l%kZuy18@3XVH+RS1#x0+d@W+V*nVlAXSV*HccZ8(dC)TIOG6Fu3zK%>-z`x1+2 ztC&KXfP97_)gtQCky91Y<1k}k7Sm>LY?tO`+IN`geLkm}N-rSQM5KC|{}*3tOY!c? z<1kCTfLV(eKGVp&f0zk(Wi2N0HDaEXeELaQ(U`E524FnP?T&xR7PkVymcj zhT#@lMU68Ix7e%HQcN_Xt)@O=rc!g9*J{M9rk!F6>0Fq%VP2!9ow%$*>J0M%%DR-YwE%yOhf+Pg83A)M%qD85W*W?3n2)HpnguZT z!F)`k)U1Jd80HgNtmadgr(rhJdNq4tmce{VyVM*t1g!=08MU~~m)->CQ<%?bu$oRV z-@t64iE8@7{08#{Emo6;2^Qj~tf^Tq$;U>3uV{>z(W!?K!}s!6G*QiCFnp(cMKjd! z{KYX_X^tA6zc^+qEmRZk&V5Zw)r7lqU(*^j;qKfwv|dfPJNFH3_QrOow$WCNc~Wb* zjdrM6qBYz`d(`k<$u-9p3Dqg@@$)JuMY8GSvt%JX3#9Mcz_g}5Obr%G!Rpq9p>{J)w|KB3iJ7m z`rpK+kcyG&E~NU6CX1Px+6VIh%oX_1@oUQ)Y~$ir*|{x$sQ?q%mSf zQ1(#Fu!z}9wfb|+)Kq1dg)o0nM=^zT9L&pNI;lAa=2e)#shgVaFl%A{q26k4hItoe zAB|CS56l*r{WM36pUro`9H8xLc%1(g#<@y(R?O^~fva(tsV&Cm^A}PDZj&0$C-wq9 zd32l$>Q*Yem-%mGd&74we`*$)>8K&!DUq2AlY@LB^TJ_f1x(q4Vu}tkpB!e2VR&s5 z}^4l+w%__;DVi}f%E zx5n3pnLiFQ!JUVuDt(yAKg=|OIr!?dJj|Sbn7I_@;8q=Qn7RKjGxIRB1g6PBt??EN zk8DwEY=t@aD*tj=K5qD-^{IN8X$EuJL1lG2Ear~GVx}EtmKQ{?K3BsWob!mo%#_2-ESL)pD(iWe?wKa>Cd`!wnfG9L zt!`JxR^5^L7BM#*lTe>0pxSM zTca`I+K~+$nzb z!nv}JYpo`nE9{rH55qI#iLRsMJWBRA7K+)6Jp$h8Y>AkcVd}Ylc-}@?_EyzN zZlIWA7tVtxx$$BOsS8pqN2>a6vKaqV!guA#ZefOb3o#Act_;JapW=AdM6Z*l+D~8_ z$@59LOZ)}Qsg9p~xIW>zH+3bkZe|mndsDYh%v8Ds_1S?`&0S-xFF7V`)ze%vth(?U z{U~gW(_C6j*kY%-A~7RqH1hcc`JCn&VBN#danoV;iYZd_EDY}>o#s;L>-^NV48|?O zE{y9dW(0i#!|SLPZoZlwW_F30ME}C1kgA1im*#vXQOS}1UB@%s7%_!(6ihB+&ThOfn? zX7<61L`-CIM`4HRAX5v5SEoJ0REcw7CL$&>7sBw{s*&lT<}$Zg>Jz=q+bhh+>eI_{ zpTKi&sxMMaN6x+6J~abiW{F9mt~_ppb-vu?stN0Sd6=`+uy<%mg=f**)ss|fU0CPd zuCbc1&R2w~62npYlPLX4Gxxwe2h+z)A$+tazPtOn{u$;qq`KaX z&oJ-6^mBW~tWAa2>t>fi8QeaPcZ-;+V%Da@Gwttch#8eyga2+r zOn+Bf%!t&7`0w{(8jD#=;WfR*by5?y&n>RwJzUmW3ft#a7auLgg{?8b^;8qK#vnIQ zP1uIFxmjwyK?%E1`d~L^4CgbFIG=xD?r@#%^M>;&`652kbxYN74XeZ4<#uJ5rZ9KA zE@L^>2-=I7_An#ePB9}XhProyxyO|s$1!ij%E4R#Gumwzv&L12=?inOD?grNytx@> zjB72XkWN9&Z7^fqL@^_%Jq*W8aBDQ?GMM2o54iPeZi2Z7W}<63fpadTyI>~66uLfY zCc?~sdC>J!^8^gPM?J~<*vl}_B4)B1EHVC5559sAxfx>op4BUedDzVpGl|wB)vIFY zelC42h1c{EH%Ck%Z9)ux`Zmoi)|l{mO?P|66uYqWM_soEIG@q96{&bUoaOq789~J` zyzZUlc8Xa;VXMw|`!Wo-*yFC`M9yat{eo2dmf##$FT<=u%p5ma%m~_tm@P0*y1s>+ zDjHYkxyjy83_Xj_eV&^q#@~a*Vm5h`%0tX=FweRzVn(Enf#Ff>S+_%D>cISknE7s( z#x#RDAZDM&w1r77#wg(84|07*qI?B3gk5hjQLCv(+FnZK{4%Nh96|EJS=9&VfpaR_QZoy-H#aF zuZv25=`i!@Vdfv0gG;Y~vGZUP&Yn@u;p`ci`bfoVfH%T(Pc(($xhXQd>Yj!=ziBaT z5yPWsk(sVA+{52Cb0rM-sC8y;gqe$&cgzfdSqSs4nNe!qGsAD5zl50g4>BuYHkz5N zIe%#8Q5Y`$BQwv#@H_1vn^^)=gnTxed0ow?X5N8$4>6ya`CQHCW{TBpakD0KKlH2j zkC5sMw^WQ@AN~aMl`B%i>%%``zIL0%M0deAEQ!4GR`(8I#4et$}fg1kh(qcxWjzUZuG2=Cc_XauUZ#P+Ej@6jI z-7GQ1+2P*cKW?6;s*f1nv--y^)|hZ_aGzVDG2!0eK3AkM;ojhW$A6!k$ISMc&wjT> zW5T_`f8BPC3HJv7b-Oes+#5XL_G-)^ysU<*O+i`FeligG2z}|POw!C?+x;~ zmk4&K;k`jV_Y%P#HM}>-F(re2YR=V|l0oVb!q?~&?+tQHsUTMk?+tQ3rGlzrinGJL z!O}r3jS2S#O9u@!Cfpk=6ExG9aBr|o&{|`{y|~<gBcn# z3Z_uZJTb-D;SOGfV4=o@J9rfW{;S6rakHl&706}j?twuh)TCNl{nBmQOm>Fn|NHX~#Yjpu@z!3Hs-X%J$*fjKVN zJey-i({PyYVU7=4JnmDChxr|*cCc8@G?@J`bp!re51cd4@9~$hUl+`o!={ktB8GS8 zP7YFY#iU+>DT$Z{K`n3S9hmZBma5qdQv;?^Q1wZl&o-F4FinEKYJP%g1k*IwswR%7 zx6@#n1-Vc8RQWLcGpfylN^0uC@Xpd{!NY1UQgeE+N6lb0ErMF}d_Mf_*b+IP5p+>A z4~9!WGw82owWc~N*rMh$7%shKuwBicYR(RJswt0WKH2FC%0KPPY6{a9IiC~MQqu$G ze3;fjJvDd2bc1OVG*&YK<_ef|gBEHYgSj52ZO~553ot`q+680Oyasa@O#5J>nvY=k zx$?Y#YlCN^QpGTQe|0dk2Zq;dkqPj`$$RDw|iI}GlbGg;!_pkR}jLMbZ`=C**J zqdDiHh^Yc|d%#bHY$n6x!we33s+k9KBFr7Z!)o4w;Zb5(c&4Q`!<>qkJI#Ct!+k6= zf5LFjj7%JR52s724U+Rz%7r;w%vLdz=opxDVTK31KIiMsdl(nP+#L)P6Rq$^1*^sQ zt9%7wMg@B^Oh1^>!C?%j1GgYQpE`aY2{Yd`$SfJTBPqCYwTf3^lwPH9^j5 z_}wrz6M`CReasTX+z<0W&`Hf_FtcGE3O|zsqv*7tlE#E@DNhe-Xv_(>`Gl}j+S^H4dQ{lLgco-&*{{M6^Lt^}nYYCWV zf;nRF2{>Z%#4J_wDoiyoYs9Q|VLs0WmEPfVk9y1epoW-e+?XG9@rHP3pdRv>AM_Lx zJySmy^zr#v8$K8GSHsWLJhD9>3|7O>)Z9ML2cyIkXNS+!3xe?)Q!4Fa76cEg;b&^j zXJIf)4L?(JJ`02SYWSI&V_part2tIC>G2vSl%Y)5o<{;-Y zQJ)pT)-VR9wV0inPxyYq%3zPitU(OFle{v(OZE6F&JK4fRs|_Bqf_0|e)Y8~D6cW$ zPQ|N1RgD>>F|P)-H74AtSRFLbm-gkc{-wy+OjXcoed88Y#SZN!FdOTtX4C1i%pLdX`2 zg-lwAWiqj0BWV^w2;uvBU)TFO_r1^8Kqk1aZ zL-ib^dMesOO&p_oD%OQsIA(Z#}mZRQx&Q}K4Fmt#~<#oM7l zj!``o?}UaqM)g#@6Efb{@L!otJr%UZzb@@op%Yg?cI|rZbevLOm4}(-}%< zp`Hqgc`r19g?cI|=Dkpskgblor+zv~VN#L(2Sq$Y%Kl zLYdzWB?*bm(peu$;h15LSszN{7&ULFD>R;CX#9clbcHfGM$P2;AT*g{#vtZ&94Q}! zayUlK+u0Dx`jJh@tzG4@1QqqvrN>he|l+d~Q#7Xd%a_ z**+hIsyXIrj`=9Glw;KVpN*k5(bPiQm8s98gwgnBuqm-BoQ8sr!?kLc6TFvl3xvaU}WkYND&-i}7q>N3!7-{Y z?#oau$Ed!zFGI_OY;~SSW|~XSd|sW4SLhC?sk-i)G9=p<$NYAhhPc z2^kxx&Cy`gwLg?BWPv>fF&AR5>kp-}90IviNIJ_gkOIiI&;*uj$hAVUSk8mYg?t;D z&QbumSxAA9t)>irYgaLSl2?wudTYOfctdduS<(n)ih^ zv#5Dr+e57~PcT>QyHJM^ymbogDZ$$KE|mX~*cRFVwGi@ss8mR$_7`Lk4j^C{;-83ig*!nvke=C`wV!$uFTPLMpWrAul4& zuc2I)3n6Wg-$E@cG|IFIG92n;xfikx@_T5Dkk~uH{s{F8i9LP)M`(y+)XbSbLn9oc z#=HLv**&xrW%iTEO#8-2D3Rqw2%Qr~LMbe59P?LbEXziY`71P@rJrN|4rQ?X$}xY3 zCbJk1$Rp*S&=i(kAatbs6Ut*5%`uvu&vG!wXnG+_287z2pwD5+fl!+h^b(c=2t8jA z(#u)qL+H0Dq*qH3Bl1+ct~bUBjg}gEXPo@rjJXc_K%C$)TMg6HKaqO^J)>$ve7#jj z>`COE^g)($E|tr^v!3`Vm5RNqYG*xJNSRiF7VeBZJL{!FVs}70>kDN}FpFSkeW?_C z>|FGVic<9~^dvj=KkuwJvCv4wUWiH5TUcl$Vt*kWQZ&`mzKh-;C*u*bt8QGHd^kyLDLda;nUWj}X!VJhhdLzfI zL<~u)KJ+=2k}+39#^`BZ$UN^MW)5UuJ)ea}4(CDk)2mr%oUj}+R_|wVYSBvp*meQ9a37dM@XA4tdT- zp2>PoKh@snT#TdX6db3g=tp3jyy^g2q3t5(;6us&3G`&NJY!BVJoUV7r z$#m2;MX&x=W>!7+XXl9Jyl=&>Zg+=w@=jd%h%ItR# zLsu6$dMC?A5IQ5}=shen3P&+h^(`zk=0-77^#LL1heYjkK0I3=Vo_rOXX|)K7XOvm zGzv#C({!6dh?QbE)Zir;r*g^e}p?U%^wmde-+;O7EM{lA#aFM69y@>oM}m8C0FZ(LaM}*$nPNLYQ0#-Xme1iOGp#TEs(8{|LQ|5 zcR}d6?CbQTA1QNGt6;fauNBglupB~l&DI-*#C`{7>w{86PaxGkTOSr8*E1a#vvvC? z$}Htu#LUr)q}b{i`5W|NAyIqvP4Z~EK`#{&8&kMZU&t}^megO6=SH3K(#*zZ(ZYWq z#euv8vDV;$11 zW%uZ{yrd}*+R6*{r7TB7X#FhIdwEIe?uM?_@6`u*N$Ku}o`JeoALbbKJmVrA5253~ z*qo(By2qmWSMSr4SXBS&eR`^pSpRB;p2jhLiEK}WK7mE`uU6`rEW2||rJl{A`d9DQ z^I7)enEUlY7V0~tWv|lbuu$JAEqj$-!a{wg6jQC2vz*E?)p|7x&3&Sn#d;mfp&YYV zuNM;Q39Qi@IVKM=bY88|TR2Ab1U{g*am-wfc|h;t7}XP4tM_oseOy^(2l_cfJqnsT`wv3+wbWj%ndM zb@~L3QN4wa=$RbzHpe`oXLF3|2Uw!#aE$5)SfWqo7}XE(s6LZpR6oF@dJzltI?}dS zsu#0t;JTLTr7YCzNHLG;3t7J5n8);57V34Rn8)>{EF&EAxZc1*y^a*~gx#p=S)|2^nU&o9k-O>wcAOUe0wj=*w7IA$OqEvw8!|2FQIv znpt`|&vSY|%io;mIen0Y=0`n*QqSwgZ?Z0$AN7QgL?N-Bibg%1CHWC~&DN-Avm6)5 zOqNRnDPd7PCyjb5i|RRP)bZ*J{I}J)Gbok9vMP`amM;RyWeF`&?I~i}Kahni^aV~D z8EMq(SyWGQqu$Rl`ffSG+o-4gF57crAO$RHc2=XF^@ofpMrL}$1J=)>4vP-{Y18L@TxAd7k`YMnkLq|Dxm%;QnlIz8=g4gZyC-$ST>Y@MFR@*89wwqS=|%3?h( zHVjyr)+P*_v=9V(6*5_w+`VJP6hP zp5DwdpXGhMmE{Qt%?Mhr4+tsK-h~{CKUJIy_7{g z$+b~$Wl>LZ_2?-Hlqa@7f1*zoQmK83%+rwh6MYKHFOVxCp9WjdeL_B6wJDH2ST^q< z^kmRyfgHjyU+f?hvqg!oo@@Iu5cOPJOtO%Np6K~1h*3{T#6&$M5fk-YkKUk^`mRSz za*>(tE58n6E`iYXPfTV(sP?`fMm@n7liLt;E!J2}s#(4XN;RTC}B~{o@lhN zsCBrDG0dXY;jYF6J$4iUcDvZ$kAUtKi@#8RabM8$J6O)lxLt z>*%^}tkE1NZ(<$pZ)_IQoIJJR2cL8~GOHDHXB>a*UA?ma+sfbnZRYXcZFGo@F`Cm~P9Mmsv86WkSj{+Mj7D zjyF2ugqC8WF%T!;K(dTPM=k}GqUActC=nv_Q0=D}OXK7R#AF+-aiwV4PcxET%F~wc z5x3_IW2q24mC16Z;YDQ3PmteG>MSFT z$oFG0wCtA~jY7)oT+~Hp;{v0ZZ;Ucqts%^1IEB8lBjkI znv4FZQO@!zEhH#V~jLWUtv7y~Q`D=^CN8t!}zyvRtjc{j-3kmW`S%Q#2|KVF5qm5(KGjxqc7sp(Vx@cbI z3q}viJP6IJe8K2tQL{r=8Uvh%-j+#UUR!Alb4(>l(Gj=G(2~Wru%Cs{5x2_lgp}ED zb4-(w#4%rSOp}o+BsOCBqLIx)PfF4V=1WEe%Q=_I{pTekWe=)LK3`da_4AT}Kk;9C z@HUCnO3c4d*PV!2ZRCkka;@GAX*RlqM72mgdcq(t8w>XonH}{m$~8tUi+ZQ!8e=Jo zdV}Q}qh5%d1N;c`tTDQT#IBat7+Zw2X`@ls6C$QRF6Jpni;=jOT(0iNf0kEDea+1bNr!6%y52A(N2#J;U2alnQ$O-#3zl#MbKjMw$@0Hoiih^=e!AUm(x6 z9ptC~Cy}TAHzxW2$+-WKHedB@uQzgJ?b=a@`CimjEu>U@MVp>hUT-u=2}YBIgi>9`fDqa9PiNyUBP*4bT`m{JY%sEgl-ZY~)E`2!#!yU|eH(<%bsLNlmc@ZI z2#NLTZ!nr=jA)PXI+YsWQtBA&HilSe7Thj~={81$#Cq(zjnsWb3$>R}*X!6`-9`~h zD`YQ}`p763QmMTQISBHxQOhx#AxA=bj3Jh7kYgdA7zO*u%)dka1NqdbXVG!2pAOk% zG{?zQ$Y!I1#Y4;mkk1Xg@{87?wkKo;WQ&n2q*B|DW40P4EQdh~5c8F>jAatX^cs`* zr#zL~8IbD{(`QU!p{H$bgnVN(2&vKj3n_v08v`s+NIB$NBlQ5;p8Fy9LAD#?S?G(7 z4@15)dW6Kb*AHrcHeW@|GQ|8~WFAO)N}W@*~EF&or`0(a2|E%;+P#QzhRlSW7%D^ zi)AMqB|i$;%z5^Q3_~L3fRMI?2@t)NBz+vMu{QB8WV$x?%sB^<;2fTgzYyb_GY^)M zg;IM!63sa*Qy~XHb}{n~m3h2;d4<25ImnU$=|kp!nI(tGn0+8$Kz271$CLCWsO_F? zE)`OykxXhuedgfd6cg3xDXj}3DQ5Z+B=~v)YA=B7ZT7R=47nMSY7VkgLu~vTV-B+{ zW7*flYr&8wsx?9GL8<-BVj-1U8{}!oShJL+8}c$_f3t$+YY2Ti?*OxwUQHVBdX^OuMjXLbpZ`}41m@uoe2+EZpvKq>DHT;G_9EZLAocT$z%tz|KZc}I>xV2x%tdB1%de0+$i?Qckf@j~M)xU~n90Y= zQi;#uo&YiVW-H5B$V-q*%^o39Z4%^7$P9CsV{#yG3&}YyLDYT?$S;s9%pxhlHQSXYlHk8~a}8qXUN0scELR0FpF!w|yE>3TmO`^aWR~lYVy-dk z-d8$Xs(oNbId;b4|Pk5H0LWP_@rBJr-5_4Q4WnT8bOZ zTp{h+tvI@Axo$GIu&AExdFJHfDRY@N8nx41?JZ`$kl5SLZZnI7lxjyKhDHZ&Gpjk~ zEM%tNo!iXKEb4t|C1%G&*=Ds4qh_y=w%}>61!nRjig`1n-al4qwg`!8C!?-$SQ~ek zJwg^}=R)XN#XHPpSyW2iFVlLy!}Ly&LU+C-W#$|qmD*J(btLlKX*O~U-9cwT%FWCZ zWhr$BeYZLJBq_I{6x}c1W2R4*QURf><%MPu%Mu8UXxwWyvpmPL$SnAeET!IucAr_t zvK}!sGE!*{2*LOlr0xwom1z!g4Bd&Jh89+t-pQ1wQltCs^C8t{63g!>RRCFRrm^_A z!o3kvV`i|7fs{fXFtb_y4XK3GnmH_|Lg+dA2hBW|>mW-J^N^V@B(^_4Y!1Z>!6h-rYfthrAz1I`W(b=?WwVavo&E z4sr#gJCLgpGZ(TkkUJrzke)!6K<kdN9YmhHZ`)rcF1hobG%v2V&z52}z7PU>cn>j3M3w~!7u&6CK zX!f$GE%=jJmn-X1+iS?&d@e~;%R+lTM0EtaWe((L$gr8tvIt_g zVgE3v3+YQx+x-u-AWmqzkC@r#%i1Z1w(>vbW)`)T6RgAwWQ^L!bSsC2O3~h8S`{p6 zPq3}w3uP%1+7nzW>mu3aWoY3jw9vCME|&5FWHiLL8d%yPbj0mswXkf2j6=*$R^}zL z)K4rsTP-X;&ia{%Nwo6wWy}GPQy{xo!z{-_&V}r1c{5~84ump~vW6~`QoyB>tT{8K z%xBrnO1)f4HRt)4)ynb|-I{ZSjA>&@w$iSY@+D+8V)n3Fg_LRfi#Q4(ds^uQ z6w{ZWj+7LunMEBbqpf}xb)=+P`B%wO>PXqwDiTtu(L2?uk$J4u!!hdEIncrjU`1W( z*h#a}Sk$pI&dOj>$Idt_TL|93gUqyb##vn~If3+EOPMRR0?0DdKF%6sDTO=_ImjAj zc^J|JIoQIhWkspgkakGAWwU$=Sr0kH@>qU{Y=ssx8irmVOB27(U4JZ;abX? z&T=lx;nqx+xscI_Il?Mrp?9T>gB)qimlFJfOt3nH$ln6$%bj3t=2ELr>KK&zw>8Z2 zI^-lFDMhqgvIj2*a+Eb$NbDE?C~K(@IV1X9#2jta$HiPIq&rUNcPGQ@jguLOImQ}{ zldB-dS$Wq{?Pd0d)E<R7la1(0_j& z>Y8X3UN7nrZ$_cICRy`^v?tKJQYdDURXRs*<%y7DNS2j7S4s|qewRDi^?__*xg(GXH^?Pj63Aqh#z1meUJqm@3%w6R)>SSf_GX3?tqLKs=l@o;`9y0e z$EfjwldNtQ_3gZqEaOJGT=Yf?`d;K@Yq}8m{mMI#=Ra0qoGgT#Y<05ygi<8g*5;ci zPmN~cDws;0W(~v%#hh;C&69P}8z5>RXIQ24r5plz6mphTe~Xl(AkRQ@tX>v+7N2CQ zm3XU+naXmuRmd_ELYb#o{VcbzoMYjo$KuFc456oW&a;wPo`TRe&9hQi*0P*$jb+&c zp)te@tPGYRmJ6*ZQi5Y}x}}v;UGmtOg1vXT<;6)OT6nQFUyALcEhi&phE>W!&umFq z$g($tw)+gLnk7AuI+o)gv_H?V8l-3oF2l17sB4Bbo%*EMa|$U zvAS8*e66U}9~VROwQjeP?h~w5AU~1S=3&4zcs|7&SO=U zSUp%j^!4X+kf+L;d=J$g+on}kZk*6It+rZ)#NHdf*y>^#TPlyQ#a6eF1-9z>Uu^Zt znBWcYi>)CR)#I_)G8T$07`y?##!3)S~U{Ny(AGRh7!5&_TGYyuj z&RTXaWv&vf4OiE@J4M*<=l} zlt8{g<`=E3N}1<=$aj#{RtXDz?Se|ZVpX#=aGo_*2g_>6FDTVwW!*3H&=WuAI^1bm zokFU#Pap}{?r&P@RTLw8A!xQ(o0Tm@?k&5Z)LT}ekT!e!#d1$rXBDxik)w6i93iE_ zzOl}lFC=QKQK5BKIm>p`z9%wwSQRW9<_I4mq)teirp8m=wpv-fxCG;nI4a+1}m2dz4}O&zuD zKUzI3YT19Xk`{~Y?x=P6vz5c5*5Qz~kVP%~FIESOTJ~QpqekXY>+m-#i$$%&VQan+ zc@&(2edBkl=m9Df`;Nw6RVo`gz5l&%Id$nBggLLVe~|xF}9&Z?VJmaYFls z7oPASwI?>ya_8`7A+ed3yM(=mDW+7r3?-;veb?|ZA#DjIETh7ikIFocKrY7GND6OR zDy0c>BP2PT{TNAKg4z%F4Ce_c(`cWkrPwQ694Bi_u}@ampiwmSRje zTS!!U4}Vb$$A*_Km$lPOOzPR*KfIacAF2!OIUt<>B*iSy=xCza4-A*a39ZAlaJ`Ve z1d5^K>!7f=Le{R1!9&AELNJy@G01#GIQ^+u47Kp+aEXvo5koCJHoR0wRNEWJI>j6p zZe+=T(9F+?;ZByRER(|hELX8)g^j1>k}hC5A)Lyh-YsF|Pt z33my>I5lEkM9j(I(L4Kg)c7bh1(&JK6R zNg*URJQ63jLe33Oc$Su{QcFReMUeBt*(~EA4?*(6rE#(XazVI(gd3y;U;4N-+$2S#`8ch}d}-Kzo=Qcviy-ep zt_U{>!CeXD6Uf!!5g}#TT@ZR=_nL54qb&6ROHsI0NTv2XO%ZQR6Z-;iT0hvC*8G@OTzAno|>Q6cW|WMw@BuuO>Xe zas`CO<7&c2Gv$%vJ9KV&AY3P;Qo98)Gtt7@a0AOj5PD1JgW)DFwHk5@ds)`yzJVN&N5a`G2SMoUw?wT~|38rBha@5l(z1L8J9k0-Jpy?RrIv^12$5Tm?)6rL z=L?C=*IJ=kXg-Hh?I^V(TrOf{PawUkp*~z8GmE=F%2OY%71EcWO4Wz!{wMQOL1vn* zu^yRYLT?8C1oBL{KCZ4WAr0Y1&a7e@!b2=7&$HnvYZ9K=F+Xw43*lOhan{P17sAVAj943VZdn<| z=XKB?*>lp5_B4evg~a+vUJMrsku9XV(HFz>2W7s?;upnD1b32&cbJQls4rNrJo)9u(4-px#&T zX1J(TmbxD?G|t!_?hz8zmf)}bP^u$5{tb%3vmTIxA@77|3c)w99+q!8crV<{F*FK# z6k^tg2RKH(_h3Ufcdg9x9%4>HOn10eNMC|_6T-*g`14)+S_O9<8B_ZH{yZQ<$;SxUVvVS70F zZCT2|h)@wq4TKw6=xs=J6#NiwV@X8JZHW0Hoc=DA!iWasF36AJ$t;Jk{2VT2$%0fN z=9h3NL1^Bd<+TMr7SdpN21$xEPp^~*$ul@h&)Stju^x45E9#?47-a( z?HgfxGmF|29J`-IZTE zDJ%(>$g|csJ5|VnU^eeKJB{V5r{!$karOi*rTSYAvZn}%+BEC+aJ2a#JC`LF>o8MD zzK}BeG6kWyQHz3vdZg7aLAWv939A7a-D ziOsM*)E;C}{Wgc$<3FVJ)24a(@}B5$dooJ~gl5kkVHZeAcnq~?V=0cbyI9_WoD2E4 zJ-=J#*$1V*K&hkc1|hK-wi$NnMzJl#d|GT`UAwDI9a6o!=vuVs#*!h16(oK&U71M0hPYDn{;1RJ<~X?v zF{j&IQtV`uqTl5+>>eRe`v3^7pEK-Uj+p?V8JlO?100hLq0DF6XdeD+H|hH4K4d=2 z9u_h3Sf+FDG+WygTMBySXqxQ_iS=IQ+KDXcXv(#dSkzIOYbQ$y_SIZ_Jd4^_&#`k^ z)P8ubohJm(V4}^BqJ`%LZN3RY&wAtqvJgV=Kaa_ykkyd$gBa?KrgtP>VE4<+;#~I{ zVlK3e&9NnY3o_kKjgt={7uy*^Qe^kAT*oo9?JX>KL#7~Rj@{4l z1Y{ayu06=o0+|lE!8X2?b$!5cqn*ss$5L#kv-}CU45e_V0|AXIyaUCOeFC2C_OMSEr_-HoSpgqBqv^Qcl?|^Uy4R! zTcgmPM%(yF)Ftk5Nmkmmzmk-RJJCl`s>yC;`4&q-@}iyjn~ZVhNqNbx7lJP-BW6G3 zS#1vv%gp*a_zi@-Y?uBnWe=7$_LM)Rq(gEM^Qzs*ax&x^$m@15%O#L`kT>k?5m{;$ zgq~hpYuEivQmNelq0zTCyMg6CmbdH{mU@CpLSv;Im zsjheI0haw)-nEBWCbD$e#y_%!=drwJdo1+L4a)Proy0=FoFwb*RF)@Ly6kk8S0OJU z<^y{?%f~Dm>+ zHoH|ykoj9172&^ja|N<~OaB6)rTh{9wg=LRzy83#fk5az754iWYuiC~fqWl`n$_?_ zAf3oTUv2oo?v88EzC!xrgudGFqn(yOYojkgwQ$JJV^J;q#V!`op3sBL87TFuUCuG8 zg}>RYaY8K|wuf2Na{X>+g=Af-g@4#NENUtKu=9oB%@wHY1myX{E@k-+a+;88A+fv2 zKka24L+zP{m_O}go$9K@;fUUG{2Q@rg~;uG1>`S#K#2Ta?QM_*XRIMxsM-^D@`c3a ziiDj48RO6zr6-!h&K#B_AW!07*qP5lOVA*sL`nj!jg>-bkty3l zLTlA^<_n2xXW}o)9&vhDF2`SQ3(+hp71eIUU!UNg=M=L<@z=Nb=Q}Md)Sf>fJ2`X0 zvebR}%UX}QyH2%`O6?&k0olcAW1;q>Kt?$|T#A;PzOu2KGv1~=mD&psdaibNr z+5dfjt+-G*xx+=(CHl(4ApZjW=y zS=4v84{{c=%s?Kx|2o*IU{T-QPIszV)OWWJb!u7Ecef97>R8lwx5qn6Y138nx3mwU2TdS=O=~?KHEfZ)#^att^`mLtoiG#_3>D-_$0)_=m+LsE zo8>tO^+;zrJwjr4z7w5ZAvM|w*fRIv-$W-lLfgGYyAXfX;@=5Q3CleEwF3VpJA*8D z;jicM?_?*{lY7hK_^So~PI1y$*0N+f<5@OB=<5Ac2NmJJc9Y&?PyI-zIT<2G?yJ-v zewvdVCvT$U=}w`L*nF)socSzjzSbE|i4;xE#X7@j@u`LF!Cb5}9eXE|*j%i$oD3E< z7i+4M%cACDO>^c8i9P$4>of~NUkX~+iGR6Hhmfd7-)wmnOL49(w8s~dFZK@8BY1Ga*fd(yx)*{rZdDs zbMO)ncZD-$6vf2qx-zIsy_q2<3s8c3MFKIaAoLacm@I|Rae9@?W4;8TakN>1v~$eW zfo$ZM!a!*Jf<^cSo z?MVJQIEb54hI#Yy1gYVQtomL?WZ1tU*sM9H9w8xQo9G2^L$Jkvi*CTJBFBekkWU$aX z*pGpfIk`e&N8FuGfsnq016$=-_MJ{qvRE6z8yfF+viFptj^%~U=Dnn-V|kI&kRs(! z*nuXx#hWrPz#M#0z-$BlTJm&PX`~jgO{BdWHC9*-v6OOi@tSbdF9Wl$CBq8z` zoB>(x6vxRd$O@-Zi2S9V19{3B5E7g5+u-DkrOdIbxCSRrNMCRj*Wk=#QCD#dPN9&f zeF)kUMdk*lm}As+-LuYoDZzE!vrcuK&~@E&&LE4ru6y3e+F!0`bzRr!R0zTQcKCYi z1!q`D>?(JalXw7?l2;(*XwNDqSxD?EWtEdEBz8r#%1M(FToJ8u(uKr+J)4}2|0&hv zWC_n53^FMiBa*Ab4aKE!UIFnHS>LRpowNoKtVn{;^HoE{k?TKFj1^O`esAgx2W zM?DRB-ANlq5<4o_IweB-64Vj5))`nG5;eDHkI5 zjWWm&POFdw_LqpE>$)GE4wm6Sx`f2`@E@HmQi2)vKRSaPqkd(6bhN|e)=_)ZkIn=Z zwb%XZ%xhg5!O}NsSYF7H`Cvz@=uQR0Y=IU(RGH+I5f`A&o++wE2)a z$lp$PV14>o+|BW$TN@xcm4#qx4aE`LCA!EOZf`Y0@2+PAypd5n-J5@ zI!ea;2BBxt9k-Ar@)3?L#JFyQkSdL26U1{TXHY45ru`D)yTw8<^8qpaLP}Xqgiz+4 z+zOWIEIYfkLdtBqVx;TCM0Y6*y-$y}PNLhuGO+^tATsabHnH4*JoII~UEEe7^7?td zCd`&|6OW-5#;#6Bxyf-tnMb(@z<=#N)lSb_CAnio4A10gB&lb4Wrn8x1o@Xc9G8dg ze0O)XW5wFg=>Am4S!s7Sg@x`16Cr!L8F4ZivbWnJq)MZ^QHmMkF3hAnRoWd`F8WO! z>uzSLWjVkdh!d(S&CNVsmRgROV;~2+r7SN)PJ|rlb_l`y!y)v$GTu#_DBDaid5|OB zVwR7%)KTshAywL+9FyT@Pm*eVoq@vvgAM>gJiq4EHfZ$Ag8(YEOQ|5 zK~8rYg~YC(r?>+w>iYQ%H|+#!PoJZ%pU-q>vZ(9lv)rXD>Y1z@w_Zr8U52`7kIHeI zgtRB9r?jTJBXL4cY2~^-CsJK)2~VKZ7ii&mZa)iM8`Cv^o;xfgw)bA(PCrSmG5VFE zuMu6~=CjbR41JAgx;s-yjYiMH^da-bZbh8@3b`!U=U+wb^u2(q0(l!k*BdeU8ba3_ zftU#&^BtKgW$w;$wGyA!p@TLR214tQRmgT=nOuEwB2QfuE$ia~7 z0yzt%CPHQhG97X{WKJM-4S6Xx#cgEdB(Z*}X0)M$62g$q#XR=11gNk}>5Hh1jFa=GYT4fM3~?QR206JqW|%mO$4 z6pE>`Z=H!{$GNW5ZDgVQ!7GI{vn+wo+r&!UR+iV1=X%H;ZVyXuAcHIkJu1e|mUZnL zNGi)ofn=~;6G#rrJ%JRktPG@t<&!{ag;Z)kL#UtR4!2&$X#OYI3ox&s%pG9a4YC+( z<4$+-snkNeS&^mOtq@Y99Sf;L%w6uF5V_r-gxuqfJ&j7m?wjs)vxLO%o9=b9g~aYk z?sfBo$lpr(KH9x*zL3~m$-QoYkiG;pqI$1eC?s|dQt1}UJlbTma3$JP>9(9Mm*R9t z8{~esbBdH{kd2TkS35(>Wh~Wh7Rx-yXNXzsPGKpBd;@vFozAit@;#*1oyqbzaJ4gO z9hTWcXd&fU=GrXUr!vnnH;E;GzKmJ!rm(Dy%9!PDnv~%B@JV;PkXXOplWv|6{CXnu zB&>}UZtee+s&|*lm|)I8y=$LEEsW)zUY>644sW>*c5ay zw^oR}3%CLDhO3=NF_qe#h`9x_)=d)uHR z^2B#Pf%ljJ#OBG6w{ud#{NEWXR^>ZBbD0Zmc$8-JZ^T!PnV_W&h8Pc!_VE! zIH9q~E$(I(H5U1mJN+VAN{#FFx(kK$C8%+|uiX|FHLlm^4sa?Tma768WGM^8zEmznLm;Uv z>jTMP`5}-T7XNeAo&uJ%KuTD$0;y%05lACTG>{IKM*`Wx@_Hb{ET0FGI72SQAAzK? z?EZynVHV5bf#eCP(Xt^caHRa`77D?;omqZz(=VgUmD*g$bBOubEoF&9RzZebW2TI` z53&aGt6RYGAY?7%H@AYN0Ybf9!)_bP>k#@9-0yBT%NLMV$RBR6l;GOtPdDRosw>v} z^rxG}qI#eHbf>V;yXCu3*NB_T@(tt@A^B2*>$<<(0wJ+u=P$QNNbK18%WYv%$If4F z8;d%2{&G86{=rhvaq+j?%d*=RB_k{o0!g|;Zo!;D(uKs@^S7HNC78kWx0@p+n8Ee8 zTgXB)xM)t-KW=fHP|tuCDUTDH<&_YrW4RW!(|I)%X%qmPRF?3z$M%reP)M$ecdJ9%0l6AF|5r~f7E-zBT;%`L`h#yJ3M#hYR>;l<2 zlFxD|ghuERBia9zF~>sQ#M;;;Qo(Wt%dU~7LaH>9WaJqY>Aj9hRce<(#z1z9WLz)h z76{$r|0|L|TV`I&d3KL1WO)H{AW9`iiss0eE(k6Ao{@!dau{OvifD6X%r?YiKt@NJ zZXl`B{(($^jEVFJsnNP0Lw^-y-$?3>GKShQ=iE_FGCuFNMy8dxe={vByyS;KNv zq?4tO<><&}mR-KW)hRM(L^5s)iS`@z4MUtY_ zX4!i{J)tK>#tM=1H}WBqBe_Ci*L5dHN~DOdW6wg&$&qRy?b>0ei+W45BlRp3A-6zI zi}VPQ$0_x7pAp#-C$GIrG8iX|JMj!lM7y0@C`;XmJUNkkDfVf|Onr>gB6Tczfef=0 zLFn8vEt0%I+CO0yZrIurIBc(zrwR#Bc8|OsYgw$xyL8?*vxe>3F^3-UpkcS}W zMVf_FYTb}!knOZ|SQp7UlVcFL-BT~-d zd@Zl=FN@R(k!zz3rDjI9NU{Hg7`opni1Z7I+M^+K&Mb%wag2KR{#B6?mNNVfP|Q^k z`);{@+G^xlofS!B*$AO^I4hFEasV>ZUCGsvu`EYI=&XKqWIRh2q!TSHjARIj+S4E# zg-jL_yWhDcGWH&-J=S}9Z6sGnrFI2M(Hk|djpVb;X8CWVP)M0w0-iQ9VoZBBdOodY0xzDmX^(CmTjy0( zM^f&Sc}Df&D1a=CR98sZlVwq4X`Eb)n2Jco{S<>yA&#kvl(US7+=7_JkyaL(t44j| z4@72G$x_E5rW*24WP~Lf@-*a;NJq7-Jr_cK;*UmB7fZPu@+Rc5NQn@<>jUy0ZUZi^TXQ|20N7(!Qi?UC^;JN4sSw-(RnMoL&lv%DSYWH}TdD(y6sdIs`IWJm~}kX?&) z1KAWwd4fve?G1?82>CoRg=IG67sywUA(lHKyP#M4o5%=D4NHH-SSIruHADc_7z961?$o zLhk_7z1%n{MvUbZ#7P+>;&nbLGgF=iAiH?R3MogUu6oGsUOEfSFnI;ChnFv;N}G%r zlD)jTdbysdR0m{luTw~kM)ETx)tmWLtS%Ehq5FHqEaxL;6yzW;^=TPX1UVEk-kZ*H zAolrVAxC;8adH~uD6fkpBOg~NkYl}}IJpKg(Mx(pW~P{1At!k`LaMaeP#4Lm-WDO1 zLhgs0<;`r6r4B{y%OPibjVy~%iX_kLf0kk@wPlcXkc+*v=VU3Guhk97_i}_(YOf;Z zGsvZ0Gs_2%pCB{6{y1?q;rU!I_j#H5Ys8F(T;VNc`2&&$De&w@imB4*YgQ8>gMbDdYuawE(2UL#92#72Fyy=IoDAv70j zj@Qca7KG+-&ho+%`y){JAhZ;>c`YpTj?HHfQ{ok^ zq88R@Zy{!9#6-OcmJcB-5p%oOsA6tIKR2Y*8)Dgtm<^CJ&uEf)eq_1ROJez(rQDmt z;tj}BcX_2O$q;Jc-ChgJ(HwJ+m-M2{y!j@aFHzS*ubkyn#QX?Z?~n>_Gs{%Y ze7~3ZlFV}%B!nKrDzBcU6hhl{vDeB{0oesHHD0%nDy@#?0WV{<%<~jvU&PdUb68p+ z6Ce+HrEzi!q|R#-QmIkQC6GtG#Acc24ahvm<6g0lO6^0)y^troVU}%>6_DlL;LB91 z5+`r@J(qfKm}L(LZNaBJ`xO~85wa4cp7xSh&g4?hcquG5Kxm(D@W!&-4QWQHXT4mO zB@o&(pYx`(tc1|^dfuDK(g|6IQjK1bl;E!R1+PhnyaIU-F)w(@Yp8`%dLT_+v5?r@ z-Iu%)mXQVMsa}ceAFrGx5oheXgjBGoxrM8}Y9UdZ-cGp%nOA#tEcABDJ|XoiJ!muC zr!;$wLdxvlA^WW*X%^C#a1!dOKwZsVhb*N zPG|=ES}*OjSPSVp#cz4jS>9ZP?FD(;D-{ye4ntkvVg0=0RkP5Sl!qYidOa+|SR4O9 zI=#f#DRZTE5=z0KJ=Pcwn2`BZ1nnBMj)MQarNaTw#pWc{sFxmh}q`GDT^RCBBtLrYjdmKO z8Zkp&xe$4L(d_+Sy=7ADD-lDzG{1Q*LSk=6`OTZsPIa|uHz4K_6j63W(SpuYL6kN1*QJ-N`>?}YOdPfUNwuF zWA?YVj77~c`^RfyQFF{Rzl}xBF-!2fS=5}jkl)Lq=DdacAt6!wMYLxv+N1kp-&)^;SkzoK+s}GO=23Ii96yIe&98I)A{I5jF5*`RiE3}7 zJ#-WdW8!K_g;ir5+ZH|3s?LdF5kf{A3&e+=!bD%$7$O8Lu$VNz-pTV*U zvRTMvA+h=V ze2!9d+N^MWb0P>vT53uY9c^#!r^M{1UqbY)s&eMJG6Dk#Z zBJ~V^f)E)q3NdH-Q>55OAkSWqseUfYL`bTT=|W;h%2dCNWz`JXFFMsv{*>y9t;4hZ znL=W9o$b$;Vo&3`a{W@4*<4qyzmW5&HJ|Gbv8cLo{p?LU)^(0wEhJXgIevo_yPWGf z&u?O>=eo}GTR4xZ>pXw#W|>*lb)H|$@(Pxe`g`;IHX*UP^8C$G>^Hcs3;bS|tz6dy z{s8AubzR`+ezs#>7x?uos;&$DAtAB4F7&<6sZ^Q$C)ah6pTx4)FLHZb` zQWmvb7x_Ibs;-Ouv0sSHV(+DW<6=KkO0aL_`;%G5b6xrV6d|#?^8F?jRad@mZ;?x? z>blg=6%t#nOZ`GAL0y;mb68H~x-RqQa~`!0FY|j@R9%<(8C!R(Yo=c+Bv#ingvNMb$OS?-dfOYnDGE#lDT}D)fzB%2Q_F!F3h-o{-q~D)i^D zsO2j3J6Kd**Z4_ai#)-;agCoY#jfPK{_9U*S;=+%*UuCZtLwl1G8R?WfBj(=RacQe zxo^k1iu~zPg5|p2pULtj*LA&LC?rN69qTIg%UM)iH~HN{Vs+i*4@j~1LT1_z=lMe{W4Nw){s`w$bLPTQPHLqG*!^3s8JE4#WuD?qku%j8Y|jp-|y_s?8!YLg}%Q&pZEE^&%@^v zcJ{k7v$M1R_v|@0Ij?JiwTwt!RY80w@q~F*1-TYUjN-h07UVN>BIot9puiwuef=z` zWkh`VvtT5QKj6HogBsQ&KCBM97?HfHg9*Em^SVB$HAtA(^+AJ0TwXT@ zjf~JpLi6&CK@;ndylxEgKeU=9uN#A_7?E-<44MrR=Cv?rvq++neRxyQ!N`0r*G)ku z>yf-}3QBh;=XF!Cni0usanNOuFt5cy#z!P|L!yTBx;e;Z&uUGjbp2wInDpNSN1>ppg;DYe|s!*p^iCS{jrZ zBrMm`pu!>v(oFqpSy0KyBb?W=po;Z~50?cUj7VO~f-zmmdDRBh1_|@34eBi7%Jqw& zo{?ucuU`ZWtViz6^dMG~*G59@;Xr=;hG#8xg>U65gru)gYo zN=Bqyb-_kPB(Glw*?Wv0*EW6~6j&tjA?I~hP{>F(=XF<5WRNhgyMlT~B(J-IJ&Z_R zD}v(BlJi;-lvyOv?^El;yMuB@1_2=--W^mJB+TpXpp_BH>+WF0=hmx_op0BozX>W0 z68i8rL5)Qc2XkKc1htIN`?-|YJwYAok@~tPi2u`Smb~r>rZFOUtqST566UokXtGG+ z2+nJD(9FmQT&~qY3+s`*RtJUM$$6~~7BV7v-50bPB+To+pwl9WDV*2+K^G&_IIsJI zZq_4t-5->Hk(}54!CFQnuZAH0CGmuLH3Yd9asBJTAfJ)*IIjnT0)vG0^){|r#ZZcH|Mf^vZji8C zj|5c~NnFA{Yz(RyxrNKs7}T&H@nK`o#faq97)IYmhLn-v4UXKP>F(N)(8#Eil=5;Afgw_VF7IAs43))ypG_MQtW67G= z1!atg=Es611_?{?Sg_h6ZWe9|)-dvWE=5zYmi0(pO~LSZa$ZeAB_op8AA|J<3G@16 zu*D*77x`qcm64}8uP1};tVi;CGMJo_oY#{+kj1m@O-d5!t+9~f_V;Po)>~O7D*HWIRvBei@{n(ih=wA zr)n<-&5Ufl!=7uk1TBp020|y3Ex{H>N+Ct3VJ`(+895&a@w^mtGBO9qXq>#g9CR_V z0LbZU2~qvR`l52Z5+r&dnHEvCkUAGV?v+6GC#iPt9w0}e6q|x+j4VM3t_RPiUHiu|T;Yw@pr{v5G4s?zkL4-t#Z$&SYaCBSFP=)~p)c@QJ=MKGtX<_IRjpOJ!p#Xyb&}r&qu81ji5Ec^QWMVc@qD$UsU*0&>7*`5_B=of#8{r zytV}K%+Sy019>yZwTSy2-oFI-1_^(M_b0&WL!mBdB93 z@oGm;5n`Vx0rfV>w>$W1QSwqUYB!V!C0(8!3C zVq4I}h?HVmka>_Tg^bwSf*gyuQfv!`Ga{wf7L>D;lww;@VUVyC+kzT{%vU9_nQD1^ z(9na&B8?VN)4@aEz1$u&8zgMuJA%xw8Vg-N-4Wz4BBj_7bXX)^TKJBjlX+(G%(x@y zVnj-@BiO@IQi>gc8XkI;`st2fcn^eZem@vt5j7V!)2RM_Fs>KR1m?Nk@yzJK(}Ws) zKbT{X_tHPT)%MDrK{X>=fE@cM-l7d^8QBJezMQi&sAnX;1T(eaX|#xU&#$bXcLps6 z3D+AtgZ#W?AMOka7!e=t43=BOtv7ZCE0{-oxHDMIi1=`4(7;mS!<|8+LBf`>GiYV0 z?Yx%g48|OsY)@w}juEk^GwA-B;c;t;&LEy|rNo}jAcGOHr!y$ENV?e58I&0$w5Kzu zGDx_V_#mibJ-nZAXvjv zV&MnDT7!fZeh_S7DOoY@3Pv58Y|pM>3?pLCuAtr`t_F7n4a_6<>pw7cgE!Nk0s>8{|Fj{C{CZV35upQq)s+2VDjU--X#7sIOBgtY&&k zc6TtrA_;ouh}Nqg2h%K~E`jDwgDhd5tAWt$&=ssVNH}751vw*<{nHf;XGHwd6)Y?; zJn1rGcLhr<;>NnJU^ye=pRS;erNlp7LA^mj|8xb-JrFAUCqauv)b+5Le!J?EU~7cu zAHjC!xt)3b5p+j*J`Lg{jmOl3;Gq@Br$LcL+^Xr*pqP1{ay(TAY4_eB zJ`J`rBL4X_XgJFH=WEJSUl^%Ii#Y#$8ZlsTgBizKkDUW4x~=hLFv}v&&tC>}7!f~z88ot#`1#AA$snPhk-I_WtB0VOy!x-8 z+aS*-{N2yANrNae+5?= zB%CXi-o%L1pwe3yks4IGc#N$<*_$g}Vi8w^N|!PsHK_D7mXaD&dWJ#58dQ2-4}^T^ z>4g?ie}&Do-thEttHrRU(-}lrgNX51K7TFxu zX^Ku*L>&MgS}~^R9D`h^wq9@7KdE{YBS(Q}&y#o)M~`8IZcQHt&8fPakul)e+JyI| z^%_RL4dev8|CP{PjO<#89V?{z>ET6W&wO)003mK8NI#f5EXn4|P-#t_}Tg0{1p}K_;X{$qZ_BbmgZFQ*5HAvW2 zhw37O%vT@7LMm5|F6+T#k#dWumvPVhHz;Y2zAC~qOwVH;FNSsqo?*H+!gGMGW1hj_ zc@#Vc=*9@oS9BBe91k8k|M`k;iSXp=R_6I0c%FcsT)o>M?cU`;o(FQEE*+oj)dO`I zBjQycCtLq~1gX29CaH3ZIIkY4D;N>49;iE6O1yfY?lMT|)dO{QF=?K!E{R*O9;8Px zQst1z1_^u0L3)}+)MD_^oO_U-72)}+p2Iv3fQNps_N%%&!ZTdgFwZ9N{0ZI|u2&eO z-TM&8+d%SkCnGXK=IJg*#6Nku_}kV$6H`3ZX{1Ul;{21ROBoUW_O7oKA6iKTj~+ej5z#MyJOE@njRIap6%DY55Z zJ=q|kJqPPKJrHX1U(;7vL|uTq{B}YXMtJh|66X2je5|~%7R}cy4ASmZL8=Pp*!ep1 zRAaN-t>^0;MxH;BDG zn+_j-U3W7=Phqlv9Hui)qf&%>qQiBLLBf7Q@~8mUT)xRx+dS1}?jVWeKqQqmGe>Wu~oTf#`a-5}u} z|45zq4&@d4`AD5<5&Zrx##MOiNZlOa8Kqm8=X~ZFrE?}F>p5Bvw+MbQ)HA0vN9z&2 zct$bLUEnzaB|TafM|h6WCCt+Vp5wrCj4qAv9IMNihx#D3=VNtEFP>WFc@}z3hMr?} zy+OiLwb8np5viTgI{sZ-gHk)A^_3fkZ`^@PG?N9xy#A+ak`ulvG6!u!H8IRobI%UJNG+IcQMZ_KKDCLcQYau9;ekA zwp?Q2aXMj;(8A;Nh#m;}d5j)q5&Tx7U3ret<1CW+2l*MRrm=d0LFOl7>7IG2I98W3 z&q24_mDM+O8S@+ggq}QpQ_o_aF+k{i<6C+T^GpUp-&FaQuChF4$NCvodPRCQOJyQ2 zx@&!+Uc<-%KZf2=h!9)AFQ*>*D=TzOsJUcn}Q}w7)+s3w|ZO}8#3A&aMX=4*~9V61lCg>4o z+SVc`@&a!2flZr{Yh7l?IBwfphlpQ?NY}w_c0z4LR zWuK($7?HA1(k(0{WuK&54HA}plI}9dd^HaiQrW+&)!CGn<*`V@BI;J~d=G8oyE-?* zGg;>|PXl;pKR8*>i119&vzX^e_Qn)FkCDxcoS~OTq^9Z>%=0dI=qA`yy|EY1Cg%AV zJal?FRqruKyXW<{E261-@^tHY@$*zYjS=xPG@oPrJQh;4BATi zEwfVM=c&5fAfcb9>S}|`SC>IEd9_s6Ga_ekrMlHhC4LH?i{R%{-Nwj`Kq?Kgosp$L zXpA~j?`GsqAT&musk<4Wvs^00SvvlGQ>Td=fLywPkW7oX5#W0|rx!BZB8ohJEpq>! zE{yO@(?!g4KXP9To@sirLE62ifzVoXnr>u7JU>l0F(RIyrq`TjefTA$$mVH!two&Y zr|I>Ki07y2W|k7qPt&ai2|YheclAKX^JnXBi>Tu=P#3WIY@IQ~_{Vu;y3V$UDgn)oTnA zo-CcGH}&8lub!v3SVY~AQqcL>d3rY^%??SFC;Rz4ooNyE7I+?jpU=}(5uO>knt67T ze=xSp(Df0XnYy7D(%1`W>V-7-LRu_>_m*uxovF9>;@QqTnRwDieQ>5$7bcf$rcPKy zWd@##W1TQlkBjh}uO~21E_l|W?C0w;Mn(W3%@^pZh|~qTnt6@|&ohv^KrfH*lC?cwR0kaSNt$p&um?35!@=q-W)u$_1Xx}MS4B+JjOg1>G-TQ_cq;TL=6RcVP&*ZR79+cXWI*Z?UEM>9e!uBb-DHu3{C?AAy4fJ}6JJ7# zcrMeMn5VzS{tJ5M=q=21ERYcf>0qAofD{6mt2>!zF_7;VWRF3x`U3JpAV1QTv#F#@ z)jL400CKrj7h5ECfTxxMsniKZ#sIk;$Q3%1k&A%*2FR5<#~|VS{$oAdBI?)RpLNO+%ifi5;kSc(ODhD8$FAoVsZyiU(D$o#}dKz16Wl6n5g7FO#jMiNlj ztXH#?Sctp~5?Z)eZ!$=@BC65585xW=M(w#qXI^b`cQ)7PN=C%y8ePSR*j%GGRVCY8 zqqkVZ*<7QyG9otD=;og$d$mTl7$mg0Mt2${yz6+g&iENg%~xMzn{U5 zq{1NK+3C%?{5sNO^P;lftSc;{COfJ6UOWxVGaWp%?!8$zMR;z}&CD|kJh6B1Eic^~ z;ki|}G0#uH(+@ni>fI5ZC3+9@ECWv_c$VnQYO=ZATLa_}AWQWuMx=I@>N$)^?JU(J zZZJ~m-H@VR`&z0;S;W=OQay$dshy>|h^3@OE!D*a32SGmF7JVm4}Y#JETT>sXlK}; z>#7LPGF{C))4@Y$U(56ggM@c-mgzl=NV%42b)zl2lxvyZY7uuQXPMs4JhOP-S*CY0 zBIR19yI4xfwM=&#BrMl5ox70o3fo4l&bNrFgw037^R>Dt!gHH0W}aJ^=Qdpy;rWFw zXP(vIITm_;q31<-Zr2N$=l9?#0?+Mwd4%TMlk)flLSTE1kJ0mQ>O@onsL^+C>ZBitoJX5fPqW>ru>;1D@H?^J`shkfrMDKcyBRqLh~9|re(KDd$ik)SQr3L8&S7L8BfrtZ4HDK@ zy&ho^wG2F$!NPhyEy8n;p20k;z(Y^K@6nYJo>jWaAnk5fyGnO5A}w>3?qWn*<|;j_ zhHP$k&!AW7ITmp}Zk4`@5owvLbQ4QS%Uq?K4HCA@Rl36<;WOxab^K<^D=hoHI>RDr zEtl(FozKV{Kz<5utk%VhbOE^q$bGt;k%8HsdJxEO^(KqB6NdZs7UmfR9(pQ$zwV6i zH0Z8gNHvk@(9mEdIj^` z&K5qTGjC1KYmLsah*|@l7olg3E{O0vtP7dvN#=Q2mq&OW(G|?|8uL7&8zVfw(@o5? z6FjdXuixpG2v4JKWuAY6XBwWGHR?7-at3?qElB-dcSfWh)m_YUEO_Q&ANQ!vSz=0> zc=k&BykM;!&d6l&(7Jc69$^tvr}UdaYxS5O2#w*7>2VfO=Rk^{X+EYGMtC0AOPHqu zJnzE7$92w9le=pzO?tRR)Q#YwGms`dijn&n`J*neNa7VBA3)C&x|ET(fP7|<8I0@% zLZx_8&tl|rAas8Cq^`7x%YD7BVxIINc>3RgZwcyZMh*rtz#vN)`4;nR(90Q_$~+r% zy+xenr*uOvq|qYkVn_{x=BIRLgy(79)eGrnDSCEHcNm`5IX@45NY8hgb%90H&Como znxD}%J$R_Tp3}9=b3b@U&vSZBglD5(%RC#vLw#_g-W}n2UhiR^KZA$P2A|j2%R+l7 zuNQQ#MN}8(^@1*p@Vuytm?v$heG2@dF73rr#yrEpb2Q5JqOOeawCF148N)m+dQ*hw zCB21tP6N;J(DRbs9pQOd?_r)w@O%e6FKgU{Q?W7(n+Sh@#@@rk1dH!I^wsNA_nt1e zgnO6w)NdzQ`Rec6@PWfT5T2UI`98#P8~Un|=zRu|L)e{IaGh^zUGp!v~ zzpre1%%tN!olD=B{>^OX77kA;A-%pTKGo`%_-@Yk3+59)NGSY=A8!5fukTnrlK(LV z_tl9ejH!tTGc)6AGV_;lSk7Ss=Wjz_t>t*@X;!cJRl-Y*oUh*gE~UrRPg#%D+aivO zJ^KnjGx_;yg7Jr^#7{q(PWr=kxR~`W0YV%HHIAIa&`KKjD{%I)qO z%rE_BUw->PB|Yk&Z?T^y^ZG>mCG{hCj+tM4bsF=_ddH^wY6j!Be~0mCe$qZg&!sF^ zWkO%gW;=v$ep0%p#4Z^hB((ClX~%k`z1TQT@Xh$+t3R6cV@xgPe4_YuMn3M+mva75 zzWtRKIpO~`+jsCecD|N&C*_q;a0vyM(E7=D^UU7kxb@BU+c2z`hu98VKb}gC$6S3% z{t`<5684qv-to}h*Ny+BzGJQ&$+)MI<1yv&JYw-Moz_X2cnWhWr^|Xu>^zazv(mnk z?TablPsYRcCgYQ=Bb(T-5>J+ksYloj@$VMq7kl32c(NSM<=8LQ&QQ;;hT_)XE z{g1Tsl+E9F2CI_sNe zLSIQ-{2~6I!RdCr5mOf;PWywoCQMP+n)EO~E9a|uoR7q9JBUSa&-p|0vwro}64ont za_GDLo#T;{|NqlN@%yL7ufCG@F8%on_J6-`*nTT<+y8wvjB#ma zhjKi*K0^Q7b+O0wi26wyH+(gY<((aDmu;^xMeUXPo8tCj=S$qiG;(2mOS;t~<4jCl z!20KKDCa3M?#g+I%;OT;c@p z!kBuO<#wAe1)sR5_Lr&#nJ`V|n-KT0&A!`LqyBAN_`>iQ=9f_P6h-t%`ga(Y@$D=V z_c{NVIuCJbmzOf1J@4?=5>3%6^+N#?WSB||Lm*%3Le$>8J`nLy2S5de+mA7 zL))(Y|L%wA{%j?;1L-f(^4s;JuO0>;omUB;jPG`Q!wp+LAC`6V2G%QKRPX=WxSh8= zCF?DF{vD0u{FC!f<`X=wHgS6NoGWUVl+T9Yd@Aw3u%6_2%+0%kcSQJPJtnxEcggvt zgdek2%Z{=%eq0*2XVU9>#GBj;+{G@68~mIpUmUdAHF)7)5SlM|LKgM$)U&z zeu2S#brGjaJCSi(=F9(dpWc_8*w=>NYVwTI9z zvX6g*>*ZYzWj!vT^mmCz!+%EPWPXtTFZ_M+c}LrEPsWXGj>~#X#&4-VnV+KJY~J5Q z@qZr=pC6NaW+wJ$oWIoJU@DnA*zg^VFD<^S{00adq2&$qwtE==otZ-n%};Z~tjMi=5qeCF{-1OmXc&_~g8Cm6>O7 zQ>Bmox4-4E_1L?7(fUo!$5$dJcG-B$wTEPWydBBo)&7PeFYUFjcHUPz7CAZpwer3D z!9Awk^zH|J^)G4Xa{eK4S(h|&yIsuBB_#eNw@;bp<++@kkIMdG?{S<eWl%nbo&&&+%mv*wh zp`72yJS_dWfzL(c9+7>HjCaTQyiU$d?72*M-{wD!f8UHRm|yt5&VMRz*Abo)dmGu0 z(RME9O|s7tIop52ds9*`6M4LghRJ@#`6B0M`F!Sf|*?G$6`7Ng2F??~J-&1&gPgMyX-=qA}->h5;%cZhhTAy-T&3(PF zeOmccmQQ2(gp*J9XSjat3zzaGhrW8>lrP55%Wyx`oRi{S2>T~dNd42EfB3j(H`K(# zc}>>A5{mv8G49cRssQ|S{(B0`Rd9HP3FSFKjPvu<^_(vCD&zPyT%Qu}D-^kCz1VVM zeldPd;d)Kw^_Tb;?-`kKGOYiooUPwfmQQ2(gj@HC|HTiwXj|eW&Kh7M1#kbq(}AaEB}3^C%2R6daw8M(`f$5{tMUX zg!i0hMQ(rdlW|=_nU5td_r9W`)X#U>pEEd=cCjky-hi)c9OKYl`2>%v>muoL|4E)d zNGSJdSAMW+}$&k{G~p`&r$#PRZq6O`(GbX|5IH2M4|Y})&rhToB9s@XYttFY~tSHzfT9mbYQ( z-`5Qu#{1$U{nSsR{*rMs8omR)J^7y+<(+w}1MgxD{kw7Y0|#6Ai4Wv5N| z+&Q5vf1K}Qr}V*xFKU@{A8GfvPoE^` z@q0kwzFhi;tarr^Hk5mvVg1^2#?&IVKbc?lP2v4S+E38FYB}@Eb7#>b{rytD-(=JA zZEY@x-H-I%|0VCo!t;EoFR9=Eb|~%X5VMa8&(o9JNAfu;&6|>bA&1@JxAlw^JzK9j^fhaq(6xs3B!4JU+qBB_tlQ>;dYsPPMxA&<8tq-JxM!`o|DUb z*;g3#o6NJ~S6Q#@VmoZ;tA1nb{3+=+#QTE0@5<$L3CEbYyk`{pSH?NHpLZhjN&Gvk zr?2<;?E8t~dwur3m)_%*%m=g&qwye%-@~xwjH!_(9#>;cn8Nq>LO%Q6M&Eog--_Qv zzx8i8UiXEIe3W1OW6v97?)}bb>?axjY2QiDQD?Bf_9% z{=jF`tBzF7^Kor|&&3_nkZyyra~=>aqPt?r-=??s>>P zl5<#I?3VK~NtgQ~B6l(8D|nP|j!Ex*ZzXz9#l~?i6VWI6h&_@|^j?a@#olDOxVowj zy&`v=Ne}&D^Xp4a{O2i=vwFgEiXGyQpR<0sHzV;CY?tT}yTz}`?INtl29}d{VB=x? zSjXvdUM2e`d5>E{@u%b~_6i=2N9#@G_P2gSugHo2lkw2M@;t?^OL5Pe%O(9r)~)i~ zL7uPJ^@+(VarhLlIjE1~R5w2=?TX|aC@<2JZI z_hpCJ^J~M0=Z<{O)W-2;V#e2*Fs9_*xTK4`gwJw*(NNN*ex*N)o@glP(a_dMIA6*8 z3LDwZS2=&FKS>uk38V9$mG_kBvHdw_<`=pbDdWiA(y3iY`;l}SAL#u2E!eX+c^N-Q zj?&*{yL-yhy3f9!;j0fgJvlDVV?%!=3}5f|O68?>Q0!ZF{+0EM*zMY}Ie+j~F7wH` z-Pihv%em^_Ltl-K$eq|n{8Zqy9;EYIDtAdFE^>B$_0?q76Wu4seDVKP7_}#QPABP6 zyf1s@-n^tsT+UxbpQJ}adEZ~&>zC*K`wGj9e|^3ufcJZn;xSdu{@<6LzS8AA*}1Ie z8rCo8UosEg&`0`h29NXoh4A_H3g+)CU%}=1@KpYukl>H89TJ!KpPr52|I@g=e43GbzE9`%)1WXBsB|0J~j?|p6})c@f>I+{Q+|ToczWlMj=MK^G&SwAHa>d*|RH-*vKS?P2 zAQ_*pVE;;7#+l@J%-#Ev{AIlBD~yhd(e^LzEedYe!FUd2+P9qB#MQ?pp3>*Nt?StS zm4*-R=bLy;$vCjD`rF@p>Ae?PZ+x5Euk5d6yzi_1+{N~Zol@TaX?r1iXoY6i>0x>0 z{fKCIFWYfnB#iP&JCSis_9cQ#_zdfj(2lP-A7y-BeiuLV)gPptMMHUR{147Y;{Ne8 zF8gYb3FW(5cpo_uA0LTZJ#zm(uFl|ed2S`=4Ko>^$m65c0iQxAA{FwXx#NP3^x{U4IdwO`DBICI5?LFSx4zW+Zqp?qUeomUq2fYzoorT*^XxD9#Z(niQ zPuhO8@8`-g56S$rn(d)pPVL(Mqc%?e!niyqv-d7z{2VaO_o3vzZTP;4@X38y+YeKi zU+&q4&letLJM4D{LcFhX%Y0%(Up>Y0^8Uap9GCgw4UWsY{vD3n&{uLE79CIIJWTHM z*!yF?xerF?FLFOj)?ai_jP{9Qx2*SMUoY)Q;-45heDyioBkNB2PMxdYKK9T5VmS%z zIEP=*;Q2$oqt@>PyPlN&mh96c6g>L9GqGPn;gj<1Zz$uuJlBfGcH}`S~pU?Kl z_d)M7e#U)W#wFgsak0yOhcU+AWyE{kMlMA~`7Dn2rdWRO_isHVdhPx?=I+VKeA`zj z_k`s6rVZu3k*8!_{7=K@*xu;+D;k%5&#TNY@ir5Wx%tDMyN2h#9n3H1u{Iu4|6sgl zyXiR($-MXy*OPqbM&eHxJf<>EwDZIe6Z-spwa^|rPs;a_J(aA-Q(t9y+wSE%v$#hD zoX(Sv;q<=jjN;OtlfPpf&ablWm2tR;?LNtb*iUkPd&lF-&MTq5X#NtH^XBidKCw&k zmFJRi+#jNK-$l&V7k-sV$Bi;`-$UTm3lKVLo|2Cgl=4e#`!o z`7=8IM&pk${~uYeydNR+vxMt8eIk#0$?Y!ue!BfGUHJY+^7tv|Qoeeb^~t_tE8FoN z1^NLp5zt%h#Mw_FbNR@yWTIwNLyk?>mS4M(cmM*Bf*1LyKMZ zoJ`L5!g2_1?_-Jo!*qL2CvtKwxR-RZ&lC9+K0n-FzVN+NvDf9hSG^&>oaYI@yx%H% zY&yRGX7+z^bN`IKe?aA>_o(Im*}vmg=+AbY81mb=yay`fl+fpMX~|d4sXaA`+u1kF z^C4Le;C*vGM-_W*{}O+SJo4jxY;+xW2J4OD(l4WUG#*{o^_4C-eP4{mN6|0gI=-hV z^OLO0rG3izj`06yA?|r{IYpn$PZHnzb0VzJ`FWh=XYG@9M(E!wSiiKZ>m%_>qbH{N z!j~lRArf@gfU)EhR?xde;?h4iQlE3Brc)sx8xk}8NT;x?FiSw1X ztm7mUzes4?U-&#s=7qlS{e6y-EZ_S(5Xt$-y=YIJ&focvP|7W#_$wMpJG`Ci?M1Gi zzHnK8yv}?Q3jPS|vEM%nzrUND9)90d_~|>oG*8}T@2l!szcq$nQi*x`ainU*Zy;*au$5cwhdI@4P2}$G!JH zE4hD!?SJoh*gvJd<-AS^UXpzu zm*xwSr*oE@IUg(ETTbrB*mF>v*D-&zUCRDz6{m~;?eBtMoQ>f2cS3x%F@oE6;j6bI zxNRSJ4K+iFXO9kMsVS`_vXWPWxqEQ zwiD5Rs*w-xjS6n(7hg?_=)ZvZFOA?=GydX1$$k)f=12Gimwlb&yO{OVM)>XcF7NGx zO zMWkG77=JQ?NB8BTN9>n+<_np1sp}f@7jtHOh6FKh}J3eN; zUqtvtPuj`J?L+EG-s2EG_IWbqN`8(bcweFQi>F@U-_MbCoy6sL!Guq~`z)d0GTz8K zGAm-QWFS!7)y@2|`EjpUq4p38~f1&_uFr{~D>-7PzQ zh2P!jYh5hsVfk){tPkZrj@T>Zx9v5&7awgW!Y}@ja+O8=EPSF*;&RR|VRSx|`Bt9S z$@muavplaB{n5C{+3$wqxjMgxD|U-L5=y;DJCBAJaQmv@_H_l9H_9h=MnfsDtWPBr zKCwgW+gB+3a?iFee&LJSDRFy0G;Gh(Zsi`Q*eT=6qDVb2=XjJ)_@%w|70Ub~>EaIw z1s8o1%KaNDM>Ldl32i&;{T%0RwqNp-Q1TW3Nf>Qs^81F?u9*8hIeA_mwaY$t$30R$ z$B3p&K7EDqod9`W`2gE5{a@rBiPV$ylV}({7nkwM&ck?~!|%z6ob)R>w-tX0E^$eh zP|jr~UF?*&gp!|}=g7P+^H>X)OV)YvoHm-j#HAkXc>{i5n)iX?KiN;)c?s(U)-U~4 zp0kQR*-u96EgF}7lJraYJt1j7^1GX_bADSmlz#MQj>~s=CH#FNX( zs5=oWQUg`7%2t2G-y`_@15(S>Ff{{zW$FNg2O#{4nuWg^_$yPxRh7zDHR=$xP#vn? z_r9)nd55VL>IhYjKMGf?0@Z-Odi<@x-)eQFI>{fUn$*#%SsjDFLe+x5W_2w7Mypo* zHLI~|tNNDO4vX&bPCywDV<)~AjZzAMM)KTiYuxm18 z@G6LR2JAco@iWzA?=0AH7Ua%?+*z>Wd+M*Tvmtkm%J$BIo^zn*`;b2u{4>Be1AH@( z{{?C|{#w)p(EkHf>s_plPPqhSzDzCjE(h;bs?DoXI<-m-!QX;dl{(Y^8F;T%d%SBA zUavC!h3cuaMQU?ejauT}jJ$3}Ublev7Vs{G?xo?zf+-&5+1^rxWz zDfJ!vy#)MB|7o?xYgTdZIpp^o@^}HVE$X-ZUQ!SCdje1>}a9A)smmow75d%QF+HAwgR1^vAYmFeYsnO+ORX8f%IJ;xv9 zozO4aTNoPxd88& z1;8$Vz6*d|0PI4@UI^?$$X*ESLSVDKQQmBX7r~~BkaiJlx(I0(0lOGFE(UfnbX*MV zVqg{C`+fzm3U5JL1+WTWmq7LsV3$Dl5@44AyVU!>KL>Wr0X_$I%>h0K_?6y}lq-Q< z>HQ$VVaGho;s6s{^*eyEE%v?{0N3@O!;m688eX7x+W4=OJJZ!JdbJJp^nGur)^+Az}5kK9A$nS*yAYkyVwgGu>0JZ^nZveId*fYSM0rm{A zXMjBe>_wFMMPM(Y%r63a5!g25yA9YjnHelPm)6%vB+wKh`%22;LS1J0n z^}`%hpcXu!RMiHB(ffIPTj?u*Q);|A%J)<$e$TvE{kB1=OOZNJT|d-QKSV6s$d)5@ zyjl+IB2anWU7!{isvcAgs40ezerx`075hx7yO8fpb>W^PDZ|Y-UeW=1DkJT zspRw3Mqq8gCaPCJQHkcO4p46cTdF<+^?{+j0JXjZ^fvr>u*2mwa zJTig(;bEoLnLOS^z8efhC9iQd*Q?p^^D1D^yVM3ljfHoY#7916%Q0RZ1brDs^Y6~b zug)X4YLx-ma`25;RH7kBt#z8mt1CuZ4`r#YE0nt6b+RfSS_;6|==k1LhXWf8tjS>C z0@Z9N@=KHReY30ckJLCwoC3a&)C7`6DW6X#@173Kdbi~%rFN*@cPK^W-JurjLF!tf z&Va=Iu)E2$u&Iz}H7)F1P;I6R)YjTm6>4BOsC4fFNL+-}bgu%`98g!QD?nXoWTg!? z8+|_m*5O)gi@FAUr0;5VKJpy3j;IB|sB9gs{dGETOjB1~fEq;Jxt{cjT#tUiJ+_=M z`VIH@?jfocB{~Q_izsZyGidjAEbjxzdw1MDboGg9vdHN|TKbp}eB=WPc? zt^I88Gf4)jrvvMp>UQtycL=^wvEsn>z;Bc$eew}AT8P|HDmVJPYW{(F%1 zRsc%}mFL|9ipsIn`z@#;!0z+bfTFQ!rPl=NKwuAh2Y;&6*FdfFsCAOWY*mTAb-2;; z9Qckl)GMH+Z17$4`s>F@xm7a{~Z>noHDfL50EH$-o38-2_ ztwO$e9;IFZ>`m{lu=Ho3@;u7pdQcyEM=n%q23qHMwGFAa0Q=0yQjf3lrKYPLRpY2E z{~wU}B_z`Q_yLxx@u@`TqDQqm@3xy3wgP;^{4DU1$7_6Qf5ca7^c{xQUTZLFv9-Rm z=RE(bkf8R`?i~h->ayNR)EP^u*ETq6yc!F>RmiQ;Va<+ebySC=x*TOoNw&24GNM-- zt7w+&c6@PT0p*+Rs9ZvUMB_fMzqa!@oww0NtTYV+Ee>h!+yiA~Kg zz>*f~xDmcO_Cs0IPRc zqoc;FpCNS;Qr|SA#VLbKZ#6Jl zc}(#h21WDCctyST5@>00v)@wx3DN>=ssA*nMWBjZ9wl*Ub)|7>b&Gr-GsHmj)hS*J zBxv=p$ZyA5idG6s{Z?SNLd#13eNZ$CY&ZS99jl`Pr@72g<&N6!S7Md6349d}D|A$m zqn7%gLGx|MW4y}ziXF|?`NKhxRZIONLDeC3iZ>b*mAuNyW|>tVt*_?$ry}(p@MS5n zG)u|cn5A}O4kYV8@(LGW^oHg-r={Le?WVOiKud$eYz_VnSiAQjwKY(7Wl5v$o32kc z840t7GZeMDZl`63-|~`DZ$S1Ve=0292CBuSj#t!%b^&X3*c9&^@KJr(wS2H0?Jq84 zY(|Q-oLonBI@x@KkuBq5G)Cn95w^Hi(&V(v^+%$nv!S^=F6*YIxU4~%<5YtOA=S<< ze}*k_lLw8gQ@o40KJ0A&b?~*tX(dc!PKUFlD}K53^Awu9DYYm?R)@tYGM<*Wa@d|% z0Da@tjmYEMpvqioR_ux!lxYX$J+K<^odv!Mhn1T=XeC(TDBH$qX3sM{^!t#Q@Bb3A z)N&4stpas{$xZCbi#-U8>ax&jE-=(Z;45<26z_5HRRSBY24Tz~Ka5we0lOO56z?yb zI@9D!`eu6{0;4uLGv?)Dy#Z>rHyG3nkeKZq464RZ-vD(7sOl7%f5)rQz*Ye3FlAc{ zs?(LS!&%kos4hd9HtDE%s;m?;998ZpJCRF|jID#>A!mW5{g}EXgROIp~c2upAt%8<1CtH##ea33Zh2~Nx(UAH8`fnbv`%KHBS}1d= z3llqbDRm6i-gcKoy=3KV1Ujz0V^prf0_7L-txWQQ7$RGJ~?dXqsdLh5I+`0aL-|15S8Q|aEY zYn0i;dhv6?x6EjXFTr{rRF)a*=!78Guhx!3##OpC%>`<3L;x6z#9NTuQoh9?) zKL$prdGTsc_aHSt?N(4UuH~mK?Qhq+J7OzI7S`vdQ70 zDbd*Y>%i`ZzG3m-p_Cg;DMP9gspO3vu~gh@A!=fL@))IdVu!mUR)zB3MiQ}Xq*A_P z;|GI!82ZMVv6%Y)*f@;qvx1s5>2K9(vNo$plip+3a3&9D{ncs` z@^}i?*E)T5E{{@2l{>07O;*Tu-~9q)%hIMo%ilm%q)E@KbP`pLDogu5_^4LP(tZev z*1Lz<9Upu+*I6~!_>|hv)duT;=Bwj#A+ZzGM`q3N^fPE{{x!fp0XEFP5!AmxwR;C+ zMM3M0Vg6!ZY42k`iqlC}V5mEQkxysh6-kU4n73y7_cPY)ENydZkQRrvIx54A8)kpx zuntGfHa1i3FO9cdt<+HDyTeR#Qxg zihym1e}vT24JCc+?AVvU$ZKz!e5W9_-B4udj`)DDazBdy0KJSx%s1nQ0-FxL9dTJ5 zeiWzG@JwKz#S4&n38;_atfV0s4xZuZo>tiW9QREl#Y57CMP8 zPP_o>F-TM;WX7&^Z745APO(dz1x1D;`>NczK&?x~+6?-hhCX{LPWDwhzIw;k;HdgA z)%CZGbXkQrI=&`HwK%FaZ3}XvS-dule7Xtw)~3A;jQVA5+D=e!0Bdy;ZI0@6RDQZ> z$xjz8`RSr1KV7tRIf-sZ)j6s;A){W0qYBdh4h!Cb1;wsJh3V1`3e#mrQ0VwddhkhY z6{K&y-i{kZJt#>nN~cktW|Dljjx9)+)Iy_=+E77{+^EiXz^Ve5TI8t8bZG}w=`xm# zPx%58H2YPjzlB-+CCt;+=^uci`mIQ(wFR~I8fSft!Dy79==xiYCz|V>=2~Mt&Ek7c zrIEp$jHSzd=4>x99OFH5YjU{_OOdnsbYI5U9cIt@Ir!}E)89#R1^KFQ4V~4Chwe)~ z0Q%@WJIg-=RDbX_JI$?*>Tp!2qq-aw?UmcEp+mh|g+KiHQ32vgmTuiMFH7+XkRM!y$W zwmbc4mdNP0lPP;9oev8t`$@f3^ph4*$b<2<%ODB-+(zSbw$pCMfDhS1USEr}621NkX-`)E%HM z16JWQS2?P{QTZv-FY{C6jC;K5g6!3htucJm4vO5mq_)4boSOd9?yLLDIMnDgw>YY; zzqGmzC)?V8)IV$wXzhO@C|WxfIf)WS6`R!qSx{rN&@;I%qvc#s#rYsr-Ox+7lnv3&wlYRXG%nsP@~ILe+JKML>OmqKIqpF!>LHjz&-wwD<` zFn_ir=`xB!j%smK zzLPEMHwZr1g>p>sz5dP7l(>N8{xXmnU(#zj14H@eg}y`NPpqopXI`k)?A z9#DNy52y&F-IoVa%A!ElDkaXUQb(0Js>)C_Vip;SdRajreWoywJ>6WN?mf^MXh9%7 zzA%uzUy;M6cq33=+Sxpu@=Z{m!3PC_w5tMRmFk4=O*!a{r^b|n=D~tM`rE@P;~-0C zJO$3GDc-kP^SYENpwgiw-JDDe0F`BS9khEo%I#t-6=F0K*E@a9j%qO!&3Ubksx_-u z!rC0x=p;HFRi(vORa#bp)mqw6m*F#OdGBo4JQSANbLw1Bv(33Y&2%+Jf>yaT`aPo0x!a9(Q&Wb|nWXe_2RVM8EeQ>)QT zEwR;kqt$t%*|pAA=a+hi)%O!$HTJ-$K4|=FaagOPYRpqc@SW^sWZZ3YHg_0L+SVx& z9nO=DPBz2IwmQvCE;ZYwwz$;h9+d2BamVJXcCTCR+D5LEt%ym9>YZ#wOj=!~lgM`x zRZgPGm7~$gRyo-wgHhWk7$6oD4v_i<#=a^ZApI@FwaNQZ>7K#}^s-LpwfcV7pr?!k zR^RUiP{)C4cH?BWlg)Khqw7&+#(ElUpHEo~*^?k!K0r!dK0wObt88hxy0T7%?ycO#Wn!}bmujiRrr-vPV-8I0Zor2p0okTrj+d&XR2EHGo3 z@eAdf=RFNsdNM!Le+d*lp{yGq^GDqPnP=(-$hl*k(Xt%ntu@r$pk^z25=Y}udkUSk z{0`Vh9-UDA5tPNAb=a$*-gIlOH&eDE-?t358&rp({DbW|SL1-Qj!^2mcVgEu;CxVY z;$Pxc4~+w4gll%*Z88*{oHV&owi%2(**0MJB&8m@m#6^0AGm!5Q3aqL1=e78a%M%l zmtAy&+eOFUEYK=>+bh5QBwbrHP$K?d5(ovo6 zZc3f8nf#I;m*+=wQ--2N>_ppGl#&Ok3)I|{V?ljks1rb?e}I)o>ZzbILCsB>4r&-E zG%B=Cx`{A1Wd^WAOzLb<$Aa47)9sy8K{dF1iyc+!sPT$!iCzf4CWnM+ij#?YP z6Hl(|pToG}c4)268%3EkZ_$}xhm%MocC_Q(+v^w|4MuIK)~uVz8=bBOOH3-wMM}b^YP#6gv!sP(WKnA} ztvE|sM3>7WFICpXd8yA_iyLLgt-$1FYSmEGL-S18XbiKfuLr=FpG9LfjZHKCIjAFh z{!ErOQfiu{j*oA-RjFei#m*)^pZOk+h0n&Qg$-&XEw>}J39=8z7Qh?i(|Y$Lv%;0F z(%Dz+s8UB&JF3P}Wsa(IR7ut&QXdARIx5e4)?qcEsBDL&u7p(^Vbx)&e{j^xpf(w7 zE2u4o`a7t0LrHBlx>{(=`WI4nAhpR^(3q9_HQZwaHq##nDrFZuXHu!;W8Kr&Dc)HN z?M;L>m)ha9v^c6QOU{Q{dtlOk+p=Wb7_WvSH@eN2mwE^&+H16#Rzfq5y}K{~w&ZzZ zkxF+ev2y@LGftj21=L`q;@$`-x8p z@X=nZXuJWwnxbe0wklod+RKv|(oOuZZV9*;ccn^P-#+A?3!sdzE4 zMX}!_^>k26y{ACYXlAJ?z*f516sKEAoer#Ipw!XIl+T}0>c=nA__qbA_MEF7RKIud zlq}`wm(fGn(ShD;(9>Pb@7RC=yjYQpvmYe4#PiL%5TKX6Mt!er`uFP)4LCcRtzT_$qs7mM0R8 z{o?r;+u^lpmugQx<^XFoo}_zoovuCG8^k|HYMYa&aa5=4GYzgp4bE4chL2iKm#KXk z56(`_gavdXpwmg5ZTc#Wj-Q!lhV+#7>{NNCes*duB<_F(GyNk#(Ky*TP{xB{@lQ75 z7C%z0mMUo8p~ir3wUH=c341S>TGOz28L$VDx5f40eb^jNe_%}2kr)D&!1@>Rx*g5X3%Yn(m~QTGMq%Yqp}@UX(-CKbP)9@ zI%}yKM17xD|G9&tALR~`zEwF$+9@b*ot1;At&O?Rjfc;1|1ISy1R(l@)A}6;G3H| z9{)EOi4TC$S@YGY>p`tI*zZAogumITa+jpd+1EB`(sz`4ht{@(K0H;aeyfRkx1VRu za!cLQw3b1?sD~$^ufxf9IN1&-+cD^4$kG$-PQynvZEbl3vRysO_8u%EyTg;pcs8{S zs`GfZ*ixF1donG96u8gB-%;_UXgO&g;>0WaRCwbJSYMQV9;oexnhJ_aRFwTwP^Vxe zU6eh0Fywwg)O=`oAAd#JbCLRy;hTgw*;14(BV1AT!-(JdB#nCF-O$paY#FJGvdPc? z1aGOa-n6=G@oA|k$C2=9X*T)vTCCa13?KPrhfmLD1C(QjUyl-f#gvG~*AcIfE#&b8 zViiVW{wAV+xZA$ZRFwS+QVPLW>GTz4UkL1Z{8bx_JX!55sLqzXON~h-OKY-k?2p@g zu&Qyuu64LWh2CA0eJ3!wcT<#oD|DqIkDBZoct)&svbC<1wJwj^?Bg=9Zi2o#XVprz z8f7~KWm~D%f*MJbnJT*l9X8j0 z3lh2LjTtUA+fjvvBA*rxre7<%4z?5yJ_6Kp)c$&;9uMp%CiN}&>H%0)?6l+#eh+z+ zl3xaY3~Dy0+`-+TXyh+(d^7#DL+o>_N{3ZB>dn+4NTojbYHH0rO5G06Rh!iF(T1){ zGzyUJ}DOm~>+1Z{50H=ysgMjyTP@r0phZSf+g znf@_Ir8U=msq(g6mU+5FvtQR>88N#C(};QHO2S5NM$17tx&}+{&P@C6pOiWQB?|lW zo)jwi=lE+fvj&~|bq$t&-ZgmYv36AH8Z142Pl~kD>>)BkWDk*bN%j!8{u?4Ykz9k3 z$JeC(1y(%@tMUy-^W}I|jIz;PjRJ$w7*Xh`B1hFZs@PGbjw*9hlcSm)Rqm(>sbi4) zYpumy974aa^9ovL=a6rMqBE4vAv6}RFxbKXZ!l6na$0hSP|KmUa+kvz4fO-G zk_v})J8DDf=_uv!TIZX<7DA>6ZmV zXQK_6dqzWLO<3qsD_w5Yjw%_lmG@Rvj;ih_r(o4)Wod5n4V4-!byV3m4Emr; zt#MSHqZ%C5=%}WlQlci~7g~#cmU=fd4}b-=LuJKNZzQNU)(`DwzZ?}`4L+Jzv(n^7 za-O#i7@hi!^)`UYhL*A38=#JaN9+yB1Aq-n8~mnH`gy|Wovwp{b(%bA7HZ6X<2a?B zyql;=1$Yi-s270I=-6%eUV58SrN6~B!f<2|NRo-K)`icXdw7VQC-2<23hs@lQcr4(hP9#Gw{TH*aTN z4eU+x)+N0~S7+XHpnmzLHxPVuXFD&IsB6Jz+gjKr5B~%44MXbnNS$lm&{}Nx1bfpP z$*Fm%LcQsYb67~732d3M@@NH@U%*d-WOr?Zg3K|&zz4wW8biR9un)k%yfGfG0&?-9+mK2dhx{U>O))L%ih7^^5Xk;uZn`8J%= zwK+=@iIKoo1MBL6O$7ENuy};uFpzkr;^i z>3C?VbbKAgdKy(aat5(PM@~Obw0?;jK1z)b`x93BG~36AQD3Fhf??8ALaM|`lngtf z7hj{vjZRn^O%FAr+5bb`okurOzyIH#&;n&ISX_X@q^$~yJMO3xiwlY=Dk=!-#G-=Y zg5rXrPAn=YE+{StYGQFgamO7sv3yhncid4EiwlYiiVKQ*Kd)CavB&fM-oN{IKKyeZ z&)e&f>%Gi-CNtAyG6|c7?-u73XEj-Oo!av;zny)Tw(}ZZ)$4n+8=>i{bwJVEjxuoQU!c5c7GITaQhPHnnwcNL4 zTf>f?wDUQ+Db#joXGc^6TDw_zM74a+W*WIZy~5mxUg2KXa!YRC2}{vGTU{vDjq zD?C2BxA~4`+)KMD#dcGR?edYe)astaVviYGhCMTr)WcqL%_{bo(W~8-EuW-!Xwwzh z)#lfau*)g-Smp#9BmS0|c-UexYwhadLI<#=iWZREr zyegK~q5)`0@u+ySs1J(D7uB*~z0Punwzp45K0sV=XFR)R)ba|TncSq(@Dth9#Vxe9 zs6M^Jy{WXQfxW}O_BFl3y=k@9$X=jw(KH=7A`LwF!N6zxCa#t~Ddq-8% zDrcWyb)sF`nMLimM$_%6C%Klj>?cL-^#Of{73Vjms0l?)DQa-U1@3f$#Q9Fc!%k*;u?)9>e|u@ z-l5jN$jHT9%e&MAc$eCuC>GMZL#2NW0Gc7J-QlCxvR?o-$J)2R?O>8@#ZEEASd;(}&a$d1^RU7-&mtClx zRm{yTYPn=HSl-8e@w^VLXjOX+x&2i;n&lqLR{Q4@*=c2;a5QbgtN+#NVefWU_X%Ii z|I^MZvClcT;nTvIk%yS6y%R324ELsm#ig};nzk=9UDPUfz%<9PT+1gb!%Xd-U=(}( z*IA?3>?c&@_Hmp~xcW4qBD|J1p<*Uy(3aZ2sr{PTipp@mpHQ)yoLy;K)BY{Svujv= zisQ{}8~%z%pUU4^bGx=Qu<{ey+1}Kr@?&!Acor%u!_Pz_WnUCViIjbh4r8Vxiffr} zdk%BCm2K6zGCTu!t_>5471jSe)?c|jidsSsn?d)o&yj7lWv!NBS0N^|jI~-f z`^&kqxNfT}!=rX}W%#aRdF3|D;7ClqHjrKlN2%_?ec<+5K}&LDFuKSXv0nOCe`P}IQUG8Pqc zlO#JMO{#pX(06JvH?^{zj2= zXo5zuSF|nPz_VZGiE5vGm0IrX;(D>2H|F+zJ=St{W^Ug+`iyhJyuS7g(6?weuW$If z3k!<51;yOFzTq!PEGTMm-}Qy@7Wb{%_rD)+Mc=&`#jYN$=v%)5##`I>lEQdv``)tw z#_Qbg<-&NK`+c_o#;fTUJ|8vxI@JF6+i!4jyutmV8(_Q<#qmb;i*11MCiFXkQSAAh z&~NMp7;kF7a|+{4?RUur7;kRBS%vZD_IqXnjJL2jzlHtY+5qD%>-TA4e#`o`8}i@p zFDv_P$|&}Htn9bb1{iOBzXJ>7t?xHt1B_SUjxUT?;a;=>#vABfUl?zod*=oiZ-^W2 ze?#0SHo$meisOxO?MV4=&(D+GrG@pKl1xo z1B^E)`a)s6LD4riz<9%=Ulhh07X5hxj5jvgZU2_%V{EiP`frc7DbZMAyeZN61{iNv zG(6sBMMrOd@fH-vTM*q4=ljLc(+lgnIC|p-nBR)%gN5-{M4#UP|rsHz>Gs{Xh8>9FGZ4y)?50md6!WoK_Y zuZ^wRb_0wzrRqsWv0pQtP!+z%o>CS5*6F%-=kfk%1@BnawYvh@r@3qO{>a{CjVk^Q z(4obiaYL z6D_I=ugy)@?=#pZ&>eK2dD&0A->S0ry7tNZ8mH@kmfxdIwh4cac5zksss6fB`<;ny z^cbYyE3hjGiME^3!>&Lk+S)h1Y~3bp8s1I1yeia+s#kbVXW#u_Q5C+sLtGoyd)?wE zk1MLeD;+DU!cQ_M6+hEl-m2E-Z0{hKw;IKAx-O+PG7ZvqWNjIPI(7b~<-PcV;`eZ; zb_%b(E-JR0*ZIUkyG6xsO06z#-9g1~r3}$$V>a^ostlnw$EO**6JB4n2RXa) zw7zQpqK-qh7q726yP$vW^H^Usi=2H=S6?(H?z;rB2G6}76QihaZOh*i$6$hO9+ zlG=j07k_7GnC@$~Eg9ACBHE{x!>YqOuYz=jV-S4;EHou*AFODi|aeJxV}>>!o6f_MN?tT zr&jD+&YgFRGPt6P>o|YuuD{icQ2RPZK=B^0sTHG`!LGC$q;K=uaWu8!WO75uO|3W^ z**2J3aT&5X53VR>gu^M5iraK*#f{V+LhZ7O@E(>K72&n;85QAq+0(P}$YH^D%>}H?**mj;!9bS7KTyYEQ(S;dABlZjEQMP@yo&9E4f3&bV zyc=ynbvT!K+B3$m9Czt7&vHOCS zRfqQqEvpXiA6isA0+tuIOUpfXgBWj0ag-Is+LcAEEvj=(c;=d0{9eq|;yI^tO?bu} zqdQ1!yO1lKJ3H5e=dcQmVtf18;$1IeHVN z->|fQ+6}3B;|P9%m02+tWar7b`hKIG55^SNZEQ{W>H3tK@IHW+zrRPb+}Wk}ix^|k z{NiVtgL;iDTm!AqCw6v!_N@%w-Jp0Tl&Tm#t_*vVm8vA#b>{GW{HQ}djvuds+ zXMY1`)r6nF&8o3~-`hTkn^p7JAC7YwTWD6z^M!UZYQlYTR?S=F%GoZnYF43slAXB* zNI#Q%NK3ObSBrjX({jF=RTG|(h7>>Z8eG(n-r?WI!M(38tna*<@YAJvHQ}dAi;Mf| z;+nUP;Y`MIm(_%y0WB=fba733D0hGIEG(-DKMh)3Gl`@25cbaH#r<@3&2l~~8p$ZD zYreNnjp(tk@~grO=9h(^r;LwW%n@qeT^gm&iR}vW?vX3W*{`h6Dt>ol(Z=DndboHc zI#JY9MP-W`U)1EHmPjYFAG|5qUiX1yM_#_DnI(4A+FVvCXG#{eMzXzlVllU_sEEE- zX|)}S->~d1*(jS8b5Y5@=P*E8`3kT9^qIA_+d;XB%yf6jzWWf9&LcNevN<1G)Tp8o z(p1`wFXko|HMyunacL8aN)~IUOST>}r7Ib)MK-_L%3Vh;UFj~Z{O8*%#oRxyZvN4f&BE8kQ;WxbSAG>{s>i*1j_=%}IM(SF zv&Z9{EU^dw+R*jn%lJ1Ne&ul+XNhyO+O2V3RJ$NzpB{8{rbgOwExoJra-^%`$TnLm zj%hPi(Qh-ESmtEfOxNSZZLU^a+vYyS-`YH)*r()8MYm+F;>xyNxrSE8Z=wz$_HcT( zn@YaS*|goZJnrg@Y`2s0k@hc<@8Ps?^vg~@*KULONk2meGNU=|c_Y~a@ z>xpGf{|;LWu{f~9*2Ff>sU141eqo30^|)=v&T2QMW3Ac+9qlSa%0PV1FVbt>7<>gRPTQ@p2B7sUmgdMG~BX%oeTo%$%Q z=oBS(b#k4yP+ZxmR`IJ&XDF`jlvezq(<6#&J2ff(*2%86baf8w+?IErU3uNxOL2JT z-4#c4_7%_TJds%D%;!B+@?6IOQYh=UG7nw+vR>@S0~lw z5yg33-d4P)%Sy!sUDgoWI8SvsR@-%T7kdv}<_zh&z2bpgcUBzMbq~eiUH2ifA9fw0 zIJfKd%4fRXqqw;1{faMleME6t*C!NLcU`3TL)Wb0+O97u{?>IVv5j*`H($@A-|Y<^ zGk&*sRR3hRjSsZ>Eb7){I@?qFdgFzYblUn({pr`cR;s}#NRS&DooS@FQ~+Z1P%+h3io&b8&s6=#*NQM|d_Imq(&ly9WC zpnS06L*>U2+c+PTAFt=BtouymJ9ocL@x1PjDX!@L9I?z9+~Z3{ug7ZThxAyZcwmpQ z2iy3=dc3DNyvISq?D2>m&nk}Wu|0pIyYd~vMq(Rhau53~iT$I;iHfII%v9WFL zoKSg-;u)1qijyjTR6MV;JMX-@I#VjQQVc4GDo(8&N9^IOsk~a-ZNI9EbUY@i0>u-m zE>S$YYO3P8sw)*E)mIUFI6G{)wT_eS1EwqX9B{Sbt=n9}^WDSgzU}3TmD|ox9JRxJ zipT8m5V4K3^KLHNb6bb^oAJ|`iK%z|?1HM}$%3|y&nRf8_;m&C8NZ{T+W6lE9ULz^ ztYwtL=Mnlnde;6?95`vw|ApyA*U~{Lq4~ zkB>n$~}vk(0%b23VJmDQbBX#Wg}Qx8=FBF#P9H;9w;eo zf*Q~>@jfW6+z^zNhN31kIvz)P-0x_pa(|}-$d&paPl_U6+5#n{tx+ItkCM{PC?)NI($YRCD-A(;X()=c zv!%tYRyy2jrIA+K-fE9QQR#T(Nhcv+nt%dnB5FX5@pDj0xd3IPOHod`0yU%2@oNw# zU6wHmxzcQumTp5?sS!1!(eZl_{~Rbi?ne>n5#&lwps2J6c~Ta|q!&>_T8fgAy?0GZ z@1RCBI{qQbD)%XBQf?*6E4KNi#PNUq2^3ojCjGE&qv=V(2 zzY{r~Y>naDyN&lW zib~I;n6wxrq$Q{xjgG&DlFGe}($f1VD}9Xe(q|~r!$$rRMWxj!CapoWXmtD+lu+&u zl$4y2j4ZV^ZDgZ#L|LgD%1ad}Qem~5p{Uf?)YEd+C?@qs327UYly*dEX;+k$2BW+b zLy3)Tl>JdsIvAy;;V3H|ftH}A_|XOZ6h9uVQtm@kvWczbm#713imyfkq;)8U=EU2L zVr!s3;+@b?)oxzUPw`z*LhT+z<5jy5C6!x(n$ZRE*HFo(rsb$w`Vb}1oOli;rO#1X z`WiK&(edw5R=KsPdNa$dLoqZb{ufF}C47e`DV3qL)CFaw9;gYG)NO+D%Jo5!UN%Y; zMWroJOxhaNq6^~Nql9w1pro`XN=qKfO8cR_bP#GrC3STu(%Y6X0!5`!C?*|?5>h=% zO5;#kIu&K5Gf`eT7d4~N@hK?M$3{+~sB{^MNmrs;bV2-Dlu+(Ql$7S61~fXJLTTmh zL|N%xl$RbvkxE;}LR2C>g`(1PC?++bg!D2>O0S}n^cG4>@1aICI=%vBl*^;6^aaXE z-=Mto16ql`iT{k8zBZTN5&uvu`Wv}Y>1bvpbwHlf6~&~D&=B-Zd{g8rSBVl*6$+#+ zQBoR+($WqnEA4{v(w->N&t~AEsI(u7Ne3ZcszV8B1ge)t74&HQ*n;kh-;M&+&O=FQ zK1xXspp5h=YC`wLpDbuj{8?1(+BSUwHKWG(OQ<$#xpz^c^byJ+jyKe#+~+8#+}9{C zeUF+|yB0ZB)?*!VrN59Tm5gB-Xmq>``O0-cfz$&vpbO%gpp|i=xsCs9Jgn#iV7ZR(ca9q<2xh^brcs=y(n#mHQkuNMED0WLMl9rL`z4twT-H zUnnn?9K%edG8Ea;X4(anNIg(g+5}ZgeNaq_qFQMSl#sSY_0skzDea6Jq&-kd+6Sej zA*fLriZW6hWu?PWlQa_LrDIUDbUcdmw>h7LN~8%WDosSy(mBYJE+pX*Oz*ZbNCQ5j9Hpp!w*h`27Vn#rq%2dZ=~>)FkbKmY}Bio+z)}0jOCT zhE}QeP!t(p>vk-PO7*B(8i!)isi;;u6D6c`QN1(;C8Z>4kS;@M=}OcnU5m2P4X8=F z1?8pNQL{7;MYgh8%||8D11KszimIh2QA~Ok)k-g*g!B@smzJTV^d@SM-bHEYBh)D6 zP*(aJHA!Eiy!1VaY;Chzi%O(*$d&#=QK{rO<|maQPwIkVQV&!sZGwEM4@yW;R4;9T z0%>cMl(t6=(#|L)?SazLKB!R|f-=%jl$GMBNje(5u9fO*sB^dpn9q6HtjX z5xLSiC@Nims-*yV(xoUSU4eY*8kCS`p?YaH3Z&amQffpE(mg08-H+1JBdAe&0%fE{ zC@W=Alk_6WNlQ^)dIL2}?;vLzTgwj-{~$a16qQITkt?l2QRzG6Nk5^O^c$*`{zM6> zjnBGC?NK0gMoFnVYLGTYDXBL~OD<}ZHb)t0E0mMAL(aA~(?KXA?T$*My^$-`qNsEr z@}xshOgaqJN=G4I8iNv20@X_=qNH>RYLL!ADd}vKmd;0w(#0qvO+{I0I%<-xMmgzv zl$UNs&C;#N+0NGO4iu5@MkUe$R4qM>V$$QNR(b{{qztN;{)3XzE2u$w9i^q^C@Xz{ z^3o@$S!za+?QJe!p%Up^6qSBN)zYshCap)cQY67#q;{x5>Vz7la?~vKL?t`eGJ2tE zsUND9YEXkT0HvjEQKPgI%1XPTCTTB}m-a=?(g7&4qb*|?Dv=IFQRzrjEsaJo$w#%) z2`C|*jOwM+QBs)t zgObuOs6qMzr6uQhmMgVIS*asxlDeV1RDqf$yFVeavn{tTDv_#DRO*karEO45+7Z=C zyP||N7}ZNLl$7>I4bs6VEe%JF(h(>t9gUi#<4|52i<+hJD6)&q?=(~*orOwKr@He{ zRJjXLwR8!JNtdG`YBvKVl$(i?(v7G=nuF3(3N=c1qO5c;YLXsAd1)bPmYzb9U2T5P zp%SSHMWvTfwe%{ANpGQA={=N?R-k$*kCM_Cs6qM$rKKNGqx3V%O24Bf>2H*mO6!@Q z)B#0yv-x#JCDKMHDs76YrAic&s!&4O64grsQBv9gHAuUlw6rH`lsuG`_Crn5K`1ZP zp=N0WitKK)8ih)vV^LJ9N7d3e6q8OxwbGd=A)Slrr70*WB~gQP8A?l6qDJXjl$CBk zP0}qWFWrusrFkf_hs|$3Dv=&Q)zYJ=R(cX8q-RmR^a4ssFQEo$8ETZ?L`~AWs9E|5 zmF#It%b{xNa}<-lMzzxSC?Tyy_0l?&l>R~uQps4>Tq;A2QWun!dY~q06O@F~EM1D6y=*eHAx?$ob)NmODj>cv&{2MayO!S zX$~5Xs_RlHQ0_j|AU%Xq(qpJmdKzV<=TVci80DlTs9AaqIiAh=ZB!z?k6h_vR4sjm zJn2hRE3HPpvZ&sV{P3Hos~Vk@}+& zX&dB9JECf7SL8{9QLPk1zO+B8mkvgOG#oWZN1*Ab*Fi@Y)D*8rDb-F!jnYMEKH9pj z0cDh%hMJ_S&=Sn8O=tI$vJeg*Zey9GJ>+WOv&N~8s-1L|G( zFmjb!grZUwRZB0T0qCdr(t>)|?LUsKq1wYyOd5%5rDM=g)VuC@kZwZL)ow0IDYpQnrH4_Y^f;QYcF&-!axbGM=~c8uwQr%kazCJE z>1VVG?Ns-BLA~po@ocqP+iJa0L~>Dyv^lcx#{U%Gs-WI=cOqA{kE5vc46^(Dde>!; zr`)S3CcTAfrT5TKwOdh8uY z4ybqC-N;q$S!8!U&56H&s->6E0JU3&?0zU-^P{24eTjVK)}n;84vkmsUno$n(=A5w2L9rZfMEvUL~KtX%f9Z=M%D6Jl|(0sIY-E6c3?OAtQ zLDhAO(JIxhMA7|iE~}7z`;)(oC?WlXlG1M|E&YkIQkzpbKBV?2a)9;ijA93xx+7oO z7$v0MC@HxpEp3i+2U(A;kaMtUJLF1(kSFbqd}(hKNVO<3%z7M%qS7HKCLM+n(orZW zjX`NCfwIzxC@-CYoI`B9Gf+f28%3q_QB1lRC8en-QfED;qo{N>ib>a_gmg3VhFiN^ zkuTkWlG5EMB`rW{>0y+S9!HTwt?x4^DrHdYFw6Z1C8SqSQhFVwrR69qeSq@PCn$2b z^=(G3^c9Lq-y%=?5yhlmkuR-B2`O?K&w|trC8bU%C6%MJ)DuOHu%-2~c2Ym9m1~9Asv7MX&6dMhoY2pq_sQB#v5(5l8-Xd2`DF> zjGU3y?sVizlaMD(M!s|r3P#!IDGew&+ImbwY3VAIm99g1=_V8zW9{amsFX%A=`NIz z?n6oGA(WOLLs{u*7F-k~FP$0dA?8-f_s!&RLAKBI8rufHbJnCKdBg&vT z@yO|H1F0Qye9Lu0u2haZsVDNKUML~;L*voXcnu1a+Xf}29no~v?ut^%c_=OIhcePZ zC@a;WoHPRErBTR9*sP945vd-z(l`{APDP${CW=YtB43(<5>gTc(q$+qU5QfCwJ0s! zfHKl8C@bBLa?(7Mm*ykqc$?n?C?Y+IT%s8QHrV zj#U&$lTcEcj8f7?C@nRhj5G~prK?a*x(?-~n~-y&jXW1cq%?A+yHHfR4|&o<$X?^m zi9d#X>1mXZo=1VS7$v18C?&my($d=~BfXEZ(#I$#eTMSVm&oB2_MG@?6p_{-SNa7- zr9Y4-ITM+m)E4>2j2nW}uuj6Xm5FQJPnDbK-N* z64Vq=74%d5UTde?&Cg`cr&znKP*mCuC8R+pDeaEZ(%vW|)uOC)Aj(OHAZLP&cNlV| zqmUvXCsHdvUB3+BUic@dD2wmOVd#xU5!%G^(Z6VjB?Vg zD8`wd&tgzQx*H{>1<3vq&51vZ?C-#w_~R%qJ!3icqdD;ma?$Ace~=yjbKQa! zhM|~rC`w32qNFq$r6nI_r4vx>Y#Z-nl#otGNokU`JI87#qnvaR^3Szg0}7;RC?#Em zGSYP@a-Oxj2}Py3C??q&CZxMiQo0YNrH4>PdJJWyr%_IN9_6LQh<`MmWh_Av={4j^ zZ=SYLuu(kl#wn$Iq7obTxfHdfm~@Oib^*kPnv^#DTOl9ohT>W zi=2yWln0S3EkwTb6bhv0P*Q3_Dd}aDmR>~}=`EC%-a|QQ1Ui zz6)}tJ&`AQ$d~p*fpic`Np&bAjX*hR6ml-H@s35VRF6Dq9P*`8Q6Qa(Qqs97BTYd$ z$<7Z>gN=6?a-}PgCtZt@(hVpr-GVaG?IVcfgY-yXIh|~wUQWQm{Es!T|jbhUF$d`6T326@$Nc*6aGz6ukp(rE8QC2z} z<)o1)FCBxN%WYQ2qlk18a-|6R3s4{hC@EcvQqmPDEnR~$(kzsfW}}>R z8_G+K$eCtyxd%n0`;jX>f}+wB$deW!U$VbSf%GCuNlQ^$dIM#ocTiUP5apy#QC?b! zBGYXKtB@;whoaI?$di6UG3ig_OKtcomyp_{K4x)?>JsVF8*M+xa_l$5SVY3XJZnPGizMN#Pv6qD{o326aJN)My7^f<~& z&!D_yuP|a)*?9j!3F#G-lwL<^X*tSDAE2D{3Cc^&$hq1^`3gm(Z;>ngh@#T3$dlHi zm=xjlhcC55fz%15q;iyzdZL`v3pv-=()uA+szIJK0Qu6kC?V~H0%bFIz5M-k}+KQRiX$wxULGh z%59CJ%59H4<#tCg<@QFtatEMG!d+z}|L+|ekdTs=xFHx6Z#n~1W?orChy#VB%< z&1D*jN;6SRx&JTa6OZ z&nPMVh0;=cUMFYJoOn6PD%TU`lqxqm*)IqO@}7qKt9@$|`p$$|-j>$}4w0iriwenv0^+ohT+f zfD+OZC@E!7T3Uj#(pxAmeS{)&Y`m2yDt(J$(l00>{fz>|_k=HCWaYY`lyV!Rv~s;s zM!6c4Rc-*vOFJTGu8p!MilBq*JQS6NqL_3TN=TzoQW}fW(y1sborChy#VB&CjW-QN zrI{!u-GUNQBT7mOP+EEnWu@m(UU~^dZnN>;KvC%f6q7zf3F#XYAnu(;N#)j|wA5w_ zBTJo7PVFjCUb)SXbG!AeLJ@Ru-ImBzZhI6}ZfE2vw>OF@SBuih4MSP!P?VRBM3Iz@ zJQ`&rALXPIkbNng_ua^qPDh?J3Hj1w6i64Ll+=JS(lnHlu0q}&Hr{o}mu^CVG#8~L zdl#3H?m{`~KIAl7--nPZJ%&8#Y2-`Kqd;1WQqmHXkzPYN>22i9v+>?XuJkeTq|Z=H z`V#rlYLt-Hpg{TsrKCSlMshA>8B$y1+-b|`h+L@~@}vsnOPirU>WflRHOff+QBK+h zId|E3J0e%w6?xKNM z6!N6!kS{f%KzbRaq*qZ!dJE;G_mJ~|jkf~1QXY9|bo>j`!&dtZ@}(b8?s3cgjGQM- zzav-r8+lUcMf8w5pg`)1Qqo2!BW;S@CvB8U)=MgOD%Pp%fY&AAvH`D3p_qMb2|JO1;%)OyjIpI@M~kmOB%<(z(c!rXXLk zD<6S$8A?f4qKtGca$c}eZa}Vd3-YAfkuS|dfixebqz6z&dKBfPCy~=+<2{R9=>_CT zFCkxAW?Euv{w4~fcToz>iGPGL%Gp(zob)+zUa=luBUkz!dD2?sOY2Yq@%>tqlu9n< z)G3vrtkebNr5?yxYCSeV{xVY^6i88&lD0q@X=~)XYVEd1uCz1qq&<)??SnGX5R{XK zBKsGy`GpANN{3tR>y{g7wQrb?v0CYPAt);yhJsbr z?kJR!#-NOpKso6|482J)n{kuRN(0_kFulBS}JG~IIF*eF+2Z{io@U-|C?WlblG3jzEv-ixCqlbOl2N2~$cb346LO_; zhv@i0c1CTEbLxFTCN=Zke zj5HeMB;QnKIaXF2I6 zl#%A5oRqd)PiuFV<)r(Nv$5qKLay`}@}#Fxz`p~_eI6(!Ek+q>3Cc;YA+MLUdmH)E z`zR%Sj55+^C?|c1oZi-BHFBjj$di6SzVrtQBWyQ0PDoA_XqQ!a*_8f&*da;1ZjCk;owbOZ{d zqftsa4rQdVC?}0a&gM4WX~>n%LY{OU@}&!{-Imtw5^IM>$1k^b(hO@S%|wB8BT7kg zP)4%r`9Xghkt=T8|9?Z2FBaQ#_NDwsVnlNjgT*GiUO$;rKBp9 zk+wuRX&`d8wefa9uCxpCq&<-@*{2(Uv>!@I2ce8qhjP*go=_TYGV7X<;mEJ_2^e*zHk5Fc)wX@H9a? zImmKfBUkz!dD2?sOY2Y|{e@Ce$yCOZ%1}<~f}DeGydKDvHbI`$2l-MI1=1EMH_Uo$ zjhsVF+ap)n8F|tk$d~p(fi%Q&b=G63<)k=DNr$71G!o^cV~{i4dK`~j=_J#k_AE?5 zzBCb~q;pV4x&Y;*06B+Qk4up&U4eY*8Wc#gP)eGOGSY3uYSnF{V+~=KWjJHMyWyW6w?6YN!wbjv=a)X z-B3!}3uUBzQBFDlITzX}!;mW-iahB^l#)iHjO3%7bOLfNvc4yqE;gNxTxk;Wq{+ya zE<%CSfKt*ll##ANIq5pf1-9*PvYa#**^g3=j;E0;-Gw~qKIBUep+I`fYA>-KPg||@ zJjzImQBGQd>_;y7%`@amZ=(Ql{ST$2k5NYY4CSOR5kFT=yVb~*)*$aP8}AnsNPnP| z%}_?_i*iyma;90|{>YWKL7ucD^3mw{t|*WOqm&dw z8EJo%lMY7Cbn7u3xzZ8Hla5BdbQ}t#u_z^tM;YlfOor*l^Oyo=FqClF0Qc@CSq|1!jnEnKH~bH)YlEmLauAfz%nLr0&SM&)RK_T&XwmBp3P8<|rd= zg>uq%$XQ^02O(G59R2>5hVttn*SNZ@2(kCb-HKUC570OB9BJWY_`y=wD zUr`{fM=2?CCCgZ7?b;z%>V!P09QjgDl#zO&oYW6Fk6GUuQZ+!%#{(6gf{=yCab+jYgj2BVRfJWu%i)PC6YqPg>te$dx9eK)MK} zqz06crlFj474n|4zSkjNx(Nl+T$GaRek|u{Yj+oNrTdU4J%oJeF_e*>Mmgzu?8R>nLlRie?BJ2AZ@})0PAgx9zX$^9owRXQCSNa2ak~4$8Qd^Xf zI-;D^4LQ$Q-wNbPo1sAJi&9cG%1HfDPTB@}&s*OekuU9v0%LxFS_N=fITjC3J#U$7pRAWym+`O*v&NHbAR zx)Iq=%^zMj2f0!TdD5LICEbfM(t{``Ekwa$`-}6GwUeH+c2bkIlU_#7f2`fB$d%qg zp7b8_r4=ZL4zJ52=S9;O)=v7y+DSj4l=L&oNWUZZCF}7w@}$zMn4i=ErKGMXCvAlI z=Wpn-spVd=`BhqOsi_LN(w4}R23l^J_1M93(k>{F_CzVkLpfe^R1oqfVF$u+C6IRq$iOtJ&OYA1(aKE?OsC8JEmpGliozW z^e#$CAEAt7cVOnE&yn-4_5B)o()Y-h)}oZO4rQdjkn^7PD7l*XNoB~Fx}ZSnfilu2 zC@1wn&imFkid<<6w!YnwD{YK?sW%EF7iFZ) zQBK+lIp12}?T{-CLcX*+3Z%VJMyf?Q=|HRf&iWpLQqo~oD;3o!vE=JBz)?=#aXVY}#N>?Lax*i46%_t+?igMB&$o<9o-i$dyJTPqI6xedz=gNGDru zqz%hB-D;&tC?!os8R;VAw6S&#$d#s{pu}=lp_FtT%1JjNr_^faB2ThAvVG|;YuCYM zdY`rHXnF{_(qqVzo<_d(JPM@6C?_pJPABX88giw#QL3{o_kEO+KDKsUEccnUlfFd0 zv>FA{8kCWKK{@FULjMN|Hq-~JX z-TLl`TxnP2p~LG2BVUT4l(av}NCzXQhqW7yT(FWre!(!D4nJ!sm*dMrda z=_%xHYPsi-CpDo!dKsmpSFLt4YxfrNrT45>T7fcB9yz_N-51D}zCpQ4%l%-reN8_j zPx>AC(%&d0m0rhqQU{c)w&$%Ya%xN)Ay?WIc~T_`q$-q>wnQ0eAj(NQpolYup{m+M zoOZK-m^&1w1fK=&soaEG2J!Y|dfc+P_4b*1316xAFdO#%bNZ4R;82 zru3wI!`QYgE1md$#x~@_w*QVhQ@1B2+@2nOg?{@Jt^cvalPD7?Hs>>m=Ta`9SpCI? z$CkT{awX*&inR^fT+ichK9-+dcx<-(?S=N{f7gX$%q#S<=fwJ1SwPMn+d78j33BI9 zXY2VKevx9^Bb?LHLVXzD;Bh#fMcYTLgyV+M>hguQHcxx}C68@CTTKjewp^?G_wv@Z zTF$zHcVR!U5-zg?_jXy|u!Q+=8+50B6Uq;F{xg0b9_Jaumb)b$NSS&wKPhl4`*&E! z_OUT-j@Itq&&N>O)y?LEZ=x;pkURL85nCBY{v6`P6w6O1p2~iG7tzX5h5GyObe8)C zbFuj>;qhw}+kRHw#_v;BP;6XVPBXEY<8T%6JIap~8}m2HpOgr9v)S|7ju`HrVeCw9 zW2cf3$8`|$d*`IPb+iDnb z6lE>X)v?5rDdD-`Y#ime{Qnv+roBC%w*O7X;r@DcA%7jQgy-=NqMiG!++E0Rimh%D zkF$l`tHie`*6thP50sxNe^G27wqkYRa!UT>uL)%!#m?7OHwat5-HG=2pu*!i;!%a% zm_lwG(Z&wmbdiLRY zEhkz(`-;y|p zGMsjM5ufHcvHi;S1$(X!CU+KP9p`V$O~F@DZlT;sv2pJwhJSOOz>6p^w947}Fg%xQ zE_&0gwK5#pCtDp9W_zM-({R7ojmQ7)w->p6*;ck6 z+HoE3&m+j&^E8S$jbdZWB&I1(79QI+wef9V*_RkTAODVvX}^Z!E{w16*p_3@e`|-Y zeO{+N+*d!upHfy9`q+8XmT4t?&0xpK@6=l^V($(pmb2G0-LQ@^9*5g`Q*wPN)f7AD z+UpJLYwNomIr|o-<##K1Z{kpjwKig(CvVY85^uzLpps(yew6s{ z^WeW`Qkh6WR3iqdML$Tx2mTBjW-FO@;l>Pq|hw=Cr%2Y`-n-2T^vXT+gv`0CCv=p^T;8{#J&^+$lV^ z=b?4(EOK^SPA1xVgl&U=J+}Nba{IAetjxrBQy!w6%4=nN&Yr^7&K#B|a<)u+{2I3S zHnEvvuX(KQ8*IzAdDv?u%l&)q|EI^lGls2~jbY1Zea>#}(zY$j+JScAy4tdPkpG|N zx(Ruk&;MzDTheYD%8r!XD0@-Dx!8O{4aM5Q zykeY7!t>$HfigFUB3#C|2t{(1Z5HBB$i>% zUl!ZCTCwB9N_f1#OPyE7`G7KjZEpMbq4;>pSrl7dK)jS<*T$|S-bAsn<`Exeo(~dj zIpH$G>uL5lbQ5jt@4sz7YilK(^Q+XI%Cf`dTR)ro=j1K6{(Jd7xt}S&Q*0e=zTtJb zk`67$jP27qV~hI|52n~PhVXU3@IpIl6V``yXOpvjVZ5O5*!tgu!~8w?0m`G4$^7lK z_fjunoBL9t&CAx+%G*5tpWcIgKt62q&^(US!ei?jzE}E|oE?uguDyTS-wcpSPK51?#I*^^?&yGLw&e21?K!t+rb^&Z^7HVPM0-E@Ng=k#cBK}ITb56j%_X2gc zjP3=u?;LS@Qmn1zs|&WrVH@*-s$K~>ciLN)~@yY z6C3v>>ffS-&tdr5cm+8dr}bFY-_D=buNK?>RMVwpI}9X_qgeaa>k-a*ZL9pL)`#*n z+q8jrEv1fg@@(Scl$R*2y`0CZC=W8mkHqzqO}cU{ajw%G@F>dh6njoA-#UhKwYDeG zCS1nfBB}rBh5jgi--BsP2J)hs+XP@)`Td(_BPrdb4 z)vD@MRjU#s`7+*_{tPi0mNqZj`C0m;&N@5S1J3Xza-Ds{sZaE?30a1W7sSgl#Cy_* z>R*>@7KuksBXyMJN?hidmt64@dpPW6Sde*1JQ^n^^_KZN`{RRU^n9J|>-4XlVmVI> z94qDba)xUXYXr?fj`MAzhX0lC63&t7oW5iqyle}Z$6&l1bHzLT8mDxQDNgCcoqCR9 zTDfMBZ_A&9aSXo=^I#D;+sP@N_P>_x=J-#^+X)9j`pwe*PvCEZd5C&KOWKn3r!(M7 zfyAGTir2u)bd4#UW0zCs=<`^#Z$YlqOYXr*zr@+^ULr2%U)eqn-oJJF-qF_~=kA8? zgZ5-Pr~e}NMgQG3-DvVB!@s*8a{5T`ko!TH6_jaRx2+*A>*4H6&UKoUX54vRz~znTY2C=l9EtF9Ib&?lDTde0aRGOsA|t`~_$M(c3H9M{jFq+qNZt zIXHEYx>ulY_%Si*3w}Z=*Ot*3+OO18N~c~@kFCUZ!#D;vFv!cS1Ri%+ePlVmL(?3l6h6c%eX3(^+2YP9GS-17auGelGh%jkLT36 zE5ido`jgT3o1A0MP{zq}Wc{3Hm9G*T2NOW9nWTPlFKHIT!aP_MRlbLp`pNy{RpP_L zTPSzI7a;$#!bP|WvW@?wY{@*Oo`TdtN-I++$B9en9NQ#crp-o<^v`9S#Kb?iEu8C$ zg5*91l9yJjVt8IM4_VI|;o;fzTV4ozxz5c-+iB0RT<=Qm7`zjg;Wrun2;`oo(>Ger zu#6L&n9SFy$I+lYNxhuqI`hboIka5o8cg!?6DtfQ zK(<90N{KtqZk+w>zgs%zTIbr*DWmsSXP(j5oldM0^%xKHK-MkVJM&t}xOK1*q%R|V z0q0r1(~h0e*+;jLy9c7{@g>8~IprvRBirFDUAYvP^X`9-|6 zKWA9BpG+ga!F6cDd8!pTG7smsadMopcX-~CEA#Ts=Q@~}Og|@FE}@jVNzOt19lUT7 zFVi@jVOW+gIWqk<$)%K-wE1yaBkLhvVuF)DHEO)GPR{zsGNRkK#GRNky~L!R&agbY ziS85349{QI{T;lNQcqzS!_j@Vl?-nPvzhX%2l$R*nO@?~wvu|l{|ApRiHw(LT+(K<;8Q@>s{rMrAbs8F{!=Al za-S`l5#c=PBMWj3A>|n2Z-5L>372xMDcfJ_Df1RDr3^14XE*Evnf`dVbcV}hkM!eY zn#v5<1BuIUTT01I&+=qg=J6^qDJO<=oN@0k?4^$< z&hgJFA7*?>C=Zq4X=n~@ppy)Lb{xx>*MJDowMIK{YK~b?EF^FHB)+~El*!cuBV)SR5oH# zUx(Zb7l6VbZA9YF;Dx%-6eiJzMo>z-d7aYflRBmB!?La3V%*zNxoe_wH{hkuxP?;s zveCh$+$_Ba<5uSX-D%ZKZ3F0adLeYJ?<>y zF2EI#y7^LgF2geC;{$jpGg3O|Z09_mU1FTuQz+$q&H|LJpclLfPCrn_$+}M_wwT}D z`AwbiGQ5CsYhV*>hv@Ut7Yv^O=RWIcyzFOHnC?7Yjw#Y-lle$YO6Q)&pNyAf-lKdd zk5+lWc@N83AEzwHxafVX6vItH_6eu1&U3x!JXyE);XG-_(Y^=qzAy-e!)TDY3o@Ov z-$oxVqWk#H_z6rm1!jUQxvjQpnVa_>HiiN;wne!6NtoR)FNoZ@f0_oq9Yt>}>B%^A!bpKA?7nZo}pORmbT<2U?AMe~tXoBwoQg0AhNwSFf8>Iq<+qFWSksZ zqWhS;$(8y@j*R=7;fDEn?hUWN6p;8F%4P5=$i2M%lrq1cC@;cz{4L7AVLM)s@xB6~ z%BE;2M@l}79AVG^wMXW!f*N)&;Ddy6NyRAWJQ1j_Zt09@f{i0 z2jqF;0LoWj6i8c%W<0UCU>bY~&N7^BxsKRpuni=(H(Y*ADfN=!L*bZnU*Igm5_fVX z?+U}RTqox2ClYgxgSW}i4~LFr(cTx11t^{0!a24$Z6=9v&NY{FE#{Ot7*~L6$oiD_ zd%u+OK8zfC=%0(1QMSu1VsgJx6$*`$WjpH>Lo6%g36FE?R*>N$;C#DK5??lIIQrg1MdI@Q zQZvfdAm>xbm42<$KXRt;Kzs;{fCu}QV~9EVZ{gpD*{}eXfNV=ypB4DE@F~doTw;Q} zU$Bdql=~?6!(otm$h!$rFK60I#7oi#%=QS^^6)4~KT+CB8N5tWfwBtJfZE{Htv
g=2RqW&6vtg5$l9 zhUV$~&dzzp=`T2CJb7}C8N%@?6JFN66lGO-9%LIeqihed{kl-fGTxw+oH=16wg7*h z?YES22Yd-f;S4z2SJvk;!@t2_Aot1TeIZ{F_G^&7xcCep_eCG4l;frxTV?%ZoKO{N zg~vJPHm7gl9JicpU5~uBAoZ8uQ`%qjdCHmInXVJL^8UH=zLL~gmOYBNyw57n0tN9- z+i<3J$_MB98q-dL-&hyt-Gx~U+s~c3*W)~Emtn_CfAl+YWM1Nbi1HVs{2x)?6piG^ zNBL|~zCe^O8Regh^3O&2hEcw4lnMLL%KsAO zZ$$ZfQQluHvK=x-`8-j+Xq0~<%0C_D>qq&PQNBx*?-%8VNBK9R{ER5SD9W!4`_Cwy z>#Ov%b*KGoCobPB$TwD><7GdSecBl>-;bmfcb=Ue7h-}=Xg_@n5?_AKL4(Cj+IU+@0T|D z7xRezZJc*CJCfTKL`9jI40N7&UmMM zk8vM?ynC1C{L<04V7zQcxo<7=jDFun-gR;6B=5X9{xhaYYktnQbxL_|n^xX#rk8io zzNU2EOOt0^-!bg8>FD1;o`E^b{+^s4!0CU=y?bYz+=D+Go`+2DTrW7|Z!qppkbBg3 z!+DR)_|NMt_nW;XLUpOleJ`m~UWVn~ZHaK)c`sP5)1~c2*Y_!MK( z49BH^+7d7K`@}nKwGMTzW9J&BDmk^GA+!W}j^2(^j%jkN@^M^=QARaWmg=Cqs-uci zZ>yl1uF~_(Q%1F4Wl}$?tm=%)t}^IkrFdgqu39;RQ$CT#@SDWfdAKtgE%; zc~0EPI~vZr8ZLdl(EM_SODFE+IdLb?saF~D)2iQt^_Tol{X8Wne>3HO95!qpkMb+8 zis$>ZhuAt9RXV;*3#wd1QtYkt5Pw`ONi9+t)Do3heW0?aWo)sJ$XdfzTdR`QI+as> zO7>?euiB*YsjVu%+NmB^yHp{yM?Io`QiXYg?NN1Cl~AR1NmW)q#y{fzIR9oy8C6-A zSJiX{Rb4-&p3#-*Nmf-2bv4yUS63r+Ej32hQR8(zH9^-`vvdRXj($$!_{arMvXPEsn^UnHPuW|Gx^7R zW|_Ct95YGHHPiSj8*i&cX1e;o%u*klchqV#PpvTv)LOHUy?c>bZx*W!W(jv|-&3EN z_thrzf!b<5RNKrdwa=_pUzzplTl1;fZ#Jp#%x0d@Y*z=(4t2!rRNtFD>bTjbPMUAj z4`#nQZN5{#m;>s9IjAm~!|GS_Jzp;!Rll2K>Q8fA{bf$7zs-;8t~sObnV)&|a9&x~ zuPWZUq7tm@Dx>wA%4GenGF!J)7V9sS$GW5P@o8^mK3lD7-B-_ANK0a#Zf9lC?X8Tu6R&=Bv9jv!Rl#u%2K&s^7GV=t)*lJ;f@f zr&-1IbgP7(X_eHotx|ff^_ZS#mDUTa$Mr(1j9zS&)l02%`hDvOz04}Fms=I|3ag@C zWj(1^Tb1-$>nXk7s;ob;s_0Lxs`@jln%-^p93|ea7mc&ssh8&sINu&KjaGSVQ$C z>lJ<78m@0yBlT@-qP}BI)pxBK`o1+&YtI~Qd6sIgXN8XOtkiLyk9EMaTE}}f=tR#( z{g7vy&fwXuGkJFDte$;3yXUk{_MFqXJ?C{E&qbZjb4eHU{HhCiF6+XcE4qm1sxIca zrb~GK(xp82bZJk(l<{OWoa<`2 zu08Hudy0Oz_iQ)R9s36EBRe+Tja4YiJ1ll=i5vT`=B;Il)%LVITzAZMmvHOtvDaLu ztMjzOj%9S+!?;jh1=m$`T`ku&cAa&AYj1llzjB7(X}b=L4Y?k;FYMSb+_$zHi#uey zg|6G`x}&c9&2z2E2kL!MOUDg`TGM;i>57*6h-A30P zaoru)J@kw-mtwA~=DKFC>*u<+T({VD>s+ZTP{j<(8Qe0Qjb#+~r>bm}}o9wz3 zuG{LmL$15*y3EfxOMA?9wOrTLbz@vN({<}z_qFS;x-L-Dne(HrtLwUcu6xII+gx|p zb$_@nP|KNL3D?zeU0>Htaor}@9dq3s*X6G5%y3YJ^y6#EWwQ}8H z*Ufj`H?I5Db+PrF`IU5CP1iMZT|d`NcHJ`9ZF1ca*IjhoZP#V4?=0<6*Hw327uUV& zx|y!q>bk40iySd?S5)e#eZ2aUv7uv~9L+*5rUBPZ_Spaa6gimooIPzG+-2KEmR6Kl zXc-l8H|?0L&^h!Xd7pHt`EG{&cxvqBk+IRqtx$mMvQEs^=!3(9ovV?WV^_|^*yoB?{Uj@ z8JjrUxdbjWww~*Hxo$Epbnf{C7dkUVjt6In<+hjhJ1+FQ-N%KFyIkUjT>>t&2c)>J z2<~Bf+Olq5HC$*ewQ)u5u`l8tvt6XG66tyT-)gb4M^&(E_0N5z*3|x%-TR8P;x~y^ zv8PR|KP3Bf4SVct@@m^IGHs+U^&fjJp{*u+K=@a{{p(&!%Qz$le{AOI3FpM9wZXiM6(W=?1utwrh#&YP(ciXg^Qum=oy> zM2<}(m?G2*pMyJU?p0nuu@mgEa(tO$yU6uX z|Wd&J9fm4$vJeh9gDQi z%Zv>jC)2fXTv6Ah)!OU2V+XiS{?=&tXzRKkT<2-&jQ!_hQ{^2%WdBI3 zXW5h~_Sowd=|#397V0rZ`eBiK(tQ{kYR`k*yhyJ!GMC8M$owLC|2*f&-SgMorOm?a zw`=+?F7!+P$2J~1ZY*PL=xiG4A8c~xa>#X&?efp}4`k0fY%f>b3EN#_E@y1_m+KN* zIk7CR%kR3#UMq7sXHQ#{v6pRE4HsIYhOUd$Ud}zY?7YYx--o==uMpWnk!J*vtsCk6 zr}d;E@*9m}e)@0N0Jeg|^Fd*G0C_N@9=L>mhgjirX&I>LS}9@*72-5Ji5=$k_GF zue3dv$Q~8>rQ1Kw`{4H2$gi5#UK`m5BXfyN5orgJ`9&>*XF#DRQ<0-UWQ{(je$U%A z*pF*$JNYd`=QnXJ?bwfu4Q;jSuJg2Uez$D6&{&zGy*))CVqI)k64%pqHC*@MBfKeP z$7G5jwu{s`vi%}^K;-#bWIu{*aoGb#+H+~eGD2;)gXpYC0Sjx}%iVFBn;x z)Ng`4XKBx&DI_+Tzl+N+&tSwwzy2Y*p5ZkH*&=s$&0i#If5*+ zr-&S1Rx;<%dVD5Jv-@0;7JGzPs9%@X{389of3CCCbnOGRkIXOPF1R&_Y3tM_1-H?j zHnJW?iG|KlPq{8Kmq^|Vj18?@Yu9ylU2ohjd(Kk7eeQM%O(9pB``uXRxnyKNAI!9& zbN47*Xse}lK8o~I(|V>kn`uMOG(U6QA=lk-T|zr2ue9sxxURSBrnyemDAdP{)IeI@ zgZH>2cc+&zZD`FSJ-5}wj@#QHG8dWiPY=W*&ov^?IySiT`^I(0T=z5XsNL57bYtH3 zP7N};F4CSOZ9J_lnV%^_Te7O_TDq>E>n6Kynd`Q>?xgGfa9z3%&YTOou7>McyKbcG z7PxMc>yEhYit9{AXU@4@SKf6ET-VEWZ@6xe>o&RWfa_%M4*izWd$?f#T4`;yU)(AF zbX`&>r&fhs7dbXX&Uw`td&8b{ZQS3sOU3D1(vKL43+)wexNg4dKE#FU7wJ7juG&^I zHgt5{fb-e&``n%4D_o)-i`?^wwBiG9-U(bLJMU-QYP+pPjuC0~hUGk%Vo!0`olCZt zcrV>vw})||HA-vWFU8n`_Ow-Tp?;IxQw_O##6tIDBmJDnnK)9b$dOC>KA~eyW2Oyl ztH?gsqcC^r?4>QK%(oYzI=iv;ZY=AgPTuEkth5_D>Bj20v72rzvJ4NcDYT48?4dMb z54*7i&+^uwU8~q<`1Z(lT}n80De2~AD&@qUaAT3YO0}G6>$-Ux-TBpEiqJ9+HE{9@ z7I9*Y-TAe1^SZmSfo^PHPG{XB%N^n7MPjl>q4jv(op!35_rded`mQbG%r6o<<4!9s zRA;NXGrvgQCO0N$l2D!;vqQ_h;4Y(QGbc8$qO-mu-0d>Gg)^2@&D?2RPVxf5D(NP zt!d?K-_XvJBXwxIEMeNvkua?_`iQa3ADHv+#6m~2$kzCuTt)v+rcG-t)7s)`{i@Yd zo&IxVJvI?*`@s69@5&ci55&3=3+>a9X_pY|{y<(@bxA9Bg=s@AEUi15w-_5bhu+2Y zv6qonTTAPg33U6 z>CHrB6>3xpZ??y(kG)xltVWH>!#nP=>T*mrB3DquyY1PDT#d;=oGZr-#`s-zRTZqDTKzVn=!eG|Ardggij$}7>!kTWAYHchZ^3D&r7@r8msQdzhXe@s#vnX+m8ucWpG zj}cjj8r6haaArbd)f)ff#Mh$o)kPWN&Cyu3&R>@Jdeo>EsvPl_XskM%?g`>YP@`H= zYo6|)vFiJD6^I{2jcP-!IpU#4wWp?rZ#gQXT~$@Io2t&(?x;~cR1LJJdKT@aYNEYW zZM2W7gTAclp?y^Yw4Ztr?XMc416Tv222x+622nes22&TKhOlI#hN^bxD=gEfVf+hX zMh#b;(Ge;Y9m(>I8pV>0dX;4wHJYUvHHPIFHI^k9^&0awY8-PlYCQ8Z>UHL1_*3vB z&P@CF>Me2&e<<{IbTZ?On!*^PrV=x18bgM!Sf-)V`QNA+Y9=~U%|>Ub zx#(=Z*)aTZmIdfswGe$rEk@_5rRaS1KDvPKIE;E%Ek_rs73dz@2idI2YlgSm~sosR}AQK{>qQxdyrk|3cmL+YNgtX@_hsPvHA*Kt-e9m@V$*; zz6a2C>JYkK9YH@)N6`)HIQpqNiEdP<(9hIqbdx%ZZdN~|ThuvptGa-0QN2`R zT}5}Q>*y|Z6Wz@#3WnO;LO)lx(Y@*p`UPL17;1JO{ZeUdsGo&?t-R4{6v#J>SlPZD!tV*Hh`BKKPo@LOBsvLSrl}CS7717J8lIHCc)Uf82 z@z+qp`d7u@Kn+`=I{r7*uq|q!chs{){zeU3p(g$w$`Ml6#wVa0*L59ydeo?gbUl0$ zYE%Z@0G|=%n4@1rAJ>i0GP(&`RyRW{>K5cYi5hCw3jY*ps977dif%`wD#|fLcR;J_ zPUv&GGy1$vMZ4;5aKg>d!X#mx);6|${wxz;QOHL%epVVAIiS0`{M_o?5BDl z`kEe$j?+WY@p>5gx*mZ}(4)|adNlf$9*e%M$D!})*U^P~0=h`Qi7wWY&?R~bx>Qd? z-_z64_w`Kl13epErstv`>UrpLy#W14FGN@9#pp`C6kVm?M?cof(A9c5x<;=+*XmX1 zI=vcQuh*iV==JCZy#f7HZ$vlhP3UKO3%W^fLpSRk=oY;T-KzJX+w@*^yWWTH&|jfD z^*87)y&v7J51@PWA@p;71l_BTqF?Ca=stZC{ZgMoztX4Cuk~5<8~ro-tv-kD*B8+5 z^dALHm_swmrKNQNy3Y&5qY7Jsp#bw@||$q|Sx+ zqV#G^3O)v<_hRzm<4}4jCOac(V#p&7iv^)QxTtn(w{Mv(7dKH zk$k98`At={fT@laG&RtN&9i7BQxko})J6-NI_RUO9$LgSK#Q6e(PE|%THG{2OPFS8 zNz(!?Wm=(+nKo!?(++*ybU@3PPH0)v8SQLR(JrPNHAzL;>rD@|m+6J}HhqZqK{;QW zzWBZ+>nnURN~^QJ!RJDa%5Ck(r=YYt>i|A4YWULd z5I#RjtFw;a3!=0->nOeuN~^Pu;|rs-I_o692uiE7PT`B8v^wiFz646Ev(Dm6p|m>d zXMAatR%e~VmqBTD)&+byl)kZb311$iKWtsbS48OpTUXIa)^+qL>n2*+`W>xe-9oEc zx6x|W9kjZ27k%2g&$KmAqZ(TruvAaWvN%Jbv|-DO_OW8nm#sMBeNpJj(%s=AaVeuwOh~PkD~O5teW`aC_N&pHvS|^kI1Tn zKZSCpu5;z@mMNsxxkNnlaVkrBpXEDA6%0BB^iZ6w-&wAd+mqyuVJIOtMQdkqn`4t#aBkzXFcojRZ;d?&jx&TlzrB-5nlr} z>KV@_^jXgqBF~{l)%0w`*Fue|?b(5U9yO|tXBWOMYE(VX9(;Y&s0NbZm;igK;$xr`r%a;@sQiXVY;-t=6@ zk3u-We%Kqi8h%bb)e|an63#05`-pcqQ zDEpVUD!v%X{^hNXFM+aud28THq3mDYXYr*`_AhTud>NGe%Uc^?4rTxH*1?xY*}uH? z@D)+^FK+{UC6xWk`y#$F%KqhTgs+OSe|ekWtE22+-e&k3DEpVU1^!u->pO2Nd`;A- zTHZEjZErjDd2a`_j<*w9*V`Gb=S@ZHd%K|xygkqtyuHvDy?xMz-o9ufZ-2D0cOcrt zI~Z;19f~&d4nv!JN1!ddqtKVVqgifW?^t|4Tl;&*;RkqMM<;nFFm^IZoAoxCO{HG{qSnoXiXDDY_?*ja0 zlryY%A$}{$p54T_wlIAJ6wQz9VW>C*L{rCEo=iol&k4e3$U4DAx$S%V;;>RkXYBI`JMT z{Vv~4d@q!Km+yCUyzdr~*HP}L`EKLiLixpgckq)@esSMj{8W_Qm+wCQZIt_IKJDdS zRYJL<_gUz2pBLTji$VAJ;?S$U0D8?AM4QDVplxH)qwQjn(DpGI(GD@0(T*`$(XKJs z(QYxxX!n?0Xpfi_v}a6Sv{y`iw0BHF^o^K8=!BTU=){;J=$kRc(6?erpdZAPLRZI> zriN=!dUP>m@as_S6~vT7H^r1kH^)>&x5QLJkH=I-PsCJ3PsUUy{|A&iQ86{}Kce)q zVxC2Rj;TrH7nHUaQyV=WQwO~eQ;+yXlzUJy4e-CB9B*S^#K)iRV`f6+sGDoA_YmM!NAB(cxV*8+P#`Z;5$M#3J#tuZc#|}n!#12Jw#tuV2j~#)2 z89Rz;zd|`f#g4{*gK~z79gFUd9fy7w`#SLhDA#wf6VOAkZxT6-a(x#&3I9FH^`eSUltp_4;?AKK~vx#=jSh_3vYDai~#w{a>N^{NE7Ck8+go??;RH51_^T zhtQJ#BWM}_QM9c8IGN>8jurls`0^;n3jZmzqW?77(tj3h<^LIN?LUXM^-|7Cm^)TmVdReV>JJ;;9@9pt}>zT*F#_%M{-j{g>Z1j;=D|7~=U{|=GGD0k%i zchQyp`$Sfu9EbhdN89jQ=nlUZ-RF-%zx2oXXcZ`}!XH4t@dwdw{R!xPe|q#ge-e7Y zpAkLi&x{`OXGIVDv!g%ylhNz`T+HVN$`yw{1^*k$miOny|AErC@#n|?iPE?67sUUC z(q{aH@PDKHI)TDyFi-?d2oyuJ21+nC8%p0kPzs*|rEec7jn9c1l`BvNpBptQB~T8Z z2j$lcl*i{oIpzl{;tQZ0^8=Oe52M@#3RK2Ff^ruqP!<0u%5`y|I$9-AgGg1Bvu)s6 zv}K?skya?bW}r6OCQt`$8>ol23p7C62VO)w1R9|o15MCQfoAASffi`zKr6IMpbeTD zXoq$UbU?cWI-%VIozWhFRJ3QH8`>+-1MMB?h4u;bL0=B^Mf(Q&qx}K{(f)zK=zzdb zbYNf@Iw&v#9UT}&ZO5QIqX~@0zlL&uI4~A{BQTE01eD`R;C1xPzy$QIz?n2x>^n2F8{%tpTp%ta3b<}sgxC~YIK0DlwRFQMgvm(eGKSJ6tr>u9y$O|*LO zcl5d7EwpCvHd;4$2dx*pi#812M;isXp{$w*Ewn|@i?$8MpzVTj=u5!>+Bq0Ry9X1{ z9>MhJ%fTeHZ!jY|D3}=?9L$Oi4`xS41e4J*!CdIrU<&$1FfTeGm>-=SEQn4C7D8tP z3!^iGMbLMG#n5@d66m5}DRgnLH2OiX47w~>4qX{6kFE+|u2l3PKO;L_K@iWm*@w3q$@pIAM@$=A;@e9z=@e9!j z@r%*7;+LW`;@?MS$1g+QjbDx~j9-B+ieH5;j$e&_5Wf~(9={&_IDP}VHhv?zF@6)e zIerVeBYqpYJAMcHdHgQ)tN1-cl4=*TWHmU+h~J?J7~j%yJ)+F`)J1m9m~-;!9u$yc+vg|G3cO#ICN@40G*x? zM3*HbpdTfqN7p1Iq3aVeq8k%3$8t4<(n=Gu;y0u8P7|`@x1zM$gk<~-0=lT#Pv$dFhOUms;VBv!(|fN~s2tc<26Rz-UyR!3h>tbqHs z(D{k2(1nR@&<_&Zq019HpxY8Vp*s^hqhBSaqTeQVLvJMZKz~o{g=S3O2hEheFPbZT zf0mUSZt&i6luzU!}rF7kIpm-xG*@A-S8ANYHtANpTLKl1lO zSNaE_ANvQPYy3mdb^e#oPy8LxPyOw!cKoBF$2{$n37kMZfgey`z!NisGs2OiA*yw- zoM)nHn^4ZPT@_6#=lM=m%TUg9QGJ%NoaZ^+GRMh>p3|*!h;N%CJI|`y=Qu^AV~(Rq z&*_(PBopb9qr9h$Wb$tklG#*$O=ffbeU1{kxjsf_bA5u$=K6;mrHK4UW^;Xp%;x$h zGTZ2TIZh?D;l@)ryh+Zk+GtPmX(GPlqiAe$G7*1rc~3i?A^CVxJDrKlb~+20?R2)} zlSFcm*-qyqvz^XOW(WO9atWT&KT2i?U6jlYx_ELaA|=V}pdTZ%gMOUMF8cZ8lMi*# zb;<0a>yz0R>y~*sVUnaAw?w8z|$N)0C>Oo|7)kDbariZiiZh9n{-Sn$ucGF{6dN=(V zncehwGP~(F$n2@-C!b2{soy2Dr(Q&6PrZb#)KkAlW>5VAnLYJ~WcJfrlg}pg)7#1H zr+1RsPw!6tnaJm4_S0XG*-w8-<`8{8Icv-ieUZ!|`d2cC=qt(DiCiOdh`vGQ5d9mO z!*#ZtCm$NFbL1?C&zUp38m@EaJVhi=&Z9}gb-tX*L<;0A?-{9!<}9H{>f&UM)FsIr zsUOQ(ipb++j?`tz9I2lmbD|!U^OAR>9zy0s{R){A_3)gRiHsz3qJEXkiFyo~Q}z6u zx6@D6?~*xHFCufQUXt?;k@v`)sy`res{WA78G38Z1~D`Ab~0z^on+3?yK}xsJwzn(dTm}KQu>QBy*1b zmCQN%O3oA_*T|fsZ;&}h|3>CgohR4c*rhsOu5$PSxw7-t#lyMw5qTun(WIsN(Ok(y ziss^JfUcXX1n*GPCv$~dIbw@H+>X*n| zsk@N5QgI`GU-i`b#o5>aWS%rZ48|;oqizC3Bm;LgqGoE!VlEZTbe8+w^Z_ zZqt8|xl3orJ;}dIXUbg;pCxy8-rmTT`$Ez#og?>AG-vK)BDr&y_w3VUb6@iA(@&7O zPgfvwpMEm;Wg<_JxldOibDyq8<~jXE?h?Gg@gjwfB#nNrH(v!rC_ z{fulWCyC@pIf~{?NhXpzr9A(B>BD4R(T|XMML$aB6T^RaYbPs(zZxYx;RIuj#sEUeoo-yry3u^O|l*<~7}z z%xk(SnSbdaDVLJ|(yx&DmmW^$UwUN9Wg@SV`IjC;=3n|XGVkfBDW{U|>9@(er)Q9P zPtQs@O=J$4_w+kt-qZ8R445BME_nmyM=}HE44DD*Q_5u`zmOR)=gAD1i)7L>;k{J; zb{-_dqwpA1fND@5T0ts|gz>;XTg_X1{4EZ64_3k_um$$PQ8))zKt2U?s@3pHOarJ#Y+ufm@J` zZfH?x14Cge`~+nzrP{$Xm=CLA3w#S_;SVtUYqd#`ABsa&r~|E`Hw=f@;4PQ|^I!=q zhc$2(a(I>M28W@MkG~-Wv*BYn4ZK>cia|}N51nBKtbxy97aRmx#}kx4!LM)!e7qi; z0dhhSC<~S0IcNqQU=X|kb732N0SDkXoP|qp6aEG(jxGpfg);CIJPQq=Idp-MFcsc| z)$l!>f^%>OV*GUbAqC1o6{rObp%wImLGUU}gL&{Cd6U^5(tTTnBB-w#?sC+H1BU<^!!-EbBz!A(=mk=@0hGhSehlSgSP08uADn<2;7?L2JLHEd zP#-$M5SRk9;S+c$1KR?6!g5#(n_wq=2|vI&xCVd1eTd;enh04SH#`i*p%Od;QqQ{K zvMFT;=ng~Rb(jiEVKwZ8Z{aN5g>;!Xe!|1>EDVH^Faf5)JXj9v;V_(s?3p>1!!yty z#=<(-27BQW+=GNHp|+TbG8yti5hx86pbj*H?l1!0gxRnPeu3*?vQkG#hJx@oRELJp z3OYeQ7z1y@eAoo%ATgU#<)J3DfKJc@`oSwO7T$sxumC=Rt?)IRfQxVgRCcA}AT!WK zP{pA#)P`oz9(uqK7zNYeU04U-!YQ}|u{k)#LJlYdkHeEt7aBurcnQYCG?)$V!zZu} z_QE0f4Kyd#M92&eLvbhz&p=&h4(*{2jDT@44d%iM_yjJ&ZHUduwuQRz67+=r@CuBT zVcOT5l+$58ya&?eR)$OQ!Y2%Gjf#DX|2{l?n(_wRgDknIGgO9#&<1+LP#6sp;B9yZ zmcSM`3>kBC?1F006Z*q2mdw1n=^ABMwtSO_1&8rTH~;V#7GZ zpgOz&ePJ^=zw2(iw1Ka~<>BzSA1KemHTWYOyB98f1wzxrQ)Yt#P!^ndR>#+Y&d?JE zz*Lw6i{L|81H0ig+yQ4=t02E6WPzgaI8=gXpar}PufimdX=YL`fcIe)Y=*Dl2>b{a z;5ys}{V=}?q=(#42+BZBXae0}BD@VBz!z`@Zh>qgr;hg-jwwVvAv5HLN1!BBhMLeE zx1S&qG`24TE6;$nus^u7*8u9L__w!ki1C6jXp}&dftO$iyah91H{1hRwy%g%@gVD-g)$EmgR)Qq>On{7 z4r5^&%!MVe0(QVb$Xt{@JUj*!peodaCeQvQp%e6j zS6~cGgxT;hY=-@C0)B=oU`o>7APKTVAt(vup(@meX3!q`z;xIH$3T{QhVmlZgxFHd z2lBzAP#Xrq2p9(oVI6FT{csenz&%L!7}G*QcpPd&N9X}#L6-F<<-4#0euf;SSzqV@ z{a`4Jg*V|nSOr_*5c~nfALn<0F)$Gp!CsJg{}?XMQ{Dt$8TK8>3PqqA)P{y2`7J3s zLJxQ)92-M95!S#K_!^{LeouK0u7gZ_o6?l!??ys#s08()B@BZ}@E+`iL+}G!hr5ti zj^hp#0GU@&$|s--JP*yGH%x&OkoyVtcjyJLz!;bdYhVX_59i=I`~{|bsLp+ucL1Le zlA~hzqkOR_UoOg5jqBRUjq;bH{H-X@A5XG>TYEc2`HWFMSClUlH^JkGq+a9-$!}$9J4@{GBvE z-*5)FqnB0H)H!&vlv~xN>-xMdqUu0hs0a0-0lWY&LPK4gJAjX=#?S+y`GK3{A$=6lT+ zJVj~Ex0>zvQnMX*7~83rbw|F`?8q~dE_|igg>N*wse!tO8U%x32n^M|)GIJd_vSfB zA2mYv<<4YZHA?qWufk{;qX($5@EVMR@$kAHrrv-FFcIE_w_p-X*2C2lJ(?#Qqt!Hc z8>YhyJ%%R}WB7Y=uW{FN63-qctGRj#e_L*fng{dsbhQB9g@v#P7Q+%)s%P*G=q$bf zoyDEfS@QKK-+nIS%g@Dp_qkZU`sD8EV!rrX$@iXX`Py?W-+Hd)OV71@=ebtC^5m}S z2A(5q`hxtbHFkfgM=8o=RbsSE>N%#Ry!H;lSA5&-GEc~R8^R?zF?*5+QX~8L; z1DxV3%^&$j^CDkpUgZ1C%iQa~%3b_x+@HU}o%b7jm3fnIGH>!l=1soGyvf&?xA_+H zFYX@y&3Bl8^A)Dne*s^J>c4sE_AcDhF?n$x<7k$d926Jg))mpyr@>;80I z4jnocYub1^N#eheu6s2+mj0j{JE)UFX{vy6+!H1TF*!;+VYpzh|EpkXP)e+V=^6^D zC;8ijq1cmVSvdBT*&B{MWv+x{RoI@PyeftRm;GNgcNx`8t#IsVH})}{$Bte*dX^;7-GhTryXMCnT`DS zirE#8yAF{JCtK|mtA2Re;qKUxu6xyT zdRk*_CsT}Zr+CdBJKl9~xNer4x6Ym4LA!o3mxFHXup2w<#%{PSlgFujQP(}@x+grc zzB0cj+*niB&Gfcm-DK=cZ|Cq@S~1RcIT`1;Q(;H1Jm4(tWY~Gs%ydrP?6Bi1IKeq` zO>*6o1SfBr>!ybtHE`W**DY{eYNE4To#f2fN^*XgjIPV-j?M4J%DA!eZmhQJ>bP!) z>*{21meC-Cvy5>WoEr4X=(s+v>+8CKt{dvQHkq9%2D@&k>xQ{*gzHARZnW#hx^A57 zUU%ID*KNq`w8=7AoaLsvuAA!?xGr;6Coik(vb!$Xb-7$u%60u+H_&zST({A6n_RcW zb=zEb#C7SDr7i(g3r$ju(SoWYT3Gc#i|{Y>$k^R>Ty%1RXd^q)!qy~pfJmFLKaF2v z&ut04W5vJyG6^*?aw$?(58^nJ#5aySf76F-f5+Ae>I1gFV8{QmePWEnQ_v*!I2uq-+rFvo+u42~nySa!ex4m)Vf(M__{*`} zpVXt!>-uf-NoQakLNFVKL!{mYr}@CB93ElFkdvn*2_?O^1WN;T{7Noo^X z#Lj79YpM?AUm%{Uy#dKdk5<(()v-Y6S1v>()s7^o;`o3)?E=#QztFD#9`hD{udOC# zD*me8fF3tn(IR#Yi>OuUq=uW(^p>=fB=se}u=)-?7Lb}83&_}G0m)3_-!!qG*2fL|hrPAm6L;%+>QqdRe+?LB4pCX^Y~MR24MUG_`$OG+=MZ zR5QYkjI$%Mts2?eIY}+B;1 zHZUulEOBar=z4D(;>W{#L_hpjUeP2~Hj^x?DVn5SLW|h-Kj)R>PpT13Qupk!jWbK; zbTmmVMhmMWsMp>Ll6|$vtYL}fvi$-RlSSq>4sGO<<3QSX39rjGd^_KFXa{?2 zLDe&>%zv1zvX>>PkMODbOFQztt-sq5Pd3>;xzHr_C^|VI)bgw0Q+24teqfJXg?`1i zI1=xh!=68y%O@>!p}l9__Q_sWP)#Bp7bAMxCz_lRJ4arO^E`mCz*B!d5wUUFBa`k~zpP^0xiEB&p9Co8FShEITsa)~;FP znASCmwDW-7>bho;{jrFBtm~RZT4Pl$`ey8!T->{kl`Ws7F5+j#-nX@6ZmH)BXp(A! zCi`R#T@pnLs@I5gNtC5@NtF4t&Mb9kWY?{cy_A4jM$Wd_FH!b%w2>WYVe48?XbZob zB1;*LCaKqLop0+FJN||3FW6c=k7U+DlT>|la)S7BaiXbO+HDaPYQ3Uu>~`2lHO|Xj z!MILnBYW%uGX9R`1T_vDYI$ zJH+2b>rrY9c=oeS=fKJ7m%tO^`Gas%Hb*<3Sy0Fso(~ohQ&Fw35zMhiM1KzKwQ+u2 z_!~I(`4X0K6L>5|b0^-7@1}(9dIj{&I$PTH2GC=~!Co6%u&RO`*;dRp=x6Oouw6e4 zdW`SEO5+UZm0FdC6`9)^&t;Obbk2`gW=or5J)Xr@V^b?(%6!?M+J z5>#|fN|zzR^h}>I_iT%F8yUvPRd*O!%zK0E)ilTB1KM?9|Kd#cv{r-X`2T`W09V z>H*h^#nf0|C%U4H5-yzqCI>8y&~xl;*&vozg)Z6!OR?Oq{^;OdpAsG#&-w-J1$+vo zIC;iCYj5$eZq2>CniM%N!(wXJK!Yq!DaBLFyg6XGgMA8#EuH$ypsDQy6@3KGwzGeK z#y8Nh6RB9(Kj!3|OmXsB*{MHxAJ?h}6|DfbI{4~$<#F5E^{30Qsyca8;Eoi!qHCb3 zmEF&JHE3#UQ0f(SWcy(;h*>N&h(9mA4vx^3$96@B!9TUH!I*On?56E!=-oDB8jn3@ zUHT-@w0Xg9TRG-=7wrd^j_13k*JMmykWbxecq)nxoyE0IIr7W#duO5_Osj={Tw=Ot1$0Gi!d;-L9TWW==+%rF zTslEm0-D+a(U*b;(>QXDov*K=4k>+4O1}a_w$tF%EY8N5JI_4C{6+Yy@Urk4SZJ+B zvM%(E;&FH)^Ua5OKHmwhl6kC0EQ)@D#h~9nMQSGZv(OsrX#kLW5Y}E^Ei4be=hjiCIX;P^$sEaK*@n9a(qN zOVHaONyvK02FON~y#?6@>4J1a-iB<4yaVZhybIX@*$H_cvKz7o+zZ(U`2eyX@*(77 z^4NVIqxr{y0(;eEoR6HyM4>^VvFV#fft73|}A;422^Bf7lmT z9tlLk{&;A0sW05r;G3J69f}uwhpfIxBHogSTXr|v7jm}*8xZC8aqDIPyOE*ud075N z<^WGhVZh>blb!t@g)FYX?%tSl1`jwyKtR< zGReDt^D+2}kG32f(kdA+;H3(J|096aJc>9?6vqC|)zpkNAtFu%#I+Wp)DLS6<@lZv zei6L0j(us%?Hr>9_9#l4vG?=Mk*dU}1pT(Y$}rm?2iS9XG3t5|ZzaA$s2!J|S8-ryf&J@PZ()rS-C|-wSTd}xD z!%;b6Oe_)980p=BR&kDk(vAdn(B?egrUx_pxS@WuNUm1nJbvjD=V=~l#t}J60ccU| ztIhqHh589Z2#&~V1y%rUD2I5ajI-e>G0xnA7{j?A?#FPB_*`Y=NU2{dXN{%QD@BVv z@N(k{u<%hKyT$XBw_4eZ7KKxK@K5J*qupVA@$n)e;r9w~u12ZJSF{CL=TQMB(diT_MgTXEBH~^XG literal 0 HcmV?d00001 From 2785f404b8431cb44712eee47acda6ed07fbbdf4 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jan 2018 16:58:05 -0600 Subject: [PATCH 26/28] (maint) add Chocolatey enhancements --- .../NuGet.Server.V2.Samples.OwinHost.csproj | 14 ++-- .../DataServices/ODataPackage.cs | 35 +++++++++ .../DataServices/PackageExtensions.cs | 37 +++++++++- .../IChocolateyServerPackage.cs | 40 ++++++++++ .../Infrastructure/IServerPackage.cs | 2 +- .../Infrastructure/ServerPackage.cs | 73 ++++++++++++++++++- .../NuGet.Server.Core.csproj | 11 +-- src/NuGet.Server.V2/NuGet.Server.V2.csproj | 18 +++-- src/NuGet.Server/NuGet.Server.csproj | 29 ++++++-- .../NuGet.Server.Core.Tests.csproj | 12 +-- .../PackageExtensionsTest.cs | 2 +- .../NuGet.Server.Tests.csproj | 15 ++-- .../NuGet.Server.V2.Tests.csproj | 18 +++-- 13 files changed, 258 insertions(+), 48 deletions(-) create mode 100644 src/NuGet.Server.Core/Infrastructure/IChocolateyServerPackage.cs diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj b/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj index 18d5b18..a9e24d1 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj +++ b/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj @@ -1,4 +1,4 @@ - + @@ -42,10 +42,14 @@ + False ..\..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll + True + False ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll + True False @@ -70,10 +74,6 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True - ..\..\packages\Owin.1.0\lib\net40\Owin.dll True @@ -87,7 +87,9 @@ ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + False ..\..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll + True False @@ -149,4 +151,4 @@ - \ No newline at end of file + diff --git a/src/NuGet.Server.Core/DataServices/ODataPackage.cs b/src/NuGet.Server.Core/DataServices/ODataPackage.cs index 9f2a356..fd2a0ca 100644 --- a/src/NuGet.Server.Core/DataServices/ODataPackage.cs +++ b/src/NuGet.Server.Core/DataServices/ODataPackage.cs @@ -66,5 +66,40 @@ public class ODataPackage public string MinClientVersion { get; set; } public string Language { get; set; } + + #region NuSpec Enhancements + public string ProjectSourceUrl { get; set; } + public string PackageSourceUrl { get; set; } + public string DocsUrl { get; set; } + public string WikiUrl { get; set; } + public string MailingListUrl { get; set; } + public string BugTrackerUrl { get; set; } + public string Replaces { get; set; } + public string Provides { get; set; } + public string Conflicts { get; set; } + // round 2 + public string SoftwareDisplayName { get; set; } + public string SoftwareDisplayVersion { get; set; } + #endregion + + #region Server Metadata Only + + public bool IsApproved { get; set; } + public string PackageStatus { get; set; } + public string PackageSubmittedStatus { get; set; } + public string PackageTestResultStatus { get; set; } + public DateTime? PackageTestResultStatusDate { get; set; } + public string PackageValidationResultStatus { get; set; } + public DateTime? PackageValidationResultDate { get; set; } + public DateTime? PackageCleanupResultDate { get; set; } + public DateTime? PackageReviewedDate { get; set; } + public DateTime? PackageApprovedDate { get; set; } + public string PackageReviewer { get; set; } + public bool IsDownloadCacheAvailable { get; set; } + public DateTime? DownloadCacheDate { get; set; } + public string DownloadCache { get; set; } + + #endregion + } } \ No newline at end of file diff --git a/src/NuGet.Server.Core/DataServices/PackageExtensions.cs b/src/NuGet.Server.Core/DataServices/PackageExtensions.cs index 03ea2c3..4447ccf 100644 --- a/src/NuGet.Server.Core/DataServices/PackageExtensions.cs +++ b/src/NuGet.Server.Core/DataServices/PackageExtensions.cs @@ -46,8 +46,36 @@ public static ODataPackage AsODataPackage(this IServerPackage package, ClientCom Listed = package.Listed, VersionDownloadCount = package.DownloadCount, MinClientVersion = package.MinClientVersion == null ? null : package.MinClientVersion.ToString(), - Language = package.Language - }; + Language = package.Language, + // enhancements + ProjectSourceUrl = package.ProjectSourceUrl == null ? null : package.ProjectSourceUrl.GetComponents(UriComponents.HttpRequestUrl, UriFormat.Unescaped), + PackageSourceUrl = package.PackageSourceUrl == null ? null : package.PackageSourceUrl.GetComponents(UriComponents.HttpRequestUrl, UriFormat.Unescaped), + DocsUrl = package.DocsUrl == null ? null : package.DocsUrl.GetComponents(UriComponents.HttpRequestUrl, UriFormat.Unescaped), + WikiUrl = package.WikiUrl == null ? null : package.WikiUrl.GetComponents(UriComponents.HttpRequestUrl, UriFormat.Unescaped), + MailingListUrl = package.MailingListUrl == null ? null : package.MailingListUrl.GetComponents(UriComponents.HttpRequestUrl, UriFormat.Unescaped), + BugTrackerUrl = package.BugTrackerUrl == null ? null : package.BugTrackerUrl.GetComponents(UriComponents.HttpRequestUrl, UriFormat.Unescaped), + Replaces = package.Replaces == null ? null : string.Join(",", package.Replaces), + Provides = package.Provides == null ? null : string.Join(",", package.Provides), + Conflicts = package.Conflicts == null ? null : string.Join(",", package.Conflicts), + // server metadata + IsApproved = package.IsApproved, + PackageStatus = package.PackageStatus, + PackageSubmittedStatus = package.PackageSubmittedStatus, + PackageTestResultStatus = package.PackageTestResultStatus, + PackageTestResultStatusDate = package.PackageTestResultStatusDate, + PackageValidationResultStatus = package.PackageValidationResultStatus, + PackageValidationResultDate = package.PackageValidationResultDate, + PackageCleanupResultDate = package.PackageCleanupResultDate, + PackageReviewedDate = package.PackageReviewedDate, + PackageApprovedDate = package.PackageApprovedDate, + PackageReviewer = package.PackageReviewer, + IsDownloadCacheAvailable = package.IsDownloadCacheAvailable, + DownloadCacheDate = package.DownloadCacheDate, + DownloadCache = package.DownloadCache == null ? null : string.Join("|", package.DownloadCache.Select(ConvertDownloadCacheToStrings)), + // enhancements round 2 + SoftwareDisplayName = package.SoftwareDisplayName, + SoftwareDisplayVersion = package.SoftwareDisplayVersion, + }; } private static IEnumerable ConvertDependencySetToStrings(PackageDependencySet dependencySet) @@ -87,5 +115,10 @@ private static string ConvertDependency(PackageDependency packageDependency, Fra return string.Format("{0}:{1}:{2}", packageDependency.Id, packageDependency.VersionSpec, VersionUtility.GetShortFrameworkName(targetFramework)); } } + + private static string ConvertDownloadCacheToStrings(DownloadCache cache) + { + return string.Format("{0}^{1}^{2}", cache.OriginalUrl, cache.FileName, cache.Checksum); + } } } \ No newline at end of file diff --git a/src/NuGet.Server.Core/Infrastructure/IChocolateyServerPackage.cs b/src/NuGet.Server.Core/Infrastructure/IChocolateyServerPackage.cs new file mode 100644 index 0000000..bf83ad3 --- /dev/null +++ b/src/NuGet.Server.Core/Infrastructure/IChocolateyServerPackage.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +namespace NuGet.Server.Core.Infrastructure +{ + using System; + using System.Collections.Generic; + + public partial interface IServerPackage + { + Uri ProjectSourceUrl { get; } + Uri PackageSourceUrl { get; } + Uri DocsUrl { get; } + Uri WikiUrl { get; } + Uri MailingListUrl { get; } + Uri BugTrackerUrl { get; } + IEnumerable Replaces { get; } + IEnumerable Provides { get; } + IEnumerable Conflicts { get; } + + string SoftwareDisplayName { get; } + string SoftwareDisplayVersion { get; } + + bool IsApproved { get; } + string PackageStatus { get; } + string PackageSubmittedStatus { get; } + string PackageTestResultStatus { get; } + DateTime? PackageTestResultStatusDate { get; } + string PackageValidationResultStatus { get; } + DateTime? PackageValidationResultDate { get; } + DateTime? PackageCleanupResultDate { get; } + DateTime? PackageReviewedDate { get; } + DateTime? PackageApprovedDate { get; } + string PackageReviewer { get; } + + bool IsDownloadCacheAvailable { get; } + DateTime? DownloadCacheDate { get; } + IEnumerable DownloadCache { get; } + + } +} diff --git a/src/NuGet.Server.Core/Infrastructure/IServerPackage.cs b/src/NuGet.Server.Core/Infrastructure/IServerPackage.cs index bfe6460..e04584e 100644 --- a/src/NuGet.Server.Core/Infrastructure/IServerPackage.cs +++ b/src/NuGet.Server.Core/Infrastructure/IServerPackage.cs @@ -7,7 +7,7 @@ namespace NuGet.Server.Core.Infrastructure { - public interface IServerPackage + public partial interface IServerPackage { string Id { get; } SemanticVersion Version { get; } diff --git a/src/NuGet.Server.Core/Infrastructure/ServerPackage.cs b/src/NuGet.Server.Core/Infrastructure/ServerPackage.cs index e5d25c3..9d1f30d 100644 --- a/src/NuGet.Server.Core/Infrastructure/ServerPackage.cs +++ b/src/NuGet.Server.Core/Infrastructure/ServerPackage.cs @@ -60,7 +60,41 @@ public ServerPackage( SemVer1IsAbsoluteLatest = false; SemVer1IsLatest = false; SemVer2IsAbsoluteLatest = false; - SemVer2IsLatest = false; + SemVer2IsLatest = false; + + //enhancements + ProjectSourceUrl = package.ProjectSourceUrl; + PackageSourceUrl = package.PackageSourceUrl; + DocsUrl = package.DocsUrl; + WikiUrl = package.WikiUrl; + MailingListUrl = package.MailingListUrl; + BugTrackerUrl = package.BugTrackerUrl; + + + Replaces = package.Replaces; + Provides = package.Provides; + Conflicts = package.Conflicts; + + // server metadata + //todo: need a place to save this local to the package itself + IsApproved = package.IsApproved; + PackageStatus = package.PackageStatus; + PackageSubmittedStatus = package.PackageSubmittedStatus; + PackageTestResultStatus = package.PackageTestResultStatus; + PackageTestResultStatusDate = package.PackageTestResultStatusDate; + PackageValidationResultStatus = package.PackageValidationResultStatus; + PackageValidationResultDate = package.PackageValidationResultDate; + PackageCleanupResultDate = package.PackageCleanupResultDate; + PackageReviewedDate = package.PackageReviewedDate; + PackageApprovedDate = package.PackageApprovedDate; + PackageReviewer = package.PackageReviewer; + IsDownloadCacheAvailable = package.IsDownloadCacheAvailable; + DownloadCacheDate = package.DownloadCacheDate; + DownloadCache = package.DownloadCache; + + SoftwareDisplayName = package.SoftwareDisplayName; + SoftwareDisplayVersion = package.SoftwareDisplayVersion; + } [JsonRequired] @@ -97,8 +131,43 @@ public ServerPackage( public string Copyright { get; set; } - public string Dependencies { get; set; } + #region NuSpec Enhancements + public Uri ProjectSourceUrl { get; set; } + public Uri PackageSourceUrl { get; set; } + public Uri DocsUrl { get; set; } + public Uri WikiUrl { get; set; } + public Uri MailingListUrl { get; set; } + public Uri BugTrackerUrl { get; set; } + public IEnumerable Replaces { get; set; } + public IEnumerable Provides { get; set; } + public IEnumerable Conflicts { get; set; } + // round 2 + public string SoftwareDisplayName { get; set; } + public string SoftwareDisplayVersion { get; set; } + #endregion + + #region Server Metadata Only + + public bool IsApproved { get; set; } + public string PackageStatus { get; set; } + public string PackageSubmittedStatus { get; set; } + public string PackageTestResultStatus { get; set; } + public DateTime? PackageTestResultStatusDate { get; set; } + public string PackageValidationResultStatus { get; set; } + public DateTime? PackageValidationResultDate { get; set; } + public DateTime? PackageCleanupResultDate { get; set; } + public DateTime? PackageReviewedDate { get; set; } + public DateTime? PackageApprovedDate { get; set; } + public string PackageReviewer { get; set; } + public bool IsDownloadCacheAvailable { get; set; } + public DateTime? DownloadCacheDate { get; set; } + public IEnumerable DownloadCache { get; set; } + + #endregion + + public string Dependencies { get; set; } + private List _dependencySets; [JsonIgnore] diff --git a/src/NuGet.Server.Core/NuGet.Server.Core.csproj b/src/NuGet.Server.Core/NuGet.Server.Core.csproj index 0b5751b..49c72fc 100644 --- a/src/NuGet.Server.Core/NuGet.Server.Core.csproj +++ b/src/NuGet.Server.Core/NuGet.Server.Core.csproj @@ -1,4 +1,4 @@ - + @@ -42,9 +42,9 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True + + False + ..\..\lib\NuGet-Chocolatey\NuGet.Core.dll @@ -77,6 +77,7 @@ + @@ -134,4 +135,4 @@ - \ No newline at end of file + diff --git a/src/NuGet.Server.V2/NuGet.Server.V2.csproj b/src/NuGet.Server.V2/NuGet.Server.V2.csproj index 0425a25..9d3f7c8 100644 --- a/src/NuGet.Server.V2/NuGet.Server.V2.csproj +++ b/src/NuGet.Server.V2/NuGet.Server.V2.csproj @@ -1,4 +1,4 @@ - + @@ -37,10 +37,14 @@ + False ..\..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll + True - ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll + ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll< False +HintPath> + True ..\..\packages\Microsoft.Web.Xdt.2.1.1\lib\net40\Microsoft.Web.XmlTransform.dll @@ -50,9 +54,9 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True + + False + ..\..\lib\NuGet-Chocolatey\NuGet.Core.dll @@ -64,7 +68,9 @@ True + False ..\..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll + True False @@ -130,4 +136,4 @@ - \ No newline at end of file + diff --git a/src/NuGet.Server/NuGet.Server.csproj b/src/NuGet.Server/NuGet.Server.csproj index 27135e7..cbe751b 100644 --- a/src/NuGet.Server/NuGet.Server.csproj +++ b/src/NuGet.Server/NuGet.Server.csproj @@ -1,4 +1,4 @@ - + @@ -18,6 +18,7 @@ v4.6 + {793B20A9-E263-4B54-BB31-305B602087CE} @@ -32,10 +33,14 @@ + False ..\..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll + True + False ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll + True False @@ -51,9 +56,8 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True + + ..\..\lib\NuGet-Chocolatey\NuGet.Core.dll @@ -65,7 +69,9 @@ True + False ..\..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll + True @@ -165,7 +171,7 @@ - @@ -176,7 +182,16 @@ - True + True + True + 1425 + / + http://localhost:40221/ + False + False + + + False @@ -193,4 +208,4 @@ copy $(ProjectDir)Web.config $(TargetDir)Web.config.transform >NUL - \ No newline at end of file + diff --git a/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj b/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj index 0bf51bc..63c89df 100644 --- a/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj +++ b/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -40,9 +40,9 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True + + False + ..\..\lib\NuGet-Chocolatey\NuGet.Core.dll @@ -115,7 +115,7 @@ - @@ -132,4 +132,4 @@ - \ No newline at end of file + diff --git a/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs b/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs index 33fb734..d55f975 100644 --- a/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs +++ b/test/NuGet.Server.Core.Tests/PackageExtensionsTest.cs @@ -85,7 +85,7 @@ public void AsODataPackage_PicksCorrectLatestProperties( SemVer2IsAbsoluteLatest = v2AbsLatest, SemVer2IsLatest = v2Latest, }; - var semVerLevel = new SemanticVersion(level, minor: 0, build: 0, specialVersion: null); + var semVerLevel = new SemanticVersion(level, minor: 0, build: 0, specialVersion: null, packageReleaseVersion:0); // Act var actual = package.AsODataPackage(new ClientCompatibility(semVerLevel)); diff --git a/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj b/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj index 0156c35..02e0b7e 100644 --- a/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj +++ b/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -29,9 +29,11 @@ ..\..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll + True ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll + True False @@ -46,9 +48,9 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True + + False + ..\..\lib\NuGet-Chocolatey\NuGet.Core.dll @@ -64,6 +66,7 @@ ..\..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll + True ..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll @@ -136,7 +139,7 @@ - @@ -153,4 +156,4 @@ - \ No newline at end of file + diff --git a/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj b/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj index 8325438..d2e1660 100644 --- a/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj +++ b/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -39,10 +39,14 @@ True + False ..\..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll + True + False ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll + True ..\..\packages\Microsoft.Web.Xdt.2.1.1\lib\net40\Microsoft.Web.XmlTransform.dll @@ -56,9 +60,9 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True + + False + ..\..\lib\NuGet-Chocolatey\NuGet.Core.dll @@ -68,7 +72,9 @@ True + False ..\..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll + True False @@ -131,7 +137,7 @@ - @@ -148,4 +154,4 @@ - \ No newline at end of file + From 3436a5ab693af5b42b7cd890a2764eb30a1d6925 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jan 2018 16:58:18 -0600 Subject: [PATCH 27/28] Helper should use chocolatey as path --- src/NuGet.Server/Core/Helpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NuGet.Server/Core/Helpers.cs b/src/NuGet.Server/Core/Helpers.cs index e4bb6d9..424a872 100644 --- a/src/NuGet.Server/Core/Helpers.cs +++ b/src/NuGet.Server/Core/Helpers.cs @@ -8,12 +8,12 @@ public static class Helpers { public static string GetRepositoryUrl(Uri currentUrl, string applicationPath) { - return GetBaseUrl(currentUrl, applicationPath) + "nuget"; + return GetBaseUrl(currentUrl, applicationPath) + "chocolatey"; } public static string GetPushUrl(Uri currentUrl, string applicationPath) { - return GetBaseUrl(currentUrl, applicationPath) + "nuget"; + return GetBaseUrl(currentUrl, applicationPath) + "chocolatey"; } public static string GetBaseUrl(Uri currentUrl, string applicationPath) From 0c3d6dee73f324ba8e27339c388670507e992099 Mon Sep 17 00:00:00 2001 From: Joel Francis <32407840+vexx32@users.noreply.github.com> Date: Thu, 4 Feb 2021 09:14:54 -0500 Subject: [PATCH 28/28] Update to .NET Framework 4.8 --- NuGet.Settings.targets | 11 +++---- .../NuGet.Server.V2.Samples.OwinHost.csproj | 6 ++-- .../app.config | 32 +++++++++---------- .../NuGet.Server.Core.csproj | 2 +- src/NuGet.Server.V2/NuGet.Server.V2.csproj | 6 ++-- src/NuGet.Server/NuGet.Server.csproj | 2 +- .../NuGet.Server.Core.Tests.csproj | 2 +- .../NuGet.Server.Tests.csproj | 2 +- .../NuGet.Server.V2.Tests.csproj | 2 +- 9 files changed, 32 insertions(+), 33 deletions(-) diff --git a/NuGet.Settings.targets b/NuGet.Settings.targets index 92d33eb..cb8b5b8 100644 --- a/NuGet.Settings.targets +++ b/NuGet.Settings.targets @@ -4,7 +4,7 @@ true $(DefineConstants);MONO; 11.0 - + @@ -18,18 +18,17 @@ Properties 512 - prompt 4 true $(DefineConstants);TRACE $(NuGetRoot)\Extended.Settings.targets - + Microsoft.VisualStudio.Shell.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL ..\..\lib\VS10\Microsoft.VisualStudio.Shell.10.0.dll - + Microsoft.Build Microsoft.VisualStudio.VCProjectEngine, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\..\lib\Microsoft.VisualStudio.VCProjectEngine.dll @@ -41,7 +40,7 @@ Microsoft.VisualStudio.VCProjectEngine, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - + Microsoft.VisualStudio.Shell.12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL ..\..\lib\VS12\Microsoft.VisualStudio.Shell.12.0.dll @@ -108,7 +107,7 @@ bin\Coverage\ true - $(DefineConstants);CODE_COVERAGE + $(DefineConstants);CODE_COVERAGE diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj b/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj index a9e24d1..47cf0e7 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj +++ b/samples/NuGet.Server.V2.Samples.OwinHost/NuGet.Server.V2.Samples.OwinHost.csproj @@ -1,4 +1,4 @@ - + @@ -10,7 +10,7 @@ Properties NuGet.Server.V2.Samples.OwinHost NuGet.Server.V2.Samples.OwinHost - v4.6 + v4.8 512 @@ -151,4 +151,4 @@ - + \ No newline at end of file diff --git a/samples/NuGet.Server.V2.Samples.OwinHost/app.config b/samples/NuGet.Server.V2.Samples.OwinHost/app.config index 10d3a53..f7c05d7 100644 --- a/samples/NuGet.Server.V2.Samples.OwinHost/app.config +++ b/samples/NuGet.Server.V2.Samples.OwinHost/app.config @@ -1,35 +1,35 @@ - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/NuGet.Server.Core/NuGet.Server.Core.csproj b/src/NuGet.Server.Core/NuGet.Server.Core.csproj index 49c72fc..6b51387 100644 --- a/src/NuGet.Server.Core/NuGet.Server.Core.csproj +++ b/src/NuGet.Server.Core/NuGet.Server.Core.csproj @@ -10,7 +10,7 @@ Properties NuGet.Server.Core NuGet.Server.Core - v4.6 + v4.8 512 diff --git a/src/NuGet.Server.V2/NuGet.Server.V2.csproj b/src/NuGet.Server.V2/NuGet.Server.V2.csproj index 9d3f7c8..a051830 100644 --- a/src/NuGet.Server.V2/NuGet.Server.V2.csproj +++ b/src/NuGet.Server.V2/NuGet.Server.V2.csproj @@ -10,7 +10,7 @@ Properties NuGet.Server.V2 NuGet.Server.V2 - v4.6 + v4.8 512 @@ -42,8 +42,8 @@ True - ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll< False -HintPath> + False + ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll True diff --git a/src/NuGet.Server/NuGet.Server.csproj b/src/NuGet.Server/NuGet.Server.csproj index cbe751b..c59cf1c 100644 --- a/src/NuGet.Server/NuGet.Server.csproj +++ b/src/NuGet.Server/NuGet.Server.csproj @@ -16,7 +16,7 @@ - v4.6 + v4.8 diff --git a/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj b/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj index 63c89df..1975ad6 100644 --- a/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj +++ b/test/NuGet.Server.Core.Tests/NuGet.Server.Core.Tests.csproj @@ -9,7 +9,7 @@ NuGet.Server.Core.Tests NuGet.Server.Core.Tests {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - v4.6 + v4.8 diff --git a/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj b/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj index 02e0b7e..6bdd537 100644 --- a/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj +++ b/test/NuGet.Server.Tests/NuGet.Server.Tests.csproj @@ -9,7 +9,7 @@ NuGet.Server.Tests NuGet.Server.Tests {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - v4.6 + v4.8 diff --git a/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj b/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj index d2e1660..2c91d89 100644 --- a/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj +++ b/test/NuGet.Server.V2.Tests/NuGet.Server.V2.Tests.csproj @@ -10,7 +10,7 @@ Properties NuGet.Server.V2.Tests NuGet.Server.V2.Tests - v4.6 + v4.8 512