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.
   [](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
: