Repository: KoenZomers/KeePassOneDriveSync Branch: master Commit: de8784c90c9c Files: 77 Total size: 725.1 KB Directory structure: gitextract_t4ju8rln/ ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── config.yml │ │ ├── feature_request.md │ │ └── question.md │ ├── PULL_REQUEST_TEMPLATE/ │ │ └── PULL_REQUEST_TEMPLATE.md │ └── auto_assign.yml ├── .gitignore ├── KeeOneDriveSync.plgx ├── KoenZomers.KeePass.OneDriveSync/ │ ├── Configuration.cs │ ├── Enums/ │ │ ├── CloudStorageType.cs │ │ └── OneDriveRefreshTokenStorage.cs │ ├── Forms/ │ │ ├── AboutForm.Designer.cs │ │ ├── AboutForm.cs │ │ ├── AboutForm.resx │ │ ├── OneDriveAboutForm.Designer.cs │ │ ├── OneDriveAboutForm.cs │ │ ├── OneDriveAboutForm.resx │ │ ├── OneDriveAskToStartSyncingDialog.Designer.cs │ │ ├── OneDriveAskToStartSyncingDialog.cs │ │ ├── OneDriveAskToStartSyncingDialog.resx │ │ ├── OneDriveAuthenticateForm.Designer.cs │ │ ├── OneDriveAuthenticateForm.cs │ │ ├── OneDriveAuthenticateForm.resx │ │ ├── OneDriveCloudTypeForm.Designer.cs │ │ ├── OneDriveCloudTypeForm.cs │ │ ├── OneDriveCloudTypeForm.resx │ │ ├── OneDriveConfigDetailsForm.Designer.cs │ │ ├── OneDriveConfigDetailsForm.cs │ │ ├── OneDriveConfigDetailsForm.resx │ │ ├── OneDriveConfigForm.Designer.cs │ │ ├── OneDriveConfigForm.cs │ │ ├── OneDriveConfigForm.resx │ │ ├── OneDriveFilePickerDialog.Designer.cs │ │ ├── OneDriveFilePickerDialog.cs │ │ ├── OneDriveFilePickerDialog.resx │ │ ├── OneDriveGraphDeviceLoginForm.Designer.cs │ │ ├── OneDriveGraphDeviceLoginForm.cs │ │ ├── OneDriveGraphDeviceLoginForm.resx │ │ ├── OneDriveGraphDeviceLoginForm1.Designer.cs │ │ ├── OneDriveRefreshTokenStorageDialog.Designer.cs │ │ ├── OneDriveRefreshTokenStorageDialog.cs │ │ ├── OneDriveRefreshTokenStorageDialog.resx │ │ ├── OneDriveRemoteLocationDialog.resx │ │ ├── OneDriveRequestInputDialog.Designer.cs │ │ ├── OneDriveRequestInputDialog.cs │ │ ├── OneDriveRequestInputDialog.resx │ │ ├── SharePointCredentialsForm.Designer.cs │ │ ├── SharePointCredentialsForm.cs │ │ ├── SharePointCredentialsForm.resx │ │ ├── SharePointDocumentLibraryPickerDialog.Designer.cs │ │ ├── SharePointDocumentLibraryPickerDialog.cs │ │ └── SharePointDocumentLibraryPickerDialog.resx │ ├── KeePass.exe.config │ ├── KeePassDatabase.cs │ ├── KoenZomers.KeePass.OneDriveSync.csproj │ ├── KoenZomersKeePassOneDriveSyncExt.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Providers/ │ │ ├── MicrosoftGraph/ │ │ │ ├── MicrosoftGraphDeviceCodeAuthCheckFailedResponse.cs │ │ │ ├── MicrosoftGraphDeviceCodeAuthCheckSucceededResponse.cs │ │ │ └── MicrosoftGraphDeviceCodeResponse.cs │ │ ├── OneDriveProvider.cs │ │ └── SharePointProvider.cs │ ├── Resources/ │ │ ├── MSGraph.pdn │ │ └── SharePoint.pdn │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── TokenHelper.cs │ ├── Utilities.cs │ ├── app.config │ └── packages.config ├── KoenZomers.KeePass.OneDriveSync.sln ├── LICENSE.md ├── NuGet.config ├── README.md ├── create-plgx.cmd └── version.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: If you believe something is not working well title: "[BUG] Brief description" labels: need to test assignees: KoenZomers --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Versions (please complete the following information):** - KeePass [e.g. 2.45]: - KeePass OneDriveSync Plugin [e.g. 2.1.1.2]: **Authentication method** - [ ] Microsoft Graph (any browser) - [ ] Microsoft Graph (built in browser) - [ ] OneDrive - [ ] OneDrive for Business - [ ] SharePoint 2013/2016/2019 **KeePass database synced with** - [ ] OneDrive for Business - [ ] OneDrive Personal ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Author url: koen@zomers.eu about: If you want to share feedback which you rather don't want to be public, share it via e-mail ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for the plugin title: "[FEATURE REQUEST] Brief description" labels: need to test assignees: KoenZomers --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/question.md ================================================ --- name: Question about: If you have a question regarding this project title: "[QUESTION] Brief description" labels: need to test assignees: KoenZomers --- **Describe your question** A clear and concise description of what your question is about. **Screenshots** If applicable, add screenshots to help explain your question. **Versions (please complete the following information):** - KeePass [e.g. 2.45]: - KeePass OneDriveSync Plugin [e.g. 2.1.1.2]: **Authentication method** - [ ] Microsoft Graph (any browser) - [ ] Microsoft Graph (built in browser) - [ ] OneDrive - [ ] OneDrive for Business - [ ] SharePoint 2013/2016/2019 **KeePass database synced with** - [ ] OneDrive for Business - [ ] OneDrive Personal ================================================ FILE: .github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md ================================================ ## Type ## - [ ] Bug Fix - [ ] New Feature - [ ] Sample ## Related Issues? ## Fixes #X, partially fixes #Y, mentioned in #Z, etc. ## What is in this Pull Request ? ## Please describe the changes you are proposing through this PR ================================================ FILE: .github/auto_assign.yml ================================================ # Set to true to add reviewers to pull requests addReviewers: true # Set to true to add assignees to pull requests addAssignees: false # A list of reviewers to be added to pull requests (GitHub user name) reviewers: - koenzomers # A number of reviewers added to the pull request # Set 0 to add all the reviewers (default: 0) numberOfReviewers: 0 # A list of assignees, overrides reviewers if set # assignees: # - assigneeA # A number of assignees to add to the pull request # Set to 0 to add all of the assignees. # Uses numberOfReviewers if unset. # numberOfAssignees: 2 # A list of keywords to be skipped the process that add reviewers if pull requests include it # skipKeywords: # - wip ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ x64/ build/ bld/ [Bb]in/ [Oo]bj/ # Roslyn cache directories *.ide/ # Visual Studio 2015/2017 cache/options directory .vs/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* #NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml ## TODO: Comment the next line if you want to checkin your ## web deploy settings but do note that will include unencrypted ## passwords #*.pubxml # NuGet Packages Directory packages/* ## TODO: If the tool you use requires repositories.config ## uncomment the next line #!packages/repositories.config # Enable "build/" folder in the NuGet Packages folder since # NuGet packages use it for MSBuild targets. # This line needs to be after the ignore of the build folder # (and the packages folder if the line above has been uncommented) !packages/build/ # Windows Azure Build Output csx/ *.build.csdef # Windows Store app package directory AppPackages/ KoenZomers.KeePass.OneDriveSync/Packages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # LightSwitch generated files GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml /.vs/config /.vs/KoenZomers.KeePass.OneDriveSync/v15/Server/sqlite3 /.vs/KoenZomers.KeePass.OneDriveSync/v15/sqlite3/db.lock /.vs/KoenZomers.KeePass.OneDriveSync/v15/sqlite3/storage.ide-wal /.vs/KoenZomers.KeePass.OneDriveSync/v15/sqlite3/storage.ide-shm ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Configuration.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Windows.Forms; using KoenZomersKeePassOneDriveSync; using Newtonsoft.Json; namespace KoenZomers.KeePass.OneDriveSync { /// /// Plugin configuration class. Contains functions to serialize/deserialize to/from JSON. /// [DataContract] public class Configuration : ICloneable { #region Constants /// /// Name under which to store these settings in the KeePass configuration store /// private const string ConfigurationKey = "KeeOneDrive"; #endregion #region Non serializable Properties /// /// Dictionary with configuration settings for all password databases. Key is the local database path, value is the configuration belonging to it. /// private static IDictionary PasswordDatabases = new Dictionary(); /// /// Boolean indicating if the syncing of this database is allowed /// public bool SyncingEnabled = true; /// /// The KeePass database to which these settings belong /// public KeePassLib.PwDatabase KeePassDatabase { get; set; } /// /// Boolean indicating if this database is currently synchronizing /// public bool IsCurrentlySyncing { get; set; } #endregion #region Serializable Properties /// /// Gets or sets refresh token that can be used to get an Access Token for OneDrive access. Will be set to ClientId;ClientSecret in the case of a SharePoint configuration. /// [DataMember] public string RefreshToken { get; set; } /// /// Gets or sets the location where the refresh token will be stored /// [DataMember] public Enums.OneDriveRefreshTokenStorage? RefreshTokenStorage { get; set; } /// /// Gets or sets the name of the OneDrive the KeePass database is synchronized with. Will be set to the SharePoint site title in the scenario of a SharePoint configuration. /// [DataMember] public string OneDriveName { get; set; } /// /// Gets or sets database file path on OneDrive relative to the user. Will be set to the site URL in the case of a SharePoint configuration. /// [DataMember] public string RemoteDatabasePath { get; set; } /// /// Gets or sets the unique identifier of the folder on OneDrive in which the file is stored. Will be set to the Document Library server relative URL in the case of a SharePoint configuration. /// [DataMember] public string RemoteFolderId { get; set; } /// /// Gets or sets the unique identifier of the drive on OneDrive in which the file is stored. If NULL then the drive of the current user will be used. /// [DataMember] public string RemoteDriveId { get; set; } /// /// Gets or sets the unique identifier of the file on OneDrive /// [DataMember] public string RemoteItemId { get; set; } /// /// Gets or sets the filename under which the database is stored on OneDrive /// [DataMember] public string RemoteFileName { get; set; } /// /// Gets or sets a boolean to indicate if the database should be synced with OneDrive /// [DataMember] public bool DoNotSync { get; set; } /// /// The SHA1 hash of the local KeePass database /// [DataMember] public string LocalFileHash { get; set; } /// /// The ETag of the KeePass database on OneDrive /// [DataMember] public string ETag { get; set; } /// /// Date and time at which the database last synced with OneDrive /// [DataMember] public DateTime? LastSyncedAt { get; set; } /// /// Date and time at which the database has last been compared with its equivallent on OneDrive /// [DataMember] public DateTime? LastCheckedAt { get; set; } /// /// Type of cloud storage used for storing the database /// [DataMember] public Enums.CloudStorageType? CloudStorageType { get; set; } #endregion #region Methods /// /// Gets the KeePass database configuration for KeePassOneDriveSync for the KeePass database of which the local path is provided /// /// Full path to where the KeePass database resides locally /// KeePassOneDriveSync settings for the provided database. If no configuration exists yet for the database, it will provide a new empty configuration set. public static Configuration GetPasswordDatabaseConfiguration(string localPasswordDatabasePath) { localPasswordDatabasePath = NormalizePath(localPasswordDatabasePath); if (!PasswordDatabases.ContainsKey(localPasswordDatabasePath)) { PasswordDatabases.Add(new KeyValuePair(localPasswordDatabasePath, new Configuration())); } return PasswordDatabases[localPasswordDatabasePath]; } /// /// Sets the KeePass database configuration for KeePassOneDriveSync for the KeePass database of which the local path is provided to the configuration that is provided /// /// Full path to where the KeePass database resides locally /// Configuration that should be set for the KeePass database. If configuration already existed for the same database, it will be overwritten. If it didn't exist yet, it will be added. public static void SetPasswordDatabaseConfiguration(string localPasswordDatabasePath, Configuration configuration) { localPasswordDatabasePath = NormalizePath(localPasswordDatabasePath); PasswordDatabases[localPasswordDatabasePath] = configuration; } /// /// Loads the configuration stored in KeePass /// public static void Load() { // Retrieve the stored configuration from KeePass var value = KoenZomersKeePassOneDriveSyncExt.Host.CustomConfig.GetString(ConfigurationKey, null); if (string.IsNullOrEmpty(value)) { return; } try { // Convert the retrieved JSON to a typed entity PasswordDatabases = JsonConvert.DeserializeObject>(value); } catch (JsonSerializationException) { MessageBox.Show("Unable to parse the plugin configuration for the KeePass OneDriveSync plugin. If this happens again, please let me know. Sorry for the inconvinience. Koen Zomers ", "KeePass OneDriveSync Plugin", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // Reset the configuration so at least it works again next time, all be it that all configuration will be lost PasswordDatabases = new Dictionary(); Save(); } // Get all database configurations which have their OneDrive Refresh Token stored in the Windows Credential Manager and retrieve them var windowsCredentialManagerDatabaseConfigs = PasswordDatabases.Where(pwdDb => pwdDb.Value.RefreshTokenStorage == Enums.OneDriveRefreshTokenStorage.WindowsCredentialManager); foreach (var windowsCredentialManagerDatabaseConfig in windowsCredentialManagerDatabaseConfigs) { windowsCredentialManagerDatabaseConfig.Value.RefreshToken = Utilities.GetRefreshTokenFromWindowsCredentialManager(windowsCredentialManagerDatabaseConfig.Key); } // Decrypt all database configurations which have their OneDrive Refresh Token stored encrypted in the config file var encryptedDatabaseConfigs = PasswordDatabases.Where(pwdDb => pwdDb.Value.RefreshTokenStorage == Enums.OneDriveRefreshTokenStorage.DiskEncrypted); foreach (var encryptedDatabaseConfig in encryptedDatabaseConfigs) { encryptedDatabaseConfig.Value.RefreshToken = string.IsNullOrWhiteSpace(encryptedDatabaseConfig.Value.RefreshToken) ? null : Utilities.Unprotect(encryptedDatabaseConfig.Value.RefreshToken); } } /// /// Saves the current configuration /// public static void Save() { // Create a new dictionary with the information to store in KeePass.config.xml var passwordDatabasesForStoring = new Dictionary(); // Loop through the entries to store foreach (var passwordDatabase in PasswordDatabases) { switch (passwordDatabase.Value.RefreshTokenStorage) { case Enums.OneDriveRefreshTokenStorage.Disk: case Enums.OneDriveRefreshTokenStorage.DiskEncrypted: // Enforce encryption of tokens previously stored in plain text passwordDatabase.Value.RefreshTokenStorage = Enums.OneDriveRefreshTokenStorage.DiskEncrypted; // Refresh token will be stored encrypted on disk, we create a copy of the configuration and encrypt the refresh token var diskConfiguration = (Configuration)passwordDatabase.Value.Clone(); diskConfiguration.RefreshToken = string.IsNullOrWhiteSpace(diskConfiguration.RefreshToken) ? null : Utilities.Protect(diskConfiguration.RefreshToken); passwordDatabasesForStoring.Add(passwordDatabase.Key, diskConfiguration); break; case Enums.OneDriveRefreshTokenStorage.KeePassDatabase: case Enums.OneDriveRefreshTokenStorage.WindowsCredentialManager: // Refresh token will not be stored on disk, we create a copy of the configuration and remove the refresh token from it so it will not be stored on disk var tempConfiguration = (Configuration) passwordDatabase.Value.Clone(); tempConfiguration.RefreshToken = null; passwordDatabasesForStoring.Add(passwordDatabase.Key, tempConfiguration); if (passwordDatabase.Value.RefreshTokenStorage == Enums.OneDriveRefreshTokenStorage.KeePassDatabase && passwordDatabase.Value.KeePassDatabase != null && !string.IsNullOrEmpty(passwordDatabase.Value.RefreshToken)) { Utilities.SaveRefreshTokenInKeePassDatabase(passwordDatabase.Value.KeePassDatabase, passwordDatabase.Value.RefreshToken); } if (passwordDatabase.Value.RefreshTokenStorage == Enums.OneDriveRefreshTokenStorage.WindowsCredentialManager) { Utilities.SaveRefreshTokenInWindowsCredentialManager(passwordDatabase.Key, passwordDatabase.Value.RefreshToken); } break; default: // Hit when user selected the do not sync database option. In that case just store the config on disk as it doesn't contain any tokens. passwordDatabasesForStoring.Add(passwordDatabase.Key, passwordDatabase.Value); break; } } // Serialize the configuration to JSON var json = JsonConvert.SerializeObject(passwordDatabasesForStoring); // Store the configuration in KeePass.config.xml KoenZomersKeePassOneDriveSyncExt.Host.CustomConfig.SetString(ConfigurationKey, json); } /// /// Returns a copy of the current instance /// public object Clone() { return MemberwiseClone(); } /// /// Deletes the complete configuration of the KeePass database on the provided local path /// /// Full local path to a KeePass database of which to delete the configuration public static void DeleteConfig(string localPasswordDatabasePath) { localPasswordDatabasePath = NormalizePath(localPasswordDatabasePath); // Verify if we have configuration available of a KeePass database stored on the provided full local path if (!PasswordDatabases.ContainsKey(localPasswordDatabasePath)) return; // Retrieve the configuration we have available about the KeePass database var config = PasswordDatabases[localPasswordDatabasePath]; // Take cleanup actions based on where the OneDrive Refresh Token is stored switch (config.RefreshTokenStorage) { case Enums.OneDriveRefreshTokenStorage.Disk: case Enums.OneDriveRefreshTokenStorage.DiskEncrypted: // No action required as it will be removed as part of removing the complete configuration break; case Enums.OneDriveRefreshTokenStorage.KeePassDatabase: // There's no way to remove it from the KeePass database without having the database open, so we'll have to leave it there break; case Enums.OneDriveRefreshTokenStorage.WindowsCredentialManager: Utilities.DeleteRefreshTokenFromWindowsCredentialManager(localPasswordDatabasePath); break; } // Remove the configuration we have available PasswordDatabases.Remove(localPasswordDatabasePath); // Initiate a save to write the results Save(); } /// /// Get the full PasswordDatabases object list, used by the configuration form /// /// internal static IDictionary GetAllConfigurations() { return PasswordDatabases; } /// /// Boolean to indicate if some process is still running that we need to wait for before we shut down KeePass /// internal static bool IsSomethingStillRunning { get { return PasswordDatabases.Any(db => db.Value.IsCurrentlySyncing); } } /// /// Normalize database path - if located under the KeePass folder (portable install) then use a relative path, otherwise use the original (absolute) /// /// Full path to a KeePass database /// If the path resides under the folder where KeePass runs from, it will return a relative path, otherwise the full path will be returned private static string NormalizePath(string localPasswordDatabasePath) { if (localPasswordDatabasePath.StartsWith(AppDomain.CurrentDomain.BaseDirectory)) { localPasswordDatabasePath = localPasswordDatabasePath.Remove(0, AppDomain.CurrentDomain.BaseDirectory.Length); } return localPasswordDatabasePath; } #endregion } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Enums/CloudStorageType.cs ================================================ namespace KoenZomers.KeePass.OneDriveSync.Enums { /// /// Defines the types of cloud storage that can be used /// public enum CloudStorageType : short { /// /// Consumer OneDrive /// OneDriveConsumer = 0, /// /// Microsoft Graph API /// MicrosoftGraph = 2, /// /// Microsoft SharePoint /// SharePoint = 3, /// /// Microsoft Graph API Device Login /// MicrosoftGraphDeviceLogin = 4, /// /// Do not sync /// None = 99 } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Enums/OneDriveRefreshTokenStorage.cs ================================================ namespace KoenZomers.KeePass.OneDriveSync.Enums { /// /// Defines the possible storage locations for the OneDrive Refresh Token /// public enum OneDriveRefreshTokenStorage : short { /// /// Saves the RefreshToken plain text in KeePass.config.xml located in %APPDATA%\KeePass /// Disk = 0, /// /// Saves the RefreshToken encrypted in the Windows Credential Manager /// WindowsCredentialManager = 1, /// /// Saves the RefreshToken in the encrypted KeePass database /// KeePassDatabase = 2, /// /// Saves the RefreshToken encrypted with DPAPI in KeePass.config.xml located in %APPDATA%\KeePass /// DiskEncrypted = 3, } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/AboutForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class AboutForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutForm)); this.ExplanationLabel = new System.Windows.Forms.Label(); this.NameLabel = new System.Windows.Forms.Label(); this.CloseButton = new System.Windows.Forms.Button(); this.HeaderLabel = new System.Windows.Forms.Label(); this.SuspendLayout(); // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(23, 73); this.ExplanationLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(608, 72); this.ExplanationLabel.TabIndex = 11; this.ExplanationLabel.Text = "This KeePass plugin allows synchronizing your KeePass databases with one or multiple OneDrive accounts. It is inspired by KeeSkyDrive by Oleksandr Senyuk. It is free to use and open source. Please leave these credits in place if you decide to use my source code. Feel free to contact me in case of questions or suggestions."; // // NameLabel // this.NameLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.NameLabel.Location = new System.Drawing.Point(23, 158); this.NameLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.NameLabel.Name = "NameLabel"; this.NameLabel.Size = new System.Drawing.Size(608, 27); this.NameLabel.TabIndex = 10; this.NameLabel.Text = "Koen Zomers "; this.NameLabel.UseMnemonic = false; // // CloseButton // this.CloseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CloseButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.CloseButton.Location = new System.Drawing.Point(455, 190); this.CloseButton.Margin = new System.Windows.Forms.Padding(5); this.CloseButton.Name = "CloseButton"; this.CloseButton.Size = new System.Drawing.Size(176, 50); this.CloseButton.TabIndex = 9; this.CloseButton.Text = "&Close"; this.CloseButton.UseVisualStyleBackColor = true; this.CloseButton.Click += new System.EventHandler(this.CloseButton_Click); // // HeaderLabel // this.HeaderLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.HeaderLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.HeaderLabel.Location = new System.Drawing.Point(21, 16); this.HeaderLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.HeaderLabel.Name = "HeaderLabel"; this.HeaderLabel.Size = new System.Drawing.Size(610, 34); this.HeaderLabel.TabIndex = 8; this.HeaderLabel.Text = "KeePass OneDriveSync"; this.HeaderLabel.UseMnemonic = false; // // AboutForm // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(645, 254); this.Controls.Add(this.ExplanationLabel); this.Controls.Add(this.NameLabel); this.Controls.Add(this.CloseButton); this.Controls.Add(this.HeaderLabel); this.Margin = new System.Windows.Forms.Padding(4); this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(410, 280); this.Name = "AboutForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "About KeePass OneDriveSync"; this.ResumeLayout(false); } #endregion private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.Label NameLabel; private System.Windows.Forms.Button CloseButton; private System.Windows.Forms.Label HeaderLabel; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/AboutForm.cs ================================================ using System.Reflection; using System.Windows.Forms; namespace KoenZomersKeePassOneDriveSync { public partial class AboutForm : Form { public AboutForm() { InitializeComponent(); var assemblyVersion = Assembly.GetCallingAssembly().GetName().Version; HeaderLabel.Text = string.Format("KeePass OneDriveSync v{0}.{1}.{2}", assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Build); } private void CloseButton_Click(object sender, System.EventArgs e) { Close(); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/AboutForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAboutForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveAboutForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.HeaderLabel = new System.Windows.Forms.Label(); this.CloseButton = new System.Windows.Forms.Button(); this.NameLabel = new System.Windows.Forms.Label(); this.ExplanationLabel = new System.Windows.Forms.Label(); this.WebsiteLinkLabel = new System.Windows.Forms.LinkLabel(); this.SuspendLayout(); // // HeaderLabel // this.HeaderLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.HeaderLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.HeaderLabel.Location = new System.Drawing.Point(18, 14); this.HeaderLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.HeaderLabel.Name = "HeaderLabel"; this.HeaderLabel.Size = new System.Drawing.Size(561, 35); this.HeaderLabel.TabIndex = 2; this.HeaderLabel.Text = "KeePass OneDriveSync"; this.HeaderLabel.UseMnemonic = false; // // CloseButton // this.CloseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CloseButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.CloseButton.Location = new System.Drawing.Point(431, 194); this.CloseButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.CloseButton.Name = "CloseButton"; this.CloseButton.Size = new System.Drawing.Size(148, 51); this.CloseButton.TabIndex = 5; this.CloseButton.Text = "&Close"; this.CloseButton.UseVisualStyleBackColor = true; this.CloseButton.Click += new System.EventHandler(this.CloseButton_Click); // // NameLabel // this.NameLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.NameLabel.Location = new System.Drawing.Point(20, 194); this.NameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.NameLabel.Name = "NameLabel"; this.NameLabel.Size = new System.Drawing.Size(405, 28); this.NameLabel.TabIndex = 6; this.NameLabel.Text = "Koen Zomers (koen@zomers.eu)"; this.NameLabel.UseMnemonic = false; // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(19, 71); this.ExplanationLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.ExplanationLabel.MinimumSize = new System.Drawing.Size(558, 118); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(558, 118); this.ExplanationLabel.TabIndex = 7; this.ExplanationLabel.Text = "This KeePass plugin allows synchronizing your KeePass databases with one or multiple OneDrive accounts. It is inspired by KeeSkyDrive by Oleksandr Senyuk. It is free to use and open source. Please leave these credits in place if you decide to use my source code. Feel free to contact me in case of questions or suggestions."; // // WebsiteLinkLabel // this.WebsiteLinkLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.WebsiteLinkLabel.AutoSize = true; this.WebsiteLinkLabel.Location = new System.Drawing.Point(19, 222); this.WebsiteLinkLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.WebsiteLinkLabel.Name = "WebsiteLinkLabel"; this.WebsiteLinkLabel.Size = new System.Drawing.Size(398, 20); this.WebsiteLinkLabel.TabIndex = 9; this.WebsiteLinkLabel.TabStop = true; this.WebsiteLinkLabel.Text = "https://github.com/KoenZomers/KeePassOneDriveSync"; this.WebsiteLinkLabel.Click += new System.EventHandler(this.WebsiteLinkLabel_Click); // // OneDriveAboutForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(597, 262); this.Controls.Add(this.CloseButton); this.Controls.Add(this.WebsiteLinkLabel); this.Controls.Add(this.ExplanationLabel); this.Controls.Add(this.NameLabel); this.Controls.Add(this.HeaderLabel); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.MaximizeBox = false; this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(619, 250); this.Name = "OneDriveAboutForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "About KeePass OneDriveSync"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label HeaderLabel; private System.Windows.Forms.Button CloseButton; private System.Windows.Forms.Label NameLabel; private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.LinkLabel WebsiteLinkLabel; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAboutForm.cs ================================================ using System; using System.Reflection; using System.Windows.Forms; namespace KoenZomersKeePassOneDriveSync { /// /// About Form /// public partial class OneDriveAboutForm : Form { public OneDriveAboutForm() { InitializeComponent(); var assemblyVersion = Assembly.GetCallingAssembly().GetName().Version; HeaderLabel.Text = string.Format("KeePass OneDriveSync v{0}.{1}.{2}.{3}", assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Build, assemblyVersion.Revision); } private void CloseButton_Click(object sender, EventArgs e) { } private void WebsiteLinkLabel_Click(object sender, EventArgs e) { System.Diagnostics.Process.Start(WebsiteLinkLabel.Text); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAboutForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 This KeePass plugin allows synchronizing your KeePass databases with one or multiple OneDrive accounts. It is inspired by KeeSkyDrive by Oleksandr Senyuk. It is free to use and open source. Please leave these credits in place if you decide to use my source code. Feel free to contact me in case of questions or suggestions. ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAskToStartSyncingDialog.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveAskToStartSyncingDialog { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.ExplanationLabel = new System.Windows.Forms.Label(); this.YesRadio = new System.Windows.Forms.RadioButton(); this.NotNowRadio = new System.Windows.Forms.RadioButton(); this.NoNeverAskAgainRadio = new System.Windows.Forms.RadioButton(); this.OKButton = new System.Windows.Forms.Button(); this.CancelButton = new System.Windows.Forms.Button(); this.OneDriveDatabasePath = new System.Windows.Forms.TextBox(); this.SuspendLayout(); // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(9, 7); this.ExplanationLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(375, 35); this.ExplanationLabel.TabIndex = 0; this.ExplanationLabel.Text = "The following KeePass database is not being synced with OneDrive. Would you like " + "to set up a synchronization connection now?"; // // YesRadio // this.YesRadio.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.YesRadio.AutoSize = true; this.YesRadio.Checked = true; this.YesRadio.Location = new System.Drawing.Point(11, 75); this.YesRadio.Margin = new System.Windows.Forms.Padding(2); this.YesRadio.Name = "YesRadio"; this.YesRadio.Size = new System.Drawing.Size(43, 17); this.YesRadio.TabIndex = 1; this.YesRadio.TabStop = true; this.YesRadio.Text = "Yes"; this.YesRadio.UseVisualStyleBackColor = true; // // NotNowRadio // this.NotNowRadio.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.NotNowRadio.AutoSize = true; this.NotNowRadio.Location = new System.Drawing.Point(11, 97); this.NotNowRadio.Margin = new System.Windows.Forms.Padding(2); this.NotNowRadio.Name = "NotNowRadio"; this.NotNowRadio.Size = new System.Drawing.Size(65, 17); this.NotNowRadio.TabIndex = 2; this.NotNowRadio.Text = "Not now"; this.NotNowRadio.UseVisualStyleBackColor = true; // // NoNeverAskAgainRadio // this.NoNeverAskAgainRadio.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.NoNeverAskAgainRadio.AutoSize = true; this.NoNeverAskAgainRadio.Location = new System.Drawing.Point(11, 118); this.NoNeverAskAgainRadio.Margin = new System.Windows.Forms.Padding(2); this.NoNeverAskAgainRadio.Name = "NoNeverAskAgainRadio"; this.NoNeverAskAgainRadio.Size = new System.Drawing.Size(220, 17); this.NoNeverAskAgainRadio.TabIndex = 3; this.NoNeverAskAgainRadio.Text = "No and never ask again for this database"; this.NoNeverAskAgainRadio.UseVisualStyleBackColor = true; // // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.OKButton.Location = new System.Drawing.Point(127, 154); this.OKButton.Margin = new System.Windows.Forms.Padding(2); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(74, 41); this.OKButton.TabIndex = 5; this.OKButton.Text = "&OK"; this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.CancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CancelButton.Location = new System.Drawing.Point(206, 154); this.CancelButton.Margin = new System.Windows.Forms.Padding(2); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(74, 41); this.CancelButton.TabIndex = 6; this.CancelButton.Text = "&Cancel"; this.CancelButton.UseVisualStyleBackColor = true; // // OneDriveDatabasePath // this.OneDriveDatabasePath.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.OneDriveDatabasePath.BackColor = System.Drawing.SystemColors.Control; this.OneDriveDatabasePath.BorderStyle = System.Windows.Forms.BorderStyle.None; this.OneDriveDatabasePath.Location = new System.Drawing.Point(12, 45); this.OneDriveDatabasePath.Name = "OneDriveDatabasePath"; this.OneDriveDatabasePath.ReadOnly = true; this.OneDriveDatabasePath.Size = new System.Drawing.Size(369, 13); this.OneDriveDatabasePath.TabIndex = 7; this.OneDriveDatabasePath.Text = "?"; // // OneDriveAskToStartSyncingDialog // this.AcceptButton = this.OKButton; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(393, 205); this.Controls.Add(this.OneDriveDatabasePath); this.Controls.Add(this.CancelButton); this.Controls.Add(this.OKButton); this.Controls.Add(this.NoNeverAskAgainRadio); this.Controls.Add(this.NotNowRadio); this.Controls.Add(this.YesRadio); this.Controls.Add(this.ExplanationLabel); this.Margin = new System.Windows.Forms.Padding(2); this.MaximizeBox = false; this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(409, 244); this.Name = "OneDriveAskToStartSyncingDialog"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Start syncing with OneDrive"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.Button OKButton; private System.Windows.Forms.Button CancelButton; private System.Windows.Forms.RadioButton YesRadio; private System.Windows.Forms.RadioButton NotNowRadio; private System.Windows.Forms.RadioButton NoNeverAskAgainRadio; private System.Windows.Forms.TextBox OneDriveDatabasePath; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAskToStartSyncingDialog.cs ================================================ using System; using System.Windows.Forms; using KoenZomers.KeePass.OneDriveSync; namespace KoenZomersKeePassOneDriveSync { public partial class OneDriveAskToStartSyncingDialog : Form { /// /// Configuration of the database for which this question is being asked for /// private readonly Configuration _configuration; public OneDriveAskToStartSyncingDialog(Configuration configuration) { InitializeComponent(); _configuration = configuration; OneDriveDatabasePath.Text = configuration.KeePassDatabase.IOConnectionInfo.Path; } private void OKButton_Click(object sender, EventArgs e) { if (NotNowRadio.Checked) { DialogResult = DialogResult.Cancel; } if (YesRadio.Checked) { DialogResult = DialogResult.OK; } if (NoNeverAskAgainRadio.Checked) { _configuration.DoNotSync = NoNeverAskAgainRadio.Checked; Configuration.Save(); DialogResult = DialogResult.OK; } Close(); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAskToStartSyncingDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAuthenticateForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveAuthenticateForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.WebBrowser = new System.Windows.Forms.WebBrowser(); this.SuspendLayout(); // // WebBrowser // this.WebBrowser.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.WebBrowser.Location = new System.Drawing.Point(-2, 0); this.WebBrowser.MinimumSize = new System.Drawing.Size(22, 25); this.WebBrowser.Name = "WebBrowser"; this.WebBrowser.ScriptErrorsSuppressed = true; this.WebBrowser.Size = new System.Drawing.Size(664, 828); this.WebBrowser.TabIndex = 0; this.WebBrowser.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(this.WebBrowser_Navigated); // // OneDriveAuthenticateForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(664, 826); this.Controls.Add(this.WebBrowser); this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(438, 390); this.Name = "OneDriveAuthenticateForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Authenticate to OneDrive"; this.ResumeLayout(false); } #endregion private System.Windows.Forms.WebBrowser WebBrowser; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAuthenticateForm.cs ================================================ using System.Windows.Forms; using KoenZomers.OneDrive.Api; namespace KoenZomersKeePassOneDriveSync { public partial class OneDriveAuthenticateForm : Form { /// /// OneDrive API instance /// public OneDriveApi OneDriveApi { get; private set; } public OneDriveAuthenticateForm(OneDriveApi oneDriveApi) { InitializeComponent(); OneDriveApi = oneDriveApi; Utilities.ApplyProxySettings(OneDriveApi); SignOut(); } /// /// Sign the current user out of OneDrive /// public void SignOut() { // First sign the current user out to make sure he/she needs to authenticate again var signoutUri = OneDriveApi.GetSignOutUri(); WebBrowser.Navigate(signoutUri); } private async void WebBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e) { // Check if the current URL contains the authorization token var authorizationCode = OneDriveApi.GetAuthorizationTokenFromUrl(e.Url.ToString()); // Verify if an authorization token was successfully extracted if (!string.IsNullOrEmpty(authorizationCode)) { // Get an access token based on the authorization token that we now have await OneDriveApi.GetAccessToken(); if (OneDriveApi.AccessToken != null) { DialogResult = DialogResult.OK; Close(); return; } } // If we're on this page, but we didn't get an authorization token, it means that we just signed out, proceed with signing in again if (e.Url.ToString().StartsWith(OneDriveApi.SignoutUri)) { var authenticateUri = OneDriveApi.GetAuthenticationUri(); WebBrowser.Navigate(authenticateUri); } } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveAuthenticateForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveCloudTypeForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveCloudTypeForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.CancelButton = new System.Windows.Forms.Button(); this.ExplanationLabel = new System.Windows.Forms.Label(); this.CloudPlatformTabs = new System.Windows.Forms.TabControl(); this.OneDriveTab = new System.Windows.Forms.TabPage(); this.GraphDeviceLoginPictureButton = new System.Windows.Forms.Button(); this.GraphPictureButton = new System.Windows.Forms.Button(); this.SharePointTab = new System.Windows.Forms.TabPage(); this.SharePointPictureButton = new System.Windows.Forms.Button(); this.CloudPlatformTabs.SuspendLayout(); this.OneDriveTab.SuspendLayout(); this.SharePointTab.SuspendLayout(); this.SuspendLayout(); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.CancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CancelButton.Location = new System.Drawing.Point(200, 432); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(168, 43); this.CancelButton.TabIndex = 1; this.CancelButton.Text = "Cancel"; this.CancelButton.UseVisualStyleBackColor = true; this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(22, 9); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(531, 37); this.ExplanationLabel.TabIndex = 0; this.ExplanationLabel.Text = "Choose the cloud service you wish to store the KeePass database on:"; // // CloudPlatformTabs // this.CloudPlatformTabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.CloudPlatformTabs.Controls.Add(this.OneDriveTab); this.CloudPlatformTabs.Controls.Add(this.SharePointTab); this.CloudPlatformTabs.Location = new System.Drawing.Point(26, 48); this.CloudPlatformTabs.Name = "CloudPlatformTabs"; this.CloudPlatformTabs.SelectedIndex = 0; this.CloudPlatformTabs.Size = new System.Drawing.Size(514, 378); this.CloudPlatformTabs.TabIndex = 0; // // OneDriveTab // this.OneDriveTab.Controls.Add(this.GraphDeviceLoginPictureButton); this.OneDriveTab.Controls.Add(this.GraphPictureButton); this.OneDriveTab.Location = new System.Drawing.Point(4, 29); this.OneDriveTab.Name = "OneDriveTab"; this.OneDriveTab.Size = new System.Drawing.Size(506, 345); this.OneDriveTab.TabIndex = 0; this.OneDriveTab.Text = "OneDrive"; this.OneDriveTab.UseVisualStyleBackColor = true; // // GraphDeviceLoginPictureButton // this.GraphDeviceLoginPictureButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.GraphDeviceLoginPictureButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.GraphDeviceLoginPictureButton.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; this.GraphDeviceLoginPictureButton.Location = new System.Drawing.Point(10, 15); this.GraphDeviceLoginPictureButton.Name = "GraphDeviceLoginPictureButton"; this.GraphDeviceLoginPictureButton.Size = new System.Drawing.Size(483, 151); this.GraphDeviceLoginPictureButton.TabIndex = 0; this.GraphDeviceLoginPictureButton.Text = "&Any browser (recommended)"; this.GraphDeviceLoginPictureButton.TextAlign = System.Drawing.ContentAlignment.BottomCenter; this.GraphDeviceLoginPictureButton.UseVisualStyleBackColor = false; this.GraphDeviceLoginPictureButton.Click += new System.EventHandler(this.GraphDeviceLoginPictureButton_Click); // // GraphPictureButton // this.GraphPictureButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.GraphPictureButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.GraphPictureButton.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; this.GraphPictureButton.Location = new System.Drawing.Point(10, 175); this.GraphPictureButton.Name = "GraphPictureButton"; this.GraphPictureButton.Size = new System.Drawing.Size(483, 151); this.GraphPictureButton.TabIndex = 1; this.GraphPictureButton.Text = "&Built-in browser"; this.GraphPictureButton.TextAlign = System.Drawing.ContentAlignment.BottomCenter; this.GraphPictureButton.UseVisualStyleBackColor = false; this.GraphPictureButton.Click += new System.EventHandler(this.GraphPictureButton_Click); // // SharePointTab // this.SharePointTab.Controls.Add(this.SharePointPictureButton); this.SharePointTab.Location = new System.Drawing.Point(4, 29); this.SharePointTab.Name = "SharePointTab"; this.SharePointTab.Size = new System.Drawing.Size(506, 345); this.SharePointTab.TabIndex = 1; this.SharePointTab.Text = "SharePoint"; this.SharePointTab.UseVisualStyleBackColor = true; // // SharePointPictureButton // this.SharePointPictureButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.SharePointPictureButton.BackColor = System.Drawing.Color.White; this.SharePointPictureButton.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; this.SharePointPictureButton.ForeColor = System.Drawing.SystemColors.ControlText; this.SharePointPictureButton.Location = new System.Drawing.Point(12, 17); this.SharePointPictureButton.Name = "SharePointPictureButton"; this.SharePointPictureButton.Size = new System.Drawing.Size(483, 129); this.SharePointPictureButton.TabIndex = 8; this.SharePointPictureButton.Text = "SharePoint 2013, 2016, 2019 & Online"; this.SharePointPictureButton.TextAlign = System.Drawing.ContentAlignment.BottomCenter; this.SharePointPictureButton.UseMnemonic = false; this.SharePointPictureButton.UseVisualStyleBackColor = false; this.SharePointPictureButton.Click += new System.EventHandler(this.SharePointPictureButton_Click); // // OneDriveCloudTypeForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(564, 489); this.Controls.Add(this.CloudPlatformTabs); this.Controls.Add(this.ExplanationLabel); this.Controls.Add(this.CancelButton); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "OneDriveCloudTypeForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Choose Cloud Platform"; this.CloudPlatformTabs.ResumeLayout(false); this.OneDriveTab.ResumeLayout(false); this.SharePointTab.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button CancelButton; private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.TabControl CloudPlatformTabs; private System.Windows.Forms.TabPage OneDriveTab; private System.Windows.Forms.Button GraphPictureButton; private System.Windows.Forms.TabPage SharePointTab; private System.Windows.Forms.Button SharePointPictureButton; private System.Windows.Forms.Button GraphDeviceLoginPictureButton; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveCloudTypeForm.cs ================================================ using System; using System.Windows.Forms; using KoenZomers.KeePass.OneDriveSync.Enums; namespace KoenZomersKeePassOneDriveSync { public partial class OneDriveCloudTypeForm : Form { /// /// The cloud storage provider that was chosen in this dialog /// public CloudStorageType ChosenCloudStorageType { get; private set; } /// /// Gets or sets the explanation text in the dialog /// public string ExplanationText { get { return ExplanationLabel.Text; } set { ExplanationLabel.Text = value; } } public OneDriveCloudTypeForm() { InitializeComponent(); GraphPictureButton.BackgroundImage = Resources.OneDriveBothClouds; GraphDeviceLoginPictureButton.BackgroundImage = Resources.OneDriveBothClouds; SharePointPictureButton.BackgroundImage = Resources.SharePoint; } private void OneDriveConsumerPicture_Click(object sender, EventArgs e) { ChosenCloudStorageType = CloudStorageType.OneDriveConsumer; DialogResult = DialogResult.OK; Close(); } private void OneDriveConsumerPictureButton_Click(object sender, EventArgs e) { ChosenCloudStorageType = CloudStorageType.OneDriveConsumer; DialogResult = DialogResult.OK; Close(); } private void GraphPictureButton_Click(object sender, EventArgs e) { ChosenCloudStorageType = CloudStorageType.MicrosoftGraph; DialogResult = DialogResult.OK; Close(); } private void SharePointPictureButton_Click(object sender, EventArgs e) { ChosenCloudStorageType = CloudStorageType.SharePoint; DialogResult = DialogResult.OK; Close(); } private void CancelButton_Click(object sender, EventArgs e) { } private void GraphDeviceLoginPictureButton_Click(object sender, EventArgs e) { ChosenCloudStorageType = CloudStorageType.MicrosoftGraphDeviceLogin; DialogResult = DialogResult.OK; Close(); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveCloudTypeForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveConfigDetailsForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveConfigDetailsForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.LocationNameLabel = new System.Windows.Forms.Label(); this.LocalKeePassPathLabel = new System.Windows.Forms.Label(); this.RemoteKeePassPathLabel = new System.Windows.Forms.Label(); this.OneDriveRefreshTokenLabel = new System.Windows.Forms.Label(); this.RemoteKeePassPathTextbox = new System.Windows.Forms.TextBox(); this.OneDriveRefreshTokenTextbox = new System.Windows.Forms.TextBox(); this.OneDriveNameTextbox = new System.Windows.Forms.TextBox(); this.CloseButton = new System.Windows.Forms.Button(); this.DeleteButton = new System.Windows.Forms.Button(); this.LastSyncedLabel = new System.Windows.Forms.Label(); this.LastSyncedTextbox = new System.Windows.Forms.TextBox(); this.LocalKeePassFileHashLabel = new System.Windows.Forms.Label(); this.LocalKeePassFileHashTextbox = new System.Windows.Forms.TextBox(); this.ForceSyncButton = new System.Windows.Forms.Button(); this.LastVerifiedLabel = new System.Windows.Forms.Label(); this.LastVerifiedTextbox = new System.Windows.Forms.TextBox(); this.StatusLabel = new System.Windows.Forms.Label(); this.RefreshTokenStorageTextBox = new System.Windows.Forms.TextBox(); this.RefreshTokenStorageLabel = new System.Windows.Forms.Label(); this.CloudStorageTypeTextBox = new System.Windows.Forms.TextBox(); this.CloudStorageTypeLabel = new System.Windows.Forms.Label(); this.OneDriveEtagTextBox = new System.Windows.Forms.TextBox(); this.OneDriveEtagLabel = new System.Windows.Forms.Label(); this.LocalDatabasePathLinkLabel = new System.Windows.Forms.LinkLabel(); this.ItemIdTextBox = new System.Windows.Forms.TextBox(); this.ItemIdLabel = new System.Windows.Forms.Label(); this.FolderIdTextBox = new System.Windows.Forms.TextBox(); this.FolderIdLabel = new System.Windows.Forms.Label(); this.DriveIdTextBox = new System.Windows.Forms.TextBox(); this.DriveIdLabel = new System.Windows.Forms.Label(); this.SuspendLayout(); // // LocationNameLabel // this.LocationNameLabel.Location = new System.Drawing.Point(16, 11); this.LocationNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.LocationNameLabel.Name = "LocationNameLabel"; this.LocationNameLabel.Size = new System.Drawing.Size(236, 20); this.LocationNameLabel.TabIndex = 0; this.LocationNameLabel.Text = "Location Name:"; this.LocationNameLabel.UseMnemonic = false; // // LocalKeePassPathLabel // this.LocalKeePassPathLabel.Location = new System.Drawing.Point(16, 58); this.LocalKeePassPathLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.LocalKeePassPathLabel.Name = "LocalKeePassPathLabel"; this.LocalKeePassPathLabel.Size = new System.Drawing.Size(236, 20); this.LocalKeePassPathLabel.TabIndex = 2; this.LocalKeePassPathLabel.Text = "Local KeePass path:"; this.LocalKeePassPathLabel.UseMnemonic = false; // // RemoteKeePassPathLabel // this.RemoteKeePassPathLabel.Location = new System.Drawing.Point(16, 83); this.RemoteKeePassPathLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.RemoteKeePassPathLabel.Name = "RemoteKeePassPathLabel"; this.RemoteKeePassPathLabel.Size = new System.Drawing.Size(236, 20); this.RemoteKeePassPathLabel.TabIndex = 4; this.RemoteKeePassPathLabel.Text = "Remote KeePass path:"; this.RemoteKeePassPathLabel.UseMnemonic = false; // // OneDriveRefreshTokenLabel // this.OneDriveRefreshTokenLabel.Location = new System.Drawing.Point(16, 109); this.OneDriveRefreshTokenLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.OneDriveRefreshTokenLabel.Name = "OneDriveRefreshTokenLabel"; this.OneDriveRefreshTokenLabel.Size = new System.Drawing.Size(236, 20); this.OneDriveRefreshTokenLabel.TabIndex = 6; this.OneDriveRefreshTokenLabel.Text = "API Refresh Token:"; this.OneDriveRefreshTokenLabel.UseMnemonic = false; // // RemoteKeePassPathTextbox // this.RemoteKeePassPathTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.RemoteKeePassPathTextbox.BackColor = System.Drawing.SystemColors.Control; this.RemoteKeePassPathTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.RemoteKeePassPathTextbox.Location = new System.Drawing.Point(260, 83); this.RemoteKeePassPathTextbox.Margin = new System.Windows.Forms.Padding(4); this.RemoteKeePassPathTextbox.Name = "RemoteKeePassPathTextbox"; this.RemoteKeePassPathTextbox.ReadOnly = true; this.RemoteKeePassPathTextbox.Size = new System.Drawing.Size(444, 15); this.RemoteKeePassPathTextbox.TabIndex = 4; this.RemoteKeePassPathTextbox.Text = "?"; // // OneDriveRefreshTokenTextbox // this.OneDriveRefreshTokenTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.OneDriveRefreshTokenTextbox.BackColor = System.Drawing.SystemColors.Control; this.OneDriveRefreshTokenTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.OneDriveRefreshTokenTextbox.Location = new System.Drawing.Point(260, 109); this.OneDriveRefreshTokenTextbox.Margin = new System.Windows.Forms.Padding(4); this.OneDriveRefreshTokenTextbox.Name = "OneDriveRefreshTokenTextbox"; this.OneDriveRefreshTokenTextbox.ReadOnly = true; this.OneDriveRefreshTokenTextbox.Size = new System.Drawing.Size(444, 15); this.OneDriveRefreshTokenTextbox.TabIndex = 5; this.OneDriveRefreshTokenTextbox.Text = "?"; // // OneDriveNameTextbox // this.OneDriveNameTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.OneDriveNameTextbox.BackColor = System.Drawing.SystemColors.Control; this.OneDriveNameTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.OneDriveNameTextbox.Location = new System.Drawing.Point(260, 11); this.OneDriveNameTextbox.Margin = new System.Windows.Forms.Padding(4); this.OneDriveNameTextbox.Name = "OneDriveNameTextbox"; this.OneDriveNameTextbox.ReadOnly = true; this.OneDriveNameTextbox.Size = new System.Drawing.Size(444, 15); this.OneDriveNameTextbox.TabIndex = 2; this.OneDriveNameTextbox.Text = "?"; // // CloseButton // this.CloseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CloseButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.CloseButton.Location = new System.Drawing.Point(572, 333); this.CloseButton.Margin = new System.Windows.Forms.Padding(4); this.CloseButton.Name = "CloseButton"; this.CloseButton.Size = new System.Drawing.Size(132, 41); this.CloseButton.TabIndex = 1; this.CloseButton.Text = "&Close"; this.CloseButton.UseVisualStyleBackColor = true; this.CloseButton.Click += new System.EventHandler(this.CloseButton_Click); // // DeleteButton // this.DeleteButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.DeleteButton.Location = new System.Drawing.Point(20, 333); this.DeleteButton.Margin = new System.Windows.Forms.Padding(4); this.DeleteButton.Name = "DeleteButton"; this.DeleteButton.Size = new System.Drawing.Size(132, 41); this.DeleteButton.TabIndex = 13; this.DeleteButton.Text = "&Delete"; this.DeleteButton.UseVisualStyleBackColor = true; this.DeleteButton.Click += new System.EventHandler(this.DeleteButton_Click); // // LastSyncedLabel // this.LastSyncedLabel.Location = new System.Drawing.Point(16, 161); this.LastSyncedLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.LastSyncedLabel.Name = "LastSyncedLabel"; this.LastSyncedLabel.Size = new System.Drawing.Size(236, 20); this.LastSyncedLabel.TabIndex = 14; this.LastSyncedLabel.Text = "Last synced with {0}:"; this.LastSyncedLabel.UseMnemonic = false; // // LastSyncedTextbox // this.LastSyncedTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.LastSyncedTextbox.BackColor = System.Drawing.SystemColors.Control; this.LastSyncedTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.LastSyncedTextbox.Location = new System.Drawing.Point(260, 161); this.LastSyncedTextbox.Margin = new System.Windows.Forms.Padding(4); this.LastSyncedTextbox.Name = "LastSyncedTextbox"; this.LastSyncedTextbox.ReadOnly = true; this.LastSyncedTextbox.Size = new System.Drawing.Size(444, 15); this.LastSyncedTextbox.TabIndex = 15; this.LastSyncedTextbox.Text = "?"; // // LocalKeePassFileHashLabel // this.LocalKeePassFileHashLabel.Location = new System.Drawing.Point(16, 212); this.LocalKeePassFileHashLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.LocalKeePassFileHashLabel.Name = "LocalKeePassFileHashLabel"; this.LocalKeePassFileHashLabel.Size = new System.Drawing.Size(236, 20); this.LocalKeePassFileHashLabel.TabIndex = 16; this.LocalKeePassFileHashLabel.Text = "Local KeePass SHA1 hash:"; this.LocalKeePassFileHashLabel.UseMnemonic = false; // // LocalKeePassFileHashTextbox // this.LocalKeePassFileHashTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.LocalKeePassFileHashTextbox.BackColor = System.Drawing.SystemColors.Control; this.LocalKeePassFileHashTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.LocalKeePassFileHashTextbox.Location = new System.Drawing.Point(260, 212); this.LocalKeePassFileHashTextbox.Margin = new System.Windows.Forms.Padding(4); this.LocalKeePassFileHashTextbox.Name = "LocalKeePassFileHashTextbox"; this.LocalKeePassFileHashTextbox.ReadOnly = true; this.LocalKeePassFileHashTextbox.Size = new System.Drawing.Size(444, 15); this.LocalKeePassFileHashTextbox.TabIndex = 17; this.LocalKeePassFileHashTextbox.Text = "?"; // // ForceSyncButton // this.ForceSyncButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.ForceSyncButton.Location = new System.Drawing.Point(432, 333); this.ForceSyncButton.Margin = new System.Windows.Forms.Padding(4); this.ForceSyncButton.Name = "ForceSyncButton"; this.ForceSyncButton.Size = new System.Drawing.Size(132, 41); this.ForceSyncButton.TabIndex = 18; this.ForceSyncButton.Text = "&Sync Now"; this.ForceSyncButton.UseVisualStyleBackColor = true; this.ForceSyncButton.Click += new System.EventHandler(this.ForceSyncButton_Click); // // LastVerifiedLabel // this.LastVerifiedLabel.Location = new System.Drawing.Point(16, 186); this.LastVerifiedLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.LastVerifiedLabel.Name = "LastVerifiedLabel"; this.LastVerifiedLabel.Size = new System.Drawing.Size(236, 20); this.LastVerifiedLabel.TabIndex = 19; this.LastVerifiedLabel.Text = "Last verified with {0}:"; this.LastVerifiedLabel.UseMnemonic = false; // // LastVerifiedTextbox // this.LastVerifiedTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.LastVerifiedTextbox.BackColor = System.Drawing.SystemColors.Control; this.LastVerifiedTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.LastVerifiedTextbox.Location = new System.Drawing.Point(260, 186); this.LastVerifiedTextbox.Margin = new System.Windows.Forms.Padding(4); this.LastVerifiedTextbox.Name = "LastVerifiedTextbox"; this.LastVerifiedTextbox.ReadOnly = true; this.LastVerifiedTextbox.Size = new System.Drawing.Size(444, 15); this.LastVerifiedTextbox.TabIndex = 20; this.LastVerifiedTextbox.Text = "?"; // // StatusLabel // this.StatusLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.StatusLabel.Location = new System.Drawing.Point(160, 333); this.StatusLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.StatusLabel.Name = "StatusLabel"; this.StatusLabel.Size = new System.Drawing.Size(264, 44); this.StatusLabel.TabIndex = 21; this.StatusLabel.Text = "Ready"; this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.StatusLabel.UseMnemonic = false; // // RefreshTokenStorageTextBox // this.RefreshTokenStorageTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.RefreshTokenStorageTextBox.BackColor = System.Drawing.SystemColors.Control; this.RefreshTokenStorageTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.RefreshTokenStorageTextBox.Location = new System.Drawing.Point(260, 135); this.RefreshTokenStorageTextBox.Margin = new System.Windows.Forms.Padding(4); this.RefreshTokenStorageTextBox.Name = "RefreshTokenStorageTextBox"; this.RefreshTokenStorageTextBox.ReadOnly = true; this.RefreshTokenStorageTextBox.Size = new System.Drawing.Size(444, 15); this.RefreshTokenStorageTextBox.TabIndex = 22; this.RefreshTokenStorageTextBox.Text = "?"; // // RefreshTokenStorageLabel // this.RefreshTokenStorageLabel.Location = new System.Drawing.Point(16, 135); this.RefreshTokenStorageLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.RefreshTokenStorageLabel.Name = "RefreshTokenStorageLabel"; this.RefreshTokenStorageLabel.Size = new System.Drawing.Size(236, 20); this.RefreshTokenStorageLabel.TabIndex = 23; this.RefreshTokenStorageLabel.Text = "Refresh Token Storage:"; this.RefreshTokenStorageLabel.UseMnemonic = false; // // CloudStorageTypeTextBox // this.CloudStorageTypeTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.CloudStorageTypeTextBox.BackColor = System.Drawing.SystemColors.Control; this.CloudStorageTypeTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.CloudStorageTypeTextBox.Location = new System.Drawing.Point(260, 34); this.CloudStorageTypeTextBox.Margin = new System.Windows.Forms.Padding(4); this.CloudStorageTypeTextBox.Name = "CloudStorageTypeTextBox"; this.CloudStorageTypeTextBox.ReadOnly = true; this.CloudStorageTypeTextBox.Size = new System.Drawing.Size(444, 15); this.CloudStorageTypeTextBox.TabIndex = 25; this.CloudStorageTypeTextBox.Text = "?"; // // CloudStorageTypeLabel // this.CloudStorageTypeLabel.Location = new System.Drawing.Point(16, 34); this.CloudStorageTypeLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.CloudStorageTypeLabel.Name = "CloudStorageTypeLabel"; this.CloudStorageTypeLabel.Size = new System.Drawing.Size(236, 20); this.CloudStorageTypeLabel.TabIndex = 24; this.CloudStorageTypeLabel.Text = "Cloud storage type:"; this.CloudStorageTypeLabel.UseMnemonic = false; // // OneDriveEtagTextBox // this.OneDriveEtagTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.OneDriveEtagTextBox.BackColor = System.Drawing.SystemColors.Control; this.OneDriveEtagTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.OneDriveEtagTextBox.Location = new System.Drawing.Point(260, 238); this.OneDriveEtagTextBox.Margin = new System.Windows.Forms.Padding(4); this.OneDriveEtagTextBox.Name = "OneDriveEtagTextBox"; this.OneDriveEtagTextBox.ReadOnly = true; this.OneDriveEtagTextBox.Size = new System.Drawing.Size(444, 15); this.OneDriveEtagTextBox.TabIndex = 27; this.OneDriveEtagTextBox.Text = "?"; // // OneDriveEtagLabel // this.OneDriveEtagLabel.Location = new System.Drawing.Point(16, 238); this.OneDriveEtagLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.OneDriveEtagLabel.Name = "OneDriveEtagLabel"; this.OneDriveEtagLabel.Size = new System.Drawing.Size(236, 20); this.OneDriveEtagLabel.TabIndex = 26; this.OneDriveEtagLabel.Text = "ETag:"; this.OneDriveEtagLabel.UseMnemonic = false; // // LocalDatabasePathLinkLabel // this.LocalDatabasePathLinkLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.LocalDatabasePathLinkLabel.Location = new System.Drawing.Point(257, 58); this.LocalDatabasePathLinkLabel.Name = "LocalDatabasePathLinkLabel"; this.LocalDatabasePathLinkLabel.Size = new System.Drawing.Size(447, 20); this.LocalDatabasePathLinkLabel.TabIndex = 28; this.LocalDatabasePathLinkLabel.TabStop = true; this.LocalDatabasePathLinkLabel.Text = "?"; this.LocalDatabasePathLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LocalDatabasePathLinkLabel_LinkClicked); // // ItemIdTextBox // this.ItemIdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ItemIdTextBox.BackColor = System.Drawing.SystemColors.Control; this.ItemIdTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.ItemIdTextBox.Location = new System.Drawing.Point(260, 261); this.ItemIdTextBox.Margin = new System.Windows.Forms.Padding(4); this.ItemIdTextBox.Name = "ItemIdTextBox"; this.ItemIdTextBox.ReadOnly = true; this.ItemIdTextBox.Size = new System.Drawing.Size(444, 15); this.ItemIdTextBox.TabIndex = 30; this.ItemIdTextBox.Text = "?"; this.ItemIdTextBox.TextChanged += new System.EventHandler(this.ItemIdTextBox_TextChanged); // // ItemIdLabel // this.ItemIdLabel.Location = new System.Drawing.Point(17, 261); this.ItemIdLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.ItemIdLabel.Name = "ItemIdLabel"; this.ItemIdLabel.Size = new System.Drawing.Size(236, 20); this.ItemIdLabel.TabIndex = 29; this.ItemIdLabel.Text = "Item Id:"; this.ItemIdLabel.UseMnemonic = false; this.ItemIdLabel.Click += new System.EventHandler(this.ItemIdLabel_Click); // // FolderIdTextBox // this.FolderIdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.FolderIdTextBox.BackColor = System.Drawing.SystemColors.Control; this.FolderIdTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.FolderIdTextBox.Location = new System.Drawing.Point(259, 284); this.FolderIdTextBox.Margin = new System.Windows.Forms.Padding(4); this.FolderIdTextBox.Name = "FolderIdTextBox"; this.FolderIdTextBox.ReadOnly = true; this.FolderIdTextBox.Size = new System.Drawing.Size(444, 15); this.FolderIdTextBox.TabIndex = 32; this.FolderIdTextBox.Text = "?"; // // FolderIdLabel // this.FolderIdLabel.Location = new System.Drawing.Point(16, 284); this.FolderIdLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.FolderIdLabel.Name = "FolderIdLabel"; this.FolderIdLabel.Size = new System.Drawing.Size(236, 20); this.FolderIdLabel.TabIndex = 31; this.FolderIdLabel.Text = "Folder Id:"; this.FolderIdLabel.UseMnemonic = false; // // DriveIdTextBox // this.DriveIdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.DriveIdTextBox.BackColor = System.Drawing.SystemColors.Control; this.DriveIdTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.DriveIdTextBox.Location = new System.Drawing.Point(259, 307); this.DriveIdTextBox.Margin = new System.Windows.Forms.Padding(4); this.DriveIdTextBox.Name = "DriveIdTextBox"; this.DriveIdTextBox.ReadOnly = true; this.DriveIdTextBox.Size = new System.Drawing.Size(444, 15); this.DriveIdTextBox.TabIndex = 34; this.DriveIdTextBox.Text = "?"; // // DriveIdLabel // this.DriveIdLabel.Location = new System.Drawing.Point(16, 307); this.DriveIdLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.DriveIdLabel.Name = "DriveIdLabel"; this.DriveIdLabel.Size = new System.Drawing.Size(236, 20); this.DriveIdLabel.TabIndex = 33; this.DriveIdLabel.Text = "Drive Id:"; this.DriveIdLabel.UseMnemonic = false; // // OneDriveConfigDetailsForm // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CloseButton; this.ClientSize = new System.Drawing.Size(720, 388); this.Controls.Add(this.StatusLabel); this.Controls.Add(this.ForceSyncButton); this.Controls.Add(this.DeleteButton); this.Controls.Add(this.CloseButton); this.Controls.Add(this.DriveIdTextBox); this.Controls.Add(this.DriveIdLabel); this.Controls.Add(this.FolderIdTextBox); this.Controls.Add(this.FolderIdLabel); this.Controls.Add(this.ItemIdTextBox); this.Controls.Add(this.ItemIdLabel); this.Controls.Add(this.LocalDatabasePathLinkLabel); this.Controls.Add(this.OneDriveEtagTextBox); this.Controls.Add(this.OneDriveEtagLabel); this.Controls.Add(this.CloudStorageTypeTextBox); this.Controls.Add(this.CloudStorageTypeLabel); this.Controls.Add(this.RefreshTokenStorageTextBox); this.Controls.Add(this.RefreshTokenStorageLabel); this.Controls.Add(this.LastVerifiedTextbox); this.Controls.Add(this.LastVerifiedLabel); this.Controls.Add(this.LocalKeePassFileHashTextbox); this.Controls.Add(this.LocalKeePassFileHashLabel); this.Controls.Add(this.LastSyncedTextbox); this.Controls.Add(this.LastSyncedLabel); this.Controls.Add(this.OneDriveNameTextbox); this.Controls.Add(this.OneDriveRefreshTokenTextbox); this.Controls.Add(this.RemoteKeePassPathTextbox); this.Controls.Add(this.OneDriveRefreshTokenLabel); this.Controls.Add(this.RemoteKeePassPathLabel); this.Controls.Add(this.LocalKeePassPathLabel); this.Controls.Add(this.LocationNameLabel); this.KeyPreview = true; this.Margin = new System.Windows.Forms.Padding(4); this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(680, 278); this.Name = "OneDriveConfigDetailsForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "OneDriveSync Configuration"; this.Load += new System.EventHandler(this.OneDriveConfigDetailsForm_Load); this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OneDriveConfigDetailsForm_KeyUp); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label LocationNameLabel; private System.Windows.Forms.Label LocalKeePassPathLabel; private System.Windows.Forms.Label RemoteKeePassPathLabel; private System.Windows.Forms.Label OneDriveRefreshTokenLabel; private System.Windows.Forms.TextBox RemoteKeePassPathTextbox; private System.Windows.Forms.TextBox OneDriveRefreshTokenTextbox; private System.Windows.Forms.TextBox OneDriveNameTextbox; private System.Windows.Forms.Button CloseButton; private System.Windows.Forms.Button DeleteButton; private System.Windows.Forms.Label LastSyncedLabel; private System.Windows.Forms.TextBox LastSyncedTextbox; private System.Windows.Forms.Label LocalKeePassFileHashLabel; private System.Windows.Forms.TextBox LocalKeePassFileHashTextbox; private System.Windows.Forms.Button ForceSyncButton; private System.Windows.Forms.Label LastVerifiedLabel; private System.Windows.Forms.TextBox LastVerifiedTextbox; private System.Windows.Forms.Label StatusLabel; private System.Windows.Forms.TextBox RefreshTokenStorageTextBox; private System.Windows.Forms.Label RefreshTokenStorageLabel; private System.Windows.Forms.TextBox CloudStorageTypeTextBox; private System.Windows.Forms.Label CloudStorageTypeLabel; private System.Windows.Forms.TextBox OneDriveEtagTextBox; private System.Windows.Forms.Label OneDriveEtagLabel; private System.Windows.Forms.LinkLabel LocalDatabasePathLinkLabel; private System.Windows.Forms.TextBox ItemIdTextBox; private System.Windows.Forms.Label ItemIdLabel; private System.Windows.Forms.TextBox FolderIdTextBox; private System.Windows.Forms.Label FolderIdLabel; private System.Windows.Forms.TextBox DriveIdTextBox; private System.Windows.Forms.Label DriveIdLabel; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveConfigDetailsForm.cs ================================================ using System; using System.Collections.Generic; using System.Windows.Forms; using KoenZomers.KeePass.OneDriveSync; using KoenZomers.KeePass.OneDriveSync.Enums; namespace KoenZomersKeePassOneDriveSync { /// /// Detail screen for a OneDriveSync configuration /// public partial class OneDriveConfigDetailsForm : Form { private KeyValuePair _configuration; /// /// Opens the configuration details screen for the provided configuration /// /// public OneDriveConfigDetailsForm(KeyValuePair configuration) { InitializeComponent(); _configuration = configuration; } private void OneDriveConfigDetailsForm_Load(object sender, EventArgs e) { ShowConfiguration(); } private void ShowConfiguration() { LocationNameLabel.Text = _configuration.Value.CloudStorageType == CloudStorageType.SharePoint ? "SharePoint site:" : "OneDrive name:"; LastSyncedLabel.Text = string.Format(LastSyncedLabel.Text, _configuration.Value.CloudStorageType); LastVerifiedLabel.Text = string.Format(LastVerifiedLabel.Text, _configuration.Value.CloudStorageType); OneDriveNameTextbox.Text = _configuration.Value.DoNotSync ? "Not enabled for sync" : _configuration.Value.CloudStorageType == CloudStorageType.SharePoint ? _configuration.Value.RemoteDatabasePath + (string.IsNullOrEmpty(_configuration.Value.OneDriveName) ? "" : " (" + _configuration.Value.OneDriveName + ")") : _configuration.Value.OneDriveName; CloudStorageTypeTextBox.Text = _configuration.Value.CloudStorageType.GetValueOrDefault(CloudStorageType.OneDriveConsumer).ToString(); LocalDatabasePathLinkLabel.Text = _configuration.Key; LocalDatabasePathLinkLabel.LinkColor = System.IO.File.Exists(_configuration.Key) ? System.Drawing.Color.Blue : System.Drawing.Color.Red; RemoteKeePassPathTextbox.Text = _configuration.Value.CloudStorageType == CloudStorageType.SharePoint ? _configuration.Value.RemoteFolderId + "/" + _configuration.Value.RemoteFileName : _configuration.Value.RemoteDatabasePath; OneDriveRefreshTokenTextbox.Text = _configuration.Value.RefreshToken; RefreshTokenStorageTextBox.Text = _configuration.Value.RefreshTokenStorage.ToString(); LastSyncedTextbox.Text = _configuration.Value.LastSyncedAt.HasValue ? _configuration.Value.LastSyncedAt.Value.ToString("dddd d MMMM yyyy HH:mm:ss") : "Never synced yet"; LastVerifiedTextbox.Text = _configuration.Value.LastCheckedAt.HasValue ? _configuration.Value.LastCheckedAt.Value.ToString("dddd d MMMM yyyy HH:mm:ss") : "Never verified yet"; LocalKeePassFileHashTextbox.Text = _configuration.Value.LocalFileHash; OneDriveEtagTextBox.Text = _configuration.Value.ETag; ForceSyncButton.Enabled = !_configuration.Value.DoNotSync; ItemIdTextBox.Text = _configuration.Value.RemoteItemId; FolderIdTextBox.Text = _configuration.Value.RemoteFolderId; DriveIdTextBox.Text = _configuration.Value.RemoteDriveId; } private void CloseButton_Click(object sender, EventArgs e) { Close(); } private void DeleteButton_Click(object sender, EventArgs e) { var answer = MessageBox.Show("Are you sure that you wish to delete the OneDrive configuration for the selected KeePass databases? This will NOT delete the actual KeePass database file, just its configuration for the KeeOneDriveSync plugin.", "Confirm removal", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2); if (answer != DialogResult.Yes) return; Configuration.DeleteConfig(_configuration.Key); Close(); } private async void ForceSyncButton_Click(object sender, EventArgs e) { if(_configuration.Value.KeePassDatabase == null) { UpdateStatus("Database must be open before it can synchronize"); return; } UpdateStatus("Synchronizing"); await KeePassDatabase.SyncDatabase(_configuration.Key, UpdateStatus, true, null); ShowConfiguration(); } private void UpdateStatus(string message) { StatusLabel.Text = message; } private void OneDriveConfigDetailsForm_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.F5) { ShowConfiguration(); } } private void LocalDatabasePathLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { if(System.IO.File.Exists(LocalDatabasePathLinkLabel.Text)) { // File still exists locally, open explorer and have it highlight the file System.Diagnostics.Process.Start("explorer.exe", "/select, \"" + LocalDatabasePathLinkLabel.Text + "\""); return; } // Check if the parent directory still exists var fileInfo = new System.IO.FileInfo(LocalDatabasePathLinkLabel.Text); if (System.IO.Directory.Exists(fileInfo.Directory.FullName)) { // Parent directory still exists, open explorer in that folder System.Diagnostics.Process.Start("explorer.exe", "\"" + fileInfo.Directory.FullName + "\""); return; } // Both the file and the parent folder no longer exist, nothing we can do } private void ItemIdLabel_Click(object sender, EventArgs e) { } private void ItemIdTextBox_TextChanged(object sender, EventArgs e) { } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveConfigDetailsForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveConfigForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveConfigForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.ExplanationLabel = new System.Windows.Forms.Label(); this.HeaderLabel = new System.Windows.Forms.Label(); this.ConfigurationListView = new System.Windows.Forms.ListView(); this.PathColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.StorageNameColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.StorageProviderColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.LastSyncedColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.ConfigurationListViewContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); this.ConfigurationListViewContextItemViewDetails = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemRenameStorage = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSyncNow = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemOpenFileLocation = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemDelete = new System.Windows.Forms.ToolStripMenuItem(); this.CloseButton = new System.Windows.Forms.Button(); this.StatusLabel = new System.Windows.Forms.Label(); this.AboutButton = new System.Windows.Forms.Button(); this.ConfigurationListViewContextItemOpenDatabase = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); this.ConfigurationListViewContextItemRefresh = new System.Windows.Forms.ToolStripMenuItem(); this.selectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSelectAll = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSelectNotAvailableLocally = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSelectNotSynced = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSelectNotSynced24Hours = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSelectNotSyncedWeek = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSelectNotSynced2Weeks = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextItemSelectNotSyncedMonth = new System.Windows.Forms.ToolStripMenuItem(); this.ConfigurationListViewContextMenu.SuspendLayout(); this.SuspendLayout(); // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(13, 36); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(433, 31); this.ExplanationLabel.TabIndex = 0; this.ExplanationLabel.Text = "Here you\'ll see all KeePass databases that are configured for use with OneDriveSy" + "nc."; this.ExplanationLabel.UseMnemonic = false; // // HeaderLabel // this.HeaderLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.HeaderLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.HeaderLabel.Location = new System.Drawing.Point(12, 9); this.HeaderLabel.Name = "HeaderLabel"; this.HeaderLabel.Size = new System.Drawing.Size(434, 23); this.HeaderLabel.TabIndex = 1; this.HeaderLabel.Text = "KeePass OneDriveSync"; this.HeaderLabel.UseMnemonic = false; // // ConfigurationListView // this.ConfigurationListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ConfigurationListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.PathColumn, this.StorageNameColumn, this.StorageProviderColumn, this.LastSyncedColumn}); this.ConfigurationListView.ContextMenuStrip = this.ConfigurationListViewContextMenu; this.ConfigurationListView.FullRowSelect = true; this.ConfigurationListView.GridLines = true; this.ConfigurationListView.HideSelection = false; this.ConfigurationListView.Location = new System.Drawing.Point(15, 55); this.ConfigurationListView.Name = "ConfigurationListView"; this.ConfigurationListView.Size = new System.Drawing.Size(430, 219); this.ConfigurationListView.TabIndex = 3; this.ConfigurationListView.UseCompatibleStateImageBehavior = false; this.ConfigurationListView.View = System.Windows.Forms.View.Details; this.ConfigurationListView.DoubleClick += new System.EventHandler(this.ConfigurationListView_DoubleClick); this.ConfigurationListView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ConfigurationListView_KeyUp); // // PathColumn // this.PathColumn.Text = "Local Path"; this.PathColumn.Width = 275; // // StorageNameColumn // this.StorageNameColumn.Text = "Storage Name"; this.StorageNameColumn.Width = 150; // // StorageProviderColumn // this.StorageProviderColumn.Text = "Storage Provider"; this.StorageProviderColumn.Width = 175; // // LastSyncedColumn // this.LastSyncedColumn.Text = "Last Synced"; this.LastSyncedColumn.Width = 175; // // ConfigurationListViewContextMenu // this.ConfigurationListViewContextMenu.ImageScalingSize = new System.Drawing.Size(20, 20); this.ConfigurationListViewContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.ConfigurationListViewContextItemViewDetails, this.ConfigurationListViewContextItemRenameStorage, this.ConfigurationListViewContextItemDelete, this.toolStripMenuItem2, this.ConfigurationListViewContextItemSyncNow, this.toolStripMenuItem1, this.selectToolStripMenuItem, this.ConfigurationListViewContextItemRefresh, this.toolStripMenuItem3, this.ConfigurationListViewContextItemOpenFileLocation, this.ConfigurationListViewContextItemOpenDatabase}); this.ConfigurationListViewContextMenu.Name = "ConfigurationListViewContextMenu"; this.ConfigurationListViewContextMenu.Size = new System.Drawing.Size(218, 198); this.ConfigurationListViewContextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.ConfigurationListViewContextMenu_Opening); // // ConfigurationListViewContextItemViewDetails // this.ConfigurationListViewContextItemViewDetails.Name = "ConfigurationListViewContextItemViewDetails"; this.ConfigurationListViewContextItemViewDetails.ShortcutKeys = System.Windows.Forms.Keys.F1; this.ConfigurationListViewContextItemViewDetails.Size = new System.Drawing.Size(217, 22); this.ConfigurationListViewContextItemViewDetails.Text = "&View details"; this.ConfigurationListViewContextItemViewDetails.Click += new System.EventHandler(this.ConfigurationListViewContextItemViewDetails_Click); // // ConfigurationListViewContextItemRenameStorage // this.ConfigurationListViewContextItemRenameStorage.Name = "ConfigurationListViewContextItemRenameStorage"; this.ConfigurationListViewContextItemRenameStorage.ShortcutKeys = System.Windows.Forms.Keys.F2; this.ConfigurationListViewContextItemRenameStorage.Size = new System.Drawing.Size(217, 22); this.ConfigurationListViewContextItemRenameStorage.Text = "&Rename storage"; this.ConfigurationListViewContextItemRenameStorage.Click += new System.EventHandler(this.ConfigurationListViewContextItemRenameStorage_Click); // // ConfigurationListViewContextItemSyncNow // this.ConfigurationListViewContextItemSyncNow.Name = "ConfigurationListViewContextItemSyncNow"; this.ConfigurationListViewContextItemSyncNow.ShortcutKeys = System.Windows.Forms.Keys.F4; this.ConfigurationListViewContextItemSyncNow.Size = new System.Drawing.Size(217, 22); this.ConfigurationListViewContextItemSyncNow.Text = "&Sync Now"; this.ConfigurationListViewContextItemSyncNow.Click += new System.EventHandler(this.ConfigurationListViewContextItemSyncNow_Click); // // ConfigurationListViewContextItemOpenFileLocation // this.ConfigurationListViewContextItemOpenFileLocation.Name = "ConfigurationListViewContextItemOpenFileLocation"; this.ConfigurationListViewContextItemOpenFileLocation.ShortcutKeys = System.Windows.Forms.Keys.F7; this.ConfigurationListViewContextItemOpenFileLocation.Size = new System.Drawing.Size(217, 22); this.ConfigurationListViewContextItemOpenFileLocation.Text = "Open file &location"; this.ConfigurationListViewContextItemOpenFileLocation.Click += new System.EventHandler(this.ConfigurationListViewContextItemOpenFileLocation_Click); // // ConfigurationListViewContextItemDelete // this.ConfigurationListViewContextItemDelete.Name = "ConfigurationListViewContextItemDelete"; this.ConfigurationListViewContextItemDelete.ShortcutKeys = System.Windows.Forms.Keys.Delete; this.ConfigurationListViewContextItemDelete.Size = new System.Drawing.Size(217, 22); this.ConfigurationListViewContextItemDelete.Text = "&Delete configuration"; this.ConfigurationListViewContextItemDelete.Click += new System.EventHandler(this.ConfigurationListViewContextItemDelete_Click); // // CloseButton // this.CloseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CloseButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.CloseButton.Location = new System.Drawing.Point(347, 280); this.CloseButton.Name = "CloseButton"; this.CloseButton.Size = new System.Drawing.Size(99, 33); this.CloseButton.TabIndex = 4; this.CloseButton.Text = "&Close"; this.CloseButton.UseVisualStyleBackColor = true; this.CloseButton.Click += new System.EventHandler(this.CloseButton_Click); // // StatusLabel // this.StatusLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.StatusLabel.Location = new System.Drawing.Point(13, 280); this.StatusLabel.Name = "StatusLabel"; this.StatusLabel.Size = new System.Drawing.Size(223, 33); this.StatusLabel.TabIndex = 5; this.StatusLabel.Text = "-"; this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.StatusLabel.UseMnemonic = false; // // AboutButton // this.AboutButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.AboutButton.Location = new System.Drawing.Point(242, 280); this.AboutButton.Name = "AboutButton"; this.AboutButton.Size = new System.Drawing.Size(99, 33); this.AboutButton.TabIndex = 6; this.AboutButton.Text = "&About"; this.AboutButton.UseVisualStyleBackColor = true; this.AboutButton.Click += new System.EventHandler(this.AboutButton_Click); // // ConfigurationListViewContextItemOpenDatabase // this.ConfigurationListViewContextItemOpenDatabase.Name = "ConfigurationListViewContextItemOpenDatabase"; this.ConfigurationListViewContextItemOpenDatabase.ShortcutKeys = System.Windows.Forms.Keys.F8; this.ConfigurationListViewContextItemOpenDatabase.Size = new System.Drawing.Size(217, 22); this.ConfigurationListViewContextItemOpenDatabase.Text = "Open &KeePass database"; this.ConfigurationListViewContextItemOpenDatabase.Click += new System.EventHandler(this.ConfigurationListViewContextItemOpenDatabase_Click); // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; this.toolStripMenuItem1.Size = new System.Drawing.Size(214, 6); // // toolStripMenuItem2 // this.toolStripMenuItem2.Name = "toolStripMenuItem2"; this.toolStripMenuItem2.Size = new System.Drawing.Size(214, 6); // // toolStripMenuItem3 // this.toolStripMenuItem3.Name = "toolStripMenuItem3"; this.toolStripMenuItem3.Size = new System.Drawing.Size(214, 6); // // ConfigurationListViewContextItemRefresh // this.ConfigurationListViewContextItemRefresh.Name = "ConfigurationListViewContextItemRefresh"; this.ConfigurationListViewContextItemRefresh.ShortcutKeys = System.Windows.Forms.Keys.F5; this.ConfigurationListViewContextItemRefresh.Size = new System.Drawing.Size(217, 22); this.ConfigurationListViewContextItemRefresh.Text = "Refresh &list"; this.ConfigurationListViewContextItemRefresh.Click += new System.EventHandler(this.ConfigurationListViewContextItemRefresh_Click); // // selectToolStripMenuItem // this.selectToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.ConfigurationListViewContextItemSelectAll, this.ConfigurationListViewContextItemSelectNotAvailableLocally, this.ConfigurationListViewContextItemSelectNotSynced}); this.selectToolStripMenuItem.Name = "selectToolStripMenuItem"; this.selectToolStripMenuItem.Size = new System.Drawing.Size(217, 22); this.selectToolStripMenuItem.Text = "S&elect"; // // ConfigurationListViewContextItemSelectAll // this.ConfigurationListViewContextItemSelectAll.Name = "ConfigurationListViewContextItemSelectAll"; this.ConfigurationListViewContextItemSelectAll.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.A))); this.ConfigurationListViewContextItemSelectAll.Size = new System.Drawing.Size(254, 22); this.ConfigurationListViewContextItemSelectAll.Text = "&All"; this.ConfigurationListViewContextItemSelectAll.Click += new System.EventHandler(this.ConfigurationListViewContextItemSelectAll_Click); // // ConfigurationListViewContextItemSelectNotAvailableLocally // this.ConfigurationListViewContextItemSelectNotAvailableLocally.Name = "ConfigurationListViewContextItemSelectNotAvailableLocally"; this.ConfigurationListViewContextItemSelectNotAvailableLocally.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) | System.Windows.Forms.Keys.A))); this.ConfigurationListViewContextItemSelectNotAvailableLocally.Size = new System.Drawing.Size(254, 22); this.ConfigurationListViewContextItemSelectNotAvailableLocally.Text = "&Not available locally"; this.ConfigurationListViewContextItemSelectNotAvailableLocally.Click += new System.EventHandler(this.ConfigurationListViewContextItemSelectNotAvailableLocally_Click); // // ConfigurationListViewContextItemSelectNotSynced // this.ConfigurationListViewContextItemSelectNotSynced.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.ConfigurationListViewContextItemSelectNotSynced24Hours, this.ConfigurationListViewContextItemSelectNotSyncedWeek, this.ConfigurationListViewContextItemSelectNotSynced2Weeks, this.ConfigurationListViewContextItemSelectNotSyncedMonth}); this.ConfigurationListViewContextItemSelectNotSynced.Name = "ConfigurationListViewContextItemSelectNotSynced"; this.ConfigurationListViewContextItemSelectNotSynced.Size = new System.Drawing.Size(254, 22); this.ConfigurationListViewContextItemSelectNotSynced.Text = "Not synced in the &past..."; // // ConfigurationListViewContextItemSelectNotSynced24Hours // this.ConfigurationListViewContextItemSelectNotSynced24Hours.Name = "ConfigurationListViewContextItemSelectNotSynced24Hours"; this.ConfigurationListViewContextItemSelectNotSynced24Hours.Size = new System.Drawing.Size(180, 22); this.ConfigurationListViewContextItemSelectNotSynced24Hours.Text = "24 &hours"; this.ConfigurationListViewContextItemSelectNotSynced24Hours.Click += new System.EventHandler(this.ConfigurationListViewContextItemSelectNotSynced24Hours_Click); // // ConfigurationListViewContextItemSelectNotSyncedWeek // this.ConfigurationListViewContextItemSelectNotSyncedWeek.Name = "ConfigurationListViewContextItemSelectNotSyncedWeek"; this.ConfigurationListViewContextItemSelectNotSyncedWeek.Size = new System.Drawing.Size(180, 22); this.ConfigurationListViewContextItemSelectNotSyncedWeek.Text = "&Week"; this.ConfigurationListViewContextItemSelectNotSyncedWeek.Click += new System.EventHandler(this.ConfigurationListViewContextItemSelectNotSyncedWeek_Click); // // ConfigurationListViewContextItemSelectNotSynced2Weeks // this.ConfigurationListViewContextItemSelectNotSynced2Weeks.Name = "ConfigurationListViewContextItemSelectNotSynced2Weeks"; this.ConfigurationListViewContextItemSelectNotSynced2Weeks.Size = new System.Drawing.Size(180, 22); this.ConfigurationListViewContextItemSelectNotSynced2Weeks.Text = "&2 Weeks"; this.ConfigurationListViewContextItemSelectNotSynced2Weeks.Click += new System.EventHandler(this.ConfigurationListViewContextItemSelectNotSynced2Weeks_Click); // // ConfigurationListViewContextItemSelectNotSyncedMonth // this.ConfigurationListViewContextItemSelectNotSyncedMonth.Name = "ConfigurationListViewContextItemSelectNotSyncedMonth"; this.ConfigurationListViewContextItemSelectNotSyncedMonth.Size = new System.Drawing.Size(180, 22); this.ConfigurationListViewContextItemSelectNotSyncedMonth.Text = "&Month"; this.ConfigurationListViewContextItemSelectNotSyncedMonth.Click += new System.EventHandler(this.ConfigurationListViewContextItemSelectNotSyncedMonth_Click); // // OneDriveConfigForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CloseButton; this.ClientSize = new System.Drawing.Size(458, 325); this.Controls.Add(this.AboutButton); this.Controls.Add(this.StatusLabel); this.Controls.Add(this.CloseButton); this.Controls.Add(this.ConfigurationListView); this.Controls.Add(this.HeaderLabel); this.Controls.Add(this.ExplanationLabel); this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(386, 217); this.Name = "OneDriveConfigForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "OneDrive Sync Configuration"; this.Load += new System.EventHandler(this.OneDriveConfigForm_Load); this.ConfigurationListViewContextMenu.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.Label HeaderLabel; private System.Windows.Forms.ListView ConfigurationListView; private System.Windows.Forms.Button CloseButton; private System.Windows.Forms.ColumnHeader PathColumn; private System.Windows.Forms.ColumnHeader StorageNameColumn; private System.Windows.Forms.ContextMenuStrip ConfigurationListViewContextMenu; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemDelete; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemViewDetails; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSyncNow; private System.Windows.Forms.Label StatusLabel; private System.Windows.Forms.Button AboutButton; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemOpenFileLocation; private System.Windows.Forms.CheckBox UseSystemProxyCheckBox; private System.Windows.Forms.ColumnHeader StorageProviderColumn; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemRenameStorage; private System.Windows.Forms.ColumnHeader LastSyncedColumn; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemOpenDatabase; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemRefresh; private System.Windows.Forms.ToolStripMenuItem selectToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSelectAll; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSelectNotAvailableLocally; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSelectNotSynced; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSelectNotSynced24Hours; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSelectNotSyncedWeek; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSelectNotSynced2Weeks; private System.Windows.Forms.ToolStripMenuItem ConfigurationListViewContextItemSelectNotSyncedMonth; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveConfigForm.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; using KeePassLib.Serialization; using KoenZomers.KeePass.OneDriveSync; using KoenZomers.KeePass.OneDriveSync.Enums; namespace KoenZomersKeePassOneDriveSync { public partial class OneDriveConfigForm : Form { /// /// Timer that resets any other status back to the default after a few seconds /// private readonly Timer _resetStatusTimer; /// /// The default status to show /// private string _defaultStatus; public OneDriveConfigForm() { InitializeComponent(); _resetStatusTimer = new Timer {Enabled = false, Interval = 10000 }; _resetStatusTimer.Tick += (sender, args) => { _resetStatusTimer.Enabled = false; UpdateStatus(_defaultStatus); }; } private void OneDriveConfigForm_Load(object sender, EventArgs e) { LoadConfigurations(); } /// /// Loads the configurations in the listview /// private void LoadConfigurations() { UpdateStatus("Loading configuration"); // Clear the existing items ConfigurationListView.Items.Clear(); var configurations = Configuration.GetAllConfigurations(); foreach (var configuration in configurations) { var configurationItem = new ListViewItem { Text = configuration.Key, Tag = configuration }; var doesDatabaseExistLocally = File.Exists(configuration.Key); var isRemoteDatabase = Regex.IsMatch(configuration.Key, @".+:[\\/]{2}.+", RegexOptions.IgnoreCase); configurationItem.BackColor = doesDatabaseExistLocally ? ConfigurationListView.BackColor : isRemoteDatabase ? Color.Orange : Color.Red; configurationItem.ToolTipText = doesDatabaseExistLocally ? "Database has been found" : isRemoteDatabase ? "Database is a remote database which is not supported" : "Database has not been found"; configurationItem.SubItems.Add(new ListViewItem.ListViewSubItem { Name = "OneDrive", Text = configuration.Value.DoNotSync ? "Not synced" : configuration.Value.OneDriveName }); configurationItem.SubItems.Add(new ListViewItem.ListViewSubItem { Name = "CloudStorage", Text = configuration.Value.CloudStorageType.HasValue ? configuration.Value.CloudStorageType.Value.ToString() : configuration.Value.DoNotSync ? "Not in cloud" : CloudStorageType.OneDriveConsumer.ToString() }); configurationItem.SubItems.Add(new ListViewItem.ListViewSubItem { Name = "LastSynced", Text = configuration.Value.LastCheckedAt.HasValue ? configuration.Value.LastCheckedAt.Value.ToString("ddd d MMMM yyyy HH:mm:ss") : "Never", Tag = configuration.Value.LastCheckedAt.HasValue ? (DateTime?) configuration.Value.LastCheckedAt.Value : null }); ConfigurationListView.Items.Add(configurationItem); } _defaultStatus = string.Format("{0} configuration{1} found", ConfigurationListView.Items.Count, ConfigurationListView.Items.Count != 1 ? "s" : ""); UpdateStatus(_defaultStatus); } private void CloseButton_Click(object sender, EventArgs e) { } private void ConfigurationListViewContextItemDelete_Click(object sender, EventArgs e) { DeleteItem(); } private void ConfigurationListViewContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e) { ConfigurationListViewContextItemDelete.Enabled = ConfigurationListView.SelectedItems.Count > 0; ConfigurationListViewContextItemViewDetails.Enabled = ConfigurationListView.SelectedItems.Count == 1; ConfigurationListViewContextItemOpenFileLocation.Enabled = ConfigurationListView.SelectedItems.Count > 0; ConfigurationListViewContextItemSyncNow.Enabled = ConfigurationListView.SelectedItems.Count > 0; ConfigurationListViewContextItemRenameStorage.Enabled = ConfigurationListView.SelectedItems.Count > 0; ConfigurationListViewContextItemOpenDatabase.Enabled = ConfigurationListView.SelectedItems.Count > 0; } private void ConfigurationListView_DoubleClick(object sender, EventArgs e) { ViewDetails(); } private void ConfigurationListViewContextItemViewDetails_Click(object sender, EventArgs e) { ViewDetails(); } private void DeleteItem() { if (ConfigurationListView.SelectedItems.Count == 0) return; var answer = MessageBox.Show("Are you sure that you wish to delete the OneDrive configuration for the selected KeePass databases? This will NOT delete the actual KeePass database file, just its configuration for the KeeOneDriveSync plugin.", "Confirm removal", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2); if (answer != DialogResult.Yes) return; foreach (ListViewItem selectedItem in ConfigurationListView.SelectedItems) { Configuration.DeleteConfig(((KeyValuePair)selectedItem.Tag).Key); } LoadConfigurations(); } private void ViewDetails() { if (ConfigurationListView.SelectedItems.Count != 1) return; var oneDriveConfigDetailsForm = new OneDriveConfigDetailsForm((KeyValuePair)ConfigurationListView.SelectedItems[0].Tag); oneDriveConfigDetailsForm.ShowDialog(); LoadConfigurations(); } private void RenameEntry() { if (ConfigurationListView.SelectedItems.Count == 0) return; var selectedDatabaseText = ConfigurationListView.SelectedItems.Count == 1 ? ((KeyValuePair)ConfigurationListView.SelectedItems[0].Tag).Value.OneDriveName : string.Format("the selected {0} databases", ConfigurationListView.SelectedItems.Count); var renameItemDialog = new Forms.OneDriveRequestInputDialog { FormTitle = string.Format("Enter new storage name name for {0}", selectedDatabaseText), InputValue = ConfigurationListView.SelectedItems.Count == 1 ? ((KeyValuePair)ConfigurationListView.SelectedItems[0].Tag).Value.OneDriveName : string.Empty }; renameItemDialog.ShowDialog(this); if (renameItemDialog.DialogResult != DialogResult.OK) return; // Loop through all selected items to update their names foreach (ListViewItem configurationItem in ConfigurationListView.SelectedItems) { var configuration = (KeyValuePair)configurationItem.Tag; // Update the name in the configuration configuration.Value.OneDriveName = renameItemDialog.InputValue; // Update the name in the configuration list shown on the screen configurationItem.SubItems[1].Text = renameItemDialog.InputValue; } Configuration.Save(); UpdateStatus("Storage Name has been changed"); } private async Task SyncNow() { foreach (ListViewItem selectedItem in ConfigurationListView.SelectedItems) { // Only allow syncing if the database is not marked with the DoNotSync flag var configuration = (KeyValuePair)selectedItem.Tag; if (configuration.Value.DoNotSync) { return; } // Ensure the database to be synced is currently open if(configuration.Value.KeePassDatabase == null) { UpdateStatus(string.Format("Database {0} must be open before it can sync", configuration.Key)); continue; } UpdateStatus(string.Format("Synchronizing database {0}", configuration.Value.KeePassDatabase.Name)); await KeePassDatabase.SyncDatabase(selectedItem.Text, UpdateStatus, true, configuration.Value); // Update the Last Synced column selectedItem.SubItems[3].Text = configuration.Value.LastCheckedAt.HasValue ? configuration.Value.LastCheckedAt.Value.ToString("ddd d MMMM yyyy HH:mm:ss") : "Never"; selectedItem.SubItems[3].Tag = configuration.Value.LastCheckedAt.HasValue ? (DateTime?) configuration.Value.LastCheckedAt.Value : null; } } private async void ConfigurationListViewContextItemSyncNow_Click(object sender, EventArgs e) { await SyncNow(); } private void UpdateStatus(string message) { StatusLabel.Text = message; // If the status message is not the default message, enable the timer to reset the status back to the default after a few seconds if (_defaultStatus != null && message != _defaultStatus) { _resetStatusTimer.Enabled = true; } } private void AboutButton_Click(object sender, EventArgs e) { var oneDriveAboutForm = new OneDriveAboutForm(); oneDriveAboutForm.ShowDialog(); } private void ConfigurationListView_KeyUp(object sender, KeyEventArgs e) { } private void ConfigurationListViewContextItemOpenFileLocation_Click(object sender, EventArgs e) { foreach (ListViewItem selectedItem in ConfigurationListView.SelectedItems) { // Get the full local path of the selected KeePass database configuration var databaseFileLocation = ((KeyValuePair)selectedItem.Tag).Key; // Open File Explorer and make it directly jump to the KeePass database if it exists, if not, just open the explorer to the folder Process.Start("explorer.exe", File.Exists(databaseFileLocation) ? string.Concat("/select, ", databaseFileLocation) : Directory.GetParent(databaseFileLocation).FullName); } } private void ConfigurationListViewContextItemRenameStorage_Click(object sender, EventArgs e) { RenameEntry(); } private void ConfigurationListViewContextItemOpenDatabase_Click(object sender, EventArgs e) { foreach (ListViewItem selectedItem in ConfigurationListView.SelectedItems) { // Get the full local path of the selected KeePass database configuration var databaseFileLocation = ((KeyValuePair)selectedItem.Tag).Key; // Open the KeePass database var databaseFile = IOConnectionInfo.FromPath(databaseFileLocation); KoenZomersKeePassOneDriveSyncExt.Host.MainWindow.OpenDatabase(databaseFile, null, false); } } private void ConfigurationListViewContextItemRefresh_Click(object sender, EventArgs e) { LoadConfigurations(); } private void ConfigurationListViewContextItemSelectAll_Click(object sender, EventArgs e) { foreach (ListViewItem item in ConfigurationListView.Items) { item.Selected = true; } } private void ConfigurationListViewContextItemSelectNotAvailableLocally_Click(object sender, EventArgs e) { foreach (ListViewItem item in ConfigurationListView.Items) { item.Selected = item.BackColor == Color.Red; } } private void ConfigurationListViewContextItemSelectNotSynced24Hours_Click(object sender, EventArgs e) { foreach (ListViewItem item in ConfigurationListView.Items) { var lastChecked = item.SubItems[3].Tag as DateTime?; item.Selected = !lastChecked.HasValue || lastChecked.Value.AddHours(24) < DateTime.Now; } } private void ConfigurationListViewContextItemSelectNotSyncedWeek_Click(object sender, EventArgs e) { foreach (ListViewItem item in ConfigurationListView.Items) { var lastChecked = item.SubItems[3].Tag as DateTime?; item.Selected = !lastChecked.HasValue || lastChecked.Value.AddDays(7) < DateTime.Now; } } private void ConfigurationListViewContextItemSelectNotSynced2Weeks_Click(object sender, EventArgs e) { foreach (ListViewItem item in ConfigurationListView.Items) { var lastChecked = item.SubItems[3].Tag as DateTime?; item.Selected = !lastChecked.HasValue || lastChecked.Value.AddDays(14) < DateTime.Now; } } private void ConfigurationListViewContextItemSelectNotSyncedMonth_Click(object sender, EventArgs e) { foreach (ListViewItem item in ConfigurationListView.Items) { var lastChecked = item.SubItems[3].Tag as DateTime?; item.Selected = !lastChecked.HasValue || lastChecked.Value.AddMonths(1) < DateTime.Now; } } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveConfigForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveFilePickerDialog.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync.Forms { partial class OneDriveFilePickerDialog { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OneDriveFilePickerDialog)); this.MyFilesContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.GoToRootToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.goupToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.newFolderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.renameToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.deleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.IconsList = new System.Windows.Forms.ImageList(this.components); this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); this.UpButton = new System.Windows.Forms.Button(); this.ExplanationLabel = new System.Windows.Forms.Label(); this.FileNameTextBox = new System.Windows.Forms.TextBox(); this.FileNameLabel = new System.Windows.Forms.Label(); this.FilesTabControl = new System.Windows.Forms.TabControl(); this.MyFilesTabPage = new System.Windows.Forms.TabPage(); this.CloudLocationPath = new System.Windows.Forms.TextBox(); this.CloudLocationPicker = new System.Windows.Forms.ListView(); this.SharedWithMeTabPage = new System.Windows.Forms.TabPage(); this.SharedWithMeUpButton = new System.Windows.Forms.Button(); this.SharedWithMePath = new System.Windows.Forms.TextBox(); this.SharedWithMePicker = new System.Windows.Forms.ListView(); this.SharedWithMeContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); this.RefreshSharedWithMeFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.GoToSharedWithMeRootTtoolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.GoUpSharedWithMeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SharedWithMeNotAvailableLabel = new System.Windows.Forms.Label(); this.MyFilesContextMenu.SuspendLayout(); this.FilesTabControl.SuspendLayout(); this.MyFilesTabPage.SuspendLayout(); this.SharedWithMeTabPage.SuspendLayout(); this.SharedWithMeContextMenu.SuspendLayout(); this.SuspendLayout(); // // MyFilesContextMenu // this.MyFilesContextMenu.ImageScalingSize = new System.Drawing.Size(20, 20); this.MyFilesContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.refreshToolStripMenuItem, this.GoToRootToolStripMenuItem, this.goupToolStripMenuItem, this.toolStripMenuItem1, this.newFolderToolStripMenuItem, this.renameToolStripMenuItem, this.deleteToolStripMenuItem}); this.MyFilesContextMenu.Name = "ListViewContextMenu"; this.MyFilesContextMenu.Size = new System.Drawing.Size(239, 202); this.MyFilesContextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.ListViewContextMenu_Opening); // // refreshToolStripMenuItem // this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; this.refreshToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F5; this.refreshToolStripMenuItem.Size = new System.Drawing.Size(238, 32); this.refreshToolStripMenuItem.Text = "&Refresh"; this.refreshToolStripMenuItem.Click += new System.EventHandler(this.RefreshToolStripMenuItem_Click); // // GoToRootToolStripMenuItem // this.GoToRootToolStripMenuItem.Name = "GoToRootToolStripMenuItem"; this.GoToRootToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.H))); this.GoToRootToolStripMenuItem.Size = new System.Drawing.Size(238, 32); this.GoToRootToolStripMenuItem.Text = "&Go to root"; this.GoToRootToolStripMenuItem.Click += new System.EventHandler(this.GoToRootToolStripMenuItem_Click); // // goupToolStripMenuItem // this.goupToolStripMenuItem.Enabled = false; this.goupToolStripMenuItem.Name = "goupToolStripMenuItem"; this.goupToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.U))); this.goupToolStripMenuItem.Size = new System.Drawing.Size(238, 32); this.goupToolStripMenuItem.Text = "Go &up"; this.goupToolStripMenuItem.Click += new System.EventHandler(this.GroupToolStripMenuItem_Click); // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; this.toolStripMenuItem1.Size = new System.Drawing.Size(235, 6); // // newFolderToolStripMenuItem // this.newFolderToolStripMenuItem.Name = "newFolderToolStripMenuItem"; this.newFolderToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); this.newFolderToolStripMenuItem.Size = new System.Drawing.Size(238, 32); this.newFolderToolStripMenuItem.Text = "&New Folder"; this.newFolderToolStripMenuItem.Click += new System.EventHandler(this.NewFolderToolStripMenuItem_Click); // // renameToolStripMenuItem // this.renameToolStripMenuItem.Name = "renameToolStripMenuItem"; this.renameToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R))); this.renameToolStripMenuItem.Size = new System.Drawing.Size(238, 32); this.renameToolStripMenuItem.Text = "R&ename"; this.renameToolStripMenuItem.Click += new System.EventHandler(this.RenameToolStripMenuItem_Click); // // deleteToolStripMenuItem // this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; this.deleteToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Delete; this.deleteToolStripMenuItem.Size = new System.Drawing.Size(238, 32); this.deleteToolStripMenuItem.Text = "&Delete"; this.deleteToolStripMenuItem.Click += new System.EventHandler(this.DeleteToolStripMenuItem_Click); // // IconsList // this.IconsList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("IconsList.ImageStream"))); this.IconsList.TransparentColor = System.Drawing.Color.Transparent; this.IconsList.Images.SetKeyName(0, "File"); this.IconsList.Images.SetKeyName(1, "Folder"); this.IconsList.Images.SetKeyName(2, "RemoteFolder"); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CancelButton.Location = new System.Drawing.Point(663, 614); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(108, 49); this.CancelButton.TabIndex = 3; this.CancelButton.Text = "&Cancel"; this.CancelButton.UseVisualStyleBackColor = true; // // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.OKButton.Enabled = false; this.OKButton.Location = new System.Drawing.Point(548, 614); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(108, 49); this.OKButton.TabIndex = 2; this.OKButton.Text = "&OK"; this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // // UpButton // this.UpButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.UpButton.Enabled = false; this.UpButton.Location = new System.Drawing.Point(681, 3); this.UpButton.Name = "UpButton"; this.UpButton.Size = new System.Drawing.Size(62, 43); this.UpButton.TabIndex = 4; this.UpButton.Text = "Up"; this.UpButton.UseVisualStyleBackColor = true; this.UpButton.EnabledChanged += new System.EventHandler(this.UpButton_EnabledChanged); this.UpButton.Click += new System.EventHandler(this.UpButton_Click); // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(14, 11); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(686, 23); this.ExplanationLabel.TabIndex = 8; this.ExplanationLabel.Text = "Select where you want to store the KeePass database. Right click for additional o" + "ptions."; // // FileNameTextBox // this.FileNameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.FileNameTextBox.Location = new System.Drawing.Point(106, 623); this.FileNameTextBox.Name = "FileNameTextBox"; this.FileNameTextBox.Size = new System.Drawing.Size(433, 26); this.FileNameTextBox.TabIndex = 1; this.FileNameTextBox.TextChanged += new System.EventHandler(this.FileNameTextBox_TextChanged); this.FileNameTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.FileNameTextBox_KeyUp); // // FileNameLabel // this.FileNameLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.FileNameLabel.Location = new System.Drawing.Point(14, 623); this.FileNameLabel.Name = "FileNameLabel"; this.FileNameLabel.Size = new System.Drawing.Size(87, 28); this.FileNameLabel.TabIndex = 9; this.FileNameLabel.Text = "Filename:"; // // FilesTabControl // this.FilesTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.FilesTabControl.Controls.Add(this.MyFilesTabPage); this.FilesTabControl.Controls.Add(this.SharedWithMeTabPage); this.FilesTabControl.Location = new System.Drawing.Point(16, 38); this.FilesTabControl.Name = "FilesTabControl"; this.FilesTabControl.SelectedIndex = 0; this.FilesTabControl.Size = new System.Drawing.Size(754, 569); this.FilesTabControl.TabIndex = 10; this.FilesTabControl.SelectedIndexChanged += new System.EventHandler(this.FilesTabControl_SelectedIndexChanged); // // MyFilesTabPage // this.MyFilesTabPage.Controls.Add(this.CloudLocationPath); this.MyFilesTabPage.Controls.Add(this.CloudLocationPicker); this.MyFilesTabPage.Controls.Add(this.UpButton); this.MyFilesTabPage.Location = new System.Drawing.Point(4, 29); this.MyFilesTabPage.Name = "MyFilesTabPage"; this.MyFilesTabPage.Padding = new System.Windows.Forms.Padding(3); this.MyFilesTabPage.Size = new System.Drawing.Size(746, 536); this.MyFilesTabPage.TabIndex = 0; this.MyFilesTabPage.Text = "My files"; this.MyFilesTabPage.UseVisualStyleBackColor = true; // // CloudLocationPath // this.CloudLocationPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.CloudLocationPath.BackColor = System.Drawing.Color.White; this.CloudLocationPath.BorderStyle = System.Windows.Forms.BorderStyle.None; this.CloudLocationPath.Location = new System.Drawing.Point(10, 15); this.CloudLocationPath.Name = "CloudLocationPath"; this.CloudLocationPath.Size = new System.Drawing.Size(668, 19); this.CloudLocationPath.TabIndex = 7; this.CloudLocationPath.TabStop = false; // // CloudLocationPicker // this.CloudLocationPicker.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.CloudLocationPicker.ContextMenuStrip = this.MyFilesContextMenu; this.CloudLocationPicker.HideSelection = false; this.CloudLocationPicker.LargeImageList = this.IconsList; this.CloudLocationPicker.Location = new System.Drawing.Point(6, 54); this.CloudLocationPicker.MultiSelect = false; this.CloudLocationPicker.Name = "CloudLocationPicker"; this.CloudLocationPicker.ShowItemToolTips = true; this.CloudLocationPicker.Size = new System.Drawing.Size(736, 470); this.CloudLocationPicker.SmallImageList = this.IconsList; this.CloudLocationPicker.TabIndex = 1; this.CloudLocationPicker.TileSize = new System.Drawing.Size(244, 70); this.CloudLocationPicker.UseCompatibleStateImageBehavior = false; this.CloudLocationPicker.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.CloudLocationPicker_ItemSelectionChanged); this.CloudLocationPicker.DoubleClick += new System.EventHandler(this.CloudLocationPicker_DoubleClick); this.CloudLocationPicker.KeyUp += new System.Windows.Forms.KeyEventHandler(this.CloudLocationPicker_KeyUp); // // SharedWithMeTabPage // this.SharedWithMeTabPage.Controls.Add(this.SharedWithMeUpButton); this.SharedWithMeTabPage.Controls.Add(this.SharedWithMePath); this.SharedWithMeTabPage.Controls.Add(this.SharedWithMePicker); this.SharedWithMeTabPage.Controls.Add(this.SharedWithMeNotAvailableLabel); this.SharedWithMeTabPage.Location = new System.Drawing.Point(4, 29); this.SharedWithMeTabPage.Name = "SharedWithMeTabPage"; this.SharedWithMeTabPage.Padding = new System.Windows.Forms.Padding(3); this.SharedWithMeTabPage.Size = new System.Drawing.Size(746, 536); this.SharedWithMeTabPage.TabIndex = 1; this.SharedWithMeTabPage.Text = "Shared with me"; this.SharedWithMeTabPage.UseVisualStyleBackColor = true; // // SharedWithMeUpButton // this.SharedWithMeUpButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.SharedWithMeUpButton.Enabled = false; this.SharedWithMeUpButton.Location = new System.Drawing.Point(681, 3); this.SharedWithMeUpButton.Name = "SharedWithMeUpButton"; this.SharedWithMeUpButton.Size = new System.Drawing.Size(62, 43); this.SharedWithMeUpButton.TabIndex = 9; this.SharedWithMeUpButton.Text = "Up"; this.SharedWithMeUpButton.UseVisualStyleBackColor = true; this.SharedWithMeUpButton.Click += new System.EventHandler(this.SharedWithMeUpButton_Click); // // SharedWithMePath // this.SharedWithMePath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.SharedWithMePath.BackColor = System.Drawing.Color.White; this.SharedWithMePath.BorderStyle = System.Windows.Forms.BorderStyle.None; this.SharedWithMePath.Location = new System.Drawing.Point(10, 15); this.SharedWithMePath.Name = "SharedWithMePath"; this.SharedWithMePath.Size = new System.Drawing.Size(663, 19); this.SharedWithMePath.TabIndex = 8; this.SharedWithMePath.TabStop = false; // // SharedWithMePicker // this.SharedWithMePicker.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.SharedWithMePicker.ContextMenuStrip = this.SharedWithMeContextMenu; this.SharedWithMePicker.HideSelection = false; this.SharedWithMePicker.LargeImageList = this.IconsList; this.SharedWithMePicker.Location = new System.Drawing.Point(6, 54); this.SharedWithMePicker.MultiSelect = false; this.SharedWithMePicker.Name = "SharedWithMePicker"; this.SharedWithMePicker.ShowItemToolTips = true; this.SharedWithMePicker.Size = new System.Drawing.Size(736, 470); this.SharedWithMePicker.SmallImageList = this.IconsList; this.SharedWithMePicker.TabIndex = 2; this.SharedWithMePicker.TileSize = new System.Drawing.Size(244, 70); this.SharedWithMePicker.UseCompatibleStateImageBehavior = false; this.SharedWithMePicker.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.SharedWithMePicker_ItemSelectionChanged); this.SharedWithMePicker.DoubleClick += new System.EventHandler(this.SharedWithMePicker_DoubleClick); this.SharedWithMePicker.KeyUp += new System.Windows.Forms.KeyEventHandler(this.SharedWithMePicker_KeyUp); // // SharedWithMeContextMenu // this.SharedWithMeContextMenu.ImageScalingSize = new System.Drawing.Size(20, 20); this.SharedWithMeContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.RefreshSharedWithMeFilesToolStripMenuItem, this.GoToSharedWithMeRootTtoolStripMenuItem, this.GoUpSharedWithMeToolStripMenuItem}); this.SharedWithMeContextMenu.Name = "ListViewContextMenu"; this.SharedWithMeContextMenu.Size = new System.Drawing.Size(233, 100); this.SharedWithMeContextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.SharedWithMeContextMenu_Opening); // // RefreshSharedWithMeFilesToolStripMenuItem // this.RefreshSharedWithMeFilesToolStripMenuItem.Name = "RefreshSharedWithMeFilesToolStripMenuItem"; this.RefreshSharedWithMeFilesToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F5; this.RefreshSharedWithMeFilesToolStripMenuItem.Size = new System.Drawing.Size(232, 32); this.RefreshSharedWithMeFilesToolStripMenuItem.Text = "&Refresh"; this.RefreshSharedWithMeFilesToolStripMenuItem.Click += new System.EventHandler(this.RefreshSharedWithMeFilesToolStripMenuItem_Click); // // GoToSharedWithMeRootTtoolStripMenuItem // this.GoToSharedWithMeRootTtoolStripMenuItem.Name = "GoToSharedWithMeRootTtoolStripMenuItem"; this.GoToSharedWithMeRootTtoolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.H))); this.GoToSharedWithMeRootTtoolStripMenuItem.Size = new System.Drawing.Size(232, 32); this.GoToSharedWithMeRootTtoolStripMenuItem.Text = "&Go to root"; this.GoToSharedWithMeRootTtoolStripMenuItem.Click += new System.EventHandler(this.GoToSharedWithMeRootTtoolStripMenuItem_Click); // // GoUpSharedWithMeToolStripMenuItem // this.GoUpSharedWithMeToolStripMenuItem.Enabled = false; this.GoUpSharedWithMeToolStripMenuItem.Name = "GoUpSharedWithMeToolStripMenuItem"; this.GoUpSharedWithMeToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.U))); this.GoUpSharedWithMeToolStripMenuItem.Size = new System.Drawing.Size(232, 32); this.GoUpSharedWithMeToolStripMenuItem.Text = "Go &up"; this.GoUpSharedWithMeToolStripMenuItem.Click += new System.EventHandler(this.GoUpSharedWithMeToolStripMenuItem_Click); // // SharedWithMeNotAvailableLabel // this.SharedWithMeNotAvailableLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.SharedWithMeNotAvailableLabel.Location = new System.Drawing.Point(6, 54); this.SharedWithMeNotAvailableLabel.Name = "SharedWithMeNotAvailableLabel"; this.SharedWithMeNotAvailableLabel.Size = new System.Drawing.Size(732, 471); this.SharedWithMeNotAvailableLabel.TabIndex = 10; this.SharedWithMeNotAvailableLabel.Text = "Shared with me is not available when using the OneDrive for Business service. Use" + " the Graph API service instead to connect to your OneDrive for Business site and" + " it will be available."; this.SharedWithMeNotAvailableLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // OneDriveFilePickerDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(786, 674); this.Controls.Add(this.FilesTabControl); this.Controls.Add(this.FileNameLabel); this.Controls.Add(this.FileNameTextBox); this.Controls.Add(this.ExplanationLabel); this.Controls.Add(this.CancelButton); this.Controls.Add(this.OKButton); this.KeyPreview = true; this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(467, 297); this.Name = "OneDriveFilePickerDialog"; this.ShowIcon = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Select location"; this.Load += new System.EventHandler(this.OneDriveFileSaveAsDialog_Load); this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OneDriveFileSaveAsDialog_KeyUp); this.MyFilesContextMenu.ResumeLayout(false); this.FilesTabControl.ResumeLayout(false); this.MyFilesTabPage.ResumeLayout(false); this.MyFilesTabPage.PerformLayout(); this.SharedWithMeTabPage.ResumeLayout(false); this.SharedWithMeTabPage.PerformLayout(); this.SharedWithMeContextMenu.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Button CancelButton; private System.Windows.Forms.Button OKButton; private System.Windows.Forms.Button UpButton; private System.Windows.Forms.ContextMenuStrip MyFilesContextMenu; private System.Windows.Forms.ToolStripMenuItem refreshToolStripMenuItem; private System.Windows.Forms.ImageList IconsList; private System.Windows.Forms.ToolStripMenuItem GoToRootToolStripMenuItem; private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.ToolStripMenuItem deleteToolStripMenuItem; private System.Windows.Forms.TextBox FileNameTextBox; private System.Windows.Forms.ToolStripMenuItem newFolderToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem renameToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem goupToolStripMenuItem; private System.Windows.Forms.Label FileNameLabel; private System.Windows.Forms.TabControl FilesTabControl; private System.Windows.Forms.TabPage MyFilesTabPage; private System.Windows.Forms.ListView CloudLocationPicker; private System.Windows.Forms.TabPage SharedWithMeTabPage; private System.Windows.Forms.ListView SharedWithMePicker; private System.Windows.Forms.TextBox CloudLocationPath; private System.Windows.Forms.ContextMenuStrip SharedWithMeContextMenu; private System.Windows.Forms.ToolStripMenuItem RefreshSharedWithMeFilesToolStripMenuItem; private System.Windows.Forms.TextBox SharedWithMePath; private System.Windows.Forms.Button SharedWithMeUpButton; private System.Windows.Forms.ToolStripMenuItem GoToSharedWithMeRootTtoolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem GoUpSharedWithMeToolStripMenuItem; private System.Windows.Forms.Label SharedWithMeNotAvailableLabel; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveFilePickerDialog.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using KoenZomers.OneDrive.Api; using KoenZomers.OneDrive.Api.Entities; namespace KoenZomersKeePassOneDriveSync.Forms { public partial class OneDriveFilePickerDialog : Form { /// /// Instance of the OneDrive API that can be used to communicate with the cloud service /// private readonly OneDriveApi _oneDriveApi; /// /// Reference to the currently displayed folder on OneDrive for the My files tab /// private OneDriveItem CurrentMyOneDriveItem; /// /// Reference to the currently displayed folder on OneDrive for the Shared With Me tab /// private OneDriveItem CurrentSharedWithMeOneDriveItem; /// /// Reference to the currently displayed folder on OneDrive for the active tab /// public OneDriveItem CurrentOneDriveItem { get { return FilesTabControl.SelectedIndex == 0 ? CurrentMyOneDriveItem : CurrentSharedWithMeOneDriveItem; } } /// /// Gets or the filename in the textbox on the screen /// public string FileName { get { return FileNameTextBox.Text; } set { FileNameTextBox.Text = value; } } /// /// Gets or sets the text to explain the purpose of the dialog /// public string ExplanationText { get { return ExplanationLabel.Text; } set { ExplanationLabel.Text = value; } } /// /// Gets or sets if the user can enter a filename which does not exist yet on their OneDrive /// public bool AllowEnteringNewFileName { get { return FileNameTextBox.Enabled; } set { FileNameTextBox.Enabled = value; } } public OneDriveFilePickerDialog(OneDriveApi oneDriveApi) { InitializeComponent(); _oneDriveApi = oneDriveApi; var sharedWithMeDisabled = (oneDriveApi is OneDriveForBusinessO365Api); SharedWithMePicker.Visible = !sharedWithMeDisabled; SharedWithMeUpButton.Visible = !sharedWithMeDisabled; SharedWithMeNotAvailableLabel.Visible = sharedWithMeDisabled; } /// /// Loads the folders in the "My files" view /// /// Folder to display the child folders and items of. If not provided or NULL, the root folder will be used. public async Task LoadFolderItems(string parentItemId = null) { CloudLocationPicker.Items.Clear(); // Check if there is a parent folder to display its children of OneDriveItemCollection itemCollection; if (parentItemId == null) { // No parent folder, get the items under the root itemCollection = await _oneDriveApi.GetDriveRootChildren(); CloudLocationPath.Text = "/drive/root:"; UpButton.Enabled = false; GoToRootToolStripMenuItem.Enabled = false; CurrentMyOneDriveItem = await _oneDriveApi.GetDriveRoot(); } else { // Parent folder provided, get its children itemCollection = await _oneDriveApi.GetChildrenByFolderId(parentItemId); CurrentMyOneDriveItem = await _oneDriveApi.GetItemById(parentItemId); GoToRootToolStripMenuItem.Enabled = true; UpButton.Enabled = CurrentMyOneDriveItem.ParentReference != null; UpButton.Tag = CurrentMyOneDriveItem.ParentReference != null ? CurrentMyOneDriveItem.ParentReference.Id : null; CloudLocationPath.Text = CurrentMyOneDriveItem.ParentReference != null ? CurrentMyOneDriveItem.ParentReference.Path + "/" + CurrentMyOneDriveItem.Name : ""; } foreach (var oneDriveItem in itemCollection.Collection) { var oneDriveListViewItem = new ListViewItem { Text = oneDriveItem.Name, Tag = oneDriveItem.RemoteItem != null ? oneDriveItem.RemoteItem.Id : oneDriveItem.Id, ImageKey = oneDriveItem.Folder != null ? "Folder" : oneDriveItem.RemoteItem != null ? "RemoteFolder" : "File", Selected = oneDriveItem.Name.Equals(FileNameTextBox.Text, StringComparison.InvariantCultureIgnoreCase) }; if (oneDriveItem.Size > 0) { oneDriveListViewItem.ToolTipText += string.Format("Size: {0:n0} bytes", oneDriveItem.Size) + Environment.NewLine; } else if (oneDriveItem.RemoteItem != null && oneDriveItem.RemoteItem.Size > 0) { oneDriveListViewItem.ToolTipText += string.Format("Size: {0:n0} bytes", oneDriveItem.RemoteItem.Size) + Environment.NewLine; } if (oneDriveItem.CreatedDateTime != null) { oneDriveListViewItem.ToolTipText += string.Format("Created: {0:d MMMM yyyy HH:mm:ss}", oneDriveItem.CreatedDateTime) + Environment.NewLine; } if (oneDriveItem.LastModifiedDateTime != null) { oneDriveListViewItem.ToolTipText += string.Format("Last modified: {0:d MMMM yyyy HH:mm:ss}", oneDriveItem.LastModifiedDateTime) + Environment.NewLine; } CloudLocationPicker.Items.Add(oneDriveListViewItem); } // Define if the OK button should be enabled FileNameTextBox_TextChanged(null, null); } /// /// Loads the items shared with the user in the "Shared with me" overview /// /// OneDriveItem to display the child folders and items of. If not provided or NULL, the root folder will be used. public async Task LoadSharedWithMeItems(OneDriveItem parentItem = null) { SharedWithMePicker.Items.Clear(); if (parentItem == null) { // Get the root of the shared with me items var itemCollection = await _oneDriveApi.GetSharedWithMe(); var sortedItemCollection = itemCollection.Collection.OrderBy(i => i.Name).OrderBy(i => i.Folder == null).OrderBy(i => i.RemoteItem.Folder == null); SharedWithMeUpButton.Enabled = false; CurrentSharedWithMeOneDriveItem = null; GoToSharedWithMeRootTtoolStripMenuItem.Enabled = false; SharedWithMePath.Text = string.Empty; foreach (var oneDriveItem in sortedItemCollection) { var oneDriveListViewItem = new ListViewItem { Text = oneDriveItem.Name, Tag = oneDriveItem, ImageKey = (oneDriveItem.RemoteItem != null && oneDriveItem.RemoteItem.Folder != null) || oneDriveItem.Folder != null ? "RemoteFolder" : "File", Selected = oneDriveItem.Name.Equals(FileNameTextBox.Text, StringComparison.InvariantCultureIgnoreCase) }; OneDriveIdentity sharedInfo = null; if (oneDriveItem.RemoteItem != null && oneDriveItem.RemoteItem.Shared != null && oneDriveItem.RemoteItem.Shared.Owner != null && oneDriveItem.RemoteItem.Shared.Owner.User != null) { sharedInfo = oneDriveItem.RemoteItem.Shared.Owner.User; } else if (oneDriveItem.Shared != null && oneDriveItem.Shared.Owner != null && oneDriveItem.Shared.Owner.User != null) { sharedInfo = oneDriveItem.Shared.Owner.User; } if (sharedInfo != null) { oneDriveListViewItem.ToolTipText = string.Format("Shared by {0} ({1})", sharedInfo.DisplayName, sharedInfo.Id); } else { oneDriveListViewItem.ToolTipText = "Shared by information not available"; } if (oneDriveItem.Size > 0) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Size: {0:n0} bytes", oneDriveItem.Size); } else if(oneDriveItem.RemoteItem != null && oneDriveItem.RemoteItem.Size > 0) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Size: {0:n0} bytes", oneDriveItem.RemoteItem.Size); } if (oneDriveItem.CreatedDateTime != null) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Created: {0:d MMMM yyyy HH:mm:ss}", oneDriveItem.CreatedDateTime); } if (oneDriveItem.LastModifiedDateTime != null) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Last modified: {0:d MMMM yyyy HH:mm:ss}", oneDriveItem.LastModifiedDateTime); } SharedWithMePicker.Items.Add(oneDriveListViewItem); } } else { // Parent folder provided, get its children var itemCollection = await _oneDriveApi.GetAllChildrenFromDriveByFolderId(parentItem.RemoteItem != null ? parentItem.RemoteItem.ParentReference.DriveId : parentItem.ParentReference.DriveId, parentItem.RemoteItem != null ? parentItem.RemoteItem.ParentReference != null ? string.IsNullOrEmpty(parentItem.RemoteItem.ParentReference.Id) || parentItem.RemoteItem.Folder != null ? parentItem.RemoteItem.Id : parentItem.RemoteItem.ParentReference.Id : parentItem.RemoteItem.Id : parentItem.Id); SharedWithMeUpButton.Tag = parentItem.Name == "root" || parentItem.ParentReference == null || parentItem.ParentReference.Id == null || (parentItem.ParentReference.Path != null && parentItem.ParentReference.Path.EndsWith("root:")) ? null : await _oneDriveApi.GetItemFromDriveById(parentItem.ParentReference.Id, parentItem.ParentReference.DriveId); CurrentSharedWithMeOneDriveItem = parentItem; GoToSharedWithMeRootTtoolStripMenuItem.Enabled = true; SharedWithMeUpButton.Enabled = true; if (CurrentSharedWithMeOneDriveItem.ParentReference != null && CurrentSharedWithMeOneDriveItem.ParentReference.Path != null) { var rootLocation = CurrentSharedWithMeOneDriveItem.ParentReference.Path.IndexOf("root:"); SharedWithMePath.Text = ((rootLocation == -1 ? CurrentSharedWithMeOneDriveItem.ParentReference.Path : CurrentSharedWithMeOneDriveItem.ParentReference.Path.Remove(0, rootLocation + 5).TrimStart(new[] { '/' })) + "/" + CurrentSharedWithMeOneDriveItem.Name).TrimStart(new[] { '/' }); } else { SharedWithMePath.Text = CurrentSharedWithMeOneDriveItem.Name; } foreach (var oneDriveItem in itemCollection) { var oneDriveListViewItem = new ListViewItem { Text = oneDriveItem.Name, Tag = oneDriveItem, ImageKey = oneDriveItem.Folder != null ? "RemoteFolder" : "File", Selected = oneDriveItem.Name.Equals(FileNameTextBox.Text, StringComparison.InvariantCultureIgnoreCase) }; OneDriveIdentity sharedInfo = null; if (oneDriveItem.RemoteItem != null && oneDriveItem.RemoteItem.Shared != null && oneDriveItem.RemoteItem.Shared.Owner != null && oneDriveItem.RemoteItem.Shared.Owner.User != null) { sharedInfo = oneDriveItem.RemoteItem.Shared.Owner.User; } else if (oneDriveItem.Shared != null && oneDriveItem.Shared.Owner != null && oneDriveItem.Shared.Owner.User != null) { sharedInfo = oneDriveItem.Shared.Owner.User; } if (sharedInfo != null) { oneDriveListViewItem.ToolTipText = string.Format("Shared by {0} ({1})", sharedInfo.DisplayName, sharedInfo.Id); } else { oneDriveListViewItem.ToolTipText = "Shared by information not available"; } if (oneDriveItem.Size > 0) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Size: {0} bytes", oneDriveItem.Size); } else if (oneDriveItem.RemoteItem != null && oneDriveItem.RemoteItem.Size > 0) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Size: {0} bytes", oneDriveItem.RemoteItem.Size); } if (oneDriveItem.CreatedDateTime != null) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Created: {0:d MMMM yyyy HH:mm:ss}", oneDriveItem.CreatedDateTime); } if (oneDriveItem.LastModifiedDateTime != null) { oneDriveListViewItem.ToolTipText += Environment.NewLine + string.Format("Last modified: {0:d MMMM yyyy HH:mm:ss}", oneDriveItem.LastModifiedDateTime); } SharedWithMePicker.Items.Add(oneDriveListViewItem); } } // Define if the OK button should be enabled FileNameTextBox_TextChanged(null, null); } private async void CloudLocationPicker_DoubleClick(object sender, EventArgs e) { if (CloudLocationPicker.SelectedItems.Count == 0) return; var selectedItem = CloudLocationPicker.SelectedItems[0]; if (selectedItem.ImageKey == "File" && OKButton.Enabled) { OKButton_Click(sender, e); return; } await LoadFolderItems(selectedItem.Tag.ToString()); } private async void UpButton_Click(object sender, EventArgs e) { var childItem = UpButton.Tag as string; await LoadFolderItems(childItem); } private async void RefreshToolStripMenuItem_Click(object sender, EventArgs e) { await LoadFolderItems(CurrentMyOneDriveItem != null ? CurrentMyOneDriveItem.Id : null); } private void OneDriveFileSaveAsDialog_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Back && UpButton.Enabled) { UpButton_Click(sender, null); } } private void OKButton_Click(object sender, EventArgs e) { DialogResult = DialogResult.OK; Close(); } private void CloudLocationPicker_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { if (CloudLocationPicker.SelectedItems.Count == 0) return; var selectedItem = CloudLocationPicker.SelectedItems[0]; if (selectedItem.ImageKey == "File") { FileNameTextBox.Text = selectedItem.Text; } } private async void GoToRootToolStripMenuItem_Click(object sender, EventArgs e) { await LoadFolderItems(); } private void ListViewContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e) { renameToolStripMenuItem.Enabled = deleteToolStripMenuItem.Enabled = CloudLocationPicker.SelectedItems.Count > 0; } private void OneDriveFileSaveAsDialog_Load(object sender, EventArgs e) { } private async void NewFolderToolStripMenuItem_Click(object sender, EventArgs e) { var newFolderDialog = new OneDriveRequestInputDialog { FormTitle = "Create new folder" }; newFolderDialog.ShowDialog(this); if (newFolderDialog.DialogResult != DialogResult.OK) return; try { var newFolderItem = await _oneDriveApi.CreateFolder(CurrentMyOneDriveItem, newFolderDialog.InputValue); MessageBox.Show("Folder has been created", "New Folder", MessageBoxButtons.OK, MessageBoxIcon.Information); await LoadFolderItems(newFolderItem.Id); } catch (Exception) { MessageBox.Show("Folder could not be created", "New Folder", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } private async void DeleteToolStripMenuItem_Click(object sender, EventArgs e) { var confirm = MessageBox.Show("Are you sure you want to delete the selected item?", "Confirm deletion", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (confirm != DialogResult.Yes) return; if (CloudLocationPicker.SelectedItems.Count == 0) return; if (CloudLocationPicker.SelectedItems[0].Tag == null) return; try { var itemToDelete = await _oneDriveApi.GetItemById(CloudLocationPicker.SelectedItems[0].Tag.ToString()); await _oneDriveApi.Delete(itemToDelete); MessageBox.Show("Item has been deleted", "Delete item", MessageBoxButtons.OK, MessageBoxIcon.Information); await LoadFolderItems(CurrentMyOneDriveItem.Id); } catch (Exception) { MessageBox.Show("Item could not be deleted", "Delete item", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } private void FileNameTextBox_TextChanged(object sender, EventArgs e) { OKButton.Enabled = CurrentOneDriveItem != null && !string.IsNullOrEmpty(FileNameTextBox.Text); } private async void RenameToolStripMenuItem_Click(object sender, EventArgs e) { if (CloudLocationPicker.SelectedItems.Count == 0) return; if (CloudLocationPicker.SelectedItems[0].Tag == null) return; var renameItemDialog = new OneDriveRequestInputDialog { FormTitle = "Enter new name", InputValue = CloudLocationPicker.SelectedItems[0].Text }; renameItemDialog.ShowDialog(this); if (renameItemDialog.DialogResult != DialogResult.OK) return; try { var oneDriveItemToRename = await _oneDriveApi.GetItemById(CloudLocationPicker.SelectedItems[0].Tag.ToString()); var operationSuccessful = await _oneDriveApi.Rename(oneDriveItemToRename, renameItemDialog.InputValue); if (operationSuccessful) { MessageBox.Show("Item has been renamed", "Rename item", MessageBoxButtons.OK, MessageBoxIcon.Information); await LoadFolderItems(CurrentMyOneDriveItem != null ? CurrentMyOneDriveItem.Id : null); } else { MessageBox.Show("Item could not be renamed", "Rename item", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } catch (Exception) { MessageBox.Show("Item could not be renamed", "Rename item", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } private void GroupToolStripMenuItem_Click(object sender, EventArgs e) { UpButton_Click(sender, e); } private void UpButton_EnabledChanged(object sender, EventArgs e) { goupToolStripMenuItem.Enabled = UpButton.Enabled; } private void CloudLocationPicker_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { CloudLocationPicker_DoubleClick(sender, e); } } private void FileNameTextBox_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter && OKButton.Enabled) { OKButton_Click(sender, e); } } private async void FilesTabControl_SelectedIndexChanged(object sender, EventArgs e) { // When switching to the "Shared with me" tab and the items have not been loaded yet, load them now if(FilesTabControl.SelectedIndex == 1 && SharedWithMePicker.Items.Count == 0) { await LoadSharedWithMeItems(); } // Ensure the OK button is properly enabled/disabled when switching tabs switch (FilesTabControl.SelectedIndex) { case 1: SharedWithMePicker_ItemSelectionChanged(sender, null); break; case 0: CloudLocationPicker_ItemSelectionChanged(sender, null); break; } } private async void RefreshSharedWithMeFilesToolStripMenuItem_Click(object sender, EventArgs e) { await LoadSharedWithMeItems(CurrentSharedWithMeOneDriveItem); } private async void SharedWithMePicker_DoubleClick(object sender, EventArgs e) { if (SharedWithMePicker.SelectedItems.Count == 0) return; var selectedItem = SharedWithMePicker.SelectedItems[0]; if (selectedItem.ImageKey == "File" && OKButton.Enabled) { OKButton_Click(sender, e); return; } await LoadSharedWithMeItems(selectedItem.Tag as OneDriveItem); } private void SharedWithMePicker_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { SharedWithMePicker_DoubleClick(sender, e); } } private async void SharedWithMeUpButton_Click(object sender, EventArgs e) { var childItem = SharedWithMeUpButton.Tag as OneDriveItem; await LoadSharedWithMeItems(childItem); } private async void GoToSharedWithMeRootTtoolStripMenuItem_Click(object sender, EventArgs e) { await LoadSharedWithMeItems(); } private async void GoUpSharedWithMeToolStripMenuItem_Click(object sender, EventArgs e) { } private void SharedWithMeContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e) { } private void SharedWithMePicker_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { if (SharedWithMePicker.SelectedItems.Count == 0) { // If we're not in a folder yet, disable the button as a new file can't be created in Shared With Me outside a folder if(string.IsNullOrEmpty(SharedWithMePath.Text)) { OKButton.Enabled = false; } return; } var selectedItem = SharedWithMePicker.SelectedItems[0]; if (selectedItem.ImageKey == "File") { FileNameTextBox.Text = selectedItem.Text; OKButton.Enabled = true; CurrentSharedWithMeOneDriveItem = selectedItem.Tag as OneDriveItem; } } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveFilePickerDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 132, 17 17, 17 AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAD8 yQAAAk1TRnQBSQFMAgEBAwEAAbABAAGwAQABQAEAAUABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgEAAEB AgABQAMAAQEBAAEgBwABAf8A/wD/AP8AMAAEAQECAwEBAgMBAQIDAQECAwEBAgMBAQIDAQECAwEBAgMB AQIDAQECAwEBAgMBAQIDAQECAwEBAgMBAQIDAQECAwEBAgMBAQIDAQECAwEBAgMBAQIDAQECAwEBAgMB AQIDAQECAwEBAgMBAQIDAQECAwEBAgMBAQIDAQECAwEBAgMAAQGsAAMCAQMDBAEFAwQBBQMCAQMDAAEB 7AADAgEDAwQBBQMEAQUDAgEDAwABAf8AtQADAgEDAwcBCgMMARADDgETAw8BFAMPARQDDwEUAw8BFAMP ARQDDwEUAw8BFAMPARQDDwEUAw8BFAMPARQDDwEUAw8BFAMPARQDDwEUAw8BFAMPARQDDwEUAw8BFAMP ARQDDwEUAw8BFAMPARQDDwEUAw8BFAMPARQDDwEUAw8BFAMOARMDDQESAwoBDQMEAQYDAAEBnwABAQMD AQQDCgENAw8BFAMRARcDDQERAwUBBwMBAQLjAAEBAwMBBAMKAQ0DDwEUAxEBFwMNAREDBQEHAwEBAv8A sAABAQMHAQkDFwEgAyQBNQMpAT4DKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFA AyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFA AyoBQAMqAUADKgFAAykBPwMoATwDIQEwAxQBHAMIAQsDAQEClwABAQMDAQQDCwEPAzMBUwFaAbkB1wH/ AR0BUgFpAf8DHgErAxIBGAMIAQsDAwEEAwABATsABAEEAgEDAwQBBgMEAQYDAwEEAwEBAgMAAQEDAAEB ewABAQMDAQQDCwEPAzMBUwFaAbkB1wH/AR0BUgFpAf8DHgErAxIBGAMIAQsDAwEEAwABATsABAEEAgED AwQBBgMEAQYDAwEEAwEBAgMAAQEDAAEB/wBMAAEBAwwBEAMkATYDNwFbBP8D9wH/A/gB/wP4Af8D+AH/ A/kB/wP6Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/QH/A/0B/wP9Af8D/gH/ A/4B/wP+Af8D/gH/A/0B/wP7Af8D+gH/A/gB/wP1Af8D8gH/A+8B/wPsAf8B6ALnAf8DbwH1AxkBIwMK AQ0DAgEDjwABAQMEAQUDCgEOA0IBdQFZAbgB1wH/AVkBuAHWAf8BNwGKAaUB/wMvAUkDJAE1AxcBIAML AQ8DBAEGAwABAScABAEEAgEDAwQBBgMIAQsDEAEWAxkBIwMeASsDFgEeAw8BFAMKAQ4DCAELAwcBCQME AQUDAAEBbwABAQMEAQUDCgEOA0IBdQFZAbgB1wH/AVkBuAHWAf8BNwGKAaUB/wMvAUkDJAE1AxcBIAML AQ8DBAEGAwABAScABAEEAgEDAwQBBgMIAQsDEAEWAxkBIwMeASsDFgEeAw8BFAMKAQ4DCAELAwcBCQME AQUDAAEB/wBBAAMBAQIDDgETAykBPwM9AWoE/wP2Af8D9wH/A/gB/wP4Af8D+QH/A/oB/wP6Af8D+gH/ A/sB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP+Af8D/gH/A/4B/wP+Af8D/QH/ A/sB/wP5Af8D9gH/A/MB/wPxAf8D7gH/AesC6gH/A+cB/wPiAf8DoAH+Ax0BKQMNAREDAwEEhwABAQME AQUDCwEPA1IBowFZAbgB1gH/AVkBuAHWAf8BWQG4AdYB/wE5AY8BqQH/AzgBXgMyAVEDKAE9AxwBJwMQ ARUDBwEJAwIBAxMABAEEAgEDAwQBBgMIAQsDDQERAxQBHAMfBCwBRANSAakBUwGwAc4B/wFaAbkB2AH/ AVoBuQHXAf8CWAFbAcsDHAEoAxoBJAMWAR4DDQESAwUBBwMBAQJnAAEBAwQBBQMLAQ8DUgGjAVkBuAHW Af8BWQG4AdYB/wFZAbgB1gH/ATkBjwGpAf8DOAFeAzIBUQMoAT0DHAEnAxABFQMHAQkDAgEDEwAEAQQC AQMDBAEGAwgBCwMNAREDFAEcAx8ELAFEA1IBqQFTAbABzgH/AVoBuQHYAf8BWgG5AdcB/wJYAVsBywMc ASgDGgEkAxYBHgMNARIDBQEHAwEBAv8APQADAQECAw8BFAMqAUADPgFsBP8C9wH2Af8D9wH/A/gB/wP4 Af8D+QH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP+ Af8D/gH/A/4B/wP+Af8D/AH/A/sB/wP5Af8D9gH/A/MB/wPwAf8D7QH/A+oB/wPkAf8D3QH/A9cB/wPK Af8DIwEzAw8BFAMEAQUDAAEBewABAQMEAQUDDAEQA1wBzQFaAbkB1wH/AVoBuQHXAf8BWQG5AdcB/wFZ AbkB1wH/ATkBjwGpAf8DOgFiAzkBXwM0AVUDKwFDAyABLwMTARoDCQEMAwQBBQMCBAMEBAEGAwcBCgMM ARADEgEZAxsEJgE5A1ABmgFJAagBrgH9AVoBuQHYAf8BWQG4AdYB/wFZAbgB1gH/AVkBuAHWAf8BWQG4 AdYB/wFZAbgB1gH/AUkBoQG+Af8DLAFEAyoBQAMmATkDHAEoAw4BEwMFAQcDAQECXwABAQMEAQUDDAEQ A1wBzQFaAbkB1wH/AVoBuQHXAf8BWQG5AdcB/wFZAbkB1wH/ATkBjwGpAf8DOgFiAzkBXwM0AVUDKwFD AyABLwMTARoDCQEMAwQBBQMCBAMEBAEGAwcBCgMMARADEgEZAxsEJgE5A1ABmgFJAagBrgH9AVoBuQHY Af8BWQG4AdYB/wFZAbgB1gH/AVkBuAHWAf8BWQG4AdYB/wFZAbgB1gH/AUkBoQG+Af8DLAFEAyoBQAMm ATkDHAEoAw4BEwMFAQcDAQEC/wA5AAMBAQIDDwEUAyoBQAM+AWwE/wL3AfYB/wP3Af8D+AH/A/gB/wP5 Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/A/0B/wP9Af8D/QH/A/4B/wP+ Af8D/gH/A/4B/wP8Af8D+wH/A/kB/wP2Af8D8wH/A/AB/wPtAf8D6gH/A+IB/wPVAf8DywH/A9cB/wPK Af8DLwFJAxEBFwMFAQcDAAEBcwABAQMEAQUDCwEPAV0BYgFkAe0BWgG6AdgB/wFaAboB2AH/AVoBugHY Af8BWgG5AdgB/wFaAbkB2AH/ATkBjwGpAf8DOgFhAzoBYgM5AV8DNQFXAy8BSQMkATUDGAEiAw8BFAMN ARIDEAEWAxgEIQEwAUwCTQGRAWsBfwGEAfcBWgG6AdkB/wFZAbkB1wH/AVkBuQHXAf8BWQG4AdYB/wFZ AbgB1gH/AVkBuAHWAf8BWQG4AdYB/wFZAbgB1gH/AVkBuAHWAf8BWQG4AdYB/wFZAbgB1gH/AVoBuQHX Af8BWgG6AdgB/wFYAmEB5gMoATwDGwEmAw0BEgMFAQcDAQECVwABAQMEAQUDCwEPAV0BYgFkAe0BWgG6 AdgB/wFaAboB2AH/AVoBugHYAf8BWgG5AdgB/wFaAbkB2AH/ATkBjwGpAf8DOgFhAzoBYgM5AV8DNQFX Ay8BSQMkATUDGAEiAw8BFAMNARIDEAEWAxgEIQEwAUwCTQGRAWsBfwGEAfcBWgG6AdkB/wFZAbkB1wH/ AVkBuQHXAf8BWQG4AdYB/wFZAbgB1gH/AVkBuAHWAf8BWQG4AdYB/wFZAbgB1gH/AVkBuAHWAf8BWQG4 AdYB/wFZAbgB1gH/AVoBuQHXAf8BWgG6AdgB/wFYAmEB5gMoATwDGwEmAw0BEgMFAQcDAQEC/wA1AAMB AQIDDwEUAyoBQAM+AWwE/wL3AfYB/wP3Af8D+AH/A/gB/wP5Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7 Af8D+wH/A/wB/wP8Af8D/AH/A/0B/wP9Af8D/QH/A/4B/wP+Af8D/gH/A/4B/wP8Af8D+wH/A/kB/wP2 Af8D8wH/A/AB/wPtAf8D6gH/A+IB/wPRAf8DvgH/A90B/wPWAf8DyQH/Az8BbAMTARoDBgEIAwABAWsA AQEDBAEGAwwBEAFbAbwB2gH/AVsBuwHZAf8BWwG7AdkB/wFbAboB2QH/AVsBugHZAf8BWwG6AdkB/wFb AboB2AH/ATkBjgGpAf8DOAFeAzkBXwM5AV8DOAFcAzUBVwMwAU0DKQE/AyYBOANKAYoBXQFhAWUB8AFb AboB2QH/AVoBuQHXAf8BWgG5AdcB/wFaAbkB1wH/AVoBuQHXAf8BWgG5AdcB/wFaAbkB1wH/AVoBuQHX Af8BWgG5AdcB/wFaAbkB1wH/AVoBuQHXAf8BWgG5AdcB/wFaAbkB1wH/AVoBuQHXAf8BWgG5AdgB/wFZ AbkB2AH/AWkBtAHHAf8BXAG7AdoB/wMtAUYDJgE4AxkBIwMNAREDBQEHAwEBAk8AAQEDBAEGAwwBEAFb AbwB2gH/AVsBuwHZAf8BWwG7AdkB/wFbAboB2QH/AVsBugHZAf8BWwG6AdkB/wFbAboB2AH/ATkBjgGp Af8DOAFeAzkBXwM5AV8DOAFcAzUBVwMwAU0DKQE/AyYBOANKAYoBXQFhAWUB8AFbAboB2QH/AVoBuQHX Af8BWgG5AdcB/wFaAbkB1wH/AVoBuQHXAf8BWgG5AdcB/wFaAbkB1wH/AVoBuQHXAf8BWgG5AdcB/wFa AbkB1wH/AVoBuQHXAf8BWgG5AdcB/wFaAbkB1wH/AVoBuQHXAf8BWgG5AdgB/wFZAbkB2AH/AWkBtAHH Af8BXAG7AdoB/wMtAUYDJgE4AxkBIwMNAREDBQEHAwEBAv8AMQADAQECAw8BFAMqAUADPgFsBP8D9gH/ A/cB/wP4Af8D+AH/A/kB/wP6Af8D+gH/A/oB/wP7Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/QH/ A/0B/wP9Af8D/gH/A/4B/wP+Af8D/gH/A/0B/wP7Af8D+QH/A/YB/wPzAf8D8AH/A+4B/wPqAf8B5QLk Af8C1QHUAf8DuQH/A6sB/wPjAf8D1gH/A8kB/wNKAY0DFQEdAwcBCQMBAQJgAAMBAQIDBAEGAwsBDwFc Ab0B3AH/AVwBvAHaAf8BXAG8AdoB/wFcAbwB2gH/AVwBuwHaAf8BWwG7AdoB/wFbAbsB2gH/AVsBuwHa Af8BOgGOAakB/wM2AVkDNwFaAzcBWgM1AVgBUAJSAaMBZwF9AYMB9wFLAaUBwgH/AUwBpwHEAf8BWgG6 AdgB/wFaAboB2AH/AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/ AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/AVoBugHYAf8BWgG6 AdgB/wHAAaQBigH/Ae0BlgFHAf8BrwGoAZsB/wFbAbsB2gH/Ay0BRQMpAT8DIQExAxYBHwMLAQ8DBAEG AwEBAkQAAwEBAgMEAQYDCwEPAVwBvQHcAf8BXAG8AdoB/wFcAbwB2gH/AVwBvAHaAf8BXAG7AdoB/wFb AbsB2gH/AVsBuwHaAf8BWwG7AdoB/wE6AY4BqQH/AzYBWQM3AVoDNwFaAzUBWAFQAlIBowFnAX0BgwH3 AUsBpQHCAf8BTAGnAcQB/wFaAboB2AH/AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/AVoBugHYAf8BWgG6 AdgB/wFaAboB2AH/AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/ AVoBugHYAf8BWgG6AdgB/wFaAboB2AH/AcABpAGKAf8B7QGWAUcB/wGvAagBmwH/AVsBuwHaAf8DLQFF AykBPwMhATEDFgEfAwsBDwMEAQYDAQEC/wAtAAMBAQIDDwEUAyoBQAM+AWwE/wP2Af8D9wH/A/gB/wP4 Af8D+QH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP+ Af8D/gH/A/4B/wP+Af8D/QH/A/sB/wP6Af8D9wH/A/QB/wPxAf8D7gH/A+sB/wPnAf8B2gLZAf8CvgG9 Af8DoQH/A/AB/wPjAf8D1QH/A8gB/wNSAacDFgEfAwcBCgMBAQJYAAMBAQIDBQEHAxQBGwFeAb4B3AH/ AV0BvQHcAf8BXQG9AdwB/wFcAb0B2wH/AVwBvQHbAf8BXAG8AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8 AdsB/wE6AY4BqAH/AVACUQGfAWcBfQGDAfcBSAGhAb4B/wE/AZYBsgH/ATkBjgGpAf8BNQGJAaQB/wE1 AYkBpAH/AUEBmAG0Af8BWwG7AdkB/wFbAbsB2QH/AVsBuwHZAf8BWwG7AdkB/wFbAbsB2QH/AVsBugHZ Af8BWwG6AdkB/wFbAboB2QH/AVsBugHZAf8BWwG6AdkB/wFbAboB2QH/AVsBugHZAf8BWwG6AdgB/wFb AboB2AH/AVsBugHYAf8BWwG6AdgB/wHEAakBkQH/AfUBngFPAf8BswGtAZ8B/wFbAbsB2QH/AykBPwMo AT0DJQE3Ax0BKgMUARsDCgENAwQBBQMBAQI8AAMBAQIDBQEHAxQBGwFeAb4B3AH/AV0BvQHcAf8BXQG9 AdwB/wFcAb0B2wH/AVwBvQHbAf8BXAG8AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8AdsB/wE6AY4BqAH/ AVACUQGfAWcBfQGDAfcBSAGhAb4B/wE/AZYBsgH/ATkBjgGpAf8BNQGJAaQB/wE1AYkBpAH/AUEBmAG0 Af8BWwG7AdkB/wFbAbsB2QH/AVsBuwHZAf8BWwG7AdkB/wFbAbsB2QH/AVsBugHZAf8BWwG6AdkB/wFb AboB2QH/AVsBugHZAf8BWwG6AdkB/wFbAboB2QH/AVsBugHZAf8BWwG6AdgB/wFbAboB2AH/AVsBugHY Af8BWwG6AdgB/wHEAakBkQH/AfUBngFPAf8BswGtAZ8B/wFbAbsB2QH/AykBPwMoAT0DJQE3Ax0BKgMU ARsDCgENAwQBBQMBAQL/ACkAAwEBAgMPARQDKgFAAz4BbAT/A/YB/wP3Af8D+AH/A/gB/wP5Af8D+gH/ A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/A/0B/wP9Af8D/QH/A/4B/wP+Af8D/gH/ A/4B/wP9Af8D+wH/A/oB/wP3Af8D9AH/A/IB/wPvAf8D7AH/A+gB/wLfAd4B/wPGAf8BpAKjAf8DyQH/ A+8B/wPiAf8D1QH/A8gB/wNYAbsDFgEfAwcBCQMAAQFQAAMBAQIDBQEHAyQBNQFfAb8B3gH/AV4BvgHd Af8BXQG+Ad0B/wFdAb4B3QH/AV0BvgHdAf8BXQG+AdwB/wFdAb0B3AH/AV0BvQHcAf8BXQG9AdwB/wFd Ab0B3AH/AToBjgGpAf8BPAGRAa0B/wE1AYgBowH/ATIBhAGfAf8BMAGCAZ0B/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wE8AZIBrgH/AVsBuwHaAf8BWwG7AdoB/wFbAbsB2gH/AVsBuwHaAf8BWwG7AdoB/wFb AbsB2QH/AVsBuwHZAf8BWwG7AdkB/wFbAbsB2QH/AVsBuwHZAf8BWwG7AdkB/wFbAbsB2QH/AVsBuwHZ Af8BWwG7AdkB/wFbAbsB2QH/AVsBuwHZAf8BygGtAZUB/wH9Ab8BlQH/AbgB0AHZAf8BXAG8AdoB/wMl ATcDJAE2AyMBNAMfAS0DGgEkAxEBFwMIAQsDBAEFAwEBAjQAAwEBAgMFAQcDJAE1AV8BvwHeAf8BXgG+ Ad0B/wFdAb4B3QH/AV0BvgHdAf8BXQG+Ad0B/wFdAb4B3AH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/ AV0BvQHcAf8BOgGOAakB/wE8AZEBrQH/ATUBiAGjAf8BMgGEAZ8B/wEwAYIBnQH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/ATwBkgGuAf8BWwG7AdoB/wFbAbsB2gH/AVsBuwHaAf8BWwG7AdoB/wFbAbsB2gH/ AVsBuwHZAf8BWwG7AdkB/wFbAbsB2QH/AVsBuwHZAf8BWwG7AdkB/wFbAbsB2QH/AVsBuwHZAf8BWwG7 AdkB/wFbAbsB2QH/AVsBuwHZAf8BWwG7AdkB/wHKAa0BlQH/Af0BvwGVAf8BuAHQAdkB/wFcAbwB2gH/ AyUBNwMkATYDIwE0Ax8BLQMaASQDEQEXAwgBCwMEAQUDAQEC/wAlAAMBAQIDDwEUAyoBQAM+AWwE/wP2 Af8D9wH/A/gB/wP4Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP9 Af8D/QH/A/0B/wP+Af8D/gH/A/4B/wP+Af8D/QH/A/wB/wP6Af8D+AH/A/UB/wPyAf8D8AH/A+0B/wPq Af8D4gH/As4BzQH/A6sB/wOSAf8D/AH/A+8B/wPiAf8D1QH/A8gB/wNSAacDFgEeAwcBCQMAAQFMAAMD AQQDLwFKAWABwAHgAf8BXgHAAd8B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHe Af8BXgG+Ad0B/wFeAb4B3QH/AV4BvgHdAf8BXgG+Ad0B/wE6AY8BqQH/AS8BggGdAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOwGRAawB/wFcAbwB2gH/AVwBvAHa Af8BXAG8AdoB/wFcAbwB2gH/AVwBvAHaAf8BXAG8AdoB/wFcAbwB2gH/AVwBvAHaAf8BXAG8AdoB/wFc AbwB2gH/AVwBvAHaAf8BXAG8AdoB/wFcAbsB2gH/AVwBuwHaAf8BXAG7AdoB/wFbAbsB2gH/AcsB5gHx Bf8BuAHdAe0B/wFcAbwB2gH/AyABLgMgAS4DHwEsAx0BKQMaASUDFQEdAw4BEwMHAQkDAwEEAwEBAjAA AwMBBAMvAUoBYAHAAeAB/wFeAcAB3wH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/ Ad4B/wFeAb4B3QH/AV4BvgHdAf8BXgG+Ad0B/wFeAb4B3QH/AToBjwGpAf8BLwGCAZ0B/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE7AZEBrAH/AVwBvAHaAf8BXAG8 AdoB/wFcAbwB2gH/AVwBvAHaAf8BXAG8AdoB/wFcAbwB2gH/AVwBvAHaAf8BXAG8AdoB/wFcAbwB2gH/ AVwBvAHaAf8BXAG8AdoB/wFcAbwB2gH/AVwBuwHaAf8BXAG7AdoB/wFcAbsB2gH/AVsBuwHaAf8BywHm AfEF/wG4Ad0B7QH/AVwBvAHaAf8DIAEuAyABLgMfASwDHQEpAxoBJQMVAR0DDgETAwcBCQMDAQQDAQEC /wAhAAMBAQIDDwEUAyoBQAM+AWwE/wP2Af8D9wH/AvgB9wH/A/gB/wP5Af8D+QH/A/oB/wP6Af8D+gH/ A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/A/0B/wP9Af8D/QH/A/0B/wP+Af8D/gH/A/4B/wP+Af8D/AH/ A/sB/wP5Af8D9gH/A/MB/wPxAf8D7gH/A+sB/wPmAf8B1QLUAf8DsgH/A5MF/wP7Af8D7gH/A+EB/wPU Af8DxwH/A0sBjgMUARwDBgEIAwABAUgAAwQBBQFfAcAB4AH/AWABwgHhAf8BYAHBAeEB/wFfAcAB4AH/ AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAb8B3wH/AV4BvwHeAf8BXgG/ Ad4B/wE6AY8BqQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BOwGRAawB/wFcAb0B2wH/AVwBvAHbAf8BXAG8AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8 AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8AdsB/wFcAbwB2wH/ AVwBvAHbAf8BXAG8AdsB/wFcAbwB2gH/AcoB5gHxBf8BuQHeAe0B/wFcAbwB2gH/AxoBJQMaASUDGgEl AxkBIwMYASEDFQEdAxEBFwMLAQ8DBgEIAwIBAwMAAQEsAAMEAQUBXwHAAeAB/wFgAcIB4QH/AWABwQHh Af8BXwHAAeAB/wFfAcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwG/Ad8B/wFe Ab8B3gH/AV4BvwHeAf8BOgGPAakB/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/ATsBkQGsAf8BXAG9AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8AdsB/wFc AbwB2wH/AVwBvAHbAf8BXAG8AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8AdsB/wFcAbwB2wH/AVwBvAHb Af8BXAG8AdsB/wFcAbwB2wH/AVwBvAHbAf8BXAG8AdoB/wHKAeYB8QX/AbkB3gHtAf8BXAG8AdoB/wMa ASUDGgElAxoBJQMZASMDGAEhAxUBHQMRARcDCwEPAwYBCAMCAQMDAAEB/wAdAAMBAQIDDwEUAyoBQAM+ AWwE/wP2Af8D9wH/A/cB/wP4Af8D+QH/A/kB/wP6Af8D+gH/A/oB/wP7Af8D+wH/A/sB/wP8Af8D/AH/ A/wB/wP8Af8D/QH/A/0B/wP9Af8D/gH/A/4B/wP+Af8D/gH/A/0B/wP7Af8D+gH/A/cB/wP1Af8D8gH/ A+8B/wPtAf8D6QH/A9oB/wG5ArgB/wGYApcJ/wP7Af8D7gH/A+EB/wPUAf8DxwH/A0ABbgMSARkDBAEG AwABAUQAAwIBAwFhAcQB4wH/AWEBwwHiAf8BYQHDAeIB/wFgAcEB4QH/AWABwQHhAf8BYAHBAeEB/wFg AcEB4AH/AWABwQHgAf8BXwHBAeAB/wFfAcEB4AH/AV8BwAHgAf8BXwHAAeAB/wE6AY8BqQH/AS8BggGd Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOwGRAawB/wFd Ab0B3AH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/AV0BvQHc Af8BXQG9AdsB/wFdAb0B2wH/AV0BvQHbAf8BXAG9AdsB/wFcAb0B2wH/AVwBvQHbAf8BXAG9AdsB/wFc Ab0B2wH/AcoB5gHyBf8BuQHeAe0B/wFcAbwB2wH/AxcBIAMWAR8DFQEdAxQBHAMUARsDEgEZAxEBFwMN ARIDCgENAwUBBwMCAQMDAAEBKAADAgEDAWEBxAHjAf8BYQHDAeIB/wFhAcMB4gH/AWABwQHhAf8BYAHB AeEB/wFgAcEB4QH/AWABwQHgAf8BYAHBAeAB/wFfAcEB4AH/AV8BwQHgAf8BXwHAAeAB/wFfAcAB4AH/ AToBjwGpAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wE7AZEBrAH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/ AV0BvQHcAf8BXQG9AdwB/wFdAb0B2wH/AV0BvQHbAf8BXQG9AdsB/wFcAb0B2wH/AVwBvQHbAf8BXAG9 AdsB/wFcAb0B2wH/AVwBvQHbAf8BygHmAfIF/wG5Ad4B7QH/AVwBvAHbAf8DFwEgAV4CWwHQA1IBpgMk ATYDFAEbAxIBGQMRARcDDQESAwoBDQMFAQcDAgEDAwABAf8AGQADAQECAw8BFAMqAUADPgFsBP8D9gH/ A/cB/wP3Af8D+AH/A/kB/wP5Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/ A/0B/wP9Af8D/QH/A/4B/wP+Af8D/gH/A/4B/wP9Af8D/AH/A/oB/wP5Af8D9gH/A/MB/wPxAf8D7gH/ A+sB/wPeAf8DwAH/A54N/wP7Af8D7QH/A+AB/wPUAf8DxwH/AzEBTQMQARYDBAEFSAABYgHFAeUB/wFi AcQB5AH/AWIBxAHjAf8BYQHDAeIB/wFhAcIB4gH/AWEBwgHiAf8BYQHCAeIB/wFgAcIB4gH/AWABwgHh Af8BYAHCAeEB/wFgAcIB4QH/AWABwQHhAf8BOgGPAakB/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATsBkQGtAf8BXQG+Ad0B/wFdAb4B3QH/AV0BvgHd Af8BXQG+Ad0B/wFdAb4B3AH/AV0BvgHcAf8BXQG+AdwB/wFdAb4B3AH/AV0BvQHcAf8BXQG9AdwB/wFd Ab0B3AH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/AV0BvQHcAf8BXQG9AdwB/wHMAecB8gX/AbkB3gHu Af8BXQG9AdsB/wMSARkDEgEYAxEBFwMRARcDEAEVAw8BFAMOARMDDQESAwoBDgMHAQoDBAEGAwEBAgMA AQEoAAFiAcUB5QH/AWIBxAHkAf8BYgHEAeMB/wFhAcMB4gH/AWEBwgHiAf8BYQHCAeIB/wFhAcIB4gH/ AWABwgHiAf8BYAHCAeEB/wFgAcIB4QH/AWABwgHhAf8BYAHBAeEB/wE6AY8BqQH/AS8BggGdAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOwGRAa0B/wFdAb4B3QH/ AV0BvgHdAf8BXQG+Ad0B/wFdAb4B3QH/AV0BvgHcAf8BXQG+AdwB/wFdAb4B3AH/AV0BvgHcAf8BXQG9 AdwB/wFdAb0B3AH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/AV0BvQHcAf8BXQG9AdwB/wFdAb0B3AH/ AcwB5wHyBf8BuQHeAe4B/wFdAb0B2wH/AWICWAHpAf0BtwGRAf8B9gGkAWkB/wHcAUkBJgH/AW0BRgFC AfUDTgGYAw4BEwMNARIDCgEOAwcBCgMEAQYDAQECAwABAf8AFQADAQECAw8BFAMqAUADPgFsBP8D9gH/ AvcB9gH/A/cB/wP4Af8D+AH/A/kB/wP6Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8 Af8D/QH/A/0B/wP9Af8D/QH/A/4B/wP+Af8D/gH/A/4B/wP9Af8D+wH/A/oB/wP3Af8D9QH/A/IB/wPw Af8D7gH/A+UB/wPNAf8DsAH/AqABnwH/A5oB/wOWAf8BlwKWAf8BywLKAf8D7QH/A+AB/wPTAf8DxgH/ AyUBNwMOARMDAwEERAABYwHFAeYB/wFjAcUB5QH/AWMBxQHlAf8BYgHEAeQB/wFiAcQB4wH/AWIBwwHj Af8BYQHDAeMB/wFhAcMB4wH/AWEBwwHjAf8BYQHDAeMB/wFhAcMB4gH/AWEBwwHiAf8BOgGPAakB/wEv AYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATsBkQGt Af8BXgG+Ad0B/wFeAb4B3QH/AV4BvgHdAf8BXgG+Ad0B/wFeAb4B3QH/AV4BvgHdAf8BXgG+Ad0B/wFd Ab4B3QH/AV0BvgHdAf8BXQG+Ad0B/wFdAb4B3QH/AV0BvgHdAf8BXQG+Ad0B/wFdAb4B3QH/AV0BvgHd Af8BXQG+Ad0B/wHMAecB8wX/AbkB3wHuAf8BXQG9AdwB/wMOARMDDwEUAw0BEgMNARIDDQERAw0BEQML AQ8DCgEOAwoBDQMIAQsDBgEIAwMBBAMBAQIDAAEBJAABYwHFAeYB/wFjAcUB5QH/AWMBxQHlAf8BYgHE AeQB/wFiAcQB4wH/AWIBwwHjAf8BYQHDAeMB/wFhAcMB4wH/AWEBwwHjAf8BYQHDAeMB/wFhAcMB4gH/ AWEBwwHiAf8BOgGPAakB/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/ATsBkQGtAf8BXgG+Ad0B/wFeAb4B3QH/AV4BvgHdAf8BXgG+Ad0B/wFeAb4B3QH/ AV4BvgHdAf8BXgG+Ad0B/wFdAb4B3QH/AV0BvgHdAf8BXQG+Ad0B/wFdAb4B3QH/AV0BvgHdAf8BXQG+ Ad0B/wFdAb4B3QH/AV0BvgHdAf8BXQG+Ad0B/wHMAecB8wX/AbkB3wHuAf8BXQG9AdwB/wHcAUkBJgH/ Af0BtgGQAf8B/AGzAY4B/wH8Aa8BigH/AfwBrQGIAf8B8gGUAVkB/wHWATkBFgH/A1YBtgMaASQDCAEL AwYBCAMDAQQDAQECAwABAf8AEQADAQECAw8BFAMqAUADPgFsBP8D9gH/A/YB/wP3Af8D+AH/A/gB/wP5 Af8D+gH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP+ Af8D/gH/A/4B/wP+Af8D/QH/A/wB/wP7Af8D+QH/A/cB/wP0Af8D8gH/A/AB/wPqAf8D3QH/A80B/wPD Af8CvQG8Af8DtwH/A7EB/wOtAf8DrAH/ArcBtgH/A9sB/wPTAf8DxgH/AyABLgMMARADAgEDQAABYwHG AecB/wFkAcYB5gH/AWQBxgHmAf8BYwHFAeUB/wFjAcUB5QH/AWIBxQHlAf8BYgHEAeQB/wFiAcQB5AH/ AWIBxAHkAf8BYgHEAeQB/wFiAcQB5AH/AWIBxAHkAf8BOgGPAakB/wEvAYIBnQH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATsBkQGtAf8BXgG/Ad4B/wFeAb8B3gH/ AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/ Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvgHdAf8BXgG+Ad0B/wHLAecB8wX/ AboB3wHuAf8BXQG9AdwB/wMLAQ8DDAEQAwoBDgMKAQ4DCgENAwkBDAMJAQwDCQEMAwgBCwMHAQoDBgEI AwQBBgMCAQMDAAEBJAABYwHGAecB/wFkAcYB5gH/AWQBxgHmAf8BYwHFAeUB/wFjAcUB5QH/AWIBxQHl Af8BYgHEAeQB/wFiAcQB5AH/AWIBxAHkAf8BYgHEAeQB/wFiAcQB5AH/AWIBxAHkAf8BOgGPAakB/wEv AYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATsBkQGt Af8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFe Ab8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvgHd Af8BXgG+Ad0B/wHLAecB8wX/AboB3wHuAf8BXQG9AdwB/wHYAT0BGQH/AfIBmgFfAf8B/AG2AZEB/wH8 Aa8BigH/AfsBqwGGAf8B+wGoAYMB/wH7AaQBaQH/AfUBlgFbAf8BrwFAAT8B/QNDAXgDBgEIAwQBBgMC AQMDAAEB/wARAAMBAQIDDwEUAyoBQAM+AWwE/wL2AfUB/wP2Af8D9wH/A/cB/wP4Af8D+QH/A/kB/wP6 Af8D+gH/A/oB/wP7Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/QH/A/0B/wP9Af8D/QH/A/4B/wP+ Af8D/gH/A/4B/wP9Af8D+wH/A/oB/wP4Af8D9gH/A/QB/wPyAf8D7wH/A+oB/wPlAf8D4AH/A9wB/wHZ AtgB/wPUAf8DzwH/A8oB/wLKAckB/wHQAs8B/wPaAf8D1gH/A6sB/QMdASkDCgENAwEBAjwAAWUByAHo Af8BZQHHAegB/wFlAccB6AH/AWQBxgHmAf8BZAHGAeYB/wFjAcYB5gH/AWMBxgHmAf8BYwHGAeYB/wFj AcUB5QH/AWMBxQHlAf8BYwHFAeUB/wFjAcUB5QH/AToBjwGqAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE7AZEBrQH/AV8BwAHfAf8BXwHAAd8B/wFf AcAB3wH/AV8BwAHfAf8BXwG/Ad8B/wFfAb8B3wH/AV8BvwHfAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHe Af8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BywHnAfMF/wG6 Ad8B7wH/AV0BvgHcAf8DCAELAwkBDAMIAQsDCAELAwcBCgMHAQkDBwEJAwcBCQMGAQgDBQEHAwUBBwME AQYDAgEDAwABASQAAWUByAHoAf8BZQHHAegB/wFlAccB6AH/AWQBxgHmAf8BZAHGAeYB/wFjAcYB5gH/ AWMBxgHmAf8BYwHGAeYB/wFjAcUB5QH/AWMBxQHlAf8BYwHFAeUB/wFjAcUB5QH/AToBjwGqAf8BLwGC AZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE7AZEBrQH/ AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwG/Ad8B/wFfAb8B3wH/AV8BvwHfAf8BXgG/ Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/AV4BvwHeAf8BXgG/Ad4B/wFeAb8B3gH/ AV4BvwHeAf8BywHnAfMF/wG6Ad8B7wH/AV0BvgHcAf8B6gGQAVUB/wH1AbEBiwH/AeIBXgE7Af8B1gE4 ARUB/wHvAY4BUwH/AfsBqAGDAf8B+wGkAWkB/wH6AaEBZgH/AfoBngFjAf8BqAFMASkB/gM/AW0DBAEG AwIBAwMAAQH/ABEAAwEBAgMPARQDKgFAAz4BbAT/A/UB/wP2Af8D9wH/A/cB/wP4Af8C+QH4Af8D+QH/ A/oB/wP6Af8D+gH/A/oB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP9Af8D/gH/ A/4B/wP+Af8D/gH/A/4B/wP8Af8D+wH/A/oB/wP4Af8D9gH/A/QB/wPyAf8D8AH/A+8B/wPsAf8D6wH/ A+kB/wPnAf8D5AH/A+IB/wHgAt8B/wHhAuAB/wPjAf8D5gH/A+gB/wNpAfIDFwEgAwUBBzwAAWYByQHp Af8BZgHJAekB/wFmAckB6QH/AWUBxwHoAf8BZAHHAegB/wFkAccB5wH/AWQBxwHnAf8BZAHHAecB/wFk AccB5wH/AWQBxgHnAf8BZAHGAecB/wFkAcYB5gH/ATsBkAGqAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE7AZEBrQH/AV8BwAHfAf8BXwHAAd8B/wFf AcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHf Af8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BzQHoAfMF/wG6 AeAB7wH/AV4BvgHdAf8DBgEIAwcBCQMGAQgDBQEHAwUBBwMEAQYDBAEGAwQBBQMEAQUDAwEEAwMBBAMB AQIDAQECAwABASQAAWYByQHpAf8BZgHJAekB/wFmAckB6QH/AWUBxwHoAf8BZAHHAegB/wFkAccB5wH/ AWQBxwHnAf8BZAHHAecB/wFkAccB5wH/AWQBxgHnAf8BZAHGAecB/wFkAcYB5gH/ATsBkAGqAf8BLwGC AZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE7AZEBrQH/ AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwHA Ad8B/wFfAcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/AV8BwAHfAf8BXwHAAd8B/wFfAcAB3wH/ AV8BwAHfAf8BzQHoAfMF/wG6AeAB7wH/AV4BvgHdAf8B6gGPAVQB/wH2AbcBkAH/AfEBowFoAf8B6wGR AVYB/wHjAWIBPgH/AdQBNAERAf8B+AGfAWQB/wH6AaEBZgH/AfoBnQFiAf8B+gGaAWAB/wHWATgBFQH/ AxEBFwMBAQIDAAEB/wARAAMBAQIDDwEUAyoBQAM+AWwE/wP1Af8D9gH/A/YB/wP3Af8D+AH/A/gB/wP5 Af8D+gH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP9 Af8D/gH/A/4B/wP+Af8D/gH/A/0B/wP8Af8D+wH/A/oB/wP4Af8D9gH/A/QB/wPzAf8D8QH/A/AB/wPu Af8D7QH/A+wB/wPsAf8D6wH/AesC6gH/A+oB/wPrAf8D6wH/A+wB/wPuAf8DIwEzAwoBDgMAAQE4AAFn AcoB6wH/AWcBygHrAf8BZwHKAeoB/wFmAckB6QH/AWUByQHpAf8BZQHIAekB/wFlAcgB6QH/AWUByAHp Af8BZQHIAegB/wFlAcgB6AH/AWUByAHoAf8BZQHHAegB/wE7AZABqgH/AS8BggGdAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOwGRAa0B/wFgAcEB4AH/AWABwQHg Af8BYAHBAeAB/wFfAcEB4AH/AV8BwQHgAf8BXwHBAeAB/wFfAcEB4AH/AV8BwQHgAf8BXwHBAeAB/wFf AcEB4AH/AV8BwAHgAf8BXwHAAeAB/wFfAcAB4AH/AV8BwAHgAf8BXwHAAeAB/wFfAcAB4AH/Ac0B6AHz Bf8BugHgAe8B/wFfAb8B3QH/AwMEBAEGAwQBBQMEAQUDAwEEAwIBAwMCAQMDAQECAwEBAgMBAQIDAAEB AwABAQMAAQEoAAFnAcoB6wH/AWcBygHrAf8BZwHKAeoB/wFmAckB6QH/AWUByQHpAf8BZQHIAekB/wFl AcgB6QH/AWUByAHpAf8BZQHIAegB/wFlAcgB6AH/AWUByAHoAf8BZQHHAegB/wE7AZABqgH/AS8BggGd Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOwGRAa0B/wFg AcEB4AH/AWABwQHgAf8BYAHBAeAB/wFfAcEB4AH/AV8BwQHgAf8BXwHBAeAB/wFfAcEB4AH/AV8BwQHg Af8BXwHBAeAB/wFfAcEB4AH/AV8BwAHgAf8BXwHAAeAB/wFfAcAB4AH/AV8BwAHgAf8BXwHAAeAB/wFf AcAB4AH/Ac0B6AHzBf8BugHgAe8B/wFfAb8B3QH/AeYBgQFGAf8B9gG3AZAB/wHxAaMBaAH/AesBkQFW Af8B5QFpAUUB/wHgAVkBNQH/AdcBPAEZAf8B+QGfAWQB/wH6AZ0BYgH/AfoBmQFfAf8B9wGTAVkB/wFa AlgBwAMAAQH/ABUAAwEBAgMPARQDKgFAAz4BbAT/A/UB/wL2AfUB/wP2Af8D9wH/AvgB9wH/A/gB/wP5 Af8D+QH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP8Af8D/QH/A/0B/wP9 Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP9Af8D/AH/A/sB/wP6Af8D+AH/A/cB/wP1Af8D8wH/A/IB/wPx Af8D8AH/A+8B/wPuAf8D7gH/A+4B/wPuAf8D7gH/A+4B/wPvAf8D7wH/AygBPQMNARIDAAEBOAABaAHL AewB/wFoAcsB7AH/AWgBywHsAf8BZgHKAesB/wFmAcoB6wH/AWYBygHqAf8BZgHJAeoB/wFmAckB6gH/ AWYByQHqAf8BZgHJAeoB/wFmAckB6QH/AWUByQHpAf8BOwGQAaoB/wEvAYIBnQH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AToBkAGsAf8BYAHBAeEB/wFgAcEB4QH/ AWABwQHhAf8BYAHBAeEB/wFgAcEB4QH/AWABwQHhAf8BYAHBAeEB/wFgAcEB4QH/AWABwQHhAf8BYAHB AeEB/wFgAcEB4AH/AWABwQHgAf8BYAHBAeAB/wFgAcEB4AH/AWABwQHgAf8BYAHBAeAB/wHNAegB9AX/ AbsB4AHwAf8BYgHBAd4B/wMBBAIBAwMCAQMDAQECAwABAQMAAQEDAAEBQAABaAHLAewB/wFoAcsB7AH/ AWgBywHsAf8BZgHKAesB/wFmAcoB6wH/AWYBygHqAf8BZgHJAeoB/wFmAckB6gH/AWYByQHqAf8BZgHJ AeoB/wFmAckB6QH/AWUByQHpAf8BOwGQAaoB/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AToBkAGsAf8BYAHBAeEB/wFgAcEB4QH/AWABwQHhAf8BYAHB AeEB/wFgAcEB4QH/AWABwQHhAf8BYAHBAeEB/wFgAcEB4QH/AWABwQHhAf8BYAHBAeEB/wFgAcEB4AH/ AWABwQHgAf8BYAHBAeAB/wFgAcEB4AH/AWABwQHgAf8BYAHBAeAB/wHNAegB9AX/AbsB4AHwAf8BYgHB Ad4B/wIxATABTQHbAUkBJQH/AfEBpQGAAf8B6wGRAVYB/wHlAWkBRQH/AeABWAE0Af8B2wFJASUB/wHU ATUBEgH/AfoBngFjAf8B+gGZAV8B/wH5AZYBXAH/AYkBRwFBAfn/ABkAAwEBAgMPARQDKgFAAz4BbAT/ A/UB/wL2AfUB/wP2Af8D9wH/A/cB/wP4Af8D+AH/A/kB/wP6Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7 Af8D/AH/A/wB/wP8Af8D/AH/A/0B/wP9Af8D/QH/A/0B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/0B/wP8 Af8D+wH/A/oB/wP5Af8D9wH/A/YB/wP1Af8D9AH/A/MB/wPyAf8D8QH/A/EB/wPxAf8D8QH/A/EB/wPx Af8D8gH/A/IB/wMpAT8DDgETAwEBAjgAAWkBzQHuAf8BaQHNAe4B/wFpAcwB7QH/AWcBywHsAf8BZwHL AewB/wFnAcsB7AH/AWcBywHsAf8BZwHLAesB/wFnAcoB6wH/AWcBygHrAf8BZwHKAesB/wFmAcoB6wH/ ATsBkAGqAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wE0AYgBowH/AVEBrgHMAf8BYAHCAeIB/wFgAcIB4gH/AWABwgHhAf8BYAHCAeEB/wFgAcIB4QH/ AWABwgHhAf8BYAHCAeEB/wFgAcIB4QH/AWABwgHhAf8BYAHCAeEB/wFgAcIB4QH/AWABwgHhAf8BYAHC AeEB/wFgAcIB4QH/AWABwgHhAf8BgAHHAeQB/wFeAcAB4QH/AV8BwQHhAf8BZQHCAeAB/wcAAQFUAAFp Ac0B7gH/AWkBzQHuAf8BaQHMAe0B/wFnAcsB7AH/AWcBywHsAf8BZwHLAewB/wFnAcsB7AH/AWcBywHr Af8BZwHKAesB/wFnAcoB6wH/AWcBygHrAf8BZgHKAesB/wE7AZABqgH/AS8BggGdAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BNAGIAaMB/wFRAa4BzAH/AWABwgHi Af8BYAHCAeIB/wFgAcIB4QH/AWABwgHhAf8BYAHCAeEB/wFgAcIB4QH/AWABwgHhAf8BYAHCAeEB/wFg AcIB4QH/AWABwgHhAf8BYAHCAeEB/wFgAcIB4QH/AWMBuwHWAf8BKQFGAVIB/wFfAcAB3wH/AYABxwHk Af8BXgHAAeEB/wFfAcEB4QH/AWUBwgHgAf8HAAEBAVgCVgG7AekBjAFRAf8B5QFpAUUB/wHgAVgBNAH/ AdsBSQElAf8B1wE6ARcB/wH6AZ4BYwH/AfoBmQFfAf8B+QGWAVwB/wHWATkBFgH//wAZAAMBAQIDDwEU AyoBQAM+AWwE/wL1AfQB/wP1Af8D9gH/A/YB/wP3Af8D+AH/A/gB/wP5Af8D+QH/A/oB/wP6Af8D+gH/ A/sB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP8Af8D/QH/A/0B/wP9Af8D/QH/A/4B/wP+Af8D/gH/ A/4F/wP9Af8D/AH/A/wB/wP7Af8D+gH/A/kB/wP3Af8D9gH/A/YB/wP1Af8D9AH/A/QB/wP0Af8D8wH/ A/QB/wP0Af8D9AH/A/UB/wMqAUADDwEUAwEBAjgAAYABzgHvAf8BgAHOAe8B/wGAAc4B7wH/AWgBzQHu Af8BaAHMAe4B/wFoAcwB7QH/AWgBzAHtAf8BaAHMAe0B/wFoAcwB7QH/AWgBzAHtAf8BaAHLAewB/wFn AcsB7AH/ATsBkAGrAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/ATYBigGlAf8BRwGiAb4B/wFhAcIB4gH/AWEBwwHiAf8BYQHDAeIB/wFh AcMB4gH/AWEBwgHiAf8BYQHCAeIB/wFhAcIB4gH/AWEBwgHiAf8BYQHCAeIB/wFhAcIB4gH/AWEBwgHi Af8BYQHCAeIB/wFhAcIB4gH/AWEBwgHiAf8BYAHCAeIB/wFgAcIB4gH/AWABwgHiAf8BZgHDAeAB/1wA AYABzgHvAf8BgAHOAe8B/wGAAc4B7wH/AWgBzQHuAf8BaAHMAe4B/wFoAcwB7QH/AWgBzAHtAf8BaAHM Ae0B/wFoAcwB7QH/AWgBzAHtAf8BaAHLAewB/wFnAcsB7AH/ATsBkAGrAf8BLwGCAZ0B/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATYBigGlAf8BRwGi Ab4B/wFhAcIB4gH/AWEBwwHiAf8BYQHDAeIB/wFhAcMB4gH/AWEBwgHiAf8BYQHCAeIB/wFhAcIB4gH/ AWEBwgHiAf8BYQHCAeIB/wFiAbABygH/AdABNAETAf8B9AGuAYgB/wHuAZsBXwH/AT4BQAFFAf8BYAHC AeIB/wFgAcIB4gH/AWABwgHiAf8BZgHDAeAB/wwAAkoBSQGJAeYBgQFGAf8B4AFZATUB/wHbAUkBJQH/ AdUBNwEUAf8B+gGeAWQB/wH6AZkBXwH/AfkBlgFcAf8B2wFBAR4B//8AGQADAQECAw8BFAMqAUADPgFs BP8D9AH/A/UB/wL2AfUB/wP2Af8D9wH/A/cB/wP4Af8C+QH4Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP7 Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/AH/A/0B/wP9Af8D/QH/A/0B/wP+Af8D/gH/A/4B/wP+ Af8D/gH/A/4B/wP9Af8D/AH/A/sB/wP7Af8D+gH/A/kB/wP4Af8D+AH/A/cB/wP3Af8D9gH/A/YB/wP3 Af8D9wH/A/cB/wP4Af8DKgFAAw8BFAMBAQI4AAGBAc8B8QH/AYEBzwHxAf8BgQHPAfEB/wFpAc4B7wH/ AWkBzgHvAf8BaQHOAe8B/wFpAc0B7wH/AWkBzQHvAf8BaQHNAe4B/wFpAc0B7gH/AWkBzQHuAf8BaAHN Ae4B/wFcAbsB2gH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATsBkQGtAf8BXwHAAeAB/wFhAcMB4wH/AWEBwwHjAf8BYQHD AeMB/wFhAcMB4wH/AWEBwwHjAf8BYQHDAeMB/wFhAcMB4wH/AWEBwwHjAf8BYQHDAeMB/wFhAcMB4wH/ AWEBwwHiAf8BYQHDAeIB/wFhAcMB4gH/AWEBwwHiAf8BYQHDAeIB/wFhAcMB4gH/AWUBwAHaAf9cAAGB Ac8B8QH/AYEBzwHxAf8BgQHPAfEB/wFpAc4B7wH/AWkBzgHvAf8BaQHOAe8B/wFpAc0B7wH/AWkBzQHv Af8BaQHNAe4B/wFpAc0B7gH/AWkBzQHuAf8BaAHNAe4B/wFcAbsB2gH/AS8BgQGcAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATsBkQGt Af8BXwHAAeAB/wFhAcMB4wH/AWEBwwHjAf8BYQHDAeMB/wFhAcMB4wH/AWEBwwHjAf8BYQHDAeMB/wFh AcMB4wH/AbQBPgEnAf8B4wFiAT0B/wH4AbgBkgH/AfoBwgGbAf8B/gHMAaUB/wGXATgBKAH/AWEBwwHi Af8BYQHDAeIB/wFhAcMB4gH/AWUBwAHaAf8QAAHXAT0BGQH/AeABWAE0Af8B2QFEASAB/wHsAYEBSAH/ AfoBnQFiAf8B+gGZAV8B/wH5AZUBWwH/AdoBQAEdAf//ABkAAwEBAgMPARQDKgFAAz4BbAT/A/QB/wL1 AfQB/wP1Af8D9gH/AvcB9gH/A/cB/wP4Af8D+AH/A/kB/wP5Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7 Af8D+wH/A/wB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/4B/wP+Af8D/gH/A/4B/wP+ Af8D/gH/A/0B/wP9Af8D/AH/A/sB/wP7Af8D+gH/A/oB/wP6Af8D+gH/A/kB/wP5Af8D+QH/A/oB/wP6 Af8D+gH/AyoBQAMPARQDAQECOAABggHRAfMB/wGCAdEB8gH/AYIB0AHyAf8BgAHPAfEB/wGAAc8B8QH/ AYABzwHwAf8BgAHPAfAB/wGAAc8B8AH/AYABzgHwAf8BgAHOAfAB/wGAAc4B7wH/AWkBzgHvAf8BaQHO Ae8B/wFnAcsB7AH/ATEBgwGeAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wE4AY0BqQH/AWABwQHhAf8BYgHEAeQB/wFiAcQB5AH/AWIBxAHkAf8BYgHE AeMB/wFiAcQB4wH/AWIBxAHjAf8BYgHEAeMB/wFiAcQB4wH/AWIBxAHjAf8BYgHEAeMB/wFiAcMB4wH/ AWIBwwHjAf8BYgHDAeMB/wFhAcMB4wH/AWEBwwHjAf8BiQHQAeoB/wFfAWgBbAHzXAABggHRAfMB/wGC AdEB8gH/AYIB0AHyAf8BgAHPAfEB/wGAAc8B8QH/AYABzwHwAf8BgAHPAfAB/wGAAc8B8AH/AYABzgHw Af8BgAHOAfAB/wGAAc4B7wH/AWkBzgHvAf8BaQHOAe8B/wFnAcsB7AH/ATEBgwGeAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE4AY0BqQH/AWABwQHh Af8BYgHEAeQB/wFiAcQB5AH/AWIBxAHkAf8BYgHEAeMB/wFiAcQB4wH/AYgBmAGkAf8B1QE3ARQB/wHy AacBgAH/AfUBrwGIAf8B9wG4AZEB/wH6AcQBnQH/Af4BywGkAf8BnQE3ASYB/wFhAcMB4wH/AWEBwwHj Af8BiQHQAeoB/wFfAWgBbAHzEAABqgFPASwB/gHWATsBGAH/Ae4BhgFMAf8B+gGhAWYB/wH6AZ0BYgH/ AfoBmQFfAf8B+QGSAVgB/wHXATwBGQH//wAZAAMBAQIDDwEUAyoBQAM+AWwE/wP0Af8C9QH0Af8D9QH/ AvYB9QH/A/YB/wP3Af8D9wH/A/gB/wL5AfgB/wP5Af8D+gH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/ A/sB/wP8Af8D/AH/A/wB/wP8Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP+Af8D/gH/A/4B/wP+Af8D/gH/ A/4B/wP+Af8D/gH/A/0B/wP8Af8D/AH/A/wB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D/AH/ AyoBQAMPARQDAQECOAABgwHSAfQB/wGDAdIB9AH/AYMB0gH0Af8BgQHRAfIB/wGBAdAB8gH/AYEB0AHy Af8BgQHQAfIB/wGBAdAB8gH/AYEB0AHxAf8BgQHQAfEB/wGBAc8B8QH/AYABzwHxAf8BgAHPAfEB/wGA Ac8B8AH/ATsBkAGqAf8BLwGBAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wE4AYwBqAH/AWABwQHhAf8BYgHEAeQB/wFiAcQB5AH/AWIBxAHkAf8BYgHEAeQB/wFi AcQB5AH/AWIBxAHkAf8BYgHEAeQB/wFiAcQB5AH/AWIBxAHkAf8BYgHEAeQB/wFiAcQB5AH/AWIBxAHk Af8BYgHEAeQB/wGSAdwB8QH/A2EB4gMxAU9gAAGDAdIB9AH/AYMB0gH0Af8BgwHSAfQB/wGBAdEB8gH/ AYEB0AHyAf8BgQHQAfIB/wGBAdAB8gH/AYEB0AHyAf8BgQHQAfEB/wGBAdAB8QH/AYEBzwHxAf8BgAHP AfEB/wGAAc8B8QH/AYABzwHwAf8BOwGQAaoB/wEvAYEBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATgBjAGoAf8BYAHBAeEB/wFiAcQB5AH/AWIBxAHkAf8BYgHE AeQB/wFjAbkB1QH/Ac4BNgEWAf8B5wGEAUkB/wHvAZ0BYgH/AfIBpgGAAf8B9gGlAWkB/wH5AZcBXQH/ AfoBrwGRAf8B/AG9AZcB/wGdATcBJgH/AZIB3AHxAf8DYQHiAzEBTwwAAzIBUAFlAl4B5QHfAU8BLAH/ AfsBqQGEAf8B+wGkAWkB/wH6AaEBZgH/AfoBnQFiAf8B+QGRAVcB/wHsAZUBWQH/AdcBPQEaAf//ABkA AwEBAgMPARQDKgFAAz4BbAT/AvQB8wH/A/QB/wL1AfQB/wP1Af8D9gH/A/YB/wP3Af8D+AH/A/gB/wP5 Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP7Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/AH/A/0B/wP9 Af8D/QH/A/0B/wP9Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/0B/wP9 Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/AyoBQAMPARQDAQECOAABhAHUAfYB/wGEAdMB9QH/ AYQB0wH1Af8BgwHSAfQB/wGCAdIB9AH/AYIB0gH0Af8BggHRAfMB/wGCAdEB8wH/AYIB0QHzAf8BggHR AfMB/wGCAdEB8wH/AYEB0QHyAf8BgQHQAfIB/wGBAdAB8gH/ATwBkQGrAf8BLwGCAZ0B/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE4AYwBqAH/AWEBwgHhAf8BYwHF AeUB/wFjAcUB5QH/AWMBxQHlAf8BYwHFAeUB/wFjAcUB5QH/AWMBxQHlAf8BYwHFAeUB/wFjAcUB5QH/ AWMBxQHlAf8BYgHFAeUB/wFiAcUB5QH/AWIBxQHlAf8BYgHFAeUB/wEsAWIBjQH/aAABhAHUAfYB/wGE AdMB9QH/AYQB0wH1Af8BgwHSAfQB/wGCAdIB9AH/AYIB0gH0Af8BggHRAfMB/wGCAdEB8wH/AYIB0QHz Af8BggHRAfMB/wGCAdEB8wH/AYEB0QHyAf8BgQHQAfIB/wGBAdAB8gH/ATwBkQGrAf8BLwGCAZ0B/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE4AYwBqAH/AWEBwgHh Af8BYwHFAeUB/wFjAcUB5QH/AasBQgEvAf8B2gFFASEB/wHpAYsBUAH/AewBlAFYAf8B7wGdAWEB/wH5 AZABVgH/Af0B1AG5Av8B4QHIAv8B5AHOAf8B+QGSAVgB/wGbATcBJAH/AS4BXAGFAf8DDgETA1EBogNf AeAB1QE3ARQB/wHZAUABHQH/AfYBogFoAf8B/AGxAYwB/wH7AasBhgH/AfsBqAGDAf8B+wGkAWkB/wH5 AZ8BZAH/AfkBkgFYAf8B9wG6AZQB/wHtAZsBXwH/AdkBRAEgAf//ABkAAwEBAgMPARQDKgFAAz4BbAT/ A/MB/wP0Af8C9QH0Af8D9QH/AvYB9QH/A/YB/wP3Af8D9wH/A/gB/wP4Af8D+QH/A/kB/wP6Af8D+gH/ A/oB/wP6Af8D+wH/A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP9Af8D/QH/ A/0B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/4F/wP+Af8D/gH/A/4B/wP+Bf8D/gH/ A/4B/wMqAUADDwEUAwEBAjgAAYUB1QH3Af8BhQHVAfcB/wGFAdUB9wH/AYQB0wH2Af8BgwHTAfUB/wGD AdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdIB9QH/AYMB0gH0Af8BgwHSAfQB/wGCAdIB9AH/AYIB0gH0 Af8BggHSAfQB/wE8AZEBqwH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BOAGMAakB/wFhAcMB4gH/AWMBxgHmAf8BYwHGAeYB/wFjAcYB5gH/AWMBxgHm Af8BYwHGAeYB/wFjAcUB5QH/AWMBxQHlAf8BYwHFAeUB/wFjAcUB5QH/AWMBxQHlAf8BYwHFAeUB/wFj AcUB5QH/AWMBxQHlAf8BGAFMAWIB/2gAAYUB1QH3Af8BhQHVAfcB/wGFAdUB9wH/AYQB0wH2Af8BgwHT AfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdIB9QH/AYMB0gH0Af8BgwHSAfQB/wGCAdIB9AH/ AYIB0gH0Af8BggHSAfQB/wE8AZEBqwH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BOAGMAakB/wFhAcMB4gH/AYQBpgG3Af8B1gE3ARQB/wHjAWMBPwH/ AecBgwFIAf8B6QGLAVAB/wH0AZIBWAH/AfsBqwGJAv8B3gHCAv8B3wHFAv8B4AHIAv8B5AHNAf8B+QGS AVgB/wHrAZEBVgH/AfEBogFmAf8B9gGyAYwB/wH8Ab0BlwH/Af0BvwGZAf8B/QG7AZUB/wH9AbYBkAH/ AfwBswGOAf8B/AGvAYoB/wH7AasBhgH/AfoBogFnAf8B+QGPAVUB/wH9AcoBrgL/AdABqgH/AfkBvgGX Af8B8AGgAWUB/wHaAUYBIgH//wAZAAMBAQIDDwEUAyoBQAM+AWwE/wPzAf8C9AHzAf8D9AH/AvUB9AH/ A/UB/wP2Af8D9gH/A/cB/wL4AfcB/wP4Af8D+QH/A/kB/wP6Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7 Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/4B/wP+ Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/AyoBQAMP ARQDAQECOAABhgHWAfkB/wGGAdYB+QH/AYYB1gH4Af8BhQHVAfcB/wGEAdUB9wH/AYQB1AH3Af8BhAHU AfcB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdMB9gH/AYMB0wH2Af8BgwHTAfUB/wGDAdMB9QH/ ATwBkQGsAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wE4AYwBqQH/AWIBwwHiAf8BZAHGAeYB/wFkAcYB5gH/AWQBxgHmAf8BZAHGAeYB/wFkAcYB5gH/ AWQBxgHmAf8BZAHGAeYB/wFkAcYB5gH/AWQBxgHmAf8BYwHGAeYB/wFjAcYB5gH/AWMBxgHmAf8BYwHG AeYB/wEYAUsBYQH/aAABhgHWAfkB/wGGAdYB+QH/AYYB1gH4Af8BhQHVAfcB/wGEAdUB9wH/AYQB1AH3 Af8BhAHUAfcB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdMB9gH/AYMB0wH2Af8BgwHTAfUB/wGD AdMB9QH/ATwBkQGsAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wE6AYgBowH/AcsBOQEZAf8B3AFLAScB/wHiAVwBOAH/AeQBZAFAAf8B6AGFAUoB/wH5 AZIBWAH/Af4B2AG5Av8B2wG9Av8B3AHAAv8B3gHEAv8B4AHHAv8B4wHMAf8B+QGSAVgC/wHMAaUB/wH+ AcgBoQH/Af4BxQGfAf8B/gHBAZsB/wH9Ab0BlwH/Af0BugGUAf8B/QG3AZEB/wH7Aa0BhwH/AfkBlgFb Af8B+QGaAWIB/wH9AeIB1gL/AewB3gL/AeABxwL/AdIBrwH/AfoBwgGcAf8B8QGmAYEB/wHbAUcBJAH/ /wAZAAMBAQIDDwEUAyoBQAM+AWwE/wLzAfIB/wPzAf8D9AH/A/QB/wP1Af8C9gH1Af8D9gH/AvcB9gH/ A/cB/wP4Af8D+AH/A/kB/wP5Af8D+gH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D/AH/ A/wB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/gH/A/4B/wP+Af8D/gH/ A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/AyoBQAMPARQDAQECOAABhwHYAfsB/wGH AdgB+gH/AYcB1wH6Af8BhgHWAfkB/wGGAdYB+QH/AYUB1gH4Af8BhQHWAfgB/wGFAdUB+AH/AYUB1QH4 Af8BhQHVAfgB/wGFAdUB9wH/AYUB1QH3Af8BhAHVAfcB/wGEAdQB9wH/ATwBkQGsAf8BLwGCAZ0B/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE4AY0BqQH/AWIBxAHj Af8BZAHHAecB/wFkAccB5wH/AWQBxwHnAf8BZAHHAecB/wFkAccB5wH/AWQBxwHnAf8BZAHHAecB/wFk AccB5wH/AWQBxwHnAf8BZAHHAecB/wFkAccB5wH/AWQBxwHnAf8BZAHGAecB/wEYAUsBYQH/aAABhwHY AfsB/wGHAdgB+gH/AYcB1wH6Af8BhgHWAfkB/wGGAdYB+QH/AYUB1gH4Af8BhQHWAfgB/wGFAdUB+AH/ AYUB1QH4Af8BhQHVAfgB/wGFAdUB9wH/AYUB1QH3Af8BhAHVAfcB/wGEAdQB9wH/ATwBkQGsAf8BLwGC AZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BhAFPAU0B/wHWAToBFwH/ AdwBTAEoAf8B3wFUATAB/wHhAVsBNwH/AfcBjwFVAf8B/AG7AZgC/wHXAbcC/wHZAboC/wHbAb0C/wHc AcAC/wHeAcMC/wHgAcYC/wHjAcwB/wH5AZIBWAL/Ac4BpwH/Af0BwwGdAf8B+wGyAYwB/wH5AZ8BZAH/ AfkBkAFVAf8B+QGNAVEB/wH7AbUBmwH/Af4B9QHuAv8B/AH6Av8C/gL/AfcB8QL/Ae0B4AL/AeIBywL/ AdQBsgH/AfsBxQGfAf8B8wGrAYYB/wHbAUcBJAH//wAZAAMBAQIDDwEUAyoBQAM+AWwE/wLzAfIB/wPz Af8C9AHzAf8D9AH/AvUB9AH/A/UB/wL2AfUB/wP2Af8D9wH/A/cB/wP4Af8D+AH/A/kB/wP5Af8D+gH/ A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/QH/ A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/gH/A/4B/wP+Af8D/gH/A/4B/wP+Af8D/gH/ A/4B/wP+Af8DKgFAAw8BFAMBAQI4AAGIAdkB/AH/AYcB2QH8Af8BhwHZAfwB/wGHAdgB+wH/AYcB1wH6 Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHWAfkB/wGF AdYB+QH/AYUB1gH4Af8BPAGRAawB/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/ATgBjQGpAf8BYgHEAeQB/wFlAcgB6AH/AWUBxwHoAf8BZQHHAegB/wFl AccB6AH/AWUBxwHoAf8BZAHHAegB/wFkAccB6AH/AWQBxwHoAf8BZAHHAecB/wFkAccB5wH/AWQBxwHn Af8BZAHHAecB/wFkAccB5wH/ARcBSwFhAf9oAAGIAdkB/AH/AYcB2QH8Af8BhwHZAfwB/wGHAdgB+wH/ AYcB1wH6Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHW AfkB/wGFAdYB+QH/AYUB1gH4Af8BPAGRAawB/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wHXATsBGAH/AdoBRQEhAf8B3AFMASgB/wHoAWIBPgH/AfkBmAFeAf8B/gHU AbIC/wHWAbQC/wHXAbYC/wHZAbkC/wHaAbwC/wHcAb8C/wHeAcMC/wHfAcYC/wHiAcoB/wH9Ac0BtAH/ Af0B1gG/Af8B/gHfAcsB/wH+AesB2gL/Ae8B4gL/AfEB5QL/AfMB6QL/AfUB7gL/AfgB8wL/AfsB+AL/ Av4C/wH5AfUC/wHvAeQC/wHkAc8C/wHXAbcB/wH8AckBogH/AfUBsQGLAf8BkgFKASgB+/8AGQADAQEC Aw8BFAMqAUADPgFsBP8D8gH/AvMB8gH/A/MB/wL0AfMB/wP0Af8D9QH/A/UB/wP2Af8D9gH/A/cB/wP3 Af8D+AH/A/gB/wP5Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/wB/wP8 Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9 Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8DKgFAAw8BFAMBAQI4AAGJAdoB/AH/AYgB2gH8Af8BiAHa AfwB/wGIAdkB+wH/AYgB2QH7Af8BhwHZAfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/ AYcB2AH7Af8BhwHYAfoB/wGHAdcB+gH/AYYB1wH6Af8BPAGSAawB/wEvAYIBnQH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATgBjQGpAf8BYgHFAeUB/wFlAcgB6QH/ AWUByAHpAf8BZQHIAekB/wFlAcgB6QH/AWUByAHpAf8BZQHIAegB/wFlAcgB6AH/AWUByAHoAf8BZQHI AegB/wFlAcgB6AH/AWUByAHoAf8BZQHIAegB/wFlAcgB6AH/ARoBTwFlAf9oAAGJAdoB/AH/AYgB2gH8 Af8BiAHaAfwB/wGIAdkB+wH/AYgB2QH7Af8BhwHZAfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGH AdgB+wH/AYcB2AH7Af8BhwHYAfoB/wGHAdcB+gH/AYYB1wH6Af8BPAGSAawB/wEvAYIBnQH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BNwFnAZQB/wHYAT4BGwH/AdoBRQEhAf8B+QGRAVcB/wH9 AcgBowL/AdMBrwL/AdQBsQL/AdUBswL/AdcBtgL/AdgBuQL/AdoBvAL/AdsBvgL/Ad0BwgL/Ad8BxQL/ AeEByQL/AeQBzQL/AeYB0QL/AegB1gL/AeoB2gL/Ae0B3wL/AfAB5AL/AfIB6AL/AfUB7QL/AfcB8gL/ AfoB+AL/Af4B/QL/AfoB9wL/AfEB5wL/AeYB0wL/AdkBvAH/Af0BzAGlAf8B9AGxAYsB/wFIAkcBg/8A GQADAQECAw8BFAMqAUADPgFsBP8C8gHxAf8D8gH/AvMB8gH/A/MB/wP0Af8D9AH/A/UB/wL2AfUB/wP2 Af8C9wH2Af8D9wH/AvgB9wH/A/gB/wL5AfgB/wP5Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP7Af8D+wH/ A/sB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/QH/A/0B/wP9Af8D/QH/ A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wMqAUADDwEUAwEBAjgAAYoB3AH8 Af8BiQHcAfwB/wGJAdsB/AH/AYkB2gH7Af8BiAHaAfsB/wGIAdoB+wH/AYgB2gH7Af8BiAHaAfsB/wGI AdkB+wH/AYgB2QH7Af8BiAHZAfsB/wGIAdkB+wH/AYcB2QH7Af8BhwHZAfsB/wE8AZIBrAH/AS8BggGd Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOAGNAakB/wFj AcUB5QH/AWYByQHpAf8BZQHJAekB/wFlAckB6QH/AWUByQHpAf8BZQHJAekB/wFlAckB6QH/AWUByQHp Af8BZQHJAekB/wFlAcgB6QH/AWUByAHpAf8BZQHIAekB/wFlAcgB6QH/AWQByAHpAf8BIwFaAYgB/2gA AYoB3AH8Af8BiQHcAfwB/wGJAdsB/AH/AYkB2gH7Af8BiAHaAfsB/wGIAdoB+wH/AYgB2gH7Af8BiAHa AfsB/wGIAdkB+wH/AYgB2QH7Af8BiAHZAfsB/wGIAdkB+wH/AYcB2QH7Af8BhwHZAfsB/wE8AZIBrAH/ AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wHTATcBFgH/AesBYQE+Af8B+gGj AWgC/wHRAasC/wHSAa0C/wHTAa8C/wHUAbAC/wHVAbMC/wHWAbUC/wHYAbgC/wHaAbwC/wHbAb4C/wHd AcIC/wHfAcUC/wHhAckC/wHjAc0C/wHlAdAC/wHoAdUC/wHqAdkC/wHtAd4C/wHwAeMC/wHyAecC/wH0 AewC/wH3AfEC/wH6AfcC/wH9AfwC/wH8AfkC/wHzAeoC/wHoAdYC/wHcAcAB/wH+AdABqQH/AdYBNwEU Af//AB0AAwEBAgMPARQDKgFAAz4BbAT/A/EB/wLyAfEB/wLzAfIB/wPzAf8C9AHzAf8D9AH/AvUB9AH/ A/UB/wL2AfUB/wP2Af8C9wH2Af8D9wH/AvgB9wH/A/gB/wL5AfgB/wP5Af8D+QH/A/oB/wP6Af8D+gH/ A/oB/wP6Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/AH/ A/wB/wP8Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/A/0B/wP9Af8D/QH/AyoBQAMPARQDAQEC OAABiwHdAfwB/wGKAd0B/AH/AYoB3QH8Af8BiQHcAfsB/wGJAdsB+wH/AYkB2wH7Af8BiQHbAfsB/wGJ AdsB+wH/AYkB2wH7Af8BiQHbAfsB/wGJAdoB+wH/AYkB2gH7Af8BiAHaAfsB/wGIAdoB+wH/ATwBkgGs Af8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE4 AY0BqQH/AWMBxQHmAf8BZgHJAeoB/wFmAckB6gH/AWYByQHqAf8BZgHJAeoB/wFmAckB6gH/AWYByQHq Af8BZgHJAeoB/wFmAckB6gH/AWYByQHqAf8BZgHJAeoB/wFmAckB6gH/AWYByQHqAf8BZQHJAeoB/wEi AVoBiAH/aAABiwHdAfwB/wGKAd0B/AH/AYoB3QH8Af8BiQHcAfsB/wGJAdsB+wH/AYkB2wH7Af8BiQHb AfsB/wGJAdsB+wH/AYkB2wH7Af8BiQHbAfsB/wGJAdoB+wH/AYkB2gH7Af8BiAHaAfsB/wGIAdoB+wH/ ATwBkgGsAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AdUBNwEUAf8B/gHK AaQC/wHQAaoC/wHQAasC/wHRAawC/wHSAa4C/wHTAbAC/wHVAbMC/wHWAbUC/wHYAbgC/wHZAbsC/wHb Ab0C/wHdAcEC/wHeAcQC/wHhAcgC/wHjAcwC/wHlAdAC/wHnAdQC/wHpAdgC/wHsAd0C/wHvAeIC/wHx AeYC/wH0AesC/wH2AfAC/wH5AfUC/wH9AfsC/wH9AfsC/wH1Ae0C/wHqAdoC/wHgAcYB/wGrAVQBMAH+ /wAhAAMBAQIDDwEUAyoBQAM+AWwE/wPxAf8C8gHxAf8D8gH/AvMB8gH/A/MB/wL0AfMB/wP0Af8C9QH0 Af8D9QH/AvYB9QH/A/YB/wP3Af8D9wH/A/gB/wP4Af8C+QH4Af8D+QH/A/kB/wP6Af8D+gH/A/oB/wP6 Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8 Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/AH/AyoBQAMPARQDAQECOAABjAHe Af0B/wGLAd4B/QH/AYoB3gH9Af8BigHdAfwB/wGKAd0B/AH/AYoB3QH8Af8BigHcAfwB/wGKAdwB+wH/ AYoB3AH7Af8BigHcAfsB/wGKAdwB+wH/AYkB3AH7Af8BiQHbAfsB/wGJAdsB+wH/AT0BkgGsAf8BLwGC AZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE5AY0BqQH/ AWQBxgHnAf8BZgHKAesB/wFmAcoB6wH/AWYBygHrAf8BZgHKAesB/wFmAcoB6wH/AWYBygHrAf8BZgHK AesB/wFmAcoB6gH/AWYBygHqAf8BZgHKAeoB/wFmAcoB6gH/AWYBygHqAf8BZQHKAeoB/wEiAVkBiAH/ aAABjAHeAf0B/wGLAd4B/QH/AYoB3gH9Af8BigHdAfwB/wGKAd0B/AH/AYoB3QH8Af8BigHcAfwB/wGK AdwB+wH/AYoB3AH7Af8BigHcAfsB/wGKAdwB+wH/AYkB3AH7Af8BiQHbAfsB/wGJAdsB+wH/AT0BkgGs Af8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AekBigFPAv8BzwGoAv8B0AGp Av8B0AGqAv8B0QGsAv8B0gGuAv8B0wGwAv8B1QGyAv8B1gG0Av8B1wG3Av8B2QG7Av8B2gG9Av8B3AHA Av8B3gHDAv8B4AHHAv8B4wHMAv8B5AHPAv8B5wHUAv8B6QHXAv8B7AHcAv8B7wHhAv8B8QHlAv8B9AHr Av8B9gHvAv8B+QH1Av8B/AH6Av8B/gH9Av8B9gHwAv8B9QHoAf8B1QE1ARIB//8AJQADAQECAw8BFAMq AUADPgFsBP8C8QHwAf8D8QH/AvIB8QH/A/IB/wLzAfIB/wPzAf8C9AHzAf8D9AH/AvUB9AH/A/UB/wP2 Af8D9gH/A/cB/wP3Af8D+AH/A/gB/wP4Af8D+QH/A/kB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP7 Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8 Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8DKgFAAw8BFAMBAQI4AAGNAeAB/QH/AYwB4AH9Af8BiwHf Af0B/wGLAd4B/AH/AYsB3gH8Af8BiwHeAfwB/wGLAd4B/AH/AYsB3QH8Af8BiwHdAfwB/wGLAd0B/AH/ AYoB3QH8Af8BigHdAfwB/wGKAd0B/AH/AYoB3AH8Af8BPQGSAawB/wEvAYIBnQH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATkBjgGpAf8BZAHHAegB/wFnAcsB6wH/ AWcBywHrAf8BZwHLAesB/wFnAcsB6wH/AWcBygHrAf8BZwHKAesB/wFnAcoB6wH/AWcBygHrAf8BZwHK AesB/wFnAcoB6wH/AWcBygHrAf8BZwHKAesB/wFmAcoB6wH/ASIBWQGIAf9oAAGNAeAB/QH/AYwB4AH9 Af8BiwHfAf0B/wGLAd4B/AH/AYsB3gH8Af8BiwHeAfwB/wGLAd4B/AH/AYsB3QH8Af8BiwHdAfwB/wGL Ad0B/AH/AYoB3QH8Af8BigHdAfwB/wGKAd0B/AH/AYoB3AH8Af8BPQGSAawB/wEvAYIBnQH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BxQE9ASEB/wHzAa0BhwL/AdABqQL/AdABqgL/AdEBrAL/ AdIBrgL/AdMBrwL/AdQBsgL/AdYBtAL/AdcBtwL/AdkBugL/AdoBvAL/AdwBwAL/Ad4BwwL/AeABxwL/ AeIBywL/AeQBzwL/AecB0wL/AekB1wL/AesB3AL/Ae4B4AL/AfAB5QL/AfMB6gL/AfUB7gL/AfkB9AL/ AfwB+QL/Av4B/wHnAZgBggH/AV4CXAHO/wApAAMBAQIDDwEUAyoBQAM+AWwE/wPwAf8C8QHwAf8D8QH/ AvIB8QH/A/IB/wLzAfIB/wPzAf8D9AH/A/QB/wP1Af8D9QH/A/YB/wP2Af8D9wH/A/cB/wL4AfcB/wP4 Af8D+AH/A/kB/wP5Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7 Af8D+wH/A/sB/wP7Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8Af8D/AH/A/wB/wP8 Af8DKgFAAw8BFAMBAQI4AAGOAeEB/QH/AYwB4QH9Af8BjAHhAf0B/wGMAd8B/AH/AYwB3wH8Af8BjAHf AfwB/wGMAd8B/AH/AYwB3wH8Af8BjAHfAfwB/wGLAd4B/AH/AYsB3gH8Af8BiwHeAfwB/wGLAd4B/AH/ AYsB3gH8Af8BPQGSAa0B/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/ATkBjgGpAf8BZAHHAegB/wFnAcsB7AH/AWcBywHsAf8BZwHLAewB/wFnAcsB7AH/ AWcBywHsAf8BZwHLAewB/wFnAcsB7AH/AWcBywHsAf8BZwHLAewB/wFnAcsB7AH/AWcBywHsAf8BZwHL AewB/wFmAcsB7AH/ASIBWQGHAf9oAAGOAeEB/QH/AYwB4QH9Af8BjAHhAf0B/wGMAd8B/AH/AYwB3wH8 Af8BjAHfAfwB/wGMAd8B/AH/AYwB3wH8Af8BjAHfAfwB/wGLAd4B/AH/AYsB3gH8Af8BiwHeAfwB/wGL Ad4B/AH/AYsB3gH8Af8BPQGSAa0B/wEvAYIBnQH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wFXAVwBZgH/AdUBNwETAf8B/QHQAakC/wHRAasC/wHSAa0C/wHTAa8C/wHUAbEC/wHV AbMC/wHXAbYC/wHZAbkC/wHaAbwC/wHcAb8C/wHdAcIC/wHgAcYC/wHiAcoC/wHkAc4C/wHmAdIC/wHo AdYC/wHrAdsC/wHuAd8C/wHwAeQC/wHyAekC/wH2Ae4B/wH9AfYB8gH/AeQBiwFeAf8BYQJbAeH/ADEA AwEBAgMPARQDKgFAAz4BbAT/AvAB7wH/AvEB8AH/AvEB8AH/AvIB8QH/A/IB/wLzAfIB/wPzAf8D8wH/ A/QB/wP0Af8D9QH/A/UB/wP2Af8D9gH/AvcB9gH/A/cB/wL4AfcB/wP4Af8D+AH/A/kB/wP5Af8D+QH/ A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/ A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wMqAUADDwEUAwEBAjgAAY4B4gH9 Af8BjQHiAf0B/wGNAeIB/QH/AY0B4QH8Af8BjQHhAfwB/wGNAeAB/AH/AY0B4AH8Af8BjQHgAfwB/wGM AeAB/AH/AYwB4AH8Af8BjAHgAfwB/wGMAd8B/AH/AYwB3wH8Af8BjAHfAfwB/wE9AZMBrQH/AS8BggGd Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGOAaoB/wFl AcgB6gH/AWgBzAHtAf8BaAHMAe0B/wFoAcwB7QH/AWgBzAHtAf8BaAHMAe0B/wFoAcwB7QH/AWgBzAHt Af8BaAHMAe0B/wFoAcwB7QH/AWgBzAHtAf8BaAHLAewB/wFoAcsB7AH/AWcBywHsAf8BLAFoAZcB/2gA AY4B4gH9Af8BjQHiAf0B/wGNAeIB/QH/AY0B4QH8Af8BjQHhAfwB/wGNAeAB/AH/AY0B4AH8Af8BjQHg AfwB/wGMAeAB/AH/AYwB4AH8Af8BjAHgAfwB/wGMAd8B/AH/AYwB3wH8Af8BjAHfAfwB/wE9AZMBrQH/ AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGO AaoB/wG7AVkBSQH/AeQBaAFEAv8B0gGtAv8B0wGvAv8B1AGxAv8B1QGzAv8B1wG2Av8B2AG5Av8B2gG7 Av8B2wG/Av8B3QHCAv8B3wHFAv8B4gHKAv8B4wHNAv8B5gHRAv8B6AHVAv8B6wHaAv8B8wHmAf8B+QHb AcwB/wHiAWgBTQH/AdUBOAEVAf8CQAE/AW7/ADkAAwEBAgMPARQDKgFAAz4BbAT/AvAB7wH/AvAB7wH/ AvEB8AH/A/EB/wLyAfEB/wPyAf8C8wHyAf8D8wH/AvQB8wH/A/QB/wP0Af8D9QH/A/UB/wP2Af8D9gH/ AvcB9gH/A/cB/wP3Af8D+AH/A/gB/wP5Af8D+QH/A/kB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6 Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7 Af8D+wH/A/sB/wMqAUADDwEUAwEBAjgAAY8B5AH9Af8BjgHjAf0B/wGOAeMB/QH/AY4B4gH8Af8BjgHi AfwB/wGOAeIB/AH/AY4B4gH8Af8BjgHhAfwB/wGNAeEB/AH/AY0B4QH8Af8BjQHhAfwB/wGNAeEB/AH/ AY0B4QH8Af8BjQHgAfwB/wE9AZMBrQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGOAaoB/wFlAckB6wH/AWgBzAHuAf8BaAHMAe4B/wFoAcwB7gH/ AWgBzAHtAf8BaAHMAe0B/wFoAcwB7QH/AWgBzAHtAf8BaAHMAe0B/wFoAcwB7QH/AWgBzAHtAf8BaAHM Ae0B/wFoAcwB7QH/AWcBzAHtAf8BLAFoAZcB/2gAAY8B5AH9Af8BjgHjAf0B/wGOAeMB/QH/AY4B4gH8 Af8BjgHiAfwB/wGOAeIB/AH/AY4B4gH8Af8BjgHhAfwB/wGNAeEB/AH/AY0B4QH8Af8BjQHhAfwB/wGN AeEB/AH/AY0B4QH8Af8BjQHgAfwB/wE9AZMBrQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGOAaoB/wFlAckB6wH/AWgBzAHuAf8B0QE2ARQB/wH3 Ab4BmgL/AdQBsQL/AdUBswL/AdYBtQL/AdgBuAL/AdkBuwL/AdsBvgL/Ad0BwQL/Ad8BxQH/AeYBiQFU Af8B2gFIASYB/wHXAT0BGgH/AW8BRwFCAfUBWgJXAb0DSwGO/wBJAAMBAQIDDwEUAyoBQAM+AWwE/wLv Ae4B/wLwAe8B/wPwAf8C8QHwAf8D8QH/AvIB8QH/A/IB/wLzAfIB/wPzAf8C9AHzAf8D9AH/A/QB/wP1 Af8D9QH/AvYB9QH/A/YB/wL3AfYB/wP3Af8D9wH/A/gB/wP4Af8D+AH/A/kB/wP5Af8D+QH/A/oB/wP6 Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7 Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wMqAUADDwEUAwEBAjgAAZAB5QH9Af8BjwHlAf0B/wGPAeUB/QH/ AY8B4wH8Af8BjwHjAfwB/wGPAeMB/AH/AY8B4wH8Af8BjgHjAfwB/wGOAeMB/AH/AY4B4gH8Af8BjgHi AfwB/wGOAeIB/AH/AY4B4gH8Af8BjgHiAfwB/wE9AZMBrQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGOAaoB/wFmAckB6wH/AWkBzQHuAf8BaQHN Ae4B/wFpAc0B7gH/AWkBzQHuAf8BaQHNAe4B/wFpAc0B7gH/AWkBzQHuAf8BaQHNAe4B/wFpAc0B7gH/ AWkBzQHuAf8BaAHNAe4B/wFoAc0B7gH/AWcBzQHuAf8BLAFoAZcB/2gAAZAB5QH9Af8BjwHlAf0B/wGP AeUB/QH/AY8B4wH8Af8BjwHjAfwB/wGPAeMB/AH/AY8B4wH8Af8BjgHjAfwB/wGOAeMB/AH/AY4B4gH8 Af8BjgHiAfwB/wGOAeIB/AH/AY4B4gH8Af8BjgHiAfwB/wE9AZMBrQH/AS8BggGdAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGOAaoB/wFmAckB6wH/AWkBzQHu Af8BaQHNAe4B/wKeAacB/wHXAT0BGgL/AdgBtgL/AdYBtQL/AdgBuAL/AdkBugL/AdsBvgL/AdwBwAL/ Ad8BxAH/AdsBSQElAf8BjAGlAbMB/wEsAWgBlwH//wBVAAMBAQIDDwEUAyoBQAM+AWwE/wLvAe4B/wPv Af8C8AHvAf8D8AH/AvEB8AH/A/EB/wLyAfEB/wPyAf8C8wHyAf8D8wH/AvQB8wH/A/QB/wP0Af8C9QH0 Af8D9QH/AvYB9QH/A/YB/wP2Af8D9wH/A/cB/wP3Af8D+AH/A/gB/wP4Af8D+QH/A/kB/wP5Af8D+gH/ A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/ A/oB/wP6Af8D+gH/A/oB/wMqAUADDwEUAwEBAjgAAZEB5gH+Af8BkAHmAf4B/wGQAeYB/AH/AZAB5QH8 Af8BkAHlAfwB/wGQAeQB/AH/AY8B5AH8Af8BjwHkAfwB/wGPAeQB/AH/AY8B5AH8Af8BjwHkAfwB/wGP AeMB/AH/AY8B4wH8Af8BjwHjAfwB/wE9AZMBrQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGOAaoB/wFmAcoB7AH/AWkBzgHvAf8BaQHOAe8B/wFp Ac4B7wH/AWkBzgHvAf8BaQHOAe8B/wFpAc4B7wH/AWkBzQHvAf8BaQHNAe8B/wFpAc0B7wH/AWkBzQHv Af8BaQHNAe8B/wFpAc0B7wH/AWgBzQHvAf8BLAFoAZcB/2gAAZEB5gH+Af8BkAHmAf4B/wGQAeYB/AH/ AZAB5QH8Af8BkAHlAfwB/wGQAeQB/AH/AY8B5AH8Af8BjwHkAfwB/wGPAeQB/AH/AY8B5AH8Af8BjwHk AfwB/wGPAeMB/AH/AY8B4wH8Af8BjwHjAfwB/wE9AZMBrQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BOQGOAaoB/wFmAcoB7AH/AWkBzgHvAf8BaQHO Ae8B/wFpAc4B7wH/AWkBzgHvAf8BwwFRATwB/wHqAZMBWwL/AdgBuAL/AdkBuQL/AdsBvQL/AdwBwAL/ Ad4BwwH/AdQBMQENAf8BYgHAAd8B/wEsAWgBlwH//wBVAAMBAQIDDwEUAyoBQAM+AWwE/wPuAf8C7wHu Af8D7wH/AvAB7wH/A/AB/wLxAfAB/wPxAf8C8gHxAf8D8gH/AvMB8gH/A/MB/wPzAf8D9AH/A/QB/wL1 AfQB/wP1Af8C9gH1Af8D9gH/A/YB/wL3AfYB/wP3Af8D9wH/AvgB9wH/A/gB/wP4Af8D+AH/A/kB/wP5 Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6 Af8D+gH/A/oB/wP6Af8D+gH/AyoBQAMPARQDAQECOAABkgHoAf4B/wGRAecB/gH/AZEB5wH9Af8BkQHm Af0B/wGRAeYB/QH/AZAB5gH9Af8BkAHlAf0B/wGQAeUB/QH/AZAB5QH9Af8BkAHlAfwB/wGQAeUB/AH/ AZAB5QH8Af8BkAHkAfwB/wGQAeQB/AH/AT0BkwGtAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE5AY4BqgH/AWcBygHtAf8BgAHOAfAB/wGAAc4B8AH/ AYABzgHwAf8BgAHOAfAB/wGAAc4B8AH/AYABzgHwAf8BgAHOAe8B/wGAAc4B7wH/AYABzgHvAf8BaQHO Ae8B/wFpAc4B7wH/AWkBzgHvAf8BaAHOAe8B/wEsAWcBlwH/aAABkgHoAf4B/wGRAecB/gH/AZEB5wH9 Af8BkQHmAf0B/wGRAeYB/QH/AZAB5gH9Af8BkAHlAf0B/wGQAeUB/QH/AZAB5QH9Af8BkAHlAfwB/wGQ AeUB/AH/AZAB5QH8Af8BkAHkAfwB/wGQAeQB/AH/AT0BkwGtAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE5AY4BqgH/AWcBygHtAf8BgAHOAfAB/wGA Ac4B8AH/AYABzgHwAf8BgAHOAfAB/wGAAc4B8AH/AYABzgHwAf8B0wEzAREB/wH7AdEBsgL/AdoBvAL/ AdwBvwL/Ad4BwwH/AdEBMgEOAf8BaAHOAe8B/wEsAWcBlwH//wBVAAMBAQIDDwEUAyoBQAM+AWwE/wLu Ae0B/wLvAe4B/wLvAe4B/wPvAf8C8AHvAf8D8AH/AvEB8AH/A/EB/wLyAfEB/wPyAf8C8wHyAf8D8wH/ A/MB/wL0AfMB/wP0Af8C9QH0Af8D9QH/A/UB/wL2AfUB/wP2Af8D9gH/A/cB/wP3Af8D9wH/A/gB/wP4 Af8D+AH/A/gB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6 Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8DKgFAAw8BFAMBAQI4AAGTAekB/gH/AZIB6QH+Af8BkgHo Af0B/wGSAecB/QH/AZEB5wH9Af8BkQHnAf0B/wGRAecB/QH/AZEB5wH9Af8BkQHmAf0B/wGRAeYB/QH/ AZEB5gH9Af8BkQHmAf0B/wGRAeYB/QH/AZAB5gH9Af8BPQGTAa0B/wEvAYIBnQH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATkBjgGqAf8BZwHLAe0B/wGAAc8B8AH/ AYABzwHwAf8BgAHPAfAB/wGAAc8B8AH/AYABzwHwAf8BgAHPAfAB/wGAAc8B8AH/AYABzwHwAf8BgAHP AfAB/wGAAc8B8AH/AYABzwHwAf8BgAHPAfAB/wFpAc8B8AH/AToBkAGqAf9oAAGTAekB/gH/AZIB6QH+ Af8BkgHoAf0B/wGSAecB/QH/AZEB5wH9Af8BkQHnAf0B/wGRAecB/QH/AZEB5wH9Af8BkQHmAf0B/wGR AeYB/QH/AZEB5gH9Af8BkQHmAf0B/wGRAeYB/QH/AZAB5gH9Af8BPQGTAa0B/wEvAYIBnQH/AS8BgQGc Af8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ATkBjgGqAf8BZwHLAe0B/wGA Ac8B8AH/AYABzwHwAf8BgAHPAfAB/wGAAc8B8AH/AYABzwHwAf8BgAHPAfAB/wGAAc8B8AH/AaoBjwGO Af8B3AFQAS4C/wHfAcMC/wHiAccB/wHAAUMBKwH/AWkBzwHwAf8BOgGQAaoB//8AVQADAQECAw8BFAMq AUADPgFsBP8C7gHtAf8C7gHtAf8C7wHuAf8C7wHuAf8D7wH/AvAB7wH/A/AB/wLxAfAB/wPxAf8C8gHx Af8D8gH/AvMB8gH/AvMB8gH/A/MB/wL0AfMB/wP0Af8D9AH/AvUB9AH/A/UB/wL2AfUB/wP2Af8D9gH/ A/YB/wP3Af8D9wH/A/cB/wL4AfcB/wP4Af8D+AH/A/gB/wP4Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5 Af8D+QH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/A/oB/wP6Af8D+gH/AyoBQAMPARQDAQECOAABlAHq Af4B/wGUAeoB/QH/AZMB6gH9Af8BkgHpAf0B/wGSAegB/QH/AZIB6AH9Af8BkgHoAf0B/wGSAegB/QH/ AZIB6AH9Af8BkgHoAf0B/wGSAecB/QH/AZIB5wH9Af8BkQHnAf0B/wGRAecB/QH/AT4BlAGtAf8BLwGC AZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE6AY8BqgH/ AWgBzAHuAf8BgQHQAfEB/wGBAdAB8QH/AYEBzwHxAf8BgQHPAfEB/wGBAc8B8QH/AYABzwHxAf8BgAHP AfEB/wGAAc8B8QH/AYABzwHxAf8BgAHPAfEB/wGAAc8B8QH/AYABzwHxAf8BaQHPAfEB/wE5AZABqgH/ aAABlAHqAf4B/wGUAeoB/QH/AZMB6gH9Af8BkgHpAf0B/wGSAegB/QH/AZIB6AH9Af8BkgHoAf0B/wGS AegB/QH/AZIB6AH9Af8BkgHoAf0B/wGSAecB/QH/AZIB5wH9Af8BkQHnAf0B/wGRAecB/QH/AT4BlAGt Af8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wE6 AY8BqgH/AWgBzAHuAf8BgQHQAfEB/wGBAdAB8QH/AYEBzwHxAf8BgQHPAfEB/wGBAc8B8QH/AYABzwHx Af8BgAHPAfEB/wGAAc8B8QH/AYABzwHxAf8BswFpAWIB/wG+AVUBRAH/AYABzwHxAf8BaQHPAfEB/wE5 AZABqgH//wBVAAMBAQIDDwEUAyoBQAM+AWwE/wLtAewB/wLuAe0B/wLuAe0B/wLvAe4B/wLvAe4B/wPv Af8C8AHvAf8D8AH/AvEB8AH/A/EB/wLyAfEB/wLyAfEB/wPyAf8C8wHyAf8D8wH/AvQB8wH/A/QB/wP0 Af8C9QH0Af8D9QH/A/UB/wL2AfUB/wP2Af8D9gH/A/YB/wP3Af8D9wH/A/cB/wP3Af8D+AH/A/gB/wP4 Af8D+AH/A/gB/wL5AfgB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/ A/kB/wMqAUADDwEUAwEBAjgAAZUB6wH+Af8BlQHrAf0B/wGUAesB/QH/AZMB6gH9Af8BkwHqAf0B/wGT AeoB/QH/AZMB6QH9Af8BkwHpAf0B/wGTAekB/QH/AZMB6QH9Af8BkwHpAf0B/wGSAekB/QH/AZIB6AH9 Af8BkgHoAf0B/wE+AZQBrQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEv AYEBnAH/AS8BgQGcAf8BOwGRAa0B/wFoAcwB7wH/AYEB0AHyAf8BgQHQAfIB/wGBAdAB8gH/AYEB0AHy Af8BgQHQAfIB/wGBAdAB8gH/AYEB0AHyAf8BgQHQAfIB/wGBAdAB8gH/AYEB0AHxAf8BgQHQAfEB/wGB AdAB8QH/AYAB0AHxAf8BOgGQAaoB/2gAAZUB6wH+Af8BlQHrAf0B/wGUAesB/QH/AZMB6gH9Af8BkwHq Af0B/wGTAeoB/QH/AZMB6QH9Af8BkwHpAf0B/wGTAekB/QH/AZMB6QH9Af8BkwHpAf0B/wGSAekB/QH/ AZIB6AH9Af8BkgHoAf0B/wE+AZQBrQH/AS8BggGdAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BLwGB AZwB/wEvAYEBnAH/AS8BgQGcAf8BOwGRAa0B/wFoAcwB7wH/AYEB0AHyAf8BgQHQAfIB/wGBAdAB8gH/ AYEB0AHyAf8BgQHQAfIB/wGBAdAB8gH/AYEB0AHyAf8BgQHQAfIB/wGBAdAB8gH/AYEB0AHxAf8BgQHQ AfEB/wGBAdAB8QH/AYAB0AHxAf8BOgGQAaoB//8AVQADAQECAw8BFAMqAUADPgFsBP8C7QHsAf8C7QHs Af8C7gHtAf8C7gHtAf8C7wHuAf8C7wHuAf8D7wH/AvAB7wH/A/AB/wLxAfAB/wPxAf8C8gHxAf8C8gHx Af8D8gH/AvMB8gH/A/MB/wPzAf8C9AHzAf8D9AH/A/QB/wL1AfQB/wP1Af8D9QH/AvYB9QH/A/YB/wP2 Af8D9gH/AvcB9gH/A/cB/wP3Af8D9wH/A/cB/wL4AfcB/wP4Af8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4 Af8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/AyoBQAMPARQDAQECOAABlgHtAf4B/wGWAe0B/QH/ AZQB7AH9Af8BlAHrAf0B/wGUAesB/QH/AZQB6wH9Af8BlAHrAf0B/wGUAesB/QH/AZQB6gH9Af8BlAHq Af0B/wGTAeoB/QH/AZMB6gH9Af8BkwHqAf0B/wGTAeoB/QH/AT4BlAGtAf8BLwGCAZ0B/wEvAYEBnAH/ AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BMQGDAZ4B/wFBAZgBtQH/AYABzgHvAf8BggHR AfMB/wGCAdEB8wH/AYEB0QHyAf8BgQHRAfIB/wGBAdEB8gH/AYEB0QHyAf8BgQHRAfIB/wGBAdEB8gH/ AYEB0QHyAf8BgQHQAfIB/wGBAdAB8gH/AYEB0AHyAf8BgAHQAfIB/wE6AZABqgH/aAABlgHtAf4B/wGW Ae0B/QH/AZQB7AH9Af8BlAHrAf0B/wGUAesB/QH/AZQB6wH9Af8BlAHrAf0B/wGUAesB/QH/AZQB6gH9 Af8BlAHqAf0B/wGTAeoB/QH/AZMB6gH9Af8BkwHqAf0B/wGTAeoB/QH/AT4BlAGtAf8BLwGCAZ0B/wEv AYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/AS8BgQGcAf8BMQGDAZ4B/wFBAZgBtQH/AYABzgHv Af8BggHRAfMB/wGCAdEB8wH/AYEB0QHyAf8BgQHRAfIB/wGBAdEB8gH/AYEB0QHyAf8BgQHRAfIB/wGB AdEB8gH/AYEB0QHyAf8BgQHQAfIB/wGBAdAB8gH/AYEB0AHyAf8BgAHQAfIB/wE6AZABqgH//wBVAAMB AQIDDwEUAyoBQAM+AWwE/wLsAesB/wLtAewB/wLtAewB/wLuAe0B/wLuAe0B/wLvAe4B/wLvAe4B/wPv Af8C8AHvAf8D8AH/AvEB8AH/AvEB8AH/A/EB/wLyAfEB/wPyAf8C8wHyAf8C8wHyAf8D8wH/AvQB8wH/ AvQB8wH/A/QB/wP0Af8C9QH0Af8D9QH/A/UB/wL2AfUB/wP2Af8D9gH/A/YB/wP2Af8C9wH2Af8D9wH/ A/cB/wP3Af8D9wH/A/cB/wP3Af8C+AH3Af8C+AH3Af8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/ AvgB9wH/AyoBQAMPARQDAQECOAABlwHuAf4B/wGWAe4B/QH/AZUB7gH9Af8BlQHtAf0B/wGVAewB/QH/ AZUB7AH9Af8BlQHsAf0B/wGVAewB/QH/AZUB7AH9Af8BlQHsAf0B/wGUAesB/QH/AZQB6wH9Af8BlAHr Af0B/wGUAesB/QH/AT4BlAGtAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEvAYEBnAH/ ATEBhAGfAf8BPgGVAbEB/wFXAbUB1AH/AYIB0QHzAf8BggHRAfMB/wGCAdEB8wH/AYIB0QHzAf8BggHR AfMB/wGCAdEB8wH/AYIB0QHzAf8BggHRAfMB/wGCAdEB8wH/AYIB0QHzAf8BggHRAfMB/wGCAdEB8wH/ AYIB0QHzAf8BgQHRAfMB/wE6AZABqgH/aAABlwHuAf4B/wGWAe4B/QH/AZUB7gH9Af8BlQHtAf0B/wGV AewB/QH/AZUB7AH9Af8BlQHsAf0B/wGVAewB/QH/AZUB7AH9Af8BlQHsAf0B/wGUAesB/QH/AZQB6wH9 Af8BlAHrAf0B/wGUAesB/QH/AT4BlAGtAf8BLwGCAZ0B/wEvAYEBnAH/AS8BgQGcAf8BLwGBAZwB/wEv AYEBnAH/ATEBhAGfAf8BPgGVAbEB/wFXAbUB1AH/AYIB0QHzAf8BggHRAfMB/wGCAdEB8wH/AYIB0QHz Af8BggHRAfMB/wGCAdEB8wH/AYIB0QHzAf8BggHRAfMB/wGCAdEB8wH/AYIB0QHzAf8BggHRAfMB/wGC AdEB8wH/AYIB0QHzAf8BgQHRAfMB/wE6AZABqgH//wBVAAMBAQIDDwEUAyoBQAM+AWwE/wLsAesB/wLs AesB/wLtAewB/wLtAewB/wLuAe0B/wLuAe0B/wLvAe4B/wLvAe4B/wPvAf8C8AHvAf8C8AHvAf8C8QHw Af8C8QHwAf8D8QH/AvIB8QH/AvIB8QH/A/IB/wLzAfIB/wPzAf8D8wH/AvQB8wH/A/QB/wP0Af8D9AH/ AvUB9AH/A/UB/wP1Af8D9QH/AvYB9QH/A/YB/wP2Af8D9gH/A/YB/wP2Af8C9wH2Af8D9wH/A/cB/wP3 Af8D9wH/A/cB/wP3Af8D9wH/A/cB/wP3Af8D9wH/A/cB/wP3Af8DKgFAAw8BFAMBAQI4AAGXAe8C/wGX Ae8B/gH/AZYB7wH+Af8BlgHuAf4B/wGWAe4B/gH/AZYB7gH9Af8BlgHtAf0B/wGWAe0B/QH/AZYB7QH9 Af8BlQHtAf0B/wGVAe0B/QH/AZUB7AH9Af8BlQHsAf0B/wGVAewB/QH/AT4BlAGtAf8BLwGCAZ0B/wEv AYEBnAH/AS8BgQGcAf8BMAGCAZ0B/wE4AY0BqAH/AUoBpAHCAf8BZwHLAe4B/wGDAdIB9AH/AYMB0gH0 Af8BgwHSAfQB/wGCAdIB9AH/AYIB0gH0Af8BggHSAfQB/wGCAdIB9AH/AYIB0gH0Af8BggHSAfQB/wGC AdIB9AH/AYIB0gH0Af8BggHSAfQB/wGCAdIB9AH/AYIB0gH0Af8BgQHSAfMB/wFIAaEBvgH/aAABlwHv Av8BlwHvAf4B/wGWAe8B/gH/AZYB7gH+Af8BlgHuAf4B/wGWAe4B/QH/AZYB7QH9Af8BlgHtAf0B/wGW Ae0B/QH/AZUB7QH9Af8BlQHtAf0B/wGVAewB/QH/AZUB7AH9Af8BlQHsAf0B/wE+AZQBrQH/AS8BggGd Af8BLwGBAZwB/wEvAYEBnAH/ATABggGdAf8BOAGNAagB/wFKAaQBwgH/AWcBywHuAf8BgwHSAfQB/wGD AdIB9AH/AYMB0gH0Af8BggHSAfQB/wGCAdIB9AH/AYIB0gH0Af8BggHSAfQB/wGCAdIB9AH/AYIB0gH0 Af8BggHSAfQB/wGCAdIB9AH/AYIB0gH0Af8BggHSAfQB/wGCAdIB9AH/AYEB0gHzAf8BSAGhAb4B//8A VQADAQECAw8BFAMqAUADPgFsBP8C6wHqAf8C7AHrAf8C7AHrAf8C7QHsAf8C7QHsAf8C7gHtAf8C7gHt Af8D7gH/Au8B7gH/Au8B7gH/AvAB7wH/AvAB7wH/A/AB/wLxAfAB/wLxAfAB/wPxAf8C8gHxAf8D8gH/ A/IB/wLzAfIB/wPzAf8D8wH/AvQB8wH/AvQB8wH/A/QB/wP0Af8C9QH0Af8D9QH/A/UB/wP1Af8D9QH/ AvYB9QH/AvYB9QH/A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/ A/YB/wP2Af8DKgFAAw8BFAMBAQI4AAGYAfEC/wGUAesB+wH/AZcB8AH+Af8BlwHvAf4B/wGXAe8B/gH/ AZcB7wH+Af8BlwHvAf4B/wGXAe4B/gH/AZYB7gH+Af8BlgHuAf4B/wGWAe4B/gH/AZYB7gH+Af8BlgHu Af0B/wGWAe0B/QH/AT4BlAGtAf8BLwGCAZ0B/wEwAYIBnQH/ATUBiQGlAf8BQwGcAbkB/wFeAb8B3wH/ AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHT AfUB/wGDAdIB9AH/AYMB0gH0Af8BgwHSAfQB/wGDAdIB9AH/AYMB0gH0Af8BgwHSAfQB/wGDAdIB9AH/ AYMB0gH0Af8BggHSAfQB/wFIAaEBvgH/aAABmAHxAv8BlAHrAfsB/wGXAfAB/gH/AZcB7wH+Af8BlwHv Af4B/wGXAe8B/gH/AZcB7wH+Af8BlwHuAf4B/wGWAe4B/gH/AZYB7gH+Af8BlgHuAf4B/wGWAe4B/gH/ AZYB7gH9Af8BlgHtAf0B/wE+AZQBrQH/AS8BggGdAf8BMAGCAZ0B/wE1AYkBpQH/AUMBnAG5Af8BXgG/ Ad8B/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/ AYMB0wH1Af8BgwHSAfQB/wGDAdIB9AH/AYMB0gH0Af8BgwHSAfQB/wGDAdIB9AH/AYMB0gH0Af8BgwHS AfQB/wGDAdIB9AH/AYIB0gH0Af8BSAGhAb4B//8AVQADAQECAw8BFAMqAUADPgFsBP8C6wHqAf8C6wHq Af8C7AHrAf8C7AHrAf8C7QHsAf8C7QHsAf8C7gHtAf8C7gHtAf8D7gH/Au8B7gH/Au8B7gH/A+8B/wLw Ae8B/wPwAf8C8QHwAf8C8QHwAf8D8QH/AvIB8QH/AvIB8QH/A/IB/wLzAfIB/wLzAfIB/wPzAf8D8wH/ AvQB8wH/AvQB8wH/A/QB/wP0Af8D9AH/AvUB9AH/AvUB9AH/A/UB/wP1Af8D9QH/A/UB/wP1Af8C9gH1 Af8C9gH1Af8C9gH1Af8C9gH1Af8D9gH/A/YB/wP2Af8D9gH/A/YB/wL2AfUB/wL2AfUB/wMqAUADDwEU AwEBAjgAAZkB8gL/AZQB7QH7Af8BmAHyAf4B/wGYAfAB/gH/AZgB8AH+Af8BmAHwAf4B/wGYAfAB/gH/ AZcB8AH+Af8BlwHwAf4B/wGXAe8B/gH/AZcB7wH+Af8BlwHvAf4B/wGXAe8B/gH/AZcB7wH+Af8BPgGV Aa0B/wEyAYUBogH/AT8BlgGyAf8BVwG1AdQB/wGEAdMB9wH/AYQB0wH2Af8BhAHTAfYB/wGDAdMB9gH/ AYMB0wH2Af8BgwHTAfYB/wGDAdMB9gH/AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHT AfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGCAdMB9QH/ AUcBogG+Af9oAAGZAfIC/wGUAe0B+wH/AZgB8gH+Af8BmAHwAf4B/wGYAfAB/gH/AZgB8AH+Af8BmAHw Af4B/wGXAfAB/gH/AZcB8AH+Af8BlwHvAf4B/wGXAe8B/gH/AZcB7wH+Af8BlwHvAf4B/wGXAe8B/gH/ AT4BlQGtAf8BMgGFAaIB/wE/AZYBsgH/AVcBtQHUAf8BhAHTAfcB/wGEAdMB9gH/AYQB0wH2Af8BgwHT AfYB/wGDAdMB9gH/AYMB0wH2Af8BgwHTAfYB/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/ AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/AYMB0wH1Af8BgwHTAfUB/wGDAdMB9QH/AYMB0wH1Af8BggHT AfUB/wFHAaIBvgH//wBVAAMBAQIDDwEUAyoBQAM+AWwE/wLqAekB/wLrAeoB/wLrAeoB/wLsAesB/wLs AesB/wLtAewB/wLtAewB/wPtAf8C7gHtAf8C7gHtAf8C7wHuAf8C7wHuAf8D7wH/AvAB7wH/AvAB7wH/ A/AB/wLxAfAB/wLxAfAB/wPxAf8C8gHxAf8C8gHxAf8D8gH/A/IB/wLzAfIB/wLzAfIB/wPzAf8D8wH/ AvQB8wH/AvQB8wH/A/QB/wP0Af8D9AH/A/QB/wL1AfQB/wL1AfQB/wP1Af8D9QH/A/UB/wP1Af8D9QH/ A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wMqAUADDwEUAwEBAjgAAZoB8wL/AZUB7gH7Af8BmQHz Af4B/wGZAfIB/gH/AZkB8gH+Af8BmQHxAf4B/wGYAfEB/gH/AZgB8QH+Af8BmAHxAf4B/wGYAfEB/gH/ AZgB8QH+Af8BmAHwAf4B/wGYAfAB/gH/AZgB8AH+Af8BPgGVAa0B/wFPAaoByQH/AYEBzwHyAf8BhAHU AfcB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/ AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHU AfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHTAfYB/wGDAdMB9gH/AUcBogG+Af9oAAGaAfMC/wGVAe4B+wH/ AZkB8wH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8QH+Af8BmAHxAf4B/wGYAfEB/gH/AZgB8QH+Af8BmAHx Af4B/wGYAfEB/gH/AZgB8AH+Af8BmAHwAf4B/wGYAfAB/gH/AT4BlQGtAf8BTwGqAckB/wGBAc8B8gH/ AYQB1AH3Af8BhAHUAfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHU AfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/ AYQB1AH2Af8BhAHUAfYB/wGEAdQB9gH/AYQB0wH2Af8BgwHTAfYB/wFHAaIBvgH//wBVAAMBAQIDDwEU AyoBQAM+AWwE/wHqAekB6AH/AuoB6QH/AusB6gH/AusB6gH/AuwB6wH/AuwB6wH/Au0B7AH/Au0B7AH/ Au0B7AH/Au4B7QH/Au4B7QH/A+4B/wLvAe4B/wLvAe4B/wPvAf8C8AHvAf8C8AHvAf8D8AH/AvEB8AH/ AvEB8AH/A/EB/wLyAfEB/wLyAfEB/wPyAf8D8gH/AvMB8gH/AvMB8gH/A/MB/wPzAf8D8wH/AvQB8wH/ AvQB8wH/AvQB8wH/A/QB/wP0Af8D9AH/A/QB/wP0Af8D9AH/A/QB/wL1AfQB/wL1AfQB/wL1AfQB/wL1 AfQB/wP0Af8D9AH/A/QB/wMqAUADDwEUAwEBAjgAAZoB8wL/AZUB7gH7Af8BmQHzAf4B/wGZAfIB/gH/ AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHy Af4B/wGZAfIB/gH/AZkB8QH+Af8BPgGUAa0B/wGGAdcB+QH/AYUB1QH3Af8BhAHVAfcB/wGEAdUB9wH/ AYQB1QH3Af8BhAHVAfcB/wGEAdUB9wH/AYQB1QH3Af8BhAHVAfcB/wGEAdQB9wH/AYQB1AH3Af8BhAHU AfcB/wGEAdQB9wH/AYQB1AH3Af8BhAHUAfcB/wGEAdQB9wH/AYQB1AH3Af8BhAHUAfcB/wGEAdQB9wH/ AYQB1AH3Af8BhAHUAfYB/wGDAdQB9gH/AUcBogG+Af9oAAGaAfMC/wGVAe4B+wH/AZkB8wH+Af8BmQHy Af4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/ AZkB8gH+Af8BmQHyAf4B/wGZAfEB/gH/AT4BlAGtAf8BhgHXAfkB/wGFAdUB9wH/AYQB1QH3Af8BhAHV AfcB/wGEAdUB9wH/AYQB1QH3Af8BhAHVAfcB/wGEAdUB9wH/AYQB1QH3Af8BhAHUAfcB/wGEAdQB9wH/ AYQB1AH3Af8BhAHUAfcB/wGEAdQB9wH/AYQB1AH3Af8BhAHUAfcB/wGEAdQB9wH/AYQB1AH3Af8BhAHU AfcB/wGEAdQB9wH/AYQB1AH2Af8BgwHUAfYB/wFHAaIBvgH//wBVAAMBAQIDDwEUAyoBQAM+AWwE/wLp AegB/wLpAegB/wLqAekB/wHrAeoB6QH/AusB6gH/AuwB6wH/AuwB6wH/AuwB6wH/Au0B7AH/Au0B7AH/ Au4B7QH/Au4B7QH/Au4B7QH/Au8B7gH/Au8B7gH/A+8B/wLwAe8B/wLwAe8B/wPwAf8D8AH/AvEB8AH/ AvEB8AH/A/EB/wLyAfEB/wLyAfEB/wLyAfEB/wPyAf8D8gH/AvMB8gH/AvMB8gH/AvMB8gH/A/MB/wPz Af8D8wH/A/MB/wL0AfMB/wL0AfMB/wL0AfMB/wL0AfMB/wL0AfMB/wL0AfMB/wL0AfMB/wL0AfMB/wL0 AfMB/wL0AfMB/wL0AfMB/wL0AfMB/wMqAUADDwEUAwEBAjgAAZoB8wL/AZUB7gH7Af8BmQHzAf4B/wGZ AfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+ Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BOwGQAagB/wGGAdYB+gH/AYUB1QH4Af8BhQHVAfgB/wGF AdUB+AH/AYUB1QH4Af8BhQHVAfgB/wGFAdUB+AH/AYUB1QH4Af8BhQHVAfgB/wGFAdUB+AH/AYUB1QH4 Af8BhQHVAfgB/wGFAdUB+AH/AYUB1QH3Af8BhQHVAfcB/wGFAdUB9wH/AYUB1QH3Af8BhQHVAfcB/wGF AdUB9wH/AYUB1QH3Af8BhAHVAfcB/wGDAdUB9wH/AVYBtAHTAf9oAAGaAfMC/wGVAe4B+wH/AZkB8wH+ Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZ AfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/ATsBkAGoAf8BhgHWAfoB/wGFAdUB+AH/AYUB1QH4 Af8BhQHVAfgB/wGFAdUB+AH/AYUB1QH4Af8BhQHVAfgB/wGFAdUB+AH/AYUB1QH4Af8BhQHVAfgB/wGF AdUB+AH/AYUB1QH4Af8BhQHVAfgB/wGFAdUB9wH/AYUB1QH3Af8BhQHVAfcB/wGFAdUB9wH/AYUB1QH3 Af8BhQHVAfcB/wGFAdUB9wH/AYQB1QH3Af8BgwHVAfcB/wFWAbQB0wH//wBVAAMBAQIDDwEUAyoBQAM+ AWwE/wLoAecB/wLpAegB/wLpAegB/wLqAekB/wLqAekB/wLrAeoB/wLsAesB/wLsAesB/wLsAesB/wLt AewB/wLtAewB/wLtAewB/wLuAe0B/wLuAe0B/wPuAf8C7wHuAf8C7wHuAf8D7wH/AvAB7wH/AvAB7wH/ AvAB7wH/A/AB/wLxAfAB/wLxAfAB/wPxAf8D8QH/AvIB8QH/AvIB8QH/AvIB8QH/A/IB/wPyAf8D8gH/ AvMB8gH/AvMB8gH/AvMB8gH/AvMB8gH/A/MB/wPzAf8D8wH/A/MB/wPzAf8D8wH/A/MB/wPzAf8D8wH/ A/MB/wPzAf8DKgFAAw8BFAMBAQI4AAGaAfMC/wGVAe0B+wH/AZkB8wH+Af8BmQHyAf4B/wGZAfIB/gH/ AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHz Af4B/wFkAcYB3QH/AV0BvQHdAf8BhgHWAfkB/wGGAdYB+QH/AYUB1gH5Af8BhQHWAfkB/wGFAdYB+QH/ AYUB1gH4Af8BhQHWAfgB/wGFAdYB+AH/AYUB1gH4Af8BhQHWAfgB/wGFAdYB+AH/AYUB1gH4Af8BhQHW AfgB/wGFAdYB+AH/AYUB1gH4Af8BhQHWAfgB/wGFAdUB+AH/AYUB1QH4Af8BhQHVAfgB/wGFAdUB+AH/ AYUB1QH4Af8BhAHVAfgB/wFYAbYB1AH/aAABmgHzAv8BlQHtAfsB/wGZAfMB/gH/AZkB8gH+Af8BmQHy Af4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/ AZkB8wH+Af8BZAHGAd0B/wFdAb0B3QH/AYYB1gH5Af8BhgHWAfkB/wGFAdYB+QH/AYUB1gH5Af8BhQHW AfkB/wGFAdYB+AH/AYUB1gH4Af8BhQHWAfgB/wGFAdYB+AH/AYUB1gH4Af8BhQHWAfgB/wGFAdYB+AH/ AYUB1gH4Af8BhQHWAfgB/wGFAdYB+AH/AYUB1gH4Af8BhQHVAfgB/wGFAdUB+AH/AYUB1QH4Af8BhQHV AfgB/wGFAdUB+AH/AYQB1QH4Af8BWAG2AdQB//8AVQADAQECAw8BFAMqAUADPgFsBP8B6AHnAeYB/wLo AecB/wHpAegB5wH/AukB6AH/AuoB6QH/AuoB6QH/AusB6gH/AusB6gH/AuwB6wH/AuwB6wH/A+wB/wLt AewB/wLtAewB/wLuAe0B/wLuAe0B/wLuAe0B/wPuAf8C7wHuAf8C7wHuAf8D7wH/AvAB7wH/AvAB7wH/ AvAB7wH/A/AB/wLxAfAB/wLxAfAB/wLxAfAB/wPxAf8D8QH/AvIB8QH/AvIB8QH/AvIB8QH/AvIB8QH/ A/IB/wPyAf8D8gH/A/IB/wPyAf8C8wHyAf8C8wHyAf8C8wHyAf8C8wHyAf8C8wHyAf8C8wHyAf8C8wHy Af8C8wHyAf8C8wHyAf8DKgFAAw8BFAMBAQI4AAGaAfMC/wGVAe0B+wH/AZkB8wH+Af8BmQHyAf4B/wGZ AfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfMB/gH/AWYBygHf Af8BYQHDAeQB/wGGAdcB+QH/AYYB1wH5Af8BhgHXAfkB/wGGAdcB+QH/AYYB1wH5Af8BhgHXAfkB/wGG AdcB+QH/AYYB1gH5Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5 Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHWAfkB/wGF AdYB+QH/AYUB1gH5Af8BhAHWAfkB/wNhAeZoAAGaAfMC/wGVAe0B+wH/AZkB8wH+Af8BmQHyAf4B/wGZ AfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfMB/gH/AWYBygHf Af8BYQHDAeQB/wGGAdcB+QH/AYYB1wH5Af8BhgHXAfkB/wGGAdcB+QH/AYYB1wH5Af8BhgHXAfkB/wGG AdcB+QH/AYYB1gH5Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5 Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHWAfkB/wGGAdYB+QH/AYYB1gH5Af8BhgHWAfkB/wGF AdYB+QH/AYUB1gH5Af8BhAHWAfkB/wNhAeb/AFUAAwEBAgMPARQDKgFAAz4BbAT/AucB5gH/AegB5wHm Af8C6AHnAf8B6QHoAecB/wLpAegB/wHqAekB6AH/AuoB6QH/AusB6gH/AusB6gH/AuwB6wH/AuwB6wH/ AuwB6wH/Au0B7AH/Au0B7AH/Au0B7AH/Au4B7QH/Au4B7QH/Au4B7QH/A+4B/wLvAe4B/wLvAe4B/wPv Af8D7wH/AvAB7wH/AvAB7wH/A/AB/wPwAf8C8QHwAf8C8QHwAf8C8QHwAf8C8QHwAf8D8QH/A/EB/wPx Af8C8gHxAf8C8gHxAf8C8gHxAf8C8gHxAf8C8gHxAf8C8gHxAf8C8gHxAf8C8gHxAf8C8gHxAf8C8gHx Af8C8gHxAf8C8gHxAf8C8gHxAf8DKgFAAw8BFAMBAQI4AAGaAfMC/wGVAe0B+wH/AZkB8wH+Af8BmQHy Af4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8wH+Af8BaQHOAeQB/wFkAccB6AH/ AYcB1wH6Af8BhwHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHX AfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/ AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfkB/wGFAdcB+QH/AYUB1wH5Af8BhQHX AfkB/wGdAd4B+wH/AbcB5wH8Af8BsQH0Af4B/wMKAQ1oAAGaAfMC/wGVAe0B+wH/AZkB8wH+Af8BmQHy Af4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AZkB8wH+Af8BaQHOAeQB/wFkAccB6AH/ AYcB1wH6Af8BhwHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHX AfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/ AYYB1wH6Af8BhgHXAfoB/wGGAdcB+gH/AYYB1wH6Af8BhgHXAfkB/wGFAdcB+QH/AYUB1wH5Af8BhQHX AfkB/wGdAd4B+wH/AbcB5wH8Af8BsQH0Af4B/wMKAQ3/AFUAAwEBAgMPARQDKgFAAz4BbAT/AuYB5QH/ AecB5gHlAf8C5wHmAf8C6AHnAf8B6QHoAecB/wLpAegB/wLpAegB/wLqAekB/wLqAekB/wLrAeoB/wLr AeoB/wLsAesB/wLsAesB/wLsAesB/wLtAewB/wLtAewB/wLtAewB/wLuAe0B/wLuAe0B/wLuAe0B/wPu Af8C7wHuAf8C7wHuAf8C7wHuAf8D7wH/AvAB7wH/AvAB7wH/AvAB7wH/AvAB7wH/A/AB/wPwAf8C8QHw Af8C8QHwAf8C8QHwAf8C8QHwAf8C8QHwAf8D8QH/A/EB/wPxAf8D8QH/A/EB/wPxAf8D8QH/A/EB/wPx Af8D8QH/A/EB/wMqAUADDwEUAwEBAjgAAZoB8wL/AZUB7QH7Af8BmQHzAf4B/wGZAfIB/gH/AZkB8gH+ Af8BmQHyAf4B/wGZAfIB/gH/AYIB0gHnAf8BZQHIAekB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGH AdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/AYcB2AH7 Af8BhwHYAfsB/wGHAdgB+wH/AYYB2AH7Af8BhgHYAfoB/wGGAdgB+gH/AYwB2gH7Af8BqgHjAfwB/wG5 AewB/QH/Ab0B8QH+Af8BwwH5Av8BoQHoAfUB/wFcAl4B6gNVAa0DQAFwAyIBMnQAAZoB8wL/AZUB7QH7 Af8BmQHzAf4B/wGZAfIB/gH/AZkB8gH+Af8BmQHyAf4B/wGZAfIB/gH/AYIB0gHnAf8BZQHIAekB/wGH AdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/AYcB2AH7 Af8BhwHYAfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/AYYB2AH7Af8BhgHYAfoB/wGG AdgB+gH/AYwB2gH7Af8BqgHjAfwB/wG5AewB/QH/Ab0B8QH+Af8BwwH5Av8BoQHoAfUB/wFcAl4B6gNV Aa0DQAFwAyIBMv8AYQADAQECAw8BFAMqAUADPgFsBP8B5gHlAeQB/wLmAeUB/wHnAeYB5QH/AucB5gH/ AegB5wHmAf8C6AHnAf8C6QHoAf8C6QHoAf8C6gHpAf8C6gHpAf8C6wHqAf8C6wHqAf8C7AHrAf8C7AHr Af8C7AHrAf8D7AH/Au0B7AH/Au0B7AH/Au0B7AH/Au4B7QH/Au4B7QH/Au4B7QH/A+4B/wLvAe4B/wLv Ae4B/wLvAe4B/wLvAe4B/wPvAf8D7wH/AvAB7wH/AvAB7wH/AvAB7wH/AvAB7wH/A/AB/wPwAf8D8AH/ A/AB/wLxAfAB/wLxAfAB/wLxAfAB/wLxAfAB/wLxAfAB/wLxAfAB/wLxAfAB/wLxAfAB/wLxAfAB/wLx AfAB/wMqAUADDwEUAwEBAjgAAZoB8wL/AZUB7QH7Af8BmQHzAf4B/wGZAfIB/gH/AZkB8gH+Af8BgwHU AegB/wFpAc0B7wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGHAdgB+wH/ AYcB2AH7Af8BhgHYAfsB/wGGAdgB+wH/AYYB2AH7Af8BmgHfAfwB/wG3AegB/QH/AbwB7wH+Af8BwwH1 Af4B/wG0AfIB+wH/AZEB4AHxAf8DXQHSA0wBkAMzAVMDEAEVlAABmgHzAv8BlQHtAfsB/wGZAfMB/gH/ AZkB8gH+Af8BmQHyAf4B/wGDAdQB6AH/AWkBzQHvAf8BhwHYAfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHY AfsB/wGHAdgB+wH/AYcB2AH7Af8BhwHYAfsB/wGGAdgB+wH/AYYB2AH7Af8BhgHYAfsB/wGaAd8B/AH/ AbcB6AH9Af8BvAHvAf4B/wHDAfUB/gH/AbQB8gH7Af8BkQHgAfEB/wNdAdIDTAGQAzMBUwMQARX/AIEA AwEBAgMNARIDKAE8AzsBZcD/AygBPAMNARIDAQECOAABmgHzAv8BlAHsAfoB/wGZAfMB/gH/AYYB2AHr Af8BaQHNAe8B/wGHAdgB+wH/AYYB2AH7Af8BhgHYAfsB/wGIAdkB+wH/AaYB4gH8Af8BuQHsAf0B/wG/ AfIB/gH/AcMB+AL/AaUB6gH3Af8BZwFqAW8B8gNVAbUDQwF4AycBOrgAAZoB8wL/AZQB7AH6Af8BmQHz Af4B/wGGAdgB6wH/AWkBzQHvAf8BhwHYAfsB/wGGAdgB+wH/AYYB2AH7Af8BiAHZAfsB/wGmAeIB/AH/ AbkB7AH9Af8BvwHyAf4B/wHDAfgC/wGlAeoB9wH/AWcBagFvAfIDVQG1A0MBeAMnATr/AKgAAQEDCgEN Ax8BLAMvAUoDNQFWAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFY AzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFY AzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFYAzUBWAM1AVgDNQFY AzUBWAM1AVgDNQFYAzUBVgMvAUoDHwEsAwoBDQMAAQE4AAFfAZIBlwH7AYUB1gHrAf8BugHpAfcB/wHB AfQC/wG5AfQB/QH/AZUB4gHzAf8DXgHVA04BmQM3AVsDFQEd2AABXwGSAZcB+wGFAdYB6wH/AboB6QH3 Af8BwQH0Av8BuQH0Af0B/wGVAeIB8wH/A14B1QNOAZkDNwFbAxUBHf8AyQADBAEGAw8BFAMYASEDHAEn AxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEo AxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEo AxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEoAxwBKAMcASgDHAEo AxwBJwMYASEDDwEUAwQBBv8A/wD/ADIAAQEDAwQEBAYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMG AQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMG AQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMG AQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBgEIAwYBCAMGAQgDBAEGAwMBBAMAAQH/AP8A/wD/AP8A /wD/AB8AAUIBTQE+BwABPgMAASgEAAEBAgABQAMAAQEBAAEBBgABCBYAA/8BABj/CAAB/AQABf8B4Af/ AeAF/wgAAfgEAAE/BP8BgAF/Bv8BgAF/BP8IAAHwBAABHwT/AQABHwH/AYABPwP/AQABHwH/AYABPwH/ CAAB8AQAAQ8D/wH+AQABDwH4AQABDwL/Af4BAAEPAfgBAAEPAf8IAAHwBAABBwP/AfwBAAEHAYABAAEH Av8B/AEAAQcBgAEAAQcB/wgAAfAEAAEBA/8B+AQAAQMC/wH4BAABAwH/CAAB8AUAA/8B8AQAAQEC/wHw BAABAQH/CAAB8AUAAX8C/wHgBQAC/wHgBQAB/wgAAfAFAAE/Av8BwAUAAX8B/wHABQABfwgAAfAFAAEf Av8BgAUAAT8B/wGABQABPwgAAfAFAAEPAv8GAAEfAf8GAAEfCAAB8AUAAQcC/wYAAQ8B/wYAAQ8IAAHw BQABAwL/BgABBwH/BgABBwgAAfAFAAEBAv8GAAEDAf8GAAEDCAAB8AUAAQEC/wGABQABAQH/AYAFAAEB CAAB8AYAAv8BgAYAAf8BgA4AAfAGAAF/Af8BgAYAAf8BgA4AAfAGAAE/Af8BgAYAAf8BgA4AAfAGAAE/ Af8BgAYAAf8BgA4AAfAGAAEfAf8BgAUAAQEB/wGABQABAQgAAfAGAAEfAf8BgAUAAX8B/wGABQABAwgA AfAGAAEfAf8BgAQAAS8C/wGABAABIAEDCAAB8AYAAR8B/wGABAABPwL/AYAEAAE4AQMIAAHwBgABHwH/ AYAEAAE/Av8BgAQAATwBAwgAAfAGAAEfAf8BgAQAAT8C/wGABAABPAEDCAAB8AYAAR8B/wGABAABfwL/ AYAEAAFwAQMIAAHwBgABHwH/AYADAAEBA/8BgAUAAQMIAAHwBgABHwH/AYADAAEBA/8BgAUAAQMIAAHw BgABHwH/AYADAAEBA/8BgAUAAQMIAAHwBgABHwH/AYADAAEBA/8BgAUAAQMIAAHwBgABHwH/AYADAAEB A/8BgAUAAQMIAAHwBgABHwH/AYADAAEBA/8BgAUAAQMIAAHwBgABHwH/AYADAAEBA/8BgAUAAQcIAAHw BgABHwH/AYADAAEBA/8BgAUAAQ8IAAHwBgABHwH/AYADAAEBA/8BgAUAAR8IAAHwBgABHwH/AYADAAEB A/8BgAUAAT8IAAHwBgABHwH/AYADAAEBA/8BgAUAAf8IAAHwBgABHwH/AYADAAEBA/8BgAQAAQMB/wgA AfAGAAEfAf8BgAMAAQED/wGABAABPwH/CAAB8AYAAR8B/wGAAwABAQP/AYADAAEBAv8IAAHwBgABHwH/ AYADAAEBA/8BgAMAAQEC/wgAAfAGAAEfAf8BgAMAAQED/wGAAwABAQL/CAAB8AYAAR8B/wGAAwABAQP/ AYADAAEBAv8IAAHwBgABHwH/AYADAAEBA/8BgAMAAQEC/wgAAfAGAAEfAf8BgAMAAQED/wGAAwABAQL/ CAAB8AYAAR8B/wGAAwABAQP/AYADAAEBAv8IAAHwBgABHwH/AYADAAEBA/8BgAMAAQEC/wgAAfAGAAEf Af8BgAMAAQED/wGAAwABAQL/CAAB8AYAAR8B/wGAAwABAQP/AYADAAEBAv8IAAHwBgABHwH/AYADAAEB A/8BgAMAAQEC/wgAAfAGAAEfAf8BgAMAAQED/wGAAwABAQL/CAAB8AYAAR8B/wGAAwABAQP/AYADAAEB Av8IAAHwBgABHwH/AYADAAEBA/8BgAMAAQEC/wgAAfAGAAEfAf8BgAMAAQED/wGAAwABAQL/CAAB8AYA AR8B/wGAAwABAQP/AYADAAEBAv8IAAHwBgABHwH/AYADAAEBA/8BgAMAAQEC/wgAAfAGAAEfAf8BgAMA AQ8D/wGAAwABDwL/CAAB8AYAAR8B/wGAAgABDwT/AYACAAEPA/8IAAHwBgABHwH/AYABAAEfBf8BgAEA AR8E/wgAAfAGAAEfAf8BgAEfBv8BgAEfBf8IAAH4BgABPxD/CAAB+AYAAT8Q/wgAGP8IAAs= 318, 17 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveGraphDeviceLoginForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveGraphDeviceLoginForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OneDriveGraphDeviceLoginForm)); this.MicrosoftLogoPictureBox = new System.Windows.Forms.PictureBox(); this.StatusLabel = new System.Windows.Forms.Label(); this.DeviceIdTextBox = new System.Windows.Forms.TextBox(); this.ExplanationLabel = new System.Windows.Forms.Label(); this.DeviceAuthLinkLabel = new System.Windows.Forms.LinkLabel(); this.OpenBrowserButton = new System.Windows.Forms.Button(); this.CopyDeviceIdButton = new System.Windows.Forms.Button(); this.CancelButton = new System.Windows.Forms.Button(); this.StartSessionTimer = new System.Windows.Forms.Timer(this.components); this.AuthenticationCheckTimer = new System.Windows.Forms.Timer(this.components); this.AuthenticationCompleteTimer = new System.Windows.Forms.Timer(this.components); this.MoreInformationLinkLabel = new System.Windows.Forms.LinkLabel(); ((System.ComponentModel.ISupportInitialize)(this.MicrosoftLogoPictureBox)).BeginInit(); this.SuspendLayout(); // // MicrosoftLogoPictureBox // this.MicrosoftLogoPictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.MicrosoftLogoPictureBox.BackColor = System.Drawing.Color.White; this.MicrosoftLogoPictureBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.MicrosoftLogoPictureBox.Location = new System.Drawing.Point(12, 12); this.MicrosoftLogoPictureBox.Name = "MicrosoftLogoPictureBox"; this.MicrosoftLogoPictureBox.Size = new System.Drawing.Size(497, 103); this.MicrosoftLogoPictureBox.TabIndex = 0; this.MicrosoftLogoPictureBox.TabStop = false; // // StatusLabel // this.StatusLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.StatusLabel.Location = new System.Drawing.Point(12, 244); this.StatusLabel.Name = "StatusLabel"; this.StatusLabel.Size = new System.Drawing.Size(497, 31); this.StatusLabel.TabIndex = 1; this.StatusLabel.Text = "Initializing"; this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // DeviceIdTextBox // this.DeviceIdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.DeviceIdTextBox.BackColor = System.Drawing.SystemColors.Control; this.DeviceIdTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; this.DeviceIdTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 24F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.DeviceIdTextBox.Location = new System.Drawing.Point(10, 284); this.DeviceIdTextBox.Name = "DeviceIdTextBox"; this.DeviceIdTextBox.Size = new System.Drawing.Size(497, 37); this.DeviceIdTextBox.TabIndex = 2; this.DeviceIdTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.DeviceIdTextBox.Visible = false; // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(12, 121); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(497, 85); this.ExplanationLabel.TabIndex = 3; this.ExplanationLabel.Text = "A unique device ID will be retrieved from Microsoft to perform your logon to OneDrive or OneDrive for Business through the Microsoft Graph in your favorite web browser. Once the device ID is shown below, click on the link that shows up on this form to open your default browser, paste in the device ID and perform the authentication to continue with setting up your KeePass synchronization. Leave this form open while you authenticate in your browser. Once you've authenticated in your browser, you may close your browser and this application should automatically proceed."; this.ExplanationLabel.UseMnemonic = false; // // DeviceAuthLinkLabel // this.DeviceAuthLinkLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.DeviceAuthLinkLabel.Location = new System.Drawing.Point(12, 220); this.DeviceAuthLinkLabel.Name = "DeviceAuthLinkLabel"; this.DeviceAuthLinkLabel.Size = new System.Drawing.Size(497, 23); this.DeviceAuthLinkLabel.TabIndex = 4; this.DeviceAuthLinkLabel.TabStop = true; this.DeviceAuthLinkLabel.Text = "Unknown"; this.DeviceAuthLinkLabel.UseMnemonic = false; this.DeviceAuthLinkLabel.Visible = false; this.DeviceAuthLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.DeviceAuthLinkLabel_LinkClicked); // // OpenBrowserButton // this.OpenBrowserButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.OpenBrowserButton.Enabled = false; this.OpenBrowserButton.Location = new System.Drawing.Point(10, 334); this.OpenBrowserButton.Name = "OpenBrowserButton"; this.OpenBrowserButton.Size = new System.Drawing.Size(106, 37); this.OpenBrowserButton.TabIndex = 5; this.OpenBrowserButton.Text = "&Open in browser"; this.OpenBrowserButton.UseVisualStyleBackColor = true; this.OpenBrowserButton.Click += new System.EventHandler(this.OpenBrowserButton_Click); // // CopyDeviceIdButton // this.CopyDeviceIdButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.CopyDeviceIdButton.Enabled = false; this.CopyDeviceIdButton.Location = new System.Drawing.Point(131, 334); this.CopyDeviceIdButton.Name = "CopyDeviceIdButton"; this.CopyDeviceIdButton.Size = new System.Drawing.Size(106, 37); this.CopyDeviceIdButton.TabIndex = 6; this.CopyDeviceIdButton.Text = "&Copy Device ID"; this.CopyDeviceIdButton.UseVisualStyleBackColor = true; this.CopyDeviceIdButton.Click += new System.EventHandler(this.CopyDeviceIdButton_Click); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CancelButton.Location = new System.Drawing.Point(403, 334); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(106, 37); this.CancelButton.TabIndex = 7; this.CancelButton.Text = "C&ancel"; this.CancelButton.UseVisualStyleBackColor = true; this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); // // StartSessionTimer // this.StartSessionTimer.Interval = 1000; this.StartSessionTimer.Tick += new System.EventHandler(this.StartSessionTimer_Tick); // // AuthenticationCheckTimer // this.AuthenticationCheckTimer.Interval = 1000; this.AuthenticationCheckTimer.Tick += new System.EventHandler(this.AuthenticationCheckTimer_Tick); // // AuthenticationCompleteTimer // this.AuthenticationCompleteTimer.Interval = 3000; this.AuthenticationCompleteTimer.Tick += new System.EventHandler(this.AuthenticationCompleteTimer_Tick); // // MoreInformationLinkLabel // this.MoreInformationLinkLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.MoreInformationLinkLabel.AutoSize = true; this.MoreInformationLinkLabel.Location = new System.Drawing.Point(259, 346); this.MoreInformationLinkLabel.Name = "MoreInformationLinkLabel"; this.MoreInformationLinkLabel.Size = new System.Drawing.Size(85, 13); this.MoreInformationLinkLabel.TabIndex = 8; this.MoreInformationLinkLabel.TabStop = true; this.MoreInformationLinkLabel.Text = "More information"; this.MoreInformationLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.MoreInformationLinkLabel_LinkClicked); // // OneDriveGraphDeviceLoginForm // this.AcceptButton = this.OpenBrowserButton; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(521, 383); this.Controls.Add(this.MoreInformationLinkLabel); this.Controls.Add(this.CancelButton); this.Controls.Add(this.CopyDeviceIdButton); this.Controls.Add(this.OpenBrowserButton); this.Controls.Add(this.DeviceAuthLinkLabel); this.Controls.Add(this.ExplanationLabel); this.Controls.Add(this.DeviceIdTextBox); this.Controls.Add(this.StatusLabel); this.Controls.Add(this.MicrosoftLogoPictureBox); this.Margin = new System.Windows.Forms.Padding(2); this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(459, 422); this.Name = "OneDriveGraphDeviceLoginForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Authenticate to OneDrive"; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.OneDriveGraphDeviceLoginForm_FormClosed); ((System.ComponentModel.ISupportInitialize)(this.MicrosoftLogoPictureBox)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.PictureBox MicrosoftLogoPictureBox; private System.Windows.Forms.Label StatusLabel; private System.Windows.Forms.TextBox DeviceIdTextBox; private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.LinkLabel DeviceAuthLinkLabel; private System.Windows.Forms.Button OpenBrowserButton; private System.Windows.Forms.Button CopyDeviceIdButton; private System.Windows.Forms.Button CancelButton; private System.Windows.Forms.Timer StartSessionTimer; private System.Windows.Forms.Timer AuthenticationCheckTimer; private System.Windows.Forms.Timer AuthenticationCompleteTimer; private System.Windows.Forms.LinkLabel MoreInformationLinkLabel; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveGraphDeviceLoginForm.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Windows.Forms; using KoenZomers.OneDrive.Api; using Newtonsoft.Json; namespace KoenZomersKeePassOneDriveSync { /// /// Performs authentication against the Microsoft Graph API using the Device Code Flow (https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code) /// public partial class OneDriveGraphDeviceLoginForm : Form { /// /// URL to link the More information link to /// private const string _moreInformationUrl = "https://github.com/KoenZomers/KeePassOneDriveSync/wiki/FAQ#how-does-the-microsoft-graph-any-browser-option-work"; /// /// URL to send a POST request to in order to retrieve a Device ID Code from Microsoft Graph /// private const string _microsoftGraphDeviceAuthorizationInitiationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode"; /// /// URL to send a POST request to in order to verify if the device session has been authenticated already /// private const string _microsoftGraphDeviceAuthorizationStatusUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; /// /// Counter to keep track of how many seconds there are left to authenticate the session /// private int _authenticatedCheckCountDownCount; /// /// Counter to keep track of how many checks to wait before actually contacting the Microsoft Graph API again to validate if the authentication has succeeded /// private int _authenticationCheckSkipCounter; /// /// Amount of validation checks to wait before actually contacting the Microsoft Graph API again to validate if the authentication has succeeded /// private int _authenticationCheckSkipValue; /// /// HTTP Client to use to communicate with the Microsoft Graph /// private HttpClient _httpClient; /// /// OneDrive Graph API instance /// public OneDriveGraphApi OneDriveApi { get; private set; } /// /// Returns the Refresh Token obtained through the Device ID login /// public string RefreshToken { get; private set; } public OneDriveGraphDeviceLoginForm(OneDriveApi oneDriveApi) { InitializeComponent(); // Cast the OneDrive instance to a Graph API instance so it can be used in this form OneDriveApi = (OneDriveGraphApi)oneDriveApi; // When the form is being closed, ensure the Timer processes are being stopped to avoid exceptions FormClosing += OneDriveGraphDeviceLoginForm_FormClosing; // Set the defaults of the components on the form StatusLabel.Text = "Communicating with the Microsoft Graph API..."; DeviceAuthLinkLabel.Visible = false; DeviceIdTextBox.Visible = false; OpenBrowserButton.Enabled = false; CopyDeviceIdButton.Enabled = false; AuthenticationCheckTimer.Enabled = false; AuthenticationCompleteTimer.Enabled = false; // Set the Microsoft logo on the form MicrosoftLogoPictureBox.BackgroundImage = Resources.MicrosoftLogo; // Set up a HttpClient obeying the possible proxy settings to communicate with the Microsoft Graph API _httpClient = CreateHttpClient(); // Enable a timer to initiate the communication with Microsoft Graph so the UI thread doesn't block StartSessionTimer.Enabled = true; } /// /// When the form is closed, ensure Timer resources are being stopped /// private void OneDriveGraphDeviceLoginForm_FormClosing(object sender, FormClosingEventArgs e) { if(AuthenticationCheckTimer != null && AuthenticationCheckTimer.Enabled) { AuthenticationCheckTimer.Enabled = false; } if(StartSessionTimer != null && StartSessionTimer.Enabled) { StartSessionTimer.Enabled = false; } if (AuthenticationCompleteTimer != null && AuthenticationCompleteTimer.Enabled) { AuthenticationCompleteTimer.Enabled = false; } } /// /// Makes a request to the Microsoft Graph API to set up a new device code flow session and shows the results on the form /// private void GetMicrosoftGraphDeviceId() { // Construct the mandatory form post parameters to send var postFormValues = new List>(); postFormValues.Add(new KeyValuePair("client_id", KoenZomersKeePassOneDriveSyncExt.GraphApiApplicationId)); postFormValues.Add(new KeyValuePair("scope", OneDriveApi.DefaultScopes.Aggregate((x, y) => string.Format("{0} {1}", x, y)))); StatusLabel.Text = "Requesting Device ID code..."; // Construct the message towards the Microsoft Graph API using (var request = new HttpRequestMessage(HttpMethod.Post, _microsoftGraphDeviceAuthorizationInitiationUrl)) { request.Content = new FormUrlEncodedContent(postFormValues); // Request the response from the webservice HttpResponseMessage response; try { response = _httpClient.SendAsync(request).Result; } catch(Exception e) { while (e.InnerException != null) e = e.InnerException; StatusLabel.Text = string.Format("Failed to connect to the Graph API ({0})", e.Message); return; } Providers.MicrosoftGraph.MicrosoftGraphDeviceCodeResponse microsoftGraphDeviceCodeResponse; try { microsoftGraphDeviceCodeResponse = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result); } catch(Exception e) { while (e.InnerException != null) e = e.InnerException; StatusLabel.Text = string.Format("Failed to parse the Graph API response ({0})", e.Message); return; } // Ensure the required information has been received if (microsoftGraphDeviceCodeResponse != null && !string.IsNullOrEmpty(microsoftGraphDeviceCodeResponse.DeviceCode) && !string.IsNullOrEmpty(microsoftGraphDeviceCodeResponse.UserCode) && microsoftGraphDeviceCodeResponse.VerificationUri != null) { DeviceIdTextBox.Text = microsoftGraphDeviceCodeResponse.UserCode; DeviceIdTextBox.Tag = microsoftGraphDeviceCodeResponse.DeviceCode; DeviceIdTextBox.Visible = true; DeviceAuthLinkLabel.Text = microsoftGraphDeviceCodeResponse.VerificationUri.AbsoluteUri; DeviceAuthLinkLabel.Visible = true; CopyDeviceIdButton.Enabled = true; OpenBrowserButton.Enabled = true; OpenBrowserButton.Focus(); _authenticatedCheckCountDownCount = microsoftGraphDeviceCodeResponse.ExpiresIn.GetValueOrDefault(180); _authenticationCheckSkipValue = microsoftGraphDeviceCodeResponse.Interval.GetValueOrDefault(2); AuthenticationCheckTimer.Enabled = true; } else { StatusLabel.Text = "Invalid response received from Microsoft Graph"; } } } /// /// Tries to parse the provided JSON string into the provided type T /// /// Type to try to parse the JSON structure in /// JSON as a string to parse /// Object of type T containing the parsed JSON /// True if the parsing succeeded, false if it failed public static bool TryParseJson(string json, out T result) { bool success = true; var settings = new JsonSerializerSettings { Error = (sender, args) => { success = false; args.ErrorContext.Handled = true; }, MissingMemberHandling = MissingMemberHandling.Error }; result = JsonConvert.DeserializeObject(json, settings); return success; } /// /// Makes a request to the Microsoft Graph API to validate if the device code session has authenticated already /// /// NULL if the authentication did not take place yet or a refresh token if it has private string HasUserAuthenticated() { // Construct the mandatory form post parameters to send var postFormValues = new List>(); postFormValues.Add(new KeyValuePair("grant_type", "urn:ietf:params:oauth:grant-type:device_code")); postFormValues.Add(new KeyValuePair("device_code", DeviceIdTextBox.Tag.ToString())); postFormValues.Add(new KeyValuePair("client_id", KoenZomersKeePassOneDriveSyncExt.GraphApiApplicationId)); // Construct the message towards the Microsoft Graph API using (var request = new HttpRequestMessage(HttpMethod.Post, _microsoftGraphDeviceAuthorizationStatusUrl)) { request.Content = new FormUrlEncodedContent(postFormValues); // Request the response from the Microsoft Graph API using (var response = _httpClient.SendAsync(request).Result) { var responseString = response.Content.ReadAsStringAsync().Result; // Try and see if the result can be parsed as an error result typically indicating that the authentication did not take place yet Providers.MicrosoftGraph.MicrosoftGraphDeviceCodeAuthCheckFailedResponse failedResult; if (TryParseJson(responseString, out failedResult)) { switch (failedResult.Error) { case "authorization_pending": // all good, just wait for it to be done break; case "authorization_declined": AuthenticationCheckTimer.Enabled = false; StatusLabel.Text = "Authorization declined"; break; case "expired_token": AuthenticationCheckTimer.Enabled = false; StatusLabel.Text = "Authentication timed out"; break; case "bad_verification_code": AuthenticationCheckTimer.Enabled = false; StatusLabel.Text = "Device code not recognized"; break; default: AuthenticationCheckTimer.Enabled = false; StatusLabel.Text = string.Format("Unexpected error: {0}", failedResult.ErrorDescription); break; } return null; } // Try and see if the result can be parsed as a successful result indicating that the authentication has taken place Providers.MicrosoftGraph.MicrosoftGraphDeviceCodeAuthCheckSucceededResponse succeededResult; if (TryParseJson(responseString, out succeededResult)) { return succeededResult.RefreshToken; } // Unknown response received AuthenticationCheckTimer.Enabled = false; StatusLabel.Text = "Unknown response received"; return null; } } } /// /// Instantiates a new HttpClient preconfigured for use. Note that the caller is responsible for disposing this object. /// /// HttpClient instance private HttpClient CreateHttpClient() { var proxySettings = Utilities.GetProxySettings(); var proxyCredentials = Utilities.GetProxyCredentials(); // Define the HttpClient settings var httpClientHandler = new HttpClientHandler { UseDefaultCredentials = proxyCredentials == null, UseProxy = proxySettings != null, Proxy = proxySettings }; // Check if we need specific credentials for the proxy if (proxyCredentials != null && httpClientHandler.Proxy != null) { httpClientHandler.Proxy.Credentials = proxyCredentials; } // Create the new HTTP Client var httpClient = new HttpClient(httpClientHandler); return httpClient; } private void CancelButton_Click(object sender, EventArgs e) { AuthenticationCheckTimer.Enabled = false; Close(); } /// /// Triggers when the communication with the Microsoft Graph should start /// private void StartSessionTimer_Tick(object sender, EventArgs e) { // Only perform this action once StartSessionTimer.Enabled = false; // Initiate the retrieval of a Device ID GetMicrosoftGraphDeviceId(); } /// /// Triggers to validate if the device id session has been authenticated /// private void AuthenticationCheckTimer_Tick(object sender, EventArgs e) { _authenticatedCheckCountDownCount--; if(_authenticatedCheckCountDownCount == 0) { StatusLabel.Text = "Session authentication has timed out"; AuthenticationCheckTimer.Enabled = false; return; } var timeRemaining = TimeSpan.FromSeconds(_authenticatedCheckCountDownCount); StatusLabel.Text = string.Format("Waiting for authentication to complete ({0:D2}:{1:D2})", timeRemaining.Minutes, timeRemaining.Seconds); // Don't connect to the Microsoft Graph API every second as otherwise it will throw an error. Wait for the amount of times as instructed by the Microsoft Graph API when setting up the device login session. _authenticationCheckSkipCounter++; if(_authenticationCheckSkipCounter < _authenticationCheckSkipValue) { return; } _authenticationCheckSkipCounter = 0; var result = HasUserAuthenticated(); if (result != null) { AuthenticationCheckTimer.Enabled = false; RefreshToken = result; DialogResult = DialogResult.OK; StatusLabel.Text = "Authentication successful"; AuthenticationCompleteTimer.Enabled = true; } } /// /// Copies the Device ID to the Windows Clipboard /// private void CopyDeviceIdButton_Click(object sender, EventArgs e) { if (!DeviceIdTextBox.Enabled) return; Clipboard.SetText(DeviceIdTextBox.Text); } private void OpenBrowserButton_Click(object sender, EventArgs e) { if (!OpenBrowserButton.Enabled) return; System.Diagnostics.Process.Start(DeviceAuthLinkLabel.Text); } private void DeviceAuthLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { if (!DeviceAuthLinkLabel.Visible) return; System.Diagnostics.Process.Start(DeviceAuthLinkLabel.Text); } private void OneDriveGraphDeviceLoginForm_FormClosed(object sender, FormClosedEventArgs e) { if (_httpClient != null) { _httpClient.Dispose(); } } private void AuthenticationCompleteTimer_Tick(object sender, EventArgs e) { AuthenticationCompleteTimer.Enabled = false; Close(); } private void MoreInformationLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { System.Diagnostics.Process.Start(_moreInformationUrl); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveGraphDeviceLoginForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 A unique device ID will be retrieved from Microsoft to perform your logon to OneDrive or OneDrive for Business through the Microsoft Graph in your favorite web browser. Once the device ID is shown below, click on the link that shows up on this form to open your default browser, paste in the device ID and perform the authentication to continue with setting up your KeePass synchronization. Leave this form open while you authenticate in your browser. Once you've authenticated in your browser, you may close your browser and this application should automatically proceed. 17, 17 166, 17 362, 17 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveGraphDeviceLoginForm1.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace KoenZomersKeePassOneDriveSync.Forms { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class OneDriveGraphDeviceLoginForm { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal OneDriveGraphDeviceLoginForm() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("KoenZomersKeePassOneDriveSync.Forms.OneDriveGraphDeviceLoginForm", typeof(OneDriveGraphDeviceLoginForm).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized resource of type System.Drawing.Point similar to {X=166,Y=17}. /// public static System.Drawing.Point AuthenticationCheckTimer_TrayLocation { get { object obj = ResourceManager.GetObject("AuthenticationCheckTimer.TrayLocation", resourceCulture); return ((System.Drawing.Point)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Point similar to {X=362,Y=17}. /// public static System.Drawing.Point AuthenticationCompleteTimer_TrayLocation { get { object obj = ResourceManager.GetObject("AuthenticationCompleteTimer.TrayLocation", resourceCulture); return ((System.Drawing.Point)(obj)); } } /// /// Looks up a localized string similar to A unique device ID will be retrieved from Microsoft to perform your logon to OneDrive or OneDrive for Business through the Microsoft Graph in your favorite web browser. Once the device ID is shown below, click on the link that shows up on this form to open your default browser, paste in the device ID and perform the authentication to continue with setting up your KeePass synchronization. Leave this form open while you authenticate in your browser. Once you've authenticated in your browser, you may close you [rest of string was truncated]";. /// public static string ExplanationLabel_Text { get { return ResourceManager.GetString("ExplanationLabel.Text", resourceCulture); } } /// /// Looks up a localized resource of type System.Drawing.Point similar to {X=17,Y=17}. /// public static System.Drawing.Point StartSessionTimer_TrayLocation { get { object obj = ResourceManager.GetObject("StartSessionTimer.TrayLocation", resourceCulture); return ((System.Drawing.Point)(obj)); } } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveRefreshTokenStorageDialog.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync { partial class OneDriveRefreshTokenStorageDialog { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.ExplanationLabel = new System.Windows.Forms.Label(); this.InKeePassDatabaseRadio = new System.Windows.Forms.RadioButton(); this.OnDiskRadio = new System.Windows.Forms.RadioButton(); this.FinishButton = new System.Windows.Forms.Button(); this.HelpMeChooseLinkLabel = new System.Windows.Forms.LinkLabel(); this.SuspendLayout(); // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(14, 11); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(744, 43); this.ExplanationLabel.TabIndex = 0; this.ExplanationLabel.Text = "You\'ve successfully authenticated to OneDrive. Where should KeePassOneDriveSync store" + " the OneDrive Refresh Token so it can keep your local database in sync with its " + "equivalent on OneDrive?"; // // InKeePassDatabaseRadio // this.InKeePassDatabaseRadio.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.InKeePassDatabaseRadio.AutoSize = true; this.InKeePassDatabaseRadio.Location = new System.Drawing.Point(18, 76); this.InKeePassDatabaseRadio.Name = "InKeePassDatabaseRadio"; this.InKeePassDatabaseRadio.Size = new System.Drawing.Size(250, 24); this.InKeePassDatabaseRadio.TabIndex = 2; this.InKeePassDatabaseRadio.Text = "In the KeePass database itself"; this.InKeePassDatabaseRadio.UseVisualStyleBackColor = true; // // OnDiskRadio // this.OnDiskRadio.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.OnDiskRadio.AutoSize = true; this.OnDiskRadio.Checked = true; this.OnDiskRadio.Location = new System.Drawing.Point(18, 108); this.OnDiskRadio.Name = "OnDiskRadio"; this.OnDiskRadio.Size = new System.Drawing.Size(186, 24); this.OnDiskRadio.TabIndex = 3; this.OnDiskRadio.TabStop = true; this.OnDiskRadio.Text = "On my local computer"; this.OnDiskRadio.UseVisualStyleBackColor = true; // // FinishButton // this.FinishButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.FinishButton.Location = new System.Drawing.Point(278, 194); this.FinishButton.Name = "FinishButton"; this.FinishButton.Size = new System.Drawing.Size(195, 63); this.FinishButton.TabIndex = 5; this.FinishButton.Text = "&Finish"; this.FinishButton.UseVisualStyleBackColor = true; this.FinishButton.Click += new System.EventHandler(this.FinishButton_Click); // // HelpMeChooseLinkLabel // this.HelpMeChooseLinkLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.HelpMeChooseLinkLabel.AutoSize = true; this.HelpMeChooseLinkLabel.Location = new System.Drawing.Point(20, 157); this.HelpMeChooseLinkLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.HelpMeChooseLinkLabel.Name = "HelpMeChooseLinkLabel"; this.HelpMeChooseLinkLabel.Size = new System.Drawing.Size(228, 20); this.HelpMeChooseLinkLabel.TabIndex = 7; this.HelpMeChooseLinkLabel.TabStop = true; this.HelpMeChooseLinkLabel.Text = "I have no idea, help me choose"; this.HelpMeChooseLinkLabel.UseMnemonic = false; this.HelpMeChooseLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.HelpMeChooseLinkLabel_LinkClicked); // // OneDriveRefreshTokenStorageDialog // this.AcceptButton = this.FinishButton; this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(771, 283); this.Controls.Add(this.HelpMeChooseLinkLabel); this.Controls.Add(this.FinishButton); this.Controls.Add(this.OnDiskRadio); this.Controls.Add(this.InKeePassDatabaseRadio); this.Controls.Add(this.ExplanationLabel); this.MaximizeBox = false; this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(602, 313); this.Name = "OneDriveRefreshTokenStorageDialog"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Where to store the OneDrive Refresh Token"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OneDriveRefreshTokenStorageDialog_FormClosing); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.Button FinishButton; private System.Windows.Forms.RadioButton InKeePassDatabaseRadio; private System.Windows.Forms.RadioButton OnDiskRadio; private System.Windows.Forms.LinkLabel HelpMeChooseLinkLabel; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveRefreshTokenStorageDialog.cs ================================================ using System; using System.Windows.Forms; using KoenZomers.KeePass.OneDriveSync; using KoenZomers.KeePass.OneDriveSync.Enums; namespace KoenZomersKeePassOneDriveSync { public partial class OneDriveRefreshTokenStorageDialog : Form { /// /// Configuration of the database for which this question is being asked for /// private readonly Configuration _configuration; public OneDriveRefreshTokenStorageDialog(Configuration configuration) { InitializeComponent(); _configuration = configuration; } private void HelpMeChooseLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { System.Diagnostics.Process.Start("https://github.com/KoenZomers/KeePassOneDriveSync/wiki/OneDrive-Refresh-Token"); } private void FinishButton_Click(object sender, EventArgs e) { Close(); } private void OneDriveRefreshTokenStorageDialog_FormClosing(object sender, FormClosingEventArgs e) { if (InKeePassDatabaseRadio.Checked) { _configuration.RefreshTokenStorage = OneDriveRefreshTokenStorage.KeePassDatabase; } if (OnDiskRadio.Checked) { _configuration.RefreshTokenStorage = OneDriveRefreshTokenStorage.DiskEncrypted; } } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveRefreshTokenStorageDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveRemoteLocationDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveRequestInputDialog.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync.Forms { partial class OneDriveRequestInputDialog { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.InputTextBox = new System.Windows.Forms.TextBox(); this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); this.SuspendLayout(); // // InputTextBox // this.InputTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.InputTextBox.Location = new System.Drawing.Point(9, 10); this.InputTextBox.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.InputTextBox.Name = "InputTextBox"; this.InputTextBox.Size = new System.Drawing.Size(301, 20); this.InputTextBox.TabIndex = 0; this.InputTextBox.TextChanged += new System.EventHandler(this.InputTextBoxTextBox_TextChanged); this.InputTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.InputTextBox_KeyPress); this.InputTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.InputTextBox_KeyUp); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CancelButton.Location = new System.Drawing.Point(237, 41); this.CancelButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(72, 32); this.CancelButton.TabIndex = 7; this.CancelButton.Text = "&Cancel"; this.CancelButton.UseVisualStyleBackColor = true; this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); // // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.OKButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.OKButton.Enabled = false; this.OKButton.Location = new System.Drawing.Point(160, 41); this.OKButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(72, 32); this.OKButton.TabIndex = 6; this.OKButton.Text = "&OK"; this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // // OneDriveRequestInputDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(318, 82); this.ControlBox = false; this.Controls.Add(this.CancelButton); this.Controls.Add(this.OKButton); this.Controls.Add(this.InputTextBox); this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "OneDriveRequestInputDialog"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.TextBox InputTextBox; private System.Windows.Forms.Button CancelButton; private System.Windows.Forms.Button OKButton; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveRequestInputDialog.cs ================================================ using System; using System.Windows.Forms; namespace KoenZomersKeePassOneDriveSync.Forms { public partial class OneDriveRequestInputDialog : Form { /// /// Gets or sets the value in the textbox /// public string InputValue { get { return InputTextBox.Text; } set { InputTextBox.Text = value; } } /// /// Title shown on the dialog /// public string FormTitle { get { return Text; } set { Text = value; } } public OneDriveRequestInputDialog() { InitializeComponent(); } private void OKButton_Click(object sender, EventArgs e) { DialogResult = DialogResult.OK; Close(); } private void CancelButton_Click(object sender, EventArgs e) { Close(); } private void InputTextBoxTextBox_TextChanged(object sender, EventArgs e) { OKButton.Enabled = InputTextBox.Text.Length > 0; } private void InputTextBox_KeyPress(object sender, KeyPressEventArgs e) { } private void InputTextBox_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) OKButton_Click(sender, e); if (e.KeyCode == Keys.Escape) Close(); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/OneDriveRequestInputDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/SharePointCredentialsForm.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync.Forms { partial class SharePointCredentialsForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.ExplanationLabel = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.SharePointUrlTextBox = new System.Windows.Forms.TextBox(); this.label2 = new System.Windows.Forms.Label(); this.ClientIdTextBox = new System.Windows.Forms.TextBox(); this.ClientSecretTextBox = new System.Windows.Forms.TextBox(); this.label3 = new System.Windows.Forms.Label(); this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); this.TestButton = new System.Windows.Forms.Button(); this.SuspendLayout(); // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(9, 7); this.ExplanationLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(423, 29); this.ExplanationLabel.TabIndex = 1; this.ExplanationLabel.Text = "Enter the details of the SharePoint 2013, 2016, 2019 or Online environment you wi" + "sh to store the KeePass database on."; // // label1 // this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.label1.Location = new System.Drawing.Point(9, 54); this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(113, 19); this.label1.TabIndex = 2; this.label1.Text = "SharePoint site URL:"; // // SharePointUrlTextBox // this.SharePointUrlTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.SharePointUrlTextBox.Location = new System.Drawing.Point(121, 54); this.SharePointUrlTextBox.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.SharePointUrlTextBox.Name = "SharePointUrlTextBox"; this.SharePointUrlTextBox.Size = new System.Drawing.Size(312, 20); this.SharePointUrlTextBox.TabIndex = 0; this.SharePointUrlTextBox.TextChanged += new System.EventHandler(this.SharePointUrlTextBox_TextChanged); this.SharePointUrlTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.SharePointUrlTextBox_KeyUp); // // label2 // this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.label2.Location = new System.Drawing.Point(9, 80); this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(113, 19); this.label2.TabIndex = 4; this.label2.Text = "Client Id:"; // // ClientIdTextBox // this.ClientIdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ClientIdTextBox.Location = new System.Drawing.Point(121, 78); this.ClientIdTextBox.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.ClientIdTextBox.Name = "ClientIdTextBox"; this.ClientIdTextBox.Size = new System.Drawing.Size(312, 20); this.ClientIdTextBox.TabIndex = 1; this.ClientIdTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ClientIdTextBox_KeyUp); // // ClientSecretTextBox // this.ClientSecretTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ClientSecretTextBox.Location = new System.Drawing.Point(121, 101); this.ClientSecretTextBox.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.ClientSecretTextBox.Name = "ClientSecretTextBox"; this.ClientSecretTextBox.Size = new System.Drawing.Size(312, 20); this.ClientSecretTextBox.TabIndex = 2; this.ClientSecretTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ClientSecretTextBox_KeyUp); // // label3 // this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.label3.Location = new System.Drawing.Point(9, 103); this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(113, 19); this.label3.TabIndex = 6; this.label3.Text = "Client Secret:"; // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CancelButton.Location = new System.Drawing.Point(320, 131); this.CancelButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(112, 28); this.CancelButton.TabIndex = 5; this.CancelButton.Text = "Cancel"; this.CancelButton.UseVisualStyleBackColor = true; this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); // // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.OKButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.OKButton.Location = new System.Drawing.Point(204, 131); this.OKButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(112, 28); this.OKButton.TabIndex = 4; this.OKButton.Text = "OK"; this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // // TestButton // this.TestButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.TestButton.Location = new System.Drawing.Point(88, 131); this.TestButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.TestButton.Name = "TestButton"; this.TestButton.Size = new System.Drawing.Size(112, 28); this.TestButton.TabIndex = 3; this.TestButton.Text = "Test Connection"; this.TestButton.UseVisualStyleBackColor = true; this.TestButton.Click += new System.EventHandler(this.TestButton_Click); // // SharePointCredentialsForm // this.AcceptButton = this.OKButton; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(441, 168); this.Controls.Add(this.TestButton); this.Controls.Add(this.OKButton); this.Controls.Add(this.CancelButton); this.Controls.Add(this.ClientSecretTextBox); this.Controls.Add(this.label3); this.Controls.Add(this.ClientIdTextBox); this.Controls.Add(this.label2); this.Controls.Add(this.SharePointUrlTextBox); this.Controls.Add(this.label1); this.Controls.Add(this.ExplanationLabel); this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "SharePointCredentialsForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Provide SharePoint details"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox SharePointUrlTextBox; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox ClientIdTextBox; private System.Windows.Forms.TextBox ClientSecretTextBox; private System.Windows.Forms.Label label3; private System.Windows.Forms.Button CancelButton; private System.Windows.Forms.Button OKButton; private System.Windows.Forms.Button TestButton; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/SharePointCredentialsForm.cs ================================================ using System; using System.Threading.Tasks; using System.Windows.Forms; namespace KoenZomersKeePassOneDriveSync.Forms { public partial class SharePointCredentialsForm : Form { #region Properties /// /// The entered SharePoint URL /// public string SharePointUrl { get { return SharePointUrlTextBox.Text; } } /// /// The entered SharePoint Client Id /// public string SharePointClientId { get { return ClientIdTextBox.Text; } } /// /// The entered SharePoint Client Secret /// public string SharePointClientSecret { get { return ClientSecretTextBox.Text; } } /// /// Boolean indicating if the SharePointUrl, SharePointClientId and SharePointClientSecret fields all contain a value /// public bool AllFieldsContainText { get { return !string.IsNullOrWhiteSpace(SharePointUrlTextBox.Text) && !string.IsNullOrWhiteSpace(ClientIdTextBox.Text) && !string.IsNullOrWhiteSpace(ClientSecretTextBox.Text); } } #endregion public SharePointCredentialsForm() { InitializeComponent(); } /// /// Triggered when the user clicks the OK button /// private void OKButton_Click(object sender, EventArgs e) { if(!EnsureAllFieldsEntered()) { DialogResult = DialogResult.None; } DialogResult = DialogResult.OK; } /// /// Validates if all fields contain a value and if not, will display a notification to the nd user /// /// True if all fields contain a value, False if this is not the case private bool EnsureAllFieldsEntered() { var allChecksPassed = AllFieldsContainText; if(!allChecksPassed) { MessageBox.Show(this, "All fields are required fields", "Please enter all fields", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } return allChecksPassed; } /// /// Uses the information provided in the form to try to establish a connection with SharePoint /// private async void TestButton_Click(object sender, EventArgs e) { // Ensure all fields contain a value if(!EnsureAllFieldsEntered()) { return; } // Ensure the entered URL is a valid URI Uri SharePointUri; if (!Uri.TryCreate(SharePointUrl, UriKind.Absolute, out SharePointUri)) { MessageBox.Show(this, "SharePoint site URL field does not contain a valid URL", "Invalid data entered", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); SharePointUrlTextBox.SelectAll(); SharePointUrlTextBox.Focus(); return; } // Test the connection try { using (var clientContext = Providers.SharePointProvider.CreateSharePointHttpClient(SharePointUri, SharePointClientId, SharePointClientSecret)) { if (await Providers.SharePointProvider.TestConnection(clientContext)) { MessageBox.Show("Connection successful", "Testing SharePoint Connectivity", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("Connection failed", "Testing SharePoint Connectivity", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } } catch(Exception ex) { MessageBox.Show("Connection failed: '" + ex.Message + "'. Check your entered Client ID and Client Secret.", "Testing SharePoint Connectivity", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); ClientIdTextBox.SelectAll(); ClientIdTextBox.Focus(); return; } } private void SharePointUrlTextBox_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.V && e.Control) { var clipText = Clipboard.GetText(); var clipTextSplitted = clipText.Split(new[] { '\r', '\n' }); if (clipTextSplitted.Length == 5) { SharePointUrlTextBox.Text = clipTextSplitted[0]; ClientIdTextBox.Text = clipTextSplitted[2]; ClientSecretTextBox.Text = clipTextSplitted[4]; e.SuppressKeyPress = false; } } e.SuppressKeyPress = true; } private void CancelButton_Click(object sender, EventArgs e) { DialogResult = DialogResult.Cancel; } private void SharePointUrlTextBox_TextChanged(object sender, EventArgs e) { } private void ClientIdTextBox_KeyUp(object sender, KeyEventArgs e) { SharePointUrlTextBox_KeyUp(sender, e); } private void ClientSecretTextBox_KeyUp(object sender, KeyEventArgs e) { SharePointUrlTextBox_KeyUp(sender, e); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/SharePointCredentialsForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/SharePointDocumentLibraryPickerDialog.Designer.cs ================================================ namespace KoenZomersKeePassOneDriveSync.Forms { partial class SharePointDocumentLibraryPickerDialog { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SharePointDocumentLibraryPickerDialog)); this.SharePointDocumentLibraryPicker = new System.Windows.Forms.ListView(); this.ListViewContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.goToRootToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.goupToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.newFoldertoolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.renameToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.deleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.showHiddenLibrariesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.IconsList = new System.Windows.Forms.ImageList(this.components); this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); this.CloudLocationPath = new System.Windows.Forms.TextBox(); this.ExplanationLabel = new System.Windows.Forms.Label(); this.FileNameLabel = new System.Windows.Forms.Label(); this.FileNameTextBox = new System.Windows.Forms.TextBox(); this.UpButton = new System.Windows.Forms.Button(); this.ListViewContextMenu.SuspendLayout(); this.SuspendLayout(); // // SharePointDocumentLibraryPicker // this.SharePointDocumentLibraryPicker.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.SharePointDocumentLibraryPicker.ContextMenuStrip = this.ListViewContextMenu; this.SharePointDocumentLibraryPicker.HideSelection = false; this.SharePointDocumentLibraryPicker.LargeImageList = this.IconsList; this.SharePointDocumentLibraryPicker.Location = new System.Drawing.Point(14, 65); this.SharePointDocumentLibraryPicker.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.SharePointDocumentLibraryPicker.MultiSelect = false; this.SharePointDocumentLibraryPicker.Name = "SharePointDocumentLibraryPicker"; this.SharePointDocumentLibraryPicker.ShowItemToolTips = true; this.SharePointDocumentLibraryPicker.Size = new System.Drawing.Size(679, 386); this.SharePointDocumentLibraryPicker.SmallImageList = this.IconsList; this.SharePointDocumentLibraryPicker.TabIndex = 0; this.SharePointDocumentLibraryPicker.TileSize = new System.Drawing.Size(244, 70); this.SharePointDocumentLibraryPicker.UseCompatibleStateImageBehavior = false; this.SharePointDocumentLibraryPicker.SelectedIndexChanged += new System.EventHandler(this.SharePointDocumentLibraryPicker_SelectedIndexChanged); this.SharePointDocumentLibraryPicker.DoubleClick += new System.EventHandler(this.SharePointDocumentLibraryPicker_DoubleClick); // // ListViewContextMenu // this.ListViewContextMenu.ImageScalingSize = new System.Drawing.Size(20, 20); this.ListViewContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.refreshToolStripMenuItem, this.goToRootToolStripMenuItem, this.goupToolStripMenuItem, this.toolStripSeparator2, this.newFoldertoolStripMenuItem, this.renameToolStripMenuItem, this.deleteToolStripMenuItem, this.toolStripSeparator1, this.showHiddenLibrariesToolStripMenuItem}); this.ListViewContextMenu.Name = "ListViewContextMenu"; this.ListViewContextMenu.Size = new System.Drawing.Size(255, 240); // // refreshToolStripMenuItem // this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; this.refreshToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F5; this.refreshToolStripMenuItem.Size = new System.Drawing.Size(254, 32); this.refreshToolStripMenuItem.Text = "&Refresh"; this.refreshToolStripMenuItem.Click += new System.EventHandler(this.refreshToolStripMenuItem_Click); // // goToRootToolStripMenuItem // this.goToRootToolStripMenuItem.Name = "goToRootToolStripMenuItem"; this.goToRootToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.H))); this.goToRootToolStripMenuItem.Size = new System.Drawing.Size(254, 32); this.goToRootToolStripMenuItem.Text = "&Go to root"; this.goToRootToolStripMenuItem.Click += new System.EventHandler(this.goToRootToolStripMenuItem_Click); // // goupToolStripMenuItem // this.goupToolStripMenuItem.Enabled = false; this.goupToolStripMenuItem.Name = "goupToolStripMenuItem"; this.goupToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.U))); this.goupToolStripMenuItem.Size = new System.Drawing.Size(254, 32); this.goupToolStripMenuItem.Text = "Go &up"; this.goupToolStripMenuItem.Click += new System.EventHandler(this.goupToolStripMenuItem_Click); // // toolStripSeparator2 // this.toolStripSeparator2.Name = "toolStripSeparator2"; this.toolStripSeparator2.Size = new System.Drawing.Size(251, 6); // // newFoldertoolStripMenuItem // this.newFoldertoolStripMenuItem.Name = "newFoldertoolStripMenuItem"; this.newFoldertoolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); this.newFoldertoolStripMenuItem.Size = new System.Drawing.Size(254, 32); this.newFoldertoolStripMenuItem.Text = "&New Folder"; this.newFoldertoolStripMenuItem.Click += new System.EventHandler(this.newFoldertoolStripMenuItem_Click); // // renameToolStripMenuItem // this.renameToolStripMenuItem.Name = "renameToolStripMenuItem"; this.renameToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R))); this.renameToolStripMenuItem.Size = new System.Drawing.Size(254, 32); this.renameToolStripMenuItem.Text = "&Rename"; this.renameToolStripMenuItem.Click += new System.EventHandler(this.renameToolStripMenuItem_Click); // // deleteToolStripMenuItem // this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; this.deleteToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Delete; this.deleteToolStripMenuItem.Size = new System.Drawing.Size(254, 32); this.deleteToolStripMenuItem.Text = "&Delete"; this.deleteToolStripMenuItem.Click += new System.EventHandler(this.deleteToolStripMenuItem_Click); // // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; this.toolStripSeparator1.Size = new System.Drawing.Size(251, 6); // // showHiddenLibrariesToolStripMenuItem // this.showHiddenLibrariesToolStripMenuItem.Name = "showHiddenLibrariesToolStripMenuItem"; this.showHiddenLibrariesToolStripMenuItem.Size = new System.Drawing.Size(254, 32); this.showHiddenLibrariesToolStripMenuItem.Text = "Show hidden libraries"; this.showHiddenLibrariesToolStripMenuItem.Click += new System.EventHandler(this.showHiddenLibrariesToolStripMenuItem_Click); // // IconsList // this.IconsList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("IconsList.ImageStream"))); this.IconsList.TransparentColor = System.Drawing.Color.Transparent; this.IconsList.Images.SetKeyName(0, "File"); this.IconsList.Images.SetKeyName(1, "Folder"); this.IconsList.Images.SetKeyName(2, "DocLib"); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CancelButton.Location = new System.Drawing.Point(585, 461); this.CancelButton.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(108, 49); this.CancelButton.TabIndex = 3; this.CancelButton.Text = "&Cancel"; this.CancelButton.UseVisualStyleBackColor = true; // // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.OKButton.Enabled = false; this.OKButton.Location = new System.Drawing.Point(469, 461); this.OKButton.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(108, 49); this.OKButton.TabIndex = 2; this.OKButton.Text = "&OK"; this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // // CloudLocationPath // this.CloudLocationPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.CloudLocationPath.BackColor = System.Drawing.SystemColors.Control; this.CloudLocationPath.BorderStyle = System.Windows.Forms.BorderStyle.None; this.CloudLocationPath.Location = new System.Drawing.Point(17, 39); this.CloudLocationPath.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.CloudLocationPath.Name = "CloudLocationPath"; this.CloudLocationPath.Size = new System.Drawing.Size(607, 19); this.CloudLocationPath.TabIndex = 6; this.CloudLocationPath.TabStop = false; // // ExplanationLabel // this.ExplanationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ExplanationLabel.Location = new System.Drawing.Point(14, 11); this.ExplanationLabel.Name = "ExplanationLabel"; this.ExplanationLabel.Size = new System.Drawing.Size(679, 46); this.ExplanationLabel.TabIndex = 8; this.ExplanationLabel.Text = "Select the document library in which you want to store the KeePass database."; // // FileNameLabel // this.FileNameLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.FileNameLabel.Location = new System.Drawing.Point(14, 471); this.FileNameLabel.Name = "FileNameLabel"; this.FileNameLabel.Size = new System.Drawing.Size(87, 28); this.FileNameLabel.TabIndex = 11; this.FileNameLabel.Text = "Filename:"; // // FileNameTextBox // this.FileNameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.FileNameTextBox.Location = new System.Drawing.Point(107, 471); this.FileNameTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.FileNameTextBox.Name = "FileNameTextBox"; this.FileNameTextBox.Size = new System.Drawing.Size(355, 26); this.FileNameTextBox.TabIndex = 1; this.FileNameTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.FileNameTextBox_KeyUp); // // UpButton // this.UpButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.UpButton.Enabled = false; this.UpButton.Location = new System.Drawing.Point(631, 8); this.UpButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.UpButton.Name = "UpButton"; this.UpButton.Size = new System.Drawing.Size(62, 50); this.UpButton.TabIndex = 4; this.UpButton.Text = "Up"; this.UpButton.UseVisualStyleBackColor = true; this.UpButton.Click += new System.EventHandler(this.UpButton_Click); // // SharePointDocumentLibraryPickerDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(706, 521); this.Controls.Add(this.UpButton); this.Controls.Add(this.FileNameLabel); this.Controls.Add(this.FileNameTextBox); this.Controls.Add(this.ExplanationLabel); this.Controls.Add(this.CloudLocationPath); this.Controls.Add(this.CancelButton); this.Controls.Add(this.OKButton); this.Controls.Add(this.SharePointDocumentLibraryPicker); this.KeyPreview = true; this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(406, 228); this.Name = "SharePointDocumentLibraryPickerDialog"; this.ShowIcon = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Select location"; this.ListViewContextMenu.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.ListView SharePointDocumentLibraryPicker; private System.Windows.Forms.Button CancelButton; private System.Windows.Forms.Button OKButton; private System.Windows.Forms.TextBox CloudLocationPath; private System.Windows.Forms.ImageList IconsList; private System.Windows.Forms.Label ExplanationLabel; private System.Windows.Forms.Label FileNameLabel; private System.Windows.Forms.TextBox FileNameTextBox; private System.Windows.Forms.Button UpButton; private System.Windows.Forms.ContextMenuStrip ListViewContextMenu; private System.Windows.Forms.ToolStripMenuItem refreshToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem goToRootToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem goupToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; private System.Windows.Forms.ToolStripMenuItem showHiddenLibrariesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem newFoldertoolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripMenuItem renameToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem deleteToolStripMenuItem; } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/SharePointDocumentLibraryPickerDialog.cs ================================================ using System; using System.Net.Http; using System.Threading.Tasks; using System.Windows.Forms; using Newtonsoft.Json.Linq; namespace KoenZomersKeePassOneDriveSync.Forms { public partial class SharePointDocumentLibraryPickerDialog : System.Windows.Forms.Form { #region Properties /// /// Returns the server relative URL of the selected SharePoint Document Library /// public string SelectedDocumentLibraryServerRelativeUrl { get { return currentViewServerRelativeUrl ?? (SharePointDocumentLibraryPicker.SelectedItems.Count > 0 ? SharePointDocumentLibraryPicker.SelectedItems[0].Tag.ToString() : null); } } /// /// Gets or sets the filename in the textbox on the screen /// public string FileName { get { return FileNameTextBox.Text; } set { FileNameTextBox.Text = value; } } /// /// Gets or sets the text shown at the top of the form /// public string ExplanationText { get { return ExplanationLabel.Text; } set { ExplanationLabel.Text = value; } } /// /// Gets or sets if the user can enter a filename which does not exist yet on their OneDrive /// public bool AllowEnteringNewFileName { get { return FileNameTextBox.Enabled; } set { FileNameTextBox.Enabled = value; } } /// /// Gets or sets if hidden document libraries should be shown /// public bool ShowHiddenLibraries { get; set; } #endregion /// /// Instance of the HttpClient that can be used to communicate with SharePoint /// private readonly HttpClient _httpClient; /// /// The server relative url of the currently selected document library /// private string documentLibraryServerRelativeUrl = null; /// /// The server relative url of the currently shown view /// private string currentViewServerRelativeUrl = null; public SharePointDocumentLibraryPickerDialog(HttpClient httpClient) { InitializeComponent(); _httpClient = httpClient; } /// /// Gets the document library items and renders them in the form /// public async Task LoadDocumentLibraryItems() { SharePointDocumentLibraryPicker.Items.Clear(); currentViewServerRelativeUrl = ""; // Request all document libraries var response = await _httpClient.GetAsync("web/lists?$select=Title,RootFolder/ServerRelativeUrl&$filter=BaseTemplate eq 101" + (ShowHiddenLibraries ? "" : " and Hidden eq false") + "&$expand=RootFolder"); // Validate if the request was successful if (!response.IsSuccessStatusCode) { // Request was unsuccessful return; } // Request was succesful. Parse the JSON result. var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); // Loop through each document library in the result foreach (var listViewItem in responseJson["value"]) { // Create a new tile in the form for each document library SharePointDocumentLibraryPicker.Items.Add(new ListViewItem { Text = listViewItem["Title"].ToString(), Tag = listViewItem["RootFolder"]["ServerRelativeUrl"].ToString(), ImageKey = "DocLib", Selected = listViewItem["Title"].ToString().Equals(FileNameTextBox.Text, StringComparison.InvariantCultureIgnoreCase) }); } // Do not allow the use of the up button or the root context menu option as this is the highest level UpButton.Enabled = false; goupToolStripMenuItem.Enabled = false; goToRootToolStripMenuItem.Enabled = false; deleteToolStripMenuItem.Enabled = false; renameToolStripMenuItem.Enabled = false; } /// /// Gets the folders and files inside a document library items and renders them in the form /// public async Task LoadDocumentLibraryFileAndFolderItems(string serverRelativeUrl) { currentViewServerRelativeUrl = serverRelativeUrl; SharePointDocumentLibraryPicker.Items.Clear(); // Request all folders using (var foldersResponse = await _httpClient.GetAsync("web/GetFolderByServerRelativeUrl('" + serverRelativeUrl + "')/Folders?$select=Name,ServerRelativeUrl,ItemCount,TimeCreated,TimeLastModified")) { // Validate if the request was successful if (!foldersResponse.IsSuccessStatusCode) { // Request was unsuccessful return; } // Request was succesful. Parse the JSON result. var foldersResponseJson = JObject.Parse(await foldersResponse.Content.ReadAsStringAsync()); // Loop through each document library in the result foreach (var listViewItem in foldersResponseJson["value"]) { // Create a new tile in the form for each document library var folderItem = new ListViewItem { Text = listViewItem["Name"].ToString(), Tag = listViewItem["ServerRelativeUrl"].ToString(), ImageKey = "Folder" }; long itemCount; if (listViewItem["ItemCount"] != null && long.TryParse(listViewItem["ItemCount"].ToString(), out itemCount)) { folderItem.ToolTipText += string.Format("Items inside: {0}", itemCount) + Environment.NewLine; } DateTime created; if (listViewItem["TimeCreated"] != null && DateTime.TryParse(listViewItem["TimeCreated"].ToString(), out created)) { folderItem.ToolTipText += string.Format("Created: {0:d MMMM yyyy HH:mm:ss}", created) + Environment.NewLine; } DateTime lastModified; if (listViewItem["TimeLastModified"] != null && DateTime.TryParse(listViewItem["TimeLastModified"].ToString(), out lastModified)) { folderItem.ToolTipText += string.Format("Last modified: {0:d MMMM yyyy HH:mm:ss}", lastModified) + Environment.NewLine; } SharePointDocumentLibraryPicker.Items.Add(folderItem); } } // Request all files using (var filesResponse = await _httpClient.GetAsync("web/GetFolderByServerRelativeUrl('" + serverRelativeUrl + "')/Files?$select=Name,ServerRelativeUrl,TimeLastModified,TimeCreated,UIVersionLabel,Length")) { // Validate if the request was successful if (!filesResponse.IsSuccessStatusCode) { // Request was unsuccessful return; } // Request was succesful. Parse the JSON result. var filesResponseJson = JObject.Parse(await filesResponse.Content.ReadAsStringAsync()); // Loop through each file in the result foreach (var listViewItem in filesResponseJson["value"]) { // Create a new tile in the form for each file var fileItem = new ListViewItem { Text = listViewItem["Name"].ToString(), Tag = listViewItem["ServerRelativeUrl"].ToString(), ImageKey = "File", Selected = listViewItem["Name"].ToString().Equals(FileNameTextBox.Text, StringComparison.InvariantCultureIgnoreCase) }; long fileSize; if (listViewItem["Length"] != null && long.TryParse(listViewItem["Length"].ToString(), out fileSize)) { fileItem.ToolTipText += string.Format("Size: {0:n0} bytes", fileSize) + Environment.NewLine; } if (listViewItem["UIVersionLabel"] != null && listViewItem["UIVersionLabel"].ToString().Length > 0) { fileItem.ToolTipText += string.Format("Version: {0}", listViewItem["UIVersionLabel"]) + Environment.NewLine; } DateTime created; if (listViewItem["TimeCreated"] != null && DateTime.TryParse(listViewItem["TimeCreated"].ToString(), out created)) { fileItem.ToolTipText += string.Format("Created: {0:d MMMM yyyy HH:mm:ss}", created) + Environment.NewLine; } DateTime lastModified; if (listViewItem["TimeLastModified"] != null && DateTime.TryParse(listViewItem["TimeLastModified"].ToString(), out lastModified)) { fileItem.ToolTipText += string.Format("Last modified: {0:d MMMM yyyy HH:mm:ss}", lastModified) + Environment.NewLine; } SharePointDocumentLibraryPicker.Items.Add(fileItem); } } // Allow the use of the up button and root context menu option as you can always navigate up to the document library list level UpButton.Enabled = true; goupToolStripMenuItem.Enabled = true; goToRootToolStripMenuItem.Enabled = true; deleteToolStripMenuItem.Enabled = false; renameToolStripMenuItem.Enabled = false; } private async void SharePointDocumentLibraryPicker_DoubleClick(object sender, EventArgs e) { if (SharePointDocumentLibraryPicker.SelectedItems.Count == 0) return; var selectedItem = SharePointDocumentLibraryPicker.SelectedItems[0]; switch (selectedItem.ImageKey) { case "DocLib": documentLibraryServerRelativeUrl = selectedItem.Tag.ToString(); await LoadDocumentLibraryFileAndFolderItems(selectedItem.Tag.ToString()); break; case "Folder": await LoadDocumentLibraryFileAndFolderItems(selectedItem.Tag.ToString()); break; case "File": if (OKButton.Enabled) { OKButton_Click(sender, e); } break; } } private void OKButton_Click(object sender, EventArgs e) { if(string.IsNullOrEmpty(SelectedDocumentLibraryServerRelativeUrl)) { MessageBox.Show(AllowEnteringNewFileName ? "Select a document library to store the KeePass database in" : "Select the KeePass file to download", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if(string.IsNullOrWhiteSpace(FileName)) { MessageBox.Show(AllowEnteringNewFileName ? "Enter the filename under which you wish to store the database" : "Select the KeePass file to download", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } DialogResult = DialogResult.OK; Close(); } private void SharePointDocumentLibraryPicker_SelectedIndexChanged(object sender, EventArgs e) { if (SharePointDocumentLibraryPicker.SelectedItems.Count > 0 && SharePointDocumentLibraryPicker.SelectedItems[0].ImageKey == "File") { FileName = SharePointDocumentLibraryPicker.SelectedItems[0].Text; } // Enable the OK button only when an item has been selected and a filename has been provided OKButton.Enabled = SharePointDocumentLibraryPicker.SelectedItems.Count > 0 && !string.IsNullOrEmpty(FileName); // Don't allow deletion or renames of complete document libraries deleteToolStripMenuItem.Enabled = renameToolStripMenuItem.Enabled = SharePointDocumentLibraryPicker.SelectedItems.Count > 0 && SharePointDocumentLibraryPicker.SelectedItems[0].ImageKey != "DocLib"; } private void showHiddenLibrariesToolStripMenuItem_Click(object sender, EventArgs e) { ShowHiddenLibraries = !ShowHiddenLibraries; showHiddenLibrariesToolStripMenuItem.Checked = ShowHiddenLibraries; refreshToolStripMenuItem_Click(sender, e); } private async void refreshToolStripMenuItem_Click(object sender, EventArgs e) { if (!string.IsNullOrEmpty(currentViewServerRelativeUrl)) { await LoadDocumentLibraryFileAndFolderItems(currentViewServerRelativeUrl); } else { await LoadDocumentLibraryItems(); } } private async void UpButton_Click(object sender, EventArgs e) { var newServerRelativeUrl = currentViewServerRelativeUrl.Remove(currentViewServerRelativeUrl.LastIndexOf('/')); if(newServerRelativeUrl.Length < documentLibraryServerRelativeUrl.Length) { await LoadDocumentLibraryItems(); } else { await LoadDocumentLibraryFileAndFolderItems(newServerRelativeUrl); } } private async void goToRootToolStripMenuItem_Click(object sender, EventArgs e) { await LoadDocumentLibraryItems(); } private void goupToolStripMenuItem_Click(object sender, EventArgs e) { UpButton_Click(sender, e); } private void FileNameTextBox_KeyUp(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Enter && OKButton.Enabled) { OKButton_Click(sender, e); } } private async void newFoldertoolStripMenuItem_Click(object sender, EventArgs e) { var newFolderDialog = new OneDriveRequestInputDialog { FormTitle = "Create new folder" }; newFolderDialog.ShowDialog(this); if (newFolderDialog.DialogResult != DialogResult.OK) return; try { var newFolderServerRelativeUrl = await Providers.SharePointProvider.CreateFolder(newFolderDialog.InputValue, SelectedDocumentLibraryServerRelativeUrl, _httpClient); MessageBox.Show("Folder has been created", "New Folder", MessageBoxButtons.OK, MessageBoxIcon.Information); // Refresh the items shown refreshToolStripMenuItem_Click(sender, e); } catch (Exception ex) { MessageBox.Show("Folder could not be created (" + ex.Message + ")", "New Folder", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } private async void deleteToolStripMenuItem_Click(object sender, EventArgs e) { if (SharePointDocumentLibraryPicker.SelectedItems.Count == 0 || SharePointDocumentLibraryPicker.SelectedItems[0].ImageKey == "DocLib") return; var selectedItem = SharePointDocumentLibraryPicker.SelectedItems[0]; var confirm = MessageBox.Show("Are you sure you want to delete the selected " + selectedItem.ImageKey.ToLowerInvariant() + "? " + (selectedItem.ImageKey == "Folder" ? "Note that folders can only be removed if there's nothing inside anymore. " : ""), "Confirm deletion", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (confirm != DialogResult.Yes) return; bool operationSuccessful = false; switch(selectedItem.ImageKey) { case "File": operationSuccessful = await Providers.SharePointProvider.DeleteFile(selectedItem.Tag.ToString(), _httpClient); break; case "Folder": operationSuccessful = await Providers.SharePointProvider.DeleteFolder(selectedItem.Tag.ToString(), _httpClient); break; default: MessageBox.Show("Item type '" + selectedItem.ImageKey + "' is not implemented for this operation.", "Delete item", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if(operationSuccessful) { MessageBox.Show(selectedItem.ImageKey + " has been deleted", "Delete item", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("Unable to delete " + selectedItem.ImageKey.ToLowerInvariant(), "Delete item", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } // Refresh the items shown refreshToolStripMenuItem_Click(sender, e); } private async void renameToolStripMenuItem_Click(object sender, EventArgs e) { if (SharePointDocumentLibraryPicker.SelectedItems.Count == 0 || SharePointDocumentLibraryPicker.SelectedItems[0].ImageKey == "DocLib") return; var selectedItem = SharePointDocumentLibraryPicker.SelectedItems[0]; var renameItemDialog = new OneDriveRequestInputDialog { FormTitle = "Enter new name", InputValue = selectedItem.Text }; renameItemDialog.ShowDialog(this); if (renameItemDialog.DialogResult != DialogResult.OK) return; bool operationSuccessful = false; switch (selectedItem.ImageKey) { case "File": operationSuccessful = await Providers.SharePointProvider.RenameFile(renameItemDialog.InputValue, selectedItem.Tag.ToString(), _httpClient); break; case "Folder": operationSuccessful = await Providers.SharePointProvider.RenameFolder(renameItemDialog.InputValue, selectedItem.Tag.ToString(), _httpClient); break; default: MessageBox.Show("Item type '" + selectedItem.ImageKey + "' is not implemented for this operation.", "Rename item", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (operationSuccessful) { MessageBox.Show(selectedItem.ImageKey + " has been renamed", "Rename item", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("Unable to rename " + selectedItem.ImageKey.ToLowerInvariant(), "Rename item", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } // Refresh the items shown refreshToolStripMenuItem_Click(sender, e); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Forms/SharePointDocumentLibraryPickerDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 140, 17 21, 14 AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABK uAAAAk1TRnQBSQFMAgEBAwEAAXgBAAF4AQABQAEAAUABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgEAAEB AgABQAMAAQEBAAEgBwABAREA/////wL/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEA /////wL/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEA/////wL/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf//AAEA/////wL/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AdEBeQFAAf8B2wGaAWgB/wHbAZsBaQH/AdwBmwFqAf8B3AGcAWoB/wHc AZwBawH/AdwBnAFrAf8B3AGdAWwB/wHcAZ0BbQH/Ad0BngFuAf8B3QGeAW4B/wHdAZ8BbwH/Ad0BnwFw Af8B3QGgAXAB/wHdAaABcQH/Ad4BoQFyAf8B3gGhAXIB/wHeAaIBcwH/Ad4BogF0Af8B3gGjAXUB/wHe AaMBdQH/Ad0BoAFxAf8ByAFfARsB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af//AAEA/////wL/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AeUBtAGTAf8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/AfsB9QHxAf8BzQFs AS4B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEA/////wL/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AeQBtAGTAf8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/AfsB9QHxAf8BzQFtAS8B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEA/////wL/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AeQBswGSAf8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/AfsB9QHxAf8BzQFuATAB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf//AAEA/////wL/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AeQBswGSAf8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/AfsB9gHx Af8BzgFuATEB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEAPP8D/QH/ A/wB/wP7Af8D+gH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/ A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/A/kB/wP5Af8D+QH/ A/kB/wP7Af8D/QH/A/4B/wP+//82/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHkAbMBkQH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH7 AfYB8QH/Ac4BbwEyAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/ A/QB/wPoAf8D3gH/A9MB/wPNAf8DzQH/A80B/wPNAf8DzQH/A80B/wPNAf8DzQH/A80B/wPNAf8DzQH/ A80B/wPNAf8DzQH/A80B/wPNAf8DzQH/A80B/wPNAf8DzQH/A80B/wPNAf8DzQH/A80B/wPNAf8DzQH/ A80B/wPRAf8D4AH/A+8B/wP2Af8D/f//Nv8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8B5AGyAZAB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B+wH2AfEB/wHOAXABMwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQA8/wPqAf8D1QH/A8AB/wOsAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/ A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/ A6EB/wOhAf8DqAH/A8YB/wPiAf8D7wH/A/v//zb/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AeQBsgGQAf8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/AfsB9gHxAf8BzgFxATUB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af//AAEAPP8D5QH/A8sB/wO1Af8DnwH/A5cB/wOgAf8DpgH/A6YB/wOmAf8DpgH/A6YB/wOmAf8DpgH/ A6YB/wOmAf8DpgH/A6YB/wOmAf8DpgH/A6YB/wOmAf8DpgH/A6YB/wOmAf8DpgH/A6YB/wOmAf8DpQH/ A6QB/wOfAf8DlQH/A5cB/wO5Af8D2wH/A+sB/wP7//82/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHjAbEBjwH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH7AfYB8gH/Ac8BcQE1Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH//wABADz/A+EB/wPDAf8DqwH/A5UB/wORAf8DpAH/A7MB/wOzAf8DswH/A7MB/wOzAf8DswH/ A7MB/wOzAf8DswH/A7MB/wOzAf8DswH/A7MB/wOzAf8DswH/A7MB/wOzAf8DswH/A7MB/wOzAf8DswH/ A7EB/wOuAf8DogH/A40B/wOIAf8DrwH/A9YB/wPoAf8D+v//Nv8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B4wGxAY4B/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B+wH2AfIB/wHPAXIBNwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B//8AAQA8/wPgAf8DwAH/A6oB/wOWAf8DmAH/A7UB/wPLAf8DywH/A8sB/wPLAf8DywH/ A8sB/wPLAf8DywH/A8sB/wPLAf8DywH/A8sB/wPLAf8DywH/A8sB/wPLAf8DywH/A8sB/wPLAf8DywH/ A8sB/wPIAf8DxAH/A7EB/wORAf8DhAH/A6wB/wPUAf8D5wH/A/r//zb/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AeMBsAGNAf8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/AfsB9gHyAf8BzwFzATgB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf//AAEAPP8D4AH/A8AB/wOtAf8DngH/A6YB/wPMAf8D6wH/A+sB/wPrAf8D6wH/ A+sB/wPrAf8D6wH/A+sB/wPrAf8D6wH/A+sB/wPrAf8D6wH/A+sB/wPrAf8D6wH/A+sB/wPrAf8D6wH/ A+sB/wPrAf8D5wH/A+EB/wPIAf8DnAH/A4cB/wOuAf8D1AH/A+cB/wP6//82/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFXAQ8B/wHF AVcBDwH/AcUBWAEPAf8BxQFYARAB/wHFAVgBEAH/AcUBWAEQAf8BxQFYARAB/wHFAVgBEAH/AcUBWAEQ Af8BxQFYARAB/wHGAVgBEQH/AcYBWAERAf8BxgFZAREB/wHGAVkBEQH/AcYBWQERAf8BxgFZAREB/wHG AVkBEQH/AcYBWQESAf8BxgFZARIB/wHGAVkBEgH/AcYBWQESAf8BxgFZARIB/wHGAVoBEgH/AcYBWgES Af8BxgFaARMB/wHGAVoBEwH/AcYBWQESAf8BxQFWAQ0B/wHjAbABjQH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH7AfYB8gH/Ac8BdAE5Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D21X/A/oB/wPzAf8D1gH/ A6MB/wOJAf8DrgH/A9QB/wPnAf8D+v//Nv8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHHAV0BFwH/AeQBtAGTAf8B5wG8AZ4B/wHnAb0BoAH/AecBvQGh Af8B6AG+AaIB/wHoAb8BowH/AegBwAGkAf8B6QHAAaUB/wHpAcEBpwH/AekBwgGnAf8B6QHDAakB/wHq AcQBqQH/AeoBxQGrAf8B6gHFAawB/wHrAcYBrQH/AesBxwGuAf8B6wHIAa8B/wHrAcgBsAH/AewByQGy Af8B7AHKAbIB/wHsAcsBtAH/AewBywG1Af8B7QHMAbYB/wHtAc0BtwH/Ae0BzgG4Af8B7QHOAbkB/wHo Ab0BoAH/AcYBWwEUAf8B4wGvAYwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B+wH2AfIB/wHQAXQBOgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQA8/wPgAf8DwAH/A68B/wOiAf8DrwH/A9tV/wP6Af8D8wH/A9YB/wOjAf8DiQH/A64B/wPUAf8D5wH/ A/pp/wH1AfsB/QH/AekB9QH8Af8B5wH0AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/ AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0 AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/ AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0 AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0AfwB/wHnAfQB/AH/AecB9AH8Af8B5wH0AfwB/wHrAfYB/AH/ AfAB+AH9Af8B+QH8Af4x/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AckBYwEfAf8B+gHyAe0B/wH9AfsB+QH/AfsB9AHwAf8B+wH0AfAB/wH7AfQB7wH/ AfsB8wHuAf8B+gHzAe0B/wH6AfIB7QH/AfoB8gHsAf8B+gHxAesB/wH6AfEB6gH/AfkB8AHqAf8B+QHw AekB/wH5Ae8B6AH/AfkB7wHoAf8B+QHuAecB/wH5Ae4B5gH/AfgB7QHmAf8B+AHtAeUB/wH4AewB5AH/ AfgB7AHjAf8B+AHrAeMB/wH4AesB4gH/AfcB6gHhAf8B9wHqAeEB/wH3AeoB4AH/Ae8B1AHBAf8BxwFc ARYB/wHjAa8BjAH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH7AfYB8gH/ AdABdQE7Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPA Af8DrwH/A6IB/wOvAf8D21X/A/oB/wPzAf8D1gH/A6MB/wOJAf8DrgH/A9QB/wPnAf8D+mn/AekB9QH8 Af8BzAHoAfgB/wHGAeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHG AeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3 Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHG AeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3 Af8BxgHmAfcB/wHGAeYB9wH/AcYB5gH3Af8BxgHmAfcB/wHIAecB+AH/AdEB6gH5Af8B3QHvAfoB/wHw AfgB/TH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8ByQFiAR8B/wH6AfIB7QH/Ae0BzQG3Af8BzwF0ATkB/wHPAXQBOAH/Ac8BcwE3Af8BzwFzATcB/wHP AXIBNgH/Ac8BcgE1Af8BzgFxATQB/wHOAXEBNAH/Ac4BcAEzAf8BzgFwATMB/wHOAW8BMgH/Ac4BbwEx Af8BzQFuATAB/wHNAW4BMAH/Ac0BbQEvAf8BzQFtAS4B/wHNAWwBLQH/AcwBbAEtAf8BzAFrASwB/wHM AWsBLAH/AcwBagErAf8BzAFqASoB/wHMAWoBKQH/AcsBaQEpAf8BygFmASQB/wHFAVYBDgH/AeIBrgGL Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/AfsB9gHyAf8B0AF2ATwB/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEAPP8D4AH/A8AB/wOvAf8DogH/ A68B/wPbVf8D+gH/A/MB/wPWAf8DowH/A4kB/wOuAf8D1AH/A+cB/wP6af8B3QHwAfoB/wGwAdwB9AH/ AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHY AfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/ AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHY AfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/ AaYB2AHzAf8BpgHYAfMB/wGmAdgB8wH/AakB2QHzAf8BtwHfAfUB/wHJAecB9wH/AekB9QH8Mf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHJAWIBHwH/ AfoB8gHsAf8B6QHBAaYB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B4gGuAYoB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B+wH3AfMB/wHRAXcBPQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQA8/wPgAf8DwAH/A68B/wOiAf8DrwH/A9tV/wP6 Af8D8wH/A9YB/wOjAf8DiQH/A64B/wPUAf8D5wH/A/pp/wHQAeoB+AH/AZMBzwHwAf8BhgHJAe4B/wGG AckB7gH/AYYByQHuAf8BhgHJAe4B/wGGAckB7gH/AYYByQHuAf8BhgHJAe4B/wGGAckB7gH/AYYByQHu Af8BhgHJAe4B/wGGAckB7gH/AYYByQHuAf8BhgHJAe4B/wGGAckB7gH/AYYByQHuAf8BhgHJAe4B/wGG AckB7gH/AYYByQHuAf8BhgHJAe4B/wGGAckB7gH/AYYByQHuAf8BhgHJAe4B/wGGAckB7gH/AYYByQHu Af8BhgHJAe4B/wGGAckB7gH/AYYByQHuAf8BhgHJAe4B/wGGAckB7gH/AYYByQHuAf8BhgHJAe4B/wGG AckB7gH/AYYByQHuAf8BigHLAe8B/wGdAdMB8QH/AbUB3gH1Af8B4AHxAfox/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AckBYgEfAf8B+gHyAewB/wHp AcIBpwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHiAa0BiQH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH7AfcB8wH/AdEBdwE+Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D21X/A/oB/wPzAf8D1gH/ A6MB/wOJAf8DrgH/A9QB/wPnAf8D+mn/Ac4B6QH4Af8BjgHNAe8B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGEAckB7gH/AZcB0QHxAf8BsQHdAfQB/wHfAfEB+jH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8ByQFiAR4B/wH6AfIB7AH/AekBwgGnAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AeIBrQGJAf8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/AfsB9wHzAf8B0QF4AUAB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf//AAEAPP8D4AH/A8AB/wOvAf8DogH/A68B/wPbVf8D+gH/A/MB/wPWAf8DowH/A4kB/wOu Af8D1AH/A+cB/wP6af8BzgHpAfgB/wGOAc0B7wH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYQByQHu Af8BlwHRAfEB/wGxAd0B9AH/Ad8B8QH6Mf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHJAWIBHgH/AfoB8QHsAf8B6QHDAagB/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8B4gGsAYgB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B+wH3AfMB/wHRAXkBQAH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQA8/wPgAf8DwAH/A68B/wOiAf8DrwH/A9tV/wP6Af8D8wH/A9YB/wOjAf8DiQH/A64B/wPUAf8D5wH/ A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/ AbEB3QH0Af8B3wHxAfox/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AckBYgEeAf8B+gHxAewB/wHqAcMBqQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHiAawBhwH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH7AfcB8wH/ AdIBegFCAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPA Af8DrwH/A6IB/wOvAf8D21X/A/oB/wPzAf8D1gH/A6MB/wOJAf8DrgH/A9QB/wPnAf8D+mn/Ac4B6QH4 Af8BjgHNAe8B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGEAckB7gH/AZcB0QHxAf8BsQHdAfQB/wHf AfEB+jH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8ByQFiAR4B/wH6AfEB7AH/AeoBxAGpAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AeIBrAGH Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/AfsB9wHzAf8B0gF6AUMB/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEAPP8D4AH/A8AB/wOvAf8DogH/ A68B/wPbVf8D+gH/A/MB/wPWAf8DowH/A4kB/wOuAf8D1AH/A+cB/wP6af8BzgHpAfgB/wGOAc0B7wH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYQByQHuAf8BlwHRAfEB/wGxAd0B9AH/Ad8B8QH6Mf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHJAWIBHgH/ AfoB8QHsAf8B6gHEAaoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B4gGsAYcB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH4AesB4gH/AeABqAGCAf8B3gGjAXUB/wHeAaIBdAH/ Ad4BoQFzAf8B3QGhAXMB/wHdAaABcQH/Ad0BnwFxAf8B3AGcAWwB/wHKAWYBJAH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQA8/wPgAf8DwAH/A68B/wOiAf8DrwH/A9tV/wP6 Af8D8wH/A9YB/wOjAf8DiQH/A64B/wPUAf8D5wH/A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0Af8B3wHxAfox/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AckBYgEeAf8B+gHxAewB/wHq AcUBqwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHhAasBhgH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/AfIB3QHNAf8ByAFeARkB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D21X/A/oB/wPzAf8D1gH/ A6MB/wOJAf8DrgH/A9QB/wPnAf8D+mn/Ac4B6QH4Af8BjgHNAe8B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGEAckB7gH/AZcB0QHxAf8BsQHdAfQB/wHfAfEB+jH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8ByQFiAR4B/wH6AfEB6wH/AeoBxQGsAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AeEBqwGGAf8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B8wHdAc4B/wHIAV8BGgH/AdcBjwFYAf8B8gHcAc0B/wHzAd4B0AH/AfMB3gHQAf8B8wHe AdEB/wHxAdgBxwH/AeEBqgGFAf8ByQFiAR4B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf//AAEAPP8D4AH/A8AB/wOvAf8DogH/A68B/wPbVf8D+gH/A/MB/wPWAf8DowH/A4kB/wOu Af8D1AH/A+cB/wP6af8BzgHpAfgB/wGOAc0B7wH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYQByQHu Af8BlwHRAfEB/wGxAd0B9AH/Ad8B8QH6Mf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHJAWIBHgH/AfoB8QHrAf8B6gHGAawB/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8B4QGqAYUB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wHz Ad4BzwH/AcgBXwEbAf8B2wGaAWkB/wH9AfoB+AH/Af4B/QH8Af8B/gH9AfwB/wH8AfcB9AH/AesByAGv Af8B0AF1ATsB/wHFAVYBDgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQA8/wPgAf8DwAH/A68B/wOiAf8DrwH/A9tV/wP6Af8D8wH/A9YB/wOjAf8DiQH/A64B/wPUAf8D5wH/ A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/ AbEB3QH0Af8B3wHxAfox/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AckBYQEdAf8B+gHxAesB/wHrAcYBrQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHhAaoBhAH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/AfMB3gHQAf8ByAFg ARwB/wHbAZkBZwH/Af0B+gH4Af8B/gH9AfwB/wH9AfoB9wH/AewBywG0Af8B0AF2ATsB/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPA Af8DrwH/A6IB/wOvAf8D21X/A/oB/wPzAf8D1gH/A6MB/wOJAf8DrgH/A9QB/wPnAf8D+mn/Ac4B6QH4 Af8BjgHNAe8B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGEAckB7gH/AZcB0QHxAf8BsQHdAfQB/wHf AfEB+jH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8ByQFhAR0B/wH6AfEB6wH/AesBxwGtAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AeEBqQGD Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B8wHfAdAB/wHIAWABHAH/AdoBmQFm Af8B/QH6AfgB/wH8AfcB9AH/AesByQGyAf8BzwFzATgB/wHFAVgBEAH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEAPP8D4AH/A8AB/wOvAf8DogH/ A68B/wPbVf8D+gH/A/MB/wPWAf8DowH/A4kB/wOuAf8D1AH/A+cB/wP6af8BzgHpAfgB/wGOAc0B7wH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYQByQHuAf8BlwHRAfEB/wGxAd0B9AH/Ad8B8QH6Mf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHJAWEBHQH/ AfoB8QHrAf8B6wHHAa4B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B4QGpAYMB/wH+Af0B/AH/ Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9 AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wHzAd8B0QH/AckBYQEdAf8B2gGYAWUB/wH7AfUB8AH/ AesBxwGvAf8B0AF3AT0B/wHFAVcBDwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQA8/wPgAf8DwAH/A68B/wOiAf8DrwH/A9tV/wP6 Af8D8wH/A9YB/wOjAf8DiQH/A64B/wPUAf8D5wH/A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0Af8B3wHxAfox/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AckBYQEdAf8B+gHwAesB/wHr AcgBrwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHgAagBggH/Af4B/QH8Af8B/gH9AfwB/wH+ Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8Af8B/gH9AfwB/wH+Af0B/AH/Af4B/QH8 Af8B/gH9AfwB/wH+Af0B/AH/AfMB3wHSAf8ByQFhAR4B/wHZAZUBYQH/AesByAGvAf8B0QF4AT8B/wHO AW8BMQH/AcUBVwEQAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D21X/A/oB/wPzAf8D1gH/ A6MB/wOJAf8DrgH/A9QB/wPnAf8D+mn/Ac4B6QH4Af8BjgHNAe8B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGEAckB7gH/AZcB0QHxAf8BsQHdAfQB/wHfAfEB+jH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8ByQFhAR0B/wH5AfAB6gH/AesByAGwAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AdsBmQFnAf8B8gHdAc4B/wHyAdwBzQH/AfIB3AHMAf8B8gHb AcsB/wHxAdoBygH/AfEB2gHJAf8B8QHZAcgB/wHxAdgBxwH/AfAB2AHGAf8B8AHXAcUB/wHwAdYBxAH/ AfAB1gHDAf8B6AG/AaMB/wHIAV8BGgH/Ac0BbgEvAf8BzwFyATcB/wHVAYgBTgH/Ae8B0gG/Af8ByAFg ARsB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf//AAEAPP8D4AH/A8AB/wOvAf8DogH/A68B/wPbVf8D+gH/A/MB/wPWAf8DowH/A4kB/wOu Af8D1AH/A+cB/wP6af8BzgHpAfgB/wGOAc0B7wH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYQByQHu Af8BlwHRAfEB/wGxAd0B9AH/Ad8B8QH6Mf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHJAWEBHQH/AfkB8AHqAf8B6wHJAbAB/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxgFbARQB/wHIAWABHAH/AcgBYAEbAf8ByAFgARsB/wHIAV8BGwH/AcgBXwEa Af8ByAFfARoB/wHIAV4BGQH/AcgBXgEZAf8BxwFeARgB/wHHAV4BGAH/AccBXQEYAf8BxwFdARcB/wHH AVsBFQH/AcUBVgENAf8BxQFXAQ4B/wHFAVcBDgH/AegBvgGhAf8B+QHvAegB/wHJAWEBHQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQA8/wPgAf8DwAH/A68B/wOiAf8DrwH/A9tV/wP6Af8D8wH/A9YB/wOjAf8DiQH/A64B/wPUAf8D5wH/ A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/ AbEB3QH0Af8B3wHxAfox/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcgBYQEcAf8B+QHwAeoB/wHsAckBsQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8B6gHGAawB/wH6AfEB6wH/AckBYgEeAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPA Af8DrwH/A6IB/wOvAf8D2zn/A/4B/wP9Af8D/QH/A/wB/wP8Af8D/AH/A/wB/wP3Af8D8QH/A9QB/wOi Af8DiAH/A64B/wPUAf8D5wH/A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0Af8B3wHxAfox/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcgBYQEcAf8B+QHwAeoB/wHsAcoBsgH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B6gHFAawB/wH6AfEB6wH/AckBYgEe Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D2zn/A/oB/wP0Af8D7gH/A+sB/wPpAf8D6QH/ A+kB/wPlAf8D3wH/A8YB/wObAf8DhwH/A60B/wPUAf8D5wH/A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0Af8B3wHxAfox/wHFAVgBEAH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcgBYQEcAf8B+QHw AeoB/wHsAcoBswH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B6gHF AasB/wH6AfEB7AH/AckBYgEeAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D2zn/A/cB/wPq Af8D4AH/A9sB/wPWAf8D1gH/A9YB/wPSAf8DzgH/A7gB/wOUAf8DhQH/A60B/wPUAf8D5wH/A/pp/wHO AekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0 Af8B3wHxAfox/wHMAWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcgBYQEcAf8B+QHwAeoB/wHsAcsBswH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8B6gHEAaoB/wH6AfEB7AH/AckBYgEeAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/ A6IB/wOvAf8D2zn/A/AB/wPWAf8DwgH/A7kB/wOwAf8DsAH/A7EB/wOuAf8DqgH/A50B/wOKAf8DhgH/ A64B/wPVAf8D6AH/A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BhAHJ Ae4B/wGXAdEB8QH/AbEB3QH0Af8B3wHxAfox/wHMAWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcgBYAEcAf8B+QHwAeoB/wHsAcsBtAH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B6gHEAakB/wH6AfEB7AH/AckBYgEeAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ /wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D2zn/A+cB/wPAAf8DogH/A5QB/wOGAf8DiAH/A4kB/wOH Af8DgwH/A4AB/wN5Af8DiAH/A7AB/wPYAf8D6QH/A/pp/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0Af8B3wHxAfox/wHMAWoBKgH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcgBYAEcAf8B+QHwAeoB/wHs AcsBtAH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B6gHEAakB/wH6 AfEB7AH/AckBYgEeAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D2zn/A+QB/wO4Af8DmQH/ A5AB/wOIAf8DjQH/A5IB/wOKAf8DegH/A4EB/wOQAf8DowH/A8MB/wPiAf8D7wH/A/tp/wHOAekB+AH/ AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0Af8B3wHx Afox/wHMAWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcgBYAEcAf8B+QHwAekB/wHtAcwBtQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8B6QHDAagB/wH6AfEB7AH/AckBYgEeAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOv Af8D2zn/A+QB/wO4Af8DnAH/A58B/wOhAf8DqwH/A7UB/wOlAf8DjgH/A5QB/wOyAf8DzQH/A98B/wPx Af8D9wH/A/1p/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGA AccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHu Af8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BhAHJAe4B/wGX AdEB8QH/AbEB3QH0Af8B3wHxAfox/wHMAWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcgBYAEcAf8B+QHvAekB/wHtAcwBtgH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B6QHCAacB/wH6AfIB7AH/AckBYgEeAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/ A+AB/wPAAf8DrwH/A6IB/wOvAf8D2zn/A+QB/wO4Af8DngH/A6QB/wOrAf8DuQH/A8cB/wO4Af8DoQH/ A6oB/wPOAf8D6gH/A/MB/wP7Af8D/QH/A/5p/wHOAekB+AH/AY4BzQHvAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHH Ae4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/AYABxwHuAf8BgAHHAe4B/wGAAccB7gH/ AYABxwHuAf8BhAHJAe4B/wGXAdEB8QH/AbEB3QH0Af8B3wHxAfox/wHMAWoBKgH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcgBYAEbAf8B+QHvAegB/wHtAc0BtwH/ AcUBVgEOAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8B6QHCAacB/wH6AfIB7AH/ AckBYgEfAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrwH/A6IB/wOvAf8D2zn/A+QB/wO4Af8DmwH/A5sB/wOb Af8DqwH/A7sB/wO9Af8DvQH/A8kB/wPhAf8D8wH/A/gB/wP9Af8D/gH/A/5p/wHRAeoB+AH/AZUB0AHw Af8BhwHKAe8B/wGHAcoB7wH/AYcBygHvAf8BhwHKAe8B/wGHAcoB7wH/AYcBygHvAf8BhwHKAe8B/wGH AcoB7wH/AYcBygHvAf8BhwHKAe8B/wGHAcoB7wH/AYcBygHvAf8BhwHKAe8B/wGHAcoB7wH/AYcBygHv Af8BhwHKAe8B/wGHAcoB7gH/AYYBygHuAf8BgwHIAe4B/wGAAccB7gH/AYMByAHuAf8BhQHJAe4B/wGG AckB7gH/AYcBygHvAf8BhwHKAe8B/wGHAcoB7wH/AYcBygHvAf8BhwHKAe8B/wGHAcoB7wH/AYcBygHv Af8BhwHKAe8B/wGHAcoB7gH/AYUByQHuAf8BhwHKAe8B/wGZAdIB8QH/AbEB3QH0Af8B3wHxAfox/wHM AWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcgBXgEZ Af8B8wHdAc4B/wHxAdkByAH/AdEBeAE+Af8BxgFaARQB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8B6QHBAaYB/wH6AfIB7AH/AckBYgEfAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+AB/wPAAf8DrgH/A6EB/wOtAf8D2AH/ A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D+wH/A/sB/wP7Af8D4QH/ A7YB/wOYAf8DkgH/A40B/wOgAf8DswH/A8QB/wPWAf8D5QH/A/IB/wP7Af8D/XX/AeIB8gH7Af8BvQHh AfYB/wG1Ad4B9QH/AbUB3gH1Af8BtQHeAfUB/wG1Ad4B9QH/AbUB3gH1Af8BtQHeAfUB/wG1Ad4B9QH/ AbUB3gH1Af8BtQHeAfUB/wG1Ad4B9QH/AbUB3gH1Af8BtQHeAfUB/wG1Ad4B9QH/AbUB3gH1Af8BtQHe AfUB/wG1Ad4B9QH/AbIB3QH0Af8BrwHbAfQB/wGZAdIB8QH/AYYByQHuAf8BlgHQAfAB/wGlAdcB8gH/ Aa0B2wH0Af8BtQHeAfUB/wG1Ad4B9QH/AbUB3gH1Af8BtQHeAfUB/wG1Ad4B9QH/AbUB3gH1Af8BtQHe AfUB/wG1Ad4B9QH/AbIB3QH0Af8BpAHXAfIB/wGbAdMB8QH/AaEB1gHyAf8BsQHdAfQB/wHfAfEB+jH/ AcwBagEqAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxgFb ARMB/wHfAaYBegH/AfcB6QHfAf8B7gHRAb0B/wHeAaQBdgH/AdsBmwFqAf8B2wGbAWoB/wHbAZwBbAH/ AdwBnQFsAf8B3AGeAW4B/wHcAZ4BbgH/AdwBnwFwAf8B3QGfAXAB/wHdAaABcgH/Ad0BoQFzAf8B3QGi AXQB/wHeAaIBdAH/Ad4BowF1Af8B3gGjAXYB/wHeAaQBdwH/Ad8BpQF4Af8B3wGlAXkB/wHfAaYBegH/ Ad8BpwGAAf8B4AGoAYEB/wHgAagBggH/AeABqQGDAf8B4AGpAYQB/wHgAaoBhQH/AeEBqwGGAf8B4QGs AYcB/wHhAawBiAH/AeEBrQGJAf8B4gGtAYoB/wHiAa4BiwH/AeIBrwGLAf8B4gGwAY0B/wHjAbABjQH/ AeMBsQGPAf8B4wGyAY8B/wHjAbIBkQH/AeQBswGRAf8B5AG0AZMB/wHkAbQBkwH/AeQBtQGVAf8B5QG2 AZUB/wH1AeQB2AH/AfoB8gHtAf8ByQFiAR8B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf//AAEAPP8D4AH/A8AB/wOrAf8DmgH/A58B/wPA Af8D2wH/A9sB/wPbAf8D2wH/A9sB/wPbAf8D2wH/A9sB/wPbAf8D2wH/A9sB/wPbAf8D2wH/A9sB/wPH Af8DpgH/A5EB/wORAf8DkwH/A6kB/wO/Af8D0wH/A+UB/wPxAf8D+AH/A/0B/wP+df8B8QH5Af0B/wHg AfEB+wH/AdwB7wH6Af8B3AHvAfoB/wHcAe8B+gH/AdwB7wH6Af8B3AHvAfoB/wHcAe8B+gH/AdwB7wH6 Af8B3AHvAfoB/wHcAe8B+gH/AdwB7wH6Af8B3AHvAfoB/wHcAe8B+gH/AdwB7wH6Af8B3AHvAfoB/wHc Ae8B+gH/AdwB7wH6Af8B2AHtAfoB/wHTAesB+QH/AbAB3AH0Af8BkAHOAe8B/wGoAdgB8wH/Ab4B4gH2 Af8BywHoAfgB/wHWAewB+QH/AdYB7AH5Af8B1gHsAfkB/wHWAewB+QH/AdYB7AH5Af8B1gHsAfkB/wHW AewB+QH/AdYB7AH5Af8B0QHqAfkB/wG7AeEB9gH/AaoB2gHzAf8BpwHYAfMB/wGxAd0B9AH/Ad8B8QH6 Mf8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AckBYwEfAf8B4gGsAYgB/wH1AeUB2gH/Af0B+gH4qf8B+gHyAe0B/wHJAWMBHwH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQA8/wPgAf8DwAH/A6gB/wOSAf8DkQH/A6gB/wO7Af8DuwH/A7sB/wO7Af8DuwH/A7sB/wO7Af8DuwH/ A7sB/wO7Af8DuwH/A7sB/wO7Af8DuwH/A60B/wOWAf8DiQH/A5AB/wOZAf8DsgH/A8wB/wPgAf8D9AH/ A/0B/wP+Af8D/gH/A/51/wH5AfwB/gH/AfEB+QH9Af8B8AH4Af0B/wHwAfgB/QH/AfAB+AH9Af8B8AH4 Af0B/wHwAfgB/QH/AfAB+AH9Af8B8AH4Af0B/wHwAfgB/QH/AfAB+AH9Af8B8AH4Af0B/wHwAfgB/QH/ AfAB+AH9Af8B8AH4Af0B/wHwAfgB/QH/AfAB+AH9Af8B8AH4Af0B/wHsAfYB/AH/AegB9AH8Af8BygHn AfcB/wGuAdsB8wH/AbcB3wH1Af8BvgHiAfYB/wHEAeUB9wH/AckB5wH4Af8ByQHnAfgB/wHJAecB+AH/ AckB5wH4Af8ByQHnAfgB/wHJAecB+AH/AckB5wH4Af8ByQHnAfgB/wHFAeUB9wH/AbIB3QH0Af8BpAHX AfIB/wGlAdcB8gH/AbEB3QH0Af8B3wHxAfox/wHMAWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHIAWEBHAH/AdABdgE7Af8B0wGE AUgB/wHTAYUBSgH/AdMBhQFKAf8B0wGEAUkB/wHTAYQBSQH/AdMBhAFIAf8B0wGEAUgB/wHTAYMBRwH/ AdMBgwFHAf8B0gGDAUcB/wHSAYIBRgH/AdIBggFGAf8B0gGCAUYB/wHSAYEBRQH/AdIBgQFFAf8B0gGB AUQB/wHSAYEBRAH/AdIBgQFEAf8B5gG5AZsB/wH6AfMB7wH/AfsB9AHwAf8B5QG1AZQB/wHVAYgBTgH/ AdEBegFBAf8B0QF5AUAB/wHRAXkBQAH/AdEBeQFAAf8B0QF5AT8B/wHRAXgBPwH/AdABeAE/Af8B0AF4 AT4B/wHQAXcBPgH/AdABdwE9Af8B0AF3AT0B/wHQAXYBPAH/AdABdgE8Af8B0AF2ATsB/wHQAXYBOwH/ AdABdQE7Af8BzwF1AToB/wHPAXUBOgH/Ac8BdQE6Af8B7QHMAbUB/wH6AfIB7QH/AckBYwEfAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ /wABADz/A+MB/wPHAf8DsAH/A5oB/wOUAf8DogH/A60B/wOtAf8DrQH/A60B/wOtAf8DrQH/A60B/wOt Af8DrQH/A60B/wOtAf8DrQH/A60B/wOtAf8DpQH/A5cB/wOSAf8DoAH/A64B/wPGAf8D3wH/A+0B/wP5 zf8B/AH9Af4B/wH5AfwB/gH/AeIB8gH6Af8BzAHoAfcB/wHFAeUB9wH/Ab8B4wH2Af8BvQHiAfYB/wG8 AeEB9gH/AbwB4QH2Af8BvAHhAfYB/wG8AeEB9gH/AbwB4QH2Af8BvAHhAfYB/wG8AeEB9gH/AbwB4QH2 Af8BuQHgAfYB/wGrAdoB8wH/AaEB1QHyAf8BpgHXAfIB/wG0Ad4B9QH/AeAB8QH6Mf8BzAFqASoB/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcsBaQEpAf8B4QGrAYYB/wH4 AewB5AH/AfUB5QHaAf8B3AGcAWsB/wHHAV4BGQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AegBwAGj Af8B+gHyAe0B/wHJAWMBHwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQA8/wPnAf8D0AH/A7oB/wOkAf8DmgH/A50B/wOgAf8DoAH/ A6AB/wOgAf8DoAH/A6AB/wOgAf8DoAH/A6AB/wOgAf8DoAH/A6AB/wOgAf8DoAH/A54B/wOaAf8DngH/ A7IB/wPFAf8D3AH/A/MB/wP6Af8D/c3/Af0C/gH/AfwB/QH+Af8B8gH5Af0B/wHoAfUB+wH/AdMB7AH5 Af8BvwHjAfYB/wG2Ad8B9QH/Aa8B3AH0Af8BrwHcAfQB/wGvAdwB9AH/Aa8B3AH0Af8BrwHcAfQB/wGv AdwB9AH/Aa8B3AH0Af8BrwHcAfQB/wGuAdsB9AH/AagB2AHzAf8BpgHXAfIB/wGvAdwB9AH/AcAB4wH2 Af8B5QHzAfsx/wHMAWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHHAV8BGQH/Ad0BoAFxAf8B+AHqAeEB/wH3AeoB4QH/AdwBngFtAf8ByAFhAR0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8B6AG/AaMB/wH6AfIB7QH/AckBYwEfAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A+8B/wPfAf8DzwH/ A8AB/wO3Af8DtwH/A7cB/wO3Af8DtwH/A7cB/wO3Af8DtwH/A7cB/wO3Af8DtwH/A7cB/wO3Af8DtwH/ A7cB/wO3Af8DtwH/A7cB/wO8Af8DzAH/A9sB/wPrAf8D/OH/Af0C/gH/AeIB8gH7Af8ByAHnAfcB/wG8 AeEB9gH/AbEB3QH0Af8BsQHdAfQB/wGxAd0B9AH/AbEB3QH0Af8BsQHdAfQB/wGxAd0B9AH/AbEB3QH0 Af8BsQHdAfQB/wGxAd0B9AH/AbEB3QH0Af8BtAHeAfUB/wHAAeMB9gH/AdAB6gH5Af8B6wH2Afwx/wHM AWoBKgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AccBXgEaAf8B2QGVAWEB/wHzAd0BzgH/AfkB8QHqAf8B4gGvAYwB/wHLAWcBJgH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8B6AG/AaIB/wH6AfIB7QH/AckBYwEgAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH//wABADz/A/kB/wPyAf8D7AH/A+YB/wPjAf8D4wH/ A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/ A+MB/wPlAf8D6wH/A/EB/wP3Af8D/eH/A/4B/wHzAfkB/QH/AegB9QH8Af8B4wHzAfsB/wHfAfEB+gH/ Ad8B8QH6Af8B3wHxAfoB/wHfAfEB+gH/Ad8B8QH6Af8B3wHxAfoB/wHfAfEB+gH/Ad8B8QH6Af8B3wHx AfoB/wHfAfEB+gH/AeAB8QH6Af8B5QHzAfsB/wHrAfYB/AH/AfcB+wH9Mf8BzAFqASoB/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcYBWwEUAf8B1wGPAVgB/wHyAdoBygH/AfgB7QHlAf8B5AG0AZIB/wHNAW4BMAH/AcUBWAEQAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFXAQ4B/wHJAWMBIAH/AesBxwGuAf8B+AHr AeMB/wHJAWIBHgH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxgFaARMB/wHP AXUBOgH/Ae8B0QG9Af8B+gHxAesB/wHrAcYBrQH/AdEBegFBAf8BygFlASMB/wHJAWMBIAH/AckBYwEg Af8ByQFjASAB/wHJAWMBIAH/AckBYwEgAf8ByQFkASAB/wHKAWQBIQH/AcoBZAEhAf8BygFkASEB/wHK AWQBIQH/AcoBZAEhAf8BzAFsAS0B/wHhAaoBhQH/AfYB6AHeAf8B7AHKAbMB/wHIAV4BGQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVcBDgH/Ac4BcAEyAf8B6QHA AaUB/wH4AewB5AH/AfoB8gHtAf8B+gHzAe4B/wH6AfMB7gH/AfoB8wHuAf8B+gHzAe4B/wH6AfMB7gH/ AfoB8wHuAf8B+gHzAe4B/wH6AfMB7gH/AfsB8wHvAf8B+wHzAe8B/wH7AfMB7wH/AfsB9AHvAf8B+wH0 Ae4B/wH5AfAB6QH/AfAB1gHEAf8B1AGFAUoB/wHFAVgBDwH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVwEPAf8BywFoASgB/wHZAZUBYQH/AeUBtwGX Af8B5wG9AaAB/wHnAb0BnwH/AecBvAGeAf8B5wG8AZ4B/wHnAbsBnQH/AecBuwGdAf8B5gG6AZwB/wHm AboBmwH/AeYBuQGaAf8B5gG5AZoB/wHmAbgBmQH/AeYBuAGYAf8B5QG1AZQB/wHeAaQBdwH/AdABdgE8 Af8BxgFZARIB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B//8AAQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFq ASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQD///// Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8A AQD/////Av8BzAFqASoB/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFW AQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/ AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B//8AAQD/////Av8BzAFqASoB/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHF AVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgENAf8BxQFWAQ0B/wHFAVYBDQH/AcUBVgEN Af8BywFoAScB/wHMAWoBKgH/AcwBagEqAf8BzAFqASoB/wHMAWoBKgH/AcwBagEqAf8BzAFqASoB/wHM AWoBKgH/AcwBagEqAf8BzAFqASoB/wHMAWoBKgH/AcwBagEqAf8BzAFqASoB/wHMAWoBKgH/AcwBagEq Af8BzAFqASoB/wHMAWoBKgH/AcwBagEqAf8BzAFqASoB/wHMAWoBKgH/AcwBagEqAf8BzAFqASoB/wHM AWoBKgH/AcwBagEqAf8BzAFqASoB//8AAQABQgFNAT4HAAE+AwABKAQAAQECAAFAAwABAQEAAQEGAAEI FgAD//8A/wD/AP8A/wD/AP8A/wAJAAs= ================================================ FILE: KoenZomers.KeePass.OneDriveSync/KeePass.exe.config ================================================ ================================================ FILE: KoenZomers.KeePass.OneDriveSync/KeePassDatabase.cs ================================================ using System; using System.Threading.Tasks; using System.Windows.Forms; using KeePass.DataExchange; using KeePassLib.Serialization; using KoenZomers.KeePass.OneDriveSync; using KoenZomers.KeePass.OneDriveSync.Enums; namespace KoenZomersKeePassOneDriveSync { /// /// Methods that regard the KeePass database /// public static class KeePassDatabase { /// /// Allows a KeePass database to be opened directly from a cloud service /// /// Method to call to update the status public static async Task OpenDatabaseFromCloudService(Action updateStatus) { // Ask which cloud service to connect to var cloudStorageProviderForm = new OneDriveCloudTypeForm {ExplanationText = "Choose the cloud service you wish to open the KeePass database from:"}; cloudStorageProviderForm.ShowDialog(); if (cloudStorageProviderForm.DialogResult != DialogResult.OK) return; // Create a new database configuration entry var databaseConfig = new Configuration { CloudStorageType = cloudStorageProviderForm.ChosenCloudStorageType }; // Verify the config object is ready to be used if (!databaseConfig.CloudStorageType.HasValue) { UpdateStatus("Invalid cloud storage provider chosen"); return; } string localKeePassDatabasePath = null; switch (databaseConfig.CloudStorageType) { case CloudStorageType.MicrosoftGraph: case CloudStorageType.OneDriveConsumer: case CloudStorageType.MicrosoftGraphDeviceLogin: localKeePassDatabasePath = await Providers.OneDriveProvider.OpenFromOneDriveCloudProvider(databaseConfig, updateStatus); break; case CloudStorageType.SharePoint: localKeePassDatabasePath = await Providers.SharePointProvider.OpenFromSharePoint(databaseConfig, updateStatus); break; } if(localKeePassDatabasePath == null) { return; } // Register the KeePass database sync information UpdateStatus("Configuring KeePass database"); databaseConfig.LocalFileHash = Utilities.GetDatabaseFileHash(localKeePassDatabasePath); databaseConfig.LastSyncedAt = DateTime.Now; databaseConfig.LastCheckedAt = DateTime.Now; // Add the new configuration to the database configuration set in memory Configuration.SetPasswordDatabaseConfiguration(localKeePassDatabasePath, databaseConfig); // Save the database configuration set to persistent storage Configuration.Save(); UpdateStatus("Opening KeePass database"); // Open the KeePass database var databaseFile = IOConnectionInfo.FromPath(localKeePassDatabasePath); KoenZomersKeePassOneDriveSyncExt.Host.MainWindow.OpenDatabase(databaseFile, null, false); UpdateStatus("KeePass database is ready to be used"); } /// /// Checks if a newer database exists on OneDrive compared to the locally opened version and syncs them if so /// /// Full path or relative path from the KeePass executable folder to where the KeePass database resides locally /// Method to call to update the status /// If True, no attempts will be made to validate if the local copy differs from the remote copy and it will always sync. If False, it will try to validate if there are any changes and not sync if there are no changes. public static async Task SyncDatabase(string localKeePassDatabasePath, Action updateStatus, bool forceSync, Configuration databaseConfig) { try { // Retrieve the KeePassOneDriveSync settings if (databaseConfig == null) { databaseConfig = Configuration.GetPasswordDatabaseConfiguration(localKeePassDatabasePath); } // If this database is already syncing, abort this attempt. This happens when the Import saves the resulting KeePass database. That by itself triggers another sync which doesn't have to go through the sync process as it just regards the temp database. if (databaseConfig.IsCurrentlySyncing) { UpdateStatus(string.Format("A synchronization is already in progress for database {0}", databaseConfig.KeePassDatabase.Name)); return; } // Check if this database explicitly does not allow to be synced with OneDrive and if syncing is enabled on this database if (databaseConfig.DoNotSync || !databaseConfig.SyncingEnabled) { return; } UpdateStatus(string.Format("Starting database synchronization for database {0}", databaseConfig.KeePassDatabase.Name)); databaseConfig.IsCurrentlySyncing = true; // Check if the current database is synced with OneDrive if (string.IsNullOrEmpty(databaseConfig.RemoteDatabasePath) && string.IsNullOrEmpty(databaseConfig.RemoteFolderId) && string.IsNullOrEmpty(databaseConfig.RemoteFileName)) { var cloudStorageType = AskIfShouldBeSynced(databaseConfig); if(!cloudStorageType.HasValue) { // No decission has been taken whether to sync the database or not // Set flag to indicate that the database is currently not syncing databaseConfig.IsCurrentlySyncing = false; UpdateStatus(string.Format("Canceled database synchronization for database {0}", databaseConfig.KeePassDatabase.Name)); return; } if (cloudStorageType.Value == CloudStorageType.None) { // Database should not be synchronized // Set flag to indicate that the database is currently not syncing databaseConfig.IsCurrentlySyncing = false; UpdateStatus(string.Format("Database {0} will not be synchronized", databaseConfig.KeePassDatabase.Name)); return; } databaseConfig.CloudStorageType = cloudStorageType.Value; Configuration.Save(); } // Start the sync operation using one of the Cloud Storage Providers bool syncSuccessful = false; switch(databaseConfig.CloudStorageType) { case CloudStorageType.MicrosoftGraph: case CloudStorageType.MicrosoftGraphDeviceLogin: case CloudStorageType.OneDriveConsumer: syncSuccessful = await Providers.OneDriveProvider.SyncUsingOneDriveCloudProvider(databaseConfig, localKeePassDatabasePath, forceSync, updateStatus); break; case CloudStorageType.SharePoint: syncSuccessful = await Providers.SharePointProvider.SyncUsingSharePointPlatform(databaseConfig, localKeePassDatabasePath, forceSync, updateStatus); break; } if(!syncSuccessful) { // Set flag to allow exiting KeePass databaseConfig.IsCurrentlySyncing = false; return; } // Update the KeePass UI so it shows the new entries KoenZomersKeePassOneDriveSyncExt.Host.MainWindow.UpdateUI(false, null, true, null, true, null, false); updateStatus(string.Format("KeePass database {0} has successfully been synchronized and uploaded", databaseConfig.KeePassDatabase.Name)); databaseConfig.LocalFileHash = Utilities.GetDatabaseFileHash(localKeePassDatabasePath); //databaseConfig.ETag = uploadResult.ETag; databaseConfig.LastSyncedAt = DateTime.Now; databaseConfig.LastCheckedAt = DateTime.Now; Configuration.Save(); } catch(Exception e) { updateStatus(string.Format("Failed to sync database {0}: {1}", databaseConfig.KeePassDatabase.Name, e.Message)); } // Set flag to allow exiting KeePass databaseConfig.IsCurrentlySyncing = false; } /// /// Asks if the database should be synced and if yes with which storage provider /// /// Database to sync for /// Storage type to sync with or NULL if it should not be synced private static CloudStorageType? AskIfShouldBeSynced(Configuration databaseConfig) { // Current database is not being syned with OneDrive, ask if it should be synced var oneDriveAskToStartSyncingForm = new OneDriveAskToStartSyncingDialog(databaseConfig); if (oneDriveAskToStartSyncingForm.ShowDialog() != DialogResult.OK) { // Dialog has been canceled. Return NULL to indicate no choice has been made. return null; } if(databaseConfig.DoNotSync) { // Don't sync and don't ask again has been chosen return CloudStorageType.None; } // Ask which cloud service to connect to var oneDriveCloudTypeForm = new OneDriveCloudTypeForm { ExplanationText = string.Format("Choose the cloud service you wish to store the KeePass database {0} on:", databaseConfig.KeePassDatabase.Name) }; if (oneDriveCloudTypeForm.ShowDialog() != DialogResult.OK) { // Dialog has been canceled. Return NULL to indicate no choice has been made. return null; } return oneDriveCloudTypeForm.ChosenCloudStorageType; } /// /// Merges the KeePass database at the provided path with the current database /// /// Configuration of the database to merge the KeePass database with /// Path to the KeePass database to merge with the currently open database /// True if successful, false if failed to merge public static bool MergeDatabases(Configuration databaseConfig, string tempDatabasePath) { var connectionInfo = IOConnectionInfo.FromPath(tempDatabasePath); var formatter = KoenZomersKeePassOneDriveSyncExt.Host.FileFormatPool.Find("KeePass KDBX (2.x)"); // Temporarily switch off syncing for this database to avoid the import operation, which causes a save, to create and endless loop databaseConfig.SyncingEnabled = false; // Import the current database with the downloaded database. Import causes a one way sync, syncing would try to update both ends which won't work for OneDrive. var importSuccessful = ImportUtil.Import(databaseConfig.KeePassDatabase, formatter, new[] { connectionInfo }, true, KoenZomersKeePassOneDriveSyncExt.Host.MainWindow, false, KoenZomersKeePassOneDriveSyncExt.Host.MainWindow); // Enable syncing of this database again databaseConfig.SyncingEnabled = true; // Remove the temporary database from the Most Recently Used (MRU) list in KeePass as its added automatically on the import action KoenZomersKeePassOneDriveSyncExt.Host.MainWindow.FileMruList.RemoveItem(tempDatabasePath); return importSuccessful.GetValueOrDefault(false); } /// /// Displays a status message on the bottom bar /// /// Message to show public static void UpdateStatus(string message) { KoenZomersKeePassOneDriveSyncExt.Host.MainWindow.SetStatusEx(message); } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/KoenZomers.KeePass.OneDriveSync.csproj ================================================  Debug AnyCPU {ED981431-32EA-4FC8-BF5B-D5A29B3AD52E} Library Properties KoenZomersKeePassOneDriveSync KoenZomersKeePassOneDriveSync v4.5.2 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false none true bin\Release\ TRACE prompt 4 false 5 False .\CredentialManagement.dll ..\..\..\..\Program Files\KeePass Password Safe 2\KeePass.exe Packages\KoenZomers.OneDrive.Api.2.3.0.3\lib\net452\KoenZomers.OneDrive.Api.dll Packages\Microsoft.IdentityModel.6.1.7600.16394\lib\net35\Microsoft.IdentityModel.dll True Packages\Microsoft.Identity.Model.Extensions.2.0.1459.0\lib\Microsoft.IdentityModel.Extensions.dll True Packages\Newtonsoft.Json.8.0.1\lib\net45\Newtonsoft.Json.dll True True Form OneDriveGraphDeviceLoginForm.cs True True OneDriveGraphDeviceLoginForm.resx Form SharePointDocumentLibraryPickerDialog.cs Form OneDriveFilePickerDialog.cs Form OneDriveRequestInputDialog.cs Form SharePointCredentialsForm.cs Form OneDriveAboutForm.cs Form OneDriveCloudTypeForm.cs Form OneDriveRefreshTokenStorageDialog.cs Form OneDriveAskToStartSyncingDialog.cs Form OneDriveAuthenticateForm.cs Form OneDriveConfigDetailsForm.cs Form OneDriveConfigForm.cs True True Resources.resx OneDriveAboutForm.cs OneDriveGraphDeviceLoginForm.cs PublicResXFileCodeGenerator OneDriveGraphDeviceLoginForm1.Designer.cs OneDriveCloudTypeForm.cs SharePointDocumentLibraryPickerDialog.cs OneDriveFilePickerDialog.cs OneDriveRequestInputDialog.cs OneDriveRefreshTokenStorageDialog.cs OneDriveAskToStartSyncingDialog.cs OneDriveAuthenticateForm.cs OneDriveConfigDetailsForm.cs OneDriveConfigForm.cs SharePointCredentialsForm.cs ResXFileCodeGenerator Resources.Designer.cs PreserveNewest REM $(SolutionDir)create-plgx.cmd ================================================ FILE: KoenZomers.KeePass.OneDriveSync/KoenZomersKeePassOneDriveSyncExt.cs ================================================ using System; using System.Windows.Forms; using KeePass.Forms; using KeePass.Plugins; using KoenZomers.KeePass.OneDriveSync; using KoenZomers.KeePass.OneDriveSync.Enums; namespace KoenZomersKeePassOneDriveSync { /// /// Plugin for KeePass to allow synchronization with OneDrive /// /// KeePass SDK documentation: http://keepass.info/help/v2_dev/plg_index.html public class KoenZomersKeePassOneDriveSyncExt : Plugin { #region Constants /// /// Client ID to use for communication with the OneDrive Consumer API /// internal const string OneDriveConsumerClientId = "000000004813F265"; /// /// Client Secret to use for communication with the OneDrive Consumer API /// internal const string OneDriveConsumerClientSecret = "GXpsZ6DX4AaNjRWFovMLIp1xS-FpbPgO"; /// /// Client ID to use for communication with the OneDrive for Business API /// internal const string OneDriveForBusinessClientId = "c1b42b56-2aa9-4f01-ad76-11644ae1a859"; /// /// Client Secret to use for communication with the OneDrive for Business API /// internal const string OneDriveForBusinessClientSecret = "ezb/qt5Iat0aE8S+ZWBtxhDtQLISEaPCkZ5agQv9MgM="; /// /// Application ID to use for communication with the Microsoft Graph API /// internal const string GraphApiApplicationId = "7bcec80a-2ffe-4713-b9ea-0150361c8209"; #endregion #region Properties /// /// Reference to the KeePass instance /// public static IPluginHost Host; #endregion #region Fields /// /// Separator line in the tools menu /// private ToolStripSeparator _toolsMenuSeparator; /// /// Config option item in the Tools menu /// private ToolStripMenuItem _toolsMenuConfigMenuItem; /// /// Open from OneDrive option in File > Open menu /// private ToolStripMenuItem _fileOpenMenuItem; /// /// Offline OneDrive option in File menu /// private ToolStripMenuItem _fileOfflineMenuItem; #endregion /// /// Returns the URL where KeePass can check for updates of this plugin /// public override string UpdateUrl { get { return "https://raw.githubusercontent.com/KoenZomers/KeePassOneDriveSync/master/version.txt"; } } /// /// Called when the Plugin is being loaded which happens on startup of KeePass /// /// True if the plugin loaded successfully, false if not public override bool Initialize(IPluginHost pluginHost) { Host = pluginHost; // Enable TLS 1.2 System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; // Load the configuration Configuration.Load(); // Bind to the events for loading and saving databases Host.MainWindow.FileOpened += OnKeePassDatabaseOpened; Host.MainWindow.FileSaved += MainWindowOnFileSaved; Host.MainWindow.FileClosed += OnKeePassDatabaseClosed; Host.MainWindow.FileClosingPost += OnKeePassDatabaseClosing; // Add the menu option for configuration under Tools var toolsmenu = Host.MainWindow.ToolsMenu.DropDownItems; _toolsMenuSeparator = new ToolStripSeparator(); _toolsMenuConfigMenuItem = new ToolStripMenuItem("OneDriveSync Options", Resources.OneDriveIcon); _toolsMenuConfigMenuItem.Click += MenuOptionsOnClick; toolsmenu.Add(_toolsMenuConfigMenuItem); // Add the menu option for configuration under File > Open var filemenu = Host.MainWindow.MainMenu.Items["m_menuFile"] as ToolStripMenuItem; if (filemenu != null) { var openmenu = filemenu.DropDownItems["m_menuFileOpen"] as ToolStripMenuItem; if (openmenu != null) { _fileOpenMenuItem = new ToolStripMenuItem("Open from OneDrive...", Resources.OneDriveIcon) { ShortcutKeys = Keys.Control | Keys.Alt | Keys.O }; _fileOpenMenuItem.Click += MenuFileOpenFromOneDriveOnClick; openmenu.DropDownItems.Add(_fileOpenMenuItem); } // Add the menu option for Offline use under File var fileLockIndex = filemenu.DropDownItems.IndexOfKey("m_menuFileLock"); _fileOfflineMenuItem = new ToolStripMenuItem("OneDriveSync is Online", Resources.OneDriveIcon) { Checked = true }; _fileOfflineMenuItem.Click += MenuFileOneDriveSyncOfflineOnClick; filemenu.DropDownItems.Insert(fileLockIndex != -1 ? fileLockIndex : filemenu.DropDownItems.Count - 1, _toolsMenuSeparator); filemenu.DropDownItems.Insert(fileLockIndex != -1 ? fileLockIndex : filemenu.DropDownItems.Count - 1, _fileOfflineMenuItem); } // Indicate that the plugin started successfully return true; } /// /// Triggered when the Plugin is being terminated /// public override void Terminate() { // Remove custom items from the menu as per recommendations on http://keepass.info/help/v2_dev/plg_index.html var optionsmenu = Host.MainWindow.ToolsMenu.DropDownItems; _toolsMenuConfigMenuItem.Click -= MenuOptionsOnClick; optionsmenu.Remove(_toolsMenuSeparator); optionsmenu.Remove(_toolsMenuConfigMenuItem); var openmenu = Host.MainWindow.MainMenu.Items["m_menuFile"] as ToolStripMenuItem; if (openmenu != null) { _fileOpenMenuItem.Click -= MenuFileOpenFromOneDriveOnClick; openmenu.DropDownItems.Remove(_fileOpenMenuItem); } _fileOfflineMenuItem.Click -= MenuFileOneDriveSyncOfflineOnClick; openmenu.DropDownItems.Remove(_fileOfflineMenuItem); } /// /// Triggered when a KeePass database is closing after the database has been saved /// private void OnKeePassDatabaseClosing(object sender, FileClosingEventArgs e) { // Check if there's still a process running that we need to wait for before allowing KeePass to terminate if (Configuration.IsSomethingStillRunning) { Host.MainWindow.SetStatusEx("Waiting for OneDriveSync to complete..."); // Record the time until we want to wait at most before exiting var waitUntil = DateTime.Now.AddSeconds(30); do { System.Threading.Thread.Sleep(1); Application.DoEvents(); } while (Configuration.IsSomethingStillRunning && DateTime.Now < waitUntil); } } /// /// Triggered when a KeePass database is being saved /// private async void MainWindowOnFileSaved(object sender, FileSavedEventArgs fileSavedEventArgs) { var databasePath = fileSavedEventArgs.Database.IOConnectionInfo.Path; var config = Configuration.GetPasswordDatabaseConfiguration(databasePath); config.KeePassDatabase = fileSavedEventArgs.Database; // Check if we should sync this database if (config.DoNotSync) return; if(!_fileOfflineMenuItem.Checked) { Host.MainWindow.SetStatusEx(string.Format("OneDriveSync has been set to offline, skipping sync for database {0}", fileSavedEventArgs.Database.Name)); return; } // Make sure it's not a remote database on i.e. an FTP or website if (!fileSavedEventArgs.Database.IOConnectionInfo.IsLocalFile()) { MessageBox.Show("KeePass OneDriveSync does not support synchronizing databases from remote locations and will therefore not be available for this database", "KeePass OneDriveSync", MessageBoxButtons.OK, MessageBoxIcon.Information); config.DoNotSync = true; Configuration.Save(); return; } await KeePassDatabase.SyncDatabase(databasePath, KeePassDatabase.UpdateStatus, true, config); // If the OneDrive Refresh Token is stored in the KeePass database, we must trigger a save of the database here so to ensure that the actual value gets saved into the KDBX if (config.RefreshTokenStorage == OneDriveRefreshTokenStorage.KeePassDatabase) { fileSavedEventArgs.Database.Save(null); } } /// /// Triggered when clicking on the OneDriveSync menu item under Tools /// private static void MenuOptionsOnClick(object sender, EventArgs e) { var oneDriveConfigForm = new OneDriveConfigForm(); oneDriveConfigForm.ShowDialog(); } /// /// Triggered when clicking on the Open from OneDrive menu item under File > Open /// private async static void MenuFileOpenFromOneDriveOnClick(object sender, EventArgs e) { await KeePassDatabase.OpenDatabaseFromCloudService(KeePassDatabase.UpdateStatus); } /// /// Triggered when clicking on the OneDriveSync Offline Mode menu item under File /// private void MenuFileOneDriveSyncOfflineOnClick(object sender, EventArgs e) { _fileOfflineMenuItem.Checked = !_fileOfflineMenuItem.Checked; _fileOfflineMenuItem.Text = _fileOfflineMenuItem.Checked ? _fileOfflineMenuItem.Text = "OneDriveSync is Online" : _fileOfflineMenuItem.Text = "OneDriveSync is Offline"; } /// /// Triggered when a KeePass database is being opened /// private async void OnKeePassDatabaseOpened(object sender, FileOpenedEventArgs fileOpenedEventArgs) { var databasePath = fileOpenedEventArgs.Database.IOConnectionInfo.Path; // Add the KeePass database instance to the already available configuration var config = Configuration.GetPasswordDatabaseConfiguration(databasePath); config.KeePassDatabase = fileOpenedEventArgs.Database; // Check if we should sync this database if (config.DoNotSync || !fileOpenedEventArgs.Database.IOConnectionInfo.IsLocalFile()) return; if(!_fileOfflineMenuItem.Checked) { Host.MainWindow.SetStatusEx(string.Format("OneDriveSync has been set to offline, skipping sync for database {0}", fileOpenedEventArgs.Database.Name)); return; } // Check if the database configuration of the opened KeePass database is set to retrieve the OneDrive Refresh Token from the KeePass database itself if (config.RefreshTokenStorage == OneDriveRefreshTokenStorage.KeePassDatabase && string.IsNullOrEmpty(config.RefreshToken)) { // Retrieve the OneDrive Refresh Token from the KeePass database that is being opened config.RefreshToken = Utilities.GetRefreshTokenFromKeePassDatabase(fileOpenedEventArgs.Database); } await KeePassDatabase.SyncDatabase(databasePath, KeePassDatabase.UpdateStatus, false, config); // If the OneDrive Refresh Token is stored in the KeePass database, we must trigger a save of the database here so to ensure that the actual value gets saved into the KDBX if (config.RefreshTokenStorage == OneDriveRefreshTokenStorage.KeePassDatabase) { fileOpenedEventArgs.Database.Save(null); } } /// /// Triggered when a KeePass database has been closed /// private static void OnKeePassDatabaseClosed(object sender, FileClosedEventArgs fileClosedEventArgs) { // Remove the KeePass database instance from the already available configuration var config = Configuration.GetPasswordDatabaseConfiguration(fileClosedEventArgs.IOConnectionInfo.Path); config.KeePassDatabase = null; } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("KoenZomers.KeePass.OneDriveSync")] [assembly: AssemblyDescription("Allows automatic synchronization of one or more KeePass databases to one or more OneDrives")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Koen Zomers")] [assembly: AssemblyProduct("KeePass Plugin")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("9cf31805-d88f-44da-89fc-0643eaa70bfc")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.1.2.3")] ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Providers/MicrosoftGraph/MicrosoftGraphDeviceCodeAuthCheckFailedResponse.cs ================================================ using Newtonsoft.Json; using System; namespace KoenZomersKeePassOneDriveSync.Providers.MicrosoftGraph { /// /// Response message from Microsoft Graph when failing verification on if the device session has authenticated /// public class MicrosoftGraphDeviceCodeAuthCheckFailedResponse { /// /// Type of error /// [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] public string Error { get; set; } /// /// Detailed description of what went wrong /// [JsonProperty("error_description", NullValueHandling = NullValueHandling.Ignore)] public string ErrorDescription { get; set; } /// /// Code(s) identifying the type of error /// [JsonProperty("error_codes", NullValueHandling = NullValueHandling.Ignore)] public long[] ErrorCodes { get; set; } /// /// Date and time when the error occurred /// [JsonProperty("timestamp", NullValueHandling = NullValueHandling.Ignore)] public DateTimeOffset? Timestamp { get; set; } /// /// Trace ID of the error /// [JsonProperty("trace_id", NullValueHandling = NullValueHandling.Ignore)] public Guid? TraceId { get; set; } /// /// Correlation ID of the error /// [JsonProperty("correlation_id", NullValueHandling = NullValueHandling.Ignore)] public Guid? CorrelationId { get; set; } /// /// Url with more information on the error /// [JsonProperty("error_uri", NullValueHandling = NullValueHandling.Ignore)] public Uri ErrorUri { get; set; } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Providers/MicrosoftGraph/MicrosoftGraphDeviceCodeAuthCheckSucceededResponse.cs ================================================ using Newtonsoft.Json; namespace KoenZomersKeePassOneDriveSync.Providers.MicrosoftGraph { /// /// Response message from Microsoft Graph when succeeding verification on if the device session has authenticated /// public class MicrosoftGraphDeviceCodeAuthCheckSucceededResponse { /// /// Type of token received. Must be Bearer. /// [JsonProperty("token_type", NullValueHandling = NullValueHandling.Ignore)] public string TokenType { get; set; } /// /// Scopes that were granted /// [JsonProperty("scope", NullValueHandling = NullValueHandling.Ignore)] public string Scope { get; set; } /// /// Time in seconds when this token will expire /// [JsonProperty("expires_in", NullValueHandling = NullValueHandling.Ignore)] public long? ExpiresIn { get; set; } /// /// Time in seconds when this token will expire /// [JsonProperty("ext_expires_in", NullValueHandling = NullValueHandling.Ignore)] public long? ExtExpiresIn { get; set; } /// /// The access token /// [JsonProperty("access_token", NullValueHandling = NullValueHandling.Ignore)] public string AccessToken { get; set; } /// /// The refresh token /// [JsonProperty("refresh_token", NullValueHandling = NullValueHandling.Ignore)] public string RefreshToken { get; set; } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Providers/MicrosoftGraph/MicrosoftGraphDeviceCodeResponse.cs ================================================ using Newtonsoft.Json; using System; namespace KoenZomersKeePassOneDriveSync.Providers.MicrosoftGraph { /// /// Response message from Microsoft Graph after requesting a device login /// public class MicrosoftGraphDeviceCodeResponse { /// /// A short string shown to the user that's used to identify the session on a secondary device /// [JsonProperty("user_code", NullValueHandling = NullValueHandling.Ignore)] public string UserCode { get; set; } /// /// A long string used to verify the session between the client and the authorization server. The client uses this parameter to request the access token from the authorization server. /// [JsonProperty("device_code", NullValueHandling = NullValueHandling.Ignore)] public string DeviceCode { get; set; } /// /// The URI the user should go to with the user_code in order to sign in /// [JsonProperty("verification_uri", NullValueHandling = NullValueHandling.Ignore)] public Uri VerificationUri { get; set; } /// /// The number of seconds before the device_code and user_code expire /// [JsonProperty("expires_in", NullValueHandling = NullValueHandling.Ignore)] public int? ExpiresIn { get; set; } /// /// The number of seconds the client should wait between polling requests /// [JsonProperty("interval", NullValueHandling = NullValueHandling.Ignore)] public int? Interval { get; set; } /// /// A human-readable string with instructions for the user. This can be localized by including a query parameter in the request of the form ?mkt=xx-XX, filling in the appropriate language culture code. /// [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] public string Message { get; set; } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Providers/OneDriveProvider.cs ================================================ using KoenZomers.KeePass.OneDriveSync; using KoenZomers.KeePass.OneDriveSync.Enums; using KoenZomers.OneDrive.Api; using KoenZomers.OneDrive.Api.Entities; using System; using System.Threading.Tasks; using System.Windows.Forms; namespace KoenZomersKeePassOneDriveSync.Providers { internal static class OneDriveProvider { /// /// Uses a Microsoft OneDrive Cloud Storage Provider (OneDrive Consumer, OneDrive for Business, Microsoft Graph) to sync the KeePass database /// /// Configuration of the database to sync /// Path to where the KeePass database to sync resides /// Flag to indicate if the sync should always take place /// Action to write status messages to to display in the UI /// True if successful, false if failed public static async Task SyncUsingOneDriveCloudProvider(Configuration databaseConfig, string localKeePassDatabasePath, bool forceSync, Action updateStatus) { // Connect to OneDrive OneDriveApi oneDriveApi = null; bool retryGettingApiInstance; do { retryGettingApiInstance = false; try { oneDriveApi = await Utilities.GetOneDriveApi(databaseConfig); } catch (KoenZomers.OneDrive.Api.Exceptions.TokenRetrievalFailedException) { // Failed to get a OneDrive API instance because retrieving an oAuth token failed. This could be because the oAuth token expired or has been removed. Show the login dialog again so we can get a new token. databaseConfig.RefreshToken = null; retryGettingApiInstance = true; } catch (Exception e) { // Build the error text to show to the end user var errorMessage = new System.Text.StringBuilder(); errorMessage.Append("Failed to connect to "); switch (databaseConfig.CloudStorageType.GetValueOrDefault(CloudStorageType.OneDriveConsumer)) { case CloudStorageType.OneDriveConsumer: errorMessage.Append("OneDrive"); break; case CloudStorageType.MicrosoftGraph: case CloudStorageType.MicrosoftGraphDeviceLogin: errorMessage.Append("Microsoft Graph"); break; default: errorMessage.Append("cloud storage provider"); break; } errorMessage.AppendLine(string.Format(" for database {0}:", databaseConfig.KeePassDatabase.Name)); errorMessage.AppendLine(); // Revert to the most inner exception to get the best details on what went wrong while (e.InnerException != null) e = e.InnerException; errorMessage.AppendLine(e.Message); // Verify if we're offline if (e.Message.Contains("remote name could not be resolved")) { // Offline, don't display a modal dialog but use the status bar instead KeePassDatabase.UpdateStatus("Can't connect. Working offline."); return false; } MessageBox.Show(errorMessage.ToString(), "Connection failed", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); } } while (retryGettingApiInstance); if (oneDriveApi == null) { switch (databaseConfig.CloudStorageType.GetValueOrDefault(CloudStorageType.OneDriveConsumer)) { case CloudStorageType.OneDriveConsumer: updateStatus(string.Format("Failed to connect to OneDrive for database {0}", databaseConfig.KeePassDatabase.Name)); break; case CloudStorageType.MicrosoftGraph: case CloudStorageType.MicrosoftGraphDeviceLogin: updateStatus(string.Format("Failed to connect to Microsoft Graph for database {0}", databaseConfig.KeePassDatabase.Name)); break; default: updateStatus(string.Format("Failed to connect to cloud service for database {0}", databaseConfig.KeePassDatabase.Name)); break; } return false; } // Save the RefreshToken in the configuration so we can use it again next time databaseConfig.RefreshToken = oneDriveApi.AccessToken.RefreshToken; // Verify if we already have retrieved the name of this OneDrive if (string.IsNullOrEmpty(databaseConfig.OneDriveName)) { // Fetch details about this OneDrive account var oneDriveAccount = await oneDriveApi.GetDrive(); databaseConfig.OneDriveName = oneDriveAccount.Owner.User.DisplayName; } Configuration.Save(); // Check if we have a location on OneDrive to sync with if (string.IsNullOrEmpty(databaseConfig.RemoteDatabasePath) && string.IsNullOrEmpty(databaseConfig.RemoteFolderId) && string.IsNullOrEmpty(databaseConfig.RemoteFileName)) { // Ask the user where to store the database on OneDrive var oneDriveFilePickerDialog = new Forms.OneDriveFilePickerDialog(oneDriveApi) { ExplanationText = "Select where you want to store the KeePass database. Right click for additional options.", AllowEnteringNewFileName = true, FileName = new System.IO.FileInfo(localKeePassDatabasePath).Name }; await oneDriveFilePickerDialog.LoadFolderItems(); var result = oneDriveFilePickerDialog.ShowDialog(); if (result != DialogResult.OK || string.IsNullOrEmpty(oneDriveFilePickerDialog.FileName)) { return false; } databaseConfig.RemoteDatabasePath = (oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference.Path : "") + "/" + oneDriveFilePickerDialog.CurrentOneDriveItem.Name + "/" + oneDriveFilePickerDialog.FileName; databaseConfig.RemoteDriveId = oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.ParentReference.DriveId : oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference.DriveId != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference.DriveId : null; if (oneDriveFilePickerDialog.CurrentOneDriveItem.File != null || (oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem != null && oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.File != null)) { databaseConfig.RemoteItemId = oneDriveFilePickerDialog.CurrentOneDriveItem.File != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.Id : oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.Id; } else { databaseConfig.RemoteFolderId = oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.ParentReference != null ? string.IsNullOrEmpty(oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.ParentReference.Id) || oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.Folder != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.Id : oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.ParentReference.Id : oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.Id : oneDriveFilePickerDialog.CurrentOneDriveItem.Id; } databaseConfig.RemoteFileName = oneDriveFilePickerDialog.FileName; Configuration.Save(); } // Retrieve the metadata of the KeePass database on OneDrive OneDriveItem oneDriveItem; if (string.IsNullOrEmpty(databaseConfig.RemoteItemId)) { // We don't have the ID of the KeePass file, check if the database is stored on the current user its drive or on a shared drive OneDriveItem folder; if (string.IsNullOrEmpty(databaseConfig.RemoteDriveId)) { // KeePass database is on the current user its drive if (string.IsNullOrEmpty(databaseConfig.RemoteFolderId)) { // No direct reference to the folder on the drive available, try locating it by its remote path oneDriveItem = await oneDriveApi.GetItem(databaseConfig.RemoteDatabasePath); } else { // Get the folder in which the KeePass file resides folder = await oneDriveApi.GetItemById(databaseConfig.RemoteFolderId); if (folder == null) { updateStatus(string.Format("Unable to download database {0} from OneDrive. Remote path cannot be found.", databaseConfig.KeePassDatabase.Name)); return false; } // Locate the KeePass file in the folder oneDriveItem = await oneDriveApi.GetItemInFolder(folder, databaseConfig.RemoteFileName); } } else { // KeePass database is on a shared drive or has not been uploaded yet folder = await oneDriveApi.GetItemFromDriveById(databaseConfig.RemoteFolderId, databaseConfig.RemoteDriveId); // Locate the KeePass file in the folder. Will return NULL if the file has not been uploaded yet. oneDriveItem = await oneDriveApi.GetItemInFolder(folder, databaseConfig.RemoteFileName); } // Check if the KeePass file has been found if (oneDriveItem != null) { // Store the direct Id to the KeePass file so we can save several API calls on future syncs databaseConfig.RemoteItemId = oneDriveItem.Id; Configuration.Save(); } } else { // We have the ID of the KeePass file, check if it's on the current user its drive or on a shared drive if (string.IsNullOrEmpty(databaseConfig.RemoteDriveId)) { // KeePass database is on the current user its drive oneDriveItem = await oneDriveApi.GetItemById(databaseConfig.RemoteItemId); } else { // KeePass database is on a shared drive oneDriveItem = await oneDriveApi.GetItemFromDriveById(databaseConfig.RemoteItemId, databaseConfig.RemoteDriveId); } } if (oneDriveItem == null) { // KeePass database not found on OneDrive updateStatus(string.Format("Database {0} does not exist yet on OneDrive, uploading it now", databaseConfig.KeePassDatabase.Name)); OneDriveItem oneDriveFolder; string fileName; if (string.IsNullOrEmpty(databaseConfig.RemoteFolderId)) { oneDriveFolder = databaseConfig.RemoteDatabasePath.Contains("/") ? await oneDriveApi.GetFolderOrCreate(databaseConfig.RemoteDatabasePath.Remove(databaseConfig.RemoteDatabasePath.LastIndexOf("/", StringComparison.Ordinal))) : await oneDriveApi.GetDriveRoot(); fileName = databaseConfig.RemoteDatabasePath.Contains("/") ? databaseConfig.RemoteDatabasePath.Remove(0, databaseConfig.RemoteDatabasePath.LastIndexOf("/", StringComparison.Ordinal) + 1) : databaseConfig.RemoteDatabasePath; } else { oneDriveFolder = databaseConfig.RemoteDriveId == null ? await oneDriveApi.GetItemById(databaseConfig.RemoteFolderId) : await oneDriveApi.GetItemFromDriveById(databaseConfig.RemoteFolderId, databaseConfig.RemoteDriveId); fileName = databaseConfig.RemoteFileName; } if (oneDriveFolder == null) { updateStatus(string.Format("Unable to upload database {0} to OneDrive. Remote path is invalid.", databaseConfig.KeePassDatabase.Name)); return false; } // Upload the database to OneDrive var newUploadResult = await oneDriveApi.UploadFileAs(localKeePassDatabasePath, fileName, oneDriveFolder); updateStatus(string.Format(newUploadResult == null ? "Failed to upload the KeePass database {0}" : "Successfully uploaded the new KeePass database {0} to OneDrive", databaseConfig.KeePassDatabase.Name)); databaseConfig.LocalFileHash = Utilities.GetDatabaseFileHash(localKeePassDatabasePath); if (newUploadResult != null) { databaseConfig.RemoteItemId = newUploadResult.Id; databaseConfig.LastCheckedAt = DateTime.Now; databaseConfig.LastSyncedAt = DateTime.Now; databaseConfig.ETag = newUploadResult.ETag; } Configuration.Save(); return false; } // Use the ETag from the OneDrive item to compare it against the local database config etag to see if the content has changed // Microsoft Graph API reports back a different ETag when uploading than the file actually gets assigned for some unknown reason. This would cause each sync attempt to sync again as the ETags differ. As a workaround we'll use the CTag which does seem reliable to detect a change to the file. if (!forceSync && oneDriveItem.CTag == databaseConfig.ETag) { updateStatus(string.Format("Database {0} is in sync", databaseConfig.KeePassDatabase.Name)); databaseConfig.LastCheckedAt = DateTime.Now; Configuration.Save(); return false; } // Download the database from OneDrive updateStatus(string.Format("Downloading KeePass database {0} from OneDrive", databaseConfig.KeePassDatabase.Name)); var temporaryKeePassDatabasePath = System.IO.Path.GetTempFileName(); var downloadSuccessful = await oneDriveApi.DownloadItemAndSaveAs(oneDriveItem, temporaryKeePassDatabasePath); if (!downloadSuccessful) { updateStatus(string.Format("Failed to download the KeePass database {0} from OneDrive", databaseConfig.KeePassDatabase.Name)); return false; } // Sync database updateStatus(string.Format("KeePass database {0} downloaded, going to sync", databaseConfig.KeePassDatabase.Name)); // Merge the downloaded database with the currently open KeePass database var syncSuccessful = KeePassDatabase.MergeDatabases(databaseConfig, temporaryKeePassDatabasePath); string localDatabaseToUpload; if (!syncSuccessful) { updateStatus(string.Format("Failed to synchronize the KeePass database {0}", databaseConfig.KeePassDatabase.Name)); var confirm = MessageBox.Show("Unable to merge the databases. Did you just change the master password for this KeePass database? If so and you would like to OVERWRITE the KeePass database stored on your OneDrive with your local database, select Yes, otherwise select No.", "Confirm overwriting your KeePass database", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2); if (confirm != DialogResult.Yes) return false; // Upload the local database localDatabaseToUpload = databaseConfig.KeePassDatabase.IOConnectionInfo.Path; updateStatus(string.Format("Uploading the local KeePass database {0} to OneDrive", databaseConfig.KeePassDatabase.Name)); } else { // Upload the synced database localDatabaseToUpload = temporaryKeePassDatabasePath; updateStatus(string.Format("Uploading the merged KeePass database {0} to OneDrive", databaseConfig.KeePassDatabase.Name)); } OneDriveItem uploadResult = null; // Due to some issues with the OneDrive API throwing random errors for no reason, retry a few times to upload the database before it will be considered failed for (var uploadAttempts = 1; uploadAttempts <= 5; uploadAttempts++) { try { if (!string.IsNullOrEmpty(databaseConfig.RemoteItemId)) { // Database is already present on OneDrive, update it uploadResult = await oneDriveApi.UpdateFile(localDatabaseToUpload, oneDriveItem); } else { // Database resides on the user its own OneDrive in the root folder if (oneDriveItem.ParentReference.Path.Equals("/drive/root:", StringComparison.CurrentCultureIgnoreCase)) { uploadResult = await oneDriveApi.UploadFileAs(localDatabaseToUpload, oneDriveItem.Name, await oneDriveApi.GetDriveRoot()); } else { if (string.IsNullOrEmpty(databaseConfig.RemoteDriveId)) { // Database resides on the user its own OneDrive in a folder uploadResult = await oneDriveApi.UploadFileAs(localDatabaseToUpload, oneDriveItem.Name, await oneDriveApi.GetItemById(oneDriveItem.ParentReference.Id)); } else { // Database resides on another OneDrive uploadResult = await oneDriveApi.UploadFileAs(localDatabaseToUpload, oneDriveItem.Name, await oneDriveApi.GetItemFromDriveById(oneDriveItem.ParentReference.Id, oneDriveItem.ParentReference.DriveId)); } } } // Uploading succeeded break; } catch (ArgumentNullException e) { // If any other exception than the one we will get if the OneDrive API throws the random error, then ensure we pass on the exception instead of swallowing it if (e.ParamName != "oneDriveUploadSession") { throw; } updateStatus(string.Format("Uploading the new KeePass database {1} to OneDrive (attempt {0})", uploadAttempts + 1, databaseConfig.KeePassDatabase.Name)); } } if (uploadResult == null) { updateStatus(string.Format("Failed to upload the KeePass database {0}", databaseConfig.KeePassDatabase.Name)); return false; } // Delete the temporary database used for merging System.IO.File.Delete(temporaryKeePassDatabasePath); // The ETag changes with every request of the item so we use the CTag instead which only changes when the file changes databaseConfig.ETag = uploadResult.CTag; return true; } /// /// Uses a Microsoft OneDrive Cloud Storage Provider (OneDrive Consumer, OneDrive for Business, Microsoft Graph) to download a KeePass database /// /// Configuration of the database to sync /// Action to write status messages to to display in the UI /// Path to the local KeePass database or NULL if the process has been aborted public static async Task OpenFromOneDriveCloudProvider(Configuration databaseConfig, Action updateStatus) { // Connect to OneDrive var oneDriveApi = await Utilities.GetOneDriveApi(databaseConfig); if (oneDriveApi == null) { switch (databaseConfig.CloudStorageType.Value) { case CloudStorageType.OneDriveConsumer: updateStatus("Failed to connect to OneDrive"); break; case CloudStorageType.MicrosoftGraph: case CloudStorageType.MicrosoftGraphDeviceLogin: updateStatus("Failed to connect to Microsoft Graph"); break; default: updateStatus("Failed to connect to cloud service"); break; } return null; } // Save the RefreshToken in the configuration so we can use it again next time databaseConfig.RefreshToken = oneDriveApi.AccessToken.RefreshToken; // Fetch details about this OneDrive account var oneDriveAccount = await oneDriveApi.GetDrive(); databaseConfig.OneDriveName = oneDriveAccount.Owner.User.DisplayName; // Ask the user to select the database to open on OneDrive var oneDriveFilePickerDialog = new Forms.OneDriveFilePickerDialog(oneDriveApi) { ExplanationText = "Select the KeePass database to open. Right click for additional options.", AllowEnteringNewFileName = false }; await oneDriveFilePickerDialog.LoadFolderItems(); var result = oneDriveFilePickerDialog.ShowDialog(); if (result != DialogResult.OK || string.IsNullOrEmpty(oneDriveFilePickerDialog.FileName)) { KeePassDatabase.UpdateStatus("Open KeePass database from OneDrive aborted"); return null; } databaseConfig.RemoteDatabasePath = (oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference.Path : "") + "/" + oneDriveFilePickerDialog.CurrentOneDriveItem.Name + "/" + oneDriveFilePickerDialog.FileName; databaseConfig.RemoteDriveId = oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.ParentReference.DriveId : oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference.DriveId != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.ParentReference.DriveId : null; if (oneDriveFilePickerDialog.CurrentOneDriveItem.File != null || (oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem != null && oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.File != null)) { databaseConfig.RemoteItemId = oneDriveFilePickerDialog.CurrentOneDriveItem.File != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.Id : oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.Id; } else { databaseConfig.RemoteFolderId = oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem != null ? oneDriveFilePickerDialog.CurrentOneDriveItem.RemoteItem.Id : oneDriveFilePickerDialog.CurrentOneDriveItem.Id; } databaseConfig.RemoteFileName = oneDriveFilePickerDialog.FileName; // Retrieve the metadata of the KeePass database on OneDrive OneDriveItem oneDriveItem; if (string.IsNullOrEmpty(databaseConfig.RemoteItemId)) { // We don't have the ID of the KeePass file, check if the database is stored on the current user its drive or on a shared drive OneDriveItem folder; if (string.IsNullOrEmpty(databaseConfig.RemoteDriveId)) { // KeePass database is on the current user its drive folder = await oneDriveApi.GetItemById(databaseConfig.RemoteFolderId); } else { // KeePass database is on a shared drive folder = await oneDriveApi.GetItemFromDriveById(databaseConfig.RemoteFolderId, databaseConfig.RemoteDriveId); } // Locate the KeePass file in the folder oneDriveItem = await oneDriveApi.GetItemInFolder(folder, databaseConfig.RemoteFileName); // Check if the KeePass file has been found if (oneDriveItem != null) { // Store the direct Id to the KeePass file so we can save several API calls on future syncs databaseConfig.RemoteItemId = oneDriveItem.Id; Configuration.Save(); } } else { // We have the ID of the KeePass file, check if it's on the current user its drive or on a shared drive if (string.IsNullOrEmpty(databaseConfig.RemoteDriveId)) { // KeePass database is on the current user its drive oneDriveItem = await oneDriveApi.GetItemById(databaseConfig.RemoteItemId); } else { // KeePass database is on a shared drive oneDriveItem = await oneDriveApi.GetItemFromDriveById(databaseConfig.RemoteItemId, databaseConfig.RemoteDriveId); } } if (oneDriveItem == null) { // KeePass database not found on OneDrive switch (databaseConfig.CloudStorageType.Value) { case CloudStorageType.OneDriveConsumer: updateStatus(string.Format("Unable to find the database {0} on your OneDrive", databaseConfig.KeePassDatabase.Name)); break; case CloudStorageType.MicrosoftGraph: updateStatus(string.Format("Unable to find the database {0} through Microsoft Graph", databaseConfig.KeePassDatabase.Name)); break; default: updateStatus("Failed to connect to cloud service"); break; } return null; } // Show the save as dialog to select a location locally where to store the KeePass database var saveFiledialog = new SaveFileDialog { Filter = "KeePass databases (*.kdbx)|*.kdbx|All Files (*.*)|*.*", Title = "Select where to store the KeePass database locally", CheckFileExists = false, FileName = oneDriveItem.Name }; var saveFileDialogResult = saveFiledialog.ShowDialog(); if (saveFileDialogResult != DialogResult.OK || string.IsNullOrEmpty(saveFiledialog.FileName)) { updateStatus("Open KeePass database from OneDrive aborted"); return null; } // Download the KeePass database to the selected location updateStatus("Downloading KeePass database"); await oneDriveApi.DownloadItemAndSaveAs(oneDriveItem, saveFiledialog.FileName); // The ETag changes with every request of the item so we use the CTag instead which only changes when the file changes databaseConfig.ETag = oneDriveItem.CTag; return saveFiledialog.FileName; } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Providers/SharePointProvider.cs ================================================ using KoenZomers.KeePass.OneDriveSync; using Newtonsoft.Json.Linq; using System; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; namespace KoenZomersKeePassOneDriveSync.Providers { internal static class SharePointProvider { /// /// Uses the Microsoft SharePoint platform (SharePoint 2013, 2016 or Online) to sync the KeePass database /// /// Configuration of the database to sync /// Path to where the KeePass database to sync resides /// Flag to indicate if the sync should always take place /// Action to write status messages to to display in the UI /// True if successful, false if failed public static async Task SyncUsingSharePointPlatform(Configuration databaseConfig, string localKeePassDatabasePath, bool forceSync, Action updateStatus) { if(! await EnsureSharePointCredentials(databaseConfig)) { return false; } using (var httpClient = CreateSharePointHttpClient(databaseConfig)) { // Check if we have a Document Library on SharePoint to sync with if (string.IsNullOrEmpty(databaseConfig.RemoteFolderId) && string.IsNullOrEmpty(databaseConfig.RemoteFileName)) { // Ask the user where to store the database on SharePoint var sharePointDocumentLibraryPickerDialog = new Forms.SharePointDocumentLibraryPickerDialog(httpClient); await sharePointDocumentLibraryPickerDialog.LoadDocumentLibraryItems(); sharePointDocumentLibraryPickerDialog.FileName = !string.IsNullOrEmpty(databaseConfig.RemoteFileName) ? databaseConfig.RemoteFileName : new System.IO.FileInfo(localKeePassDatabasePath).Name; var result = sharePointDocumentLibraryPickerDialog.ShowDialog(); if (result != DialogResult.OK || string.IsNullOrEmpty(sharePointDocumentLibraryPickerDialog.SelectedDocumentLibraryServerRelativeUrl)) { return false; } databaseConfig.RemoteFolderId = sharePointDocumentLibraryPickerDialog.SelectedDocumentLibraryServerRelativeUrl; databaseConfig.RemoteFileName = sharePointDocumentLibraryPickerDialog.FileName; Configuration.Save(); } // Ensure we have the SharePoint site name if(string.IsNullOrEmpty(databaseConfig.OneDriveName)) { // We don't have the SharePoint site name yet, retrieve it now by triggering TestConnection await TestConnection(httpClient, databaseConfig); Configuration.Save(); } // Retrieve the KeePass database from SharePoint var serverRelativeSharePointUrl = string.Concat(databaseConfig.RemoteFolderId, "/", databaseConfig.RemoteFileName); var eTag = await GetEtagOfFile(httpClient, serverRelativeSharePointUrl); if (eTag == null) { // KeePass database not found on OneDrive updateStatus(string.Format("Database {0} does not exist yet on SharePoint, uploading it now", databaseConfig.KeePassDatabase.Name)); // Upload the database to SharePoint eTag = await UploadFile(databaseConfig.KeePassDatabase.IOConnectionInfo.Path, databaseConfig.RemoteFolderId, databaseConfig.RemoteFileName, httpClient, true); updateStatus(string.Format(eTag == null ? "Failed to upload the KeePass database {0}" : "Successfully uploaded the new KeePass database {0} to SharePoint", databaseConfig.KeePassDatabase.Name)); databaseConfig.LocalFileHash = Utilities.GetDatabaseFileHash(localKeePassDatabasePath); if (eTag != null) { databaseConfig.LastCheckedAt = DateTime.Now; databaseConfig.LastSyncedAt = DateTime.Now; databaseConfig.ETag = eTag; } Configuration.Save(); return false; } // Use the ETag from the SharePoint item to compare it against the local database config etag to see if the content has changed if (!forceSync && eTag == databaseConfig.ETag) { updateStatus(string.Format("KeePass database {0} is in sync", databaseConfig.KeePassDatabase.Name)); databaseConfig.LastCheckedAt = DateTime.Now; Configuration.Save(); return false; } // Download the database from SharePoint updateStatus(string.Format("Downloading KeePass database {0} from SharePoint", databaseConfig.KeePassDatabase.Name)); var temporaryKeePassDatabasePath = System.IO.Path.GetTempFileName(); var downloadSuccessful = DownloadFile(temporaryKeePassDatabasePath, serverRelativeSharePointUrl, httpClient); if (! await downloadSuccessful) { updateStatus(string.Format("Failed to download the KeePass database {0} from SharePoint", databaseConfig.KeePassDatabase.Name)); return false; } // Sync database updateStatus(string.Format("KeePass database {0} downloaded, going to sync", databaseConfig.KeePassDatabase.Name)); // Merge the downloaded database with the currently open KeePass database var syncSuccessful = KeePassDatabase.MergeDatabases(databaseConfig, temporaryKeePassDatabasePath); string localDatabaseToUpload; if (!syncSuccessful) { updateStatus(string.Format("Failed to synchronize the KeePass database {0}", databaseConfig.KeePassDatabase.Name)); var confirm = MessageBox.Show(string.Format("Unable to merge the KeePass database {0}. Did you just change the master password for this KeePass database? If so and you would like to OVERWRITE the KeePass database stored on your SharePoint site with your local database, select Yes, otherwise select No.", databaseConfig.KeePassDatabase.Name), "Confirm overwriting your KeePass database", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2); if (confirm != DialogResult.Yes) return false; // Upload the local database updateStatus(string.Format("Uploading the local KeePass database {0} to SharePoint", databaseConfig.KeePassDatabase.Name)); localDatabaseToUpload = databaseConfig.KeePassDatabase.IOConnectionInfo.Path; } else { // Upload the synced database updateStatus(string.Format("Uploading the merged KeePass database {0} to SharePoint", databaseConfig.KeePassDatabase.Name)); localDatabaseToUpload = temporaryKeePassDatabasePath; } var uploadResult = await UploadFile(localDatabaseToUpload, databaseConfig.RemoteFolderId, databaseConfig.RemoteFileName, httpClient, false); // Delete the temporary database used for merging System.IO.File.Delete(temporaryKeePassDatabasePath); databaseConfig.ETag = uploadResult; return true; } } /// /// Tries to retrieve the ETag of the file at the provided server relative URL from SharePoint /// /// HttpClientt to use for the SharePoint communication /// Server relative URL of the file to query for /// ETag of the file or NULL if unable to find the file public static async Task GetEtagOfFile(HttpClient httpClient, string serverRelativeUrl) { // Retrieve the ETag of the file using (var response = await httpClient.GetAsync("web/GetFileByServerRelativeUrl('" + serverRelativeUrl + "')?$select=ETag")) { // Check if the attempt was successful if(!response.IsSuccessStatusCode) { // Attempt failed return null; } // Attempt was successful, parse the JSON response var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); // Validate if ETag node exists in the result JToken value; if (responseJson.TryGetValue("ETag", out value)) { // ETag node exists, return it return value.Value(); } } return null; } /// /// Uploads a file to SharePoint /// /// Full path to where the file to upload resides locally /// Server relative URL where the file should be uploaded. Should not include the filename. /// Filename under which to store the file in SharePoint /// HttpClientt to use for the SharePoint communication /// ETag of the uploaded file if successful, NULL if it failed public static async Task UploadFile(string localDatabasePath, string serverRelativeUrl, string fileName, HttpClient httpClient, bool asNewFile) { try { // Get a FormDigest to send to SharePoint var formDigest = await GetFormDigest(httpClient); // Validate that the FormDigest was retrieved successfully if(string.IsNullOrEmpty(formDigest)) { // No FormDigest available return null; } if(!asNewFile) { // Send a check out request for the file, in case require checkout is enabled on the SharePoint Document Library using (var checkOutRequest = new HttpRequestMessage(HttpMethod.Post, "web/GetFileByServerRelativeUrl('" + serverRelativeUrl + "/" + fileName + "')/CheckOut()")) { checkOutRequest.Headers.Add("X-RequestDigest", formDigest); await httpClient.SendAsync(checkOutRequest); } } // Construct a new HTTP message using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, asNewFile ? "web/GetFolderByServerRelativeUrl('" + serverRelativeUrl + "')/Files/Add(url='" + fileName + "',overwrite=true)?$select=ETag" : "web/GetFileByServerRelativeUrl('" + serverRelativeUrl + "/" + fileName + "')/$value")) { // Add the FormDiges to the request header httpRequest.Headers.Add("X-RequestDigest", formDigest); if (!asNewFile) { // Add the header to indicate an update to an existing file should be done httpRequest.Headers.Add("X-HTTP-Method", "PUT"); } // Open the local file to upload using (var fileContent = new StreamContent(System.IO.File.OpenRead(localDatabasePath))) { // Set the BODY content to the file byes httpRequest.Content = fileContent; // Send the bytes of the local file to the upload location on SharePoint var response = await httpClient.SendAsync(httpRequest); // Verify if the file was uploaded successfully if (!response.IsSuccessStatusCode) { // Upload failed return null; } // Send a check in request of the file, in case require checkout is enabled on the SharePoint Document Library using (var checkInRequest = new HttpRequestMessage(HttpMethod.Post, "web/GetFileByServerRelativeUrl('" + serverRelativeUrl + "/" + fileName + "')/CheckIn(comment='" + (asNewFile ? "Added" : "Updated") + " by " + httpClient.DefaultRequestHeaders.UserAgent + "',checkintype=0)")) { checkInRequest.Headers.Add("X-RequestDigest", formDigest); await httpClient.SendAsync(checkInRequest); } if (asNewFile) { // Parse the result of the upload new file request to get the ETag var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); // Validate if a ETag node exists in the result JToken value; if (responseJson.TryGetValue("ETag", out value)) { // ETag node exists, return it return value.Value(); } } else { // Updating a file does not support retrieval of the new ETag, so send out another request to get it var eTag = await GetEtagOfFile(httpClient, serverRelativeUrl + "/" + fileName); return eTag; } } } return null; } catch(Exception) { return null; } } /// /// Downloads a file from SharePoint /// /// Full path to where to download the file to /// Server relative URL where the file should be downloaded from. Should include the filename. /// HttpClient to use for the SharePoint communication /// File instance representing the uploaded file if successful, NULL if it failed public static async Task DownloadFile(string localDatabasePath, string serverRelativeUrl, HttpClient httpClient) { try { // Request the file contents using (var response = await httpClient.GetStreamAsync("web/GetFileByServerRelativeUrl('" + serverRelativeUrl + "')/$value")) { // Open the local file using (var fileStream = System.IO.File.Create(localDatabasePath)) { // Copy the downloaded bytes to the local file await response.CopyToAsync(fileStream); } } return true; } catch (Exception) { return false; } } /// /// Deletes a file at the provided location in SharePoint /// /// Server relative URL to the file to delete /// HttpClient to use for the SharePoint communication /// True if successful, false if failed public static async Task DeleteFile(string serverRelativeFilePath, HttpClient httpClient) { try { // Get a FormDigest to send to SharePoint var formDigest = await GetFormDigest(httpClient); // Validate that the FormDigest was retrieved successfully if (string.IsNullOrEmpty(formDigest)) { // No FormDigest available return false; } // Construct a new HTTP message using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, "web/GetFileByServerRelativeUrl('" + serverRelativeFilePath + "')")) { // Add the FormDiges to the request header httpRequest.Headers.Add("X-RequestDigest", formDigest); // Disable concurrency control httpRequest.Headers.Add("If-Match", "*"); // Instruct to perform a DELETE operation httpRequest.Headers.Add("X-HTTP-Method", "DELETE"); // Send the request to SharePoint var response = await httpClient.SendAsync(httpRequest); // Verify if the request was processed successfully if (!response.IsSuccessStatusCode) { // Request failed return false; } // Request was successful return true; } } catch (Exception) { return false; } } /// /// Deletes a folder at the provided location in SharePoint /// /// Server relative URL to the folder to delete /// HttpClient to use for the SharePoint communication /// True if successful, false if failed public static async Task DeleteFolder(string serverRelativeFolderPath, HttpClient httpClient) { try { // Get a FormDigest to send to SharePoint var formDigest = await GetFormDigest(httpClient); // Validate that the FormDigest was retrieved successfully if (string.IsNullOrEmpty(formDigest)) { // No FormDigest available return false; } // Construct a new HTTP message using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, "web/GetFolderByServerRelativeUrl('" + serverRelativeFolderPath + "')")) { // Add the FormDiges to the request header httpRequest.Headers.Add("X-RequestDigest", formDigest); // Disable concurrency control httpRequest.Headers.Add("If-Match", "*"); // Instruct to perform a DELETE operation httpRequest.Headers.Add("X-HTTP-Method", "DELETE"); // Send the request to SharePoint var response = await httpClient.SendAsync(httpRequest); // Verify if the request was processed successfully if (!response.IsSuccessStatusCode) { // Request failed return false; } // Request was successful return true; } } catch (Exception) { return false; } } /// /// Renames a folder at the provided location in SharePoint /// /// The new name to assign to the folder /// Server relative URL to the folder to rename /// HttpClient to use for the SharePoint communication /// True if successful, false if failed public static async Task RenameFolder(string newFolderName, string serverRelativeFolderPath, HttpClient httpClient) { try { // Get a FormDigest to send to SharePoint var formDigest = await GetFormDigest(httpClient); // Validate that the FormDigest was retrieved successfully if (string.IsNullOrEmpty(formDigest)) { // No FormDigest available return false; } // Define the server relative url of the parent folder in which the folder resides var parentPath = serverRelativeFolderPath.Remove(serverRelativeFolderPath.LastIndexOf('/')); // Construct a new HTTP message using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, "web/GetFolderByServerRelativeUrl('" + serverRelativeFolderPath + "')/moveto(newurl='" + parentPath + "/" + newFolderName + "')")) { // Add the FormDiges to the request header httpRequest.Headers.Add("X-RequestDigest", formDigest); // Disable concurrency control httpRequest.Headers.Add("If-Match", "*"); // Instruct to perform a MERGE operation httpRequest.Headers.Add("X-HTTP-Method", "MERGE"); // Provide the POST body content //httpRequest.Content = new StringContent("{ '__metadata': { 'type': 'SP.Folder' }, 'Name': '" + newFolderName + "' }", System.Text.Encoding.UTF8, "application/json"); // Send the request to SharePoint var response = await httpClient.SendAsync(httpRequest); // Verify if the request was processed successfully if (!response.IsSuccessStatusCode) { // Request failed return false; } // Request was successful return true; } } catch (Exception) { return false; } } /// /// Renames a file at the provided location in SharePoint /// /// The new name to assign to the file /// Server relative URL to the file to rename /// HttpClient to use for the SharePoint communication /// True if successful, false if failed public static async Task RenameFile(string newFileName, string serverRelativeFilePath, HttpClient httpClient) { try { // Get a FormDigest to send to SharePoint var formDigest = await GetFormDigest(httpClient); // Validate that the FormDigest was retrieved successfully if (string.IsNullOrEmpty(formDigest)) { // No FormDigest available return false; } // Define the server relative url of the folder in which the file resides var parentPath = serverRelativeFilePath.Remove(serverRelativeFilePath.LastIndexOf('/')); // Construct a new HTTP message using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, "web/GetFileByServerRelativeUrl('" + serverRelativeFilePath + "')/moveto(newurl='" + parentPath + "/" + newFileName + "',flags=0)")) { // Add the FormDiges to the request header httpRequest.Headers.Add("X-RequestDigest", formDigest); // Disable concurrency control httpRequest.Headers.Add("If-Match", "*"); // Instruct to perform a MERGE operation httpRequest.Headers.Add("X-HTTP-Method", "MERGE"); // Send the request to SharePoint var response = await httpClient.SendAsync(httpRequest); // Verify if the request was processed successfully if (!response.IsSuccessStatusCode) { // Request failed return false; } // Request was successful return true; } } catch (Exception) { return false; } } /// /// Creates a new folder at the provided location in SharePoint /// /// Name of the new folder to create /// Server relative URL to the location where to create the new folder /// HttpClient to use for the SharePoint communication /// Server relative URL of the new folder if successful or NULL if failed to create the new folder public static async Task CreateFolder(string folderName, string serverRelativeUrl, HttpClient httpClient) { try { // Get a FormDigest to send to SharePoint var formDigest = await GetFormDigest(httpClient); // Validate that the FormDigest was retrieved successfully if (string.IsNullOrEmpty(formDigest)) { // No FormDigest available return null; } // Construct a new HTTP message using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, "web/GetFolderByServerRelativeUrl('" + serverRelativeUrl + "')/Folders/Add('" + folderName + "')?$select=ServerRelativeUrl")) { // Add the FormDiges to the request header httpRequest.Headers.Add("X-RequestDigest", formDigest); // Send the request to SharePoint var response = await httpClient.SendAsync(httpRequest); // Verify if the request was processed successfully if (!response.IsSuccessStatusCode) { // Request failed return null; } // Request was successful. Parse the result of the request. var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); // Validate if a ServerRelativeUrl node exists in the result JToken value; if (responseJson.TryGetValue("ServerRelativeUrl", out value)) { // ServerRelativeUrl node exists, return it return value.Value(); } } } catch (Exception) { return null; } return null; } /// /// Requests a FormDigest from SharePoint which is needed when requesting changes to SharePoint /// /// HttpClient to use for the SharePoint communication /// FormDirect if successful or NULL if unable to retrieve the FormDigest public static async Task GetFormDigest(HttpClient httpClient) { try { // Request a RequestDigest to allow uploading to SharePoint using (var response = await httpClient.PostAsync("contextInfo", new StringContent("Hello"))) { // Verify if the RequestDigest was retrieved successfully if (!response.IsSuccessStatusCode) { // Request failed return null; } // Request was successful. Parse the result of the request. var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); // Validate if a FormDigestValue node exists in the result JToken value; if (responseJson.TryGetValue("FormDigestValue", out value)) { // FormDigestValue node exists, return it return value.Value(); } } } catch (Exception) { return null; } return null; } /// /// Creates a SharePoint ClientContext based on a Configuration file specific for a SharePoint synchronization /// /// Configuration set to be specific for a SharePoint synchronization /// SharePoint HttpClient or NULL if unable to establish one based on the provided configuration public static HttpClient CreateSharePointHttpClient(Configuration databaseConfig) { // Collect the SharePoint variables required to connect var sharePointUri = new Uri(databaseConfig.RemoteDatabasePath); var sharePointClientId = databaseConfig.RefreshToken.Remove(databaseConfig.RefreshToken.IndexOf(';')); var sharePointClientSecret = databaseConfig.RefreshToken.Remove(0, databaseConfig.RefreshToken.IndexOf(';') + 1); return CreateSharePointHttpClient(sharePointUri, sharePointClientId, sharePointClientSecret); } /// /// Creates a SharePoint ClientContext based on the provided SharePoint Uri, ClientId and ClientSecret /// /// Uri of the SharePoint site to connect to /// ClientId to use for the Low Trust to connect to SharePoint /// ClientSecret to use for the Low Trust to connect to SharePoint /// SharePoint HttpClient or NULL if unable to establish one based on the provided configuration public static HttpClient CreateSharePointHttpClient(Uri sharePointUri, string sharePointClientId, string sharePointClientSecret) { // Get the realm for the SharePoint site var realm = TokenHelper.GetRealmFromTargetUrl(sharePointUri); // Get the access token for the URL var accessToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, sharePointUri.Authority, realm, sharePointClientId, sharePointClientSecret).AccessToken; // Connect to SharePoint var httpClientHandler = new HttpClientHandler() { Proxy = Utilities.GetProxySettings(), PreAuthenticate = true, UseDefaultCredentials = false, Credentials = Utilities.GetProxyCredentials() }; // Set the base URI to use for all calls var httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(sharePointUri.OriginalString + "/_api/") }; // Configure the HTTP headers for each request httpClient.DefaultRequestHeaders.Accept.Clear(); httpClient.DefaultRequestHeaders.Add("Accept", "application/json; odata=nometadata"); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var assemblyVersion = Assembly.GetCallingAssembly().GetName().Version; httpClient.DefaultRequestHeaders.Add("User-Agent", string.Format("KoenZomers KeePass OneDriveSync v{0}.{1}.{2}.{3}", assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Build, assemblyVersion.Revision)); return httpClient; } /// /// Form to request the SharePoint details to connect to /// /// Configuration which needs to map to a SharePoint environment /// True if successful, false if failed to receive a SharePoint configuration private static bool RequestSharePointDetails(Configuration databaseConfig) { var sharePointCredentialsDialog = new Forms.SharePointCredentialsForm(); var result = sharePointCredentialsDialog.ShowDialog(); if (result != DialogResult.OK) { return false; } // Copy the entered data into the database configuration databaseConfig.RefreshToken = string.Format("{0};{1}", sharePointCredentialsDialog.SharePointClientId, sharePointCredentialsDialog.SharePointClientSecret); databaseConfig.RemoteDatabasePath = sharePointCredentialsDialog.SharePointUrl; Configuration.Save(); return true; } /// /// Test the connection with the provided HttpClient /// /// The HttpClient to use to test the connection /// If config is provided, the drive name will be updated with the actual title (optional) /// True if connection successful, False if the test failed public static async Task TestConnection(HttpClient httpClient, Configuration databaseConfig = null) { try { // Perform a simple get operation to test the access to SharePoint using (var response = await httpClient.GetAsync("web?$select=Title")) { // Verify if the request was successful if (!response.IsSuccessStatusCode) { // Request failed return false; } // Request was successful. Check if a database config was provided which we can update with the actual site title. if (databaseConfig == null) { // No database config was provided. No need to parse the result. return true; } // Database config was provided. Parse the result of the request. var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); // Validate if a Title node exists in the result JToken value; if (responseJson.TryGetValue("Title", out value)) { // Title node exists, update the database config with the site title databaseConfig.OneDriveName = value.Value(); } } return true; } catch (Exception) { return false; } } /// /// Ensures that the provided database config contains information to connect to SharePoint. If not, it will prompt for the end user to provide the details. /// /// Databaseconfig to check for the presence of SharePoint authentication information /// True if succeeded to get SharePoint authentication information, false if failed public static async Task EnsureSharePointCredentials(Configuration databaseConfig) { if (string.IsNullOrEmpty(databaseConfig.RefreshToken) || databaseConfig.RefreshToken.IndexOf(';') == -1 || string.IsNullOrEmpty(databaseConfig.RemoteDatabasePath)) { // Configuration does not have a SharePoint config in it yet, ask for connection details bool retryGettingApiInstance; do { retryGettingApiInstance = false; try { var requestSharePointDetailsSuccessful = RequestSharePointDetails(databaseConfig); if (!requestSharePointDetailsSuccessful) { return false; } using (var httpClient = CreateSharePointHttpClient(databaseConfig)) { if (! await TestConnection(httpClient, databaseConfig)) { MessageBox.Show("Connection failed. Please ensure you are able to connect to the SharePoint farm", "Connecting to SharePoint", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); retryGettingApiInstance = true; } } } catch (Exception e) { // Build the error text to show to the end user var errorMessage = new System.Text.StringBuilder(); errorMessage.Append("Failed to connect to SharePoint:"); errorMessage.AppendLine(); errorMessage.AppendLine(e.Message); // If there's an inner exception, show its message as well as it typically gives more detail why it went wrong if (e.InnerException != null) { errorMessage.AppendLine(e.InnerException.Message); // Verify if we're offline if (e.InnerException.Message.Contains("remote name could not be resolved")) { // Offline, don't display a modal dialog but use the status bar instead KeePassDatabase.UpdateStatus("Can't connect. Working offline."); return false; } } MessageBox.Show(errorMessage.ToString(), "Connection failed", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); retryGettingApiInstance = true; } } while (retryGettingApiInstance); } return true; } /// /// Download a KeePass database from SharePoint /// /// Configuration of the database to sync /// Action to write status messages to to display in the UI /// Path to the local KeePass database or NULL if the process has been aborted public static async Task OpenFromSharePoint(Configuration databaseConfig, Action updateStatus) { if(! await EnsureSharePointCredentials(databaseConfig)) { return null; } using (var httpClient = CreateSharePointHttpClient(databaseConfig)) { // Ask the user where to store the database on SharePoint var sharePointDocumentLibraryPickerDialog = new Forms.SharePointDocumentLibraryPickerDialog(httpClient) { ExplanationText = "Select the KeePass database to open. Right click for additional options.", AllowEnteringNewFileName = false }; await sharePointDocumentLibraryPickerDialog.LoadDocumentLibraryItems(); var result = sharePointDocumentLibraryPickerDialog.ShowDialog(); if (result != DialogResult.OK || string.IsNullOrEmpty(sharePointDocumentLibraryPickerDialog.SelectedDocumentLibraryServerRelativeUrl)) { updateStatus("Open KeePass database from SharePoint aborted"); return null; } databaseConfig.RemoteFolderId = sharePointDocumentLibraryPickerDialog.SelectedDocumentLibraryServerRelativeUrl; databaseConfig.RemoteFileName = sharePointDocumentLibraryPickerDialog.FileName; // Show the save as dialog to select a location locally where to store the KeePass database var saveFiledialog = new SaveFileDialog { Filter = "KeePass databases (*.kdbx)|*.kdbx|All Files (*.*)|*.*", Title = "Select where to store the KeePass database locally", CheckFileExists = false, FileName = sharePointDocumentLibraryPickerDialog.FileName }; var saveFileDialogResult = saveFiledialog.ShowDialog(); if (saveFileDialogResult != DialogResult.OK || string.IsNullOrEmpty(saveFiledialog.FileName)) { updateStatus("Open KeePass database from SharePoint aborted"); return null; } // Download the KeePass database to the selected location updateStatus("Downloading KeePass database"); // Retrieve the KeePass database from SharePoint var serverRelativeSharePointUrl = string.Concat(databaseConfig.RemoteFolderId, "/", databaseConfig.RemoteFileName); var downloadSuccessful = await DownloadFile(saveFiledialog.FileName, serverRelativeSharePointUrl, httpClient); // Get the ETag of the database so we can determine if it's in sync databaseConfig.ETag = await GetEtagOfFile(httpClient, serverRelativeSharePointUrl); return downloadSuccessful ? saveFiledialog.FileName : null; } } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace KoenZomersKeePassOneDriveSync { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("KoenZomersKeePassOneDriveSync.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap MicrosoftLogo { get { object obj = ResourceManager.GetObject("MicrosoftLogo", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap MSGraph { get { object obj = ResourceManager.GetObject("MSGraph", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap OneDrive { get { object obj = ResourceManager.GetObject("OneDrive", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap OneDriveBothClouds { get { object obj = ResourceManager.GetObject("OneDriveBothClouds", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap OneDriveConsumer { get { object obj = ResourceManager.GetObject("OneDriveConsumer", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap OneDriveForBusiness { get { object obj = ResourceManager.GetObject("OneDriveForBusiness", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap OneDriveIcon { get { object obj = ResourceManager.GetObject("OneDriveIcon", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap SharePoint { get { object obj = ResourceManager.GetObject("SharePoint", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Resources\mslogo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Resources\MSGraph.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Resources\OneDrive.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Resources\OneDriveBothClouds.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Images\OneDriveConsumer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Resources\OneDriveForBusiness.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Resources\OneDriveIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Resources\SharePoint.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ================================================ FILE: KoenZomers.KeePass.OneDriveSync/TokenHelper.cs ================================================ using Microsoft.IdentityModel; using Microsoft.IdentityModel.SecurityTokenService; using Microsoft.IdentityModel.S2S.Protocols.OAuth2; using Microsoft.IdentityModel.S2S.Tokens; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IdentityModel.Selectors; using System.IdentityModel.Tokens; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; using System.Text; using System.Web; using System.Web.Configuration; using System.Web.Script.Serialization; using AudienceRestriction = Microsoft.IdentityModel.Tokens.AudienceRestriction; using SecurityTokenHandlerConfiguration = Microsoft.IdentityModel.Tokens.SecurityTokenHandlerConfiguration; using X509SigningCredentials = Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials; using System.Net.Http; namespace KoenZomersKeePassOneDriveSync { public static class TokenHelper { #region public fields /// /// SharePoint principal. /// public const string SharePointPrincipal = "00000003-0000-0ff1-ce00-000000000000"; /// /// Lifetime of HighTrust access token, 12 hours. /// public static readonly TimeSpan HighTrustAccessTokenLifetime = TimeSpan.FromHours(12.0); #endregion public fields #region public methods /// /// Retrieves the context token string from the specified request by looking for well-known parameter names in the /// POSTed form parameters and the querystring. Returns null if no context token is found. /// /// HttpRequest in which to look for a context token /// The context token string public static string GetContextTokenFromRequest(HttpRequest request) { return GetContextTokenFromRequest(new HttpRequestWrapper(request)); } /// /// Retrieves the context token string from the specified request by looking for well-known parameter names in the /// POSTed form parameters and the querystring. Returns null if no context token is found. /// /// HttpRequest in which to look for a context token /// The context token string public static string GetContextTokenFromRequest(HttpRequestBase request) { string[] paramNames = { "AppContext", "AppContextToken", "AccessToken", "SPAppToken" }; foreach (string paramName in paramNames) { if (!string.IsNullOrEmpty(request.Form[paramName])) { return request.Form[paramName]; } if (!string.IsNullOrEmpty(request.QueryString[paramName])) { return request.QueryString[paramName]; } } return null; } /// /// Retrieves an access token from ACS to call the source of the specified context token at the specified /// targetHost. The targetHost must be registered for the principal that sent the context token. /// /// Context token issued by the intended access token audience /// Url authority of the target principal /// An access token with an audience matching the context token's source public static OAuth2AccessTokenResponse GetAccessToken(SharePointContextToken contextToken, string targetHost) { string targetPrincipalName = contextToken.TargetPrincipalName; // Extract the refreshToken from the context token string refreshToken = contextToken.RefreshToken; if (String.IsNullOrEmpty(refreshToken)) { return null; } string targetRealm = Realm ?? contextToken.Realm; return GetAccessToken(refreshToken, targetPrincipalName, targetHost, targetRealm); } /// /// Uses the specified authorization code to retrieve an access token from ACS to call the specified principal /// at the specified targetHost. The targetHost must be registered for target principal. If specified realm is /// null, the "Realm" setting in web.config will be used instead. /// /// Authorization code to exchange for access token /// Name of the target principal to retrieve an access token for /// Url authority of the target principal /// Realm to use for the access token's nameid and audience /// Redirect URI registered for this add-in /// An access token with an audience of the target principal public static OAuth2AccessTokenResponse GetAccessToken( string authorizationCode, string targetPrincipalName, string targetHost, string targetRealm, Uri redirectUri) { if (targetRealm == null) { targetRealm = Realm; } string resource = GetFormattedPrincipal(targetPrincipalName, targetHost, targetRealm); string clientId = GetFormattedPrincipal(ClientId, null, targetRealm); // Create request for token. The RedirectUri is null here. This will fail if redirect uri is registered OAuth2AccessTokenRequest oauth2Request = OAuth2MessageFactory.CreateAccessTokenRequestWithAuthorizationCode( clientId, ClientSecret, authorizationCode, redirectUri, resource); // Get token OAuth2S2SClient client = new OAuth2S2SClient(); OAuth2AccessTokenResponse oauth2Response; try { oauth2Response = client.Issue(AcsMetadataParser.GetStsUrl(targetRealm), oauth2Request) as OAuth2AccessTokenResponse; } catch (RequestFailedException) { if (!string.IsNullOrEmpty(SecondaryClientSecret)) { oauth2Request = OAuth2MessageFactory.CreateAccessTokenRequestWithAuthorizationCode( clientId, SecondaryClientSecret, authorizationCode, redirectUri, resource); oauth2Response = client.Issue(AcsMetadataParser.GetStsUrl(targetRealm), oauth2Request) as OAuth2AccessTokenResponse; } else { throw; } } catch (WebException wex) { using (StreamReader sr = new StreamReader(wex.Response.GetResponseStream())) { string responseText = sr.ReadToEnd(); throw new WebException(wex.Message + " - " + responseText, wex); } } return oauth2Response; } /// /// Uses the specified refresh token to retrieve an access token from ACS to call the specified principal /// at the specified targetHost. The targetHost must be registered for target principal. If specified realm is /// null, the "Realm" setting in web.config will be used instead. /// /// Refresh token to exchange for access token /// Name of the target principal to retrieve an access token for /// Url authority of the target principal /// Realm to use for the access token's nameid and audience /// An access token with an audience of the target principal public static OAuth2AccessTokenResponse GetAccessToken( string refreshToken, string targetPrincipalName, string targetHost, string targetRealm) { if (targetRealm == null) { targetRealm = Realm; } string resource = GetFormattedPrincipal(targetPrincipalName, targetHost, targetRealm); string clientId = GetFormattedPrincipal(ClientId, null, targetRealm); OAuth2AccessTokenRequest oauth2Request = OAuth2MessageFactory.CreateAccessTokenRequestWithRefreshToken(clientId, ClientSecret, refreshToken, resource); // Get token OAuth2S2SClient client = new OAuth2S2SClient(); OAuth2AccessTokenResponse oauth2Response; try { oauth2Response = client.Issue(AcsMetadataParser.GetStsUrl(targetRealm), oauth2Request) as OAuth2AccessTokenResponse; } catch (RequestFailedException) { if (!string.IsNullOrEmpty(SecondaryClientSecret)) { oauth2Request = OAuth2MessageFactory.CreateAccessTokenRequestWithRefreshToken(clientId, SecondaryClientSecret, refreshToken, resource); oauth2Response = client.Issue(AcsMetadataParser.GetStsUrl(targetRealm), oauth2Request) as OAuth2AccessTokenResponse; } else { throw; } } catch (WebException wex) { using (StreamReader sr = new StreamReader(wex.Response.GetResponseStream())) { string responseText = sr.ReadToEnd(); throw new WebException(wex.Message + " - " + responseText, wex); } } return oauth2Response; } /// /// Retrieves an app-only access token from ACS to call the specified principal /// at the specified targetHost. The targetHost must be registered for target principal. If specified realm is /// null, the "Realm" setting in web.config will be used instead. /// /// Name of the target principal to retrieve an access token for /// Url authority of the target principal /// Realm to use for the access token's nameid and audience /// An access token with an audience of the target principal public static OAuth2AccessTokenResponse GetAppOnlyAccessToken( string targetPrincipalName, string targetHost, string targetRealm, string clientId = null, string clientSecret = null) { if (targetRealm == null) { targetRealm = Realm; } if(clientId == null) { clientId = ClientId; } if(clientSecret == null) { clientSecret = ClientSecret; } string resource = GetFormattedPrincipal(targetPrincipalName, targetHost, targetRealm); string formattedClientId = GetFormattedPrincipal(clientId, HostedAppHostName, targetRealm); OAuth2AccessTokenRequest oauth2Request = OAuth2MessageFactory.CreateAccessTokenRequestWithClientCredentials(formattedClientId, clientSecret, resource); oauth2Request.Resource = resource; // Get token OAuth2S2SClient client = new OAuth2S2SClient(); OAuth2AccessTokenResponse oauth2Response; try { oauth2Response = client.Issue(AcsMetadataParser.GetStsUrl(targetRealm), oauth2Request) as OAuth2AccessTokenResponse; } catch (RequestFailedException) { if (!string.IsNullOrEmpty(SecondaryClientSecret)) { oauth2Request = OAuth2MessageFactory.CreateAccessTokenRequestWithClientCredentials(clientId, SecondaryClientSecret, resource); oauth2Request.Resource = resource; oauth2Response = client.Issue(AcsMetadataParser.GetStsUrl(targetRealm), oauth2Request) as OAuth2AccessTokenResponse; } else { throw; } } catch (WebException wex) { using (StreamReader sr = new StreamReader(wex.Response.GetResponseStream())) { string responseText = sr.ReadToEnd(); throw new WebException(wex.Message + " - " + responseText, wex); } } return oauth2Response; } /// /// Returns the SharePoint url to which the add-in should redirect the browser to request consent and get back /// an authorization code. /// /// Absolute Url of the SharePoint site /// Space-delimited permissions to request from the SharePoint site in "shorthand" format /// (e.g. "Web.Read Site.Write") /// Url of the SharePoint site's OAuth authorization page public static string GetAuthorizationUrl(string contextUrl, string scope) { return string.Format( "{0}{1}?IsDlg=1&client_id={2}&scope={3}&response_type=code", EnsureTrailingSlash(contextUrl), AuthorizationPage, ClientId, scope); } /// /// Returns the SharePoint url to which the add-in should redirect the browser to request consent and get back /// an authorization code. /// /// Absolute Url of the SharePoint site /// Space-delimited permissions to request from the SharePoint site in "shorthand" format /// (e.g. "Web.Read Site.Write") /// Uri to which SharePoint should redirect the browser to after consent is /// granted /// Url of the SharePoint site's OAuth authorization page public static string GetAuthorizationUrl(string contextUrl, string scope, string redirectUri) { return string.Format( "{0}{1}?IsDlg=1&client_id={2}&scope={3}&response_type=code&redirect_uri={4}", EnsureTrailingSlash(contextUrl), AuthorizationPage, ClientId, scope, redirectUri); } /// /// Returns the SharePoint url to which the add-in should redirect the browser to request a new context token. /// /// Absolute Url of the SharePoint site /// Uri to which SharePoint should redirect the browser to with a context token /// Url of the SharePoint site's context token redirect page public static string GetAppContextTokenRequestUrl(string contextUrl, string redirectUri) { return string.Format( "{0}{1}?client_id={2}&redirect_uri={3}", EnsureTrailingSlash(contextUrl), RedirectPage, ClientId, redirectUri); } /// /// Retrieves an S2S access token signed by the application's private certificate on behalf of the specified /// WindowsIdentity and intended for the SharePoint at the targetApplicationUri. If no Realm is specified in /// web.config, an auth challenge will be issued to the targetApplicationUri to discover it. /// /// Url of the target SharePoint site /// Windows identity of the user on whose behalf to create the access token /// An access token with an audience of the target principal public static string GetS2SAccessTokenWithWindowsIdentity( Uri targetApplicationUri, WindowsIdentity identity) { string realm = string.IsNullOrEmpty(Realm) ? GetRealmFromTargetUrl(targetApplicationUri) : Realm; JsonWebTokenClaim[] claims = identity != null ? GetClaimsWithWindowsIdentity(identity) : null; return GetS2SAccessTokenWithClaims(targetApplicationUri.Authority, realm, claims); } /// /// Get authentication realm from SharePoint /// /// Url of the target SharePoint site /// String representation of the realm GUID public static string GetRealmFromTargetUrl(Uri targetApplicationUri) { WebRequest request = WebRequest.Create(targetApplicationUri.ToString().TrimEnd('/') + "/_vti_bin/client.svc"); request.Headers.Add("Authorization: Bearer "); try { // We're expecting an exception here as the server should return a 401 telling us how we can authenticate using (request.GetResponse()) { } } catch (WebException e) { if (e.Response == null) { return null; } string bearerResponseHeader = e.Response.Headers["WWW-Authenticate"]; if (string.IsNullOrEmpty(bearerResponseHeader)) { return null; } const string bearer = "Bearer realm=\""; int bearerIndex = bearerResponseHeader.IndexOf(bearer, StringComparison.Ordinal); if (bearerIndex < 0) { return null; } int realmIndex = bearerIndex + bearer.Length; if (bearerResponseHeader.Length >= realmIndex + 36) { string targetRealm = bearerResponseHeader.Substring(realmIndex, 36); Guid realmGuid; if (Guid.TryParse(targetRealm, out realmGuid)) { return targetRealm; } } } return null; } /// /// Determines if this is a high trust add-in. /// /// True if this is a high trust add-in. public static bool IsHighTrustApp() { return SigningCredentials != null; } /// /// Ensures that the specified URL ends with '/' if it is not null or empty. /// /// The url. /// The url ending with '/' if it is not null or empty. public static string EnsureTrailingSlash(string url) { if (!string.IsNullOrEmpty(url) && url[url.Length - 1] != '/') { return url + "/"; } return url; } #endregion #region private fields // // Configuration Constants // private const string AuthorizationPage = "_layouts/15/OAuthAuthorize.aspx"; private const string RedirectPage = "_layouts/15/AppRedirect.aspx"; private const string AcsPrincipalName = "00000001-0000-0000-c000-000000000000"; private const string AcsMetadataEndPointRelativeUrl = "metadata/json/1"; private const string S2SProtocol = "OAuth2"; private const string DelegationIssuance = "DelegationIssuance1.0"; private const string NameIdentifierClaimType = JsonWebTokenConstants.ReservedClaims.NameIdentifier; private const string TrustedForImpersonationClaimType = "trustedfordelegation"; private const string ActorTokenClaimType = JsonWebTokenConstants.ReservedClaims.ActorToken; // // Environment Constants // private static string GlobalEndPointPrefix = "accounts"; private static string AcsHostUrl = "accesscontrol.windows.net"; // // Hosted add-in configuration // private static readonly string ClientId = string.IsNullOrEmpty(WebConfigurationManager.AppSettings.Get("ClientId")) ? WebConfigurationManager.AppSettings.Get("HostedAppName") : WebConfigurationManager.AppSettings.Get("ClientId"); private static readonly string IssuerId = string.IsNullOrEmpty(WebConfigurationManager.AppSettings.Get("IssuerId")) ? ClientId : WebConfigurationManager.AppSettings.Get("IssuerId"); private static readonly string HostedAppHostNameOverride = WebConfigurationManager.AppSettings.Get("HostedAppHostNameOverride"); private static readonly string HostedAppHostName = WebConfigurationManager.AppSettings.Get("HostedAppHostName"); private static readonly string ClientSecret = string.IsNullOrEmpty(WebConfigurationManager.AppSettings.Get("ClientSecret")) ? WebConfigurationManager.AppSettings.Get("HostedAppSigningKey") : WebConfigurationManager.AppSettings.Get("ClientSecret"); private static readonly string SecondaryClientSecret = WebConfigurationManager.AppSettings.Get("SecondaryClientSecret"); private static readonly string Realm = WebConfigurationManager.AppSettings.Get("Realm"); private static readonly string ServiceNamespace = WebConfigurationManager.AppSettings.Get("Realm"); private static readonly string ClientSigningCertificatePath = WebConfigurationManager.AppSettings.Get("ClientSigningCertificatePath"); private static readonly string ClientSigningCertificatePassword = WebConfigurationManager.AppSettings.Get("ClientSigningCertificatePassword"); private static readonly X509Certificate2 ClientCertificate = (string.IsNullOrEmpty(ClientSigningCertificatePath) || string.IsNullOrEmpty(ClientSigningCertificatePassword)) ? null : new X509Certificate2(ClientSigningCertificatePath, ClientSigningCertificatePassword); private static readonly X509SigningCredentials SigningCredentials = (ClientCertificate == null) ? null : new X509SigningCredentials(ClientCertificate, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest); #endregion #region private methods private static string GetAcsMetadataEndpointUrl() { return Path.Combine(GetAcsGlobalEndpointUrl(), AcsMetadataEndPointRelativeUrl); } private static string GetFormattedPrincipal(string principalName, string hostName, string realm) { if (!String.IsNullOrEmpty(hostName)) { return String.Format(CultureInfo.InvariantCulture, "{0}/{1}@{2}", principalName, hostName, realm); } return String.Format(CultureInfo.InvariantCulture, "{0}@{1}", principalName, realm); } private static string GetAcsPrincipalName(string realm) { return GetFormattedPrincipal(AcsPrincipalName, new Uri(GetAcsGlobalEndpointUrl()).Host, realm); } private static string GetAcsGlobalEndpointUrl() { return String.Format(CultureInfo.InvariantCulture, "https://{0}.{1}/", GlobalEndPointPrefix, AcsHostUrl); } private static JsonWebSecurityTokenHandler CreateJsonWebSecurityTokenHandler() { JsonWebSecurityTokenHandler handler = new JsonWebSecurityTokenHandler(); handler.Configuration = new SecurityTokenHandlerConfiguration(); handler.Configuration.AudienceRestriction = new AudienceRestriction(AudienceUriMode.Never); handler.Configuration.CertificateValidator = X509CertificateValidator.None; List securityKeys = new List(); securityKeys.Add(Convert.FromBase64String(ClientSecret)); if (!string.IsNullOrEmpty(SecondaryClientSecret)) { securityKeys.Add(Convert.FromBase64String(SecondaryClientSecret)); } List securityTokens = new List(); securityTokens.Add(new MultipleSymmetricKeySecurityToken(securityKeys)); handler.Configuration.IssuerTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver( new ReadOnlyCollection(securityTokens), false); SymmetricKeyIssuerNameRegistry issuerNameRegistry = new SymmetricKeyIssuerNameRegistry(); foreach (byte[] securitykey in securityKeys) { issuerNameRegistry.AddTrustedIssuer(securitykey, GetAcsPrincipalName(ServiceNamespace)); } handler.Configuration.IssuerNameRegistry = issuerNameRegistry; return handler; } private static string GetS2SAccessTokenWithClaims( string targetApplicationHostName, string targetRealm, IEnumerable claims) { return IssueToken( ClientId, IssuerId, targetRealm, SharePointPrincipal, targetRealm, targetApplicationHostName, true, claims, claims == null); } private static JsonWebTokenClaim[] GetClaimsWithWindowsIdentity(WindowsIdentity identity) { JsonWebTokenClaim[] claims = new JsonWebTokenClaim[] { new JsonWebTokenClaim(NameIdentifierClaimType, identity.User.Value.ToLower()), new JsonWebTokenClaim("nii", "urn:office:idp:activedirectory") }; return claims; } private static string IssueToken( string sourceApplication, string issuerApplication, string sourceRealm, string targetApplication, string targetRealm, string targetApplicationHostName, bool trustedForDelegation, IEnumerable claims, bool appOnly = false) { if (null == SigningCredentials) { throw new InvalidOperationException("SigningCredentials was not initialized"); } #region Actor token string issuer = string.IsNullOrEmpty(sourceRealm) ? issuerApplication : string.Format("{0}@{1}", issuerApplication, sourceRealm); string nameid = string.IsNullOrEmpty(sourceRealm) ? sourceApplication : string.Format("{0}@{1}", sourceApplication, sourceRealm); string audience = string.Format("{0}/{1}@{2}", targetApplication, targetApplicationHostName, targetRealm); List actorClaims = new List(); actorClaims.Add(new JsonWebTokenClaim(JsonWebTokenConstants.ReservedClaims.NameIdentifier, nameid)); if (trustedForDelegation && !appOnly) { actorClaims.Add(new JsonWebTokenClaim(TrustedForImpersonationClaimType, "true")); } // Create token JsonWebSecurityToken actorToken = new JsonWebSecurityToken( issuer: issuer, audience: audience, validFrom: DateTime.UtcNow, validTo: DateTime.UtcNow.Add(HighTrustAccessTokenLifetime), signingCredentials: SigningCredentials, claims: actorClaims); string actorTokenString = new JsonWebSecurityTokenHandler().WriteTokenAsString(actorToken); if (appOnly) { // App-only token is the same as actor token for delegated case return actorTokenString; } #endregion Actor token #region Outer token List outerClaims = null == claims ? new List() : new List(claims); outerClaims.Add(new JsonWebTokenClaim(ActorTokenClaimType, actorTokenString)); JsonWebSecurityToken jsonToken = new JsonWebSecurityToken( nameid, // outer token issuer should match actor token nameid audience, DateTime.UtcNow, DateTime.UtcNow.Add(HighTrustAccessTokenLifetime), outerClaims); string accessToken = new JsonWebSecurityTokenHandler().WriteTokenAsString(jsonToken); #endregion Outer token return accessToken; } #endregion #region AcsMetadataParser // This class is used to get MetaData document from the global STS endpoint. It contains // methods to parse the MetaData document and get endpoints and STS certificate. public static class AcsMetadataParser { public static X509Certificate2 GetAcsSigningCert(string realm) { JsonMetadataDocument document = GetMetadataDocument(realm); if (null != document.keys && document.keys.Count > 0) { JsonKey signingKey = document.keys[0]; if (null != signingKey && null != signingKey.keyValue) { return new X509Certificate2(Encoding.UTF8.GetBytes(signingKey.keyValue.value)); } } throw new Exception("Metadata document does not contain ACS signing certificate."); } public static string GetDelegationServiceUrl(string realm) { JsonMetadataDocument document = GetMetadataDocument(realm); JsonEndpoint delegationEndpoint = document.endpoints.SingleOrDefault(e => e.protocol == DelegationIssuance); if (null != delegationEndpoint) { return delegationEndpoint.location; } throw new Exception("Metadata document does not contain Delegation Service endpoint Url"); } private static JsonMetadataDocument GetMetadataDocument(string realm) { string acsMetadataEndpointUrlWithRealm = String.Format(CultureInfo.InvariantCulture, "{0}?realm={1}", GetAcsMetadataEndpointUrl(), realm); byte[] acsMetadata; using (WebClient webClient = new WebClient()) { acsMetadata = webClient.DownloadData(acsMetadataEndpointUrlWithRealm); } string jsonResponseString = Encoding.UTF8.GetString(acsMetadata); JavaScriptSerializer serializer = new JavaScriptSerializer(); JsonMetadataDocument document = serializer.Deserialize(jsonResponseString); if (null == document) { throw new Exception("No metadata document found at the global endpoint " + acsMetadataEndpointUrlWithRealm); } return document; } public static string GetStsUrl(string realm) { JsonMetadataDocument document = GetMetadataDocument(realm); JsonEndpoint s2sEndpoint = document.endpoints.SingleOrDefault(e => e.protocol == S2SProtocol); if (null != s2sEndpoint) { return s2sEndpoint.location; } throw new Exception("Metadata document does not contain STS endpoint url"); } private class JsonMetadataDocument { public string serviceName { get; set; } public List endpoints { get; set; } public List keys { get; set; } } private class JsonEndpoint { public string location { get; set; } public string protocol { get; set; } public string usage { get; set; } } private class JsonKeyValue { public string type { get; set; } public string value { get; set; } } private class JsonKey { public string usage { get; set; } public JsonKeyValue keyValue { get; set; } } } #endregion } /// /// A JsonWebSecurityToken generated by SharePoint to authenticate to a 3rd party application and allow callbacks using a refresh token /// public class SharePointContextToken : JsonWebSecurityToken { public static SharePointContextToken Create(JsonWebSecurityToken contextToken) { return new SharePointContextToken(contextToken.Issuer, contextToken.Audience, contextToken.ValidFrom, contextToken.ValidTo, contextToken.Claims); } public SharePointContextToken(string issuer, string audience, DateTime validFrom, DateTime validTo, IEnumerable claims) : base(issuer, audience, validFrom, validTo, claims) { } public SharePointContextToken(string issuer, string audience, DateTime validFrom, DateTime validTo, IEnumerable claims, SecurityToken issuerToken, JsonWebSecurityToken actorToken) : base(issuer, audience, validFrom, validTo, claims, issuerToken, actorToken) { } public SharePointContextToken(string issuer, string audience, DateTime validFrom, DateTime validTo, IEnumerable claims, SigningCredentials signingCredentials) : base(issuer, audience, validFrom, validTo, claims, signingCredentials) { } public string NameId { get { return GetClaimValue(this, "nameid"); } } /// /// The principal name portion of the context token's "appctxsender" claim /// public string TargetPrincipalName { get { string appctxsender = GetClaimValue(this, "appctxsender"); if (appctxsender == null) { return null; } return appctxsender.Split('@')[0]; } } /// /// The context token's "refreshtoken" claim /// public string RefreshToken { get { return GetClaimValue(this, "refreshtoken"); } } /// /// The realm portion of the context token's "audience" claim /// public string Realm { get { string aud = Audience; if (aud == null) { return null; } string tokenRealm = aud.Substring(aud.IndexOf('@') + 1); return tokenRealm; } } private static string GetClaimValue(JsonWebSecurityToken token, string claimType) { if (token == null) { throw new ArgumentNullException("token"); } foreach (JsonWebTokenClaim claim in token.Claims) { if (StringComparer.Ordinal.Equals(claim.ClaimType, claimType)) { return claim.Value; } } return null; } } /// /// Represents a security token which contains multiple security keys that are generated using symmetric algorithms. /// public class MultipleSymmetricKeySecurityToken : SecurityToken { /// /// Initializes a new instance of the MultipleSymmetricKeySecurityToken class. /// /// An enumeration of Byte arrays that contain the symmetric keys. public MultipleSymmetricKeySecurityToken(IEnumerable keys) : this(UniqueId.CreateUniqueId(), keys) { } /// /// Initializes a new instance of the MultipleSymmetricKeySecurityToken class. /// /// The unique identifier of the security token. /// An enumeration of Byte arrays that contain the symmetric keys. public MultipleSymmetricKeySecurityToken(string tokenId, IEnumerable keys) { if (keys == null) { throw new ArgumentNullException("keys"); } if (String.IsNullOrEmpty(tokenId)) { throw new ArgumentException("Value cannot be a null or empty string.", "tokenId"); } foreach (byte[] key in keys) { if (key.Length <= 0) { throw new ArgumentException("The key length must be greater then zero.", "keys"); } } id = tokenId; effectiveTime = DateTime.UtcNow; securityKeys = CreateSymmetricSecurityKeys(keys); } /// /// Gets the unique identifier of the security token. /// public override string Id { get { return id; } } /// /// Gets the cryptographic keys associated with the security token. /// public override ReadOnlyCollection SecurityKeys { get { return securityKeys.AsReadOnly(); } } /// /// Gets the first instant in time at which this security token is valid. /// public override DateTime ValidFrom { get { return effectiveTime; } } /// /// Gets the last instant in time at which this security token is valid. /// public override DateTime ValidTo { get { // Never expire return DateTime.MaxValue; } } /// /// Returns a value that indicates whether the key identifier for this instance can be resolved to the specified key identifier. /// /// A SecurityKeyIdentifierClause to compare to this instance /// true if keyIdentifierClause is a SecurityKeyIdentifierClause and it has the same unique identifier as the Id property; otherwise, false. public override bool MatchesKeyIdentifierClause(SecurityKeyIdentifierClause keyIdentifierClause) { if (keyIdentifierClause == null) { throw new ArgumentNullException("keyIdentifierClause"); } // Since this is a symmetric token and we do not have IDs to distinguish tokens, we just check for the // presence of a SymmetricIssuerKeyIdentifier. The actual mapping to the issuer takes place later // when the key is matched to the issuer. if (keyIdentifierClause is SymmetricIssuerKeyIdentifierClause) { return true; } return base.MatchesKeyIdentifierClause(keyIdentifierClause); } #region private members private List CreateSymmetricSecurityKeys(IEnumerable keys) { List symmetricKeys = new List(); foreach (byte[] key in keys) { symmetricKeys.Add(new InMemorySymmetricSecurityKey(key)); } return symmetricKeys; } private string id; private DateTime effectiveTime; private List securityKeys; #endregion } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/Utilities.cs ================================================ using System; using System.IO; using System.Net; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using CredentialManagement; using KeePassLib; using KoenZomers.KeePass.OneDriveSync; using KoenZomers.KeePass.OneDriveSync.Enums; using KoenZomers.OneDrive.Api; namespace KoenZomersKeePassOneDriveSync { /// /// Utilities for the KeePass plugin /// public static class Utilities { #region File Hashes /// /// Calculates the SHA1 file hash of the provided KeePass database /// /// Full path to where the KeePass database resides locally /// SHA1 file hash public static string GetDatabaseFileHash(string localKeePassDatabasePath) { using (var fileStream = new FileStream(localKeePassDatabasePath, FileMode.Open)) { using (var cryptoProvider = new SHA1CryptoServiceProvider()) { var hash = BitConverter.ToString(cryptoProvider.ComputeHash(fileStream)).Replace("-", string.Empty); return hash; } } } #endregion #region OneDrive /// /// Returns an active OneDriveApi instance. If a RefreshToken is available, it will set up an instance based on that, otherwise it will show the login dialog /// /// Configuration of the KeePass database /// Active OneDrive instance or NULL if unable to get an authenticated instance public static async Task GetOneDriveApi(Configuration databaseConfig) { OneDriveApi cloudStorage; switch (databaseConfig.CloudStorageType.GetValueOrDefault(CloudStorageType.OneDriveConsumer)) { case CloudStorageType.OneDriveConsumer: cloudStorage = new OneDriveConsumerApi(KoenZomersKeePassOneDriveSyncExt.OneDriveConsumerClientId, KoenZomersKeePassOneDriveSyncExt.OneDriveConsumerClientSecret); break; case CloudStorageType.MicrosoftGraph: case CloudStorageType.MicrosoftGraphDeviceLogin: cloudStorage = new OneDriveGraphApi(KoenZomersKeePassOneDriveSyncExt.GraphApiApplicationId); break; default: throw new ArgumentOutOfRangeException(string.Format("Cloud storage type {0} is not supported", databaseConfig.CloudStorageType)); } // Ensure we have a refresh token if (string.IsNullOrEmpty(databaseConfig.RefreshToken)) { // No refresh token available, perform the login process if (databaseConfig.CloudStorageType.GetValueOrDefault(CloudStorageType.OneDriveConsumer) == CloudStorageType.MicrosoftGraphDeviceLogin) { // Using Microsoft Graph Device ID Code Flow var oneDriveGraphDeviceLoginForm = new OneDriveGraphDeviceLoginForm(cloudStorage); var deviceLoginResult = oneDriveGraphDeviceLoginForm.ShowDialog(); if (deviceLoginResult != System.Windows.Forms.DialogResult.OK) { return null; } // Save the configuration so we keep the Refresh Token databaseConfig.RefreshToken = oneDriveGraphDeviceLoginForm.RefreshToken; Configuration.Save(); } else { // Using any of the other authentication options var oneDriveAuthenticateForm = new OneDriveAuthenticateForm(cloudStorage); var result = oneDriveAuthenticateForm.ShowDialog(); if (result != System.Windows.Forms.DialogResult.OK) { return null; } // Check if we already know where to store the Refresh Token for this database if (!databaseConfig.RefreshTokenStorage.HasValue) { // We don't know yet where the Refresh Token for this database should be stored, ask the user to choose var oneDriveRefreshTokenStorageForm = new OneDriveRefreshTokenStorageDialog(databaseConfig); oneDriveRefreshTokenStorageForm.ShowDialog(); } // Save the configuration so we keep the Refresh Token var oneDriveApi = oneDriveAuthenticateForm.OneDriveApi; databaseConfig.RefreshToken = oneDriveApi.AccessToken.RefreshToken; Configuration.Save(); return oneDriveApi; } } try { ApplyProxySettings(cloudStorage); await cloudStorage.AuthenticateUsingRefreshToken(databaseConfig.RefreshToken); return cloudStorage; } catch (KoenZomers.OneDrive.Api.Exceptions.TokenRetrievalFailedException) { // Something went wrong with retrieving the access token throw; } catch (Exception e) { // If specifically the HttpRequestException occurred, it likely contains more information on what went wrong, so pass it up to the callstack if (e.GetType().ToString() == "System.Net.Http.HttpRequestException") { throw; } // Occurs if no connection can be made with the OneDrive service. It will be handled properly in the calling code. return null; } } #endregion #region Proxy Support /// /// Gets correct web proxy settings based on KeePass proxy configuration /// /// IWebProxy conforming instance with the proper proxy configuraton public static IWebProxy GetProxySettings() { IWebProxy webProxy = null; // Create the WebProxy object based on KeePass configuration switch (KeePass.Program.Config.Integration.ProxyType) { case ProxyServerType.None: webProxy = null; break; case ProxyServerType.Manual: webProxy = new WebProxy(string.Concat(KeePass.Program.Config.Integration.ProxyAddress, ":", KeePass.Program.Config.Integration.ProxyPort)); break; case ProxyServerType.System: webProxy = WebRequest.DefaultWebProxy; break; } return webProxy; } /// /// Gets correct web proxy credentials based on KeePass proxy configuration /// /// NetworkCredential instance with the proper proxy credentials public static NetworkCredential GetProxyCredentials() { var networkCredential = KeePass.Program.Config.Integration.ProxyAuthType == ProxyAuthType.Manual ? new NetworkCredential(KeePass.Program.Config.Integration.ProxyUserName, KeePass.Program.Config.Integration.ProxyPassword) : null; return networkCredential; } /// /// Applies the correct web proxy settings to the provided OneDriveApi instance based on KeePass proxy configuration /// /// OneDriveApi instance to apply the proper proxy settings to public static void ApplyProxySettings(OneDriveApi oneDriveApi) { // Set the WebProxy to use oneDriveApi.ProxyConfiguration = GetProxySettings(); // Configure the credentials to use for the proxy oneDriveApi.ProxyCredential = GetProxyCredentials(); } #endregion #region Windows Credential Manager /// /// Saves the provided OneDrive Refresh Token in the Windows Credential Manager /// /// Full local path to the KeePass database for which to save the OneDrive Refresh Token /// The OneDrive Refresh Token to store securely in the Windows Credential Manager public static void SaveRefreshTokenInWindowsCredentialManager(string databaseFilePath, string refreshToken) { // Some refresh tokens can be longer than the maximum permitted 512 characters for a password field. If this is the case, put the first 512 characters of the refresh token in the password field and the rest in the comments field. Also the comments field has a limit. So far I have not run into this limit yet. string passwordPart1; string passwordPart2; if (refreshToken != null && refreshToken.Length > 512) { passwordPart1 = refreshToken.Remove(512); passwordPart2 = refreshToken.Remove(0, 512); } else { passwordPart1 = refreshToken; passwordPart2 = string.Empty; } using (var saved = new Credential(databaseFilePath, passwordPart1, string.Concat("KoenZomers.KeePass.OneDriveSync:", databaseFilePath), CredentialType.Generic) {PersistanceType = PersistanceType.LocalComputer}) { saved.Description = passwordPart2; saved.Save(); } } /// /// Retrieves a OneDrive Refresh Token from the Windows Credential Manager /// /// Full local path to the KeePass database for which to retrieve the OneDrive Refresh Token /// OneDrive Refresh Token if available or NULL if no Refresh Token found for the provided database public static string GetRefreshTokenFromWindowsCredentialManager(string databaseFilePath) { using (var credential = new Credential {Target = string.Concat("KoenZomers.KeePass.OneDriveSync:", databaseFilePath), Type = CredentialType.Generic}) { credential.Load(); // Concatenate the contents of the password and comments fields to retrieve the refresh token return credential.Exists() ? credential.Password + (!string.IsNullOrEmpty(credential.Description) ? credential.Description : string.Empty) + (!string.IsNullOrEmpty(credential.Target) ? credential.Target : string.Empty) : null; } } /// /// Deletes a OneDrive Refresh Token from the Windows Credential Manager /// /// Full local path to the KeePass database for which to delete the OneDrive Refresh Token public static void DeleteRefreshTokenFromWindowsCredentialManager(string databaseFilePath) { using (var credential = new Credential {Target = string.Concat("KoenZomers.KeePass.OneDriveSync:", databaseFilePath), Type = CredentialType.Generic}) { // Verify if we have stored a token for this database if (credential.Exists()) { // Delete the Windows Credential Manager entry credential.Delete(); } } } #endregion #region KeePass Database Storage /// /// Saves the provided OneDrive Refresh Token in the provided KeePass database /// /// KeePass database instance to store the Refresh Token in /// The OneDrive Refresh Token to store securely in the KeePass database public static void SaveRefreshTokenInKeePassDatabase(PwDatabase keePassDatabase, string refreshToken) { keePassDatabase.CustomData.Set("KoenZomers.KeePass.OneDriveSync.RefreshToken", refreshToken); } /// /// Retrieves a OneDrive Refresh Token from the provided KeePass database /// /// KeePass database instance to get the Refresh Token from /// OneDrive Refresh Token if available or NULL if no Refresh Token found for the provided database public static string GetRefreshTokenFromKeePassDatabase(PwDatabase keePassDatabase) { var refreshToken = keePassDatabase.CustomData.Get("KoenZomers.KeePass.OneDriveSync.RefreshToken"); return refreshToken; } #endregion #region DPAPI /// /// Encrypts a Refresh Token using the DPAPI Protect method with current user scope /// /// The Refresh Token to encrypt /// The Base64-encoded encrypted refresh token or NULL if encryption fails public static string Protect(string refreshToken) { // Token should be plain ASCII text, get raw byte data for encoding var rawToken = Encoding.ASCII.GetBytes(refreshToken); try { // Encrypt using DPAPI with user scope, can only be decrypted by currently logged-in user var rawEncryptedToken = ProtectedData.Protect(rawToken, null, DataProtectionScope.CurrentUser); // Base64-encode encrypted token for JSON string compatibility var encryptedToken = Convert.ToBase64String(rawEncryptedToken); return encryptedToken; } catch (Exception) { // If encryption fails, lose the token return null; } } /// /// Decrypt a Refresh Token using the DPAPI Protect method with current user scope /// /// The encrypted Refresh Token to decrypt, Base64-encoded /// The decrypted refresh token or NULL if decryption fails public static string Unprotect(string encryptedRefreshToken) { // Decode Base64-encoded encrypted data var rawEncryptedToken = Convert.FromBase64String(encryptedRefreshToken); try { // Decrypt using DPAPI with user scope, only possible if the currently logged-in user is the same as the one who encrypted it var rawToken = ProtectedData.Unprotect(rawEncryptedToken, null, DataProtectionScope.CurrentUser); // Get string data from byte array var token = Encoding.ASCII.GetString(rawToken); return token; } catch (Exception) { // If decryption fails, lose the token return null; } } #endregion } } ================================================ FILE: KoenZomers.KeePass.OneDriveSync/app.config ================================================  ================================================ FILE: KoenZomers.KeePass.OneDriveSync/packages.config ================================================  ================================================ FILE: KoenZomers.KeePass.OneDriveSync.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KoenZomers.KeePass.OneDriveSync", "KoenZomers.KeePass.OneDriveSync\KoenZomers.KeePass.OneDriveSync.csproj", "{ED981431-32EA-4FC8-BF5B-D5A29B3AD52E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{078F6134-F1AF-4F2A-907F-C8AC4C976834}" ProjectSection(SolutionItems) = preProject KoenZomers.KeePass.OneDriveSync\CredentialManagement.dll = KoenZomers.KeePass.OneDriveSync\CredentialManagement.dll EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C51FE94D-1D06-497B-97AA-5B80030F6E7A}" ProjectSection(SolutionItems) = preProject create-plgx.cmd = create-plgx.cmd NuGet.config = NuGet.config version.txt = version.txt EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Screenshots", "Screenshots", "{1985E673-364B-4E4F-9DF4-7F110239AEB0}" ProjectSection(SolutionItems) = preProject Screenshots\AuthenticateToOneDrive.png = Screenshots\AuthenticateToOneDrive.png Screenshots\ChooseCloudStorage.png = Screenshots\ChooseCloudStorage.png Screenshots\ConfigurationDetails.png = Screenshots\ConfigurationDetails.png Screenshots\ConfigurationsContextMenu.png = Screenshots\ConfigurationsContextMenu.png Screenshots\ConfigurationsOverview.png = Screenshots\ConfigurationsOverview.png Screenshots\CredentialManagerControlPanel.png = Screenshots\CredentialManagerControlPanel.png Screenshots\KeePassCheckForUpdates.png = Screenshots\KeePassCheckForUpdates.png Screenshots\KeePassOneDriveSyncConfigurationOptions.png = Screenshots\KeePassOneDriveSyncConfigurationOptions.png Screenshots\KeePassOneDriveSyncOptions.png = Screenshots\KeePassOneDriveSyncOptions.png Screenshots\KeePassPluginUpToDate.png = Screenshots\KeePassPluginUpToDate.png Screenshots\KeePassProxyOptions.png = Screenshots\KeePassProxyOptions.png Screenshots\KeePasstoolsPlugins.png = Screenshots\KeePasstoolsPlugins.png Screenshots\KeePassVerifyPluginPresent.png = Screenshots\KeePassVerifyPluginPresent.png Screenshots\OfflineMode.png = Screenshots\OfflineMode.png Screenshots\OneDriveConsentToPriviledges.png = Screenshots\OneDriveConsentToPriviledges.png Screenshots\OneDriveDatabaseUploaded.png = Screenshots\OneDriveDatabaseUploaded.png Screenshots\OneDriveSelectLocation.png = Screenshots\OneDriveSelectLocation.png Screenshots\OneDriveTwoFactorAuthentication.png = Screenshots\OneDriveTwoFactorAuthentication.png Screenshots\StartSyncing.png = Screenshots\StartSyncing.png Screenshots\WhereToStoreOneDriveToken.png = Screenshots\WhereToStoreOneDriveToken.png EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{C681B3E0-217B-497C-A067-12FE1CC6DE69}" ProjectSection(SolutionItems) = preProject README.md = README.md EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {ED981431-32EA-4FC8-BF5B-D5A29B3AD52E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED981431-32EA-4FC8-BF5B-D5A29B3AD52E}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED981431-32EA-4FC8-BF5B-D5A29B3AD52E}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED981431-32EA-4FC8-BF5B-D5A29B3AD52E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {508E11B0-378C-44C7-B479-337C56E78BBB} EndGlobalSection EndGlobal ================================================ FILE: LICENSE.md ================================================ Eclipse Public License - v 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. ================================================ FILE: NuGet.config ================================================  ================================================ FILE: README.md ================================================ # KeePass OneDrive Sync > Notice: Through the years I have enjoyed building and maintaining this code. I have been surprised to see how many people started using this plugin and how often around the globe I met people that asked me if I was the person behind the plugin. Time however is scarse and as much as I would like to, I'm having too many projects to work on in my spare time, so I need to get rid of some, this unfortunately being one. I will therefore no longer maintain this code. Feel free to fork off of it and make your own version out of it. I'll be happy to link to it. ![](https://github.com/KoenZomers/KeePassOneDriveSync/workflows/.NET%20Core/badge.svg) ![](https://img.shields.io/github/downloads/koenzomers/KeePassOneDriveSync/total.svg) ![](https://img.shields.io/github/issues/koenzomers/KeePassOneDriveSync.svg) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) A free plugin for KeePass (installable and portable) that allows syncing of multiple password databases from multiple OneDrives to a local version. It allows you to synchronize an unlimited amount of KeePass databases with an unlimited amount of OneDrives. So i.e. you can synchronize your personal KeePass database with your personal OneDrive and your work related KeePass with a OneDrive for Business on Office 365 that is shared among your colleagues, if you wish. This way it's also easy to be able to access your same KeePass database from all of your devices. Note that this plugin is only supported on Windows based platforms, but [has been reported to work on \*nix as well using Mono](https://github.com/KoenZomers/KeePassOneDriveSync/wiki/System-Requirements#nix-through-mono). It can also operate in conjunction with other KeePass plugins serving other operating systems such as KeePass2Android on Android devices. ## Download ## Download the PLGX and place it inside your KeePass\Plugins folder. Typically this will be `C:\Program Files (x86)\KeePass Password Safe 2\Plugins` or if you're using KeePass Portable, put it in a subfolder called Plugins from where your keepass.exe is located. When upgrading from a previous version of this plugin, simply ensure KeePass is closed, overwrite the existing PLGX and start KeePass again. [You can find the latest stable version here](../../releases/latest) ## Documentation ## - [System requirements](https://github.com/KoenZomers/KeePassOneDriveSync/wiki/System-Requirements) - [Installation instructions](https://github.com/KoenZomers/KeePassOneDriveSync/wiki/Installation-instructions) - [Configuration options](https://github.com/KoenZomers/KeePassOneDriveSync/wiki/Configuration-options) - [Checking for updates](https://github.com/KoenZomers/KeePassOneDriveSync/wiki/Update-check) - [Troubleshooting](https://github.com/KoenZomers/KeePassOneDriveSync/wiki/Troubleshooting) - [Frequently Asked Questions](https://github.com/KoenZomers/KeePassOneDriveSync/wiki/FAQ) ## Latest Version Version 2.1.2.2 - November 24, 2021 - Fixed the issue where the plugin cannot check for an update [#151](https://github.com/KoenZomers/KeePassOneDriveSync/issues/151) Version 2.1.2.1 - May 22, 2020 - Bugfix causing opening of the KeePassOneDriveSync about dialog to crash KeePass. Thanks to Drew O'Hara for letting me know! Version 2.1.2.0 - May 22, 2020 - Fixed a bug where uploading a KeePass database to a document library on SharePoint having "Require documents to be checked out before they can be edited?" set to Yes would never make the KeePass database show up in SharePoint [issue 131](https://github.com/KoenZomers/KeePassOneDriveSync/issues/131) - Updated the SharePoint connector so that it now makes use of versioning in the SharePoint document library allowing you to see in SharePoint when it was updated and allowing you to revert to previous versions of the KeePass database just like already was the case when storing the KeePass database on OneDrive for Business - Removed OneDrive option in the cloud storage providers as there really should not be a reason anymore to use it instead of the Microsoft Graph options on the first tab - Removed the wording Microsoft Graph on the first tab of the cloud storage providers as I noticed it is confusing for many people. Named them OneDrive & OneDrive for Business instead. Technically, they're still using the Microsoft Graph to connect to OneDrive, so it's just a visual change. - Fixed a small issue when refreshing the SharePoint location picker or enabling/disabling showing of hidden libraries not entirely working well - Changed that in the OneDrive and SharePoint file pickers, if an item in the folder has the same name as shown in the filename textbox, that it will get selected by default to make it clearer that it already exists in that folder - Added tooltips for files and folders in the SharePoint file picker to show additional information on these items when hovering over them such as creation date/time, last modified date/time, file size and file version number - Updated links pointing to the GitHub documentation to the new GitHub Wiki location [Full version history](https://github.com/KoenZomers/KeePassOneDriveSync/releases) ## Special Thanks Special thanks to Oleksandr Senyuk for making [KeeSkyDrive](http://sourceforge.net/projects/keeskydrive/) as it has inspired me to create this plugin. ================================================ FILE: create-plgx.cmd ================================================ ECHO Creating KeePass plugin package set PLUGIN_NAME=KeeOneDriveSync set SourceFolder=KoenZomers.KeePass.OneDriveSync rd /s /q %~dp0%SourceFolder%\obj %~dp0%SourceFolder%\bin del %~dp0%PLUGIN_NAME%.plgx "C:\Program Files\KeePass Password Safe 2\KeePass.exe" --plgx-create %~dp0%SourceFolder% --plgx-prereq-net:4.0 ren %~dp0%SourceFolder%.plgx %PLUGIN_NAME%.plgx ECHO KeePass Plugin package has been created ================================================ FILE: version.txt ================================================ : KoenZomers.KeePass.OneDriveSync:2.1.2.2 :