From 22ecf0d06f063e7e733364488351be17bdd4fc3c Mon Sep 17 00:00:00 2001 From: David Sparer Date: Thu, 27 Dec 2018 14:13:51 -0600 Subject: [PATCH] readded forms and controls for the credential manager --- .../XmlCredentialManagerUpgrader.cs | 135 ++ .../Connection/AbstractConnectionRecord.cs | 6 +- ...siteCredentialRepoUnlockerForm.Designer.cs | 349 +++++ .../CompositeCredentialRepoUnlockerForm.cs | 150 +++ .../CompositeCredentialRepoUnlockerForm.resx | 1138 +++++++++++++++++ .../Forms/CredentialManagerForm.Designer.cs | 138 ++ mRemoteV1/UI/Forms/CredentialManagerForm.cs | 65 + mRemoteV1/UI/Forms/CredentialManagerForm.resx | 145 +++ .../CredentialEditorPage.Designer.cs | 226 ++++ .../CredentialEditorPage.cs | 73 ++ .../CredentialEditorPage.resx | 120 ++ .../CredentialListPage.Designer.cs | 108 ++ .../CredentialListPage.cs | 148 +++ .../CredentialListPage.resx | 120 ++ .../CredentialRepositoriesPage.Designer.cs | 131 ++ .../CredentialRepositoriesPage.cs | 112 ++ .../CredentialRepositoriesPage.resx | 120 ++ .../CredentialRepositoryPageEditorFactory.cs | 15 + ...CredentialRepositoryEditorPage.Designer.cs | 198 +++ .../XmlCredentialRepositoryEditorPage.cs | 96 ++ .../XmlCredentialRepositoryEditorPage.resx | 123 ++ ...dentialRepositorySelectionPage.Designer.cs | 93 ++ .../CredentialRepositorySelectionPage.cs | 49 + .../CredentialRepositorySelectionPage.resx | 120 ++ .../KeePassRepositorySelector.cs | 13 + .../XmlCredentialRepositorySelector.cs | 13 + ...ialRepositoryTypeSelectionPage.Designer.cs | 135 ++ .../CredentialRepositoryTypeSelectionPage.cs | 84 ++ ...CredentialRepositoryTypeSelectionPage.resx | 123 ++ .../ICredentialManagerPage.cs | 11 + .../CredentialManagerUpgradeForm.Designer.cs | 318 +++++ .../UI/Forms/CredentialManagerUpgradeForm.cs | 126 ++ .../Forms/CredentialManagerUpgradeForm.resx | 131 ++ mRemoteV1/UI/Forms/UnlockerFormFactory.cs | 16 + mRemoteV1/mRemoteV1.csproj | 87 ++ 35 files changed, 5032 insertions(+), 3 deletions(-) create mode 100644 mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs create mode 100644 mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.cs create mode 100644 mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerForm.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerForm.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerForm.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.resx create mode 100644 mRemoteV1/UI/Forms/CredentialManagerPages/ICredentialManagerPage.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.Designer.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs create mode 100644 mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.resx create mode 100644 mRemoteV1/UI/Forms/UnlockerFormFactory.cs diff --git a/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs b/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs new file mode 100644 index 00000000..ee450d6a --- /dev/null +++ b/mRemoteV1/Config/Serializers/Versioning/XmlCredentialManagerUpgrader.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Security; +using System.Xml.Linq; +using mRemoteNG.App; +using mRemoteNG.Config.Serializers.CredentialSerializer; +using mRemoteNG.Connection; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.Security.Authentication; +using mRemoteNG.Security.Factories; +using mRemoteNG.Tools; +using mRemoteNG.Tree; +using System.Globalization; + +namespace mRemoteNG.Config.Serializers.Versioning +{ + public class XmlCredentialManagerUpgrader : IDeserializer + { + private readonly CredentialServiceFacade _credentialsService; + private readonly IDeserializer _decoratedDeserializer; + + public string CredentialFilePath { get; set; } + + + public XmlCredentialManagerUpgrader(CredentialServiceFacade credentialsService, string credentialFilePath, IDeserializer decoratedDeserializer) + { + if (credentialsService == null) + throw new ArgumentNullException(nameof(credentialsService)); + if (decoratedDeserializer == null) + throw new ArgumentNullException(nameof(decoratedDeserializer)); + + _credentialsService = credentialsService; + CredentialFilePath = credentialFilePath; + _decoratedDeserializer = decoratedDeserializer; + } + + public ConnectionTreeModel Deserialize(string serializedData) + { + var serializedDataAsXDoc = EnsureConnectionXmlElementsHaveIds(serializedData); + var upgradeMap = UpgradeUserFilesForCredentialManager(serializedDataAsXDoc); + var serializedDataWithIds = $"{serializedDataAsXDoc.Declaration}{serializedDataAsXDoc}"; + + var connectionTreeModel = _decoratedDeserializer.Deserialize(serializedDataWithIds); + + if (upgradeMap != null) + ApplyCredentialMapping(upgradeMap, connectionTreeModel.GetRecursiveChildList()); + + return connectionTreeModel; + } + + private XDocument EnsureConnectionXmlElementsHaveIds(string serializedData) + { + var xdoc = XDocument.Parse(serializedData); + xdoc.Declaration = new XDeclaration("1.0", "utf-8", null); + var adapter = new ConfConsEnsureConnectionsHaveIds(); + adapter.EnsureElementsHaveIds(xdoc); + return xdoc; + } + + public static decimal? GetVersionFromConfiguration(XDocument xdoc) + { + var versionString = xdoc.Root?.Attribute("ConfVersion")?.Value; + return versionString != null + ? (decimal?)decimal.Parse(versionString, CultureInfo.InvariantCulture) + : null; + } + + public Dictionary UpgradeUserFilesForCredentialManager(XDocument xdoc) + { + if ((GetVersionFromConfiguration(xdoc) ?? 0) >= 2.7m) + { + return null; + } + + var cryptoProvider = new CryptoProviderFactoryFromXml(xdoc.Root).Build(); + var encryptedValue = xdoc.Root?.Attribute("Protected")?.Value; + var auth = new PasswordAuthenticator(cryptoProvider, encryptedValue, () => MiscTools.PasswordDialog("", false)); + if (!auth.Authenticate(Runtime.EncryptionKey)) + throw new Exception("Could not authenticate"); + + var newCredRepoKey = auth.LastAuthenticatedPassword; + + var credentialHarvester = new CredentialHarvester(); + var harvestedCredentials = credentialHarvester.Harvest(xdoc, newCredRepoKey); + + var newCredentialRepository = BuildXmlCredentialRepo(newCredRepoKey); + + AddHarvestedCredentialsToRepo(harvestedCredentials, newCredentialRepository); + newCredentialRepository.SaveCredentials(newCredRepoKey); + + _credentialsService.AddRepository(newCredentialRepository); + return credentialHarvester.ConnectionToCredentialMap; + } + + private ICredentialRepository BuildXmlCredentialRepo(SecureString newCredRepoKey) + { + var cryptoFromSettings = new CryptoProviderFactoryFromSettings(); + var credRepoSerializer = new XmlCredentialPasswordEncryptorDecorator( + cryptoFromSettings.Build(), + new XmlCredentialRecordSerializer()); + var credRepoDeserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()); + + var xmlRepoFactory = new XmlCredentialRepositoryFactory(credRepoSerializer, credRepoDeserializer); + var newRepo = xmlRepoFactory.Build( + new CredentialRepositoryConfig + { + Source = CredentialFilePath, + Title = "Converted Credentials", + TypeName = "Xml", + Key = newCredRepoKey + } + ); + newRepo.LoadCredentials(newCredRepoKey); + return newRepo; + } + + private void AddHarvestedCredentialsToRepo(IEnumerable harvestedCredentials, ICredentialRepository repo) + { + foreach (var credential in harvestedCredentials) + repo.CredentialRecords.Add(credential); + } + + public void ApplyCredentialMapping(IDictionary map, IEnumerable connectionRecords) + { + foreach (var connectionInfo in connectionRecords) + { + Guid id; + Guid.TryParse(connectionInfo.ConstantID, out id); + if (map.ContainsKey(id)) + connectionInfo.CredentialRecordId = map[id].Id.Maybe(); + } + } + } +} \ No newline at end of file diff --git a/mRemoteV1/Connection/AbstractConnectionRecord.cs b/mRemoteV1/Connection/AbstractConnectionRecord.cs index cefa53b7..25ec7639 100644 --- a/mRemoteV1/Connection/AbstractConnectionRecord.cs +++ b/mRemoteV1/Connection/AbstractConnectionRecord.cs @@ -141,7 +141,7 @@ namespace mRemoteNG.Connection [LocalizedAttributes.LocalizedCategory("strCategoryConnection", 2), LocalizedAttributes.LocalizedDisplayName("strPropertyNameUsername"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionUsername")] - [Obsolete("Use the CredentialRecordId property")] + [Obsolete("Use the CredentialRecord property")] [Browsable(false)] public virtual string Username { @@ -153,7 +153,7 @@ namespace mRemoteNG.Connection LocalizedAttributes.LocalizedDisplayName("strPropertyNamePassword"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionPassword"), PasswordPropertyText(true)] - [Obsolete("Use the CredentialRecordId property")] + [Obsolete("Use the CredentialRecord property")] [Browsable(false)] public virtual string Password { @@ -164,7 +164,7 @@ namespace mRemoteNG.Connection [LocalizedAttributes.LocalizedCategory("strCategoryConnection", 2), LocalizedAttributes.LocalizedDisplayName("strPropertyNameDomain"), LocalizedAttributes.LocalizedDescription("strPropertyDescriptionDomain")] - [Obsolete("Use the CredentialRecordId property")] + [Obsolete("Use the CredentialRecord property")] [Browsable(false)] public string Domain { diff --git a/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.Designer.cs b/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.Designer.cs new file mode 100644 index 00000000..24e30c07 --- /dev/null +++ b/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.Designer.cs @@ -0,0 +1,349 @@ +namespace mRemoteNG.UI.Forms +{ + sealed partial class CompositeCredentialRepoUnlockerForm + { + /// + /// 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(CompositeCredentialRepoUnlockerForm)); + this.objectListViewRepos = new Controls.Base.NGListView(); + this.olvColumnName = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); + this.olvColumnStatusIcon = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); + this.labelUnlocking = new Controls.Base.NGLabel(); + this.labelPassword = new Controls.Base.NGLabel(); + this.buttonUnlock = new Controls.Base.NGButton(); + this.buttonClose = new Controls.Base.NGButton(); + this.labelRepoTitle = new Controls.Base.NGLabel(); + this.textBoxType = new Controls.Base.NGTextBox(); + this.textBoxTitle = new Controls.Base.NGTextBox(); + this.labelRepoType = new Controls.Base.NGLabel(); + this.textBoxSource = new Controls.Base.NGTextBox(); + this.labelRepoSource = new Controls.Base.NGLabel(); + this.textBoxId = new Controls.Base.NGTextBox(); + this.labelId = new Controls.Base.NGLabel(); + this.labelPasswordError = new Controls.Base.NGLabel(); + this.imgPasswordError = new System.Windows.Forms.PictureBox(); + this.imgUnlocked = new System.Windows.Forms.PictureBox(); + this.labelUnlocked = new Controls.Base.NGLabel(); + this.secureTextBoxPassword = new mRemoteNG.UI.Controls.SecureTextBox(); + ((System.ComponentModel.ISupportInitialize)(this.objectListViewRepos)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.imgPasswordError)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.imgUnlocked)).BeginInit(); + this.SuspendLayout(); + // + // objectListViewRepos + // + this.objectListViewRepos.AllColumns.Add(this.olvColumnName); + this.objectListViewRepos.AllColumns.Add(this.olvColumnStatusIcon); + this.objectListViewRepos.CellEditUseWholeCell = false; + this.objectListViewRepos.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.olvColumnName, + this.olvColumnStatusIcon}); + this.objectListViewRepos.Cursor = System.Windows.Forms.Cursors.Default; + this.objectListViewRepos.Dock = System.Windows.Forms.DockStyle.Left; + this.objectListViewRepos.EmptyListMsg = "No Credential Repositories Found"; + this.objectListViewRepos.HasCollapsibleGroups = false; + this.objectListViewRepos.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.objectListViewRepos.HideSelection = false; + this.objectListViewRepos.Location = new System.Drawing.Point(0, 0); + this.objectListViewRepos.MultiSelect = false; + this.objectListViewRepos.Name = "objectListViewRepos"; + this.objectListViewRepos.SelectAllOnControlA = false; + this.objectListViewRepos.ShowGroups = false; + this.objectListViewRepos.Size = new System.Drawing.Size(175, 272); + this.objectListViewRepos.TabIndex = 3; + this.objectListViewRepos.UseCompatibleStateImageBehavior = false; + this.objectListViewRepos.UseOverlays = false; + this.objectListViewRepos.View = System.Windows.Forms.View.Details; + this.objectListViewRepos.SelectionChanged += new System.EventHandler(this.objectListViewRepos_SelectionChanged); + // + // olvColumnName + // + this.olvColumnName.FillsFreeSpace = true; + this.olvColumnName.Groupable = false; + this.olvColumnName.Hideable = false; + this.olvColumnName.IsEditable = false; + this.olvColumnName.Searchable = false; + this.olvColumnName.Sortable = false; + // + // olvColumnStatusIcon + // + this.olvColumnStatusIcon.Groupable = false; + this.olvColumnStatusIcon.IsEditable = false; + this.olvColumnStatusIcon.Searchable = false; + this.olvColumnStatusIcon.Sortable = false; + this.olvColumnStatusIcon.Text = ""; + this.olvColumnStatusIcon.Width = 20; + // + // labelUnlocking + // + this.labelUnlocking.AutoSize = true; + this.labelUnlocking.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.labelUnlocking.Location = new System.Drawing.Point(181, 9); + this.labelUnlocking.Name = "labelUnlocking"; + this.labelUnlocking.Size = new System.Drawing.Size(77, 16); + this.labelUnlocking.TabIndex = 1; + this.labelUnlocking.Text = "Unlocking"; + // + // labelPassword + // + this.labelPassword.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.labelPassword.AutoSize = true; + this.labelPassword.Location = new System.Drawing.Point(190, 174); + this.labelPassword.Name = "labelPassword"; + this.labelPassword.Size = new System.Drawing.Size(53, 13); + this.labelPassword.TabIndex = 2; + this.labelPassword.Text = "Password"; + // + // buttonUnlock + // + this.buttonUnlock.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonUnlock.Location = new System.Drawing.Point(386, 237); + this.buttonUnlock.Name = "buttonUnlock"; + this.buttonUnlock.Size = new System.Drawing.Size(75, 23); + this.buttonUnlock.TabIndex = 1; + this.buttonUnlock.Text = "Unlock"; + this.buttonUnlock.UseVisualStyleBackColor = true; + this.buttonUnlock.Click += new System.EventHandler(this.buttonUnlock_Click); + // + // buttonClose + // + this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonClose.Location = new System.Drawing.Point(467, 237); + this.buttonClose.Name = "buttonClose"; + this.buttonClose.Size = new System.Drawing.Size(75, 23); + this.buttonClose.TabIndex = 2; + this.buttonClose.Text = "Close"; + this.buttonClose.UseVisualStyleBackColor = true; + this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click); + // + // labelRepoTitle + // + this.labelRepoTitle.AutoSize = true; + this.labelRepoTitle.Location = new System.Drawing.Point(190, 64); + this.labelRepoTitle.Name = "labelRepoTitle"; + this.labelRepoTitle.Size = new System.Drawing.Size(27, 13); + this.labelRepoTitle.TabIndex = 6; + this.labelRepoTitle.Text = "Title"; + // + // textBoxType + // + this.textBoxType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxType.Location = new System.Drawing.Point(237, 90); + this.textBoxType.Name = "textBoxType"; + this.textBoxType.ReadOnly = true; + this.textBoxType.Size = new System.Drawing.Size(306, 20); + this.textBoxType.TabIndex = 9; + this.textBoxType.TabStop = false; + // + // textBoxTitle + // + this.textBoxTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxTitle.Location = new System.Drawing.Point(237, 64); + this.textBoxTitle.Name = "textBoxTitle"; + this.textBoxTitle.ReadOnly = true; + this.textBoxTitle.Size = new System.Drawing.Size(306, 20); + this.textBoxTitle.TabIndex = 8; + this.textBoxTitle.TabStop = false; + // + // labelRepoType + // + this.labelRepoType.AutoSize = true; + this.labelRepoType.Location = new System.Drawing.Point(190, 90); + this.labelRepoType.Name = "labelRepoType"; + this.labelRepoType.Size = new System.Drawing.Size(31, 13); + this.labelRepoType.TabIndex = 7; + this.labelRepoType.Text = "Type"; + // + // textBoxSource + // + this.textBoxSource.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.textBoxSource.Location = new System.Drawing.Point(237, 116); + this.textBoxSource.Multiline = true; + this.textBoxSource.Name = "textBoxSource"; + this.textBoxSource.ReadOnly = true; + this.textBoxSource.Size = new System.Drawing.Size(306, 46); + this.textBoxSource.TabIndex = 11; + this.textBoxSource.TabStop = false; + // + // labelRepoSource + // + this.labelRepoSource.AutoSize = true; + this.labelRepoSource.Location = new System.Drawing.Point(190, 116); + this.labelRepoSource.Name = "labelRepoSource"; + this.labelRepoSource.Size = new System.Drawing.Size(41, 13); + this.labelRepoSource.TabIndex = 10; + this.labelRepoSource.Text = "Source"; + // + // textBoxId + // + this.textBoxId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxId.Location = new System.Drawing.Point(237, 38); + this.textBoxId.Name = "textBoxId"; + this.textBoxId.ReadOnly = true; + this.textBoxId.Size = new System.Drawing.Size(305, 20); + this.textBoxId.TabIndex = 13; + this.textBoxId.TabStop = false; + // + // labelId + // + this.labelId.AutoSize = true; + this.labelId.Location = new System.Drawing.Point(191, 38); + this.labelId.Name = "labelId"; + this.labelId.Size = new System.Drawing.Size(18, 13); + this.labelId.TabIndex = 12; + this.labelId.Text = "ID"; + // + // labelPasswordError + // + this.labelPasswordError.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.labelPasswordError.AutoSize = true; + this.labelPasswordError.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.labelPasswordError.ForeColor = System.Drawing.Color.DarkRed; + this.labelPasswordError.ImageAlign = System.Drawing.ContentAlignment.TopLeft; + this.labelPasswordError.Location = new System.Drawing.Point(191, 213); + this.labelPasswordError.Name = "labelPasswordError"; + this.labelPasswordError.Size = new System.Drawing.Size(115, 13); + this.labelPasswordError.TabIndex = 14; + this.labelPasswordError.Text = "Incorrect password"; + this.labelPasswordError.Visible = false; + // + // imgPasswordError + // + this.imgPasswordError.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.imgPasswordError.Image = global::mRemoteNG.Resources.exclamation; + this.imgPasswordError.Location = new System.Drawing.Point(176, 192); + this.imgPasswordError.Name = "imgPasswordError"; + this.imgPasswordError.Size = new System.Drawing.Size(16, 16); + this.imgPasswordError.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.imgPasswordError.TabIndex = 15; + this.imgPasswordError.TabStop = false; + this.imgPasswordError.Visible = false; + // + // imgUnlocked + // + this.imgUnlocked.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.imgUnlocked.Image = global::mRemoteNG.Resources.tick; + this.imgUnlocked.Location = new System.Drawing.Point(188, 241); + this.imgUnlocked.Name = "imgUnlocked"; + this.imgUnlocked.Size = new System.Drawing.Size(16, 16); + this.imgUnlocked.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.imgUnlocked.TabIndex = 16; + this.imgUnlocked.TabStop = false; + this.imgUnlocked.Visible = false; + // + // labelUnlocked + // + this.labelUnlocked.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.labelUnlocked.AutoSize = true; + this.labelUnlocked.Location = new System.Drawing.Point(204, 242); + this.labelUnlocked.Name = "labelUnlocked"; + this.labelUnlocked.Size = new System.Drawing.Size(174, 13); + this.labelUnlocked.TabIndex = 17; + this.labelUnlocked.Text = "The selected repository is unlocked"; + this.labelUnlocked.Visible = false; + // + // secureTextBoxPassword + // + this.secureTextBoxPassword.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.secureTextBoxPassword.Location = new System.Drawing.Point(193, 190); + this.secureTextBoxPassword.Name = "secureTextBoxPassword"; + this.secureTextBoxPassword.Size = new System.Drawing.Size(348, 20); + this.secureTextBoxPassword.TabIndex = 0; + this.secureTextBoxPassword.UseSystemPasswordChar = true; + // + // CompositeCredentialRepoUnlockerForm + // + this.AcceptButton = this.buttonUnlock; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.buttonClose; + this.ClientSize = new System.Drawing.Size(554, 272); + this.Controls.Add(this.labelUnlocked); + this.Controls.Add(this.imgUnlocked); + this.Controls.Add(this.imgPasswordError); + this.Controls.Add(this.labelPasswordError); + this.Controls.Add(this.textBoxId); + this.Controls.Add(this.labelId); + this.Controls.Add(this.textBoxSource); + this.Controls.Add(this.labelRepoSource); + this.Controls.Add(this.secureTextBoxPassword); + this.Controls.Add(this.textBoxType); + this.Controls.Add(this.buttonUnlock); + this.Controls.Add(this.textBoxTitle); + this.Controls.Add(this.buttonClose); + this.Controls.Add(this.labelPassword); + this.Controls.Add(this.labelRepoType); + this.Controls.Add(this.objectListViewRepos); + this.Controls.Add(this.labelUnlocking); + this.Controls.Add(this.labelRepoTitle); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(500, 310); + this.Name = "CompositeCredentialRepoUnlockerForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Unlock Credential Repository"; + this.Shown += new System.EventHandler(this.CompositeCredentialRepoUnlockerForm_Shown); + ((System.ComponentModel.ISupportInitialize)(this.objectListViewRepos)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.imgPasswordError)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.imgUnlocked)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.Base.NGListView objectListViewRepos; + private Controls.Base.NGLabel labelUnlocking; + private Controls.Base.NGLabel labelPassword; + private Controls.SecureTextBox secureTextBoxPassword; + private Controls.Base.NGButton buttonUnlock; + private Controls.Base.NGButton buttonClose; + private Controls.Base.NGLabel labelRepoTitle; + private Controls.Base.NGTextBox textBoxType; + private Controls.Base.NGTextBox textBoxTitle; + private Controls.Base.NGLabel labelRepoType; + private BrightIdeasSoftware.OLVColumn olvColumnName; + private Controls.Base.NGTextBox textBoxSource; + private Controls.Base.NGLabel labelRepoSource; + private Controls.Base.NGTextBox textBoxId; + private Controls.Base.NGLabel labelId; + private BrightIdeasSoftware.OLVColumn olvColumnStatusIcon; + private Controls.Base.NGLabel labelPasswordError; + private System.Windows.Forms.PictureBox imgPasswordError; + private System.Windows.Forms.PictureBox imgUnlocked; + private Controls.Base.NGLabel labelUnlocked; + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.cs b/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.cs new file mode 100644 index 00000000..68da119a --- /dev/null +++ b/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.cs @@ -0,0 +1,150 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; + +namespace mRemoteNG.UI.Forms +{ + public sealed partial class CompositeCredentialRepoUnlockerForm : Form + { + private readonly CompositeRepositoryUnlocker _repositoryUnlocker; + + public CompositeCredentialRepoUnlockerForm(CompositeRepositoryUnlocker repositoryUnlocker) + { + if (repositoryUnlocker == null) + throw new ArgumentNullException(nameof(repositoryUnlocker)); + + _repositoryUnlocker = repositoryUnlocker; + InitializeComponent(); + SetupListView(); + ApplyLanguage(); + ApplyTheme(); + Themes.ThemeManager.getInstance().ThemeChanged += ApplyTheme; + } + + private void ApplyTheme() + { + if (!Themes.ThemeManager.getInstance().ThemingActive) return; + BackColor = Themes.ThemeManager.getInstance().ActiveTheme.ExtendedPalette.getColor("Dialog_Background"); + ForeColor = Themes.ThemeManager.getInstance().ActiveTheme.ExtendedPalette.getColor("Dialog_Foreground"); + } + + private void buttonUnlock_Click(object sender, EventArgs e) + { + try + { + _repositoryUnlocker.Unlock(secureTextBoxPassword.SecString); + SelectNextLockedRepo(); + } + catch (Exception exception) + { + Console.WriteLine(exception); + ShowPasswordError(true); + } + finally + { + secureTextBoxPassword.Clear(); + } + } + + private void buttonClose_Click(object sender, EventArgs e) + { + Close(); + } + + private void SelectNextLockedRepo() + { + _repositoryUnlocker.SelectNextLockedRepository(); + objectListViewRepos.SelectedObject = _repositoryUnlocker.SelectedRepository; + } + + private void objectListViewRepos_SelectionChanged(object sender, EventArgs e) + { + objectListViewRepos.RefreshObjects(_repositoryUnlocker.Repositories.ToList()); + var selectedRepo = objectListViewRepos.SelectedObject as ICredentialRepository; + _repositoryUnlocker.SelectedRepository = selectedRepo; + ShowRepoDetails(selectedRepo); + ShowPasswordError(false); + UnlockRequired(!selectedRepo?.IsLoaded ?? false); + } + + private void ShowRepoDetails(ICredentialRepository repo) + { + textBoxId.Text = repo?.Config.Id.ToString() ?? ""; + textBoxTitle.Text = repo?.Config.Title ?? ""; + textBoxType.Text = repo?.Config.TypeName ?? ""; + textBoxSource.Text = repo?.Config.Source ?? ""; + } + + private void UnlockRequired(bool isUnlockRequired) + { + buttonUnlock.Enabled = isUnlockRequired; + secureTextBoxPassword.Enabled = isUnlockRequired; + imgUnlocked.Visible = objectListViewRepos.SelectedObject != null && !isUnlockRequired; + labelUnlocked.Visible = objectListViewRepos.SelectedObject != null && !isUnlockRequired; + } + + private void ShowPasswordError(bool shouldErrorBeActive) + { + labelPasswordError.Visible = shouldErrorBeActive; + imgPasswordError.Visible = shouldErrorBeActive; + secureTextBoxPassword.BackColor = shouldErrorBeActive ? Color.MistyRose : SystemColors.Window; + } + + #region Setup + private void SetupListView() + { + olvColumnName.AspectGetter = rowObject => ((ICredentialRepository)rowObject).Config.Title; + olvColumnStatusIcon.AspectGetter = rowObject => string.Empty; + olvColumnStatusIcon.ImageGetter = rowObject => ((ICredentialRepository)rowObject).IsLoaded ? "unlocked" : "locked"; + objectListViewRepos.SmallImageList = SetupImageList(); + } + + private ImageList SetupImageList() + { + var imageList = new ImageList + { + ColorDepth = ColorDepth.Depth32Bit, + ImageSize = new Size(16, 16), + TransparentColor = Color.Transparent + }; + imageList.Images.Add(Resources.arrow_left); + imageList.Images.SetKeyName(0, "unlocking"); + imageList.Images.Add(Resources.tick); + imageList.Images.SetKeyName(1, "unlocked"); + imageList.Images.Add(Resources._lock); + imageList.Images.SetKeyName(2, "locked"); + + return imageList; + } + + private void CompositeCredentialRepoUnlockerForm_Shown(object sender, EventArgs e) + { + PopulateListView(); + objectListViewRepos.SelectedIndex = 0; + } + + private void PopulateListView() + { + objectListViewRepos.SetObjects(_repositoryUnlocker.Repositories); + } + + private void ApplyLanguage() + { + Text = Language.UnlockCredentialRepository; + labelUnlocking.Text = Language.Unlocking; + labelId.Text = Language.strID; + labelRepoTitle.Text = Language.strTitle; + labelRepoType.Text = Language.strType; + labelRepoSource.Text = Language.Source; + labelPassword.Text = Language.strTitlePassword; + labelPasswordError.Text = Language.IncorrectPassword; + labelUnlocked.Text = Language.RepositoryIsUnlocked; + buttonUnlock.Text = Language.Unlock; + buttonClose.Text = Language.strButtonClose; + } + #endregion + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.resx b/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.resx new file mode 100644 index 00000000..46b52893 --- /dev/null +++ b/mRemoteV1/UI/Forms/CompositeCredentialRepoUnlockerForm.resx @@ -0,0 +1,1138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + AAABAA4AEBAQAAEABAAoAQAA5gAAADAwAAABAAgAqA4AAA4CAAAgIAAAAQAIAKgIAAC2EAAAGBgAAAEA + CADIBgAAXhkAABAQAAABAAgAaAUAACYgAAAwMAAAAQAYAKgcAACOJQAAICAAAAEAGACoDAAANkIAABgY + AAABABgASAcAAN5OAAAQEAAAAQAYAGgDAAAmVgAAAAAAAAEAIACVTwAAjlkAADAwAAABACAAqCUAACOp + AAAgIAAAAQAgAKgQAADLzgAAGBgAAAEAIACICQAAc98AABAQAAABACAAaAQAAPvoAAAoAAAAEAAAACAA + AAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArK2tAO7v8ADT09MATU1NAAoJCQDtYhwAJCMjADw7 + OwB0KQUAnlErAPKETQDAQQQAf39/AD4oHQAyFwkA231PAAdmZmZmZmZwdzMzMzMzM3dnd3d3d3d3dkZm + ZmZmZmZkRtbW3TNmc3ROuIiJDETMNE5YuIkgRsx0TqlbuRIADERO+vW3ESIgZEREREYSZgI0RERERxEi + ETRGZmZnIhEsZEZmZmZ3d2ZkZ3d3d3d3d3Z3MzMzMzMzdwdmZmZmZmZwgAHgBgAA4AYAAOAGAADgBgAA + 4AYAAOAGAADgBgAA4AYAAOAGAADgBgAA4AYAAOAGAADgBgAA4AYAAOAGgAHgBigAAAAwAAAAYAAAAAEA + CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQIAAAUFBQAJBQMADgcDAAoGBAAPBwQACwgGAAkJ + CQAOCgkADAwMABIHAgAdCwEAHw0DABkLBAAeDAQAEQ4MABEREQAVEhAAFBQUABgVFAAZGRkAHxsZAB0d + HQAhDQIAIQ8GACQQBgAlEgkAJxUMACoWDAA5FAIAPBQAACoYEAAtGhAAKRsUACscFAAgGxkAIR4dACge + GQAuIBkAPCQZACIiIgAmJiYAKCgoACwsLAAwMDAANTU1ADo6OgA+Pj4AQRcBAEIYAwBHGAAARBsGAEkY + AQBGHQkAVhwAAFkeAQBdHwAASSEMAF4gAQBEJBQASyQQAEAmGQBZKxQAWC4ZAGAgAABkIgAAYCQGAGwj + AABtJAAAYiYJAGQpDAByJwIAdScAAHcoAQB4KAAAfCkAAHctCABmLBAAbjUZAHg5GQBCQkIARENDAEVF + RQBJSUkATk5OAFBQUABaWloAXV1dAGFhYQBlZWUAaGhoAG5ubgBwcHAAdHR0AHl5eQB9engAfX19AIIr + AACFLAAAgS4FAIgtAACNLwAAjjECAIoyBgCPOQ0AkTAAAJUxAACQNAYAlDQFAJk0AQCdNAAAmDUEAJI2 + CQCUNwgAlTkLAJM5DACWOw4AmDsNAIM5FACEOhUAhj0ZAIg+GQCVPRAApTYAAKY5AwCqOAAArTkAAK4+ + BgCjPQoApD0JAKg9CAC1PAAAsz4EALk+AQC/PwAAk0EZAJdDGQCYQxkAmUQZAK5BCgC4QgcAvl0sAMFB + AADFQQAAwEQFAMhCAADNRAAAyUUEAM9IBQDSRgAA1UYAANhHAADXSAEA2UgAAN1KAADfTAMA0E0MAMJO + FADNVRoAz1gdANhVFADZXR4A4EoAAOVMAADqTwEA7FABAO1SBADuVAcA71cKAPBZDQDnWBAA8FsQAOVg + HgDxYBgA8WUfANBbIQDQXycA0GIsANBkLwDRZTAA8WYhAPJoIwDyaCQA8msoAPFvLQDycC8A83M0APN1 + NgDzdjgA83o9AIB8egCDf30A83xAAPR/RACFgH4A9IBGAPSETQD0iFIA9YtWAPWRXwD1lGMA9ZVlAPaX + aAD2nG8A96J5APekegD3pX0AgYGBAIaCgACEhIQAioqKAI6OjgCQkJAAlpaWAJmZmQCenp4AoaGhAKWl + pQCqqqoAra2tALGxsQC1tbUAubm5AL29vQDBwcEAxcXFAMnJyQDNzc0A0tLSANXV1QDa2toA3d3dAOLi + 4gDk5OQA6enpAO3t7QDx8fEA9fX1APj4+AAAAAAACAAvAA4AUAAVAHAAGwCQACEAsAAmAM8ALADwAD4R + /wBYMf8AcVH/AIxx/wCmkf8Av7H/ANrR/wD///8AAAAAAABUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAATVAAAAAAAAAAALQAAAggICAgICAgICAgICAgICAgICAgKCAgICAgICAgICAgICAgC + AAAtAAAAAAAVABMvVVVVVVVVVVVVVVVVVVVVVVVVVlVWVVVVVVVVVVVVVVVVVVVVLxMAFQAAAC0AKVRV + VFRVVFVUVVRVVFVUVVRVVFVUVFRUVFRVVFVUVVRVVFVUVVRVVFQpAC0AAAATU1NUVFRTVFNUU1RTVFNU + U1RUU1RUVFRUVFRTVFRUU1RUVFNUVFRTVFRTEwAAVAAuUlJSUlNSU1JTUlNSU1JTUlNSU1JTUlJSUlJS + UlJTUlNSU1JTUlNSU1JTLgBTEwIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMAIRAAgvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwgAAAIuLi4u + Li4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLgIAAAItLS0tLS0tLS0tLS0tLS0t + LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQIAAAIsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCws + LCwsLCwsLCwsLCwsLCwsLAIAAAIqKyorKysrKysrKysrKysrKysrKyorKysrKysrKysqKyorKysrKioq + KioqKwIAAAIpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKQIAAAIXFxcX + JSUlJRclJSUlJRcXJSUlJRcpKy0tLSspFxcXFxcXFyosLS0tLCkXFwIAAAIVFRUoeYuIQCQnUIuKTyYW + QIiLej5T2d7e3NZSFRUVFRUVKl7Z29ra1VEVFQIAAAATExR3f2tjdiM/ZDc1TTwiaTs7Qnhg4NrX09Rf + FRMTExMTL9lZWVhYYVsTEwIAAAERERJ7j39vjCFOcEhBaD0ggURERXvR4dvX1NXSERERERERVddZWFdY + 1lYREQAAAAAKChB0mJCEjR1Hf2VKcDocg0xMSXXD497a1tfQEQoKCgoKWdVbWVhY2C8KCgAAAAAICAlx + ppuTkRxGj3xqfTYbgGZlYnPA5uDd2dphCggICAgKYdNcW1pZ2SsICAAAAAACAgdsrKeklBpDmYd/hDQZ + hW9uanK/6OPg3NxhCAICAgIR2dBeXFta2RcCAgAAAAACAgVst66qlRk7pZqSjzINhn98b3K/6ubj399h + AgICAgpV39JhXlxc1goCAgAAAAAAAAFnvLq3nRg7rKijljEMj4+Gfm2/7Onm4uLcVVRVWNXh1tTSYV7Y + VQIAAAAAAAAAAAFmxMG9oR9Jtq+ppDgzmZaShmdg7ezp5uTm5ePj4d3a2dfV0tXUEQAAAAAAAAAAAAFm + x8bFuaKtvr23qpycpqSak0hf7e3t6+jm5ePi4N/e3dvY3eQtAAAAAAAAAAAAAAFmy8vKysrLycfEurWu + rqynlx5f7u3t7ezr6ujn5uXj4t/c2d7QCgAAAAAAAAAAAAFlzc7Ozs/PzszIxcG9u7irSgZf7u3t7e3t + 7ezs6+ro5+Tg3NneVgIAAAAAAAACAAI1jrO0tLS0s7OxsKCfnoE4CwJf7u7t7e3t5+jn5+jq7Ojl4NzZ + 2RECAAAAAAACAgIFDg8ZDxkPDQ0PDQ0PDgYCAgJf7u7t7u7WKioqKyou2ezn49/c3ysCAgAAAAAIAggI + AgICAggCCAIIAggCCAIIAghh7u7u7e5hCAICAgICVOzq5eHe4TAIAgAAAAAICAgICAoICAgKCAgICAoI + CggKCAph7u7u7u7QCggICggKV+3r6OTg41MICgAAAAIKChEKCgoRCgoKChEKCgoKCgoKChFh7u7u7u7h + XV1cXF3Q5u3t6ubi5C8KCgAAAAARERETERMRExETERMRERERERMRExPQ7+7u7u7v7u/u7+7u7e3t7Onm + 4CoREQIAAAITExMTExMTExMTExMTExUTExMTExXS7+/u7+7u7u7u7u7t7u3t7ezrXxUTFAAAAAIVFRUV + FRUVFRUVFRUVFRUVFRUVFRfS7+/u7u7v7u7u7u7u7u3t7e3bLBUVFQIAAAIlFyUXJRclFyUXKRclFyUX + KRclFylh7+/v7+7v7u/u7u7u7u7u6tYvKRclFwIAAAIpKSkpKSkpKSkpKSkpKSkpKSkpKSpU3uTk5OTk + 5OTk5OTj4NvQVSwpKSkpKQIAAAIrKysrKysrKysrKysrKysrKysrKysrLi8vLy8vLy8vLy8uLSwrKysr + KysrKwIAAAIsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLAIAAAgtLi0u + LS4tLi0uLS4tLi0uLS4tLi0uLS4tLi0uLS4tLi0uLS4tLi0uLS4tLQIAAAIuLi4uLi4uLi4uLi4uLi4u + Li4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLggAAAgvMC8wLzAvMC8wLzAvMC8wLzAvMC8wLzAvMC8w + LzAvMC8wLzAvMC8wLzAvLwIAEwIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMAITVAAuU1JSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlNSLgBUAAARU1RU + VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRTEwAAAC0AKVVUVVRVVFVUVVRVVFVU + VVRVVFVUVVRVVFVUVVRVVFVUVVRVVFVUVVQpAC0AAAAVABMvVVVVVVZWVlZWVlZWVlZWVlZWVlZWVlZW + VlZWVlZWVlZVVVVVLxMAFQAAAAAALQAAAggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKCAgC + AAAtAAAAAAAAAABUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATVAAAAAAA+AAAAAAf + ZmbgAAAAAAdmZsAAAAAAA2ZmgAAAAAABZmaAAAAAAAFmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAA + AAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAA + ZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAA + AAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAA + ZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAA + AAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmaAAAAAAAFmZoAAAAAAAWZmwAAAAAAD + ZmbgAAAAAAdmZvgAAAAAH2ZmKAAAACAAAABAAAAAAQAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAFBQUACQkJAA0NDQARCQUAFgoFABwKAQAdDAQAEhISABQUFAAaGhoAHRsaAB0dHQAhEAgAJRUNAD8W + AQAnGhMAJB0aACofGgA6JBoAISEhACQkJAAoKCgALi4uADMwLgAwMDAANjY2ADo6OgA9PT0AQRYBAEIZ + BABFHAgAUhsAAEkhDQBGJBMARykaAFIsGgBaLxoAYiAAAGUkBABnKAgAaiwNAHYnAAB6KgMAdy8LAGUu + EwBjMhoAaTQaAHE2GgBEREQASkpKAFNTUwBWU1IAVVVVAFlZWQBdXV0AaFlRAHJdUwB0X1QAd2JXAHRi + WgB6ZVsAYmJiAGVlZQBpaWkAbW1tAHFxcQB0dHQAenp6AHx8fACBLwYAjS8AAIQxCACANA4AiTMIAI82 + CgCQMwQAljUFAJkzAACcNAEAnTcEAJU4CgCeOwoApTcAAKQ4AgCqOQAAszsAALI8AAC1PQAAvj8AAL9A + AQC6SxQAwUAAAMVBAADNRAAA10cAANhIAADPWR4A4UwBAOZNAADjUAYA6lADAOhQBADvVwsA71gMAPFd + EwDxYxwA8WQdANteIADaYycA3GkwAN1zPgDyZyIA8m4sAPNzMwDzdTUA83Y4ANV3SADdekkA3XxLAPSD + SgD0h1AA9pZmAPaZagD2mmwAgoKCAIWFhQCIiIgAjY2NAJGRkQCWlpYAm5ubAJ2dnQCioqIApaWlAKmp + qQCtra0AsLCwALW1tQC4uLgAvr6+AMDAwADGxsYAyMjIAM3NzQDQ0NAA1dXVANnZ2QDc3NwA4eHhAOTk + 5ADr6+sA7e3tAPHx8QD19fUA/69xAP/BkQD/0rEA/+XRAP///wAAAAAALwMAAFAEAABwBgAAkAkAALAK + AADPDAAA8A4AAP8gEgD/PjEA/1xRAP96cQD/l5EA/7axAP/U0QD///8AAAAAAC8ADgBQABcAcAAhAJAA + KwCwADYAzwBAAPAASQD/EVoA/zFwAP9RhgD/cZwA/5GyAP+xyAD/0d8A////AAAAAAAvACAAUAA2AHAA + TACQAGIAsAB4AM8AjgDwAKQA/xGzAP8xvgD/UccA/3HRAP+R3AD/seUA/9HwAP///wAAAAAALAAvAEsA + UABpAHAAhwCQAKUAsADEAM8A4QDwAPAR/wDyMf8A9FH/APZx/wD3kf8A+bH/APvR/wD///8AAAAAABsA + LwAtAFAAPwBwAFIAkABjALAAdgDPAIgA8ACZEf8ApjH/ALRR/wDCcf8Az5H/ANyx/wDr0f8A////AAAA + AAAIAC8ADgBQABUAcAAbAJAAIQCwACYAzwAsAPAAPhH/AFgx/wBxUf8AjHH/AKaR/wC/sf8A2tH/AP// + /wAAAAAcAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHAAAAABDABQbHBwcHBwcHBwcHBwcHBwcHBwcHBwc + HBsUAUMAAAAbMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIaAAAcDDExMTExMTExMTExMTExMTExMTEx + MTExMTExMTEMHAIXHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBcCABYaGhoaGhoaGhsaGhoaGhoa + GhoaGhoaGhoaGhoaFgAAFRkZGRkZGRkZGRkXGRkZFxcZFxcZGRkXGRcXGRcVAAAMFhYWFhYWFhYWFhYW + FhYWFhYWFhYWFhYWFhYWFgwAAAoUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUCgAACQoSLzATES4w + IwskMCUYfoB+MgoKCgoZRH5+PwwIAAADCS1VR0kiKyAsEEgmRjyLg39/CQkJCUFANzd/FAMAAAIDKV5Y + SyFQKkoOTStMPY2GgYADAwMDRT83N38IAgAAAQIoZWBNH1pPTQ1UR087kYqFgQICAgKAQkA+fQIBAAAB + ASdqZ1AeY11UB1hTVTqVj4qBAQEBF4ZFQkFBAQEAAAAAJ3RxUh5pZVgHXVpYOZeUj45FRX+Jgn9FgRYA + AAAAAAAmeXhsW3NwZFhjYFw4mZiVkY+Ni4qIhIlBAQAAAAAAACZ8fHx8enlzcGtoTjSamZmYlpWTkY6K + hoQIAAAAAAAAHXV3d3d3b25tYVEHM5qZmZmVlpWVlpCKhj8BAAAAAQEBBQUFBQUFBQUEAQE1mpqZiQwM + ChWHlY6JgwEBAAABAgICAgICAgICAgICAjaampqHAgICAn+XkoyHAwEAAAIIAwMDAwMDAwMDAwMIN5qa + mpWEhISGlpmWkYUIAgAAAwkJCQkJCQkJCQkJCQk3mpqampqampmZmZmWQAkIAAAJCgoKCgoKCgoKCgoK + DD+ampqampqampqamYYVCgkAAAoVFRUVFRUVFRUVFRUVMpGUlJSUlJOTkIhAFhUVCgAAFBYWFhYWFhYW + FhYWFhYWGRoaGhoaGhoXFxYWFhYMAAAVGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRUAABYaGhob + GhsaGxobGhsaGxobGhsaGxobGhsaGhsaFgACFxwcMRwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcMRwXAhwM + MTExMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTExMQwcAAAaMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIb + AAAAQwEUGhsbGxsbGxsbGxsbGxsbGxsbGxsbGxwaFABDAAAAABwCAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAIcAAAA4AAAB4AAAAGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACAAAABgAAAAeAAAAcoAAAAGAAAADAAAAABAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUF + BQAICAgADg4OABcJAwAdDAMAFhYWABgYGAAfHx8AMBICADQWBwA/FwIAORwOAC0eFgAgHx8AISAfADQg + FgA8IxYAICAgACcnJwApKSkALS0tADIyMgA0NDQAOjo6ADw8PABDGwcASRkAAE8fBwBIIQ4AVCUOAGMn + CQByJgAAfywCAHEvDgB5MQwAdjIQAHc1FAByNhkAeDgZAEA/PwBOQz0AYUc6AH5TPQBDQ0MARUVFAEhI + SABMTEwAXFxcAGJiYgBkZGQAampqAG5ubgB3d3cAenp6AHx8fACMLgAAjzEBAI0yBACCNg8AmDMBAJg7 + DACbPg8AozgCAKY4AQCpOAAArj0EALo+AAC/QAAAilg/AMNAAADKQwAAz0UBAMhGBQDeSgAA6VEFAO1Z + DgDgVhEA8WYhAPJrKADybiwA7HAyAItbQwCOX0gA9IFIAPSESwD2mGkA9p1xAIGBgQCGhoYAioqKAJOT + kwCYmJgAoaGhAKWlpQCpqakArq6uALKysgC2trYAurq6AMHBwQDHx8cAycnJAM3NzQDT09MA1dXVANvb + 2wDd3d0A4eHhAOTk5ADo6OgA7e3tAPLy8gD19fUAQFAAAFpwAAB0kAAAjrAAAKnPAADC8AAA0f8RANj/ + MQDe/1EA4/9xAOn/kQDv/7EA9v/RAP///wAAAAAALyYAAFBBAABwWwAAkHQAALCOAADPqQAA8MMAAP/S + EQD/2DEA/91RAP/kcQD/6pEA//CxAP/20QD///8AAAAAAC8UAABQIgAAcDAAAJA+AACwTQAAz1sAAPBp + AAD/eREA/4oxAP+dUQD/r3EA/8GRAP/SsQD/5dEA////AAAAAAAvAwAAUAQAAHAGAACQCQAAsAoAAM8M + AADwDgAA/yASAP8+MQD/XFEA/3pxAP+XkQD/trEA/9TRAP///wAAAAAALwAOAFAAFwBwACEAkAArALAA + NgDPAEAA8ABJAP8RWgD/MXAA/1GGAP9xnAD/kbIA/7HIAP/R3wD///8AAAAAAC8AIABQADYAcABMAJAA + YgCwAHgAzwCOAPAApAD/EbMA/zG+AP9RxwD/cdEA/5HcAP+x5QD/0fAA////AAAAAAAsAC8ASwBQAGkA + cACHAJAApQCwAMQAzwDhAPAA8BH/APIx/wD0Uf8A9nH/APeR/wD5sf8A+9H/AP///wAAAAAAGwAvAC0A + UAA/AHAAUgCQAGMAsAB2AM8AiADwAJkR/wCmMf8AtFH/AMJx/wDPkf8A3LH/AOvR/wD///8AAAAAAAgA + LwAOAFAAFQBwABsAkAAhALAAJgDPACwA8AA+Ef8AWDH/AHFR/wCMcf8AppH/AL+x/wDa0f8A////AAAA + GAIBAQEBAQEBAQEBAQEBAQEBAhgAAAADGC8vLy8vLy8vLy8vLy8vLy8vLxgDABkXLS0tLS0tLi0uLS0t + LS0tLS0tLS0XGAEZGRkZGRkZGRkZGRkoGRkZGRkZGRkZAgEXFxcXFxcXFxcXFxcWFhcXFxcXFhcWAQAU + FBQUFBQUFBQUFBQUFBQUFBQUFBQUAAASDw8PDw8PDw8PEhMTExISEhITExMSAAAGET07ECMiDSQjKmBd + NgcGBjBYWDQHAAADHkZCHTkhDDogU2NcWgMDAzUxMDQDAAACHEtIGkRACj85UmdgWwICA1o0MjECAAAB + G1BNGktICUNBRWxlXxQVM1w3NxkAAAAAG1VRPlBMPEpHK3BtaWdkYV5cWgEAAAAAG1dXV1ZUUE5JKXFw + b25saWVgXQcAAAABBCYnJycnJB8FKHFwalhYWWlnYDIBAAACAgICAgICAgICLHFxYwICAl1rZFkCAAAD + AwMDAwMDAwMDL3Fxb2FhYm5vaTYDAAAHBwcHBwcHBwcHL3FxcXFxcXBwaxcGAAASEhISEhISEhISLWtt + bGxtbGhfLhISAQEUFBQUFBQUFBQUFBYWFhYWFhUVFBQUAAEXFxcXFxcXFxcXFxcXFxcXFxcXFxcXAQIZ + KBkoGSgZKBkoGSgZKBkoGSgZGSgZAhgXLS0uLS4tLi0uLS4tLi0uLS4tLi0XGAADGC8vLy8vLy8vLy8v + Ly8vLy8vLxgDAAAAGAIBAQEBAQEBAQEBAQEBAQEBAhgAAMAAAwCAAAEAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACAAAEAwAADACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgYGAAoI + BwAPCQcACwsLABAKBwAQEBAAFRUVAB0dHQAoDgEAMREAADYWBgAhHx0APB8QACghHQAgICAALCwsADQo + IgAxMTEANzc3ADo6OgBeIgUAUyQNAEEpHQBGKx0AbykGAGgsDgB4KAEAfSwDAFc5KQBCQkIARkZGAEhI + SABMTEwAUVFRAFRUVABdXV0AampqAG5ubgCZNQIAnT8QAKM3AQCuOgAAgEovAIxMLACVTSkAwEAAANBF + AADRUxQA6FQKAO9iHADmaisA83tAAOmCTwDpiloAiYmJAJqamgCenp4AoKCgAKmpqQCurq4Av7+/AMPD + wwDKysoAz8/PANnZ2QDi4uIA5ubmAOnp6QDu7u4A8fHxAPX19QAR/1sAMf9xAFH/hwBx/50Akf+yALH/ + yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAAIP8SAD3/MQBb/1EAef9xAJj/ + kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAAZ/AAAHj/EQCK/zEAnP9RAK7/ + cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAAqc8AAMLwAADR/xEA2P8xAN7/ + UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAAsI4AAM+pAADwwwAA/9IRAP/Y + MQD/3VEA/+RxAP/qkQD/8LEA//bRAP///wAAAAAALxQAAFAiAABwMAAAkD4AALBNAADPWwAA8GkAAP95 + EQD/ijEA/51RAP+vcQD/wZEA/9KxAP/l0QD///8AAAAAAC8DAABQBAAAcAYAAJAJAACwCgAAzwwAAPAO + AAD/IBIA/z4xAP9cUQD/enEA/5eRAP+2sQD/1NEA////AAAAAAAvAA4AUAAXAHAAIQCQACsAsAA2AM8A + QADwAEkA/xFaAP8xcAD/UYYA/3GcAP+RsgD/scgA/9HfAP///wAAAAAALwAgAFAANgBwAEwAkABiALAA + eADPAI4A8ACkAP8RswD/Mb4A/1HHAP9x0QD/kdwA/7HlAP/R8AD///8AAAAAACwALwBLAFAAaQBwAIcA + kAClALAAxADPAOEA8ADwEf8A8jH/APRR/wD2cf8A95H/APmx/wD70f8A////AAAAAAAbAC8ALQBQAD8A + cABSAJAAYwCwAHYAzwCIAPAAmRH/AKYx/wC0Uf8AwnH/AM+R/wDcsf8A69H/AP///wAAAAAACAAvAA4A + UAAVAHAAGwCQACEAsAAmAM8ALADwAD4R/wBYMf8AcVH/AIxx/wCmkf8Av7H/ANrR/wD///8AAB4PCA8I + DwgICAgICAgeAB4eICAgICAgICAgICAgHh4HFBQUFBQUFBQUFBQUFBQHBhAQEBAQEBAQEBAQEBAQBgQP + GA4YDhcRIx8ICBQiEgQBDSoaGxYcKzw3BwYmJCIBAAsxGS4VKSw/OQIGNyUeAAAKNCgyJy8tREA6Ozg3 + BAAACTY2NTMwHUZGQ0E/Ow8AAAIDBQUFBBBHQAcHPT4jAQEGBgYGBgYTR0Y/P0VCIQEECA8ICAgIFENE + RERBNw8EBhIQEhASEBISEhISEBIQBgcUFBQUFBQUFBQUFBQUFAceHiAgICAgICAgICAgIB4eAB4ICAgI + CAgICAgICAgeAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIABAAAoAAAAMAAAAGAAAAABABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAASEhIFBQUAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBFBQU + SEhIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExAAAAAAAABQUFCQkJCQkJCQkJCQkJCQkJCQkJCQkJ + CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJ + CQkJCQkJCQkJCQkJCQkJCQkJCQkJBQUFAAAAAAAAMTExAAAAAAAAAAAAAAAAAAAAFxcXAAAAFBQUOzs7 + TExMTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5O + Tk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTExMOzs7FBQUAAAAFxcX + AAAAAAAAAAAAMDAwAAAAICAgSkpKS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tL + S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tL + S0tLS0tLS0tLS0tLS0tLSkpKICAgAAAAMDAwAAAAAAAAAAAAEhISRkZGR0dHR0dHR0dHR0dHR0dHR0dH + R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dH + R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHRkZGEhISAAAAAAAASEhIAAAA + MzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0ND + Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0ND + Q0NDQ0NDQ0NDMzMzAAAAR0dHExMTBAQEPT09Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/ + Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/ + Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/PT09BAQEEhISAgICBwcHOjo6Ojo6Ojo6Ojo6 + Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6 + Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6 + BwcHAQEBAAAABgYGNTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1 + NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1 + NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1BgYGAAAAAAAABgYGMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBgYGAAAAAAAABQUF + LCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCws + LCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCws + LCwsLCwsLCwsLCwsBQUFAAAAAAAABAQEJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycn + JycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycn + JycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnBAQEAAAAAAAABAQEIiIiIiIiIiIiIiIi + IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi + IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi + BAQEAAAAAAAAAwMDHR0dHR0dHR0dHh0dIR4dIh8dIR8dHx4dHR0dHh0dIB4dIh8dIh8dIB4dHR0dHR0d + Hx4dIR8dIh8dIR4dHh0dICAgKioqLy8vLy8vLy8vKioqICAgHR0dHR0dHR0dHR0dHR0dHR0dHh4eJSUl + LS0tLy8vLy8vLy8vKioqICAgHR0dHR0dAwMDAAAAAAAAAwMDGRkZGRkZGhkZPCQZhj0ZmUMZk0IZWS4Z + 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 + 29vb4+Pj7Ozs3t7ezs7Ovr6+rq6uoqKioaGhEBAQAgICAgICAAAAAAAAAAAAAAAABAQEBAQEBAQECgYE + GgsEHw0EHw0EHw0EHw0EHw0EHw0EHw0EHw0EHw0EHw0EHgwEGAsEDwcEBgUEBAQEBgYGenp69fX19PT0 + 8/Pz8/Pz9PT0lJSUJycnJiYmJiYmJiYmJiYmNDQ0oaGh6+vr2dnZyMjIubm5q6ururq6KCgoBAQEBAQE + AAAAAAAAAAAAAAAABwcHBwcHBwcHBgYGBgYGBgYGBgYGBgYGBgYGBgYGBwcHBwcHBwcHBwcHBwcHBwcH + BwcHBwcHBwcHBwcHCQkJe3t79vb29PT09PT08/Pz9fX1fn5+CAgIBgYGBgYGBgYGBgYGBwcHSkpK7e3t + 4+Pj0tLSwsLCtLS0wsLCPT09BwcHBgYGAAAAAAAAAAAAAQEBCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJ + CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJDAwMfX199vb29PT09PT09PT09fX1gICA + DAwMCgoKCgoKCgoKCgoKDAwMWVlZ8PDw6+vr3NzczMzMvb29x8fHRkZGCQkJCQkJAQEBAAAAAAAAAQEB + DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N + Dw8Pf39/9vb29fX19fX19PT09vb2wMDAcHBwb29vb29vb29vcHBwgICA0tLS8vLy7+/v5eXl1tbWx8fH + y8vLOzs7DQ0NDQ0NAQEBAAAAAAAAAgICERERERERERERERERERERERERERERERERERERERERERERERER + ERERERERERERERERERERERERERERERERExMTgYGB9/f39fX19fX19fX19fX19/f39vb29vb29vb29vb2 + 9fX19fX18/Pz8fHx8fHx7u7u4uLi1NTUvLy8JiYmERERERERAgICAAAAAAAAAgICFRUVFRUVFRUVFRUV + FRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFxcXg4OD9/f39vb2 + 9vb29vb29vb29fX19fX19fX19PT09PT09PT08/Pz8/Pz8vLy8vLy8fHx7u7u5ubmeHh4GRkZFRUVFRUV + AgICAAAAAAAAAwMDGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZ + GRkZGRkZGRkZGRkZHBwchYWF+Pj49vb29vb29vb29vb29vb29vb29fX19fX19fX19fX19PT09PT08/Pz + 8vLy8fHx8fHxqqqqLi4uGhoaGRkZGRkZAwMDAAAAAAAAAwMDHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e + Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eICAgfX199/f39/f39/f39vb29vb29vb2 + 9vb29vb29vb29fX19fX19fX19PT09PT09PT04+Pjl5eXODg4Hx8fHh4eHh4eHh4eAwMDAAAAAAAABAQE + IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj + IyMjS0tLs7Ozzs7Oz8/Pzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozc3NyMjIvb29p6engICATk5OLCwsIyMj + IyMjIyMjIyMjIyMjBAQEAAAAAAAABQUFKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo + KCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKioqMzMzODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4 + ODg4NjY2MjIyLS0tKSkpKCgoKCgoKCgoKCgoKCgoKCgoKCgoBQUFAAAAAAAABQUFLCwsLCwsLCwsLCws + LCwsLCwsLCwsLCwsLCwsLCwsLCwsLS0tLS0tLCwsLS0tLS0tLCwsLS0tLS0tLCwsLS0tLCwsLCwsLCws + LCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCws + BQUFAAAAAAAABgYGMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx + MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx + MTExMTExMTExMTExMTExMTExMTExMTExBgYGAAAAAAAABgYGNjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2 + NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2 + NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2BgYGAAAAAgICBwcH + Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7Ozs7Ozs7BwcHAQEBExMTBAQEPj4+Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/ + Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/ + Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pj4+BAQEExMTSEhIAAAAMzMzRERERERERERE + RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE + REREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREMzMz + AAAASEhIAAAAAAAAEhISR0dHSEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhI + SEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhI + SEhISEhISEhISEhISEhISEhIR0dHEhISAAAAAAAAAAAAMTExAAAAICAgS0tLS0tLS0tLS0tLS0tLS0tL + S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tL + S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLICAgAAAAMTExAAAAAAAAAAAA + FxcXAAAAFBQUOzs7TExMTk5OT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09P + T09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PTk5OTExM + Ozs7FBQUAAAAFxcXAAAAAAAAAAAAAAAAAAAAMTExAAAAAAAABQUFCQkJCgoKCgoKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK + CgoKCgoKCgoKCgoKCgoKCgoKCQkJBQUFAAAAAAAAMTExAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUlJ + FBQUAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBFBQUSUlJAAAAAAAAAAAA + AAAAAAAA+AAAAAAfZmbgAAAAAAdmZsAAAAAAA2ZmgAAAAAABZmaAAAAAAAFmZgAAAAAAAGZmAAAAAAAA + ZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAA + AAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAA + ZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAA + AAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAA + ZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmaAAAAAAAFmZoAA + AAAAAWZmwAAAAAADZmbgAAAAAAdmZvgAAAAAH2ZmKAAAACAAAABAAAAAAQAYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAD4+PgkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkJCT4+PgAAAAAAAAAAAAAAAHR0dAICAiEhITg4 + ODs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7Ozg4OCEhIQICAnR0dAAAAAAAAAEBATY2NkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpK + SkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSjY2NgEBAQAAAD09 + PR0dHURERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE + RERERERERERERERERERERERERERERERERERERB0dHT09PQkJCSwsLD4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+ + Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+ + Pj4+PiwsLAkJCQAAACkpKTc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3 + Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NykpKQAAAAAAACMjIy8vLy8vLy8v + Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8v + Ly8vLy8vLy8vLy8vLy8vLyMjIwAAAAAAAB4eHigoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo + KCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKB4eHgAAAAAA + ABgYGCEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEh + ISEhISEhISEhISEhISEhISEhISEhISEhISEhIRgYGAAAAAAAABMTExoaGiofGmk0GnA2GjokGiQdGmMy + GnE3GkcpGh0bGlIsGnI3GlovGjMwLoODg42NjYaGhkpKShoaGhoaGhoaGhoaGi8vL3p6eoSEhIODg2Rk + ZB4eHhMTEwAAAAAAAA4ODhMTE2UuE6s5AI4vAIA0DkYkE3oqA1IbAHcvCycaE4QxCGIgAIEvBnRiWri4 + uJubm4iIiIiIiBQUFBMTExMTExMTE2xsbGdnZ11dXVxcXImJiR4eHg4ODgAAAAAAAAoKCg0NDWosDc1E + ALM7AI82CkkhDZ01AnYnAIkzCCUVDZc2BXYnAJAzBHplW8HBwaWlpZCQkI2NjQ4ODg0NDQ0NDQ0NDX19 + fWZmZl9fX1xcXImJiREREQoKCgAAAAAAAAYGBggICGcoCOpQA9dHAJc2BkUcCL9AAZw0AJY1BSEQCKU5 + A40vAJw1AndiV9DQ0La2tqKioo+PjwkJCQgICAgICAsLC4yMjHFxcWhoaGJiYoKCggoKCgYGBgAAAAAA + AAMDAwQEBGUkBPFjHO9XC503BEIZBOFMAcVBAKQ4Ah0MBLQ9AaU3AKk5AXRfVN7e3sjIyLS0tJKSkgUF + BQQEBAUFBSsrK6WlpXx8fHFxcWtra29vbwQEBAMDAwAAAAAAAAEBAQEBAWMiAfN2OPJuLJ47CkEWAfFd + E+hQBLM8AB0LAcVCAL4/ALc9AHJdU+zs7Nra2sfHx8TExHx8fHx8fIqKirCwsJaWlomJiXx8fJKSkikp + KQEBAQEBAQAAAAAAAAAAAAAAAGIgAPSHUfSDStteILpLFPN1NfJnIuNQBrI8AeZNANhIAMFAAGhZUfLy + 8u3t7eHh4dHR0cjIyMDAwLm5ubOzs6ysrJ2dnbGxsWpqagICAgAAAAAAAAAAAAAAAAAAAAAAAGIgAPaa + bPaabPaZavaabfaWZvSHUPNzM/JnIvFkHe9YDJkzAFZTUvPz8/Hx8fDw8Ozs7Ofn5+Dg4NjY2NDQ0MbG + xrW1taOjo56enhISEgAAAAAAAAAAAAAAAAEBAQEBAT8WAdV3SN17St17St18S916SN1zPtxpMNpjJ89Z + HpU4ChwKAVNTU/T09PPz8/Ly8vHx8eLi4uLi4uDg4OLi4uPj487Ozra2tqenp2ZmZgICAgEBAQAAAAAA + AAMDAwUFBQcFBRQKBRcLBRcLBRcLBRcLBRcLBRcLBRYKBREJBQcFBQUFBVVVVfX19fT09PPz87CwsBwc + HBsbGxsbGyIiIqqqquDg4MfHx7CwsJeXlwcHBwMDAwAAAAAAAAYGBgkJCQkJCQkJCQkJCQkJCQkJCQkJ + CQkJCQkJCQkJCQkJCQkJCQkJCVhYWPX19fT09PT09KenpwoKCgkJCQkJCQoKCoaGhuvr69XV1b6+vqmp + qQ4ODgYGBgAAAAAAAAoKCg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODltbW/b2 + 9vX19fX19d/f352dnZ2dnZ2dnaSkpOTk5PDw8OTk5M7OzqKiohEREQoKCgAAAAAAAA8PDxQUFBQUFBQU + FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFF9fX/f39/b29vb29vX19fX19fT09PT09PPz8/Pz + 8/Ly8u/v7+Tk5GlpaRQUFA8PDwAAAAAAABQUFBsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb + GxsbGxsbG2NjY/f39/f39/f39/b29vb29vX19fX19fT09PT09PPz8/Hx8aOjoyQkJBsbGxQUFAAAAAAA + ABkZGSIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIkdHR8/Pz9zc3Nzc3Nzc3Nzc + 3Nvb29vb29jY2MzMzK6urmtrayoqKiIiIiIiIhkZGQAAAAAAAB8fHykpKSkpKSkpKSkpKSkpKSkpKSkp + KSkpKSkpKSkpKSkpKSkpKSkpKSoqKjIyMjQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDMzMy8vLysrKykpKSkpKSkp + KSkpKR8fHwAAAAAAACQkJDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCQkJAAAAAAAACkpKTc3Nzc3Nzc3 + Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3 + Nzc3Nzc3Nzc3Nzc3Nzc3NykpKQAAAAkJCSwsLD4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+ + Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+PiwsLAkJCT4+ + Ph0dHUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVF + RUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRR0dHT4+PgAAAAEBATc3N0tLS0tLS0tLS0tLS0tLS0tL + S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tL + Szc3NwEBAQAAAAAAAHV1dQICAiEhITg4ODs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozg4OCEhIQICAnV1dQAAAAAAAAAAAAAAAD4+PgoK + CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAoKCj4+PgAAAAAAAAAAAOAAAAeAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAgAAAAYAAAAHgAAAHKAAAABgAAAAwAAAAAQAYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAADs7OwcHBwQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE + BAQEBAQEBAQEBAcHBzs7OwAAAAAAAAAAAA0NDTk5OUxMTExMTExMTExMTExMTExMTExMTExMTExMTExM + TExMTExMTExMTExMTExMTExMTExMTExMTDk5OQ0NDQAAADs7OzQ0NEVFRUVFRUVFRUVFRUVFRUVFRUVF + RUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRTQ0NDo6OgYGBjw8PDw8PDw8PDw8 + PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PAYGBgMD + AzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz + MzMzMzMzMwMDAwICAikpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkp + KSkpKSkpKSkpKSkpKSkpKSkpKQICAgEBAR8fHyAfHyIgHyEgHyAfHyEgHyEgHx8fHyEgHyIgHyAgICcn + JygoKCMjIx8fHx8fHx8fHyEhISgoKCgoKCMjIx8fHwEBAQEBARYWFjwjFpg7DII2DzQgFnoyDXEvDi0e + FnYyEHkxDGFHOq+vr6GhoXl5eRcXFxYWFhYWFlxcXIKCgoGBgW5ubhcXFwEBAQAAAA4ODlQlDsNAAK49 + BEghDo8xAX8sAjkcDo0yBHImAI5fSLq6upiYmIuLiw8PDw4ODg4ODnd3d2JiYlxcXG9vbw4ODgAAAAAA + AAcHB08fB+lRBc9GAkMbB79AAKY4ATQWB6M4AowuAItbQ87Ozq+vr5OTkwgICAcHBwsLC4qKim5ubmRk + ZGJiYgcHBwAAAAAAAAICAksbAvJrKOBWET8XAulSBs9FADASAro+AKk4AIpYP+Li4sfHx6mpqSgoKCws + LGpqapmZmXx8fHt7ezw8PAICAgAAAAAAAAAAAEkYAPSES+xwMps+D/JvLe1ZDpgzAd5KAMpDAH5TPfDw + 8OTk5NXV1czMzMPDw7KysqWlpZmZmYeHhwQEBAAAAAAAAAAAAAAAAEgYAPadcfaccPaecvaYafSBSPJu + LPFmIchGBU5DPfPz8/Hx8e7u7ujo6OHh4dbW1sjIyK6urqKiohgYGAAAAAAAAAAAAAMDAxcJA3I2GXg5 + GXg5Gng4GXc1FHYyEGMnCR0MA0A/P/T09PPz89vb24GBgYCAgIaGhtXV1cvLy62trWVlZQMDAwAAAAAA + AAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICENDQ/X19fT09Lq6ugkJCQgICAkJCaCgoN/f38DA + wIODgwgICAAAAAAAAA8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PD0lJSfb29vX19ejo6LOzs7Ky + sra2turq6u3t7dXV1Xp6eg8PDwAAAAEBARcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXF09PT/f3 + 9/b29vb29vX19fX19fT09PPz8/Ly8tzc3DY2NhcXFwEBAQICAiAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgIENDQ9zc3OPj4+Li4uLi4uLi4uDg4NPT06mpqUhISCEhISAgIAICAgICAioqKioqKioqKioq + KioqKioqKioqKioqKioqKioqKisrKzExMTIyMjIyMjIyMjIyMjIyMi4uLisrKyoqKioqKioqKgICAgMD + AzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0 + NDQ0NDQ0NAMDAwYGBj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09 + PT09PT09PT09PT09PT09PT09PQcHBzs7OzQ0NEZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZG + RkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRjQ0NDs7OwAAAA0NDTk5OUxMTE1NTU1NTU1NTU1NTU1N + TU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTUxMTDk5OQ0NDQAAAAAAAAAAADs7OwcHBwUF + BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQcHBzs7OwAAAAAAAMAA + A0GAAAFBAAAAQQAAAEEAAABBAAAAQQAAAEEAAABBAAAAQQAAAEEAAABBAAAAQQAAAEEAAABBAAAAQQAA + AEEAAABBAAAAQQAAAEEAAABBAAAAQQAAAEGAAAFBwAADQSgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABDQ0MdHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR1DQ0MA + AABBQUFCQkJHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dCQkJBQUEWFhY6Ojo6Ojo6 + Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoWFhYQEBAsLCwsLCwsLCwsLCwsLCwsLCws + LCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwQEBALCwshHx1HKx0oIR1FKx0pIR1BKR00KCJUVFREREQdHR0d + HR07OztSUlIxMTELCwsGBgY8HxCuOgBoLA54KAFTJA19LAOASi+urq6Li4sREREQEBBubm5dXV1QUFAG + BgYCAgI2FgboVApvKQbAQABeIgWjNwGMTCzLy8uenp4HBwcRERGIiIhqampAQEACAgIAAAAxEQDze0Cd + PxDvYhyZNQLQRQCVTSnp6enPz8+goKCqqqqampqKiooLCwsAAAAAAAAoDgHoiVrqi1vpgk/maivRUxRX + OSnz8/Pw8PDj4+Pa2trLy8uoqKgfHx8AAAACAgIHBwcPCQcQCgcQCgcPCgcKCAcuLi719fXPz88TExMU + FBS/v7/Dw8NVVVUCAgIGBgYRERERERERERERERERERERERE3Nzf29vbw8PDJycnKysru7u7h4eFMTEwG + BgYLCwseHh4eHh4eHh4eHh4eHh4eHh46Ojrm5ubp6enp6enn5+fY2NiKioogICALCwsQEBAtLS0tLS0t + LS0tLS0tLS0tLS0tLS0xMTEyMjIyMjIyMjIuLi4tLS0tLS0QEBAWFhY7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsWFhZBQUFDQ0NISEhISEhISEhISEhISEhISEhISEhISEhISEhI + SEhISEhISEhDQ0NBQUEAAABDQ0MdHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR1DQ0MA + AACAAaxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAA + rEGAAaxBiVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAABPXElEQVR42u2dCZAc13nf3+yJ + BbC4D+K+CBAESJAAQRIERFESJdKUZMpSMZbNSJYUyVGJlh1FimXJjm+7knJKcqqkUuyUVIoTJxVfZceW + LVuJKPM+QBI8RBAkQYIACYIg7sWxWOwxeb/X/c287unued0zu7MDvH/tq57p6entftPf/33X+15JtRZz + dLtWt8t1uy7czgw/Y39ni6/Pw6MZGNXt6fD1Kd326vZkuGX/8VZdWGmC/1+fbu/R7RbdfkK3q1t14x4e + kwjP6faPut2n2726DU7UP54oAkDgP67bXao6wnt4eNQCDeEvdfsfKiCEccV4EkCPbp/U7Su6rRrvG/Hw + uAixT7f/qNt/0+3CePyD8SAA1PxPq0Dwl4xXz3h4XEI4qNt/0u2/qiabB80mgA/o9g3lR3wPj/EAGsEv + 6vb3zTphswgAgf/Put3Zgk7x8LjU8Le6fUEFhNAQmkEAH9btu8o79zw8JhI4Cz+l2183cpJGCAAn39d0 + +3yre8LD4xLGN3X7kiroJCxKACTwfE+3m1p99x4eHuoR3T6oCiQUFSGA5bp9X7cNzbjyuXPnqv7+fjVr + 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 + avffN97/bKIIQIBWQJERCOEnVJBZ6OFxqYPMvX9UgcBTvGNcRvskTDQBxIGP4FoVrEwsWzSEzvC9h8fF + gqdVUIufEX5v+F62dSftjBf+P2mGUkV8m/xZAAAAAElFTkSuQmCCKAAAADAAAABgAAAAAQAgAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAABcAAAAtwAAAOsAAAD+AAAA/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8A + AAD+AAAA6wAAALcAAABcAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAM4AAAD/AAAA/wUFBf8J + CQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8J + CQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8J + CQn/CQkJ/wkJCf8JCQn/BQUF/wAAAP8AAAD/AAAAzgAAACgAAAAAAAAAAAAAAAAAAAAoAAAA6AAAAP8U + FBT/Ozs7/0xMTP9OTk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9O + Tk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9O + Tk7/Tk5O/05OTv9OTk7/Tk5O/05OTv9OTk7/TExM/zs7O/8UFBT/AAAA/wAAAOgAAAAoAAAAAAAAAAYA + AADPAAAA/yAgIP9KSkr/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9L + S0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9L + S0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9KSkr/ICAg/wAAAP8A + AADPAAAABgAAAFwAAAD/EhIS/0ZGRv9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9H + R0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9H + R0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9H + R0f/RkZG/xISEv8AAAD/AAAAXQAAALcAAAD/MzMz/0NDQ/9DQ0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9D + Q0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9D + Q0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9DQ0P/Q0ND/0NDQ/9D + Q0P/Q0ND/0NDQ/9DQ0P/Q0ND/zMzM/8AAAD/AAAAuAAAAOwEBAT/PT09/z8/P/8/Pz//Pz8//z8/P/8/ + Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/ + Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/ + Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z09Pf8EBAT/AAAA7QAAAP0HBwf/Ojo6/zo6Ov86 + Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86 + Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86 + Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov8HBwf/AAAA/gAAAP8G + Bgb/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81 + NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81 + NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf81NTX/NTU1/zU1Nf8G + Bgb/AAAA/wAAAP8GBgb/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8w + MDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8w + MDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8w + MDD/MDAw/zAwMP8GBgb/AAAA/wAAAP8FBQX/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8s + LCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8s + LCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8s + LCz/LCws/ywsLP8sLCz/LCws/ywsLP8FBQX/AAAA/wAAAP8EBAT/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8n + Jyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8n + Jyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8n + Jyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8nJyf/Jycn/ycnJ/8EBAT/AAAA/wAAAP8EBAT/IiIi/yIiIv8i + IiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8i + IiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8i + IiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8EBAT/AAAA/wAAAP8D + AwP/HR0d/x0dHf8dHR3/Hh0d/yEeHf8iHx3/IR8d/x8eHf8dHR3/Hh0d/yAeHf8iHx3/Ih8d/yAeHf8d + HR3/HR0d/x8eHf8hHx3/Ih8d/yEeHf8eHR3/ICAg/yoqKv8vLy//Ly8v/y8vL/8qKir/ICAg/x0dHf8d + HR3/HR0d/x0dHf8dHR3/HR0d/x4eHv8lJSX/LS0t/y8vL/8vLy//Ly8v/yoqKv8gICD/HR0d/x0dHf8D + AwP/AAAA/wAAAP8DAwP/GRkZ/xkZGf8aGRn/PCQZ/4Y9Gf+ZQxn/k0IZ/1kuGf8gGxn/LiAZ/3g5Gf+Y + Qxn/l0MZ/241Gf8oHhn/HxsZ/1guGf+TQRn/mUQZ/4g+Gf9AJhn/REND/6Kiov+2trb/s7Oz/6+vr/+X + l5f/Q0ND/xkZGf8ZGRn/GRkZ/xkZGf8ZGRn/GRkZ/yQkJP91dXX/oqKi/6enp/+mpqb/pqam/5GRkf9C + QkL/GRkZ/xkZGf8DAwP/AAAA/wAAAP8CAgL/FBQU/xQUFP8YFRT/gzkU/6s5AP+UMQD/hCwA/5g7Df8r + HBT/WSsU/4EuBf9WHAD/ShgA/3ctCP9EJBT/KRsU/485Df9dHwD/XyAA/2QiAP+EOhX/fXp4/729vf+l + paX/l5eX/4qKiv+Ojo7/enp6/xYWFv8UFBT/FBQU/xQUFP8UFBT/FBQU/zk5Of+fn5//YmJi/15eXv9c + XFz/XV1d/39/f/9mZmb/FRUV/xQUFP8CAgL/AAAA/wAAAP8CAgL/EBAQ/xAQEP8VEhD/lTwQ/8JAAP+t + OQD/nTQA/65BCv8tGhD/ZiwQ/5g1A/9vJQD/YCAA/4oyBv9LJBD/KhgQ/6M9Cv9sJAD/bCMA/2wkAP+W + PhH/hoKA/8LCwv+pqan/m5ub/42Njf+RkZH/g4OD/xISEv8QEBD/EBAQ/xAQEP8QEBD/EBAQ/05OTv+a + mpr/YmJi/11dXf9bW1v/W1tb/5WVlf9QUFD/EBAQ/xAQEP8CAgL/AAAA/wAAAP8BAQH/DAwM/wwMDP8R + Dgz/kzkM/9hHAP/EQQD/tTwA/7hCB/8qFgz/ZCkM/607Av+JLQD/eCgA/5g1BP9JIQz/JxUM/6g9CP98 + KQD/eSgA/3YnAP+WOw7/hYB+/8nJyf+zs7P/pKSk/5aWlv+YmJj/gYGB/w4ODv8MDAz/DAwM/wwMDP8M + DAz/DQ0N/2JiYv+RkZH/ZmZm/2FhYf9eXl7/XV1d/56env86Ojr/DAwM/wwMDP8BAQH/AAAA/wAAAP8B + AQH/CQkJ/wkJCf8OCgn/kjYJ/+tPAf/cSQD/zkQA/8BEBf8nEwn/YiYJ/8JCAv+kNgD/kTAA/6Y5A/9G + HQn/JBIJ/64+Bv+NLwD/iC0A/4IrAP+VOQv/g399/9PT0/++vr7/sLCw/6Kiov+jo6P/f39//wsLC/8J + CQn/CQkJ/wkJCf8JCQn/CwsL/319ff+Kior/bW1t/2hoaP9jY2P/YWFh/6Ghof8qKir/CQkJ/wkJCf8B + AQH/AAAA/wAAAP8AAAD/BgYG/wYGBv8LCAb/kDQG//BbEP/uUgT/5kwA/8lFBP8kEAb/YCQG/9dIAf+/ + PwD/rDkA/7Q9Av9EGwb/IQ8G/7M+BP+eNAD/lzIA/44vAP+UNwj/gX17/9zc3P/Kysr/vLy8/66urv+t + ra3/fn5+/wgICP8GBgb/BgYG/wYGBv8GBgb/EhIS/6Ojo/9/f3//dXV1/25ubv9oaGj/ZWVl/6Kiov8c + HBz/BgYG/wYGBv8AAAD/AAAA/wAAAP8AAAD/AwMD/wMDA/8JBQP/jzID//JoJP/xYBj/8FkN/89IBf8i + DgP/XiID/+pPAf/aSAD/xkIA/8NCAf9CGAP/Hw0D/7k/Av+vOgD/pzcA/5s0AP+UNQb/gHx6/+Xl5f/V + 1dX/yMjI/7q6uv+4uLj/fX19/wUFBf8DAwP/AwMD/wQEBP8LCwv/TU1N/7m5uf+EhIT/fHx8/3V1df9u + bm7/bW1t/5WVlf8NDQ3/AwMD/wMDA/8AAAD/AAAA/wAAAP8AAAD/AQEB/wEBAf8HAwH/jjAB//N1Nv/y + by3/8mkk/9BNDP8gDAH/XSAB//FbEP/uVAf/4EoA/9JGAP9BFwH/HQsB/8BBAf/BQAD/tz0A/6o4AP+U + NAT/f3t5/+3t7f/g4OD/1NTU/8fHx//Dw8P/ra2t/0xMTP9LS0v/TU1N/11dXf+Pj4//wcHB/5eXl/+O + jo7/hYWF/3x8fP90dHT/nZ2d/0xMTP8DAwP/AQEB/wEBAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8G + AgD/ji8A//SARv/zfED/83c5/9hVFP88FAD/dCcA//JoI//xZR//71cK/+RMAP9bHgD/RxgA/9ZHAP/S + RgD/yEIA/7k9AP+NMQP/fXp4//Ly8v/s7Oz/4uLi/9bW1v/MzMz/19fX/87Ozv/Ly8v/yMjI/8LCwv+y + srL/pKSk/6CgoP+ampr/j4+P/4WFhf+QkJD/jo6O/xEREf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8A + AAD/AAAA/wAAAP8FAQD/ji8A//WLVv/0iFL/9IVN//FvLv/ZXR7/5WAe//N5PP/zdjj/8mgk//BYDP/f + TAP/30sB/+xQAf/lTAD/2UgA/81EAP9yJwL/enl4//Pz8//w8PD/7+/v/+np6f/d3d3/1dXV/8/Pz//J + ycn/xMTE/7+/v/+7u7v/tra2/7Gxsf+qqqr/np6e/7CwsP/Pz8//Ly8v/wEBAf8AAAD/AAAA/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8FAQD/ji8A//aXaP/2l2j/9pZm//WUZP/2lmb/9pZn//WUY//1 + i1b/9H9E//JwL//xZiH/8WEZ//FhGf/wWg//7VIE/9RGAP85FAL/eHh4//T09P/x8fH/8fHx//Dw8P/u + 7u7/6enp/+Tk5P/f39//2dnZ/9TU1P/Pz8//ysrK/8TExP+6urr/ra2t/5+fn/+zs7P/gICA/w4ODv8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8FAgD/hy0A//eieP/3pHr/96R6//ekev/3 + pX3/96Z9//ejev/2nG//9ZFf//SETf/zez//83U3//NzNP/yayj/51gQ/3coAf8OBwP/eHh4//T09P/y + 8vL/8vLy//Hx8f/x8fH/8PDw//Dw8P/v7+//7Ozs/+np6f/k5OT/3t7e/9fX1//Nzc3/vr6+/66urv+g + oKD/tbW1/1BQUP8DAwP/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AgIC/wICAv8EAwL/SBkC/75dLP/Q + ZC//0WUv/9FlL//RZTD/0WUw/9BlL//QYiz/0F8n/9BbIf/PWB3/zVUa/8JOFP+kPQn/WB8C/xIHAv8F + BQT/eXl5//X19f/z8/P/8/Pz//Ly8v/z8/P/8fHx/9vb2//a2tr/2tra/9ra2v/b29v/4+Pj/+zs7P/e + 3t7/zs7O/76+vv+urq7/oqKi/6Ghof8QEBD/AgIC/wICAv8AAAD/AAAA/wAAAP8AAAD/BAQE/wQEBP8E + BAT/CgYE/xoLBP8fDQT/Hw0E/x8NBP8fDQT/Hw0E/x8NBP8fDQT/Hw0E/x8NBP8fDQT/HgwE/xgLBP8P + BwT/BgUE/wQEBP8GBgb/enp6//X19f/09PT/8/Pz//Pz8//09PT/lJSU/ycnJ/8mJib/JiYm/yYmJv8m + Jib/NDQ0/6Ghof/r6+v/2dnZ/8jIyP+5ubn/q6ur/7q6uv8oKCj/BAQE/wQEBP8AAAD/AAAA/wAAAP8A + AAD/BwcH/wcHB/8HBwf/BgYG/wYGBv8GBgb/BgYG/wYGBv8GBgb/BgYG/wcHB/8HBwf/BwcH/wcHB/8H + Bwf/BwcH/wcHB/8HBwf/BwcH/wcHB/8JCQn/e3t7//b29v/09PT/9PT0//Pz8//19fX/fn5+/wgICP8G + Bgb/BgYG/wYGBv8GBgb/BwcH/0pKSv/t7e3/4+Pj/9LS0v/CwsL/tLS0/8LCwv89PT3/BwcH/wYGBv8A + AAD/AAAA/wAAAP8BAQH/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8J + CQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8MDAz/fX19//b29v/09PT/9PT0//T09P/1 + 9fX/gICA/wwMDP8KCgr/CgoK/woKCv8KCgr/DAwM/1lZWf/w8PD/6+vr/9zc3P/MzMz/vb29/8fHx/9G + Rkb/CQkJ/wkJCf8BAQH/AAAA/wAAAP8BAQH/DQ0N/w0NDf8NDQ3/DQ0N/w0NDf8NDQ3/DQ0N/w0NDf8N + DQ3/DQ0N/w0NDf8NDQ3/DQ0N/w0NDf8NDQ3/DQ0N/w0NDf8NDQ3/DQ0N/w0NDf8PDw//f39///b29v/1 + 9fX/9fX1//T09P/29vb/wMDA/3BwcP9vb2//b29v/29vb/9wcHD/gICA/9LS0v/y8vL/7+/v/+Xl5f/W + 1tb/x8fH/8vLy/87Ozv/DQ0N/w0NDf8BAQH/AAAA/wAAAP8CAgL/ERER/xEREf8RERH/ERER/xEREf8R + ERH/ERER/xEREf8RERH/ERER/xEREf8RERH/ERER/xEREf8RERH/ERER/xEREf8RERH/ERER/xEREf8T + ExP/gYGB//f39//19fX/9fX1//X19f/19fX/9/f3//b29v/29vb/9vb2//b29v/19fX/9fX1//Pz8//x + 8fH/8fHx/+7u7v/i4uL/1NTU/7y8vP8mJib/ERER/xEREf8CAgL/AAAA/wAAAP8CAgL/FRUV/xUVFf8V + FRX/FRUV/xUVFf8VFRX/FRUV/xUVFf8VFRX/FRUV/xUVFf8VFRX/FRUV/xUVFf8VFRX/FRUV/xUVFf8V + FRX/FRUV/xUVFf8XFxf/g4OD//f39//29vb/9vb2//b29v/29vb/9fX1//X19f/19fX/9PT0//T09P/0 + 9PT/8/Pz//Pz8//y8vL/8vLy//Hx8f/u7u7/5ubm/3h4eP8ZGRn/FRUV/xUVFf8CAgL/AAAA/wAAAP8D + AwP/GRkZ/xkZGf8ZGRn/GRkZ/xkZGf8ZGRn/GRkZ/xkZGf8ZGRn/GRkZ/xkZGf8ZGRn/GRkZ/xkZGf8Z + GRn/GRkZ/xkZGf8ZGRn/GRkZ/xkZGf8cHBz/hYWF//j4+P/29vb/9vb2//b29v/29vb/9vb2//b29v/1 + 9fX/9fX1//X19f/19fX/9PT0//T09P/z8/P/8vLy//Hx8f/x8fH/qqqq/y4uLv8aGhr/GRkZ/xkZGf8D + AwP/AAAA/wAAAP8DAwP/Hh4e/x4eHv8eHh7/Hh4e/x4eHv8eHh7/Hh4e/x4eHv8eHh7/Hh4e/x4eHv8e + Hh7/Hh4e/x4eHv8eHh7/Hh4e/x4eHv8eHh7/Hh4e/x4eHv8gICD/fX19//f39//39/f/9/f3//b29v/2 + 9vb/9vb2//b29v/29vb/9vb2//X19f/19fX/9fX1//T09P/09PT/9PT0/+Pj4/+Xl5f/ODg4/x8fH/8e + Hh7/Hh4e/x4eHv8DAwP/AAAA/wAAAP8EBAT/IyMj/yMjI/8jIyP/IyMj/yMjI/8jIyP/IyMj/yMjI/8j + IyP/IyMj/yMjI/8jIyP/IyMj/yMjI/8jIyP/IyMj/yMjI/8jIyP/IyMj/yMjI/8jIyP/S0tL/7Ozs//O + zs7/z8/P/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Nzc3/yMjI/729vf+np6f/gICA/05OTv8s + LCz/IyMj/yMjI/8jIyP/IyMj/yMjI/8EBAT/AAAA/wAAAP8FBQX/KCgo/ygoKP8oKCj/KCgo/ygoKP8o + KCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8o + KCj/Kioq/zMzM/84ODj/ODg4/zg4OP84ODj/ODg4/zg4OP84ODj/ODg4/zg4OP84ODj/NjY2/zIyMv8t + LS3/KSkp/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8FBQX/AAAA/wAAAP8FBQX/LCws/ywsLP8s + LCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8tLS3/LS0t/ywsLP8tLS3/LS0t/ywsLP8t + LS3/LS0t/ywsLP8tLS3/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8s + LCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8FBQX/AAAA/wAAAP8G + Bgb/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8x + MTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8x + MTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8xMTH/MTEx/zExMf8G + Bgb/AAAA/wAAAP8GBgb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82 + Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82 + Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82Njb/NjY2/zY2Nv82 + Njb/NjY2/zY2Nv8GBgb/AAAA/wAAAP0HBwf/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87 + Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87 + Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87 + Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/8HBwf/AAAA/gAAAOwEBAT/Pj4+/z8/P/8/Pz//Pz8//z8/P/8/ + Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/ + Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/ + Pz//Pz8//z8/P/8/Pz//Pz8//z8/P/8/Pz//Pz8//z4+Pv8EBAT/AAAA7AAAALcAAAD/MzMz/0RERP9E + RET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9E + RET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9E + RET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/zMzM/8AAAD/AAAAtwAAAFwA + AAD/EhIS/0dHR/9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9I + SEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9I + SEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/R0dH/xISEv8A + AAD/AAAAXAAAAAYAAADOAAAA/yAgIP9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9L + S0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9L + S0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9L + S0v/ICAg/wAAAP8AAADOAAAABgAAAAAAAAAoAAAA6AAAAP8UFBT/Ozs7/0xMTP9OTk7/T09P/09PT/9P + T0//T09P/09PT/9PT0//T09P/09PT/9PT0//T09P/09PT/9PT0//T09P/09PT/9PT0//T09P/09PT/9P + T0//T09P/09PT/9PT0//T09P/09PT/9PT0//T09P/09PT/9PT0//T09P/09PT/9PT0//T09P/09PT/9O + Tk7/TExM/zs7O/8UFBT/AAAA/wAAAOgAAAAoAAAAAAAAAAAAAAAAAAAAKAAAAM4AAAD/AAAA/wUFBf8J + CQn/CgoK/woKCv8KCgr/CgoK/woKCv8KCgr/CgoK/woKCv8KCgr/CgoK/woKCv8KCgr/CgoK/woKCv8K + Cgr/CgoK/woKCv8KCgr/CgoK/woKCv8KCgr/CgoK/woKCv8KCgr/CgoK/woKCv8KCgr/CgoK/woKCv8K + Cgr/CgoK/woKCv8JCQn/BQUF/wAAAP8AAAD/AAAAzgAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYA + AABcAAAAtgAAAOsAAAD+AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD+AAAA6wAAALYAAABcAAAABgAAAAAAAAAAAAAAAOAAAAAA + B2ZmwAAAAAADZmaAAAAAAAFmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYA + AAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAA + AGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYA + AAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAA + AGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYA + AAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZgAAAAAAAGZmAAAAAAAAZmYAAAAAAABmZoAAAAAA + AWZmwAAAAAADZmbgAAAAAAdmZigAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFAAAADBAAAA9gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD2AAAAwQAAAFAA + AAAAAAAAAAAAAAAAAACLAgIC/yEhIf84ODj/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87 + Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zg4OP8h + ISH/AgIC/wAAAIsAAAAAAAAAUQEBAf82Njb/SkpK/0pKSv9KSkr/SkpK/0pKSv9KSkr/SkpK/0pKSv9K + Skr/SkpK/0pKSv9KSkr/SkpK/0pKSv9KSkr/SkpK/0pKSv9KSkr/SkpK/0pKSv9KSkr/SkpK/0pKSv9K + Skr/SkpK/0pKSv82Njb/AQEB/wAAAFEAAADCHR0d/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9E + RET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9ERET/RERE/0RERP9E + RET/RERE/0RERP9ERET/RERE/0RERP8dHR3/AAAAwgAAAPYsLCz/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+ + Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+ + Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/ywsLP8AAAD2AAAA/ykpKf83Nzf/Nzc3/zc3N/83 + Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83 + Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/KSkp/wAAAP8AAAD/IyMj/y8vL/8v + Ly//Ly8v/y8vL/8vLy//Ly8v/y8vL/8vLy//Ly8v/y8vL/8vLy//Ly8v/y8vL/8vLy//Ly8v/y8vL/8v + Ly//Ly8v/y8vL/8vLy//Ly8v/y8vL/8vLy//Ly8v/y8vL/8vLy//Ly8v/y8vL/8jIyP/AAAA/wAAAP8e + Hh7/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8o + KCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/ygoKP8oKCj/KCgo/x4eHv8A + AAD/AAAA/xgYGP8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8h + ISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8h + ISH/GBgY/wAAAP8AAAD/ExMT/xoaGv8qHxr/aTQa/3A2Gv86JBr/JB0a/2MyGv9xNxr/Ryka/x0bGv9S + LBr/cjca/1ovGv8zMC7/g4OD/42Njf+Ghob/SkpK/xoaGv8aGhr/Ghoa/xoaGv8vLy//enp6/4SEhP+D + g4P/ZGRk/x4eHv8TExP/AAAA/wAAAP8ODg7/ExMT/2UuE/+rOQD/ji8A/4A0Dv9GJBP/eioD/1IbAP93 + Lwv/JxoT/4QxCP9iIAD/gS8G/3RiWv+4uLj/m5ub/4iIiP+IiIj/FBQU/xMTE/8TExP/ExMT/2xsbP9n + Z2f/XV1d/1xcXP+JiYn/Hh4e/w4ODv8AAAD/AAAA/woKCv8NDQ3/aiwN/81EAP+zOwD/jzYK/0khDf+d + NQL/dicA/4kzCP8lFQ3/lzYF/3YnAP+QMwT/emVb/8HBwf+lpaX/kJCQ/42Njf8ODg7/DQ0N/w0NDf8N + DQ3/fX19/2ZmZv9fX1//XFxc/4mJif8RERH/CgoK/wAAAP8AAAD/BgYG/wgICP9nKAj/6lAD/9dHAP+X + Ngb/RRwI/79AAf+cNAD/ljUF/yEQCP+lOQP/jS8A/5w1Av93Ylf/0NDQ/7a2tv+ioqL/j4+P/wkJCf8I + CAj/CAgI/wsLC/+MjIz/cXFx/2hoaP9iYmL/goKC/woKCv8GBgb/AAAA/wAAAP8DAwP/BAQE/2UkBP/x + Yxz/71cL/503BP9CGQT/4UwB/8VBAP+kOAL/HQwE/7Q9Af+lNwD/qTkB/3RfVP/e3t7/yMjI/7S0tP+S + kpL/BQUF/wQEBP8FBQX/Kysr/6Wlpf98fHz/cXFx/2tra/9vb2//BAQE/wMDA/8AAAD/AAAA/wEBAf8B + AQH/YyIB//N2OP/ybiz/njsK/0EWAf/xXRP/6FAE/7M8AP8dCwH/xUIA/74/AP+3PQD/cl1T/+zs7P/a + 2tr/x8fH/8TExP98fHz/fHx8/4qKiv+wsLD/lpaW/4mJif98fHz/kpKS/ykpKf8BAQH/AQEB/wAAAP8A + AAD/AAAA/wAAAP9iIAD/9IdR//SDSv/bXiD/uksU//N1Nf/yZyL/41AG/7I8Af/mTQD/2EgA/8FAAP9o + WVH/8vLy/+3t7f/h4eH/0dHR/8jIyP/AwMD/ubm5/7Ozs/+srKz/nZ2d/7Gxsf9qamr/AgIC/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/2IgAP/2mmz/9pps//aZav/2mm3/9pZm//SHUP/zczP/8mci//FkHf/v + WAz/mTMA/1ZTUv/z8/P/8fHx//Dw8P/s7Oz/5+fn/+Dg4P/Y2Nj/0NDQ/8bGxv+1tbX/o6Oj/56env8S + EhL/AAAA/wAAAP8AAAD/AAAA/wEBAf8BAQH/PxYB/9V3SP/de0r/3XtK/918S//dekj/3XM+/9xpMP/a + Yyf/z1ke/5U4Cv8cCgH/U1NT//T09P/z8/P/8vLy//Hx8f/i4uL/4uLi/+Dg4P/i4uL/4+Pj/87Ozv+2 + trb/p6en/2ZmZv8CAgL/AQEB/wAAAP8AAAD/AwMD/wUFBf8HBQX/FAoF/xcLBf8XCwX/FwsF/xcLBf8X + CwX/FwsF/xYKBf8RCQX/BwUF/wUFBf9VVVX/9fX1//T09P/z8/P/sLCw/xwcHP8bGxv/Gxsb/yIiIv+q + qqr/4ODg/8fHx/+wsLD/l5eX/wcHB/8DAwP/AAAA/wAAAP8GBgb/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8J + CQn/CQkJ/wkJCf8JCQn/CQkJ/wkJCf8JCQn/CQkJ/1hYWP/19fX/9PT0//T09P+np6f/CgoK/wkJCf8J + CQn/CgoK/4aGhv/r6+v/1dXV/76+vv+pqan/Dg4O/wYGBv8AAAD/AAAA/woKCv8ODg7/Dg4O/w4ODv8O + Dg7/Dg4O/w4ODv8ODg7/Dg4O/w4ODv8ODg7/Dg4O/w4ODv8ODg7/W1tb//b29v/19fX/9fX1/9/f3/+d + nZ3/nZ2d/52dnf+kpKT/5OTk//Dw8P/k5OT/zs7O/6Kiov8RERH/CgoK/wAAAP8AAAD/Dw8P/xQUFP8U + FBT/FBQU/xQUFP8UFBT/FBQU/xQUFP8UFBT/FBQU/xQUFP8UFBT/FBQU/xQUFP9fX1//9/f3//b29v/2 + 9vb/9fX1//X19f/09PT/9PT0//Pz8//z8/P/8vLy/+/v7//k5OT/aWlp/xQUFP8PDw//AAAA/wAAAP8U + FBT/Gxsb/xsbG/8bGxv/Gxsb/xsbG/8bGxv/Gxsb/xsbG/8bGxv/Gxsb/xsbG/8bGxv/Gxsb/2NjY//3 + 9/f/9/f3//f39//29vb/9vb2//X19f/19fX/9PT0//T09P/z8/P/8fHx/6Ojo/8kJCT/Gxsb/xQUFP8A + AAD/AAAA/xkZGf8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8iIiL/IiIi/yIiIv8i + IiL/R0dH/8/Pz//c3Nz/3Nzc/9zc3P/c3Nz/29vb/9vb2//Y2Nj/zMzM/66urv9ra2v/Kioq/yIiIv8i + IiL/GRkZ/wAAAP8AAAD/Hx8f/ykpKf8pKSn/KSkp/ykpKf8pKSn/KSkp/ykpKf8pKSn/KSkp/ykpKf8p + KSn/KSkp/ykpKf8qKir/MjIy/zQ0NP80NDT/NDQ0/zQ0NP80NDT/NDQ0/zMzM/8vLy//Kysr/ykpKf8p + KSn/KSkp/ykpKf8fHx//AAAA/wAAAP8kJCT/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8w + MDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8wMDD/MDAw/zAwMP8w + MDD/MDAw/zAwMP8wMDD/MDAw/yQkJP8AAAD/AAAA/ykpKf83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83 + Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83 + Nzf/Nzc3/zc3N/83Nzf/Nzc3/zc3N/83Nzf/KSkp/wAAAP8AAAD2LCws/z4+Pv8+Pj7/Pj4+/z4+Pv8+ + Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+ + Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8+Pj7/Pj4+/z4+Pv8sLCz/AAAA9gAAAMEdHR3/RUVF/0VFRf9F + RUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9F + RUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/x0dHf8AAADBAAAAUAEBAf83 + Nzf/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9L + S0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/9LS0v/S0tL/0tLS/83Nzf/AQEB/wAAAFAA + AAAAAAAAigICAv8hISH/ODg4/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87 + Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/84ODj/ISEh/wICAv8A + AACKAAAAAAAAAAAAAAAAAAAAUAAAAMEAAAD1AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8A + AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPUA + AADBAAAAUAAAAAAAAAAAwAAAA4AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgAAAAcAAAAMoAAAAGAAAADAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAD8AAADEAwMD+gQEBP8EBAT/BAQE/wQEBP8EBAT/BAQE/wQEBP8EBAT/BAQE/wQEBP8E + BAT/BAQE/wQEBP8EBAT/BAQE/wQEBP8DAwP6AAAAxAAAAD8AAAAAAAAAPwgICPk5OTn/TExM/0xMTP9M + TEz/TExM/0xMTP9MTEz/TExM/0xMTP9MTEz/TExM/0xMTP9MTEz/TExM/0xMTP9MTEz/TExM/0xMTP9M + TEz/OTk5/wgICPkAAAA/AAAAxDQ0NP9FRUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9F + RUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/0VFRf9FRUX/RUVF/zQ0NP8AAADFAgIC+jw8PP88 + PDz/PDw8/zw8PP88PDz/PDw8/zw8PP88PDz/PDw8/zw8PP88PDz/PDw8/zw8PP88PDz/PDw8/zw8PP88 + PDz/PDw8/zw8PP88PDz/PDw8/zw8PP8CAgL6AwMD/zMzM/8zMzP/MzMz/zMzM/8zMzP/MzMz/zMzM/8z + MzP/MzMz/zMzM/8zMzP/MzMz/zMzM/8zMzP/MzMz/zMzM/8zMzP/MzMz/zMzM/8zMzP/MzMz/zMzM/8D + AwP/AgIC/ykpKf8pKSn/KSkp/ykpKf8pKSn/KSkp/ykpKf8pKSn/KSkp/ykpKf8pKSn/KSkp/ykpKf8p + KSn/KSkp/ykpKf8pKSn/KSkp/ykpKf8pKSn/KSkp/ykpKf8CAgL/AQEB/x8fH/8gHx//IiAf/yEgH/8g + Hx//ISAf/yEgH/8fHx//ISAf/yIgH/8gICD/Jycn/ygoKP8jIyP/Hx8f/x8fH/8fHx//ISEh/ygoKP8o + KCj/IyMj/x8fH/8BAQH/AQEB/xYWFv88Ixb/mDsM/4I2D/80IBb/ejIN/3EvDv8tHhb/djIQ/3kxDP9h + Rzr/r6+v/6Ghof95eXn/FxcX/xYWFv8WFhb/XFxc/4KCgv+BgYH/bm5u/xcXF/8BAQH/AAAA/w4ODv9U + JQ7/w0AA/649BP9IIQ7/jzEB/38sAv85HA7/jTIE/3ImAP+OX0j/urq6/5iYmP+Li4v/Dw8P/w4ODv8O + Dg7/d3d3/2JiYv9cXFz/b29v/w4ODv8AAAD/AAAA/wcHB/9PHwf/6VEF/89GAv9DGwf/v0AA/6Y4Af80 + Fgf/ozgC/4wuAP+LW0P/zs7O/6+vr/+Tk5P/CAgI/wcHB/8LCwv/ioqK/25ubv9kZGT/YmJi/wcHB/8A + AAD/AAAA/wICAv9LGwL/8mso/+BWEf8/FwL/6VIG/89FAP8wEgL/uj4A/6k4AP+KWD//4uLi/8fHx/+p + qan/KCgo/ywsLP9qamr/mZmZ/3x8fP97e3v/PDw8/wICAv8AAAD/AAAA/wAAAP9JGAD/9IRL/+xwMv+b + Pg//8m8t/+1ZDv+YMwH/3koA/8pDAP9+Uz3/8PDw/+Tk5P/V1dX/zMzM/8PDw/+ysrL/paWl/5mZmf+H + h4f/BAQE/wAAAP8AAAD/AAAA/wAAAP9IGAD/9p1x//accP/2nnL/9php//SBSP/ybiz/8WYh/8hGBf9O + Qz3/8/Pz//Hx8f/u7u7/6Ojo/+Hh4f/W1tb/yMjI/66urv+ioqL/GBgY/wAAAP8AAAD/AAAA/wMDA/8X + CQP/cjYZ/3g5Gf94ORr/eDgZ/3c1FP92MhD/YycJ/x0MA/9APz//9PT0//Pz8//b29v/gYGB/4CAgP+G + hob/1dXV/8vLy/+tra3/ZWVl/wMDA/8AAAD/AAAA/wgICP8ICAj/CAgI/wgICP8ICAj/CAgI/wgICP8I + CAj/CAgI/wgICP9DQ0P/9fX1//T09P+6urr/CQkJ/wgICP8JCQn/oKCg/9/f3//AwMD/g4OD/wgICP8A + AAD/AAAA/w8PD/8PDw//Dw8P/w8PD/8PDw//Dw8P/w8PD/8PDw//Dw8P/w8PD/9JSUn/9vb2//X19f/o + 6Oj/s7Oz/7Kysv+2trb/6urq/+3t7f/V1dX/enp6/w8PD/8AAAD/AQEB/xcXF/8XFxf/FxcX/xcXF/8X + Fxf/FxcX/xcXF/8XFxf/FxcX/xcXF/9PT0//9/f3//b29v/29vb/9fX1//X19f/09PT/8/Pz//Ly8v/c + 3Nz/NjY2/xcXF/8BAQH/AgIC/yAgIP8gICD/ICAg/yAgIP8gICD/ICAg/yAgIP8gICD/ICAg/yAgIP9D + Q0P/3Nzc/+Pj4//i4uL/4uLi/+Li4v/g4OD/09PT/6mpqf9ISEj/ISEh/yAgIP8CAgL/AgIC/yoqKv8q + Kir/Kioq/yoqKv8qKir/Kioq/yoqKv8qKir/Kioq/yoqKv8rKyv/MTEx/zIyMv8yMjL/MjIy/zIyMv8y + MjL/Li4u/ysrK/8qKir/Kioq/yoqKv8CAgL/AwMD/zQ0NP80NDT/NDQ0/zQ0NP80NDT/NDQ0/zQ0NP80 + NDT/NDQ0/zQ0NP80NDT/NDQ0/zQ0NP80NDT/NDQ0/zQ0NP80NDT/NDQ0/zQ0NP80NDT/NDQ0/zQ0NP8D + AwP/AgIC+j09Pf89PT3/PT09/z09Pf89PT3/PT09/z09Pf89PT3/PT09/z09Pf89PT3/PT09/z09Pf89 + PT3/PT09/z09Pf89PT3/PT09/z09Pf89PT3/PT09/z09Pf8DAwP6AAAAxDQ0NP9GRkb/RkZG/0ZGRv9G + Rkb/RkZG/0ZGRv9GRkb/RkZG/0ZGRv9GRkb/RkZG/0ZGRv9GRkb/RkZG/0ZGRv9GRkb/RkZG/0ZGRv9G + Rkb/RkZG/zQ0NP8AAADEAAAAPwgICPk5OTn/TExM/01NTf9NTU3/TU1N/01NTf9NTU3/TU1N/01NTf9N + TU3/TU1N/01NTf9NTU3/TU1N/01NTf9NTU3/TU1N/01NTf9MTEz/OTk5/wgICPkAAAA/AAAAAAAAAD8A + AADEAwMD+gUFBf8FBQX/BQUF/wUFBf8FBQX/BQUF/wUFBf8FBQX/BQUF/wUFBf8FBQX/BQUF/wUFBf8F + BQX/BQUF/wUFBf8DAwP6AAAAxAAAAD8AAAAAgAABQQAAAEEAAABBAAAAQQAAAEEAAABBAAAAQQAAAEEA + AABBAAAAQQAAAEEAAABBAAAAQQAAAEEAAABBAAAAQQAAAEEAAABBAAAAQQAAAEEAAABBAAAAQQAAAEGA + AAFBKAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMLCwvEHBwc/R0dHf8d + HR3/HR0d/x0dHf8dHR3/HR0d/x0dHf8dHR3/HR0d/x0dHf8cHBz9CwsLxAAAACMJCQnEQkJC/0dHR/9H + R0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0dHR/9HR0f/R0dH/0JCQv8JCQnEFRUV/To6Ov86 + Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/Ojo6/zo6Ov86Ojr/FRUV/RAQEP8s + LCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/ywsLP8sLCz/LCws/xAQEP8L + Cwv/IR8d/0crHf8oIR3/RSsd/ykhHf9BKR3/NCgi/1RUVP9ERET/HR0d/x0dHf87Ozv/UlJS/zExMf8L + Cwv/BgYG/zwfEP+uOgD/aCwO/3goAf9TJA3/fSwD/4BKL/+urq7/i4uL/xEREf8QEBD/bm5u/11dXf9Q + UFD/BgYG/wICAv82Fgb/6FQK/28pBv/AQAD/XiIF/6M3Af+MTCz/y8vL/56env8HBwf/ERER/4iIiP9q + amr/QEBA/wICAv8AAAD/MREA//N7QP+dPxD/72Ic/5k1Av/QRQD/lU0p/+np6f/Pz8//oKCg/6qqqv+a + mpr/ioqK/wsLC/8AAAD/AAAA/ygOAf/oiVr/6otb/+mCT//maiv/0VMU/1c5Kf/z8/P/8PDw/+Pj4//a + 2tr/y8vL/6ioqP8fHx//AAAA/wICAv8HBwf/DwkH/xAKB/8QCgf/DwoH/woIB/8uLi7/9fX1/8/Pz/8T + ExP/FBQU/7+/v//Dw8P/VVVV/wICAv8GBgb/ERER/xEREf8RERH/ERER/xEREf8RERH/Nzc3//b29v/w + 8PD/ycnJ/8rKyv/u7u7/4eHh/0xMTP8GBgb/CwsL/x4eHv8eHh7/Hh4e/x4eHv8eHh7/Hh4e/zo6Ov/m + 5ub/6enp/+np6f/n5+f/2NjY/4qKiv8gICD/CwsL/xAQEP8tLS3/LS0t/y0tLf8tLS3/LS0t/y0tLf8t + LS3/MTEx/zIyMv8yMjL/MjIy/y4uLv8tLS3/LS0t/xAQEP8VFRX9Ozs7/zs7O/87Ozv/Ozs7/zs7O/87 + Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/8VFRX9CQkJxENDQ/9ISEj/SEhI/0hISP9I + SEj/SEhI/0hISP9ISEj/SEhI/0hISP9ISEj/SEhI/0hISP9DQ0P/CQkJxAAAACMLCwvEHBwc/R0dHf8d + HR3/HR0d/x0dHf8dHR3/HR0d/x0dHf8dHR3/HR0d/x0dHf8cHBz9CwsLxAAAACMAAKxBAACsQQAArEEA + AKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxB + + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerForm.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerForm.Designer.cs new file mode 100644 index 00000000..050ae5a4 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerForm.Designer.cs @@ -0,0 +1,138 @@ +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms +{ + partial class CredentialManagerForm + { + /// + /// 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(CredentialManagerForm)); + this.panelMain = new System.Windows.Forms.Panel(); + this.olvPageList = new Controls.Base.NGListView(); + this.olvColumnPage = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); + this.pnlBottom = new System.Windows.Forms.Panel(); + this.buttonClose = new Controls.Base.NGButton(); + ((System.ComponentModel.ISupportInitialize)(this.olvPageList)).BeginInit(); + this.pnlBottom.SuspendLayout(); + this.SuspendLayout(); + // + // panelMain + // + this.panelMain.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.panelMain.Location = new System.Drawing.Point(152, 0); + this.panelMain.Name = "panelMain"; + this.panelMain.Size = new System.Drawing.Size(555, 375); + this.panelMain.TabIndex = 4; + // + // olvPageList + // + this.olvPageList.AllColumns.Add(this.olvColumnPage); + this.olvPageList.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.olvPageList.CellEditUseWholeCell = false; + this.olvPageList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.olvColumnPage}); + this.olvPageList.Cursor = System.Windows.Forms.Cursors.Default; + this.olvPageList.FullRowSelect = true; + this.olvPageList.HasCollapsibleGroups = false; + this.olvPageList.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.olvPageList.HideSelection = false; + this.olvPageList.IsSearchOnSortColumn = false; + this.olvPageList.Location = new System.Drawing.Point(0, 0); + this.olvPageList.MultiSelect = false; + this.olvPageList.Name = "olvPageList"; + this.olvPageList.RowHeight = 30; + this.olvPageList.SelectAllOnControlA = false; + this.olvPageList.ShowFilterMenuOnRightClick = false; + this.olvPageList.ShowGroups = false; + this.olvPageList.ShowHeaderInAllViews = false; + this.olvPageList.Size = new System.Drawing.Size(154, 424); + this.olvPageList.TabIndex = 5; + this.olvPageList.UseCompatibleStateImageBehavior = false; + this.olvPageList.View = System.Windows.Forms.View.Details; + // + // olvColumnPage + // + this.olvColumnPage.AspectName = "PageName"; + this.olvColumnPage.CellVerticalAlignment = System.Drawing.StringAlignment.Center; + this.olvColumnPage.FillsFreeSpace = true; + this.olvColumnPage.Groupable = false; + this.olvColumnPage.Hideable = false; + this.olvColumnPage.IsEditable = false; + this.olvColumnPage.Searchable = false; + this.olvColumnPage.Sortable = false; + // + // pnlBottom + // + this.pnlBottom.Controls.Add(this.buttonClose); + this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlBottom.Location = new System.Drawing.Point(0, 374); + this.pnlBottom.Name = "pnlBottom"; + this.pnlBottom.Size = new System.Drawing.Size(707, 50); + this.pnlBottom.TabIndex = 6; + // + // buttonClose + // + this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonClose.Location = new System.Drawing.Point(620, 15); + this.buttonClose.Name = "buttonClose"; + this.buttonClose.Size = new System.Drawing.Size(75, 23); + this.buttonClose.TabIndex = 1; + this.buttonClose.Text = "Close"; + this.buttonClose.UseVisualStyleBackColor = true; + this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click); + // + // CredentialManagerForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(707, 424); + this.Controls.Add(this.olvPageList); + this.Controls.Add(this.panelMain); + this.Controls.Add(this.pnlBottom); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.Name = "CredentialManagerForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Credential Manager"; + ((System.ComponentModel.ISupportInitialize)(this.olvPageList)).EndInit(); + this.pnlBottom.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.Panel panelMain; + private Controls.Base.NGListView olvPageList; + private System.Windows.Forms.Panel pnlBottom; + private Controls.Base.NGButton buttonClose; + private BrightIdeasSoftware.OLVColumn olvColumnPage; + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerForm.cs b/mRemoteV1/UI/Forms/CredentialManagerForm.cs new file mode 100644 index 00000000..35a992b9 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerForm.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using mRemoteNG.UI.Forms.CredentialManagerPages; +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms +{ + public partial class CredentialManagerForm : Form + { + private readonly IEnumerable _pageControls; + + public CredentialManagerForm(IEnumerable pageControls) + { + if (pageControls == null) + throw new ArgumentNullException(nameof(pageControls)); + + _pageControls = pageControls; + InitializeComponent(); + ThemeManager.getInstance().ThemeChanged += ApplyTheme; + ApplyTheme(); + ApplyLanguage(); + SetupListView(); + olvPageList.SelectedIndex = 0; + } + + private void SetupListView() + { + olvColumnPage.ImageGetter = ImageGetter; + olvPageList.SelectionChanged += (sender, args) => ShowPage(olvPageList.SelectedObject as Control); + olvPageList.SetObjects(_pageControls); + } + + private void ShowPage(Control page) + { + if (page == null) return; + panelMain.Controls.Clear(); + panelMain.Controls.Add(page); + page.Dock = DockStyle.Fill; + } + + private object ImageGetter(object rowObject) + { + var page = rowObject as ICredentialManagerPage; + return page?.PageIcon; + } + private void ApplyTheme() + { + if (!ThemeManager.getInstance().ThemingActive) return; + BackColor = ThemeManager.getInstance().ActiveTheme.ExtendedPalette.getColor("Dialog_Background"); + ForeColor = ThemeManager.getInstance().ActiveTheme.ExtendedPalette.getColor("Dialog_Foreground"); + } + + private void ApplyLanguage() + { + Text = Language.strCredentialManager; + buttonClose.Text = Language.strButtonClose; + } + + private void buttonClose_Click(object sender, EventArgs e) + { + Close(); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerForm.resx b/mRemoteV1/UI/Forms/CredentialManagerForm.resx new file mode 100644 index 00000000..ab36ee6a --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerForm.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + AAABAAEAEBAAAAAAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAA + AAA+ncp7NpbR5TOQzOsyi8vtO5XCh////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BQqzhzcTr9/9/4fb/n+b3/zKKye86kLqR////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUSw4//G9Pv/Q9bx/0jb9f+C4fX/MYjI8TmNt5n///8B////Af///wH///8B////Af// + /wH///8B////Af///wFEsOPzu+/6/znR8f8oxe7/Ttz2/4Xi9/8yi8rvOYu1m////wH///8B////Af// + /wH///8B////Af///wH///8BRLDj//D8/v+w7vr/Q9j0/yjI7v9B1/T/ieL3/zKLy+04iLKj////Af// + /wH///8B////Af///wH///8B////AUSw45dEsOP/RLDj/6vq+f9O2PP/K8nv/z3W8/+K4ff/MorK7zOD + uN0pfdb/LIXY/zaOwcv///8B////Af///wH///8B////AUSw4//x/P7/u/H7/3vk9v8o0vD/N9T1/4Pg + 9v8+qeP/oPP8/6n1/P8rgtf/NYu/z////wH///8B////Af///wFEsOOXRLDj/0Wy4/92xer/rO76/znW + 8v9N2/X/ZeT3/zzO8v8yye//he/7/yuB1/81iLvV////Af///wH///8B////Af///wFEsOMDRLDjl2/E + 6v+A5ff/PdHx/13b9f9p3/b/UNfz/zTN7/+F7/v/KX/W/zSGutn///8B////Af///wH///8B////AUSw + 4//V9/z/ief4/37k9/9+5Pf/fuT3/4Ll9/9H1vL/OM7w/671/P8pfNb/////Af///wH///8B////Af// + /wFEsOP/vvL7/37k9/9+5Pf/geX3/5Tp+P+88fv/i9rz/0nd9f/B+P3/MJDa/////wH///8B////Af// + /wH///8BRLDj/974/P+N5/j/fuT3/5Tp+P+86fj/RLDj/0Ks4//u/P7/Mpjd/zmVyL////8B////Af// + /wH///8B////AUSw45dEsOP/zvX8/43n+P+h7Pn/RLDj/0Sw4///////OaHf/zmVyL////8B////Af// + /wH///8B////Af///wH///8BRLDjl0Sw4//O9fz/nuv5/77y+//+////RLDj/0Kr353///8B////Af// + /wH///8B////Af///wH///8B////Af///wFEsOOXRLDj/974/P/e+Pz/RLDj/0Sw45f///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AUSw45dEsOP/RLDj/0Sw45f///8B////Af// + /wH///8BAAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA + //8AAP//AAD//w== + + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.Designer.cs new file mode 100644 index 00000000..80ff6115 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.Designer.cs @@ -0,0 +1,226 @@ +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + sealed partial class CredentialEditorPage + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.labelId = new Controls.Base.NGLabel(); + this.textBoxId = new Controls.Base.NGTextBox(); + this.buttonCancel = new Controls.Base.NGButton(); + this.buttonAccept = new Controls.Base.NGButton(); + this.labelPassword = new Controls.Base.NGLabel(); + this.textBoxPassword = new mRemoteNG.UI.Controls.SecureTextBox(); + this.labelDomain = new Controls.Base.NGLabel(); + this.textBoxDomain = new Controls.Base.NGTextBox(); + this.labelUsername = new Controls.Base.NGLabel(); + this.textBoxUsername = new Controls.Base.NGTextBox(); + this.labelTitle = new Controls.Base.NGLabel(); + this.textBoxTitle = new Controls.Base.NGTextBox(); + this.labelRepositoryName = new Controls.Base.NGLabel(); + this.textBoxRepositoryName = new Controls.Base.NGTextBox(); + this.SuspendLayout(); + // + // labelId + // + this.labelId.AutoSize = true; + this.labelId.Location = new System.Drawing.Point(3, 35); + this.labelId.Name = "labelId"; + this.labelId.Size = new System.Drawing.Size(16, 13); + this.labelId.TabIndex = 23; + this.labelId.Text = "Id"; + // + // textBoxId + // + this.textBoxId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxId.Location = new System.Drawing.Point(89, 35); + this.textBoxId.Name = "textBoxId"; + this.textBoxId.ReadOnly = true; + this.textBoxId.Size = new System.Drawing.Size(208, 20); + this.textBoxId.TabIndex = 22; + // + // buttonCancel + // + this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(222, 174); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(75, 23); + this.buttonCancel.TabIndex = 21; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.UseVisualStyleBackColor = true; + this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); + // + // buttonAccept + // + this.buttonAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonAccept.Location = new System.Drawing.Point(141, 174); + this.buttonAccept.Name = "buttonAccept"; + this.buttonAccept.Size = new System.Drawing.Size(75, 23); + this.buttonAccept.TabIndex = 20; + this.buttonAccept.Text = "Accept"; + this.buttonAccept.UseVisualStyleBackColor = true; + this.buttonAccept.Click += new System.EventHandler(this.buttonAccept_Click_1); + // + // labelPassword + // + this.labelPassword.AutoSize = true; + this.labelPassword.Location = new System.Drawing.Point(3, 139); + this.labelPassword.Name = "labelPassword"; + this.labelPassword.Size = new System.Drawing.Size(53, 13); + this.labelPassword.TabIndex = 19; + this.labelPassword.Text = "Password"; + // + // textBoxPassword + // + this.textBoxPassword.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxPassword.Location = new System.Drawing.Point(89, 139); + this.textBoxPassword.Name = "textBoxPassword"; + this.textBoxPassword.PasswordChar = '•'; + this.textBoxPassword.Size = new System.Drawing.Size(208, 20); + this.textBoxPassword.TabIndex = 18; + // + // labelDomain + // + this.labelDomain.AutoSize = true; + this.labelDomain.Location = new System.Drawing.Point(3, 113); + this.labelDomain.Name = "labelDomain"; + this.labelDomain.Size = new System.Drawing.Size(43, 13); + this.labelDomain.TabIndex = 17; + this.labelDomain.Text = "Domain"; + // + // textBoxDomain + // + this.textBoxDomain.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxDomain.Location = new System.Drawing.Point(89, 113); + this.textBoxDomain.Name = "textBoxDomain"; + this.textBoxDomain.Size = new System.Drawing.Size(208, 20); + this.textBoxDomain.TabIndex = 16; + // + // labelUsername + // + this.labelUsername.AutoSize = true; + this.labelUsername.Location = new System.Drawing.Point(3, 87); + this.labelUsername.Name = "labelUsername"; + this.labelUsername.Size = new System.Drawing.Size(55, 13); + this.labelUsername.TabIndex = 15; + this.labelUsername.Text = "Username"; + // + // textBoxUsername + // + this.textBoxUsername.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxUsername.Location = new System.Drawing.Point(89, 87); + this.textBoxUsername.Name = "textBoxUsername"; + this.textBoxUsername.Size = new System.Drawing.Size(208, 20); + this.textBoxUsername.TabIndex = 14; + // + // labelTitle + // + this.labelTitle.AutoSize = true; + this.labelTitle.Location = new System.Drawing.Point(3, 61); + this.labelTitle.Name = "labelTitle"; + this.labelTitle.Size = new System.Drawing.Size(27, 13); + this.labelTitle.TabIndex = 13; + this.labelTitle.Text = "Title"; + // + // textBoxTitle + // + this.textBoxTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxTitle.Location = new System.Drawing.Point(89, 61); + this.textBoxTitle.Name = "textBoxTitle"; + this.textBoxTitle.Size = new System.Drawing.Size(208, 20); + this.textBoxTitle.TabIndex = 12; + // + // labelRepositoryName + // + this.labelRepositoryName.AutoSize = true; + this.labelRepositoryName.Location = new System.Drawing.Point(3, 9); + this.labelRepositoryName.Name = "labelRepositoryName"; + this.labelRepositoryName.Size = new System.Drawing.Size(57, 13); + this.labelRepositoryName.TabIndex = 24; + this.labelRepositoryName.Text = "Repository"; + // + // textBoxRepositoryName + // + this.textBoxRepositoryName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxRepositoryName.Location = new System.Drawing.Point(89, 9); + this.textBoxRepositoryName.Name = "textBoxRepositoryName"; + this.textBoxRepositoryName.ReadOnly = true; + this.textBoxRepositoryName.Size = new System.Drawing.Size(208, 20); + this.textBoxRepositoryName.TabIndex = 25; + // + // CredentialEditorPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.textBoxRepositoryName); + this.Controls.Add(this.labelRepositoryName); + this.Controls.Add(this.labelId); + this.Controls.Add(this.textBoxId); + this.Controls.Add(this.buttonCancel); + this.Controls.Add(this.buttonAccept); + this.Controls.Add(this.labelPassword); + this.Controls.Add(this.textBoxPassword); + this.Controls.Add(this.labelDomain); + this.Controls.Add(this.textBoxDomain); + this.Controls.Add(this.labelUsername); + this.Controls.Add(this.textBoxUsername); + this.Controls.Add(this.labelTitle); + this.Controls.Add(this.textBoxTitle); + this.MinimumSize = new System.Drawing.Size(300, 200); + this.Name = "CredentialEditorPage"; + this.Size = new System.Drawing.Size(300, 200); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.Base.NGLabel labelId; + private Controls.Base.NGTextBox textBoxId; + private Controls.Base.NGButton buttonCancel; + private Controls.Base.NGButton buttonAccept; + private Controls.Base.NGLabel labelPassword; + private Controls.SecureTextBox textBoxPassword; + private Controls.Base.NGLabel labelDomain; + private Controls.Base.NGTextBox textBoxDomain; + private Controls.Base.NGLabel labelUsername; + private Controls.Base.NGTextBox textBoxUsername; + private Controls.Base.NGLabel labelTitle; + private Controls.Base.NGTextBox textBoxTitle; + private Controls.Base.NGLabel labelRepositoryName; + private Controls.Base.NGTextBox textBoxRepositoryName; + } +} diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs new file mode 100644 index 00000000..45188fdc --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.cs @@ -0,0 +1,73 @@ +using System; +using System.Windows.Forms; +using mRemoteNG.Credential; +using mRemoteNG.Security; +using mRemoteNG.UI.Controls.PageSequence; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + public sealed partial class CredentialEditorPage : SequencedControl + { + private readonly ICredentialRecord _credentialRecord; + private readonly ICredentialRepository _credentialRepository; + + public CredentialEditorPage(ICredentialRecord credentialRecord, ICredentialRepository credentialRepository) + { + if (credentialRecord == null) + throw new ArgumentNullException(nameof(credentialRecord)); + if (credentialRepository == null) + throw new ArgumentNullException(nameof(credentialRepository)); + + InitializeComponent(); + ApplyTheme(); + ApplyLanguage(); + _credentialRecord = credentialRecord; + _credentialRepository = credentialRepository; + FillInForm(); + Dock = DockStyle.Fill; + } + + private void ApplyLanguage() + { + Text = Language.strCredentialEditor; + labelId.Text = Language.strID; + labelTitle.Text = Language.strTitle; + labelUsername.Text = Language.strPropertyNameUsername; + labelDomain.Text = Language.strPropertyNameDomain; + labelPassword.Text = Language.strPropertyNamePassword; + buttonAccept.Text = Language.strAccept; + buttonCancel.Text = Language.strButtonCancel; + } + + private void FillInForm() + { + textBoxRepositoryName.Text = _credentialRepository.Config.Title; + textBoxId.Text = _credentialRecord.Id.ToString(); + textBoxTitle.Text = _credentialRecord.Title; + textBoxUsername.Text = _credentialRecord.Username; + textBoxDomain.Text = _credentialRecord.Domain; + textBoxPassword.Text = _credentialRecord.Password.ConvertToUnsecureString(); + } + + private void SaveFormToCredential() + { + _credentialRecord.Title = textBoxTitle.Text; + _credentialRecord.Username = textBoxUsername.Text; + _credentialRecord.Domain = textBoxDomain.Text; + _credentialRecord.Password = textBoxPassword.Text.ConvertToSecureString(); + } + + private void buttonAccept_Click_1(object sender, EventArgs e) + { + SaveFormToCredential(); + if (!_credentialRepository.CredentialRecords.Contains(_credentialRecord)) + _credentialRepository.CredentialRecords.Add(_credentialRecord); + RaiseNextPageEvent(); + } + + private void buttonCancel_Click(object sender, EventArgs e) + { + RaisePreviousPageEvent(); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.resx b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialEditorPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.Designer.cs new file mode 100644 index 00000000..561663bd --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.Designer.cs @@ -0,0 +1,108 @@ +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + sealed partial class CredentialListPage + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.buttonAdd = new Controls.Base.NGButton(); + this.buttonRemove = new Controls.Base.NGButton(); + this.buttonEdit = new Controls.Base.NGButton(); + this.credentialRecordListView = new mRemoteNG.UI.Controls.CredentialRecordListView(); + this.SuspendLayout(); + // + // buttonAdd + // + this.buttonAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonAdd.Image = global::mRemoteNG.Resources.key_add; + this.buttonAdd.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonAdd.Location = new System.Drawing.Point(71, 241); + this.buttonAdd.Name = "buttonAdd"; + this.buttonAdd.Size = new System.Drawing.Size(99, 32); + this.buttonAdd.TabIndex = 3; + this.buttonAdd.Text = "Add"; + this.buttonAdd.UseVisualStyleBackColor = true; + this.buttonAdd.Click += new System.EventHandler(this.buttonAdd_Click); + // + // buttonRemove + // + this.buttonRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonRemove.Enabled = false; + this.buttonRemove.Image = global::mRemoteNG.Resources.key_delete; + this.buttonRemove.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonRemove.Location = new System.Drawing.Point(281, 241); + this.buttonRemove.Name = "buttonRemove"; + this.buttonRemove.Size = new System.Drawing.Size(99, 32); + this.buttonRemove.TabIndex = 4; + this.buttonRemove.Text = "Remove"; + this.buttonRemove.UseVisualStyleBackColor = true; + this.buttonRemove.Click += new System.EventHandler(this.buttonRemove_Click); + // + // buttonEdit + // + this.buttonEdit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonEdit.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonEdit.Location = new System.Drawing.Point(176, 241); + this.buttonEdit.Name = "buttonEdit"; + this.buttonEdit.Size = new System.Drawing.Size(99, 32); + this.buttonEdit.TabIndex = 5; + this.buttonEdit.Text = "Edit"; + this.buttonEdit.UseVisualStyleBackColor = true; + this.buttonEdit.Click += new System.EventHandler(this.buttonEdit_Click); + // + // credentialRecordListView + // + this.credentialRecordListView.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.credentialRecordListView.Location = new System.Drawing.Point(0, 0); + this.credentialRecordListView.Name = "credentialRecordListView"; + this.credentialRecordListView.Size = new System.Drawing.Size(383, 235); + this.credentialRecordListView.TabIndex = 6; + // + // CredentialListPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.credentialRecordListView); + this.Controls.Add(this.buttonEdit); + this.Controls.Add(this.buttonAdd); + this.Controls.Add(this.buttonRemove); + this.Name = "CredentialListPage"; + this.Size = new System.Drawing.Size(383, 276); + this.ResumeLayout(false); + + } + + #endregion + private Controls.Base.NGButton buttonAdd; + private Controls.Base.NGButton buttonRemove; + private Controls.Base.NGButton buttonEdit; + private Controls.CredentialRecordListView credentialRecordListView; + } +} diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.cs new file mode 100644 index 00000000..d627ba64 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using BrightIdeasSoftware; +using mRemoteNG.Credential; +using mRemoteNG.Tree; +using mRemoteNG.UI.Controls.PageSequence; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + public sealed partial class CredentialListPage : SequencedControl, ICredentialManagerPage + { + private readonly ICredentialRepositoryList _credentialRepositoryList; + + public string PageName { get; } = Language.strCategoryCredentials; + public Image PageIcon { get; } = Resources.key; + public IConfirm> DeletionConfirmer { get; set; } = new AlwaysConfirmYes(); + + public CredentialListPage(ICredentialRepositoryList credentialRepositoryList) + { + if (credentialRepositoryList == null) + throw new ArgumentNullException(nameof(credentialRepositoryList)); + + _credentialRepositoryList = credentialRepositoryList; + InitializeComponent(); + ApplyTheme(); + ApplyLanguage(); + credentialRecordListView.CellClick += HandleCellDoubleClick; + credentialRecordListView.SelectionChanged += ObjectListView1OnSelectionChanged; + credentialRecordListView.KeyDown += ObjectListView1OnEnterPressed; + credentialRecordListView.KeyDown += OnAPressed; + credentialRecordListView.KeyDown += OnDeletePressed; + credentialRecordListView.CredentialRepositoryList = _credentialRepositoryList; + } + + + + private void ApplyLanguage() + { + Text = Language.strCredentialManager; + buttonAdd.Text = Language.strAdd; + buttonRemove.Text = Language.strRemove; + } + + private void RemoveSelectedCredential() + { + var selectedCredential = credentialRecordListView.SelectedObject; + if (!DeletionConfirmer.Confirm(new[] { selectedCredential.Key })) return; + selectedCredential.Value.CredentialRecords.Remove(selectedCredential.Key); + RaiseCredentialsChangedEvent(this); + } + + private void RemoveSelectedCredentials() + { + var selectedCredentials = credentialRecordListView.SelectedObjects.ToArray(); + if (!DeletionConfirmer.Confirm(selectedCredentials.Select(i => i.Key))) return; + foreach(var item in selectedCredentials) + item.Value.CredentialRecords.Remove(item.Key); + RaiseCredentialsChangedEvent(this); + } + + private void HandleCellDoubleClick(object sender, CellClickEventArgs cellClickEventArgs) + { + if (cellClickEventArgs.ClickCount < 2) return; + EditCredential(credentialRecordListView.SelectedObject); + } + + private void AddCredential() + { + var sequence = new PageSequence(Parent, + this, + new CredentialRepositorySelectionPage(_credentialRepositoryList), + new SequencedControl(), + this + ); + RaiseNextPageEvent(); + } + + private void EditCredential(KeyValuePair credentialAndRepository) + { + if (credentialAndRepository.Key == null || credentialAndRepository.Value == null) return; + var sequence = new PageSequence(Parent, + this, + new CredentialEditorPage(credentialAndRepository.Key, credentialAndRepository.Value), + this + ); + RaiseNextPageEvent(); + } + + private void buttonAdd_Click(object sender, EventArgs e) + { + AddCredential(); + } + + private void buttonEdit_Click(object sender, EventArgs e) + { + EditCredential(credentialRecordListView.SelectedObject); + } + + private void buttonRemove_Click(object sender, EventArgs e) + { + if (credentialRecordListView.MultipleObjectsSelected) + RemoveSelectedCredentials(); + else + RemoveSelectedCredential(); + } + + private void ObjectListView1OnEnterPressed(object sender, KeyEventArgs keyEventArgs) + { + if (keyEventArgs.KeyCode != Keys.Enter) return; + EditCredential(credentialRecordListView.SelectedObject); + keyEventArgs.Handled = true; + keyEventArgs.SuppressKeyPress = true; + } + + private void OnAPressed(object sender, KeyEventArgs keyEventArgs) + { + if (keyEventArgs.KeyCode != Keys.A) return; + AddCredential(); + keyEventArgs.Handled = true; + keyEventArgs.SuppressKeyPress = true; + } + + private void OnDeletePressed(object sender, KeyEventArgs keyEventArgs) + { + if (keyEventArgs.KeyCode != Keys.Delete) return; + if (credentialRecordListView.MultipleObjectsSelected) + RemoveSelectedCredentials(); + else + RemoveSelectedCredential(); + keyEventArgs.Handled = true; + keyEventArgs.SuppressKeyPress = true; + } + + private void ObjectListView1OnSelectionChanged(object sender, EventArgs eventArgs) + { + buttonRemove.Enabled = credentialRecordListView.SelectedObjects.Any(); + } + + public event EventHandler CredentialsChanged; + private void RaiseCredentialsChangedEvent(object sender) + { + CredentialsChanged?.Invoke(sender, EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.resx b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialListPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.Designer.cs new file mode 100644 index 00000000..b690be9c --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.Designer.cs @@ -0,0 +1,131 @@ +using mRemoteNG.Credential.Repositories; +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + sealed partial class CredentialRepositoriesPage + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + CredentialRepositoryList credentialRepositoryList1 = new CredentialRepositoryList(); + this.buttonAdd = new Controls.Base.NGButton(); + this.buttonRemove = new Controls.Base.NGButton(); + this.buttonEdit = new Controls.Base.NGButton(); + this.credentialRepositoryListView = new mRemoteNG.UI.Controls.CredentialRepositoryListView(); + this.buttonToggleLoad = new Controls.Base.NGButton(); + this.SuspendLayout(); + // + // buttonAdd + // + this.buttonAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonAdd.Image = global::mRemoteNG.Resources.key_add; + this.buttonAdd.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonAdd.Location = new System.Drawing.Point(105, 237); + this.buttonAdd.Name = "buttonAdd"; + this.buttonAdd.Size = new System.Drawing.Size(99, 32); + this.buttonAdd.TabIndex = 5; + this.buttonAdd.Text = "Add"; + this.buttonAdd.UseVisualStyleBackColor = true; + this.buttonAdd.Click += new System.EventHandler(this.buttonAdd_Click); + // + // buttonRemove + // + this.buttonRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonRemove.Enabled = false; + this.buttonRemove.Image = global::mRemoteNG.Resources.key_delete; + this.buttonRemove.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonRemove.Location = new System.Drawing.Point(315, 237); + this.buttonRemove.Name = "buttonRemove"; + this.buttonRemove.Size = new System.Drawing.Size(99, 32); + this.buttonRemove.TabIndex = 6; + this.buttonRemove.Text = "Remove"; + this.buttonRemove.UseVisualStyleBackColor = true; + this.buttonRemove.Click += new System.EventHandler(this.buttonRemove_Click); + // + // buttonEdit + // + this.buttonEdit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonEdit.Enabled = false; + this.buttonEdit.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonEdit.Location = new System.Drawing.Point(210, 237); + this.buttonEdit.Name = "buttonEdit"; + this.buttonEdit.Size = new System.Drawing.Size(99, 32); + this.buttonEdit.TabIndex = 8; + this.buttonEdit.Text = "Edit"; + this.buttonEdit.UseVisualStyleBackColor = true; + this.buttonEdit.Click += new System.EventHandler(this.buttonEdit_Click); + // + // credentialRepositoryListView + // + this.credentialRepositoryListView.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.credentialRepositoryListView.CredentialRepositoryList = credentialRepositoryList1; + this.credentialRepositoryListView.DoubleClickHandler = null; + this.credentialRepositoryListView.Location = new System.Drawing.Point(0, 0); + this.credentialRepositoryListView.Name = "credentialRepositoryListView"; + this.credentialRepositoryListView.RepositoryFilter = null; + this.credentialRepositoryListView.Size = new System.Drawing.Size(417, 231); + this.credentialRepositoryListView.TabIndex = 9; + // + // buttonToggleLoad + // + this.buttonToggleLoad.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonToggleLoad.Enabled = false; + this.buttonToggleLoad.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonToggleLoad.Location = new System.Drawing.Point(3, 237); + this.buttonToggleLoad.Name = "buttonToggleLoad"; + this.buttonToggleLoad.Size = new System.Drawing.Size(99, 32); + this.buttonToggleLoad.TabIndex = 10; + this.buttonToggleLoad.Text = "Load"; + this.buttonToggleLoad.UseVisualStyleBackColor = true; + this.buttonToggleLoad.Click += new System.EventHandler(this.buttonToggleLoad_Click); + // + // CredentialRepositoriesPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.buttonToggleLoad); + this.Controls.Add(this.credentialRepositoryListView); + this.Controls.Add(this.buttonEdit); + this.Controls.Add(this.buttonAdd); + this.Controls.Add(this.buttonRemove); + this.Name = "CredentialRepositoriesPage"; + this.Size = new System.Drawing.Size(417, 272); + this.ResumeLayout(false); + + } + + #endregion + + private Controls.Base.NGButton buttonAdd; + private Controls.Base.NGButton buttonRemove; + private Controls.Base.NGButton buttonEdit; + private Controls.CredentialRepositoryListView credentialRepositoryListView; + private Controls.Base.NGButton buttonToggleLoad; + } +} diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs new file mode 100644 index 00000000..153ee0e8 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.cs @@ -0,0 +1,112 @@ +using System; +using System.Drawing; +using System.Windows.Forms; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.UI.Controls; +using mRemoteNG.UI.Controls.PageSequence; +using mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages; +using mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositorySelectors; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + public sealed partial class CredentialRepositoriesPage : SequencedControl, ICredentialManagerPage + { + private readonly ICredentialRepositoryList _providerCatalog; + private readonly UnlockerFormFactory _unlockerFactory; + + public string PageName { get; } = "Sources"; + public Image PageIcon { get; } = Resources.folder_key; + + public CredentialRepositoriesPage(ICredentialRepositoryList providerCatalog, UnlockerFormFactory unlockerFactory) + { + if (providerCatalog == null) + throw new ArgumentNullException(nameof(providerCatalog)); + if (unlockerFactory == null) + throw new ArgumentNullException(nameof(unlockerFactory)); + + _providerCatalog = providerCatalog; + _unlockerFactory = unlockerFactory; + InitializeComponent(); + ApplyTheme(); + credentialRepositoryListView.CredentialRepositoryList = providerCatalog; + credentialRepositoryListView.SelectionChanged += (sender, args) => UpdateUi(); + credentialRepositoryListView.DoubleClickHandler = EditRepository; + } + + + + private void UpdateUi() + { + var selectedRepository = credentialRepositoryListView.SelectedRepository; + buttonRemove.Enabled = selectedRepository != null; + buttonEdit.Enabled = selectedRepository != null; + buttonToggleLoad.Enabled = selectedRepository != null; + UpdateLoadToggleButton(selectedRepository); + } + + private void buttonAdd_Click(object sender, EventArgs e) + { + var addRepoSequence = new PageSequence(Parent, + this, + new CredentialRepositoryTypeSelectionPage( + new ISelectionTarget[] + { + new XmlCredentialRepositorySelector(), + //new KeePassRepositorySelector() + }, + _providerCatalog + ) + { Dock = DockStyle.Fill }, + new SequencedControl(), + this + ); + RaiseNextPageEvent(); + } + + private void buttonEdit_Click(object sender, EventArgs e) + { + var selectedRepository = credentialRepositoryListView.SelectedRepository; + if (selectedRepository == null) return; + EditRepository(selectedRepository); + } + + private bool EditRepository(ICredentialRepository repository) + { + if (!repository.IsLoaded) return false; + var editorPage = CredentialRepositoryPageEditorFactory.BuildXmlCredentialRepositoryEditorPage(repository.Config, _providerCatalog); + var pageSequence = new PageSequence(Parent, + this, + editorPage, + this + ); + RaiseNextPageEvent(); + return true; + } + + private void buttonRemove_Click(object sender, EventArgs e) + { + var selectedRepository = credentialRepositoryListView.SelectedRepository; + if (selectedRepository == null) return; + if (_providerCatalog.Contains(selectedRepository.Config.Id)) + _providerCatalog.RemoveProvider(selectedRepository); + } + + private void UpdateLoadToggleButton(ICredentialRepository selectedRepository) + { + if (selectedRepository == null) return; + buttonToggleLoad.Text = selectedRepository.IsLoaded ? "Unload" : "Load"; + } + + private void buttonToggleLoad_Click(object sender, EventArgs e) + { + var selectedRepository = credentialRepositoryListView.SelectedRepository; + if (selectedRepository.IsLoaded) + selectedRepository.UnloadCredentials(); + else + _unlockerFactory.Build(new[] {selectedRepository}).ShowDialog(this); + credentialRepositoryListView.RefreshObjects(); + UpdateUi(); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.resx b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoriesPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs new file mode 100644 index 00000000..76940ac9 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/CredentialRepositoryPageEditorFactory.cs @@ -0,0 +1,15 @@ +using System.Windows.Forms; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.UI.Controls.PageSequence; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages +{ + public class CredentialRepositoryPageEditorFactory + { + public static SequencedControl BuildXmlCredentialRepositoryEditorPage(T config, ICredentialRepositoryList repositoryList) where T : ICredentialRepositoryConfig + { + return new XmlCredentialRepositoryEditorPage(config, repositoryList) {Dock = DockStyle.Fill}; + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.Designer.cs new file mode 100644 index 00000000..7953bbbb --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.Designer.cs @@ -0,0 +1,198 @@ +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages +{ + partial class XmlCredentialRepositoryEditorPage + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.labelPageTitle = new Controls.Base.NGLabel(); + this.buttonBrowseFiles = new Controls.Base.NGButton(); + this.labelFilePath = new Controls.Base.NGLabel(); + this.textBoxFilePath = new Controls.Base.NGTextBox(); + this.selectFilePathDialog = new System.Windows.Forms.SaveFileDialog(); + this.buttonConfirm = new Controls.Base.NGButton(); + this.textBoxId = new Controls.Base.NGTextBox(); + this.labelId = new Controls.Base.NGLabel(); + this.buttonBack = new Controls.Base.NGButton(); + this.newPasswordBoxes = new mRemoteNG.UI.Controls.NewPasswordWithVerification(); + this.textBoxTitle = new Controls.Base.NGTextBox(); + this.labelTitle = new Controls.Base.NGLabel(); + this.SuspendLayout(); + // + // labelPageTitle + // + this.labelPageTitle.AutoSize = true; + this.labelPageTitle.Location = new System.Drawing.Point(29, 6); + this.labelPageTitle.Name = "labelPageTitle"; + this.labelPageTitle.Size = new System.Drawing.Size(132, 13); + this.labelPageTitle.TabIndex = 0; + this.labelPageTitle.Text = "XML Credential Repository"; + // + // buttonBrowseFiles + // + this.buttonBrowseFiles.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonBrowseFiles.Location = new System.Drawing.Point(276, 166); + this.buttonBrowseFiles.Name = "buttonBrowseFiles"; + this.buttonBrowseFiles.Size = new System.Drawing.Size(75, 23); + this.buttonBrowseFiles.TabIndex = 1; + this.buttonBrowseFiles.Text = "Browse"; + this.buttonBrowseFiles.UseVisualStyleBackColor = true; + this.buttonBrowseFiles.Click += new System.EventHandler(this.buttonBrowseFiles_Click); + // + // labelFilePath + // + this.labelFilePath.AutoSize = true; + this.labelFilePath.Location = new System.Drawing.Point(29, 124); + this.labelFilePath.Name = "labelFilePath"; + this.labelFilePath.Size = new System.Drawing.Size(48, 13); + this.labelFilePath.TabIndex = 2; + this.labelFilePath.Text = "File Path"; + // + // textBoxFilePath + // + this.textBoxFilePath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxFilePath.Location = new System.Drawing.Point(32, 140); + this.textBoxFilePath.Name = "textBoxFilePath"; + this.textBoxFilePath.Size = new System.Drawing.Size(319, 20); + this.textBoxFilePath.TabIndex = 3; + // + // selectFilePathDialog + // + this.selectFilePathDialog.DefaultExt = "xml"; + this.selectFilePathDialog.Filter = "XML|*.xml"; + // + // buttonConfirm + // + this.buttonConfirm.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonConfirm.Location = new System.Drawing.Point(266, 279); + this.buttonConfirm.Name = "buttonConfirm"; + this.buttonConfirm.Size = new System.Drawing.Size(75, 23); + this.buttonConfirm.TabIndex = 9; + this.buttonConfirm.Text = "Confirm"; + this.buttonConfirm.UseVisualStyleBackColor = true; + this.buttonConfirm.Click += new System.EventHandler(this.buttonConfirm_Click); + // + // textBoxId + // + this.textBoxId.Location = new System.Drawing.Point(32, 51); + this.textBoxId.Name = "textBoxId"; + this.textBoxId.ReadOnly = true; + this.textBoxId.Size = new System.Drawing.Size(238, 20); + this.textBoxId.TabIndex = 10; + this.textBoxId.TabStop = false; + // + // labelId + // + this.labelId.AutoSize = true; + this.labelId.Location = new System.Drawing.Point(29, 35); + this.labelId.Name = "labelId"; + this.labelId.Size = new System.Drawing.Size(18, 13); + this.labelId.TabIndex = 11; + this.labelId.Text = "ID"; + // + // buttonBack + // + this.buttonBack.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonBack.Location = new System.Drawing.Point(185, 279); + this.buttonBack.Name = "buttonBack"; + this.buttonBack.Size = new System.Drawing.Size(75, 23); + this.buttonBack.TabIndex = 12; + this.buttonBack.Text = "Back"; + this.buttonBack.UseVisualStyleBackColor = true; + this.buttonBack.Click += new System.EventHandler(this.buttonBack_Click); + // + // newPasswordBoxes + // + this.newPasswordBoxes.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.newPasswordBoxes.Location = new System.Drawing.Point(32, 192); + this.newPasswordBoxes.MinimumSize = new System.Drawing.Size(0, 100); + this.newPasswordBoxes.Name = "newPasswordBoxes"; + this.newPasswordBoxes.PasswordChar = '\0'; + this.newPasswordBoxes.Size = new System.Drawing.Size(319, 100); + this.newPasswordBoxes.TabIndex = 13; + this.newPasswordBoxes.UseSystemPasswordChar = true; + // + // textBoxTitle + // + this.textBoxTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxTitle.Location = new System.Drawing.Point(32, 97); + this.textBoxTitle.Name = "textBoxTitle"; + this.textBoxTitle.Size = new System.Drawing.Size(319, 20); + this.textBoxTitle.TabIndex = 15; + // + // labelTitle + // + this.labelTitle.AutoSize = true; + this.labelTitle.Location = new System.Drawing.Point(29, 81); + this.labelTitle.Name = "labelTitle"; + this.labelTitle.Size = new System.Drawing.Size(27, 13); + this.labelTitle.TabIndex = 14; + this.labelTitle.Text = "Title"; + // + // XmlCredentialRepositoryEditorPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.textBoxTitle); + this.Controls.Add(this.labelTitle); + this.Controls.Add(this.buttonBack); + this.Controls.Add(this.labelId); + this.Controls.Add(this.textBoxId); + this.Controls.Add(this.buttonConfirm); + this.Controls.Add(this.textBoxFilePath); + this.Controls.Add(this.labelFilePath); + this.Controls.Add(this.buttonBrowseFiles); + this.Controls.Add(this.labelPageTitle); + this.Controls.Add(this.newPasswordBoxes); + this.MinimumSize = new System.Drawing.Size(300, 260); + this.Name = "XmlCredentialRepositoryEditorPage"; + this.Size = new System.Drawing.Size(354, 305); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.Base.NGLabel labelPageTitle; + private Controls.Base.NGButton buttonBrowseFiles; + private Controls.Base.NGLabel labelFilePath; + private Controls.Base.NGTextBox textBoxFilePath; + private System.Windows.Forms.SaveFileDialog selectFilePathDialog; + private Controls.Base.NGButton buttonConfirm; + private Controls.Base.NGTextBox textBoxId; + private Controls.Base.NGLabel labelId; + private Controls.Base.NGButton buttonBack; + private Controls.NewPasswordWithVerification newPasswordBoxes; + private Controls.Base.NGTextBox textBoxTitle; + private Controls.Base.NGLabel labelTitle; + } +} diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs new file mode 100644 index 00000000..8afd7b05 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs @@ -0,0 +1,96 @@ +using System; +using System.Windows.Forms; +using mRemoteNG.Config; +using mRemoteNG.Config.DataProviders; +using mRemoteNG.Config.Serializers.CredentialSerializer; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.Security.Factories; +using mRemoteNG.UI.Controls.PageSequence; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages +{ + public partial class XmlCredentialRepositoryEditorPage : SequencedControl + { + private readonly ICredentialRepositoryConfig _repositoryConfig; + private readonly ICredentialRepositoryList _repositoryList; + + public XmlCredentialRepositoryEditorPage(ICredentialRepositoryConfig repositoryConfig, ICredentialRepositoryList repositoryList) + { + if (repositoryConfig == null) + throw new ArgumentNullException(nameof(repositoryConfig)); + if (repositoryList == null) + throw new ArgumentNullException(nameof(repositoryList)); + + _repositoryConfig = repositoryConfig; + _repositoryList = repositoryList; + InitializeComponent(); + PopulateFields(); + } + + private void PopulateFields() + { + textBoxId.Text = _repositoryConfig.Id.ToString(); + textBoxTitle.Text = _repositoryConfig.Title; + textBoxFilePath.Text = _repositoryConfig.Source; + newPasswordBoxes.SetPassword(_repositoryConfig.Key); + } + + private void SaveValuesToConfig() + { + _repositoryConfig.Title = textBoxTitle.Text; + _repositoryConfig.Source = textBoxFilePath.Text; + _repositoryConfig.Key = newPasswordBoxes.SecureString; + } + + private void buttonBrowseFiles_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(_repositoryConfig.Source)) + selectFilePathDialog.FileName = _repositoryConfig.Source; + var dialogResult = selectFilePathDialog.ShowDialog(this); + if (dialogResult == DialogResult.OK) + { + textBoxFilePath.Text = selectFilePathDialog.FileName; + } + } + + private void buttonConfirm_Click(object sender, EventArgs e) + { + if (!AllRequiredFieldsFilledOut()) return; + SaveValuesToConfig(); + if (!_repositoryList.Contains(_repositoryConfig.Id)) + { + var newCredentialRepository = BuildXmlRepoFromSettings(_repositoryConfig); + _repositoryList.AddProvider(newCredentialRepository); + newCredentialRepository.SaveCredentials(_repositoryConfig.Key); + } + RaiseNextPageEvent(); + } + + private ICredentialRepository BuildXmlRepoFromSettings(ICredentialRepositoryConfig config) + { + var cryptoFromSettings = new CryptoProviderFactoryFromSettings(); + var credRepoDataProvider = new FileDataProvider(config.Source); + var credRepoSerializer = new XmlCredentialPasswordEncryptorDecorator( + cryptoFromSettings.Build(), + new XmlCredentialRecordSerializer()); + var credRepoDeserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()); + + return new XmlCredentialRepository( + config, + new CredentialRecordSaver(credRepoDataProvider, credRepoSerializer), + new CredentialRecordLoader(credRepoDataProvider, credRepoDeserializer) + ); + } + + private bool AllRequiredFieldsFilledOut() + { + return newPasswordBoxes.PasswordsMatch && !string.IsNullOrEmpty(textBoxFilePath.Text); + } + + private void buttonBack_Click(object sender, EventArgs e) + { + RaisePreviousPageEvent(); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.resx b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.resx new file mode 100644 index 00000000..e60a0f88 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.Designer.cs new file mode 100644 index 00000000..c2570fa1 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.Designer.cs @@ -0,0 +1,93 @@ +using mRemoteNG.Credential.Repositories; +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + sealed partial class CredentialRepositorySelectionPage + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + CredentialRepositoryList credentialRepositoryList1 = new CredentialRepositoryList(); + this.credentialRepositoryListView = new mRemoteNG.UI.Controls.CredentialRepositoryListView(); + this.buttonContinue = new Controls.Base.NGButton(); + this.buttonBack = new Controls.Base.NGButton(); + this.SuspendLayout(); + // + // credentialRepositoryListView + // + this.credentialRepositoryListView.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.credentialRepositoryListView.CredentialRepositoryList = credentialRepositoryList1; + this.credentialRepositoryListView.DoubleClickHandler = null; + this.credentialRepositoryListView.Location = new System.Drawing.Point(0, 0); + this.credentialRepositoryListView.Name = "credentialRepositoryListView"; + this.credentialRepositoryListView.Size = new System.Drawing.Size(368, 137); + this.credentialRepositoryListView.TabIndex = 0; + // + // buttonContinue + // + this.buttonContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonContinue.Location = new System.Drawing.Point(278, 143); + this.buttonContinue.Name = "buttonContinue"; + this.buttonContinue.Size = new System.Drawing.Size(75, 23); + this.buttonContinue.TabIndex = 1; + this.buttonContinue.Text = "Continue"; + this.buttonContinue.UseVisualStyleBackColor = true; + this.buttonContinue.Click += new System.EventHandler(this.buttonContinue_Click); + // + // buttonBack + // + this.buttonBack.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonBack.Location = new System.Drawing.Point(197, 143); + this.buttonBack.Name = "buttonBack"; + this.buttonBack.Size = new System.Drawing.Size(75, 23); + this.buttonBack.TabIndex = 2; + this.buttonBack.Text = "Back"; + this.buttonBack.UseVisualStyleBackColor = true; + this.buttonBack.Click += new System.EventHandler(this.buttonBack_Click); + // + // CredentialRepositorySelectionPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.buttonBack); + this.Controls.Add(this.buttonContinue); + this.Controls.Add(this.credentialRepositoryListView); + this.Name = "CredentialRepositorySelectionPage"; + this.Size = new System.Drawing.Size(368, 169); + this.ResumeLayout(false); + + } + + #endregion + + private Controls.CredentialRepositoryListView credentialRepositoryListView; + private Controls.Base.NGButton buttonContinue; + private Controls.Base.NGButton buttonBack; + } +} diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.cs new file mode 100644 index 00000000..53166e92 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.cs @@ -0,0 +1,49 @@ +using System; +using System.Windows.Forms; +using mRemoteNG.Credential; +using mRemoteNG.UI.Controls.PageSequence; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + public sealed partial class CredentialRepositorySelectionPage : SequencedControl + { + public CredentialRepositorySelectionPage(ICredentialRepositoryList credentialRepositoryList) + { + if (credentialRepositoryList == null) + throw new ArgumentNullException(nameof(credentialRepositoryList)); + + InitializeComponent(); + ApplyTheme(); + Dock = DockStyle.Fill; + credentialRepositoryListView.RepositoryFilter = repository => repository.IsLoaded; + credentialRepositoryListView.CredentialRepositoryList = credentialRepositoryList; + credentialRepositoryListView.DoubleClickHandler = DoubleClickHandler; + } + + private void Continue(ICredentialRepository credentialRepository) + { + var newCred = new CredentialRecord(); + var newCredPage = new CredentialEditorPage(newCred, credentialRepository); + RaisePageReplacementEvent(newCredPage, RelativePagePosition.NextPage); + RaiseNextPageEvent(); + } + + private bool DoubleClickHandler(ICredentialRepository credentialRepository) + { + if (credentialRepository == null) return false; + Continue(credentialRepository); + return true; + } + + private void buttonContinue_Click(object sender, EventArgs e) + { + if (credentialRepositoryListView.SelectedRepository == null) return; + Continue(credentialRepositoryListView.SelectedRepository); + } + + private void buttonBack_Click(object sender, EventArgs e) + { + RaisePreviousPageEvent(); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.resx b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectionPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs new file mode 100644 index 00000000..debdecb2 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/KeePassRepositorySelector.cs @@ -0,0 +1,13 @@ +using System.Drawing; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.UI.Controls; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositorySelectors +{ + public class KeePassRepositorySelector : ISelectionTarget + { + public string Text { get; set; } = "KeePass"; + public Image Image { get; } = Resources.keepass_32x32; + public ICredentialRepositoryConfig Config { get; } = new CredentialRepositoryConfig {TypeName = "KeePass"}; + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs new file mode 100644 index 00000000..88e7ebe5 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositorySelectors/XmlCredentialRepositorySelector.cs @@ -0,0 +1,13 @@ +using System.Drawing; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.UI.Controls; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositorySelectors +{ + public class XmlCredentialRepositorySelector : ISelectionTarget + { + public string Text { get; set; } = "XML"; + public Image Image { get; } = Resources.xml; + public ICredentialRepositoryConfig Config { get; } = new CredentialRepositoryConfig {TypeName = "Xml"}; + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.Designer.cs new file mode 100644 index 00000000..b11bd059 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.Designer.cs @@ -0,0 +1,135 @@ +using mRemoteNG.Themes; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + sealed partial class CredentialRepositoryTypeSelectionPage + { + /// + /// 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 Component 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.buttonContinue = new Controls.Base.NGButton(); + this.objectListView = new Controls.Base.NGListView(); + this.olvColumnName = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); + this.imageListLarge = new System.Windows.Forms.ImageList(this.components); + this.buttonBack = new Controls.Base.NGButton(); + ((System.ComponentModel.ISupportInitialize)(this.objectListView)).BeginInit(); + this.SuspendLayout(); + // + // buttonContinue + // + this.buttonContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonContinue.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonContinue.Location = new System.Drawing.Point(333, 312); + this.buttonContinue.Name = "buttonContinue"; + this.buttonContinue.Size = new System.Drawing.Size(75, 23); + this.buttonContinue.TabIndex = 4; + this.buttonContinue.Text = "Continue"; + this.buttonContinue.UseVisualStyleBackColor = true; + this.buttonContinue.Click += new System.EventHandler(this.buttonContinue_Click); + // + // objectListView + // + this.objectListView.AllColumns.Add(this.olvColumnName); + this.objectListView.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.objectListView.CellEditUseWholeCell = false; + this.objectListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.olvColumnName}); + this.objectListView.CopySelectionOnControlC = false; + this.objectListView.CopySelectionOnControlCUsesDragSource = false; + this.objectListView.Cursor = System.Windows.Forms.Cursors.Default; + this.objectListView.FullRowSelect = true; + this.objectListView.HasCollapsibleGroups = false; + this.objectListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.objectListView.HideSelection = false; + this.objectListView.LargeImageList = this.imageListLarge; + this.objectListView.Location = new System.Drawing.Point(0, 0); + this.objectListView.MultiSelect = false; + this.objectListView.Name = "objectListView"; + this.objectListView.SelectAllOnControlA = false; + this.objectListView.SelectColumnsOnRightClick = false; + this.objectListView.SelectColumnsOnRightClickBehaviour = BrightIdeasSoftware.ObjectListView.ColumnSelectBehaviour.None; + this.objectListView.ShowFilterMenuOnRightClick = false; + this.objectListView.ShowHeaderInAllViews = false; + this.objectListView.Size = new System.Drawing.Size(422, 297); + this.objectListView.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.objectListView.TabIndex = 5; + this.objectListView.UseCompatibleStateImageBehavior = false; + this.objectListView.View = System.Windows.Forms.View.LargeIcon; + // + // olvColumnName + // + this.olvColumnName.AspectName = "Text"; + this.olvColumnName.FillsFreeSpace = true; + this.olvColumnName.Groupable = false; + this.olvColumnName.ImageAspectName = ""; + this.olvColumnName.IsEditable = false; + this.olvColumnName.Searchable = false; + this.olvColumnName.ShowTextInHeader = false; + // + // imageListLarge + // + this.imageListLarge.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; + this.imageListLarge.ImageSize = new System.Drawing.Size(32, 32); + this.imageListLarge.TransparentColor = System.Drawing.Color.Transparent; + // + // buttonBack + // + this.buttonBack.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonBack.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonBack.Location = new System.Drawing.Point(252, 312); + this.buttonBack.Name = "buttonBack"; + this.buttonBack.Size = new System.Drawing.Size(75, 23); + this.buttonBack.TabIndex = 6; + this.buttonBack.Text = "Back"; + this.buttonBack.UseVisualStyleBackColor = true; + this.buttonBack.Click += new System.EventHandler(this.buttonBack_Click); + // + // CredentialRepositorySelectionPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.buttonBack); + this.Controls.Add(this.objectListView); + this.Controls.Add(this.buttonContinue); + this.Name = "CredentialRepositorySelectionPage"; + this.Size = new System.Drawing.Size(422, 338); + ((System.ComponentModel.ISupportInitialize)(this.objectListView)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private Controls.Base.NGButton buttonContinue; + private Controls.Base.NGListView objectListView; + private BrightIdeasSoftware.OLVColumn olvColumnName; + private System.Windows.Forms.ImageList imageListLarge; + private Controls.Base.NGButton buttonBack; + } +} diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs new file mode 100644 index 00000000..2e5cd31c --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using BrightIdeasSoftware; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; +using mRemoteNG.UI.Controls; +using mRemoteNG.UI.Controls.PageSequence; +using mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPages; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + public sealed partial class CredentialRepositoryTypeSelectionPage : SequencedControl + { + private readonly ICredentialRepositoryList _repositoryList; + + public CredentialRepositoryTypeSelectionPage(IEnumerable> selectionTargets, ICredentialRepositoryList repositoryList) + { + if (selectionTargets == null) + throw new ArgumentNullException(nameof(selectionTargets)); + if (repositoryList == null) + throw new ArgumentNullException(nameof(repositoryList)); + + _repositoryList = repositoryList; + InitializeComponent(); + ApplyTheme(); + SetupListView(selectionTargets); + } + + + private void SetupListView(IEnumerable> selectionTargets) + { + olvColumnName.ImageGetter = ImageGetter; + objectListView.MouseDoubleClick += ObjectListViewOnMouseDoubleClick; + objectListView.SetObjects(selectionTargets); + } + + private object ImageGetter(object rowObject) + { + var selection = rowObject as ISelectionTarget; + if (selection == null) return ""; + var imgHash = selection.Image.GetHashCode().ToString(); + if (!objectListView.LargeImageList.Images.ContainsKey(imgHash)) + objectListView.LargeImageList.Images.Add(imgHash, selection.Image); + return imgHash; + } + + private void ObjectListViewOnMouseDoubleClick(object sender, MouseEventArgs mouseEventArgs) + { + if (mouseEventArgs.Clicks < 2) return; + OLVColumn column; + var listItem = objectListView.GetItemAt(mouseEventArgs.X, mouseEventArgs.Y, out column); + var clickedNode = listItem.RowObject as ISelectionTarget; + if (clickedNode == null) return; + NextPage(clickedNode); + } + + private void buttonContinue_Click(object sender, EventArgs e) + { + var selection = objectListView.SelectedObject as ISelectionTarget; + if (selection == null) return; + NextPage(selection); + } + + private void NextPage(ISelectionTarget selection) + { + var editorPage = BuildEditorPage(selection); + RaisePageReplacementEvent(editorPage, RelativePagePosition.NextPage); + RaiseNextPageEvent(); + } + + private SequencedControl BuildEditorPage(ISelectionTarget selection) + { + var editorPage = CredentialRepositoryPageEditorFactory.BuildXmlCredentialRepositoryEditorPage(selection.Config, _repositoryList); + editorPage.Dock = DockStyle.Fill; + return editorPage; + } + + private void buttonBack_Click(object sender, EventArgs e) + { + RaisePreviousPageEvent(); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.resx b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.resx new file mode 100644 index 00000000..c6ab0601 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryTypeSelectionPage.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/ICredentialManagerPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/ICredentialManagerPage.cs new file mode 100644 index 00000000..3a832ab1 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/ICredentialManagerPage.cs @@ -0,0 +1,11 @@ +using System.Drawing; + +namespace mRemoteNG.UI.Forms.CredentialManagerPages +{ + public interface ICredentialManagerPage + { + string PageName { get; } + + Image PageIcon { get; } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.Designer.cs b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.Designer.cs new file mode 100644 index 00000000..2cf2508e --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.Designer.cs @@ -0,0 +1,318 @@ +namespace mRemoteNG.UI.Forms +{ + partial class CredentialManagerUpgradeForm + { + /// + /// 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(CredentialManagerUpgradeForm)); + this.newCredRepoPathDialog = new System.Windows.Forms.SaveFileDialog(); + this.tabControl = new mRemoteNG.UI.Controls.HeadlessTabControl(); + this.tabPageWelcome = new System.Windows.Forms.TabPage(); + this.textBoxConfConPathTab1 = new Controls.Base.NGTextBox(); + this.labelConfConsPathHeaderOnTab1 = new Controls.Base.NGLabel(); + this.buttonExit = new Controls.Base.NGButton(); + this.labelDescriptionOfUpgrade = new Controls.Base.NGLabel(); + this.buttonPerformUpgrade = new Controls.Base.NGButton(); + this.buttonNewFile = new Controls.Base.NGButton(); + this.buttonOpenFile = new Controls.Base.NGButton(); + this.tabPageUpgradeOptions = new System.Windows.Forms.TabPage(); + this.textBoxConfConPathTab2 = new Controls.Base.NGTextBox(); + this.buttonNewRepoPathBrowse = new Controls.Base.NGButton(); + this.labelWhereToSaveCredFile = new Controls.Base.NGLabel(); + this.textBoxCredRepoPath = new Controls.Base.NGTextBox(); + this.buttonExecuteUpgrade = new Controls.Base.NGButton(); + this.labelSetPassword = new Controls.Base.NGLabel(); + this.newPasswordWithVerification1 = new mRemoteNG.UI.Controls.NewPasswordWithVerification(); + this.labelConfConsPathHeaderOnTab2 = new Controls.Base.NGLabel(); + this.buttonBack = new Controls.Base.NGButton(); + this.newConnectionsFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.tabControl.SuspendLayout(); + this.tabPageWelcome.SuspendLayout(); + this.tabPageUpgradeOptions.SuspendLayout(); + this.SuspendLayout(); + // + // newCredRepoPathDialog + // + this.newCredRepoPathDialog.Filter = "Xml|*.xml|All files|*.*"; + this.newCredRepoPathDialog.Title = "New credential repository path"; + // + // tabControl + // + this.tabControl.Controls.Add(this.tabPageWelcome); + this.tabControl.Controls.Add(this.tabPageUpgradeOptions); + this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl.ItemSize = new System.Drawing.Size(60, 20); + this.tabControl.Location = new System.Drawing.Point(0, 0); + this.tabControl.Name = "tabControl"; + this.tabControl.SelectedIndex = 0; + this.tabControl.Size = new System.Drawing.Size(420, 402); + this.tabControl.TabIndex = 5; + // + // tabPageWelcome + // + this.tabPageWelcome.BackColor = System.Drawing.SystemColors.Control; + this.tabPageWelcome.Controls.Add(this.textBoxConfConPathTab1); + this.tabPageWelcome.Controls.Add(this.labelConfConsPathHeaderOnTab1); + this.tabPageWelcome.Controls.Add(this.buttonExit); + this.tabPageWelcome.Controls.Add(this.labelDescriptionOfUpgrade); + this.tabPageWelcome.Controls.Add(this.buttonPerformUpgrade); + this.tabPageWelcome.Controls.Add(this.buttonNewFile); + this.tabPageWelcome.Controls.Add(this.buttonOpenFile); + this.tabPageWelcome.Location = new System.Drawing.Point(4, 24); + this.tabPageWelcome.Name = "tabPageWelcome"; + this.tabPageWelcome.Padding = new System.Windows.Forms.Padding(3); + this.tabPageWelcome.Size = new System.Drawing.Size(412, 374); + this.tabPageWelcome.TabIndex = 0; + this.tabPageWelcome.Text = "welcomePage"; + // + // textBoxConfConPathTab1 + // + this.textBoxConfConPathTab1.Location = new System.Drawing.Point(30, 177); + this.textBoxConfConPathTab1.Multiline = true; + this.textBoxConfConPathTab1.Name = "textBoxConfConPathTab1"; + this.textBoxConfConPathTab1.ReadOnly = true; + this.textBoxConfConPathTab1.Size = new System.Drawing.Size(376, 55); + this.textBoxConfConPathTab1.TabIndex = 6; + // + // labelConfConsPathHeaderOnTab1 + // + this.labelConfConsPathHeaderOnTab1.AutoSize = true; + this.labelConfConsPathHeaderOnTab1.Location = new System.Drawing.Point(10, 161); + this.labelConfConsPathHeaderOnTab1.Name = "labelConfConsPathHeaderOnTab1"; + this.labelConfConsPathHeaderOnTab1.Size = new System.Drawing.Size(104, 13); + this.labelConfConsPathHeaderOnTab1.TabIndex = 5; + this.labelConfConsPathHeaderOnTab1.Text = "Connection file path:"; + // + // buttonExit + // + this.buttonExit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.buttonExit.Location = new System.Drawing.Point(142, 343); + this.buttonExit.Name = "buttonExit"; + this.buttonExit.Size = new System.Drawing.Size(139, 23); + this.buttonExit.TabIndex = 4; + this.buttonExit.Text = "Exit"; + this.buttonExit.UseVisualStyleBackColor = true; + this.buttonExit.Click += new System.EventHandler(this.buttonExit_Click); + // + // labelDescriptionOfUpgrade + // + this.labelDescriptionOfUpgrade.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelDescriptionOfUpgrade.Location = new System.Drawing.Point(6, 20); + this.labelDescriptionOfUpgrade.Name = "labelDescriptionOfUpgrade"; + this.labelDescriptionOfUpgrade.Size = new System.Drawing.Size(400, 141); + this.labelDescriptionOfUpgrade.TabIndex = 0; + this.labelDescriptionOfUpgrade.Text = resources.GetString("labelDescriptionOfUpgrade.Text"); + // + // buttonPerformUpgrade + // + this.buttonPerformUpgrade.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.buttonPerformUpgrade.Location = new System.Drawing.Point(142, 256); + this.buttonPerformUpgrade.Name = "buttonPerformUpgrade"; + this.buttonPerformUpgrade.Size = new System.Drawing.Size(139, 23); + this.buttonPerformUpgrade.TabIndex = 1; + this.buttonPerformUpgrade.Text = "Upgrade"; + this.buttonPerformUpgrade.UseVisualStyleBackColor = true; + this.buttonPerformUpgrade.Click += new System.EventHandler(this.buttonPerformUpgrade_Click); + // + // buttonNewFile + // + this.buttonNewFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.buttonNewFile.DialogResult = System.Windows.Forms.DialogResult.Abort; + this.buttonNewFile.Location = new System.Drawing.Point(142, 314); + this.buttonNewFile.Name = "buttonNewFile"; + this.buttonNewFile.Size = new System.Drawing.Size(139, 23); + this.buttonNewFile.TabIndex = 3; + this.buttonNewFile.Text = "Create and open new file"; + this.buttonNewFile.UseVisualStyleBackColor = true; + this.buttonNewFile.Click += new System.EventHandler(this.buttonNewFile_Click); + // + // buttonOpenFile + // + this.buttonOpenFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.buttonOpenFile.Location = new System.Drawing.Point(142, 285); + this.buttonOpenFile.Name = "buttonOpenFile"; + this.buttonOpenFile.Size = new System.Drawing.Size(139, 23); + this.buttonOpenFile.TabIndex = 2; + this.buttonOpenFile.Text = "Open a different file"; + this.buttonOpenFile.UseVisualStyleBackColor = true; + this.buttonOpenFile.Click += new System.EventHandler(this.buttonOpenFile_Click); + // + // tabPageUpgradeOptions + // + this.tabPageUpgradeOptions.BackColor = System.Drawing.SystemColors.Control; + this.tabPageUpgradeOptions.Controls.Add(this.textBoxConfConPathTab2); + this.tabPageUpgradeOptions.Controls.Add(this.buttonNewRepoPathBrowse); + this.tabPageUpgradeOptions.Controls.Add(this.labelWhereToSaveCredFile); + this.tabPageUpgradeOptions.Controls.Add(this.textBoxCredRepoPath); + this.tabPageUpgradeOptions.Controls.Add(this.buttonExecuteUpgrade); + this.tabPageUpgradeOptions.Controls.Add(this.labelSetPassword); + this.tabPageUpgradeOptions.Controls.Add(this.newPasswordWithVerification1); + this.tabPageUpgradeOptions.Controls.Add(this.labelConfConsPathHeaderOnTab2); + this.tabPageUpgradeOptions.Controls.Add(this.buttonBack); + this.tabPageUpgradeOptions.Location = new System.Drawing.Point(4, 24); + this.tabPageUpgradeOptions.Name = "tabPageUpgradeOptions"; + this.tabPageUpgradeOptions.Padding = new System.Windows.Forms.Padding(3); + this.tabPageUpgradeOptions.Size = new System.Drawing.Size(412, 374); + this.tabPageUpgradeOptions.TabIndex = 1; + this.tabPageUpgradeOptions.Text = "upgradePage"; + // + // textBoxConfConPathTab2 + // + this.textBoxConfConPathTab2.Location = new System.Drawing.Point(27, 32); + this.textBoxConfConPathTab2.Multiline = true; + this.textBoxConfConPathTab2.Name = "textBoxConfConPathTab2"; + this.textBoxConfConPathTab2.ReadOnly = true; + this.textBoxConfConPathTab2.Size = new System.Drawing.Size(377, 41); + this.textBoxConfConPathTab2.TabIndex = 9; + // + // buttonNewRepoPathBrowse + // + this.buttonNewRepoPathBrowse.Location = new System.Drawing.Point(329, 143); + this.buttonNewRepoPathBrowse.Name = "buttonNewRepoPathBrowse"; + this.buttonNewRepoPathBrowse.Size = new System.Drawing.Size(75, 23); + this.buttonNewRepoPathBrowse.TabIndex = 8; + this.buttonNewRepoPathBrowse.Text = "Browse"; + this.buttonNewRepoPathBrowse.UseVisualStyleBackColor = true; + this.buttonNewRepoPathBrowse.Click += new System.EventHandler(this.buttonNewRepoPathBrowse_Click); + // + // labelWhereToSaveCredFile + // + this.labelWhereToSaveCredFile.AutoSize = true; + this.labelWhereToSaveCredFile.Location = new System.Drawing.Point(14, 98); + this.labelWhereToSaveCredFile.Name = "labelWhereToSaveCredFile"; + this.labelWhereToSaveCredFile.Size = new System.Drawing.Size(228, 13); + this.labelWhereToSaveCredFile.TabIndex = 7; + this.labelWhereToSaveCredFile.Text = "Where should we save the new credential file?"; + // + // textBoxCredRepoPath + // + this.textBoxCredRepoPath.Location = new System.Drawing.Point(27, 117); + this.textBoxCredRepoPath.Name = "textBoxCredRepoPath"; + this.textBoxCredRepoPath.Size = new System.Drawing.Size(377, 20); + this.textBoxCredRepoPath.TabIndex = 6; + // + // buttonExecuteUpgrade + // + this.buttonExecuteUpgrade.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonExecuteUpgrade.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonExecuteUpgrade.Location = new System.Drawing.Point(329, 343); + this.buttonExecuteUpgrade.Name = "buttonExecuteUpgrade"; + this.buttonExecuteUpgrade.Size = new System.Drawing.Size(75, 23); + this.buttonExecuteUpgrade.TabIndex = 5; + this.buttonExecuteUpgrade.Text = "Upgrade"; + this.buttonExecuteUpgrade.UseVisualStyleBackColor = true; + // + // labelSetPassword + // + this.labelSetPassword.AutoSize = true; + this.labelSetPassword.Location = new System.Drawing.Point(13, 194); + this.labelSetPassword.Name = "labelSetPassword"; + this.labelSetPassword.Size = new System.Drawing.Size(201, 13); + this.labelSetPassword.TabIndex = 4; + this.labelSetPassword.Text = "Set password for the credential repository"; + // + // newPasswordWithVerification1 + // + this.newPasswordWithVerification1.Location = new System.Drawing.Point(27, 220); + this.newPasswordWithVerification1.MinimumSize = new System.Drawing.Size(0, 100); + this.newPasswordWithVerification1.Name = "newPasswordWithVerification1"; + this.newPasswordWithVerification1.PasswordChar = '\0'; + this.newPasswordWithVerification1.Size = new System.Drawing.Size(377, 100); + this.newPasswordWithVerification1.TabIndex = 3; + this.newPasswordWithVerification1.UseSystemPasswordChar = false; + // + // labelConfConsPathHeaderOnTab2 + // + this.labelConfConsPathHeaderOnTab2.AutoSize = true; + this.labelConfConsPathHeaderOnTab2.Location = new System.Drawing.Point(6, 16); + this.labelConfConsPathHeaderOnTab2.Name = "labelConfConsPathHeaderOnTab2"; + this.labelConfConsPathHeaderOnTab2.Size = new System.Drawing.Size(85, 13); + this.labelConfConsPathHeaderOnTab2.TabIndex = 1; + this.labelConfConsPathHeaderOnTab2.Text = "Connections file:"; + // + // buttonBack + // + this.buttonBack.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonBack.Location = new System.Drawing.Point(248, 343); + this.buttonBack.Name = "buttonBack"; + this.buttonBack.Size = new System.Drawing.Size(75, 23); + this.buttonBack.TabIndex = 0; + this.buttonBack.Text = "Back"; + this.buttonBack.UseVisualStyleBackColor = true; + this.buttonBack.Click += new System.EventHandler(this.buttonBack_Click); + // + // newConnectionsFileDialog + // + this.newConnectionsFileDialog.Filter = "Xml|*.xml|All files|*.*"; + this.newConnectionsFileDialog.Title = "Create new connection file"; + // + // CredentialManagerUpgradeForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(420, 402); + this.ControlBox = false; + this.Controls.Add(this.tabControl); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Name = "CredentialManagerUpgradeForm"; + this.ShowInTaskbar = false; + this.Text = "Credential Manager Upgrade"; + this.tabControl.ResumeLayout(false); + this.tabPageWelcome.ResumeLayout(false); + this.tabPageWelcome.PerformLayout(); + this.tabPageUpgradeOptions.ResumeLayout(false); + this.tabPageUpgradeOptions.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private Controls.Base.NGLabel labelDescriptionOfUpgrade; + private Controls.Base.NGButton buttonPerformUpgrade; + private Controls.Base.NGButton buttonOpenFile; + private Controls.Base.NGButton buttonNewFile; + private Controls.Base.NGButton buttonExit; + private mRemoteNG.UI.Controls.HeadlessTabControl tabControl; + private System.Windows.Forms.TabPage tabPageWelcome; + private System.Windows.Forms.TabPage tabPageUpgradeOptions; + private Controls.Base.NGButton buttonBack; + private Controls.Base.NGLabel labelConfConsPathHeaderOnTab2; + private Controls.Base.NGButton buttonExecuteUpgrade; + private Controls.Base.NGLabel labelSetPassword; + private Controls.NewPasswordWithVerification newPasswordWithVerification1; + private Controls.Base.NGLabel labelWhereToSaveCredFile; + private Controls.Base.NGTextBox textBoxCredRepoPath; + private System.Windows.Forms.SaveFileDialog newCredRepoPathDialog; + private Controls.Base.NGButton buttonNewRepoPathBrowse; + private Controls.Base.NGLabel labelConfConsPathHeaderOnTab1; + private Controls.Base.NGTextBox textBoxConfConPathTab1; + private Controls.Base.NGTextBox textBoxConfConPathTab2; + private System.Windows.Forms.SaveFileDialog newConnectionsFileDialog; + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs new file mode 100644 index 00000000..465cabf6 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.cs @@ -0,0 +1,126 @@ +using System; +using System.Windows.Forms; +using System.Xml.Linq; +using mRemoteNG.App; +using mRemoteNG.Config.Serializers; +using mRemoteNG.Config.Serializers.Versioning; +using mRemoteNG.Tree; + +namespace mRemoteNG.UI.Forms +{ + public partial class CredentialManagerUpgradeForm : Form, IDeserializer + { + private string _connectionFilePath; + private string _newCredentialRepoPath; + + public XmlCredentialManagerUpgrader DecoratedDeserializer { get; set; } + + public string ConnectionFilePath + { + get { return _connectionFilePath; } + set + { + _connectionFilePath = value; + UpdateUi(); + } + } + + public string NewCredentialRepoPath + { + get { return _newCredentialRepoPath; } + set + { + _newCredentialRepoPath = value; + UpdateUi(); + } + } + + public CredentialManagerUpgradeForm() + { + InitializeComponent(); + ApplyLanguage(); + UpdateUi(); + } + + public ConnectionTreeModel Deserialize(string serializedData) + { + var xdoc = XDocument.Parse(serializedData); + if (!WeCanUpgradeFromThisVersion(xdoc)) + return DecoratedDeserializer.Deserialize(serializedData); + + var result = ShowDialog(); + if (result != DialogResult.OK) return new ConnectionTreeModel(); + DecoratedDeserializer.CredentialFilePath = NewCredentialRepoPath; + return DecoratedDeserializer.Deserialize(serializedData); + } + + private bool WeCanUpgradeFromThisVersion(XDocument xdoc) + { + return XmlCredentialManagerUpgrader.GetVersionFromConfiguration(xdoc) < 2.8m; + } + + private void ApplyLanguage() + { + // tab 1 + labelDescriptionOfUpgrade.Text = Language.strCredentialManagerUpgradeDescription; + labelConfConsPathHeaderOnTab1.Text = $@"{Language.strConnectionFilePath}:"; + buttonPerformUpgrade.Text = Language.strUpgrade; + buttonOpenFile.Text = Language.strOpenADifferentFile; + buttonNewFile.Text = Language.strCreateAndOpenNewFile; + buttonExit.Text = Language.strMenuExit; + + // tab 2 + labelConfConsPathHeaderOnTab2.Text = $@"{Language.strConnectionFilePath}:"; + labelWhereToSaveCredFile.Text = "Where should we save the new credential file?"; + labelSetPassword.Text = "Set password for the credential repository"; + buttonNewRepoPathBrowse.Text = Language.strButtonBrowse; + buttonBack.Text = Language.strBack; + buttonExecuteUpgrade.Text = Language.strUpgrade; + } + + private void UpdateUi() + { + textBoxConfConPathTab1.Text = ConnectionFilePath; + textBoxConfConPathTab2.Text = ConnectionFilePath; + textBoxCredRepoPath.Text = NewCredentialRepoPath; + } + + private void buttonPerformUpgrade_Click(object sender, EventArgs e) + { + tabControl.SelectedTab = tabPageUpgradeOptions; + } + + private void buttonOpenFile_Click(object sender, EventArgs e) + { + var loadConnectionsDialog = DialogFactory.BuildLoadConnectionsDialog(); + var dialogResult = loadConnectionsDialog.ShowDialog(this); + if (dialogResult == DialogResult.OK) + ConnectionFilePath = loadConnectionsDialog.FileName; + } + + private void buttonNewFile_Click(object sender, EventArgs e) + { + var dialogResult = newConnectionsFileDialog.ShowDialog(this); + if (dialogResult != DialogResult.OK) return; + Runtime.ConnectionsService.NewConnectionsFile(newConnectionsFileDialog.FileName); + Close(); + } + + private void buttonExit_Click(object sender, EventArgs e) + { + Shutdown.Quit(); + } + + private void buttonNewRepoPathBrowse_Click(object sender, EventArgs e) + { + var dialogResult = newCredRepoPathDialog.ShowDialog(this); + if (dialogResult == DialogResult.OK) + NewCredentialRepoPath = newCredRepoPathDialog.FileName; + } + + private void buttonBack_Click(object sender, EventArgs e) + { + tabControl.SelectedTab = tabPageWelcome; + } + } +} \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.resx b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.resx new file mode 100644 index 00000000..1b26d8b9 --- /dev/null +++ b/mRemoteV1/UI/Forms/CredentialManagerUpgradeForm.resx @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + In v1.76 we have introduced a credential management system. This feature requires a significant change in how we store and interact with credentials within mRemoteNG. You will be required to perform a one-way upgrade of your mRemoteNG connections file. + +This page will walk you through the process of upgrading your connections file or give you a chance to open a different connections file if you do not want to perform the upgrade. + + + 193, 21 + + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/UnlockerFormFactory.cs b/mRemoteV1/UI/Forms/UnlockerFormFactory.cs new file mode 100644 index 00000000..4f002400 --- /dev/null +++ b/mRemoteV1/UI/Forms/UnlockerFormFactory.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using mRemoteNG.Credential; +using mRemoteNG.Credential.Repositories; + +namespace mRemoteNG.UI.Forms +{ + public class UnlockerFormFactory + { + public CompositeCredentialRepoUnlockerForm Build(IEnumerable repositories) + { + return new CompositeCredentialRepoUnlockerForm( + new CompositeRepositoryUnlocker(repositories) + ); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 734b439c..0b3e8a3b 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -188,6 +188,7 @@ + @@ -490,6 +491,64 @@ + + Form + + + CompositeCredentialRepoUnlockerForm.cs + + + Form + + + CredentialManagerForm.cs + + + UserControl + + + CredentialEditorPage.cs + + + UserControl + + + CredentialListPage.cs + + + UserControl + + + CredentialRepositoriesPage.cs + + + + UserControl + + + XmlCredentialRepositoryEditorPage.cs + + + UserControl + + + CredentialRepositorySelectionPage.cs + + + + + UserControl + + + CredentialRepositoryTypeSelectionPage.cs + + + + Form + + + CredentialManagerUpgradeForm.cs + Form @@ -670,6 +729,7 @@ UnhandledExceptionWindow.cs + @@ -798,6 +858,33 @@ NewPasswordWithVerification.cs + + CompositeCredentialRepoUnlockerForm.cs + + + CredentialManagerForm.cs + + + CredentialEditorPage.cs + + + CredentialListPage.cs + + + CredentialRepositoriesPage.cs + + + XmlCredentialRepositoryEditorPage.cs + + + CredentialRepositorySelectionPage.cs + + + CredentialRepositoryTypeSelectionPage.cs + + + CredentialManagerUpgradeForm.cs + FrmChoosePanel.cs Designer