From 4c37c92f7300d5d30c3833b9188979ccb4cc1c0e Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 15 Mar 2023 14:38:06 +0100 Subject: [PATCH] improve backend --- csharp/App/Backend/Controllers/Controller.cs | 519 ++++++------------ csharp/App/Backend/Controllers/Credentials.cs | 6 - csharp/App/Backend/DataTypes/Credentials.cs | 6 + csharp/App/Backend/DataTypes/Folder.cs | 3 + .../{Model => DataTypes}/Installation.cs | 7 +- .../Backend/DataTypes/Methods/Credentials.cs | 25 + .../App/Backend/DataTypes/Methods/Folder.cs | 73 +++ .../Backend/DataTypes/Methods/Installation.cs | 47 ++ .../App/Backend/DataTypes/Methods/Session.cs | 119 ++++ csharp/App/Backend/DataTypes/Methods/User.cs | 342 ++++++++++++ .../Backend/DataTypes/TreeNode.Equality.cs | 23 + .../Backend/{Model => DataTypes}/TreeNode.cs | 7 +- .../App/Backend/{Model => DataTypes}/User.cs | 7 +- csharp/App/Backend/Database/Create.cs | 38 ++ csharp/App/Backend/Database/Db.cs | 211 ++----- csharp/App/Backend/Database/Delete.cs | 63 +++ csharp/App/Backend/Database/Fake.cs | 14 +- csharp/App/Backend/Database/Folder.cs | 91 --- csharp/App/Backend/Database/Installation.cs | 47 -- csharp/App/Backend/Database/Read.cs | 86 +++ csharp/App/Backend/Database/Update.cs | 64 +++ csharp/App/Backend/Database/User.cs | 254 --------- csharp/App/Backend/Model/Folder.cs | 6 - csharp/App/Backend/Model/Relations/Session.cs | 35 -- .../Backend/Model/Relations/User2Folder.cs | 9 - .../Model/Relations/User2Installation.cs | 9 - csharp/App/Backend/Model/TreeNode.Equality.cs | 22 - csharp/App/Backend/Program.cs | 21 +- .../Backend/{Model => }/Relations/Relation.cs | 2 +- csharp/App/Backend/Relations/Session.cs | 44 ++ csharp/App/Backend/Relations/User2Folder.cs | 9 + .../Backend/Relations/User2Installation.cs | 9 + csharp/App/Backend/Utils/Crypto.cs | 22 - csharp/App/Backend/db.sqlite | Bin 303104 -> 311296 bytes 34 files changed, 1205 insertions(+), 1035 deletions(-) delete mode 100644 csharp/App/Backend/Controllers/Credentials.cs create mode 100644 csharp/App/Backend/DataTypes/Credentials.cs create mode 100644 csharp/App/Backend/DataTypes/Folder.cs rename csharp/App/Backend/{Model => DataTypes}/Installation.cs (80%) create mode 100644 csharp/App/Backend/DataTypes/Methods/Credentials.cs create mode 100644 csharp/App/Backend/DataTypes/Methods/Folder.cs create mode 100644 csharp/App/Backend/DataTypes/Methods/Installation.cs create mode 100644 csharp/App/Backend/DataTypes/Methods/Session.cs create mode 100644 csharp/App/Backend/DataTypes/Methods/User.cs create mode 100644 csharp/App/Backend/DataTypes/TreeNode.Equality.cs rename csharp/App/Backend/{Model => DataTypes}/TreeNode.cs (67%) rename csharp/App/Backend/{Model => DataTypes}/User.cs (75%) create mode 100644 csharp/App/Backend/Database/Create.cs create mode 100644 csharp/App/Backend/Database/Delete.cs delete mode 100644 csharp/App/Backend/Database/Folder.cs delete mode 100644 csharp/App/Backend/Database/Installation.cs create mode 100644 csharp/App/Backend/Database/Read.cs create mode 100644 csharp/App/Backend/Database/Update.cs delete mode 100644 csharp/App/Backend/Database/User.cs delete mode 100644 csharp/App/Backend/Model/Folder.cs delete mode 100644 csharp/App/Backend/Model/Relations/Session.cs delete mode 100644 csharp/App/Backend/Model/Relations/User2Folder.cs delete mode 100644 csharp/App/Backend/Model/Relations/User2Installation.cs delete mode 100644 csharp/App/Backend/Model/TreeNode.Equality.cs rename csharp/App/Backend/{Model => }/Relations/Relation.cs (95%) create mode 100644 csharp/App/Backend/Relations/Session.cs create mode 100644 csharp/App/Backend/Relations/User2Folder.cs create mode 100644 csharp/App/Backend/Relations/User2Installation.cs delete mode 100644 csharp/App/Backend/Utils/Crypto.cs diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index 39aa52ccb..322242871 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -1,438 +1,283 @@ -using System.Net; -using System.Text; -using System.Web.Http; using InnovEnergy.App.Backend.Database; -using InnovEnergy.App.Backend.Model; -using InnovEnergy.App.Backend.Model.Relations; -using InnovEnergy.App.Backend.Utils; +using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.App.Backend.DataTypes.Methods; +using InnovEnergy.App.Backend.Relations; using Microsoft.AspNetCore.Mvc; -using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor; +using static System.Net.HttpStatusCode; +using static System.String; +using Folder = InnovEnergy.App.Backend.DataTypes.Folder; +using Installation = InnovEnergy.App.Backend.DataTypes.Installation; +using Object = System.Object; +using User = InnovEnergy.App.Backend.DataTypes.User; namespace InnovEnergy.App.Backend.Controllers; [ApiController] -[Microsoft.AspNetCore.Mvc.Route("api/")] +[Route("api/")] public class Controller { + private static readonly HttpResponseMessage _Unauthorized = new HttpResponseMessage(Unauthorized); + private static readonly HttpResponseMessage _Ok = new HttpResponseMessage(OK); + private static readonly HttpResponseMessage _BadRequest = new HttpResponseMessage(BadRequest); + [Returns] - [Returns(HttpStatusCode.Unauthorized)] - [Returns(HttpStatusCode.BadRequest)] - [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(Login)}")] + [Returns(Unauthorized)] + [Returns(BadRequest)] + [HttpPost($"{nameof(Login)}")] public Object Login(Credentials credentials) { - if (String.IsNullOrWhiteSpace(credentials.Username) || - String.IsNullOrWhiteSpace(credentials.Password)) - return new HttpResponseException(HttpStatusCode.BadRequest); + var session = credentials.Login(); - using var db = Db.Connect(); - var user = db.GetUserByEmail(credentials.Username); - - - if (user is null) - return new HttpResponseException(HttpStatusCode.BadRequest); - - if (!VerifyPassword(credentials.Password, user)) - return new HttpResponseException(HttpStatusCode.Unauthorized); - - var ses = new Session(user); - db.NewSession(ses); - return new {ses.Token, user.Language}; + return session is null + ? _Unauthorized + : session; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(Logout)}")] + [Returns(OK)] + [Returns(Unauthorized)] + [HttpPost($"{nameof(Logout)}")] public Object Logout() { - var caller = GetCaller(); + var session = GetSession(); - if (caller is null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - return db.DeleteSession(caller.Id); - } - - - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetInstallationS3Key)}")] - public Object GetInstallationS3Key(Int64 installationId) - { - var caller = GetCaller(); - if (caller is null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var installation = db - .GetAllAccessibleInstallations(caller) - .FirstOrDefault(i => i.Id == installationId); - - if(installation == null) - { - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - } - - var key = db.GetInstallationS3Key(installationId); - return key ?? db.CreateAndSaveInstallationS3ApiKey(installation); + return session.Logout() + ? _Ok + : _Unauthorized; } - [Returns] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetUserById)}")] - public Object GetUserById(Int64 id) - { - var caller = GetCaller(); - if (caller is null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); + // [Returns] + // [Returns(HttpStatusCode.Unauthorized)] + // [HttpGet($"{nameof(GetUserById)}")] + // public Object GetUserById(Int64 id) + // { + // var caller = GetCaller(); + // if (caller is null) + // return new HttpResponseMessage(HttpStatusCode.Unauthorized); + // + // var user = Db.GetUserById(id); + // + // if (user is null || !caller.HasAccessTo(user)) + // return new HttpResponseMessage(HttpStatusCode.Unauthorized); + // + // return user; + // } - var user = db - .GetDescendantUsers(caller) - .FirstOrDefault(u => u.Id == id); - - return user as Object ?? new HttpResponseMessage(HttpStatusCode.Unauthorized); - } + // + // [Returns] + // [Returns(HttpStatusCode.Unauthorized)] + // [HttpGet($"{nameof(GetInstallationById)}")] + // public Object GetInstallationById(Int64 id) + // { + // var caller = GetCaller(); + // if (caller == null) + // return new HttpResponseMessage(HttpStatusCode.Unauthorized); + // + // var installation = Db.GetInstallationById(id); + // + // if (installation is null || !caller.HasAccessTo(installation)) + // return new HttpResponseMessage(HttpStatusCode.Unauthorized); + // + // return installation; + // } - [Returns] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetInstallationById)}")] - public Object GetInstallationById(Int64 id) - { - var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var installation = db - .GetAllAccessibleInstallations(caller) - .FirstOrDefault(i => i.Id == id); - - return installation as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound); - } - - - [Returns] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetFolderById)}")] - public Object GetFolderById(Int64 id) - { - var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var folder = db - .GetAllAccessibleFolders(caller) - .FirstOrDefault(f => f.Id == id); - - return folder as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound); - } + // [Returns] + // [Returns(HttpStatusCode.Unauthorized)] + // [HttpGet($"{nameof(GetFolderById)}")] + // public Object GetFolderById(Int64 id) + // { + // var caller = GetCaller(); + // if (caller == null) + // return new HttpResponseMessage(HttpStatusCode.Unauthorized); + // + // var folder = Db.GetFolderById(id); + // + // if (folder is null || !caller.HasAccessTo(folder)) + // return new HttpResponseMessage(HttpStatusCode.Unauthorized); + // + // return folder; + // } [Returns] // assuming swagger knows about arrays but not lists (JSON) - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllInstallations)}/")] + [Returns(Unauthorized)] + [HttpGet($"{nameof(GetAllInstallations)}/")] public Object GetAllInstallations() { - var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); + var user = GetSession()?.User; - return db - .GetAllAccessibleInstallations(caller) - .ToList(); // important! + return user is null + ? _Unauthorized + : user.AccessibleInstallations(); } [Returns] // assuming swagger knows about arrays but not lists (JSON) - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllFolders)}/")] + [Returns(Unauthorized)] + [HttpGet($"{nameof(GetAllFolders)}/")] public Object GetAllFolders() { - var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - return db - .GetAllAccessibleFolders(caller) - .ToList(); // important! + var user = GetSession()?.User; + + return user is null + ? _Unauthorized + : user.AccessibleFolders(); } - [Returns] // assuming swagger knows about arrays but not lists (JSON) - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetTree)}/")] - public Object GetTree() - { - var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var folders = db - .GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure - .Select(f => PopulateChildren(db, f)); - - var installations = db.GetDirectlyAccessibleInstallations(caller); - - return folders - .Concat(installations) - .ToList(); // important! - } + // [Returns] // assuming swagger knows about arrays but not lists (JSON) + // [Returns(Unauthorized)] + // [HttpGet($"{nameof(GetUsersOfFolder)}/")] + // public Object GetUsersOfFolder(Int64 folderId) + // { + // var caller = GetCaller(); + // if (caller == null) + // return new HttpResponseMessage(Unauthorized); + // + // var folder = Db.GetFolderById(folderId); + // + // if (folder is null || !caller.HasAccessTo(folder)) + // return new HttpResponseMessage(Unauthorized); + // + // return descendantUsers; + // } [Returns] // assuming swagger knows about arrays but not lists (JSON) - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")] + [Returns(Unauthorized)] + [HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")] public Object GetAllFoldersAndInstallations() { - var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var folders = db.GetAllAccessibleFolders(caller) as IEnumerable; - var installations = db.GetAllAccessibleInstallations(caller); + var user = GetSession()?.User; - return folders - .Concat(installations) - .ToList(); // important! + return user is null + ? _Unauthorized + : user.AccessibleFoldersAndInstallations(); } - - private static Folder PopulateChildren(Db db, Folder folder, HashSet? hs = null) - { - // TODO: remove cycle detector - hs ??= new HashSet(); - if (!hs.Add(folder.Id)) - throw new Exception("Cycle detected: folder " + folder.Id); - var installations = db.GetChildInstallations(folder); - var folders = db - .GetChildFolders(folder) - .Select(c => PopulateChildren(db, c, hs)); - - folder.Children = folders.Concat(installations).ToList(); - - return folder; - } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateUser)}/")] + + [Returns(OK)] + [Returns(Unauthorized)] + [HttpPost($"{nameof(CreateUser)}/")] public Object CreateUser(User newUser) { - var caller = GetCaller(); - using var db = Db.Connect(); - if (caller == null || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); + var session = GetSession(); - newUser.ParentId = caller.Id; - - return db.CreateUser(newUser); + return session.Create(newUser) + ? newUser + : _Unauthorized ; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateInstallation)}/")] + [Returns(OK)] + [Returns(Unauthorized)] + [HttpPost($"{nameof(CreateInstallation)}/")] public Object CreateInstallation(Installation installation) { - var caller = GetCaller(); - using var db = Db.Connect(); - if (caller == null || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - var id = db.CreateInstallation(installation); - - return db.AddToAccessibleInstallations(caller.Id, id); + var session = GetSession(); + return session.Create(installation) + ? installation + : _Unauthorized; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateFolder)}/")] + [Returns(OK)] + [Returns(Unauthorized)] + [Returns(InternalServerError)] + [HttpPost($"{nameof(CreateFolder)}/")] public Object CreateFolder(Folder folder) { - var caller = GetCaller(); - using var db = Db.Connect(); - if (caller == null || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - var id = db.CreateFolder(folder); - return db.AddToAccessibleFolders(caller.Id, id); + var session = GetSession(); + return session.Create(folder) + ? folder + : _Unauthorized; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateUser)}/")] + [Returns(OK)] + [Returns(Unauthorized)] + [HttpPut($"{nameof(UpdateUser)}/")] public Object UpdateUser(User updatedUser) { - var caller = GetCaller(); - using var db = Db.Connect(); - if (caller == null || !db.IsParentOfChild(caller.Id, updatedUser) || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - return db.UpdateUser(updatedUser); + var session = GetSession(); + + return session.Update(updatedUser) + ? updatedUser + : _Unauthorized; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateInstallation)}/")] + [Returns(OK)] + [Returns(Unauthorized)] + [HttpPut($"{nameof(UpdateInstallation)}/")] public Object UpdateInstallation(Installation installation) { - var caller = GetCaller(); + var session = GetSession(); - if (caller is null || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var installationFromAccessibleInstallations = db - .GetAllAccessibleInstallations(caller) - .FirstOrDefault(i => i.Id == installation.Id); - - if (installationFromAccessibleInstallations == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - // TODO: accessibility by other users etc - // TODO: sanity check changes - // foreach(var property in installationFromAccessibleInstallations.GetType().GetProperties()){ - // if(installation.GetType().GetProperties().Contains(property)) - // { - // property.SetValue(installationFromAccessibleInstallations, property.GetValue(installation)); - // } - // } - - return db.UpdateInstallation(installation); + return session.Update(installation) + ? installation + : _Unauthorized; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateFolder)}/")] + [Returns(OK)] + [Returns(Unauthorized)] + [HttpPut($"{nameof(UpdateFolder)}/")] public Object UpdateFolder(Folder folder) { - var caller = GetCaller(); + var session = GetSession(); - if (caller is null || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var installationFromAccessibleFolders = db - .GetAllAccessibleFolders(caller) - .FirstOrDefault(f => f.Id == folder.Id); - - if (installationFromAccessibleFolders == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - // TODO: accessibility by other users etc - // TODO: sanity check changes - - // foreach(var property in installationFromAccessibleFolders.GetType().GetProperties()){ - // if(folder.GetType().GetProperties().Contains(property)) - // { - // property.SetValue(installationFromAccessibleFolders, property.GetValue(folder)); - // } - // } - - return db.UpdateFolder(installationFromAccessibleFolders); + return session.Update(folder) + ? folder + : _Unauthorized; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteUser)}/")] + [Returns(OK)] + [Returns(Unauthorized)] + [HttpDelete($"{nameof(DeleteUser)}/")] public Object DeleteUser(Int64 userId) { - var caller = GetCaller(); - - if (caller is null || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); - - var userToBeDeleted = db - .GetDescendantUsers(caller) - .FirstOrDefault(u => u.Id == userId); + var session = GetSession(); + var user = Db.GetUserById(userId); - if (userToBeDeleted is null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - return db.DeleteUser(userToBeDeleted); + return session.Delete(user) + ? _Ok + : _Unauthorized; } - [Returns(HttpStatusCode.OK)] - [Returns(HttpStatusCode.Unauthorized)] - [Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteInstallation)}/")] + [Returns(OK)] + [Returns(Unauthorized)] + [HttpDelete($"{nameof(DeleteInstallation)}/")] public Object DeleteInstallation(Int64 installationId) { - var caller = GetCaller(); - - if (caller is null || !caller.HasWriteAccess) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); + var session = GetSession(); + var installation = Db.GetInstallationById(installationId); - var installationToBeDeleted = db - .GetAllAccessibleInstallations(caller) - .FirstOrDefault(i => i.Id == installationId); - - if (installationToBeDeleted is null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - return db.DeleteInstallation(installationToBeDeleted); + return session.Delete(installation) + ? _Ok + : _Unauthorized; } - [ProducesResponseType(200)] [ProducesResponseType(401)] - [Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteFolder)}/")] + [HttpDelete($"{nameof(DeleteFolder)}/")] public Object DeleteFolder(Int64 folderId) { - var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - using var db = Db.Connect(); + var session = GetSession(); - var folderToDelete = db - .GetAllAccessibleFolders(caller) - .FirstOrDefault(f => f.Id == folderId); + var folder = Db.GetFolderById(folderId); + + return session.Delete(folder) + ? _Ok + : _Unauthorized; - if (folderToDelete is null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - - return db.DeleteFolder(folderToDelete); } - - private static User? GetCaller() + private static Session? GetSession() { var ctxAccessor = new HttpContextAccessor(); - return ctxAccessor.HttpContext?.Items["User"] as User; + return ctxAccessor.HttpContext?.Items["Session"] as Session; } - - private static Boolean VerifyPassword(String password, User user) - { - var pwdBytes = Encoding.UTF8.GetBytes(password); - var saltBytes = Encoding.UTF8.GetBytes(user.Salt + "innovEnergy"); - var pwdHash = Crypto.ComputeHash(pwdBytes, saltBytes); - - return user.Password == pwdHash; - } - } diff --git a/csharp/App/Backend/Controllers/Credentials.cs b/csharp/App/Backend/Controllers/Credentials.cs deleted file mode 100644 index f3149f7e8..000000000 --- a/csharp/App/Backend/Controllers/Credentials.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace InnovEnergy.App.Backend.Controllers; - -[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] -public record Credentials(String Username, String Password); \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Credentials.cs b/csharp/App/Backend/DataTypes/Credentials.cs new file mode 100644 index 000000000..d17c3f063 --- /dev/null +++ b/csharp/App/Backend/DataTypes/Credentials.cs @@ -0,0 +1,6 @@ +using System.Diagnostics.CodeAnalysis; + +namespace InnovEnergy.App.Backend.DataTypes; + +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] +public record Credentials(String Username, String Password); \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Folder.cs b/csharp/App/Backend/DataTypes/Folder.cs new file mode 100644 index 000000000..f383ef483 --- /dev/null +++ b/csharp/App/Backend/DataTypes/Folder.cs @@ -0,0 +1,3 @@ +namespace InnovEnergy.App.Backend.DataTypes; + +public class Folder : TreeNode {} \ No newline at end of file diff --git a/csharp/App/Backend/Model/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs similarity index 80% rename from csharp/App/Backend/Model/Installation.cs rename to csharp/App/Backend/DataTypes/Installation.cs index b6c4b7968..c3f60e2e2 100644 --- a/csharp/App/Backend/Model/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -1,4 +1,4 @@ -namespace InnovEnergy.App.Backend.Model; +namespace InnovEnergy.App.Backend.DataTypes; public class Installation : TreeNode @@ -14,7 +14,6 @@ public class Installation : TreeNode public Double Long { get; set; } public String S3Bucket { get; set; } = ""; - public String? S3Key { get; set; } - -} + public String S3Key { get; set; } = ""; +} \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Methods/Credentials.cs b/csharp/App/Backend/DataTypes/Methods/Credentials.cs new file mode 100644 index 000000000..06db0d78c --- /dev/null +++ b/csharp/App/Backend/DataTypes/Methods/Credentials.cs @@ -0,0 +1,25 @@ +using InnovEnergy.App.Backend.Database; +using InnovEnergy.App.Backend.Relations; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.App.Backend.DataTypes.Methods; + +public static class CredentialsMethods +{ + public static Session? Login(this Credentials credentials) + { + if (credentials.Username.IsNull() || credentials.Password.IsNull()) + return null; + + var user = Db.GetUserByEmail(credentials.Username); + + if (user is null || !user.VerifyPassword(credentials.Password)) + return null; + + var session = new Session(user); + + return Db.Create(session) + ? session + : null; + } +} \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Methods/Folder.cs b/csharp/App/Backend/DataTypes/Methods/Folder.cs new file mode 100644 index 000000000..274574a4f --- /dev/null +++ b/csharp/App/Backend/DataTypes/Methods/Folder.cs @@ -0,0 +1,73 @@ +using InnovEnergy.App.Backend.Database; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.App.Backend.DataTypes.Methods; + +public static class FolderMethods +{ + public static IEnumerable ChildFolders(this Folder parent) + { + return Db + .Folders + .Where(f => f.ParentId == parent.Id); + } + + public static IEnumerable ChildInstallations(this Folder parent) + { + return Db + .Installations + .Where(f => f.ParentId == parent.Id); + } + + public static IEnumerable DescendantFolders(this Folder parent) + { + return parent.Traverse(ChildFolders); + } + + public static Boolean IsDescendantOf(this Folder folder, Folder ancestor) + { + return folder + .Ancestors() + .Any(u => u.Id == ancestor.Id); + } + + public static IEnumerable Ancestors(this Folder folder) + { + return folder.Unfold(Parent); + } + + public static Folder? Parent(this Folder folder) + { + return IsAbsoluteRoot(folder) + ? null + : Db.GetFolderById(folder.ParentId); + } + + public static Boolean IsAbsoluteRoot(this Folder folder) + { + return folder.ParentId == 0; // root has ParentId 0 by definition + } + + public static Boolean IsRelativeRoot(this Folder folder) + { + return folder.ParentId < 0; // root has ParentId 0 by definition + } + + public static Boolean WasMoved(this Folder folder) + { + if (folder.IsRelativeRoot()) + return false; + + var existingFolder = Db.GetFolderById(folder.Id); + + return existingFolder is not null + && existingFolder.ParentId != folder.ParentId; + } + + public static Boolean Exists(this Folder folder) + { + return Db.Folders.Any(f => f.Id == folder.Id); + } + +} + diff --git a/csharp/App/Backend/DataTypes/Methods/Installation.cs b/csharp/App/Backend/DataTypes/Methods/Installation.cs new file mode 100644 index 000000000..795e8a1dc --- /dev/null +++ b/csharp/App/Backend/DataTypes/Methods/Installation.cs @@ -0,0 +1,47 @@ +using InnovEnergy.App.Backend.Database; + +namespace InnovEnergy.App.Backend.DataTypes.Methods; + + +public static class InstallationMethods +{ + public static IEnumerable Ancestors(this Installation installation) + { + var parentFolder = Parent(installation); + + return parentFolder is null + ? Enumerable.Empty() + : parentFolder.Ancestors(); + } + + public static Folder? Parent(this Installation installation) + { + return installation.IsRelativeRoot() + ? null + : Db.GetFolderById(installation.ParentId); + } + + public static Boolean IsRelativeRoot(this Installation i) + { + return i.ParentId < 0; + } + + public static Boolean WasMoved(this Installation installation) + { + if (installation.IsRelativeRoot()) + return false; + + var existingInstallation = Db.GetInstallationById(installation.Id); + + return existingInstallation is not null + && existingInstallation.ParentId != installation.ParentId; + } + + public static Boolean Exists(this Installation installation) + { + return Db.Installations.Any(i => i.Id == installation.Id); + } + + +} + diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs new file mode 100644 index 000000000..fc66f90d1 --- /dev/null +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -0,0 +1,119 @@ +using InnovEnergy.App.Backend.Database; +using InnovEnergy.App.Backend.Relations; + +namespace InnovEnergy.App.Backend.DataTypes.Methods; + +public static class SessionMethods +{ + public static Boolean Create(this Session? session, Folder? folder) + { + var user = session?.User; + + return user is not null + && folder is not null + && user.HasWriteAccess + && user.HasAccessTo(folder.Parent()) + && Db.Create(folder); + } + + public static Boolean Update(this Session? session, Folder? folder) + { + var user = session?.User; + + return user is not null + && folder is not null + && user.HasWriteAccess + && user.HasAccessTo(folder) + && (folder.IsRelativeRoot() || user.HasAccessTo(folder.Parent())) + && Db.Update(folder); + } + + public static Boolean Delete(this Session? session, Folder? folder) + { + var user = session?.User; + + return user is not null + && folder is not null + && user.HasWriteAccess + && user.HasAccessTo(folder) // TODO: && user.HasAccessTo(folder.Parent()) ??? + && Db.Delete(folder); + } + + + public static Boolean Create(this Session? session, Installation? installation) + { + var user = session?.User; + + return user is not null + && installation is not null + && user.HasWriteAccess + && user.HasAccessTo(installation.Parent()) + && Db.Create(installation); + } + + public static Boolean Update(this Session? session, Installation? installation) + { + var user = session?.User; + + return user is not null + && installation is not null + && user.HasWriteAccess + && installation.Exists() + && user.HasAccessTo(installation) + && (installation.IsRelativeRoot() || user.HasAccessTo(installation.Parent())) // TODO: triple check this + && Db.Update(installation); + } + + public static Boolean Delete(this Session? session, Installation? installation) + { + var user = session?.User; + + return user is not null + && installation is not null + && user.HasWriteAccess + && user.HasAccessTo(installation) // TODO: && user.HasAccessTo(installation.Parent()) ??? + && Db.Delete(installation); + } + + public static Boolean Create(this Session? session, User? newUser) + { + var sessionUser = session?.User; + + if (sessionUser is null || newUser is null || !sessionUser.HasWriteAccess) + return false; + + newUser.ParentId = sessionUser.Id; // Important! + + return Db.Create(newUser); + } + + public static Boolean Update(this Session? session, User? editedUser) + { + var sessionUser = session?.User; + + return sessionUser is not null + && editedUser is not null + && sessionUser.HasWriteAccess + && sessionUser.HasAccessTo(editedUser) + && (editedUser.IsRelativeRoot() || sessionUser.HasAccessTo(editedUser.Parent())) // TODO: triple check this + && Db.Update(editedUser); + } + + public static Boolean Delete(this Session? session, User? userToDelete) + { + var sessionUser = session?.User; + + return sessionUser is not null + && userToDelete is not null + && sessionUser.HasWriteAccess + && sessionUser.HasAccessTo(userToDelete) // TODO: && user.HasAccessTo(installation.Parent()) ??? + && Db.Delete(userToDelete); + } + + public static Boolean Logout(this Session? session) + { + return session is not null + && Db.Sessions.Delete(s => s.Token == session.Token) > 0; + } + +} \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Methods/User.cs b/csharp/App/Backend/DataTypes/Methods/User.cs new file mode 100644 index 000000000..99988f7e8 --- /dev/null +++ b/csharp/App/Backend/DataTypes/Methods/User.cs @@ -0,0 +1,342 @@ +using System.Net.Http.Headers; +using System.Net.Mail; +using System.Security.Cryptography; +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; +using InnovEnergy.App.Backend.Database; +using InnovEnergy.Lib.Utils; +using Convert = System.Convert; +using static System.Text.Encoding; + + +namespace InnovEnergy.App.Backend.DataTypes.Methods; + + +public static class UserMethods +{ + public static IEnumerable AccessibleInstallations(this User user) + { + var direct = user.DirectlyAccessibleInstallations(); + var fromFolders = user + .AccessibleFolders() + .SelectMany(u => u.ChildInstallations()); + + return direct + .Concat(fromFolders) + .Distinct(); + } + + public static IEnumerable AccessibleFolders(this User user) + { + return user + .DirectlyAccessibleFolders() + .SelectMany(f => f.DescendantFolders()) + .Distinct(); + + // Distinct because the user might have direct access + // to a child folder of a folder he has already access to + } + + public static IEnumerable AccessibleFoldersAndInstallations(this User user) + { + var folders = user.AccessibleFolders() as IEnumerable; + var installations = user.AccessibleInstallations(); + + return folders.Concat(installations); + } + + public static IEnumerable DirectlyAccessibleInstallations(this User user) + { + return Db + .User2Installation + .Where(r => r.UserId == user.Id) + .Select(r => r.InstallationId) + .Select(Db.GetInstallationById) + .NotNull() + .Do(i => i.ParentId = -1); // hide inaccessible parents from calling user + } + + public static IEnumerable DirectlyAccessibleFolders(this User user) + { + return Db + .User2Folder + .Where(r => r.UserId == user.Id) + .Select(r => r.FolderId) + .Select(Db.GetFolderById) + .NotNull() + .Do(i => i.ParentId = -1); // hide inaccessible parents from calling user; + } + + public static IEnumerable ChildUsers(this User parent) + { + return Db + .Users + .Where(f => f.ParentId == parent.Id); + } + + public static IEnumerable DescendantUsers(this User parent) + { + return parent.Traverse(ChildUsers); + } + + public static Boolean IsDescendantOf(this User user, User ancestor) + { + return user + .Ancestors() + .Any(u => u.Id == ancestor.Id); + } + + private static IEnumerable Ancestors(this User user) + { + return user.Unfold(Parent); + } + + public static Boolean VerifyPassword(this User user, String password) + { + return user.Password == user.SaltAndHashPassword(password); + } + + public static String SaltAndHashPassword(this User user, String password) + { + var dataToHash = $"{password}{user.Salt()}"; + + return dataToHash + .Apply(UTF8.GetBytes) + .Apply(SHA256.HashData) + .Apply(Convert.ToBase64String); + } + + public static User? Parent(this User u) + { + return u.IsAbsoluteRoot() + ? null + : Db.GetUserById(u.ParentId); + } + + public static Boolean IsAbsoluteRoot(this User u) + { + return u.ParentId == 0; + } + + public static Boolean IsRelativeRoot(this User u) + { + return u.ParentId < 0; + } + + public static Boolean HasDirectAccessTo(this User user, Folder folder) + { + return Db + .User2Folder + .Any(r => r.FolderId == folder.Id && r.UserId == user.Id); + } + + public static Boolean HasAccessTo(this User user, Folder? folder) + { + if (folder is null) + return false; + + return folder + .Ancestors() + .Any(user.HasDirectAccessTo); + } + + public static Boolean HasDirectAccessTo(this User user, Installation installation) + { + return Db + .User2Installation + .Any(r => r.InstallationId == installation.Id && r.UserId == user.Id); + } + + public static Boolean HasAccessTo(this User user, Installation? installation) + { + if (installation is null) + return false; + + return user.HasDirectAccessTo(installation) || + installation.Ancestors().Any(user.HasDirectAccessTo); + } + + public static Boolean HasAccessTo(this User user, User? other) + { + if (other is null) + return false; + + return other + .Ancestors() + .Skip(1) // Important! skip self, user cannot delete or edit himself + .Contains(user); + } + + public static String Salt(this User user) + { + // + id => salt unique per user + // + InnovEnergy => globally unique + + return $"{user.Id}InnovEnergy"; + } + + + private static Byte[] HmacSha256Digest(String message, String secret) + { + // var encoding = new UTF8Encoding(); + // var keyBytes = encoding.GetBytes(secret); + // var messageBytes = encoding.GetBytes(message); + // var cryptographer = new HMACSHA256(keyBytes); + // return cryptographer.ComputeHash(messageBytes); + + var keyBytes = UTF8.GetBytes(secret); + var messageBytes = UTF8.GetBytes(message); + + return HMACSHA256.HashData(keyBytes, messageBytes); + } + + private static String BuildSignature(String method, String path, String data, Int64 time, String secret) + { + var messageToSign = ""; + messageToSign += method + " /v2/" + path + "\n"; + messageToSign += data + "\n"; + + // query strings + messageToSign += "\n"; + // headers + messageToSign += "\n"; + + messageToSign += time; + + Console.WriteLine("Message to sign:\n" + messageToSign); + + var hmac = HmacSha256Digest(messageToSign, secret); + return Convert.ToBase64String(hmac); + } + + // public Object CreateAndSaveUserS3ApiKey(User user) + // { + // //EXOSCALE API URL + // const String url = "https://api-ch-dk-2.exoscale.com/v2/"; + // const String path = "access-key"; + // + // //TODO HIDE ME + // const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; + // const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; + // + // var installationList = User2Installation + // .Where(i => i.UserId == user.Id) + // .SelectMany(i => Installations.Where(f => i.InstallationId == f.Id)) + // .ToList(); + // + // + // var instList = new JsonArray(); + // + // foreach (var installation in installationList) + // { + // instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"}); + // } + // + // var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList}; + // var stringPayload = jsonPayload.ToJsonString(); + // + // var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; + // var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret); + // + // var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature; + // + // var client = new HttpClient(); + // client.DefaultRequestHeaders.Authorization = + // new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader); + // + // var content = new StringContent(stringPayload, Encoding.UTF8, "application/json"); + // + // + // var response = client.PostAsync(url+path, content).Result; + // + // if (response.StatusCode.ToString() != "OK") + // { + // return response; + // } + // + // var responseString = response.Content.ReadAsStringAsync().Result; + // return Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"')); + // // return SetUserS3ApiKey(user, newKey); + // + // } + + public static Object CreateAndSaveInstallationS3ApiKey(Installation installation) + { + //EXOSCALE API URL + const String url = "https://api-ch-dk-2.exoscale.com/v2/"; + const String path = "access-key"; + + //TODO HIDE ME + const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; + const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; + + + var jsonPayload = new JsonObject + { + ["name"] = installation.Id, + ["operations"] = new JsonArray + { + "list-sos-bucket", + "get-sos-object" + }, + ["content"] = new JsonArray + { + new JsonObject + { + ["domain"] = "sos", + ["resource-name"] = installation.Name, + ["resource-type"] = "bucket" + } + } + }; + + var stringPayload = jsonPayload.ToJsonString(); + + var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60; + + var signature = BuildSignature("POST", path, stringPayload, unixExpiration, secret); + + var authHeader = "credential=" + apiKey + ",expires=" + unixExpiration + ",signature=" + signature; + + var client = new HttpClient(); + + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader); + + var content = new StringContent(stringPayload, UTF8, "application/json"); + + var response = client.PostAsync(url + path, content).Result; + + if (response.StatusCode.ToString() != "OK") + { + return response; + } + + var responseString = response.Content.ReadAsStringAsync().Result; + var newKey = Regex + .Match(responseString, "key\\\":\\\"([A-Z])\\w+") + .ToString() + .Split('"') + .Last(); + + installation.S3Key = newKey; + Db.Update(installation); + return newKey; + } + + + + // TODO + private static Boolean IsValidEmail(String email) + { + try + { + var emailAddress = new MailAddress(email); + } + catch + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/TreeNode.Equality.cs b/csharp/App/Backend/DataTypes/TreeNode.Equality.cs new file mode 100644 index 000000000..e9c6767df --- /dev/null +++ b/csharp/App/Backend/DataTypes/TreeNode.Equality.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; + +namespace InnovEnergy.App.Backend.DataTypes; + +public abstract partial class TreeNode +{ + public override Boolean Equals(Object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + + return Equals((TreeNode)obj); + } + + protected Boolean Equals(TreeNode other) => Id == other.Id; + + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] + public override Int32 GetHashCode() => Id.GetHashCode(); + + public static Boolean operator ==(TreeNode? left, TreeNode? right) => Equals(left, right); + public static Boolean operator !=(TreeNode? left, TreeNode? right) => !Equals(left, right); +} \ No newline at end of file diff --git a/csharp/App/Backend/Model/TreeNode.cs b/csharp/App/Backend/DataTypes/TreeNode.cs similarity index 67% rename from csharp/App/Backend/Model/TreeNode.cs rename to csharp/App/Backend/DataTypes/TreeNode.cs index d02890a62..de24da0b8 100644 --- a/csharp/App/Backend/Model/TreeNode.cs +++ b/csharp/App/Backend/DataTypes/TreeNode.cs @@ -1,6 +1,6 @@ using SQLite; -namespace InnovEnergy.App.Backend.Model; +namespace InnovEnergy.App.Backend.DataTypes; public abstract partial class TreeNode { @@ -12,10 +12,7 @@ public abstract partial class TreeNode [Indexed] // parent/child relation public Int64 ParentId { get; set; } - [Ignore] // not in DB, can be used in typescript as type discriminator + [Ignore] public String Type => GetType().Name; - [Ignore] - public IReadOnlyList? Children { get; set; } - } \ No newline at end of file diff --git a/csharp/App/Backend/Model/User.cs b/csharp/App/Backend/DataTypes/User.cs similarity index 75% rename from csharp/App/Backend/Model/User.cs rename to csharp/App/Backend/DataTypes/User.cs index 6e4e248ad..70ea9e034 100644 --- a/csharp/App/Backend/Model/User.cs +++ b/csharp/App/Backend/DataTypes/User.cs @@ -1,17 +1,14 @@ using SQLite; -namespace InnovEnergy.App.Backend.Model; +namespace InnovEnergy.App.Backend.DataTypes; public class User : TreeNode { [Indexed] public String Email { get; set; } = null!; public Boolean HasWriteAccess { get; set; } = false; - public String Salt { get; set; } = null!; public String Language { get; set; } = null!; public String Password { get; set; } = null!; // TODO: must reset pwd -} - - \ No newline at end of file +} \ No newline at end of file diff --git a/csharp/App/Backend/Database/Create.cs b/csharp/App/Backend/Database/Create.cs new file mode 100644 index 000000000..875b5ddef --- /dev/null +++ b/csharp/App/Backend/Database/Create.cs @@ -0,0 +1,38 @@ +using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.App.Backend.DataTypes.Methods; +using InnovEnergy.App.Backend.Relations; + + +namespace InnovEnergy.App.Backend.Database; + + +public static partial class Db +{ + public static Boolean Create(Installation installation) + { + // SQLite wrapper is smart and *modifies* t's Id to the one generated (autoincrement) by the insertion + return Connection.Insert(installation) > 0; + } + + public static Boolean Create(Folder folder) + { + return Connection.Insert(folder) > 0; + } + + public static Boolean Create(User user) + { + if (GetUserByEmail(user.Email) is not null) // TODO: User unique by username instead of email? + return false; + + user.Password = user.SaltAndHashPassword(user.Password); + + return Connection.Insert(user) > 0; + } + + + public static Boolean Create(Session session) + { + return Connection.Insert(session) > 0; + } + +} \ No newline at end of file diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index b2e488228..3e2fd815c 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -1,16 +1,20 @@ -using System.Diagnostics.CodeAnalysis; -using InnovEnergy.App.Backend.Model; -using InnovEnergy.App.Backend.Model.Relations; +using System.Reactive.Linq; +using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.App.Backend.DataTypes.Methods; +using InnovEnergy.App.Backend.Relations; using InnovEnergy.Lib.Utils; using SQLite; + + namespace InnovEnergy.App.Backend.Database; + public static partial class Db { internal const String DbPath = "./db.sqlite"; - public static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath); + private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath); public static TableQuery Sessions => Connection.Table(); public static TableQuery Folders => Connection.Table(); @@ -18,149 +22,55 @@ public static partial class Db public static TableQuery Users => Connection.Table(); public static TableQuery User2Folder => Connection.Table(); public static TableQuery User2Installation => Connection.Table(); - - public static Int32 NbUser2Installation => User2Installation.Count(); - public static Int32 NbUser2Folder => User2Folder.Count(); - public static Int32 NbFolders => Folders.Count(); - public static Int32 NbInstallations => Installations.Count(); - public static Int32 NbUsers => Users.Count(); - - - public static Folder? GetFolderById(Int64 id) - { - return Folders - .FirstOrDefault(f => f.Id == id); - } - - public static Installation? GetInstallationById(Int64 id) - { - return Installations - .FirstOrDefault(i => i.Id == id); - } - - public static User? GetUserById(Int64 id) - { - return Users - .FirstOrDefault(u => u.Id == id); - } - [SuppressMessage("ReSharper", "AccessToDisposedClosure")] static Db() { // on startup create/migrate tables - using var db = new SQLiteConnection(DbPath); - - db.RunInTransaction(() => + Connection.RunInTransaction(() => { - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); }); + + + var installation = Installations.First(); + UserMethods.CreateAndSaveInstallationS3ApiKey(installation); + + + Observable.Interval(TimeSpan.FromDays(1)) + .StartWith(0) // Do it right away (on startup) + .Subscribe(Cleanup); // and then daily } - - // the C in CRUD - private static Int64 Create(TreeNode treeNode) + + + private static Boolean RunTransaction(Func func) { + var savepoint = Connection.SaveTransactionPoint(); + var success = false; + try { - Connection.Insert(treeNode); - return SQLite3.LastInsertRowid(Connection.Handle); + success = func(); } - catch (Exception e) + finally { - return -1; + if (success) + Connection.Release(savepoint); + else + Connection.RollbackTo(savepoint); } + + return success; } - - private static Boolean Create(Session session) - { - try - { - Connection.Insert(session); - return true; - } - catch (Exception e) - { - return false; - } - } - - // the U in CRUD - private static Boolean Update(TreeNode treeNode) - { - try - { - Connection.InsertOrReplace(treeNode); - return true; - } - catch (Exception e) - { - return false; - } - } - - // the D in CRUD - private static Boolean Delete(TreeNode treeNode) - { - try - { - Connection.Delete(treeNode); - return true; - } - catch (Exception e) - { - return false; - } - } - - public static IEnumerable GetAllAccessibleInstallations(User user) - { - var direct = GetDirectlyAccessibleInstallations(user); - var fromFolders = GetAllAccessibleFolders(user) - .SelectMany(GetChildInstallations); - - return direct - .Concat(fromFolders) - .Distinct(); - } - - public static IEnumerable GetAllAccessibleFolders(User user) - { - return GetDirectlyAccessibleFolders(user) - .SelectMany(GetDescendantFolders) - .Distinct(); - - // Distinct because the user might have direct access - // to a child folder of a folder he has already access to - } - - - public static IEnumerable GetDirectlyAccessibleInstallations(User user) - { - return User2Installation - .Where(r => r.UserId == user.Id) - .Select(r => r.InstallationId) - .Select(GetInstallationById) - .NotNull() - .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user - } - - public static IEnumerable GetDirectlyAccessibleFolders(User user) - { - return User2Folder - .Where(r => r.UserId == user.Id) - .Select(r => r.FolderId) - .Select(GetFolderById) - .NotNull() - .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user; - } + public static Boolean AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId) { @@ -201,45 +111,22 @@ public static partial class Db } - public static User? GetUserByToken(String token) + private static void Cleanup(Int64 _) { - return Sessions - .Where(s => s.Token == token).ToList() - .Where(s => s.Valid) - .Select(s => s.UserId) - .Select(GetUserById) - .FirstOrDefault(); + DeleteS3Keys(); + DeleteStaleSessions(); } - - public static Boolean NewSession(Session ses) => Create(ses); - - public static Boolean DeleteSession(Int64 id) + private static void DeleteStaleSessions() { - try - { - Sessions.Delete(u => u.UserId == id); - return true; - } - catch (Exception e) - { - return false; - } + var deadline = DateTime.Now - Session.MaxAge; + Sessions.Delete(s => s.LastSeen < deadline); } - public static String? GetInstallationS3Key(Int64 installationId) + private static void DeleteS3Keys() { - return Installations - .FirstOrDefault(i => i.Id == installationId)? - .S3Key; - } - - public static void DeleteAllS3Keys() - { - foreach (var installation in Installations.ToList()) - { - installation.S3Key = null; - Update(installation); - } + void DeleteKeys() => Installations.Do(i => i.S3Key = "").ForEach(Update); // TODO + + Connection.RunInTransaction(DeleteKeys); } } \ No newline at end of file diff --git a/csharp/App/Backend/Database/Delete.cs b/csharp/App/Backend/Database/Delete.cs new file mode 100644 index 000000000..0e495afde --- /dev/null +++ b/csharp/App/Backend/Database/Delete.cs @@ -0,0 +1,63 @@ +using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.App.Backend.DataTypes.Methods; +using InnovEnergy.App.Backend.Relations; + + +namespace InnovEnergy.App.Backend.Database; + + +public static partial class Db +{ + public static Boolean Delete(Folder folder) + { + return RunTransaction(DeleteFolderAndAllItsDependencies); + + Boolean DeleteFolderAndAllItsDependencies() + { + return folder + .DescendantFolders() + .All(DeleteDescendantFolderAndItsDependencies); + } + + Boolean DeleteDescendantFolderAndItsDependencies(Folder f) + { + User2Folder .Delete(r => r.FolderId == f.Id); + Installations.Delete(r => r.ParentId == f.Id); + + return Folders.Delete(r => r.Id == f.Id) > 0; + } + } + + public static Boolean Delete(Installation installation) + { + return RunTransaction(DeleteInstallationAndItsDependencies); + + Boolean DeleteInstallationAndItsDependencies() + { + User2Installation.Delete(i => i.InstallationId == installation.Id); + return Installations.Delete(i => i.Id == installation.Id) > 0; + } + } + + public static Boolean Delete(User user) + { + return RunTransaction(DeleteUserAndHisDependencies); + + Boolean DeleteUserAndHisDependencies() + { + User2Folder .Delete(u => u.UserId == user.Id); + User2Installation.Delete(u => u.UserId == user.Id); + + return Users.Delete(u => u.Id == user.Id) > 0; + } + } + + + #pragma warning disable CS0618 + + // private!! + private static Boolean Delete(Session session) + { + return Sessions.Delete(s => s.Id == session.Id) > 0; + } +} \ No newline at end of file diff --git a/csharp/App/Backend/Database/Fake.cs b/csharp/App/Backend/Database/Fake.cs index 7eca66ce8..ab144c11d 100644 --- a/csharp/App/Backend/Database/Fake.cs +++ b/csharp/App/Backend/Database/Fake.cs @@ -1,4 +1,4 @@ -using InnovEnergy.App.Backend.Model.Relations; +using InnovEnergy.App.Backend.Relations; namespace InnovEnergy.App.Backend.Database; @@ -18,7 +18,7 @@ public static partial class Db private static void CreateFakeUserTree() { - foreach (var userId in Enumerable.Range(1, NbUsers)) + foreach (var userId in Enumerable.Range(1, Users.Count())) { var user = GetUserById(userId); if (user is null) @@ -34,7 +34,7 @@ public static partial class Db private static void CreateFakeFolderTree() { - foreach (var folderId in Enumerable.Range(1, NbFolders)) + foreach (var folderId in Enumerable.Range(1, Folders.Count())) { var folder = GetFolderById(folderId); if (folder is null) @@ -50,7 +50,7 @@ public static partial class Db private static void LinkFakeInstallationsToFolders() { - var nFolders = NbFolders; + var nFolders = Folders.Count(); foreach (var installation in Installations) { @@ -64,8 +64,8 @@ public static partial class Db foreach (var uf in User2Folder) // remove existing relations Connection.Delete(uf); - var nFolders = NbFolders; - var nUsers = NbUsers; + var nFolders = Folders.Count(); + var nUsers = Users.Count(); foreach (var user in Users) while (Random.Shared.Next((Int32)(nUsers - user.Id + 1)) != 0) @@ -84,7 +84,7 @@ public static partial class Db foreach (var ui in User2Installation) // remove existing relations Connection.Delete(ui); - var nbInstallations = NbInstallations; + var nbInstallations = Installations.Count(); foreach (var user in Users) while (Random.Shared.Next(5) != 0) diff --git a/csharp/App/Backend/Database/Folder.cs b/csharp/App/Backend/Database/Folder.cs deleted file mode 100644 index 9383354c2..000000000 --- a/csharp/App/Backend/Database/Folder.cs +++ /dev/null @@ -1,91 +0,0 @@ -using InnovEnergy.App.Backend.Model; -using InnovEnergy.Lib.Utils; - -namespace InnovEnergy.App.Backend.Database; - -public static partial class Db -{ - public static IEnumerable GetChildFolders(this Folder parent) - { - return Folders.Where(f => f.ParentId == parent.Id); - } - - public static IEnumerable GetChildInstallations(this Folder parent) - { - return Installations.Where(f => f.ParentId == parent.Id); - } - - public static IEnumerable GetDescendantFolders(this Folder parent) - { - return parent.Traverse(GetChildFolders); - } - - public static Boolean IsDescendantOf(this Folder folder, Int64 ancestorFolderId) - { - return Ancestors(folder) - .Any(u => u.Id == ancestorFolderId); - } - - public static Boolean IsDescendantOf(this Folder folder, Folder ancestor) - { - return IsDescendantOf(folder, ancestor.Id); - } - - private static IEnumerable Ancestors(this Folder child) - { - return child.Unfold(GetParent); - } - - public static Folder? GetParent(this Folder f) - { - return IsRoot(f) - ? null - : GetFolderById(f.ParentId); - } - - public static Boolean IsRoot(this Folder f) - { - return f.ParentId == 0; // root has ParentId 0 by definition - } - - public static Int64 CreateFolder(Folder folder) - { - return Create(folder); - } - - public static Boolean UpdateFolder(Folder folder) - { - // TODO: no circles in path - - return Update(folder); - } - - // These should not be necessary, just Update folder/installation with new parentId - - // public Boolean ChangeParent(Installation child, Int64 parentId) - // { - // child.ParentId = parentId; - // return UpdateInstallation(child); - // } - // - // public Boolean ChangeParent(Folder child, Int64 parentId) - // { - // child.ParentId = parentId; - // return UpdateFolder(child); - // } - - public static Boolean DeleteFolder(Folder folder) - { - // Delete direct children - User2Folder .Delete(f => f.FolderId == folder.Id); - Installations.Delete(i => i.ParentId == folder.Id); - - // recursion - Folders.Where(f => f.ParentId == folder.Id) - .ForEach(DeleteFolder); - - return Delete(folder); - } - -} - diff --git a/csharp/App/Backend/Database/Installation.cs b/csharp/App/Backend/Database/Installation.cs deleted file mode 100644 index 3220637c0..000000000 --- a/csharp/App/Backend/Database/Installation.cs +++ /dev/null @@ -1,47 +0,0 @@ -using InnovEnergy.App.Backend.Model; -using SQLite; - -namespace InnovEnergy.App.Backend.Database; - -public static partial class Db -{ - public static IEnumerable Ancestors(this Installation installation) - { - var parentFolder = GetParent(installation); - - return parentFolder is null - ? Enumerable.Empty() - : Ancestors(parentFolder); - } - - public static Folder? GetParent(this Installation installation) - { - return IsRoot(installation) - ? null - : GetFolderById(installation.ParentId); - } - - public static Boolean IsRoot(this Installation i) - { - return i.ParentId == 0; // root has ParentId 0 by definition - } - - public static Int64 CreateInstallation(this Installation installation) - { - return Create(installation); - } - - public static Boolean UpdateInstallation(this Installation installation) - { - return Update(installation); - } - - public static Boolean DeleteInstallation(this Installation installation) - { - User2Installation.Delete(i => i.InstallationId == installation.Id); - - return Delete(installation); - } - -} - diff --git a/csharp/App/Backend/Database/Read.cs b/csharp/App/Backend/Database/Read.cs new file mode 100644 index 000000000..312d0c94d --- /dev/null +++ b/csharp/App/Backend/Database/Read.cs @@ -0,0 +1,86 @@ +using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.App.Backend.Relations; + + +namespace InnovEnergy.App.Backend.Database; + + +public static partial class Db +{ + public static Folder? GetFolderById(Int64 id) + { + return Folders + .FirstOrDefault(f => f.Id == id); + } + + public static Installation? GetInstallationById(Int64 id) + { + return Installations + .FirstOrDefault(i => i.Id == id); + } + + public static User? GetUserById(Int64 id) + { + return Users + .FirstOrDefault(u => u.Id == id); + } + + // private!! + private static Session? GetSessionById(Int64 id) + { + #pragma warning disable CS0618 + + return Sessions + .FirstOrDefault(u => u.Id == id); + + #pragma warning restore CS0618 + } + + + public static User? GetUserByEmail(String email) + { + return Users + .FirstOrDefault(u => u.Email == email); + } + + public static Session? GetSession(String token) + { + var session = Sessions + .FirstOrDefault(s => s.Token == token); + + // cannot use session.Valid in the DB query above. + // It does not exist in the db (IgnoreAttribute) + + if (session is null) + return null; + + if (!session.Valid) + { + Delete(session); + return null; + } + + return session; + } + + public static User? GetUserBySessionToken(String token) + { + var session = Sessions + .FirstOrDefault(s => s.Token == token); + + // cannot user session.Expired in the DB query above. + // It does not exist in the db (IgnoreAttribute) + + if (session is null) + return null; + + if (!session.Valid) + { + Delete(session); + return null; + } + + return GetUserById(session.UserId); + } + +} \ No newline at end of file diff --git a/csharp/App/Backend/Database/Update.cs b/csharp/App/Backend/Database/Update.cs new file mode 100644 index 000000000..e552df33f --- /dev/null +++ b/csharp/App/Backend/Database/Update.cs @@ -0,0 +1,64 @@ +using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.App.Backend.DataTypes.Methods; +using InnovEnergy.App.Backend.Relations; + + +namespace InnovEnergy.App.Backend.Database; + + +public static partial class Db +{ + public static Boolean Update(Folder folder) + { + if (folder.IsRelativeRoot()) // TODO: triple check + { + var original = GetFolderById(folder.Id); + if (original is null) + return false; + + folder.ParentId = original.ParentId; + } + + return Connection.InsertOrReplace(folder) > 0; + } + + public static Boolean Update(Installation installation) + { + if (installation.IsRelativeRoot()) // TODO: triple check + { + var original = GetInstallationById(installation.Id); + if (original is null) + return false; + + installation.ParentId = original.ParentId; + } + + return Connection.InsertOrReplace(installation) > 0; + } + + + public static Boolean Update(User user) + { + var originalUser = GetUserById(user.Id); + + return originalUser is not null + && user.Id == originalUser.Id // these columns must not be modified! + && user.ParentId == originalUser.ParentId + && user.Email == originalUser.Email + && user.Password == originalUser.Password + && Connection.InsertOrReplace(user) > 0; + } + + public static Boolean Update(this Session session) + { + #pragma warning disable CS0618 + var originalSession = GetSessionById(session.Id); + #pragma warning restore CS0618 + + return originalSession is not null + && session.Token == originalSession.Token // these columns must not be modified! + && session.UserId == originalSession.UserId + && Connection.InsertOrReplace(session) > 0; + } + +} \ No newline at end of file diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs deleted file mode 100644 index 5d91f3eee..000000000 --- a/csharp/App/Backend/Database/User.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System.Net.Http.Headers; -using System.Net.Mail; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; -using InnovEnergy.App.Backend.Model; -using InnovEnergy.App.Backend.Utils; -using InnovEnergy.Lib.Utils; - - -namespace InnovEnergy.App.Backend.Database; - -public static partial class Db -{ - public static IEnumerable GetChildUsers(this User parent) - { - return Users - .Where(f => f.ParentId == parent.Id); - } - - public static IEnumerable GetDescendantUsers(this User parent) - { - return parent.Traverse(GetChildUsers); - } - - public static Boolean IsDescendantOf(this User user, User ancestor) - { - return Ancestors(user) - .Any(u => u.Id == ancestor.Id); - } - - private static IEnumerable Ancestors(this User child) - { - return child.Unfold(GetParent); - } - - public static User? GetParent(this User u) - { - return IsRoot(u) - ? null - : GetUserById(u.ParentId); - } - - public static Boolean IsRoot(this User u) - { - return u.ParentId == 0; // root has ParentId 0 by definition - } - - public static Boolean HasDirectAccessToFolder(this User user, Folder folder) - { - return HasDirectAccessToFolder(user, folder.Id); - } - - public static Boolean HasDirectAccessToFolder(this User user, Int64 folderId) - { - return User2Folder.Any(r => r.FolderId == folderId && r.UserId == user.Id); - } - - public static Boolean HasAccessToFolder(this User user, Int64 folderId) - { - var folder = GetFolderById(folderId); - if (folder is null) - return false; - - return Ancestors(folder).Any(f => HasDirectAccessToFolder(user, f)); - } - - public static User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email); - - public static Int64 CreateUser(User user) - { - if (GetUserByEmail(user.Email) is not null) - return -1; // TODO: User with that email already exists - - //Salting and Hashing password - var salt = Crypto.GenerateSalt(); - var hashedPassword = Crypto.ComputeHash(Encoding.UTF8.GetBytes(user.Password), - Encoding.UTF8.GetBytes(salt + "innovEnergy")); - - user.Salt = salt; - user.Password = hashedPassword; - - return Create(user); - } - - - private static Byte[] HmacSha256Digest(String message, String secret) - { - var encoding = new UTF8Encoding(); - var keyBytes = encoding.GetBytes(secret); - var messageBytes = encoding.GetBytes(message); - var cryptographer = new HMACSHA256(keyBytes); - - return cryptographer.ComputeHash(messageBytes); - } - - private static String BuildSignature(String method, String path, String data, Int64 time, String secret) - { - var messageToSign = ""; - messageToSign += method + " /v2/" + path + "\n"; - messageToSign += data + "\n"; - - // query strings - messageToSign += "\n"; - // headers - messageToSign += "\n"; - - messageToSign += time; - - Console.WriteLine("Message to sign:\n" + messageToSign); - - var hmac = HmacSha256Digest(messageToSign, secret); - return Convert.ToBase64String(hmac); - } - - // public Object CreateAndSaveUserS3ApiKey(User user) - // { - // //EXOSCALE API URL - // const String url = "https://api-ch-dk-2.exoscale.com/v2/"; - // const String path = "access-key"; - // - // //TODO HIDE ME - // const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; - // const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; - // - // var installationList = User2Installation - // .Where(i => i.UserId == user.Id) - // .SelectMany(i => Installations.Where(f => i.InstallationId == f.Id)) - // .ToList(); - // - // - // var instList = new JsonArray(); - // - // foreach (var installation in installationList) - // { - // instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"}); - // } - // - // var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList}; - // var stringPayload = jsonPayload.ToJsonString(); - // - // var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; - // var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret); - // - // var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature; - // - // var client = new HttpClient(); - // client.DefaultRequestHeaders.Authorization = - // new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader); - // - // var content = new StringContent(stringPayload, Encoding.UTF8, "application/json"); - // - // - // var response = client.PostAsync(url+path, content).Result; - // - // if (response.StatusCode.ToString() != "OK") - // { - // return response; - // } - // - // var responseString = response.Content.ReadAsStringAsync().Result; - // return Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"')); - // // return SetUserS3ApiKey(user, newKey); - // - // } - - public static Object CreateAndSaveInstallationS3ApiKey(Installation installation) - { - //EXOSCALE API URL - const String url = "https://api-ch-dk-2.exoscale.com/v2/"; - const String path = "access-key"; - - //TODO HIDE ME - const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; - const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; - - var instList = new JsonArray(); - instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"}); - - var jsonPayload = new JsonObject { ["name"] = installation.Id, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList}; - var stringPayload = jsonPayload.ToJsonString(); - - var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; - var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret); - - var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature; - - var client = new HttpClient(); - client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader); - - var content = new StringContent(stringPayload, Encoding.UTF8, "application/json"); - - - var response = client.PostAsync(url+path, content).Result; - - if (response.StatusCode.ToString() != "OK") - { - return response; - } - - var responseString = response.Content.ReadAsStringAsync().Result; - var newKey = Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"')); - - installation.S3Key = newKey; - UpdateInstallation(installation); - return newKey; - } - - public static Boolean UpdateUser(User user) - { - var oldUser = GetUserById(user.Id); - if (oldUser == null) - return false; // TODO: "User doesn't exist" - - //Checking for unchangeable things - // TODO: depends on privileges of caller - - user.Id = oldUser.Id; - user.ParentId = oldUser.ParentId; - user.Email = oldUser.Email; - - return Update(user); - } - - public static Boolean DeleteUser(User user) - { - User2Folder.Delete(u => u.UserId == user.Id); - User2Installation.Delete(u => u.UserId == user.Id); - - //Todo check for orphaned Installations/Folders - - // GetChildUsers() - - return Delete(user); - } - - - // TODO - private static Boolean IsValidEmail(String email) - { - try - { - var emailAddress = new MailAddress(email); - } - catch - { - return false; - } - - return true; - } -} \ No newline at end of file diff --git a/csharp/App/Backend/Model/Folder.cs b/csharp/App/Backend/Model/Folder.cs deleted file mode 100644 index 041c0f479..000000000 --- a/csharp/App/Backend/Model/Folder.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace InnovEnergy.App.Backend.Model; - -public class Folder : TreeNode -{ - -} \ No newline at end of file diff --git a/csharp/App/Backend/Model/Relations/Session.cs b/csharp/App/Backend/Model/Relations/Session.cs deleted file mode 100644 index 829cc9a60..000000000 --- a/csharp/App/Backend/Model/Relations/Session.cs +++ /dev/null @@ -1,35 +0,0 @@ -using SQLite; - -namespace InnovEnergy.App.Backend.Model.Relations; - -public class Session : Relation -{ - [Indexed] public String Token { get => Left ; set => Left = value;} - [Indexed] public Int64 UserId { get => Right; set => Right = value;} - [Indexed] public DateTime ExpiresAt { get; set; } - - [Ignore] public Boolean Valid => ExpiresAt > DateTime.Now; - [Ignore] public Boolean Expired => !Valid; - - [Obsolete("To be used only by serializer")] - public Session() - {} - - public Session(User user) : this(user, TimeSpan.FromDays(7)) - { - } - - public Session(User user, TimeSpan validFor) - { - Token = CreateToken(); - UserId = user.Id; - ExpiresAt = DateTime.Now + validFor; - } - - private static String CreateToken() - { - var token = new Byte[16]; // 128 bit - Random.Shared.NextBytes(token); - return Convert.ToBase64String(token); - } -} \ No newline at end of file diff --git a/csharp/App/Backend/Model/Relations/User2Folder.cs b/csharp/App/Backend/Model/Relations/User2Folder.cs deleted file mode 100644 index f93cab568..000000000 --- a/csharp/App/Backend/Model/Relations/User2Folder.cs +++ /dev/null @@ -1,9 +0,0 @@ -using SQLite; - -namespace InnovEnergy.App.Backend.Model.Relations; - -internal class User2Folder : Relation -{ - [Indexed] public Int64 UserId { get => Left ; set => Left = value;} - [Indexed] public Int64 FolderId { get => Right; set => Right = value;} -} \ No newline at end of file diff --git a/csharp/App/Backend/Model/Relations/User2Installation.cs b/csharp/App/Backend/Model/Relations/User2Installation.cs deleted file mode 100644 index fcd2760dd..000000000 --- a/csharp/App/Backend/Model/Relations/User2Installation.cs +++ /dev/null @@ -1,9 +0,0 @@ -using SQLite; - -namespace InnovEnergy.App.Backend.Model.Relations; - -internal class User2Installation : Relation -{ - [Indexed] public Int64 UserId { get => Left ; set => Left = value;} - [Indexed] public Int64 InstallationId { get => Right; set => Right = value;} -} \ No newline at end of file diff --git a/csharp/App/Backend/Model/TreeNode.Equality.cs b/csharp/App/Backend/Model/TreeNode.Equality.cs deleted file mode 100644 index f37cd51d7..000000000 --- a/csharp/App/Backend/Model/TreeNode.Equality.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace InnovEnergy.App.Backend.Model; - -public abstract partial class TreeNode -{ - // Note: Only consider Id, but not ParentId for TreeNode equality checks - protected Boolean Equals(TreeNode other) - { - return Id == other.Id; - } - - public override Boolean Equals(Object? obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((TreeNode)obj); - } - - [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] - public override Int32 GetHashCode() => Id.GetHashCode(); -} \ No newline at end of file diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index 7b6df34b9..d49fa856c 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -8,11 +8,9 @@ public static class Program { public static void Main(String[] args) { - using (var db = Db.Connect()) - db.CreateFakeRelations(); - - Observable.Interval(TimeSpan.FromDays(1)).Subscribe((_) => deleteInstallationS3KeysDaily()); + Db.CreateFakeRelations(); + var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly @@ -47,22 +45,19 @@ public static class Program app.Run(); } - private static void deleteInstallationS3KeysDaily() - { - using var db = Db.Connect(); - db.DeleteS3KeysDaily(); - - } + private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next) { var headers = ctx.Request.Headers; - var hasToken = headers.TryGetValue("auth", out var token); + var hasToken = headers.TryGetValue("auth", out var token) ; if (hasToken) { - using var db = Db.Connect(); - ctx.Items["User"] = db.GetUserByToken(token.ToString()); + var session = Db.GetSession(token); + + if (session is not null) + ctx.Items["User"] = session; } await next(ctx); diff --git a/csharp/App/Backend/Model/Relations/Relation.cs b/csharp/App/Backend/Relations/Relation.cs similarity index 95% rename from csharp/App/Backend/Model/Relations/Relation.cs rename to csharp/App/Backend/Relations/Relation.cs index a791a4609..7e37b87ab 100644 --- a/csharp/App/Backend/Model/Relations/Relation.cs +++ b/csharp/App/Backend/Relations/Relation.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using SQLite; -namespace InnovEnergy.App.Backend.Model.Relations; +namespace InnovEnergy.App.Backend.Relations; public abstract class Relation { diff --git a/csharp/App/Backend/Relations/Session.cs b/csharp/App/Backend/Relations/Session.cs new file mode 100644 index 000000000..016ffa0ce --- /dev/null +++ b/csharp/App/Backend/Relations/Session.cs @@ -0,0 +1,44 @@ +using InnovEnergy.App.Backend.Database; +using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.Lib.Utils; +using SQLite; + +namespace InnovEnergy.App.Backend.Relations; + +public class Session : Relation +{ + public static TimeSpan MaxAge { get; } = TimeSpan.FromDays(7); + + [Unique ] public String Token { get => Left ; init => Left = value;} + [Indexed] public Int64 UserId { get => Right; init => Right = value;} + [Indexed] public DateTime LastSeen { get; set; } + + [Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge + && !User.IsNull(); + + [Ignore] public User User => _User ??= Db.GetUserById(UserId)!; + + + private User? _User; + + [Obsolete("To be used only by deserializer")] + public Session() + {} + + public Session(User user) + { + _User = user; + Token = CreateToken(); + UserId = user.Id; + LastSeen = DateTime.Now; + } + + private static String CreateToken() + { + var token = new Byte[24]; + Random.Shared.NextBytes(token); + return Convert.ToBase64String(token); + } + + +} \ No newline at end of file diff --git a/csharp/App/Backend/Relations/User2Folder.cs b/csharp/App/Backend/Relations/User2Folder.cs new file mode 100644 index 000000000..0cd1fa191 --- /dev/null +++ b/csharp/App/Backend/Relations/User2Folder.cs @@ -0,0 +1,9 @@ +using SQLite; + +namespace InnovEnergy.App.Backend.Relations; + +public class User2Folder : Relation +{ + [Indexed] public Int64 UserId { get => Left ; init => Left = value;} + [Indexed] public Int64 FolderId { get => Right; init => Right = value;} +} \ No newline at end of file diff --git a/csharp/App/Backend/Relations/User2Installation.cs b/csharp/App/Backend/Relations/User2Installation.cs new file mode 100644 index 000000000..f898253db --- /dev/null +++ b/csharp/App/Backend/Relations/User2Installation.cs @@ -0,0 +1,9 @@ +using SQLite; + +namespace InnovEnergy.App.Backend.Relations; + +public class User2Installation : Relation +{ + [Indexed] public Int64 UserId { get => Left ; init => Left = value;} + [Indexed] public Int64 InstallationId { get => Right; init => Right = value;} +} \ No newline at end of file diff --git a/csharp/App/Backend/Utils/Crypto.cs b/csharp/App/Backend/Utils/Crypto.cs deleted file mode 100644 index c176fd284..000000000 --- a/csharp/App/Backend/Utils/Crypto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Security.Cryptography; - -namespace InnovEnergy.App.Backend.Utils; - -public static class Crypto -{ - public static String ComputeHash(Byte[] bytesToHash, Byte[] salt) - { - using var mySHA256 = SHA256.Create(); - var hashValue = mySHA256.ComputeHash(bytesToHash); - // var hashValue = new Rfc2898DeriveBytes(hashValue, salt, 10000); - return Convert.ToBase64String(hashValue); - } - - public static String GenerateSalt() - { - var bytes = new Byte[128 / 8]; - var rng = RandomNumberGenerator.Create(); - rng.GetBytes(bytes); - return Convert.ToBase64String(bytes); - } -} \ No newline at end of file diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/db.sqlite index 1d3664c4c98cee0228162b17f2968614b4de4861..2522ae1e7040bcbe0b029c43f8478d4149c143c0 100644 GIT binary patch delta 116751 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 z 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 Thw=+ZA+v2inmLe3(}Vv5KrJG8 delta 114950 zcmaI92Y3`m5-8l=6Lw~HXIHxlC`W`Ogb+dqAwAI+*^U_XakcDBN^-4qCQ|Ol4cAuoccaKi zUOOt#$@55ny9>^YS4}QkNnwt_JV)oicgpB9ZtVxhY(9YHvrX~rRnLBP4N%;qq&sxi zVezRao{w-Y5J${3*CbcE^KJ8S_pAH^XDj1Ak!H*=vc(L&ozC>{waeNH_j<=SjuVb5 z{jmCsTT`1WpBSI>z2-RiZF#pmKshW&NdK0$O5--~PYvu(=*HzheNvU+!NB#fZki${ zFh{n=0xcrD@}xkcn0AUWbHQ9C@N7)6OPjnzNSRE_(O7oiQ*TGE1*Uu3b0aV|p@U0t zsLNSlp3Wk9WT4R3#F3KMGC$wZC(z87$E84Qcqbke7#-djnkPpW8;L!eJM+5hEQ1HK zyd7n6n8|^05uJEkU|&Q}CAnC2<^-}6a{_(hoQB*%b{1x7EJqLQi*KU}seI<-jKG`W z#jd7{JzX-Lozr!e!vjeXZDpko(D@~@zo|L;Fjs-bvUIRhU_(rME&`jPI`jB|C%%&r zrxmh1pqRn~>5Vc2agADXU!Z@ZPFxLaY}C^*wHvHgvBq-sz_zfCJSI>T*$#fkM7IwQ z)0Z-(xNW?%5VVwr#93fK`^dl_(XoN+@$C(dGmGWs>MV^14n{W#{5z@_PYmpeZo^%H zBfbuSb+PS0;S+pad3fM_WQWLj=PBmQK{YVeG16I#f9iq3v26lJy=|SU;`)I#%hy&7D+UQ8s%*r^*S7rdI@_61p3nUQL{xQ9(RV8r8{2^-N-I!FpMGASod)aA#OA z?hRav?ZMrF()gZ=3Uam&Y>6ofG)Wp4VR{~7s%M4peCGMD=bC4Q=Wb8nA#V$5mM5?x z*Qx`C@4&U(B!2bGXSvZ~v)$)ezWa2H z`;7Y;Pr&`Sr?2}f&qU9|9?P@WbEjvO`?{yC`#sMB&wHLKPq`<|)5-m_=VSN3J<~k{ zJx4raJgq#Lo+!_7_jB%tJ{P#u!316@SeX{Pj{Je@qeui~V?i9<=lzh#wv6(j>ZF||=XBBXrf4ggUtv0&uk zZ(VS)cffG#uXoRI_i-EM z$L0ldgE`qOHp5)+x-PmlyQZ=KI{$FK;5_8CoFkmAoNnVI4CjIG zstX{R?AzRm#|B>A+ysW?ADgpam^ayy!8-=}Z)pL+#J?p&>z*`NVObCfUYZvJBlhZ+ zCJ^4f*;1gzv>BkVqV7B}u%acP#s$(LF4YHGLQwc-Q##iJZwHcjhrnNfc%C1KyDNz| z3KZW}$Xf&|@5)T>;L2r5ISG0}i~QuS)21(+oIH4b#pKEJ?s>ZOw$5q*YXX1gY)j{D z1B16^LiAs@tvPQVIJzxCOG+qGSQ|I#In+CQA zC-c<6U%|%k`^=^cXj!s79aJ@MdrQrc*<4}8pp5Spr2UN{7j(_pUVPVD_#|t_1pRPS>Iv$0;m7T@UQpm83;v$0{tlSPv|%YQm!f zU+iibD`0%Ym_BLh}=p5NQVk*oj)VtB4UE->ismX0(D*v!@g>+Vhr9JzZ$n<*@Y9hN+^ z-Osz*ng`7&*IMUq&Z)+AqpN<2zoIwQc5C5|TJ=ZXRe6;imX9hurCN28_FHV8<(cO*5=Lu?ihe)+(P__kmY=S`L^XOtOw45-0lKDO6#g_|+A_{8Uy53Tn0x17;L zEsLl@<9&mcMX+7dGAx(ZjD+SzSb@^+9=6=aPFSuml9qVN0V++0SNj%Rwp5SV=$5od z{KD|X-iemeO{i7)tA(+?0|Ag$&iDxLs}%> z33lEmriQ6jyO2V&K$LN(7yIt9+*X?qx9DpN&#dsRvP`RW$W*z2?s(DfJ7(!t%TVhS zKx+?H`7A5a>SE@ARP9N5fPU}X%5Zkja#@|p0u4^T|>hOainmJFs2-$chfO$Ig>IbKS4G+nD#6E|=f=zO$e4 zsL@Q{uIutVZMoxP={>2VqnmnOoE4dTGuPB6NM`7-Ia%ngWVbw||lMy~0 z@F`D$S8cQstOPRy!UJ`S8Z3NzLpVDCV#bq0kma%c>reZAHQ-h;M26f4$*;S*ES%jB z#%vNYCi>5%$1GnYpwp>2F`p0U(#I{I8;Cb1;-p$cfB3jRJpW@JF9o z5--b?U}mp>Pwbp#S;C7k@eRzOTj;#Ry3)S z(Am&D=Gt=KR0}3NqC&AAso#IC##avLC~RFyXms25|Elrz0|iHhMhXtYm;Y7eb3y9} zVx8OqiHv==#y8rEwZbV>bVM_u8oz&bT{znfRD2;fNAqobuF6+$MOj`m7pzM09tDbj zezmVLcxafJamy9aH_ko30>V?Q5v)2pjeN%vl+bq0=PJ`B;ys+AL28Os9 zVjNprFIE@FnGQdT;XP!}boZO?uI33d-n9X8=yFIEdgvGQ=GwiQ7ZTeZ>gVb(<(kq$ zJ|u@rtHlj5=hS`|53|CCk=IfHNB3F(y5(b+EZgSuJSEDYK4s;3zEH&#Dv#sd=>jG-|&!U zqZ|DETAg<&%urh}3fOW37UcF0u{=Y}bkIDxI|`U|-SSSbw811FnF+C{t}lfdUmHY% zP+Rn)c5f{87F)(Z83s zPtp+%W$_z2)pnjR}j5_x^V|qtn`%aHJpX?#kPB-hNg@Z=ygkAEV^pcUD4{=IRw14Va;P{!X=_Z zJLcH2^AQfo>UDQ7^PHLCy36H&+5Q8gRDWD=rR~*BhXt#%vC6YbTltKfCT$ge@$T#q zF@;~>yx;O#qNW$=lA=-qi1F|D=KI!Lo|>Mdcv3M2s}b*8fYxjJgwSY@9UoSO&pB&F z)bu7xlYr=OP5!simurRB^guP2l8z94<$v|Qe9NfmPR>lkQJA40E%QO%SksLpC*2|O zFW#?)sYj~m8fuLS`S7C!zBZOw(}e~$MP;Nt<72-s6&UYqrhuyLAP(q1KDJ=m4Xf!C z(ly#}<0myfpXIFSNNTlx2GET^UGD2-X*C@}69>A|vJaN}c0!l!Zw(-O64XBQ`|2!T zO*=x9@EDLD{IoWl-3MY8hnyEV?ETp?-+a(vTat&w#OAMm2Gh(A;JnC80?ujLz}afz z=PSb5O3P8xhFG@~Z6Nd0=SzH1R#Z(P<%cvhFjTGhVpTXh3*=jedPPZNzO3>&fqDUn zM7`!i^ZUL8>i2>M?0kew1e;iaHeXeHEq6^m5orj?*S=ceO9KnFBq9yteAL&~zW$cC zCXWf6$i zUmgE)1RP@=Y3dj1Wi_ZySG%im%J0gu20oA-Co8e7@0*2@-w<=-V3Y<(*$MPYeeMWELYIVxKVfv8C! z_PbOq&&q*iYkM9@{4Sd>3zU491sednzRTy8ffv7P1?x%q`!qeViIt(S^a37e@qIF1 z6d3e>InNDKZ5UBfvZ2J z!REx5KNj-^fy|$p@T@@TPkFGVVEt6YO9PjFYR`KIe)}nzcMZh;oCT}+Za?FyeA>@B zydtpe=XAaz@G$(H6Zrh+LOwl^`b!aPos9ma09O55ere5T1+M*)qs!?QER>5Ac;N3} zlKALA@~;E<#K62?o59Y)-e23my8HECGkM1?zZLPZfwsRT@h*Xpzoo$D!?NEB(%i|_ zu!K(Ig>CYZyOmWenm#9aPNow4_zS;xHgEK^u6?3SGPs~M1c^RGmroav8rFW5jy>C8wpga|N}d4#nMDozE8w z!QPQBI<|s;-u9n#hHXzXEze{Mv^0z%rUds0Zh4zojTV?ix6RhUmu0@jJJxbk(g37D z3-faDM};qesY{tp>A|h!C)ix$i^8*W?zAFj(6mUw0OqOSRD-XI>%Uoknd!I#kZ0*r zC;;4=A%kAQm-^T#i!G#>MzaXSu3!(D&-1Y>R@it7(soD(e6@#L;cU|`tuenBHwpg1 zg142W-p(1c2hJ9I_iK&ql);{^1=~87*T9xY1w|bjsUs3nN!Q#E66TPg$HA+8gZ5a? zMHJ9(1^QqMi7)pRodgB1!abN7>nDv;HcIXEo5 zK=dmTUmVWPt+KSKq;p~sQi|Y77hmf2SgtuCG`9bj$*UvRPOiwh$8t>~C!skI%~%Xo zzG3Gr*F+SclzN5~IcRZS?VV-0=aJ6rDF7(>Rh0Lj6*d8NX3yS`pa$=7bI4F@t*|oE z0|^YdLGTN}w`GS+DGB`iSqFIe2kt@gE24^jYhE@R>s|R+<9lP0{vYiERPS&&U7M9} zVPAWk+)FwuHQu~G0LgHsndZI88cY?z>}c%kgyl&iyO9GS4?o6o*qV9FawL)Tgh$W5 zKb|k~b-ZFFCsNuJp~JSnfQU?d|m^5X%ThLC;!Yu$+dOm-Iqz z@8vb#6id$y=Y!v_zP(h9Nv=x;S3TBX5W8>9;_SwsDYK^WC5zlLV?6_sd-=+efU6#oG zkTGj<#JR0^NIrfXu*ApKSz)ncCK{BOWL6KXDe7WM)4_E&2+_?m%l(qOqj}Oy;b&nv z_mgv`F<5_CZ>$A0#W9nossB+sC{b*KyiU3y-q^fJ<`b`>@UMi4D~v_M6mF~*hvY=sOnv%p+b zfy5HhdA+aNc!zB!mNyuj1u^i(4oy01s5B5odpETu*q7neV0XBO2 z-c|>lU|l$?135~l6Wd1c(A;dk%BR2{jlCQquF!URGY(nxX)ty#WY^xDMTP8Y2F9KU za`y~{6y&OD4qq1D`7FrY!II`MiA2Lh*NrCDhT-~2 zb6(SVfT{cervmDtwI=c)`CT)^Qz0UinZ|6rzhg76bFPz~a%fUpagSQ1zwPd7{N~yr zca#oG(PF(2d@=iYbLWWW^0RCUzEdF5Sz>V6tg_jQ%H~h5?Ab!>AS8l2`eRT1#ma zB7|wmQ~7*h6uUgRQr>3i_A-k)L0^5oJ+F%D{q$q2nXEm*{HPMUBX|xvf|or;7oui| zWWeSY@~OdxTk!}V+iIyD$zdt}A`3^3X5rCD$* zcw*ptvjt!8eb5!l)?xN#_XE!~^aJTmTOvoc!f11-9fsk}mH}(#+h??UTJvQQse|uv zS$ffJOrw}>U{broa(AaGuwmRCY6P3gp(ytSIpD@CHW&cWA>`HDVX9 z-eaE$+8lB?cm^Ds zLHYgS3c^h$fp!?y7WEG76>UmkCyZfR?EQdq~XJlL*3Z>3ix zTZIZs%H_e6B|KJNnq;+rWkeDWzSy6~C{sijOA9_w$`g5Z@MtM-#;by__v2Zj@}A(I zeR)eYU9@8g3!du3o0tV+A#<_fEYZ46>-^xRemqARAyq)fi~93~;G}*$gD(m8?#r|C zwG69^!V1zw%e?$HZIZjpDw{rMa`J$(ikXYbmJFU>HhXr?@adK1OJ}lmxmF&ml#6+A zWgni!oxvp~yqRyQ^gGj83#+}-*y;%jKG04T^Z`rMps$4I@vPvRr99nCR?>l?oNUpu zWpdBpHzmBaUeem?46orkir@!*I85Cqf{jXfzP>igf|uR-IXpO|gr{Z9O0!xaSq{8S zo<3{Jg33zZuClTpsC9BhPS^5^+0!RiR0J#g@=SBFvWm%tRyR;vKM_3Gm$wjXeQ;-g zo~EQ4W8{6o{e5||p5M~ShZfy=a5<<^aT>ejWmwb7J6&DDa#3ynKUtn6j>AfzWh8LS z^9$O5u6hRF?86I{dB$$(gpF0~wGFUy7%UE%S2%ahBjSY4ul=E3mXC6!{ za@1r6 zXkupB?74a50($R%=uVw+zp-0{1(%ob%qZZUf7lpwd8i<%2=r?Z+W5o}fOIPR4HbeDGKaPjYWCMzB__nbjIL382~bejHAM z?JKZ)fLYrI7nJe>eIK-L10@msrGz&(+1_B!ePwepPa99%90krfbF>9>d+j#9WQ}dA zN9I;M&~>3Rilu|ub@C=>$t?|@nEa|yuBXyhfBwWxqf7aSZ=1(7921OM?|ZC_-HeB z9$Jh>gBG>PZ`q3MliY7|*`)GC$=xd8EJ&}issC`l2zGDnh7L?N?_%5X{$WR51KikK zUYHQv)0ekt?X6_H=2UjO_YypDiKYHO14%IRi3t^D=9esoJV+mNPsRLcjDfw;mGvUe zHQx3%SG+sVY6rU&Fxr%Uys5!1dakkO$u+GK-q^!xpnNxtW0nJrVoP|I!A}0eworzQ z=+y>ms;nN$dxB9VJV!5pKW8>n8LSq{;OaL?bz^6n9^AOY-kRRz@JxmiV+H16vyrQs zKj{3_Io7z&6OEqw6}Ab^QUo;FQK`PG_7MLQ{giV`ntYe^hg80K{~#~ypQcjQN|79s z#HbONBwmCK*Ca}cXhOt!^70+L&Ue|0tckmA7aH)tjN1%hw{4c z9w%ULHH+pcn%FUcdwnQm7N=knHP=jPc#+K6w!x4;cww8nc_=%@1@!&_kf2P1O;mW7 z3e#ajHg@M=n16W0U*?bIwnnEdN^Si%z~{igYMEpM;CAc{$Us-nk83ZOTIBp z!9)t5m^KLV?p)GiO+ zCnQpSK|4X1kqjQmE5l)jNU2Gx#m$8tV?k#0 zmneE=NO*kU?XjS4-D*?=2W238qC6BGZ{aw;rs$X{2^P(JIpxcqsc^R9Av4{z&1Je@ zcP=po=ua76YR9xB$DQh5>U8A|F^l)&ugKX_P*P9r593~Vu{wu*kX#L!u%-Za@VYqM zDksg8li-%ckjb#L8NCNy(axsj94*4o%|9yRwT&2zY9_;ip@MiMa>%1|B3Pc)z{+4A zCB>u#+@0Ay5x3lqz{+$kRgfids~~q0sBM$wvPO}12#?)Z<=~80^mc6BZ43in#PF2k z@W8Cl$r{X}T~pzKAoM(w3`T}TuO3jwYr>UhU^O&@ytE-DtC|4AqXJ}@9`ZSSV9!)o zD;s-3SJQ62zoe5&!RFH-vG+a$T~7_kfaCSjG+g063K3`xbwTRDr9+?T;HE=h8914I z!d4UD|1%v1cq3R>O`_b=b}k?^ZU(HY*1{5S%58K2k94BgW7CocgM1Td^fxH*#thW# zHaKTcM*B*X>*HWcm<64;g4N!5B1Q^9|2i}a24U$j5PTeo(V&p0XM^D0wV2TJTQhR+4(fEHGB?Q8}e*x0?F6F)5e*Q=(A^%Va+<4xTO>w2k0kr_^Q0# zrudpg@JCGdEAEa)CwYhSeW}VbQ%T{EsJ)bPdW;z9_}($uyxUo#t&*Mk>4#d(<$}j# z!*IaP#h+|H8J}1$22KJ?c%@H;<1SU^tws!O`9U>b<12tAvOl!Fff5~A4oaJ}PdNz- z@od};w-8Wp~_e2xZDO(DIJLea|Ae%Or~Y01_?@|OlH z|53pg`bL3vmXcu_JaWxqJ~3Ea!DFKbsMncl#=)pqP6{N=qM8bq!l;M@+bz3|J&+UF zTNd&3@hkcXV7y^5h*s^q5EwUM&fx!;GlFRg0G|ipiiI?QOpjm0S41YU$4)?%Oap-U zLmiY>L+A0Z&r)s1-C77@toXW$*EC{}8n430H)_<|`iDyFKb-A@rxsAWXxNR2Tn@W4 z^{$1mIge)tNkk0EFD>Mgy%2XLGYOVgWB`l@e=X+l-f}0PhkXkbt&%hcqT#G z-SFZbxdLu2-#U2xcRL}Ff(TU|v*K&SZN35(;42$^*)WjiBSZG?2okDU#;pi;31gA{ z!h#|)dQp8Pua4;f!`lJg1rII>afVxfo91(?Gh6kroo#H8A=`Mx-Q3)1I$R5#pEyg5 z$BZm}tFG{4S_MyVyyqCGUQyGPyA&?Zm)@63#TD4}D}ZBfd|;TfW)e+M4P)%J8W?-0 zU~;$5JlIYIonE#8e->tmX^D*)FJ{WtwGa-<>tWt6BZ~s1QifB*syfj4C~&p$6q<<=I_>Ti;A-u` zpKgmQsJ^Ba>_;uT7kU^=E=>GR8wTOAdYEGi4?)j%^q^6Qp4ojR z^y~p;j3Nur11Q_|m7oki^gNRAw(obK77BDYh@#fO z95NcxkfAiKH7NVw8c0KS0;5ByeS>2Zt%b~Milx@r2SggSSzcZX+uZZ5=$b(^7uoM> zz&NAU@ipPB9t0SOx-KQ-pss&j2Lj9k4Go}BL4ybJ_3J@HDhOb2JleSf;2Yfu0!#o# z`%{=|*a10mConqPO0DTt3-HA-#`&Qewg z^cQaAYll%t30z5!UT4oT}h8Sk2;pIQh0MApKonjs>Cim;=NFab7(k zw`3ba;SueZmUCvxdZph<(j$Fp0q@xWe3bl-JxIm1!Kb;H-0DdI}Xe? zC-fo;1^99&IMq{d9);dK+DDs^pB1~{wd+7o=qyqk35@Z<+zkpH0Ez9)elO9<#-++KK~8yIFPEib6! z0(jt?z4(^neo%y+KhbzcO|Q6{`wORL z2#06yv(#D-b&-Z=5wRloe!e(r7kgq-q7_#A&*O{1V+X)!9bkU-ht@CH$OUWpER;eNh6ri1Yf z%(p3)W!Ad?Ic_!{0tWrtEpM%po}*}q(?hSrkUNyD2U{34VxcE^{RI{89Og?SC(jCp z^G&rnNlVU$&-{7>Vp8NW^CVkA&oqRL^+$O*ERt&-x1m8yKv(-70j4-HWj6=>3-gJu9J}ajo>gG1T)$cq^Gq=f%lE;80ZXm-}E=SqJ!}|3mIY zaL%|k?6&400W`0Nun<}6GE;7CRHJu1x1X;pV<$}MB)p9&kCwZ+!Zl6#NPpb1KpC#* z!b|TIv$3{a{n#^$|81T&zIAsq?htqKa=E2+zvL1t__wT-Plq^n`+xjf9_zxq$zC0Z z;D#u9u@_#~Y^N!cf+QwIy)Wadv4_F9P07pc^&_ww`w;kv>p^h;#$-JTKNwD|i+oAs z#L6&ums*=esv=@om3D^vBWILPim|lXBuYB$-~m-<&+w(OsoUcA$S*R*gzoH>KRFq$ z&30XaVaV=+?&vhI{r&w>Zh5$d& zl51mbdjKfm15xb0g$w#S@dyf?QK3`aKy35{%yONzskfow5kLc1aHz93j{0a|x66Yt zvBPoxT6-6Pw1#7E+7)oSVlaLRC9`&Wz<+)Lj6WXQCy+A99dVd9eHhMRjHxWEfJb6) zvkX+BWqdQB>r4`ZxW^6Io>y3GMlKEoHHEH8DFZ)VwwRt+9=ZIIw$GX&T!f5HnknM1qx${oKG!E|8?D%U+1Tm$)cJ||mE#-dzun8V zm$b)?*NtoHN1D&*foV{I#rAPGX&S4Jb{KNI0 z^PC=~x*c)qmrAN>XvvOo`tSNVt(EqJaasAn`K0lLDLGx{X!%jSUX53#!D;e6N>Kh# z>8`JLbaU-d)8rSV_uPfXU9MiPujN-=(XLA49eu9R()?J7)3c49N(XhC@ql(o_xK&B z)H3Zs$7;uU=_mbJt&8&~xUzM>d_aBO^{gXRg=^BTKjgoSC`XoStJ2r8TYuTHQP;FO z?Syj|9OxOU{q9V0-Rqcb?$^GQlU#xw^G)*-f)hQ zo|b=g9)`=Yr?eQy6j^fk`xtks>zwz%Wv^PNedN5X_fra$ zH{63&hw_{@Lp@}6c8@gfS4YY@%2)c2juys#K2@4sos0ce>K$ZH`Jup}t5uuPxUmI=@zT81tNqohy}#dV+af$#RS^JlaBKv#YJ^ zoH5Z|=8kZ-a+Nxc7+iYZ@u&KdYm&OqU8nwTPBeaSNzP~GjgJ55*Bwur&6MTxYPm-C zSIP6`*|H&zb8Lmz^d)i^{bT0?t_RG2c{aQIy1P3Dx?8xj+^NP#Zq@S}T(17Y>}AgL zyykh)b5+YRpY)v3AMhOa+y{5Tw`(%~&$C@I@qh9=^6T4HH z@^g_ds5p))_5OfqX5KQ4i^pEKe zwh`|~Pick)ReXbBvS@?ADPBM@iI-6Sli1%>WDns3lbT{-B0qp&f|!J0q?m{xOH4q} zT8u;B5~C3$iaQXv#RvqMVlaZ9VjzNSQHr3M=#L;?^h3~8^hD5I^g!SrCb}c+B03=` z6de&ni6R6#k3!Ic{eU2s?M09#-bXN2tV56`S|Au8NeFTUiPBWuiKQZ;AyD~$5O_H8 zTO#RL$`WA+lKo->LXCfbU?Sg*Kw)2?D!Rnt?>M5ICC+0_so01hmR~?%asxp;r_1+a z7hrSmQv7w33k>A53Dp@}4Um!$^n;hO&|ugk{2e;nkDv*uAOW@=p+O-ZgJ2-{AxLAd zAm}MBBFN$+5oCi<093IFK_^Z+^YUiq4dy75QW2TRUqn#C*I@@E>9N6L3*z#`W&|xb zQud2Dz7>mQ{6hpXKZRfbdlEr+J{iGqP6iy!omld+PZ11aJCSt4C@eSy@h$|>Qu%X; z?8Jiz%GgD-v zrzUOqLs%NhXCfHIe#WO&`c6SMS*Dw~7i*@9T?odCcM&w^-y`V8r$KW+Z@(N%BPEh; z7#s(JlER)vkPkOPfnwW8EKC+}A}A6k5k!gm5RB*lMUclYA;^dOO#s3;eXL-j2Mdxo zj9`MeAAu zaRotJ@i2nk;y8lN{7VE*PJypCBe_f2MSQw=5LUGm#6TX8#!@6B1tp4SuoNyHLy#yM zBPih{_5k*~>W5-`J|3~@5<}2Vkmq*g^k7@Q43T*}89^GOF2=IEvDA^1hH`lfmWH!; zkeB`mSTM!62!@GI5DXB+ZAbAsmXbLQ8#g-$CBH}!Wa}L9I9870We5yL{AP;JvD8%j z4}mIPLNJnFM$m!Zg}}?+MKDXej3AxYAQ&dDBWT1vLY7*kW1&cVfnb983_-kj3&9jW zCpCH41K4utG(QP#f-v?H=`)<-NjJ%drDhZlyb_Iw zq5LW~9L$NqJQ#XVa`w$a>}dr4X%dA6nIFR91YU~I zB~sk%KLh{vOvQ=}PUbNfX|EsKhE?tR;qP!#MIm2_?Z!#u3~rH$r6}G8Tcot0FO_sm zLR^Xzi=Yc1fmMEE3<9Y&1@uJW#kw(k4+1z5iFIR(|L6Pz|G1+Fe|xvVx?;W^L3d8W zr)vi+OyOq`q;m4k0elvgnlK8e6FAu>oUcV>EMJHqnOBnjeY_JEN3(Ab6tI6|KdD_Q zT2J8D5ZQr~{R-F(tjp<%xDg@_!ASl!g8pz83P3cE#}?yye@WkHiSC8SW`gWJp6|yx zcPXd3etl^EFXNpN+hg3*gw3H?(L+!G9nOEVyC4VCSsk`4TI9NnFnd1!wy(tBl$qFW z3V#a0I9`sRFI$Dc&B!u^tQJe9%tFwGtw50K=UotbIXQAOk%grs{yTy~PU9_ucgIp= zwg!QR{~LjeA3@;eFv1|7Qkb8}$R9hiXRs=o5if(-gUH`ZQb3fTZrbrbutg?Mq4+a_ zQ_N5}&G$10V_h@z z`AVoucCzE1LgsT;!mrC&3csqeGu9itEdrfi1xk@Rd(bnOxi#?^0EZ^(u}MSb34Q~&jRLf2>(*7bintP?q(5=3GnMy#SYJd%%KWWnJR?mI7?G)U+gJ9!1hxF zdD?729xz*wrDn4;Xzbb3vBfO*g6D1Ko+)1Q{Es6H4n4h_4NHMUq zaLllcIYZD?JcE-JXYied@foj?COF~f&{OU!&SUOV?j!Dd-Mige-5a!V?$vM&UN&30 zXSpZ4$GV5RySm%C3*4FRRCk;^!fm>l`J4H@`GqUb{K%MSzH44LUpD_G&4BHjbLIo) z{eE-5GQhl3d&Tsdi_LlF40ED68m=5>Nt@07W+%9N(8NrJO(UP_R7bdeaeeFh%=Llm zE!Qiq=bb}c54j#NOxOLceXi}UO|I3h6`o)9mt6B)(_Lk*J6r?J9j>OXG*_G}+~w3q zI&V0CaDL(Z(D|1073Xu#C!G&FA9VV6JMVI?cP@7>aL#s4h6VCG`3r~B+0WU*+1lA$ z`puc-Y~=Jf9mcQ5chVBid#*i8Y28eT@Pm$4EC4q#g28hNAze|D=Ddf1aEv!Rye!sOZ5tUhCV_5LAy^c(Yxx!dcHi>vt5tZBXpN8dpZ#eufdmeQ>=C}Y~k2>tw@7Us4>!@)o^?a)uYA^Y+p6!_680i?~=p#*V+z>tM*r5(k_rgulP3mfOg}PXst4>opt3|L0 zk*db3K1W+;6ID`fC_gA)C?6_sd9E7W{K_lJ^U71oW#z1PPC2d|RJO@IjT${o*{G~i z{K_K7HDxYrv5a?oqwi2gK&H`IDN^#3EMu*DP)Sw7@grdJ4f%iYoxfM*=N(=2LGn3R zY~LsEmAA3z?6S(mx9 z4(_skCw=BwExjSV>^bQwk{**TXi4rR#whI%=@cB#-|y;d|4&*2|AXs+tHf{z!gXR8 z!J}d*!K+du8;XD5gw;1;u?@KgpkJ`7><#z>Mj%qTUrfcZxkyZ>;{T2Kof5^$D)Dcd zctEV6_E*L2#Bb3S>=~X%_0O;m2nNL%f=~O|AN0pld=A0K#DfH%6iGJZZxdW2o+S7L zzkU_R#RtVXf{$?j(VKs)vLUM{xJ!IR z@R&G4@CvW9A%nCPy1BBDikHP88*-c5%iPxaWvTiEmOV)2x%*@xI_YJZNOw;?-DjRZJ0vnQ(}M(`J|gL>n2RJAzMeyPfelXA#si1 zNq+Vwtf1s>S6?RB^9(fZz!s zY{+e&Ji*uBEHAJjTTAp#jJ83h4ZKu&UVKFGfY?d!IA2ZG$CGU!6ZV*&N7{cd+ar&$ zO;kS0uMm7x)DS!((rw7Ty9sSqS|xs?@)hynO-PAom6$w{%7?j^`aA5R;z?l-r-#K- zD&OxHUF<(Zp$+*|8?qe)*Ne3TH;O8PheWCk`AHkHYefH085NIz8bL2L%Lou*E?J>m0Dr!Fe`lOxSb6Ht+#qo9+OQAp8OL^-X9G;{$9D zm7ft61n=bq1h0r21n&{?H({&|x&LMbf7wQ`htU|4 zWe?VerEtR6iem(K3cI|M54FqO?q(;KsJw%>CAgh`M%`}rQSqGko?uYe)j?**(jfbi z@Y_Uh8?p}}|Ap?iBxtnNw)kCqFEzT0FSa2&L2!lSCb(PJ z^#T4Yl>_`=He}a{Oh8casIWDB$}e6a#AbGg>NndwKO=Fp$O7@*w^Y4}+y1bLeM`+Y z*>-$TyhGLJ#cKpNiWnR6$8JJ|W1% zx*lN~TZfAoP{O))Hi)BEkBWl?*NUz-JM$fHc=WoJ$==oJksQ84KPjDsw;wCJzA$yVF9&tC4 zog=2$F}7HTKhpg(dkU;JVRpr^&B)XINV9H2k8i)jRFMA}VgYpsUa-dw|W6P=ny zRDGxTn4rZ!zX?qnvbS%-(>LLMddyO&xPtGpA-mxElc|1T%eqc{O{(WV+u*bfU^Ih0 z_y!v+vB4-@WchoTJ(;80ULDRAqp+%)e?@@3W`kYWuzG+jalINbiv%P9T+9`~5f{rj zMLPd-_PjZiIhF~?pSW1Y$C)dcqsm_9Rf!>ptl~e|;2?sfyc+>{XOAUz%nUHc5^)H7 zTasly%+$qpc&L$7Y_V9tj0b>!XM^{!;o>{oDpMB;2gD!29uv#3ZjoqDfJ08e#X`{y zTQ01|-vwcanlB^*e6S6Qv1)#ayE$`I+UxvE(G069IizU-812(k7GS%|Tz7Bgs1Q4` zrh+#n!2F+MUBw7@C3DY{5&$^ni6dAykCR5`@rib6pbgw^_z3e{JBmCbNHKHy6G&t( zZDN&+5^Pw`VGJU8(gxs!P=fRN(6GFt`#;PvhjzsMFfbfgH-~Sw0e=!N?jdTk#%gh2-_@bJ}lAHf|1xD)TKwQnKQ z*(N;Xx9$r(+PD!8dJQyV;0%O)Yy&Dje;*H3-UAf_LMOlR7+&=Ua009lj;q*bN8x-6 zeM>Ru`w))*C0dPY`{P;DQo4?W2dsB~2xsNjTfW+UbS#59z{9cy|AT8vGuOcJGy7X( zaQebNRrT!u-~^4j!-}pgq5J!AHqJgddEq^{#t;A@*WV@_P(tB+gW)_zZ#rpA=S1)f z*7A?(G^<<-Ujc)IkZ`i7l%)VZ>=WR0C>-qQ8KQ&j(>{fR9bL~ug?(S0SjBTfXQ9G3 z6GZDy=XB|?9zM|HGZ1YG9K-2G$8hMx2_8^+|1&tLSZ*n`_Q$;%PVUeB9L`G51^Qk7 z+41k61N{s*xnf@hZ#adM^d%krbk=qZooGST9sd&OCxE~mLI>6G>{0u#@P__TOVrvI zn^WlPg7Cn^ulanqN+8v?3mpx_18gsT#TWRN!x^$-(i{nZ9en;Z97W9oZ@KLTHlF=j z{tbG|CU6S-qBR_Sp-*=PhrZ8i3S&(+BrK#%+g zUzu1kYqAfVaSC-tCd1C&`w0%m&I3~pMH7_LQEVhx^*fldBbe;A)9J{>EyjwhCg zTYTbyU-^pU#gFUHGPB`0IhYDg$KN`CkLLrs{08?LF2dQSN%(YWLw||C^6JEL<97zf z`t0M%;1>2bZ)o7)XCqGj0Y}xQ9RP0dIe3$yNc-0TsY0x3JnVw^*fb zE(aZ8&T#f5*xCLvBiPygs*wM0^#3Oy{0*lLY~@1-#eZV>+W@5CWPZaTZRp{dKOvZ@ zaQJL+XoTUw$o-KoZK+;0}Z2Ws#yP{6Ia%fRw%8q3rTvh0+^(xmEHjikwaE%_Ae*vFV){pl2$$ok03 zOORIiwi`Wf9!{j*FeGl41)KqO)Xoj*4QB^H!(ql%;LS@yp%uM(rb8?bAHD%9D(DH@ z6QJS-RKS->J+<~{mm1t|t4qLFe9c<8CkS3@>js)Ee z{udDcL>`CNT1`|(j_0W^=((Jnt6`A8ZU9TYjzAxkp(}qlUe4;!N0n-`y<;;W)A^Zi%A;|alMBDTAkp2j@cIzXx>0> z|LYbryl}>QAqhtZuW@=<;1R3gT1ix`eWKrPjtsfQstC4sAoM+-#HMkFb7*4};XDkG z-Nfqc9juC+40t%*y_DuAk`DXss*05zlkB@g)!b7FpRMX-9yJr`i|=^VqKkf7Z=$Vp z{OTxEpH|x__bZ9=YSC6^(mZ~Ihl%%C_2&JZAX?VhUr8d$cm#J(gjnG_Vnx;2muktx zcuYPw62z*pTy?3T$prOzEfNgF4q2&nN%ZL>I*5V&!pu52uL~|gWYCY_j{;c} zEoWT{$x6Wh1zHmZ=29)G4vsa$5VluViS-!f&&`3cwKjIa2@O59cE+KTB zA4B>upI8j{5nOfgM2H*+BW6V-{Jsx{;s~ozU2N!UJUAQjXaN@q z?yXB85)N5f8V6wm z?!P(ed{jZa;z%~*#S+LR%sMaOZ7TzQM?9G!tS*esOgH#dc7mvZyH9$Z{b_p|$rvgg zNe~cXYb5&kfSZx z4g`d(5h{X>1>`Y7r&_E-dbn;g)?Y~cU4 z@4w^R`D708-wceFNgr+?z2V3j+#GDeWfG-NZ?;dXc z$87I93I8b94*1t;Ed5)3qV}>@jmz*VdNAIzWs44uswdEst!KG4KdUMiVst~~ zn@|LywHYwf?^fag%Tj3ndl4`c4|MvHwn=w5@7>uJBgt;ysDvD;q2t(Mh;ZfbKpzS{ zWHy{njuu15br7O^6P_eMBYfThLi9GU0eyGPwi!x_-!@t)wP}*faVYYVN z=ZbLF8^20!V?11y>ww>x5H)ZcXryDE`YRlkoe2-TrSz5`k~7iWVHnS+e1W(};tzC% zVf+LPdpi>)t-*Jjf+IS?uzv=mEvFf>!7Sf*#~`~K>^O&f-Cl3P1FL(09S?z2@MVfy zw?R-it&_jlH#!9wSHIuU&^anO#j|QgOASY450#U z7}hN!N;FfTrf+nHt|x(~*w=h0q~UsNLl^K};}8hFfJ|w-3REPa&^q*7Ntd%oXe89J z8<-gGO4q@6GQg)vX!P~xx~+j&8@gFP{ZT!BzABQ$X$udRDQmedi#_4LrBhCMvl3&Qdu7*5kd>c*V*j@~ew zwnN8LLZbskSl$OZo&&m@9P(ad$S48b;rO0(Yxvop&)8W43{8N`&vg?+Ni)Xx?7lFe zjXVrJ*h_A*0#drz7kbD73E%?yttg0`_UZ=`z;;gEIO4QnyiVv3J+}tQ#u7&j4)c9~ zknC=7m@%R8fw6IAsaVnpGAnS`IJRDc-1}YSv=Sw+*ABaTN;jk#TDbY3cv-aLr{JGe z*yQ|+r#a`c^^WE6t)SkjKYLslV#R(QV!9< z;8Z;rf)?Zx_4X}N!s7#;A%G7+kXRnlKK8qB2xQsoASqfFN}93#qhl~BdJNKuDiV!6 z5w$jIterG_>pi4?JHG<6^c)5Qw)6ojrrsU;*FMlw9v>*yg(t(NX1z($(GwWx{)Na<3nE2<2(Lh@qjBs%3MQ)#O*R;A-5=9A;C1=nc1eAO z#1ok28d;WK?2tDEPhcOEYw1WtHW88jhwJEF;YmC=bGQtgw<0-1^?5Bi`}zR*lU*Ig zDu&aI+ud97fqI92tyebqR2vM<#p;_9|%$}XE_P$&y>w>9 zwu6fMsp0g-w-fihCaXmt`HWN?^M4g|mYmXEzHg z!Ptv2=@56F>l$ipZ@ONeSLVt)oUxd7T`H4YT%B7*xR=sH?~NnqULM7X!QU8aH7-T8 zUtM&5pwdq)uETXT7kE0aOGoK*N*I-|nt}pWnJl^dbV#T6)L1>Ixc2;`{)^h`|JVsJ zv{PyA=?F2gjJLSbh4M$$;ec`UV!a6wazUN*hV}hP`jmBJrs!d@?uE#8IcJq)=}c~) zfZeVyfUT2j3-PH_#Quq}(^y((F_!H?R-Au{?luYbxH7D6`hs1j$`3y?IfcSimoKLa z6%eY|?-)s@t^f}hmNY7Q^2l^q6ZE+yp3;tJ$n6qbQaQKj%V;;;wH7*A_R{*OcsL{Y zEa(kqSrxtIUJQ%leC+hLpP{8m^ifucM2`78+-1EGZ-%LAC5>GD*N5(|uj7NtV!W%# zH(Oq!Y2D+qsY-?aip3o{{XRZ31r4mhh3Asbr!gi;yaLJ^njg3)Myp>a$mvEhxd zGR2T(a%nLxZDbn`-iRz~f+tV*B*^zi==;tx4_%Lo+YtG~I z5sPM`;V~b@2FrpJ6+^lw#b?1zwnTc_lW_i_x@(p`zgUS~c-mU>qr=xmm(XywKC@Vt zyJ4f7v+)v>NfZjq^qK$j$mKSuOAcHRR%R{*2~K!z=SP=o|Re zaC|-vnVwD z-UTBEZ#G9+4+ZVRsopaCRd=aAC00+ojgh|(I===dWBb=NTA(jo-PaEiAA7WXAfGS1 zdd{3ebM5yF>5`D2GctP%Zo@r|;cWX~c$1^T^wwgW?L0g;@G66&a4wNies6_7x41bJ zEuzKfSwn4k{$|%$qR%Z`vC?MqG^|w9Ov%LqIN$j?>3XW@IwJp{msxH8 z=s7b+JHydAgZsumhXN?OW6xi2x5ZQd;k;q7>^VGN^-f+!rr=gwSZ}^C-h-a6HMi*V zO1M3zO!7)-cklyK^hn~27NK)8SbX(`PwRBa-`=WsT6gPGy;bo^yKlp(^x+FR|6oqZ z#m&9Kuc|x?$eSoH9DOdu7WZN&D{NcIu)Mhx_k0TM-hcWBLvqe=4TeZ|(((CHz4zi0 zeSUH6q1*9%OZVdETi@T{Rjovo+fZ3%Etb78@=A1#u7o~+d5e2qXz{3mIRCo)*G_qvGD!dldfIUiI zc-Nv^_0(GE8{#VFbCKd4rv}N7t562>p}*;ZQJ}0{xElKFpr7|2D>EW}u{F>?4gFmH zT|QIFjdyOR>NGE7ES|&J#D$s8blVzyR>V?&i}BWp!hb%;$K8RldnR71;PerC>>{+ju?1}q`7cmn4wXCYnN zWBGV0TA)>i2^B@neXMV}uEQp@%4#G1xC>cJ_xi(4NIwEuJNAFSN8>lsJ$fE_aXm*! z%Z(j=-;8?^!4Qq%=AA4|IMdv_=$&zed}qAR;Fd+?zwEcb2a*!*66b4t9wFCQ_ruS? zE#?_>g6I%`C*C#oXsnWUAKDgYF<0xg)pw6eWlH7BP6t`XAjZYj=wzFMl8C)`W#dMh%1Zb>dG=f5fI_u)>CU4n=b zya)d@)y!)V0q3$oF4iE%DM^z7{TFwm2JV)zbLF~;oSsf;E|%ZMmlikoKe{!jrm?o6 zP;bC-$aguwE$=8bMEmO zQQ>|40i0hl3dq(LJJ}g9Y2VJbW@ZgA{JbcMiyN=z3%$tCcWbpmx%7M=4w_UJQ zeQ{|zUmD+LYWUyspgy~#w$pdVXr^;Tb>}s2jr)gR2O-eWS}a@2nGUM+XPZ&UYDvEf*PB)1ap!P!J7J7d3obD6%Y z{Cez;%FdMzeE8fbw@yEe7KFC(x$VFpqBS*@S%Uy-yD_=PWU%^ z_1xNc&3Gp`5Q~yfu z5BIG9q(2!G%=UW+yj?*im=Scs>My#t4|e5{pBt?8hqxKPKIVgs@FQ=PKh`fE%)|0f z>+wdP6TT9Z^FQ}bdez->!L{MaU}R7t2)!=O$Nr<+l9I^jdhsysx|p@Dh^mJ?}m2e&dO7h&S82(|t2c zhmFI7;nnVD_zoEvy5a0_eAqVJS=M>gec!z;G~7{PudoUh;mZoQg}K4Gunvlxh(EUB zdA&+3y;XY@ZGoY2IEt;yEZ;bLJc`~zN+?M@jSz@>{;58Bjf=@ z$A5!_>#$#60mNZW*jMO&St zb(7^d9V8a~pqbJ+Mi1?I9v`?=yPHm_wzh>3%E-PNPirniBY82YU6Vs`Ga8QwZK`b` z?F>0Xv<|*~Aq0oMgg$BQ4q`X$c0xxQW{)Y^G+r53r}4neY1%6Oh zwDY#or2$qup}2`OZv?I;WK8YYHdil>|`JW@^;uB7p#O-wkZxVC9IclbIQHcoSg zGlZ(z0?LDcU1)URVY1b4Ce6^BXxm~QZKXSo>T^h*G@G`)XVJFeOxiY_LECaXk9iu; zV-BW~ru_}Hy@Ka<7oQTxC{ZzPGO0@P#Ps45NxpspZTs>hb$1-e4alQ}=yN@7V`FGL zY&320>W^(=ByF=s(6;1o+O{4>TXQIF%U?^|T0>}Cmd7|Z9mMiA*rtWYv(+nRld5e* z+pMc;+vzIWhCI5pTz`_w^rLOLzO*gRv+0ZVCK(=qux-r>DC=7P^|-Pi}7^vQte2#+S0aE8``#PP1{OW(6#{&O|I0EiYL#S zcX%RxGxiEl|1wHamq#TBmy%qyDQ}z5wo+r->WyfdXh>V50pH@RODK@6Pur69cw3jY zS#@Yzi+yg?s6}$+nzXG_gSK9E+E%DW+mcmTu1wn!m1t{Jq;0$cZA+J@vn@v(o(Qg& zA+1vy+q4KuQ6N#0w#7@()-6t3rxuuIofj^TrQoiSZcDwGVKtncWDPPOGfC^r1Ohr(gnyojjc}|?I}*QR1QaS?|1*c>F4B*HK=M|-2ky$j6L|{<+y@VF*WJB+#ZKu#^nmt5*0k+1GBS!c& zy7WB!=2&QaDBKk%n9d(H-uxupUS0#sS!gQ3CIsM6X8mj(5LDnZG>#T@8g{crq_{kQy=P?0^3 z)v34p8~oe+9Dj~K855od`n~)PSSs%lw9Lx*fp2(cyzfxYp7f4;&w2a2-D$L{^Dx6X z*PDmg&11YFUVpEP*V=31)%Gf4cC+RF<^Jq`<$mbC;U04jx{tYq?lyP5yVA{Z=ekoc zv3aoD7Zv&yZWFhbTfxn8E$1)iC+AD&1Lt*2Vt&SX#M$X=aqe`MI~ixDGtn90WIMf_ z4o-7SV6Nt*%Q%5!*uP`;@~8GY_RIFOn7jO-eUF`IueNWoZ?>n~W9{qgtL*M}JNq)b zzFpNWg{jKI`px>*`owx06O<2IPgoCH_gEXPRp=p@YfZJrSVOG-R#&Ty)zqqORkVs( zcH*zZ&xwC0K1{rUwPz0`9!(S^wkFmkR-_Y)5_1w$5~CA?6a5lh60I>AxlW=|qIkkd zXy&ixH|8nxZS!UGu=#|!%iL~mG;cF=%sJ*1bCfyA?1P1CTbhl`nr3-3%e3NuVgmBN z@y`7Q7GgdSeim@^x4?y*k`eK zW3R@J#Gb<1&G*MP#n#4d#nj>(;l=B^*j2G^u{Nr4GPWBVjoXYIW3Dm97;Ox~O1YhkRz?$}wow5KAldp~SU2}8{bT(t{U!ad{yGk%rc#n9wyB#X4tmf|XMtnJm?U6lbYrJZEz(CTz`x z%3xJVNN}=*qYTGUggnX-C^c5%D4WtO*@>J@^JVlSW;$on&8)2z&)Ia7I^w2zthx#b z=gNf7VDCAsbfKIrl`El~#mXvB&XmeEP|jfGcqng_%4HCzUx=T^@!3dugFK~n5T~+o zB2rF~DVIPwnU(l}y=jtE;_h#n$jW|@Ca`opqQ}eVd??4UGK4giB?r>=EL{$13`=FP z%V^pKt>i*zN3pgPq>(JmNBjtR$Q2NWOK~0IhjIKgD2GbrPKeh@@fnEMN^vie4&kJu zAPr`zKcqn{wL!vbIAI#IQ*$;Alshd)f^1GO1yaP)ct}^XR0+~mBsrHt8bFfeVdws| zvju~nv#B2|W03l?G!0T8mac)+o2BcpOE0<0+C)xzQ%@x$K@U#Q0|~lICC~(K)5tu}F8TElX{&PaC<< zW{9n&_!#1^;P|nSTCrq8YROV@NG({xL$IkiOE+QX%jM1wLU|c0laQLR)Ckh0kkVR$ z^0g@`6CEHmVW~OxZcKY8;7ulHQzID-Wkac?+uMMZy`a2ADpx>RpOrPCtS6PXL98pq zJrL_iaU+t}rljdaQ;4;AubUBHQ^wx|Werx&g0i|)ZiTWME9qgb%2G{8RamMHsWM9e zq)O-0Rb(j*T?Ljd#{tXB1Kw)l{+DCzFvOQ-sT!m*EKzopW~msYQY?*xRFWn7l@ctK zhE$v-4^lCf%0S9usR5)EOWhzPS!#-N2KYF6UQ6^iEU==DoAWIV^eWNwwW>HYfe6FrqE(2SNUu*W1}Dq zWgSXyJ}f3;XCeRdVKG_E$!CKZYmaC)n6XMYA`GUk#n1-L$%n{xe72b}x=6N}u^EVm&7`Copk$*N zYXl`5%^2P(bMj#`aV9mOgvq3oI8r`rCX^{iY%&eX5133{Gc?o@`D`-{&YI4Y(x7bU z#D&Ya6!B~`4K$W>@?kR(KMoR`Ok*-6Hkk&jT0Sf$9di+sZ8_l_NHCe$B}6n^Oas&P zbMj#^DdlR!v&l5@T;;RLG)5yDCKKNn4DB$qY&4BI(6Z4q@RpyG&qkBalZ~d4gpQ1+ zW~7l2Hj|DHBa+WXlaCIgiD)`HY$iohQGn5elCqd>rh%t6A2w5H`V2Y{Y^JX1oUt&P z(9$yin+a(?B$!M{aY$@2;Sn__pADu?WxOWG&ctD0FX=FJ(_k>MOD{-lFLin>*j{4h zb51_&B_+HUVr72*=#?TTztZ_%WRnR$8#(zfnUoYwgnZac923?v9~P68s8;gXWMUd_ zPClDV_2t0icrF6;`!6536Kx7Ng^2>wwv($l#|bP zQy+$C*iDK)3Xu(`j_)ry`D{0JG;8wNZgR%4-Gl?IoP4&MaI=t;&vsMSRzrl{qyyza zgyF>fhYJ~`h3UlA&4pyLbQ2_4PUz57%7@`B`gOLPy7nZrFr1{#fykCqXZymIQ^%WT zPM*qKSid}&PD*GX*iT69;yHQ!u!4lv143UCrb6gLLMaHnNoa-Cy<#?;Al(C1Pg30o zRS#C(0sEWRoy9{?c4O%Yr0YuQvLSRKp&5kEMVd||z>eiz$*J+Yw*&GE{GGtu<$OKi0(!=FR(<+|h+qf&Z$H~}AT2ZsZ~9%09@ zRoFDF7gh_)g~jke*9gu9KLuY0pWws5iQrgpIM|O*13U2vJug@rEDILlTlMr{0=`QR z4hG-@LC2s~&=j8ts=-lOF?=L2{B!18SR}=dbmb`HTE{ zX@9yu0pAM-`vd$Qen-C*MkUn4H-mB*Y~bRn!8z|I?`!W9xJNtT9rF%*`@Oy1PH!7N z9jx`1d5iGzV7fQK8|e-926#QZj$SLTsaLPaK^oj+UvQ9i0-q5MyZha}?oND2$aB}a z%iKk7dY(Joo#2kd*MtFX54WS+%5Cb_bE~=K++wcl8qPU3+0?*j*AZr=j@;CukBClckL7Q zG5fH+-`;EQw720i!&-Zpy(n$Z!!IpbZde&(i&_HuzFY>tycK`=GMk$9 z%xY#ivzY1rr*ky;!#@^39N!<`i|-TL;(774@nz}wqWHY{^!SAM$oSy+fOrplrf3y! z8n1^B73I#mLi;K9b?g&-t2lun1&85#d~a-LY+Ec3-z%2I7RBbprpG44M#Am*0C+p^ z7;6QmUOaQuXE4BsyH8+$PtTochj77#gW4bZH z7-qptzsL+*2FY^7|u|5$5x8) zS0fDc^T6o}v$^8sikB(E5faB=s@PPqiDF~LMv4s;8z{m@k4V!G)mMsoiggv?sf&N0 zwqh;Cnu;|Pt1H6I3a78CSVgh2VkO0jiWT6ji+3om6mWvW;j)Tl6yeQ=^>Ea}EU8#R zu{hBcA)FgAvlQXNg2PG0P%%*S;jDxccuL_a!si9+ZAD8FPMTP6D#jJLu;K*ZCWYhS zD}yN%@sXUu!QYC1DgFs(F&y!S;yFb)*)zrueGjam7~@UuL2k#A6I-#@lnM`hzSuqthiTkkK#j$ zyA>Z)+y$m-$3mqjP~54wL-7H{`xWm~yjSra#qEmQ6t^ngt+++;F2&9K{Nn^RDMh|w zp5jKu4T|d(*D2nqc!%Qcifa|uD6UpqMKnclo8n4-{-Ic*6w4KtDK1sKRdI>pEsD8{ zixqPe7bz}O%qT8Ubj4|kHz-b3 zoT8YXtb|F56BQ>Yj#nI~I9Bm`#W9Mbi7^q3QXHu`LUFj_FvX#g>EJr0xK?q9;$X!= ziq|L(RLoY46t7miN^yWEvjSVyt8VlBm*iZv9gD^^pis#ry_vSKBE{_(>Vm7;=TdBt*yWfjXP zmR2mKSW>ZsVsXV{idl*&#iU}$&p%EePzqnsQ*;#_MO)EQOemU)amARTp{OegMNQKG zo1cGO_M%l_`Bj6#orWvRs2QqXT_fse^mTI@w8(4dnJ6Q_^skMieD>! zrTA~fFBQK~{9N%f#ZMJKQ9PyivEoOPY5zl|_(1V}#rG87RXnNqj^f*jZz;a1_=e)^ ziYF9bGY#7i{;Lvj!%v}CFbskCvcyXgFG?JfctHYAQ7Pq7i6auvOFSn5$8eP7FafXm z{2Z4M$rX_KSw>SEA)z!%tRt??Nk{e_W^J>?@hcE+1)w@o~07seaMMg)`N3&Ur< zM($_!kc>a`q=}m6;|`Guam@UV)x(=e15Qvzl>w{OTjlm4?8fbyPXo>#=3b6 z?KkY_V)xh!?TXf5ANMCE zHYPfU1LNu6!V>-r=Vj|nt7PI&BZ?Z>+TOSvbsYm}uthbS61X zylqyk#6om?KV()hcRMTMTZ1y;lVL&pd!w~e8m`8Rgv?~)H{Q|W z!f?tbeh%{pGUv|Hbqk$5;($K4v9F;B+d2rJmb;(xn4?C;$y z^R;lS7rM7%TGOM>9T;h_I6gPtA@*{@H5VK8W1GTT{WIP*SRQOmaM=06dER;2x-n>i zffI*g%l-A<-|;H&jI<~5y8VbX(tF99W==33@G4n_mT!NaXziEAn7?#c|BziXNc*3A z!`)tH!mDLH6@TCSm-D?dz-nkt_m4T<38#)zIkDB48JuwKV6=agu`bx+nZ}{;d+VlPrk{x2 z9y9H+@fP8Nv~$Lq;63Z@kG&bTjfJ6y(TEG2P4?385_eW&sJX&>-y9TP77XydOS~7p z=r^&q8}Iqw$4k2#tZuPs#<#&9tD<>VFvPetzSOT{9>BMaIk9=+Hs=G3)qKM46RYgM zZ@+Ke8Q)<%VRQ=LaIbT#1)7}~ZuDPwe=*W0!q5HN5|Mc}enYGu21oXAhWK1xOki8t zK!jZ1P7`4l%fQIIhegO0Zng+JvEmsKa*aD&gk0lx5Mc*;LP5-_WyGA3f^0UtvJON5w>I@PrZc0dr)&aRv!^wUi71vv2eEtx!SEQ!b@3k zScFYk*eyb?c)N?RF)Q|nkZazmBBYwvz%C6eILkD&Z6dsclRTi_#j#I4Ry-*}u6)Oe zkSpJoBIL@qBrFLXqnQY~@@*u-8oa|15pwNYT!dWvS|a4!#12)8&Z064kBd;&zavD* z_3s=JR^S+%4p+d_MOco0q_GIO0`4Y4u7JCWur%on5tiZ|ZWkffz&D7n1S{?pAy>gA z@Jh~#wIbv?xT6TU4vvYC>tHOF5{7iF)*|FO7zupd;~^1pB|KMPsRSe_Cqk}-aX~gG z*eQQ2)PzU7RA{Phg=Ec!nqPg@z8lcUh2gKH-QJA~?muI<$YeMxG{uk65!)1Rt`1tNnn5 zy+XdDj}igb$^AvZb#faKoa6+Lir^g<0fJ=IwVv394Iab^$f@fKH9KU~d(UU@s|}3)oUJ z2Ma{-5GfF`n-#bN50avn2zIdo_pgu?HAPUsirYl6lZ8DZ*ula^5j;Rinu_3l;>{ws zk0b68!M&uIC4ze_IO{gFts>Y?nyDh#M#NFK603{gZX$AYi;BOCm=?iiBFft)KIE+; z$S1`x5#$l8iC`lU7qEd?Oa$wRBSo-|hy&h9ERB)QcaVSraXYb$2-XrCh+qw|n+R4D zaS^NdmvYp*JMR8Uia^m{LBw5HPSi!Pj1xX7f~BOuwcJX?W444Nwu|5vQs5_Zp-3AV z9;d~m!H?u{k{u#gL<-!Hg{(Lz;FpZ<%x)1Zpcs6#;f6ahbv}hhh~Q>oV-eg$tRsSX zDm<5Wd`bj!SjdQAHpNU9!7Ngwwe{*Hez*u`Pz;{`8;P(&(}^gO(|A`D!5diEB!a0F z(^CXfh`3Lai6~){IKfI0Ok@G&WC9CEMKGR{R1v{Awd+{oSlr9&NkDdtAr2A2Xd;Z> zC?d+lNIn8GWdtd3kB1XSiC~yYFqDYmbe-b0#P%W>LPRki%m>&nfkP}YAa zlGzl6auIRVT_U)e6fh)Lv0{-32C#5I1pO%nk6%CH3=#C@hz(c+j1-OVLeGjNBIrem z8Y1Xfv}+Giq(so2h)e4xe;)>-D<{E4c2QBCi7;5Qk%7nRN>YpyK}RBPUk4(d^7gzZ zPOu#*=7^vz5eB3UN8o0)W?{7muAmqc*;d5S0h>g1RKc zMNo%`B3zq@LRE`6Qv@~nq>#ZiNC6{Moruy`jo4iTRXM@EB9KiC2$d-Yhpt4#V_uO{ z6pEk%DPTCtv*JP6-*Tj>EP}GcxgsdTG228?niOL&Oq~_T;F6@MC<58WK&?}pV|I(6 z7{$aykVQlZO>x9g5hPjIB7%@&@Vnf|z{UF%Zi~m*BLRig<&?Nd4hwlAkj;!EBCse4 z&MZO19XE-nWaCA@5@X>>5g07wh(I^14+;NII^Yc9 z|G{Yv3jZ7_Y774?v6=9HCtfZ5Gn`<9@PAVgzY^yNU$!=G6aLSn=q&u7h?fZeN8aN> z;s3zG!@@t!!WQ9wPf60Y@MUvjoAAHoD7d}(hN8*~pI`pV3jZq#ga76PcMJbZ7M>LT z7cA@*{^yjWs_?nNF-!QLQW%#Y8yt5D{}d^T37?xAIKz)9Tt6-32Y|uC|3K~WKJilF zzsHB(A^dkq(O&o`i4%qY4iVo>-sS{5h5r@{yM+HH3-=5E4N8JzzD}$q{1e1>!hem} z1787%Q-yz=&3Gw%2p~l(dqHk!V|*(7bVHYCjlbv z(h(JYp6Cc)Ha2z(Up6)l;L8BTc)~wq;q{+iG13|pIfU2(^c2aNm{}pr*sognYxr&W zX?QX`9v;Cwg1zAb;pT8{xFozeoEBam4hj2(UBWBECSk3xe3%MNj1~AH_#7hzj$=`^ zeV7)vJ=hSe#2A6u!6b|j7#Q>lIs}&o^#htf;03z>8%-cc`)^_z`9Z9lw$tC@-{CLC zqG>a*WZF>wD!)4xOKawn3&7&O?fr$30AG3^U>v|vtk%89yAQMOR`c|MsorRBkk`k% z60_ABdNsVVUeYts^Z$eUITl7c?mmxY(H?T|MW6pFH`l$%y}=#h4srWo@<1y$-N>!s zmc?>tapx@Fc|XNMXfLCe|8Zv*`uNv7E1ZSSY`pgu%^kShsqa*AN}^|9!@_4@!4>@L zSoZ8``(dnkwi!M8OYHgfjrKT992j7CwOiXw?Amq(JIhX3e^@_QpQ981Rjg~a-`b6r z`8=97kjA`$Db^@!kk!}fWVN&!TGg$xSkf$(_&xC*dhOp%yp%YUcnq@!?oQl^{`v)p zndqz^mbg06J<&FCDW(imL^r)<{)IKn{tf3uxZ(eKm zH@li`&@o@ztbhpviTEG!AMh&wZv2v0=HVW=C4s~}>m8${6;A_lwB0mnh)(Qfz^h#2ig zKX?pcH6BscL`*d^yQ?r8>Kx85hLFqrm>!DW*3irYg88!#=Z%yQE$Za(6>ey>zCPup>I$QfD!}WbgiMj z6Cy^wK@{IW#Lzd=jzm(7eG^*4IFzW`NZARZE|XpXk%zuD5Whg=p>OF+v^x`-LLU28 zUvB`B$G+7USrB>bTYaoKlPSE3j$3~KBn*9nG#$|x`KD|2u@*$8kcYn2F9{`vzCkH| zg~(&y>g%H*V(c4fai0n?_zfaPd}azU`VAx6>gxj`WB41T*bfoo-$;8cL>>TFpK`pA z2f)?Wo`uNc-|7n&Kg{Fb>NkUu$G_Fn4?^VeZ*{TqPo@y#-*l}mmZQiNVgMXOkqZ$c z;7CL%P8aeRxVjidohjroaCNaJMW&F)z}1B}#Y`c_z)|AwAYu?4i74`gBU!{UcA3Hv zQrj6KM!_KgR(r}6Vi=sR)zMGjBzYWMopw-S92_a{g@}Q0x>gG_Iw4~y9Aqj&g*+Cn zmVOU*<#hJ#Wc39%oN(_yR9DbR>wXoU;;N)|akI!bXd zv>oJbR4&>}Z63sSEMi5ROkrC-SOrLJSh@kxtz}Bu=?W67=%*pJl44hgEv0zeOlJyP z&~8=cLfc$w&p^DKMJ!5~DZGrt%KAQt%~;gFfOsj3Vj9GzEE-iHHjxSTKx`}%42Rf= zMb7hvQrrw}0}|7fu{u_!@Df&&@hYq@#dmQ*_2|qh+fddeWhI@;Ae-Gvm}Q(PWV2gI ztVL3oU7=NK4<%dOO5z8IY;`LN7>+`=x)rggVx|yQSJx_Hft*ZXRUXk=kzYPUkH#A&gjQl^ltZbh*hB3s>x`a=-e>Q)rpA+pu2h_%5pg|NCpt7t;WMi(Bd zA+ph}sP}`&Hn*Zc(M=b!(XFVz3N?%_omnY}Y<0`)*F$8hTV8a>abb0ZR(>XwY;?=& zFqDOCbjymb5D%j(w6a4X!RA70i)c2vWpy$UY;wzDIjc;eOFvaspP7aXo2z5#7$3XA z=nAcj0f}vH87h}-bIWM!A+pUa!?%HLZW+-GN!jL>X@YpRxn;x)P{QU4t#m6$Fu9QC zAet?1X?=#7DL}c0vJT2$0SqogScd|(x25#}BJ8aWUug#+!r(#_CUNkC$4}^Zk9d;BAeVS ztSgo&V3V5?Lm;xnO<~EtOu_a1{HL@JAhXF$X}94gU~+|)ibI0M6B-u7#2HnF6-B zmi`Kk3yX_6b1StfDNuLK1<^#>eKLR ztaqC!sK*1bH$bXOl2{eb6x5+n+Jm90&8l{gYOypCku@o@6NDNhlt5f{KEyA0I}@h$ zp%$J+f9-eSm*J`KJ$T$W9=;GBM%Uvb;jZw$a7(x$9j*zNVV3vJ;mmLf=6Mef2Vqoq z&#+V2CcHds6xIo=VqACe(8s&l-@zHYt$l@&-R}i&1TSOK_krMvU=PN2Zx1#FcLul7 zBG|!PjP5R41iNq04exKQf=h!-f|^04pmdN59E|Y(!~ezq&i}&y$WOoHzlK5H&-zcv zb+G-r{f!vrz1&~y&-Z8fQ~hypaxxh2b-nz~ep|meoNkgk$ntP}gU=zp=Kb#dh_T+E zdhdI0davLu?;z%h@AV3>_SR-^9j1!k>Mg{0?;E{Im@Yol8|d}(x?{xm6<#y1fmh3` zoc79iS)L0okbkz;AM9IFp_0o%As08mB)7fnVvgb}n-oI<@htSQf*;J*<=cm;D<&%KzIwg@NF& z+b`MA!%5}i_Cxj#35&iMj+4wlKDV2`q|wIjO^ULf1sE$yb5dtSq?h}q{! z+qQL#2mcu}&_B06v|hkpy$gTquE;W+b?vg~<*aD)7^Hq?x?zPO@RKmHVTHlK=!O*r z0oOIGa1D^%u);u~Z^H`NK(~e!BA`Qq3ReTI8enr-;|7g@hK(CE1Zp>K(7=Slfu`AY zBx+04A~eaaDN#eBx*C=l#R}5O5#d561-quqil;p4YPSk zy@uJQ455pUlrafILMJrH7KBT(HA4O9Z(b3ve)N|NqGyk^e@L8@I4kkH#2Ja-Bz~3n zMS|B`tRMX(LqAIVAc1v6>CoRxd`GAkeG8=Zy3scjtQ&nT@s-5CCBBsSLgI6Y&m=yT z_(bBA#K(j>(MJ*=N_;@56}>OfKfAqS0rARz@QmYz9@k~GeWNxVbBcWUyO8wQ8Vyv6JgX00i$LJ&q-j^423Xi zhHyyYpu_=*XC&A?NY&^m3RQ`ol-Mu9PC}|g>?EX0#7;u0MC>G_a+GE-A(bQc5>h#O znD(d~NhcxE9vON_VzoSW!=j9unOd>8P6wc9rNN(OIIC#FYdy>L}4cqP;{riMA4LBw9;cA<;^r zr9=yf<`U`4B`=d`CUL1mQ$j3iBGFi)kwim@1_V8Vvm{%O;4BH-dc1|RB*<`s#NoUk+aJ%Q{=Vr~S*FPVTv2Rrr-N(fT>O(H?Fa!HDpLGu?a4wXDrSHMg@p z*4yG&aVI%nx}}_}oVVRdK@(|Ob*$cYzi?4FF%D$f=v43=$M@e)y9NH8 zXtpl18oIZK^MYUf$-#H_w_yY4xO;hc%>606)@u>83~un+!&6*f-y6(v=31X&<;pDk zed~?jF?*$T9eN-tdJo!JPKltqa~i|h^sr`dm)*ji@6C43gsrXI;6dx9Amg|3=X*<> zA=Xp&qgG)s$a%;~AN0!F-Mr`AXZDRxes$ucwF-TM5>Dkm?v-)T|sljG-(Y~P;o7GkvrY4(pxp^Xu8Y3e=wU^8Ty{;O z_H8ygSixiIZ1k`q!PM+(RPsw_S7rU)>?+Jxv(deZ=r9}ItJwd=?1~)zBpbb}*neww zdDefQjlNaHznhJ|Rq(y+GDPubHo8`k?nrhi);}v-apST}u=;Fvx;X2rWusq}kB@#; zuyl5cS&)sfTL>H3=vBq>PGqB372%`V=v77dkZjJ?mCd={*$!#7d$Vo2m;u@7PDSFK z+2~G1;%~Cior>_t>^Mcovtt+(gK(#8gX6Er)|tO#3!-*sw#MljM1QkBEBcFQl#l-8 z!^@W4^ax$3I7%iuOWKkVdQkCWzeZgQejsYkMyH9wm94yH(RUnuF#49&$0GEc;$)l%J*P;P8=>bE)S`bAwS&=@ zM14#21?vYz=r%?CcM-Zx!Tu4tP4WBMspu0{bdJz%iUU@R&~1txPslc3yXZp>-W#FQ z6vtf@q0&f!|J0azh=nbpf8^p#@woaja7vFI3cLG%L0e;=Wv z6uX~`&{2xRJuRDoBO~;aLhVM+5pfOAG8adO)0}R9bchvs(LthE86BY0s}MayxFLF) zWA{f-aq832lgzHse&+FLA0?X`J;A{<(c?tzkLWQD?~5Mg@E6e|L@_OTm}pdq_Hy~t z_C$L)VtDir5t*@@xjA}}*)rNi)V4;2%y%Ph9@^1Pj{Yv%!Rl4f14OYl;ud22=sphq z5Z%ih5Zy!6AC9&&>qpyg)@ihEw{m6y_Q*7uH9v;HgDY8)DI ztFdjgl4F}iD>(Law4B2;qh%cSWy{fsxaH_ZOGvA&k8a^q-$l8cs#(O1$0kt@2VaO5 z(V4fR)+1IhpM!Nd=mKX%Xg|`s4mTg$Me|u3L^m@JMmO=!b~KM@REXvhwI$IUj&Bvs z=J@&1EY|-!n#uZ9G=sS^x{>v}qUl62CYnY}>no!hIO3aVDsx@L%}G6)OhNt1XcDXU zM-w?bC7M7qN=M^~Vn{TOGCzo}XWkZZt1=#Ot5T0fkyd{p8p*Niq7lqm zjrmU0m8iWHbs>rcQD^4ds1wH@h^{2URCZ+jzNiCH+ZnZ&zyC$ljw61G+A^0!-0mzD zwdUX-(G{HjNYsj`w~ktJctg~J<8P0+@mVptoP)I@ZhZEOnsIP?bSbk!)RZ|XYQk&~ zH3lc^+RsrVRy-PU-L3r|HQ?}P(IrIv`lvop+Yr@b{i>)gmD9md9S*jOYBMKBwMgA5 zs!1pj)!?0eiK>(TOg-GAh3|xW9!vE#?hO+)M7*_aV%>l~M(K60wl$hsdU#X|kA|O8 zf+xduNBbC8KzBw9{|L{J?o_zCuT$JGY#aYA8bv}5Ch(Q=ONxCjT=(S^qhfly7XF5z zB%kn5lK(H4l>dxJ%JfCMr2J<*Ql>B3CFMWkkurVJE-4fLh)2rwMZ2W@XFO7-{|A?p zD76}#MWW~KpYceUzG#<}|BOdUax~;#luJs^fp%~ght4|eLyqORug)b8RsYu>Dbt!8 zVt4e^;h6AW^M7_p`OkQyOq2Jai*iZ%&v>LvU$jfgf5syvcZ*sWn)5%pq(r|QdI9NX zp<518TkOKUa+dc0!y_fS1>wByVq8-GGaf17Xze0hQvNd@DbpA2lJcMNNSRKQ!5!t{ z4v7YkFx&y7mHyvcQvNd@DbpA2lJcMNNJ;kZ;#^Wv_wj$zY=17wCFMWkk&^EJ#kr)UO#1J8q{IxLi*!l(&v>LHN0U5; z;eT{ViG)02?7!=g689f2^Df3EB|U%teUFsn=<(uQQqr&g_dQakFWMy~e*PcuNQq&w z7wM9c?$dwYBV`)HurAIeZP8%JfCMr2J<* zQl_O#%4bms+VOyR9UHM~r+rg~`Jmg4coA#ltU%(0(`{G98Y~ql4*5Bjrh+S#C9(&T? zit$lv{H4Zne_>)PR%Dunc`qaUL9z1YNx#3}JvPzr5MSuG^qXLuR8_yMpXEEg9{b72 z_s)7hnpyD?vF(KJ(SBi8Ra<(zb0b6#)`Is2SF&JNRYwmKV~HO70!Rp@10=*)GdnR@ROBg_u57mb40jn+b|t~1E#A3qdJKO8HFdBz{H+bt*2-RX`MKw3IY;@xA)_AzB9F!Ol-)E1o2br(g{jqRJ2Mph8lD6w&?U1su*Nqp= zedZoa26AlO7=>9rXRRNtugtH^lg7_h7M9N35#JYEo;Yc3#fl|AK*j%LcfN7(y zNFF0a50QL>*g_qql?{JfsA%tVr^S6cn$|E9&mJ^W^x#&!yiHS+{HrR$pE~H>bkz_S;799n`!1l?_4sv5-pMJ!WY_5B=Py34}{O}bO)38+)mS!lkJJPWBA(62gcWSnkk!XL&OQ= z^Ezi3KCe?orD_q2i6p+O(=5m2WyD4z^%4;o-;`KgB=I4grg0|IjYvQaH#EP}`cA|= zdYWy;TvD_XsYOKGs|Cc$B6TwnXN*oPj_0z7 zvcP3g7pWUbUtOfQ%()^pg~DY-ipw5;XdH!cf?VkE5MEDVTnHDwOGS!vFpUJm5?^Un zKaskY65t94TUnabOQa$S*A^)*WZ3a43gg7Mu;B`NTP-!KuSoSEeGif1Le){ExGdq} zMHdd|NJncaACJq7CsJHy+KUvrVqpJJ$}gjYC?uB>k(KCe;V>7H79!P#oX@>9|e4pI`9DBMB$I)bAOHq78| zsh5s7Ft2?`niMp_T~f}XZYRP&DGdG~Dc#W&O_6RZ!sDdJ4EcX4^5cdu8`w4&d_wv* z@Bu@F#Vf5?Tr%WB4!1t(fE2An$c2lhSW~#jkr8?8p)f2O-|H44d{&0TRf@fcsKTmA z22`oCZw?}SH$9@rdW5;K7Ws6#93BSvo{+N=nJ_4?$R`F5N^EkAJSi2;wg>rE6wT{~ zC96sM!6n4&ifl<~qB@0}ilikOa@AAheyS;)Hry{G0=^=0GeTiZfaRA}YpYe-7}GL4jH%QReQb)yKdtE6(u4fif(>L?`X!*r{VM!EvB&`vxlfW|c3_f5gq(k0Q-mK<1e{V7 zC~j4}Rgr(7h6txom^^n7tBdeCN$HOUt|=y~a4ljrk<5~mZfB~B@Kp+fuq*KJ1R<0P z>7Jm92>VjF3j8)Hjw4pa+NzS$8$~7Mw+MWg^x$A3=GOhLxKoiY0Q&JV9F!ok2L%j% zM|&rkB1MrCtFj_oOkp^Y_*^kAdBN2Jh07>^IHg6BEn{gBE+aiWYTTj7^?xbl;Hk)w z0i0f)qIhsMKmQQsD+Om6!c|n*kd$7F$YB?S$qAL>jl?2%V(@arw*uTp`eGvNE-5|y z6!~|^5+U0KF#RGWNQsb3b4od1Ns5pkBk&N#gBujNv?JVBhNagexImet!p(?ixYUr8 zKCs|#<$wxvB?MP8{B#6({<%Jf;!a8cMnw2xTp=kvMtjNy5tys=VteGAA(7DPUW)Fje+`vm zx?S2%#;4eptyyQR)8-{c73<6RC&qf`i1iVcemrg+jUToSSdYVx!~OBqu}7U(tS#1h z_;XldEpTR7v#}K9SZkOy5KbMsuKVj_J()I6T3X^v;&l9k@vp>}iH{PvRHJd?c;aZ{ zfLT0#4(l~unRq<0J8^$vOJaTOcITtSs>G7mTh4uO`7k>%H8B=W9|k7+Cb}lt8E1_a ziROt$iQ0+EiPDK=!orG?XX0O*r_C?TkFaXwar3Boz_{Oh+}sUE5L?Xkv2=B#ySWPf zAQr%9;uFrD=4^ASIToXOPh&pWM;M5D9OFC=gpY^2!~0|7ok^Hawmw`1XCDjTFLOhz zOE?u1%7%pljhXTNVc)Q8*e+}yHgZOXwZqC`=`a~up@u1Cr-LtpkIWkJcVn*@b)1#K z+wrQgBhE9y@pNz$Gt3?jb_e$dTY~km--A`LU6^CG0Nz2S24mw+th{l`c`z6TKOudC zu0cDqSpZUG0PkC zeSy_UzmLyyHhZ6AMDy#h;aF?rkoQDRmr$m?Zxg6EM|Sh%!~>3WsDQeNQ2-9Ox)@PXuG_igtTv$ykQ+8F{jBu}{yWATx# z@oU`;a7A*9JKvq*PK8|J29*o@?(4tCX0P!zh%E1n+xZq&)H96 z-H;vj-S&F4FzFvXIoF8PgTxsk#gFhPk-Cqm#`s4Cpbu@iKG{>T#?Eq%~X-%IvJBdzmzB)sBSOBE^-)wIaoqtv6o1uAqpSI7mwp zaHpCRuNNtM2g!U^bS#vQp^&mIvj)RtxXDCZ%ra{ifbO^RCNl^6e+gb z2v?zSACY`fde~egQk6(E8hKlug!v*Ry<6c@O7p>2ij;I>g{}l3DN2DOeMG!QBtIn1 z70LIBX_0)F5~j7KA}Reftq{pKDe7vG64AV$5D8a2oDk`ie#O%6vFQ%gVTyjB&AC!e2aL9!XW4D|Lr%4 zaU$fV!#L$EZY=VjpHm1U=?6evGUVratn%&(uWe6Cy>!Zla6c7pNG$roNS;6`JVqoB zDE5{N|4^JG8I&T97U^(+6asz>$X^_VabvknF-p1i1c#9x1%$J7r1G6RQaNrMAwq5_ zgPabmNa>6G9}X8uekR~-^$F609hqnZ@f8nOP7XnS%0QnI3=<(=5q!6?RRq~`kS{li zA1abl6}kO7RD@e7{J+?H?>MW9tlxX@J~`~O&p8cEH#s&SqL{{|pcqgP z4ox3WyW2)Vgl@0{n3Z5eK*fYIGfsA#ah#yzILUFG;235c=YH3!b&$FDdGCFn_r1@( z|EM4R{d%pc+WYLP{j1u$YOk8;=O;MKX&X78Xxs8PILTr7Ey2?HeyEq|JU{${7%FiN zD{;pvvBJ*r!*$}PA*)cFZGlKZZA{=y@t@`ApL7_WEjYo?zi6oW`CEl<0%3}=mq_3O zzgFUffv-r0GyVK3hv9>QXZWGs#o({(Z*4ng5dBU5KAmW^h8NQfJ>%W&hdt}^F4bDQ7`jXs|6A_lKXvyEF{fk9^sGm?iKQ;x*+=-vG{oHe%^Io zkiP0_C-NxSM|y33{#LcL`K7#5drVHuA@0`|T(sHS{K8vexW+I1M3C;_M}m|5!s}WF z0;wv2{C-ayDEZd~=`UQkR|HcpDAK-79O(5+g6PZh`Vt1A-YN!q&u0Y@(r2`2bb7NG z=vbao11Ixj#q+p$#{2p0hFZk<|KN?3_$#m2cr$gk`#Z0F*f?~spEI~vzNgwdwNw3k zn`Z}Y>SYBtCjMl3S$ZG@PuSX>NIEN7{u#m_%yBy^$oTKv#Of^8uJu)63eR#wPe zF3csSz0G7nk(>O`#U6ybqb>-kiL`pFpLdNJ;zBdt>WBB5E4Qjb_EK_-AG(0YM7R9V zEV+{_WLF4_KyLQKxoUz;GvZ<}dW-!0Luy&%hbtEUZy4jS-McBki#mL8YK(-e9B1Z##9RkZmgCD;>IeO zZ^404RMi9SY<;{0_m&nm&(G_f1%~eLkS5DZka>QnoeTJYIaJ|@()q6Y+XVuxMD3jG z=WE11*AMl+BG#=AZozqCXOJ+*VW{0Lfia(Uf9DQ2j0?{3^UL-AgY*^yV}mwB?Z;tYCqrTF#NsYj}0sKYwYiyf>-(Bdk)8i z-!y|>2^=Tc-w8v{p^E^`P^{DbzQ}Nr;qej<<+A7xmM6L*Bfi?b@~hn|zuLX>-*>On zw*&IO$e5hXEr2>o1<#b*2t3136_CGmcn*#?yN>($TZIQ4Yjz#P^0y8va&!WE9|vFU zUTITuyw=@CE)_V^P&Jvq^}zvDjR%LBU!Pd`dx-r#*l>{HK*Irs{SE1qzS_N#OU)Z` z?G8bGG6DZCm#Ffl9Q@S&{>1QOL+!uuYj+9$)$D&U{IlVo3_mpdqv2P(SAMm7o zcCY+u_sXw!uXLMNFoXUxkx1~ThWb*IzxAai_@>$Qxt+iDB`5fX+4a2uf4^>j|G@D3 zIwk*qZ1+mGsBi~-!2yw1Slt?IP3JD~r$i3(Y?^rEthXaCR1uP3`IV1FE|I~4)7f9v=f)bTZ_<7@CUOQ+-OB2R-lz{a5iY)}W-pboG>9bkhx zzy@`I4e9_J)B!fA18ndEJHQ_d-#66PPxyalfB#mngyA=4__d+Fe`4{^ujFs=UBh1* zzGL{d;V%q-Zm9PY`SgAQ^?m~NeggG=0_A=x$pQ3+0`-Oh^@alVh64450>5tu_@3c+ z4ZmafZNqOFzGkTJ(a8S|`}^yL+x2M>$E#*|#c-S9%Z4u*zG(P@;q!)D4WBc7*6^AH&>=a}HScl==hN}!$8nzp@8J1QUS#Efj;WEQJ4VM}&F}%a@ zcEiPnt%kQ5wiq@WHW}XPu;do)a^^RiW07EJi~Yc|{oQD|z_7t^zM)=8($BNM=NjH* zILC0d;f;#i|2LRnmf`h=*BM@Gc#Yvq!>bLiGMr&J-Ef-Wm4;IduQ0q^u*4Z(W`;`* zFEN~Ac(LI{h8G%6HoU-alHo+d^96bDb)Mn5hUWRtcz80&10j zS|xljqg4WGm4I3$pjHW}Rl@fdS|y-X2{={ne~8uzsC5Erm4I3$pjHW}RRU_2fLbM> zRtcz80&10jS|y-X38+;9YL$TJ>Lr3`oq%V{B?iwjoM3pS;TeXf8=hu3UXWgNoZ+d4 zV+~I+JlXIh!_tXHPB1*)@HoR`4UaKA+VCjDBMpx*Jlyaw!$S=ZF+AAtAcrOQtqf;= zfI0Rz+|O`d!><|cW4O2BUWR)b?qN8_aCgJq3`ZO8s>mesC^PJ0IMQ&0;c&xxL2f6l zB2cRc)G7kCia@O*P^$>kDgw2NK&>KBtEdFgIs&zhK&>NC>*&AuHFYV%0kn=lts_wD z2-G?P50qaHY8CO;SE~rrDgw2NK&>KBs|eI80=0@jts+pX2&9VK+hDCD3|dE^))A<6 z1Zow5T1B8%5jcW*wH2UN5vWxJ4wDlGhZ+ts9BeqqaG>7*BphId{)YVw`x^E!>}^|F zzb1a4cqj4WQsO&_R}#-Ao?s93)rmG15Z|1bm$)u*W#Xd5If-#B9X>SiwZ!Pe&_tg^ zF_DU0%d+85;(v_47yoJe2k~#jUtq!T`uKh99Cv5DIldr%W9%7L311qY7(XL^QtZO` zk?{lKd$5DtfOt(jh(}qp{Fm78VsFQO6n>JN#Lg6Zy~oBBKVVym7g$KVKD;lyJ9T|{ zCwo#X2ybKo@ujJKS>?PaoEViD)Z}HkV{?b(_Q~ynib{E7_|s)M)K zeB$e@2i_R03+@e81xu6Pj6KeFf(^k9!HnRN;CwcoI59XP*q;^M9l@Ajc+fxSnQRQQ zK_pxHd-l)lE%*!eo%mMre%Xzl{RJP)cCqV3Yj$DwrtCG@D^ls~WcHjGn>{9baB6&Z z@9e1T;ABhkOg5d!XA_w(GJnhbA@eSuLw_&jCpToaXSQaZ%B;=YlUbSkklh-N;w$J| zGxIaEQio-xXQr^}#A$r}e0XL*VlwY`R`TRbJ-bfy$Yl8R`LpEg^q6z)v(-$O{#l9Vznx2q8C3SrA>h#g+gVKAYcS&wa?it(0 z#uK&aFgADjqQj$uO2aZY{-69ed4DpOdW3x~#`DXsPR2^9_S7Bhj97~NHZnGHPGnr< zxJYD_m-3$SgCvBle*C~~VG40um@f5$s>o!oz^WOyBIN-;?N-m+?*|K=={7(6PhFC- z%+G$VWVN6DhY~jJY*O-|AFL@wS~=K*e#WiZWp_@O{}w;ntcf7kRLF6WE)w73XU1VgZ4xz+H{mh8i*_8OGpLYAauJN;vXqqlRd%qGkt?j6g zX(ZNMb1YZJt;)LJ&)ycB>Lp8a{Nys%99H@Tw^s25zu*?0t?&zPYC$jig&*263g1_9 zhhO-vl6(BZYZccwm2CA3ZdugBe!(q8YxfK8D!$|wp4K!R;!!0f7Dl>e`TuIE__`uQ&$;j!#3GyNx5QEIL>zp$Y~+_HKsqs+2nf#rX&zA|QM zO5yK}g>JoCn;*Jn)yAIL=E@B-5`3bBdYNV<@0P2{KILjz?&sWMy}SH^Yn70N>VlkY zB>aJqT*+;JyUZ`Rg`yC5YRmlmiJAzKH4;uXV*Bmh=@$-C%bh$K83{Wp#4Vd!>K9h2 zWvQQcM_uZN=a?ldy1QbjpZk`Xa;`Nk@e6MK9b}-omiYN2E2O89+{YF2S0#7w$Y&(y z&WslKkXjz_3lA!J+Alny)nuY_|v zxX(B8?RnqC_of2&KLwDn{fAkf`3j8@<8aNX#11hAyk zmmO+>xO@63-ocwCblKT%-Zjr1`_4b<=Ukd6{O}k}ykmED+PiB6kMlOv>CfYS?q`|^ z;!ajJO;^ife!jyX);02QU zAfA!XozMn9e4=96(@4%;*bRQpHG&rKi5w!kNA1_TOaX5r+N?j`A5_RnO7lmE5zM65Bs5O z9}oMvU*JN2U~c#ZInGksuJI4~;Ucv>#JYzHaqR=ku@#G}4%v)eEf4zn$Bcx#DtUl6 za1}D8LiBQ$JlT+5P51NWq(a6ixsMlyM#3IOa)Tt{oH*OGQmjT z(%j?czo8aLSxE`zZiajO&@~yzwU#J!)q%xHWqYMRLJNG>1!lcYb5wU z$!b4*&PeVaM@oL+noKuuQ`FS$=k_%c{8mX9vw%jziX}Y1VmZJ_E;JH+sDv)VU3^H| zEa5~WxigHEf?q1@@bgh4;j0yLh>@H->kdEX8rt1{em7#Y`zKa)d*R37AHsLSABEo{ z7~0d}+Hf@==eDxb<81bMoD!ZJjth?o4+{6-W8D5>b(qY3k^5`zce$VEUeA3!_gt>@ zc<#YmXKq=pIoFVz#fFX-<|gD$${n8DH@9nU5TDoP*v9d*;KSfoge&__ur1gUJQ~~| z+|AyNxAGzFb-~o&g5XRZo(~W94Mr1*tXB~5(EMrkgY3K6H?!a7d3iJ6$ljM-MR>8B zd0L*Cy)=7Xc6|1@>>)fSk1S;eWP7qNqsJri`+O4nqs+H5FJ+$Q;dl+-!7k1$BsAEJ z%*6x*J2i7m=D^Gto`w5n2)va3Cqck|%X9D#*m-d)`z}7jQ}CSx{^A?!^wjiZo_$YF zADP}Sy<2){T7suXQ=g?iOufezi{DGVntGNmW*a%to|zPUdxa!6#a$jHcmNY6-y4Fo=^ z`a{(_Rc};%v+DV(C#xQ&ZF51|h-OYKpw=_{Mt?46?J%@V@ zn0q_A466G%4`5bQ|a4 zahPu7Jp4cy+c=Z%A{e_k!%m?X-NbpggxOxhCeCcE?%j`{$X$NPt0$=Xnkq?Je<4hF zajw4+=Be%2czLx(Nv%IUQC>aI<$e((n>dqYAdEenVZ(9HaqefKvmITJpDmp6bC}ia z;0)UnHcQ!=@lts;8#rUUj6Ax3bG`Q@e)ez1SOvRA*#z7(U6C(9uXc1DevJoTKNLnh zc=BXmx^;7X9SL>o=6W`@F0axfJjc3D~xo0>;7Ewb}OuUkxKVCu|5Q&yh@ny#-L+vU#vn=4!TV z7VCphjn6sQTSPAQY$g{$ZCA5pGi(Hm9h+e_Bs|UKc^EoA%5FO3UrmB>nn0@aRApDe z#ww%9t!BSwvL8qu-L84C$1Sj$?V2&tbXT)mGwf8{Y}Q;wiQG4Z_xsN2J?$tcldm1ZyHDf&h#!k(!QMiwAWj+nnjhY90jnKoKbqPri)ucDT zhzL(6`RTe%^WgJIsM|CT_U^=Zpt`fL1C(7a?T=lW$#XbH_GpF?Wp?$xu5f8z?9uFd zg9gKNi{?R>!q}o2+ZYn=B?$+48!_&wMigQ79?lITwlN9MfbFhqf3oQA%!4HG?CR0F zFTEB{aAQ&)fPa)LvJL+(Y8(q2DQv(j+#|$2z-z!bT#YEt>Uw3;h=(adc~%b<*8fbh z43RATyvHyO7GuBbVS|*F#6Qsamp}(NdOHbJIQz-(QQ_>DgCU$`p8`WRVS~w{s@czb z2qUVAaUMn$&3@VrR5bf}_h3adxUQF z6tCPv=qAO>EvTy5w-;_yv&!r1J%dqYv+r3jWYhQh9t=~}>?;iy)s%z_F{*6#l}45< zX-c`jRX6+Y0gJ0`E_qZp`+CbTqMHw7hBR8xxV!l<&@Hv&`H>^mhy zjcWQ{ofLGDw*R{Q@T+RpNtFL?R1^Oon2Khd{K83UI~z7p88z0eidi?C%;!nrb>6)g zRWj=krEVmXVtV3M#jNu>p{kg5UK4&4)AvfXbhF(kW|dd#-G)^qvlgl9R>`bA1Gh?M zt+x~-lIeT3(_ku?wNj4=rsRGFqbg=C649-SS)0PGidm~Cr;1t2{oJjJS-VSu>#veo zEB8mYN@lIMjRY#0wc}wbnYH`FR5EL?A)zW}t#>;V#gwD3g(8_kB|DnQc}{>KnXn8D z#e|`~-G?e0PnHsjDS2ApNTyKk!|sDL!KpBn%vuH(-71;2DVQo|?M#>|W-Yf@w<>0> zcPkXdlybJ>SIMm9mh4u^tnCd$GC7SNBt$Xke`=+R=vK|FnF>?QtU=4VRWobozPiy& z->aEGp3zcxjmK!L8_5*zce`{Q;dtCXr|DY@gth}W|nG3K~*$s z>R~FHHFEzTnq-ltjb=(YbWhzXnl;ii(M;UvdG`S4-ULN5CFNuIRWobo&b#|+N_4gx z%@lVtR7JDKYnO(qs#!A|hH6sibQq!un?p*~Ofq3q&8!)XQ6;md_b5h{%${`I-AJbI z^}H5_X2PV>R5F#RWcIua8 zCe=tM*^Od~@i~ktnLR}jkWBK>FLa}r;=Tu?N@mZiW94onGv&!WiC~Ji)Cxy3g>!+s z&`h!)nJ9OuWcKtJr*t8i;uk8K3B7})NG3_yzNHJr6ytpuRWf@HfT?8mq;|WIOcGuM zQ^o8_?RKeR_B>r%<=8KoQ~Q_5lZ+J$CTc|E*4p@^o?Rpe6D>@f)=qA5m3kX@>p zJ?b%{npIx4T#zo6&FTb3WK)a}K+#R1&)`?#tUe~j@lno{$H1@)-K=CsIDN1BV2rAp zWG9d6W_3^4Me3$5yO2#14kRI}DY>7*sIpmI1yk9qrhdCrHmh%dsccqLfnCTZ_g}U5 zC>-4sigt9Va8`4(b*XSxOSN|)oE)exOm(w*B5sw#H*&8L z{(NI@Euqhs=Wfe25{_$n?jlxKpOQP009?D}hUV&cKM*CT`JaOKm}LK6@M`dE@Hip2 zI(B+LFgchIoE#h(>_=R!Awg}B3!>T2vVY9}D*Kb{cT3rA`hwtI;*Z~+U6h@Zy()VN zF9^nEkIf!T*sKxR{@LnmGV@PjivK3_)6DlWuM#QivCRFMyE98Ow-6=kT7rgOkU1lB zA`|BOW_HaC%Jj+vnMnH6^ats8({HA~lirrz!Yujy>ATZQ)3>He>1&xGpO`)^eSG?m zbZKt_eGf?YNT*W&N_|9d@3&JwOno!;Lh31Isk>9lQ@0Vu`-ar?)J3VY?d`zssbTB} zoKMAgJMbrF@83#(FZn8O2Oec^dsT7?VczB@uSs5>oRmC0c|!8A#D>NC#PYFHJo=C5pQFEyz7_o* zF9)_nH|+Fs;F{>=(Mi!Wq9;ZVBeLEo-VM}5v(c)^Cy@^#zl{7i@@*oHZ;q^w+#6XL zxjnKda#LhxO$ zmp-4CGPGmu->c;zv5Wp)Zi9zu$lAdd2gB&#eXp2+Y5!iNFYeO*y+~);MgNX>9GU6f ztC;QO$*+t4UFb5X_U}b`#_OVgCn>tzrQLh+P|+Xl--}6%+P@bWad*+blMof{((b+J zJp$G4z345$uiblb514lE#So_5docsk?p@E4-koyx#YX22yOv_;+vU_mC=OKpFVcB* zY42VX;iY#MKO@X8?cR%L!|2}0UJavnhoxcKxff3+3!S@Up^xg)-o40kOc%Yo7)80X zb1(9Y-ld&;ac`J*?#0q5QqsFi%Db`By;FKEM(y2;GHC0fcNf3t4Bb0^sQ`L+*p)Et z+>7*kUD~-9C&9FH*W+m4t`|uA_Tpr`+P4=Oc6ODtb1%v!EM3~U7o|baxyu2&pxV0^ zWpvi1y?asRU3+(}b9#44+KyklcP9HOite40qxwMbzot#khs{4_)-{ zB;{$Ni|$>lQor=?7$qs)yBMFvsQr6!2u%C;Vjr0H@5OmA?ca+$dv(#jYZhqW=*v(# zc(F>0q=y&ESgor>A1_=kvv%@DdD+yZoqTaGjM~Yw>H|tAFG*KH>E(q=OV@6`=yi}w zyZPc&7~MSQFC7&9JnVQ9YA;`uF0)g6`QjM~n0E8p8awIcNqQK5dU@C-`03>3Ks1d` zdU=f8g`L{T7yHAslP{J?NGC4|U&W}seDP?Q_VUFsFzw`}`FCn3U-Y&?>E!AE^x}5X z%ge#B(#eZ)F}djFF|yZSC!M?)d2Z;Wm&Z6Brk#B8JQ$rkwhLhN@i3kaJL%#j3)iTV zE?#ICemZ&lT#rur_$sedl($En^zvA*f@vpD%<*z3oqQFGQJ%-By?lYUH=Wwc7rZ7Y zy}bBu$FJQy5%|iTbn{}o8%jSfbOV%*Ug&L5?dJ>fB-$zce90>mv1&)}tlH5R!6lt4@{A!k#dd&%zkosHdDH&977CvoIH? z@>xJ{I+0Jx*`I`}o&}M;PF2rB9(K64#KJWE$fp#v9_yi+iCd`?{S@PBC;}=}ngt3< zq1<$x2PzwZ8~qeF8r6w_;znmWRX_DK(NA$py&#}MH{;(&^|!DuR+Ug{3#&?~{!Ap4 zlEjUIN~Z0&RYKWQqTGpuiji@1ClX4^b1K~(ZW$M6;a|ZmOE8L3({$Js)iP-U}z|ZWUSmd zz~#OZBNFO+`4mPJw93nSC23u%p?Pm3R5dg&uK_w$L-SX_&`@$oLqkF(*CLE)D8|b$ zqM%}wdqO2NulF1hDprKMQ#CZNBBC0aKc4)mq4`n`R7EsTr_!k+nm+@Eh*G@#PBavj zg(0D^ypDRs_Gefk3msSvEB?tK6(EYdW+!*sF>*{J5)hKu44xZ$`LNa ztpXZ)jTluxL*%yu0mXeeZsb#P({pvGeul_X2l|OyehKm^ZrWFe>Sri#^g7T_-v5Lb zLMQD=s`?p9ci5r&8H#9kpr5{%<0-2{Oem6p9NF(%V!Ril3TWp(w!FQ|s8dA2KFaaa_DMsX? z0|6x^veu#c85{vSTy5MD9mpr%i(rQ;<8JLx^$bLtJ5W#D=t75zr=FRJXUPkgpz2Wd z3}nF7fqGVXS?QoUR6euwV92NMWyj)H^~`!(q5HU$-S|;Y{OE88;wjl*f});6k<LLHdxyW!|Bwhm(o;Ph(@E-MOQJ?%YpfFK0{1rT*p7 zs?0~;8iE-Y@DsTB)UqluaS2ctUnP3q1;bPwl`|)2y z8j6oLq#BaJ0PMSqeGvAEe~HZQ;cX0OMjrH`28&dy+x!h-H z{7f$;XVlP;Y4C-N)t@qqe6M~MnOhv&kZ0YS6MKAd74LusFcKVaUz4W)p!fJ0Z6%!5WD%VsB==wY| zznG%kF4?I~Vy8{c9~KQ_k9xUSA)CC7r56VTYlHsb)Xb;Zn_~r5YWyuTHT{d!*B&1< z@QP?5_Jp^bnDACx(%PR5qB9E(eHVnm{v1#*rI-grqpij5iOJrr+T?cp@bfd#8ww5K z0@o6qndF$~S9`ZK#01;W)OZqYG-EO%3qkLBEFdg1ojjtzz-cPeyMlb~W@^ zPzd(nq(;g08ixH(ndqY8gALgQ2~F=x-Ux`Jyw689C>;?I-7T#IO;UVjzj+=9-UJ}7Zz0Slz~G_ zh9Y<1g5pl;v1|Hq969*%r^y9u2%~e04Gq-`a$3a)%TJS=e2;W=VeD!yMP~Vlx#+Fc z=Uv)RT#(+WPDn$Yl>K1&k-6xKDsRd1pXH(pmfy|G#yrl@;Y`^s659j5h5@vSw{8+z}TT<<02K53|K9HsY*6HfB|v3GP~&DQj9 zyl6wLalfDsYB5Oi%7uNoHae^L6vx_CZppDbWZ~+bRP})!9BeN=2|bux@n`!+Z|HH! z%${V~eW#y;ho5s(kA738XFI&xclt%XC%>qA>WK~6#y!-oH?mxp;eDg?s-v$q6dUCg zu(fkJ$b{a}a&cE$|@qTXqTXwn@l+Lxdz8K?&?W2miqjmqZU-Yu&Cl84B z7`7pZ{RwTD>?cRWzerz_SekqyHaW8(^^BiV_Z}g4(z-;mH(zf6tz+r_zBw{FqtK8p$9FPwYM5^-RyU*?=Y4r)<$^r1 zYjjTUiK!#0`r*}uhCG_GqQtL_ zj?V2fA^j13plCZMlF}=yH?;J1)9N{qAq~kgl1c)1jj(^T8wF0|+R?3XAk`Qt@L#)- zzL4$fwd#%QE&z790L6GiO*x`9zrVCcskw`HiQZDYHW=>B+bKQQh4j-34XJWW54)q7 z=Hci|5YF_L?R1(X=QQWmK8jt0GFwi5sV36AhU^-hSKPm$SWZaoxE~?*b9alDtEZQ_ zEa_kfH?aO{e#NryRh_;3&E2Czi^tGvT6a1WndFM3E=Xs$`=>Ny8@$9~T_qHr*6_)c)RJxZZG`;abB-3?DXp$nZhK2f&gPc)v5m z%PVw%FMn}5&lx^z_>AEe!_9`944*dqPyL+I|K{Jb)6Xf~CnrSt_X-a8-FArJ8vA=S zD4Te>{Sm<~b95SZ7~XBT%5bG&yJ4H*3d7}wcNs1-ywh;0;S&9{ngicqhT9Dn8@3wW zX4qobY}jOYtKlt%HybW8Tqs!Y7s`f>h70u5Y8(w_m~U7zoM$-K@Fv4KhO-TCG`zuZ zmf`h=*BM@Gc#YxA%7Lyn!&Qbe45u4TGrZDps^Jxa!)(XMp|)crc&XUIOAMz74)F^Y z8(w5sy3ojE!wU>28BR1jUvRKrIM480!*dMJHayF4g5jBlXBeJtc$&jfK_d=O@Hlgv zYB<*L6vLAZPcl5w@C3u-4UaQC*68oYuLxIw_%-Otzj?28pEE3Jq)Y$ z{^!6NaR4lsBQMz3_WlKP_IF^IHOv^M4O50m!-QenFlML$2Pik9_dle{9KNCF(Cxd^ zhc71Q2!ygOQ z+D6#m8}|1P4PSRy%KyL&-#7f8;dc$cWB6^uZyCO3_)Wua7=GPwyWy*buNZDKd|8oi z=w33zi-s>4K5w|y@HxY04WBXGVz}9Gli|~b8x5Z_eA4gbHu7)}=?^wKoLD-EX_USW8-;bn%G8eU>J#jtd-k&6s3G@NXBf#D>>iH7GJo@aQj z;W>t98=hr2!SGDOGaQ!kr<>t4!|{UuAA&@kX!%YsJYH}Q+eLhLzwitB8~nN9&jfd) z|Np6w(Y8$y_!Im4O~W4>{>bnR!Ci@JV5myQ-#@Uwzi+5QhFygWR3QUZ$Uqgc5=4~@ z{HA66hT+!@Rmt$Hl7XsZpeh-tN(QQsjq(dBWV`qU6*3-VRLH=sM7i-^OTO-fpAb~W z#grL9P?_Y1F04!{9o}6DE0g@S1eOUWF8|XDk=hed=ce{ee#)nBZ|8oL`*vGqW<&lRx`1#!KpYiSkkj@RF*Q5HAwoB{lwk4DqsafENkv;=;Nt zpLck4L@D_?VSfG#|78|p#F%)wtP?RK=D?VU!B$9=%gn>DjAMi>%qi;Bh2uJm8}>&j&&nfUFjIVdy7Bp0cX^VUhG`f;#2rjhU$pgPM` zKNhC5O!Z7_m6@Q&+Xtp|O!dsAmziVYW2+FRQ%u9^VN5Y$V>Yp@Gfcyd;&{w3Re8g_ z6&Q7jX&5WM$~wg~Y(JPzF%7$vX|ytPOyoWRrc+GA(&SFN+zrr_qb#Q>Cp9J0b>)Px zp{z$J$HhHVmVcCEYLpX?Dw~Kq;@r#|m#f4$n7PEVFGfCgVi{N2Q!8tq8ueNXW=6Gf zp7;mJq%J`fT3tpmXcYcyH05siXF5NtwHmK>^l|)Gsh_0>jWd+(j(fUuKLefiWu_|~WzN5G zswQQ=u<;6Kc0ot<=WJ%7(&DQg#)YPIL)3V?05P7r>5J)*ttA&MiOqST(Zhpz#=IycOfo zYGmTK@hE4!8{?7M<_2B{Jwp9eu)~#|pxc~Ic)YN0?v6-XF^2Yi+pOF`uCtz^BQbRVTZ z>&xn6T$_D9QWiqul#=KDWR;Rle&U}>Hu#Cpl|1ez{;p)ZpZHA4Hb3!k#qyDo2mFM5 z?oJqkSN+5XYI?~}{9ehke!|6&!1bpajXZ*xdnus%cSjo$N;sGTb{(dFocaaJBL2YVz z$WN?L@}QqsqGY=tY*F%@AFNgKuy3)rBwm=-34_<;*3up?lXM9fkb1=cIs>IW|P!FE6EYGHfH&;CYz z+x?6?2IMTWq+NiR?S9$?@OahFx`RPnWP(@y%!`^Ba+?u?$Vo$a)lXfbrH z>MMT61txgKFJ%hmOIKppq+F1ZSN!CU%$0PhxA~cO)UwS_x%LHFS+Oio^0J?GAuJ#r z70b01QZzzDJ2}Od{Zwgdg}Iu>G|wz-$u5ar^0Q4wGA{JQOMcp=dC|{ysta;%@G(e#V8T zfH-Z~>ZjT@&2xUnwV3Dp^f%3tKHh~Ye)9#*zxC(|0 zu{0?cIAe>Sx}jn@S;=NU<9^R(KfTQ?X(s?!^XH^ozadZ<}aw^xX}Z|NX@Z59Gf$k{*XXP>PQSFKO_naOG?J?W?a zWxn*@N}ljDzo-yb!C015EHjOypRSP8D`c#Zl+)8E{G_`%ANRBFdOYr@T?Ik!alSS?Eq2+)#HrDqRm)F(C$fZi!$bX~Cy)O_{lvcvzcBo#;pc|`F#NmWXNI2| zexjun++WGOVQ82$3=FeiNe-WJ23{HHKjtnP{%4BYskCI;@}~@wh6%&CV2Q)W%n&t< z7*-kjhX32Hk}EIqF(|)3@i#MkBsjz;7O=y=82;JtPlg{F{?YIQ!#^0lZ}@w|-x>ba z@Hd9P)|a9j@I5p9N^mg4K*L`ezGL{dp}ycEoxb1#^#vEG@3+9WB;6pNFX$ZV`z`*~ z_gkR8-vZyzm!bpxgudVc^#vEGFStN`!3F9IE>K@^f%<|A)E8WP`oVK@An;kiJ^bJq z!!3fl`@v?zO@h1m!PABtOGcg&9PI~B8a`q8xZz`hyZXVSh8qmm8?F-^#aPtv5yOWK zA2NJUa2G##z_4_`k^2nqHN3}gjp1s;Zo@9aPQwnvy9G!3!79U*hV6!Jf+PH3g~L*? z+zfXaE;GE-aH(LuA1pDv!|-;)#fGhhw;8q=HXAk>-fDP@B7wbbHp3#rg@$FrM#BY$ z4Tkd#wF)`GdG_~Q!C?&R4d)onHoVdB20`xsV3rW@dc*4suQj~JaHiqahF2NRFr02U z&G1UYse(iN;0nXb4KD*r;<(fdml#elyx8y}!wU^38(ttdn8y{viH7GJo@aQj;W>t9 z3l8#wv-JLlOfbWlhG!U_Zg`sEc)@{wFwXE)!2y0S*6N@NmP!3=cIt#PDFlgA5NeJiu^&!~L}JaDaWy@HNAI4EHwN z%WzM_Jq*Vf?ryl7;b_BM4M!R7VmQ*UG(t!}KNxOUZ#c|wsNoR9!G?nj2O17A>~Gl5 zu&-es!`^~@{h&^;k6#LE9SM3F))@8_?Cl3V466-`h6TgCVQ82$3=Fe|8N;+;N|F0N zX@-Pgogc&vV}?<~h+&nXZ|FJ9{>$(S!+#onZuk$wzZ-rgSgP~0pPJzl!;cOBX84ie zUk(3a_-DgE8GdN^N5c;c|6usO;qMK92bTE7+25MsH-^79e9!P#hVL5w((oO_w+(+` z_;bUb8UEDpEyJG}zNz;=C-7r4{K)VP!CF82L&Mh%e_;51!|xe>*YG=r-!}Y~;cJH9 zH2j9)*Y*DAK-WVwj~G5|_>f`gK_R{T>;s1P3)cAA`wZ_jyvJ~j;cCNf z!!E;4!w$o{4ObbiG;DWR%C?zdh2e6;y9}2Z-f6hhaEV|~KYNGa?S_jDTMchBY%y#$ zY*OTn*sVf(_}N9q~%w(??q$(SYxw|Y;lyA-LstIJDqZKLydjsdoZNe&A-(WiU9+yomu%NfWleG4hT4T^ z6LQqPh}oSCk4)ps0Po?3M8zKuQ>+)hvAgh-T zzC3zeaY)dM?>1QN>{iE1iEmDk?>2G^FW$*aa?Cf?TyxEoD|cx~Exh2%t0pV1Pq~7X z%B9?XT(mFG0Xye#WAXBa!ou@{KAfBWm_4xn?22e(uTx*ieVX=Pe)VQ`j)g4v%?FhhghK|Y9Q|H1Yuzs~+N`~B?p>~q;CvJYiDvv+2j zvh%aoXRpj&$b|YS*(0<2XLrvI&GycQ*;wWunLlTKy(1RPNVg29}g zJ|TTr`fKS?Y>rZs&ZPa+C#ero@21{NeJ8aowI#J7bzf=~8tWpS&`8A=5r5B@ZW5 z?5>20tz~bXN-&tWEEvqAiTe|GCzd8|O_UPXCay?akT@f8LgFwsBep1ZQ|#*4rLps4<73Ch4vFm@ z+a)$2)-#rg`O#0Je~A7v`lIM;(HEi{qidsUqHWREXgNANdR25v^xWvU=rPfQqI*P# zNBc&L(PZR{$VZXiM}86cVdNW;t&t}q4@J64k!6vlNGWn%WNKt`WJ2Vm$PtnKBD+O~ zM(QHDNTll1st>B(t@?4*x2s;R+FZ4v>fXu=nDRJnDwpk*Tbb|{n3Lhphy2`#h}$}F_d4yaB; z*UPuxE18C7E_@J7XQAtR!gLn8o|(&)%tGVkQ_+=7LQ8JuMOW%9bUo9nE18AHeF{-y zR#qt;f{|HhjLc@PWDZ*LJfA4HGa)GSR;bQG*UQ(O?K%rxPsEmXW}#(Tor%wOrlFZP zpNf%LXfYD)pq*)Gp>6n?hsHk{Bh%0_N8ZZu+jSy(C^Nn7Ohhw>&PT`X*D7NcvR$X4 zhcY?b&NMV$qP(>0Ec8(Mw7*?vp@$MWpq*K0DY6+O)6k^shf!ythsxKY?aV@BWG=p4 zC!vQDlBE3#rn@C->JqFv3q6Eu)6Oik?+xM0+jgCV9wLWi5?T^2#;CKRF zIte}CQrtQTJwT#7wCg1FfJrc&gdT7{Zk>c4APX1TbrO02U(vVgB=i7@MbKW-S?B?@ z*mh>2IVAA}+K-e&_LrvLeuSga@;4|7I2&qF0s|C9K^hsY1^kLh4FG1cCF zkTPNsv>&MKVDcW|^1gs^e~cxs|2bIqb5{A0`)X1?b8i2dvXQWTl-&s1TN&SCx9_E_ z7i>>uSHt$$;T@xF0=C_iO@ZyEOj_7zWyiuwyDF7mJ<6Q}ADOrBqQ;r9k;?dvyM2Un zOH&;##(pdTXs;JzKZ%UcKFrZZ{6p2x0)+M<%J?q6eXw(Hg${CbHU5D*-QRBjbbuxu zjlaM1%bnCujpxGpDmxw4$0daIRwfl*C#-KARx7N}VHDF#is_@bOpO|^!Kk9?j4GNX z2?N@$s@Yqvs;Xvh^s`-6v-jELQPu3Nx4Wul@2R*EP2cM+w*;CAJC%egnY|_SLpzd* z@jQ&Gn7zIEP!zL@$e|BFkxZdal}M|i+51$Oie}y3FhrA(pDg@nM>8cN=oToVS)qug z(4{0*)znO=CYksdZF*VjkdQVs6B|n}+K^1iLg<_})yz7Hpj2u@G{uZmwyA2?$@#aT znq=eJw5e#;A%Ja&Cbk4jC9^IHQ^~Bmfy_u|73FM)qM1Tz&21{0b%=W#qA4c=Q_ZX! z51XOOed-93)rMwP+1aUR)_F^?BAQ|)cwrl=DHJ(tyTVbru{KrBy0c-bnsuk+Ml~hn zc8tg-2OSJk)vPPPR5j&#wV|3U_o+J$skgp)+f+2`#=sCw7uU26 z)r1kjt4&3-R^m0asc6=+BBo6>vz8S^ZD=M5Y1(Z_ri36x3ER+2j2FXHGHZ8-sbtpD zp4(J1YrUtTC(uwN6H?fwqFGCC(uQb~U9OO7W^FH0s%F;e`6DW>!?R+Y`#)373& zVr{^vx>>syOm(xi7N)wXmjl_9gljOOo1BHTVPsQ`i!iEg)=DcvH+`>{TtZ~C%IhTq zwl;K=ps2^N+^!AXgdU1fWwYi)n9632+$+c?-u+>yCTuippfWnLwgFONjl^(iQ_<8A zv27}vHP@3;MYARXQ_-wB0H&f@GYlr8S@LQSp0-*o@?y$S)vS>);BBg!H3KlJYHEDi zHdW0Uw7pG5v*r?*ie`mRW*AY1yj}RaSjaC^t~Q(?;@Ho zE_EB43A+HMlG!7Kl~gl(a1B;)E8zYbOf|CyYdcqo0_-_z@HP>Z@3TE|5xDiZoZ^fvJSv>-yC}s)k%W#!U0vJG%Oi8;Qie?J! zCYy?8HSK!E3@yKeTQ##<+S>{=lYzG%lU{>d7|0_^T{4tCwn0me{n0gjxsh6l`D-cXM-aRB#!K{{=Qo*dg z6sCe%%{{&X!K6TLy%nmL5@vRV>Sc8wia{@>n8i>L%yIC0NuY{Z&Aqom6;lgW#Vnpl zN>xna1=_<`1eDL3kK@0icj70&4_?0Om}p&TTKb~&*(@wSGQEF#_w=xIpL9MQPko;H zbL!XZoATY%E2(EvkEZU|N8y`OH>IvlU6ML)$D{BFdldd6`4Nx8ZztbKzLtC;xiPsm zxhC0`T%25(oWqu%mn6?ij!PbsJTSROvOd`-NhGzzKbe{SUE=2@rfI*Ccs}t|;*msm zVtL}WL}TK{#0&zXoXFlv#&sQ--rCH~v^&*QHX7UViRMh#Y)G=4vp;-8x!tgqd=~vMTG=;cOZ3s`{n1s?B~jTo~VTjw7eNpvM)$gm`uKJ;Dn^Mw!Q@X0X&%H#sg;pX# z3)|!67HuZQ<1o@p#KVRbR1N>Rf!4MqTOmC4%2S6FcU_%N|s?9PkU7jf!CtFYJqm!La&ND zhnr4S0@p2tYOh+L;r>7WxYB>=ulryBID+y0|NM1~uv!>1$Ztj;TNpG5ML=5^HE>N% z#HfRYf^?`YI%p`MA}tIW2!+_lwx6;S$jz8RihK^rpg}0=-LjAR zc^+!n+ZhqKmc7)-Xuf4nXWWEw4>j^|-ZI7+88x)*uEsv7bIWed`T}&cqj!>ES4|)% zI!YOxP|GgL5T6zuHWcV@T6EY@K;v2%HgN8JDTYyloF?m}TXfVQ(H>hEHHdK;MjbX3 z5Z#uNjvM&MxZJ|HLHu;+Ejn;091UaOK$&$g#tkrf{uYJ}l8}K_i;f!#XTfybP>_?< zaYLaWOvVjf;b52!8w&KYEesp5A-^p;YA6*ZKzpi@uBfGlGP;_UYGsIOOVJksr zXTb8xj=~)}_mdcNYCIBS;Ec~=%(|2?)PiGQfIIEn&+}TLMMn*V%dzUHfo4QfMhzq# z0MkK3VL#jq8svAkVAN4VVFXM^4TVE-Ginfb8%7;A6zJJobkrbMvROwBg*h-CH58^K z%FS#BrFk~P88=9FT2ZqO9OR-mGjQNErotFEz@j8%*dPg4LK!y*r8zb;aKL{V>^fys zRP(jY%`-;xHEO&DHq#}ss@g&%l%Ba-l zsmi#V%~!aD++WR%8#s?+FfweAljBZo)?q_|UbUHFgBTIhX2uN!V=PGYy=F!Z1d_ZF z#=rrFTsAXqfYFYcb=V+rFPj-QNQBF$FzUb|&yclQ2M&3(wwZwgS>!Br+>oa^HtVoK zB3L%-up!SYie`olQcN?$ie?=+jR9b@b_WtTvRS)>@GLUZ9Y|otSE2L=8Vpjdul5Kb&l}BX zrX*MkrAH9T8_j0A1fdH^s(pf%MV}x>x}#<~1rEADO#6f|1=Btutbx%d;AMc+Syj{hJXOtb8ty)-3Zb+# zmCaCY0hP^g7xEyRQsjeBbW`X`{3@K`;V{+Ba0G62Q`}EN5l*2C@uQray@-MeXE+Nt z!YOV(eQHKI`HC=ZZhGZALe7OJ=X=S5XagA+-Ka1xLcO>aUsVK+$O z=%z&bq}OXw;S8v?CWI6JVffKaixAqR!Wp2zO(LA+SqD|!3?#f|lj>$b^KVk!3?$xm z6S^r$x8Yae3?^bkIK?<0T2ko@BwTJ2(kW&xL=)P{37ij8>16#|xk;rnkk^z=NGEQ3 z&L$PkpafIllnA9wDxAS7Fcr?=95SnL2J#xBX_989NjD*!)ITSJRi!hy1}oAj#uqTE zcCyy3+=Ou= z47CcX>M4O%n^Zkn%~ozgJtYrebDNM)p>&f?Cp#*w0sW+;s#v)R^-OsICd3mHvk6VA zoq;?YH>q|8GFR1vc1l6?olPp9K}ocu3H20nnG#e!1KvqBA)jJ=0*ZbLMQEE;K!fox z70`f}+f9dlnOy}m7>gSLE?y4|D- znmrw+3YtBhgsPxf`Hr>;1tlT9La7M_g+?(Vps@2`1C;fJseWer!_ZH>ydG;pKIMQ6 zWSjb^kIOyx6sCJgx`3tH8Ldcsh*Cd5-R--S`slVG@fm)fNA>54=?Nm_?r z)iXO6rs~NelyZ}*XV!ZNih4>(a_1tRLV2gz6liv7zlf(ASqtDD5uE1fwz0ot+t}A^ z+t@X18(X?9ToB$6P7f~*&k0Wrj}8wA$ArVeJ|Sb5+~>JJ=YE}gEBF1}cJ__^_ibaR zv2Cnu3v*O%f3}4g!nQDBE@s=p{3Q79j%{OC1xtdPgPVex!KK0Z!D-3;`G|37vX0sC zsBDc=(!Ei>lh~HnEPJCQRwb4sZb{73>F`NRho6`@oayjg6N3}I53 z;@^qC9N!$@5Wg?JDt^a~jZ@EykB=W0KRCXZZk*a9&PNKdk7Mu0-nNZXH^$b+*68La z<=Bm}8L=s`(z&s5OqU-N+cP#I)<4!GmX3MRkE8EL-_hyv7o$%zUA{WHg6ZsJk$*(~9QjS;XOSO7wnv_eJRW&4(z#={{EEmVHb*%ja_CN* zqg3tKIQ6ws)r(adtJbn>kNguaFV;rbpDuH;Hp2cFCd!Lxgfh){3r1~){kdxw(+Zi; zEmv zNN$>;URYqV-B;yW!ShOdsiZS{cPq{BAG? zF|gBcGlr1^wPMskOrH@j1~IsKBx=<$OrMe*Tn90Icn{R7gP1=1z;qDPhX>YH9mMpJ zQ@g;Gh|IQ5(sJlES|v?LE0?$>(Tb9;yBy(@+J6q3IV?T^%X|?p0 zSPQKa)H)J&rm(u3U}q>B4m(|$oY`s0=oVVXE92p!b(|DlEBAftsbA8uj&3IDDU!4{ z4?9`e9IkM*9*rRK{b3AW zV7*~FdXXunRvo?6${4d%M=!dUW2=r{YLCNsn4DWLuf+3E>!FTI(>g?o>UAVc$1lD5 zzz$MdHS9oTd9obfvM>~A-QUqwP#wSY(o^17lTIMh*OYO8weF*A3T$s>3|336dnrXn zTlaJYEycfw7-eRvb&Rqraqli}=CCmCrp8@hqm@Zp-&Gm!ep+?#QX})5tqfi$=L#5O z7ufMIhAyyyFh(v+E7sIPb>vdRP_$J?E;S5ATXp18^WRE3cNQsvC<^!Nva1`(PWP0F zC~B}X+T$o1iw2^s;`(M0#XwMm1x*wTWCF)XZmW@xNVth02nK?nh%fwIe&Kx z?o;2bu4=mPqnNo+?;3qGanV6&^myV@Rtb_b6nul+GnX$Y-|a^F-%c8Z9o3Gcp_=-O zWDWY9-XQ9%QM5hOIcpTbymQv@URvj@QJB*3I%2iDjdIfPu{&vKkFaynC^8gDgL+Ts z#yO+t`wN|OM(GoD&fsrw|J6BZ6g|Y!Icdx#Nkd1fV{+Cg+K%bS8gzS;AkX}LTLhA6Ghp#Uf9 zD(Z<uOp zY^u#`f^d_HtltILM6tvbP*cTE34%?67f?_W#jQ{nnq?=-(DZ)*nv|~47-q8mRrJhP zn40=7XFyHL_fZZs)j4PiQ?qQLOwDqMVxXyF=H&r3CCT++Z0Z69*i`TX(%i%X$XoO1 z-=$dq&J}*2AmCKhZ?rc#Ws!iK3Nld+(8<0kgDgzW0T_!-$GDpfxT(F^p9Brg!DE!c zIbca30H=!C5?iwC23nR5qjRuD{tej4HtBY%8l8==<%WZIscCXH6O_r>ETTlts$u+x z!P)3^0#5DozDJszjc&@r@kguz)~;9LVvZ+8aVWY3ZNnE;!p=}9EmL@uLD zP3HFg5Fo{2bKOmgFyjre(UsS zeQgE@(9{mvC(5BF`!D)}SU^qo&2&8t&Hh|iA+zGN?bjw0WBUbO!uADe->(UwiSK*W8sjx>`?dWjxqgvX Gv;7A*Y(*yk