Compare commits

..

80 Commits

Author SHA1 Message Date
Faryan Rezagholi
8a5f0f248e open credential manager from connection tree window 2021-09-04 00:52:04 +02:00
Faryan Rezagholi
c1931ff4cd hide password/username/domain fields in connection properties 2021-09-04 00:27:42 +02:00
Faryan Rezagholi
12cb7ad7b0 merged from develop 2021-09-01 01:26:21 +02:00
David Sparer
4349b1b65b dont save settings on individual settings pages 2019-05-19 16:23:00 -05:00
David Sparer
bb04605dc3 Merge branch 'develop' into reapply_credential_manager
# Conflicts:
#	mRemoteNGTests/Connection/DefaultConnectionInheritanceTests.cs
#	mRemoteNGTests/TestHelpers/ConnectionInfoHelpers.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormat.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionNodeSerializer27.cs
#	mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializer.cs
#	mRemoteV1/Connection/AbstractConnectionRecord.cs
#	mRemoteV1/Connection/ConnectionInitiator.cs
#	mRemoteV1/Connection/Protocol/RDP/RdpProtocol.cs
#	mRemoteV1/Resources/Help/ui_external_tools.htm
#	mRemoteV1/Resources/Language/Language.resx
#	mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs
#	mRemoteV1/UI/Forms/OptionsPages/CredentialsPage.Designer.cs
#	mRemoteV1/UI/Window/ConfigWindow.cs
#	mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs
#	mRemoteV1/mRemoteV1.csproj
2019-05-19 16:19:18 -05:00
David Sparer
294bd2f7a4 fixed event invocator warnings 2019-03-19 20:25:59 -05:00
David Sparer
a3aa323cce updated html documentation for #680 2019-03-19 20:15:16 -05:00
David Sparer
355cd63acb Merge branch 'develop' into reapply_credential_manager 2019-03-19 18:01:37 -05:00
David Sparer
93d50b0818 Merge branch 'develop' into reapply_credential_manager
# Conflicts:
#	mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManager27DeserializerTests.cs
#	mRemoteNGTests/packages.config
#	mRemoteV1/Config/Connections/SqlConnectionsSaver.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionNodeSerializer27.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs
#	mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializer.cs
#	mRemoteV1/Connection/AbstractConnectionRecord.cs
#	mRemoteV1/Connection/ConnectionInfoInheritance.cs
#	mRemoteV1/Connection/ConnectionsService.cs
#	mRemoteV1/Connection/Protocol/Http/Connection.Protocol.HTTPBase.cs
#	mRemoteV1/Properties/Settings.settings
#	mRemoteV1/Resources/Language/Language.de.resx
#	mRemoteV1/Resources/Language/Language.resx
#	mRemoteV1/Resources/Language/Language.ru.resx
#	mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs
#	mRemoteV1/mRemoteV1.csproj
2019-03-09 13:49:23 -06:00
Faryan Rezagholi
8779776ad5 removed minimize button from credentials manager 2019-02-20 19:45:18 +01:00
Faryan Rezagholi
3bb285d180 removed unused form 2019-02-20 19:42:30 +01:00
Faryan Rezagholi
052796c794 adjusted text box positioning 2019-02-20 19:08:25 +01:00
Faryan Rezagholi
33a6dbb4c3 made more hardcoded strings translateable 2019-02-20 18:32:35 +01:00
Faryan Rezagholi
953ddaa292 merged develop 2019-02-19 22:39:58 +01:00
Faryan Rezagholi
c065b86dbd revised credential manager forms design 2019-02-19 22:38:12 +01:00
David Sparer
95859b96ff Merge branch 'develop' into reapply_credential_manager
# Conflicts:
#	mRemoteV1/App/Export.cs
#	mRemoteV1/App/Initialization/CredsAndConsSetup.cs
#	mRemoteV1/App/Runtime.cs
#	mRemoteV1/Config/Connections/ConnectionsLoadedEventArgs.cs
#	mRemoteV1/Config/Connections/ConnectionsSavedEventArgs.cs
#	mRemoteV1/Config/Connections/CsvConnectionsSaver.cs
#	mRemoteV1/Config/Connections/SaveConnectionsOnEdit.cs
#	mRemoteV1/Config/CredentialHarvester.cs
#	mRemoteV1/Config/CredentialRepositoryListLoader.cs
#	mRemoteV1/Config/CredentialRepositoryListSaver.cs
#	mRemoteV1/Config/DataProviders/FileDataProvider.cs
#	mRemoteV1/Config/Import/MRemoteNGCsvImporter.cs
#	mRemoteV1/Config/Import/MRemoteNGXmlImporter.cs
#	mRemoteV1/Config/Import/RemoteDesktopConnectionImporter.cs
#	mRemoteV1/Config/Putty/PuttySessionsRegistryProvider.cs
#	mRemoteV1/Config/Putty/PuttySessionsXmingProvider.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormat.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionNodeSerializer27.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsSerializer.cs
#	mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs
#	mRemoteV1/Config/Serializers/MiscSerializers/PuttyConnectionManagerDeserializer.cs
#	mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializer.cs
#	mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializer.cs
#	mRemoteV1/Connection/AbstractConnectionRecord.cs
#	mRemoteV1/Connection/ConnectionInfo.cs
#	mRemoteV1/Connection/ConnectionInfoInheritance.cs
#	mRemoteV1/Connection/ConnectionInitiator.cs
#	mRemoteV1/Connection/ConnectionsService.cs
#	mRemoteV1/Connection/DefaultConnectionInfo.cs
#	mRemoteV1/Connection/Protocol/Http/Connection.Protocol.HTTPBase.cs
#	mRemoteV1/Connection/Protocol/ICA/IcaProtocol.cs
#	mRemoteV1/Connection/Protocol/IntegratedProgram.cs
#	mRemoteV1/Connection/Protocol/ProtocolBase.cs
#	mRemoteV1/Connection/Protocol/PuttyBase.cs
#	mRemoteV1/Connection/Protocol/RDP/RdpProtocol.cs
#	mRemoteV1/Connection/Protocol/VNC/Connection.Protocol.VNC.cs
#	mRemoteV1/Connection/PuttySessionInfo.cs
#	mRemoteV1/Credential/CredentialRecordTypeConverter.cs
#	mRemoteV1/Credential/CredentialServiceFacade.cs
#	mRemoteV1/Credential/CredentialServiceFactory.cs
#	mRemoteV1/Credential/Records/UnavailableCredentialRecord.cs
#	mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs
#	mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs
#	mRemoteV1/Properties/Settings.settings
#	mRemoteV1/Schemas/mremoteng_confcons_v2_7.xsd
#	mRemoteV1/Tools/CustomCollections/FullyObservableCollection.cs
#	mRemoteV1/Tools/Extensions.cs
#	mRemoteV1/Tools/ExternalTool.cs
#	mRemoteV1/Tools/ExternalToolArgumentParser.cs
#	mRemoteV1/UI/Controls/Base/NGComboBox.cs
#	mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs
#	mRemoteV1/UI/Controls/CredentialRecordListBox.cs
#	mRemoteV1/UI/Controls/QuickConnectToolStrip.cs
#	mRemoteV1/UI/Forms/OptionsPages/CredentialsPage.cs
#	mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs
#	mRemoteV1/UI/Forms/frmMain.cs
#	mRemoteV1/UI/Forms/frmOptions.cs
#	mRemoteV1/UI/Menu/ToolsMenu.cs
#	mRemoteV1/UI/Window/ConfigWindow.cs
#	mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs
#	mRemoteV1/UI/Window/ConnectionTreeWindow.cs
#	mRemoteV1/UI/Window/ConnectionWindow.cs
#	mRemoteV1/app.config
2019-02-17 11:21:22 -06:00
David Sparer
ba62c17ea2 added some interface documentation 2019-01-27 18:01:34 -06:00
David Sparer
1f296f2f72 slight fix to the optional<T> tests 2019-01-27 16:59:02 -06:00
David Sparer
89bb4d45b3 minor tweaks to the import form 2019-01-27 16:58:39 -06:00
David Sparer
6e417ed47e ensure specified creds can be parsed even if no connectioninfo is selected 2019-01-27 12:33:14 -06:00
David Sparer
f3a7d97016 allow specifying specific credential record in external tool arguments
related to #680
2019-01-27 11:31:41 -06:00
David Sparer
530a4e165d extracted interface from credential service to make testing easier 2019-01-27 09:58:28 -06:00
David Sparer
9e217dba79 began creating cred import form 2019-01-23 22:16:08 -06:00
David Sparer
d524df6315 minor efficiency gain 2019-01-22 13:04:58 -06:00
David Sparer
5e224f5b5b ensure assigned cred id property cannot be null 2019-01-22 12:51:41 -06:00
David Sparer
3649927618 fixed bug with connection tree not appearing 2019-01-22 12:48:12 -06:00
David Sparer
4b736176bf request iconnectiontree interface in more places 2019-01-22 06:01:33 -06:00
David Sparer
f78ca1e9fd some more deserialization refactoring 2019-01-21 17:52:18 -06:00
David Sparer
739112a3ff added tests for SaveConnectionsOnEdit
had to create some new interfaces and refactor a bit to make it testable
2019-01-20 15:45:09 -06:00
David Sparer
923b9efd40 began converting the database serializers to work with cred manager 2019-01-20 14:51:09 -06:00
David Sparer
f0d0b5ae21 reduced duplication in rdcm tests 2019-01-20 13:46:34 -06:00
David Sparer
8906e08704 made the rdcm 2.7 importer compatible with credential management 2019-01-20 12:38:39 -06:00
David Sparer
9ead7e8e16 fixed an issue with putty session importing
cred id was not being set. also added a test for it
2019-01-20 11:18:40 -06:00
David Sparer
788ca79ece fixed rdp file importer to support cred manager 2019-01-20 11:15:33 -06:00
David Sparer
88558a353c fixed putty connection manager credential serialization 2019-01-19 14:22:04 -06:00
David Sparer
7fd9abbbc8 resolved csv deserializer test 2019-01-19 13:19:06 -06:00
David Sparer
b029b35df7 Merge remote-tracking branch 'origin/develop' into reapply_credential_manager
# Conflicts:
#	mRemoteV1/Properties/Settings.Designer.cs
#	mRemoteV1/Properties/Settings.settings
#	mRemoteV1/app.config
2019-01-19 12:51:43 -06:00
David Sparer
187ca5e55b started to redo how cred harvesting works in order to apply it to all importers/deserializers 2019-01-14 16:32:51 -06:00
David Sparer
53c26d8a91 updated tests and references to using cred records rather than user/domain/pass of the connection 2019-01-14 14:02:41 -06:00
David Sparer
159f25b8a1 fixed exttools arg parser tests 2019-01-14 09:54:19 -06:00
David Sparer
f4904f350e minor cleanup 2019-01-14 07:59:09 -06:00
David Sparer
ea59f6ad9d centralized credential lookup logic 2019-01-14 07:56:31 -06:00
David Sparer
4bba05f737 minor cleanup of cred harvester, slightly more efficient 2019-01-14 07:35:37 -06:00
David Sparer
569cf38f24 fixed a cred harvester test 2019-01-14 07:35:13 -06:00
David Sparer
6f8cde4d8e added a method comment 2019-01-13 14:45:07 -06:00
David Sparer
753ea9b421 connections now correctly use default cred if no credential is supplied 2019-01-13 14:42:24 -06:00
David Sparer
a5ea867b6d began hooking up the default credential to the manager 2019-01-13 12:14:28 -06:00
David Sparer
80228966f9 double clicking cred repo tree item opens editor page 2019-01-13 11:27:42 -06:00
David Sparer
b8298ecb14 minor display changes of the repo manager 2019-01-13 11:19:03 -06:00
David Sparer
ee1db6ff8b refactored some methods/fields to be more descriptive 2019-01-13 11:07:58 -06:00
David Sparer
aab1fb1dd2 minor cleanup 2019-01-13 11:05:45 -06:00
David Sparer
fbea9d1ede hooked up remaining cred repo management buttons 2019-01-13 10:57:33 -06:00
David Sparer
75cfc9c75c Merge branch 'develop' into reapply_credential_manager
# Conflicts:
#	mRemoteV1/Properties/Settings.Designer.cs
#	mRemoteV1/Properties/Settings.settings
#	mRemoteV1/UI/Forms/frmMain.cs
#	mRemoteV1/app.config
2019-01-13 08:52:51 -06:00
David Sparer
f60a481902 Merge branch 'develop' into reapply_credential_manager 2019-01-09 10:27:26 -06:00
David Sparer
e89c77a1bc fixed mis-merge 2019-01-09 10:26:40 -06:00
David Sparer
45766b8c12 Merge branch 'develop' into reapply_credential_manager
# Conflicts:
#	mRemoteV1/UI/Forms/OptionsPages/CredentialsPage.Designer.cs
2019-01-09 07:32:09 -06:00
David Sparer
1c44fcb070 Merge branch 'develop' into reapply_credential_manager
# Conflicts:
#	mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs
#	mRemoteV1/UI/Controls/Base/NGComboBox.cs
#	mRemoteV1/UI/Controls/NewPasswordWithVerification.Designer.cs
#	mRemoteV1/mRemoteV1.csproj
2019-01-05 07:48:43 -06:00
David Sparer
25aa815a82 began reworking the repo list management screen 2018-12-31 14:17:31 -06:00
David Sparer
a2542f1b18 rearranged some cred manager UI files 2018-12-31 11:13:48 -06:00
David Sparer
2e0979dc5a simplified screens for creating and editing creds 2018-12-31 11:05:36 -06:00
David Sparer
e2ebf25b7b Merge branch 'develop' into reapply_credential_manager 2018-12-31 09:32:47 -06:00
David Sparer
cc44b830a2 scaled images in the unlocker form 2018-12-30 15:28:47 -06:00
David Sparer
687cb6c7bc cred unlocker wont appear if there are no repos to unlock 2018-12-30 14:42:37 -06:00
David Sparer
15f028157e inserted a new page in the upgrade process to view harvested credentials 2018-12-30 14:33:42 -06:00
David Sparer
63ebef56b0 updated cred manager upgrade text with correct version 2018-12-30 11:32:24 -06:00
David Sparer
258093e52a when harvesting creds, consider Password when determining if a credential set is a duplicate
This prevents issues when the username and domain are the same but passwords are different (such as for local admin accounts)
2018-12-28 16:25:33 -06:00
David Sparer
b09fdcd5f5 credential objects now used when establishing connections 2018-12-28 16:25:33 -06:00
David Sparer
2277e95dd2 fixed tests 2018-12-28 16:25:33 -06:00
David Sparer
e0486bec7d simplified some of the credential management classes 2018-12-28 16:25:33 -06:00
David Sparer
29d44d103d fixed bug preventing FullyObservableCollection from generating events 2018-12-28 16:25:32 -06:00
David Sparer
44aa100566 minor change to chkbox location 2018-12-28 16:25:32 -06:00
David Sparer
23eb9cc44b added option to auto close the cred unlocker form when all repos are unlocked 2018-12-28 16:25:32 -06:00
David Sparer
f1797282d9 readded option to unlock cred repos on startup 2018-12-28 16:25:32 -06:00
David Sparer
0c0740d488 cred upgrade form now only lets you upgrade if all required fields are filled in 2018-12-28 16:25:32 -06:00
David Sparer
a4faaa20c3 cred manager upgrader now correctly uses custom password provided during upgrade 2018-12-28 16:25:29 -06:00
David Sparer
ce03b74d48 serialization is now working as expected 2018-12-28 16:25:29 -06:00
David Sparer
ce8ada05f5 hooked up the credential manager upgrader 2018-12-28 16:25:29 -06:00
David Sparer
b4dfe5beb6 hooked up tools menu button for cred manager 2018-12-28 16:25:29 -06:00
David Sparer
22ecf0d06f readded forms and controls for the credential manager 2018-12-28 16:25:29 -06:00
David Sparer
1e66787422 readded the credential record property to the connection info class 2018-12-28 16:25:28 -06:00
489 changed files with 31084 additions and 152979 deletions

View File

@@ -3,32 +3,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.77.3]
## [Unreleased]
### Added
- #2138: Improve compatibility with Remote Desktop Connection Manager v2.83
- #2123: Thycotic Secret Server - Added 2FA OTP support
### Changed
- #1546: Enable resize without reconnect for RDP Version Rdc9 or higher
## [1.77.2]
### Added
- #2086: Replace WebClient with async HttpClient for updater.
- #1850: Minify config xml
- #1770: Added missing RDP performance settings
- #1516: added API to access credential vault (Thycotic Secret Server) by specifying SSAPI:ID as username
- #1476: Configurable backups. Can now edit/set backup frequency, backup path, and max number of backup files.
- #1427: Fix RDP local desktop scale not taking effect on remote
- #1770: Added missing RDP performance settings
- #1332: Added option to hide menu strip container
- #870: Added option to push inheritance settings to child nodes recursively
- #545: Option to minimize to system tray on closing
- #503: SSH Execute a single command after login
- #420: SSH tunneling implemented
- #327: Added Alternative Shell for RDP settings
- #319: Override quick connect username when using user@domain
- #283: Support for native PowerShell remoting as new protocol
- #xxx: Add external connector to retrieve ip address from Amazon EC2 Instance IDs
- #1850: Minify config xml
### Changed
- #2102: Extended the field RenderingEngine from 10 chars to 16
- #2022: Replaced CefSharp with WebView2
- #2014: Revised icons
- #2013: Removed components check
@@ -39,33 +24,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- #1767: Turned about window into a simple popup form
- #1690: Replaced GeckoFX (Firefox) with CefSharp (Chromium)
- #1325: Language resource files cleanup
- #xxxx: Secret Server connector via new field "API User ID" instead of SSAPI: prefix
### Fixed
- #2125: Fixed string parsing logic for Quick Connect toolbar.
- #2122: Fix to avoid throwing exception incase if not able decrypt connections and ask to open another one or create a new.
- #2117: Fix of broken Links due migration to .NET 6 and branch renaming
- #2098: Fix failed BinaryFileTest
- #2097: Fix failed tests related to mRemoteNGTests.UI.Window.ConfigWindowTests
- #2096: Corrected encryption code of LegacyRijndaelCryptographyProvider
- #2089: Fixed the exception thrown by menu buttons "Documentation" and "Website"
- #2087: Fixed application crash, when the update file is launched from the application
- #2079: Fixed theme files not being copied to output directory
- #2012: Updated PuTTYNG to v0.76
- #1884: Allow setting Port when using MSSQL
- #1783: Added missing inheritance properties to SQL scripts
- #1773: Connection issue with MySql - Missing fields in
- #1773: Connection issue with mysql - Missing fields in
- #1756: Cannot type any character on MultiSSH toolbar
- #1720: Show configuration file name in title of password prompt form
- #1713: Sound redirection does not work if Clipboard redirection is set to No
- #1632: 1.77.1 breaks RDP drive and sound redirection
- #1610: Menu bar changes to English when canceling options form
- #1610: Menu bar changes to english when cancelling options form
- #1595: Unhandled exception when trying to browse through non existent multi ssh history with keyboard key strokes
- #1589: Update SQL tables instead of rewriting them
- #1465: REGRESSION: Smart Cards redirection to Remote Desktop not working
- #1363: Don't show "Disk Usage" button in installer
- #1337: Unhandled exception after closing mRemoteNG
- #359: Making a VNC connection to an unreachable host causes the application to not respond for 20-30 seconds
- #618: Do not break the Windows Clipboard Chain when exiting.
## [1.77.1] - 2019-09-02
### Added

View File

@@ -278,3 +278,62 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -90,10 +90,6 @@ Copyright © 2004 Marc Merritt © 2008 Felix Deimel
# Included Components
**[CefSharp](https://github.com/cefsharp/CefSharp)**
Copyright © The CefSharp Authors
MIT License
**[DockPanel Suite](https://github.com/dockpanelsuite/dockpanelsuite)**
Copyright © 2018 @roken and @lextm (formerly Weifen Luo)
MIT License

View File

@@ -1,180 +0,0 @@
namespace ExternalConnectors.AWS
{
partial class AWSConnectionForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tbAccesKeyID = new System.Windows.Forms.TextBox();
this.tbAccesKey = new System.Windows.Forms.TextBox();
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.tableLayoutPanel1.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.SuspendLayout();
//
// tbAccesKeyID
//
this.tbAccesKeyID.Dock = System.Windows.Forms.DockStyle.Fill;
this.tbAccesKeyID.Location = new System.Drawing.Point(152, 4);
this.tbAccesKeyID.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.tbAccesKeyID.Name = "tbAccesKeyID";
this.tbAccesKeyID.Size = new System.Drawing.Size(490, 23);
this.tbAccesKeyID.TabIndex = 0;
//
// tbAccesKey
//
this.tbAccesKey.Dock = System.Windows.Forms.DockStyle.Fill;
this.tbAccesKey.Location = new System.Drawing.Point(152, 43);
this.tbAccesKey.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.tbAccesKey.Name = "tbAccesKey";
this.tbAccesKey.Size = new System.Drawing.Size(490, 23);
this.tbAccesKey.TabIndex = 2;
//
// btnOK
//
this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(219, 12);
this.btnOK.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(88, 26);
this.btnOK.TabIndex = 10;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
//
// btnCancel
//
this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(338, 12);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(88, 26);
this.btnCancel.TabIndex = 11;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 22.92994F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 77.07006F));
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.tbAccesKeyID, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.tbAccesKey, 1, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(646, 102);
this.tableLayoutPanel1.TabIndex = 12;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.Location = new System.Drawing.Point(4, 0);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(140, 39);
this.label1.TabIndex = 2;
this.label1.Text = "Access Key ID";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Dock = System.Windows.Forms.DockStyle.Fill;
this.label2.Location = new System.Drawing.Point(4, 39);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(140, 39);
this.label2.TabIndex = 4;
this.label2.Text = "Access Key";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// tableLayoutPanel2
//
this.tableLayoutPanel2.ColumnCount = 5;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 23F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
this.tableLayoutPanel2.Controls.Add(this.btnOK, 1, 0);
this.tableLayoutPanel2.Controls.Add(this.btnCancel, 3, 0);
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 112);
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.RowCount = 1;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(646, 50);
this.tableLayoutPanel2.TabIndex = 13;
//
// AWSConnectionForm
//
this.AcceptButton = this.btnOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(646, 162);
this.Controls.Add(this.tableLayoutPanel2);
this.Controls.Add(this.tableLayoutPanel1);
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.Name = "AWSConnectionForm";
this.Text = "AWS EC2 API Login Data";
this.Activated += new System.EventHandler(this.AWSConnectionForm_Activated);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
public System.Windows.Forms.TextBox tbAccesKeyID;
public System.Windows.Forms.TextBox tbAccesKey;
private System.Windows.Forms.Button btnOK;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
}
}

View File

@@ -1,16 +0,0 @@
namespace ExternalConnectors.AWS
{
public partial class AWSConnectionForm : Form
{
public AWSConnectionForm()
{
InitializeComponent();
}
private void AWSConnectionForm_Activated(object sender, EventArgs e)
{
tbAccesKeyID.Focus();
}
}
}

View File

@@ -1,60 +0,0 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,119 +0,0 @@
using Amazon;
using Amazon.EC2;
using Amazon.EC2.Model;
using Microsoft.Win32;
namespace ExternalConnectors.AWS
{
public class EC2FetchDataService
{
private static DateTime lastFetch;
private static List<InstanceInfo>? lastData;
// input must be in format "AWSAPI:instanceid" where instanceid is the ec2 instance id, e.g. i-066f750a76c97583d
public static async Task<string> GetEC2InstanceDataAsync(string input, string region)
{
// get secret id
if (!input.StartsWith("AWSAPI:"))
throw new Exception("calling this function requires AWSAPI: input");
string InstanceID = input[7..];
// init connection credentials, display popup if necessary
AWSConnectionData.Init();
var alldata = await GetEC2IPDataAsync(region);
var found = alldata.Where(x => x.InstanceId == InstanceID).SingleOrDefault();
return (found == null) ? "" : found.PublicIP;
}
private static async Task<List<InstanceInfo>> GetEC2IPDataAsync(string region)
{
// caching
TimeSpan timeSpan = DateTime.Now - lastFetch;
if (timeSpan.TotalMinutes < 1 && lastData != null)
return lastData;
//AWSConfigs.AWSRegion = AWSConnectionData.region;
AWSConfigs.AWSRegion = region;
string awsAccessKeyId = AWSConnectionData.awsKeyID;
string awsSecretAccessKey = AWSConnectionData.awsKey;
var _client = new AmazonEC2Client(awsAccessKeyId, awsSecretAccessKey, RegionEndpoint.EUCentral1);
bool done = false;
List<InstanceInfo> instanceList = new();
var request = new DescribeInstancesRequest();
while (!done)
{
DescribeInstancesResponse response = await _client.DescribeInstancesAsync(request);
foreach (var reservation in response.Reservations)
{
foreach (var instance in reservation.Instances)
{
string vmname = "";
foreach (var tag in instance.Tags)
{
if (tag.Key == "Name")
{
vmname = tag.Value;
}
}
InstanceInfo inf = new(instance, vmname);
instanceList.Add(inf);
}
}
request.NextToken = response.NextToken;
if (response.NextToken == null)
{
done = true;
}
}
lastData = instanceList.OrderBy(x => x.Name).ToList();
lastFetch = DateTime.Now;
return lastData;
}
public static class AWSConnectionData
{
private static readonly RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteAWSInterface");
public static string awsKeyID = "";
public static string awsKey = "";
//public static string _region = "eu-central-1";
public static void Init()
{
if (awsKey != "")
return;
// display gui and ask for data
AWSConnectionForm f = new();
f.tbAccesKeyID.Text = "" + key.GetValue("KeyID");
f.tbAccesKey.Text = "" + key.GetValue("Key");
//f.tbRegion.Text = "" + key.GetValue("Region");
//if (f.tbRegion.Text == null || f.tbRegion.Text.Length < 2)
// f.tbRegion.Text = region;
_ = f.ShowDialog();
if (f.DialogResult != DialogResult.OK)
return;
// store values to memory
awsKeyID = f.tbAccesKeyID.Text;
awsKey = f.tbAccesKey.Text;
//region = f.tbRegion.Text;
// write values to registry
key.SetValue("KeyID", awsKeyID);
key.SetValue("Key", awsKey);
//key.SetValue("Region", region);
key.Close();
}
}
}
}

View File

@@ -1,34 +0,0 @@
using Amazon.EC2.Model;
using System;
namespace ExternalConnectors.AWS
{
public class InstanceInfo
{
public string InstanceId { get; }
public string Name { get; }
public string Status { get; }
public string PublicIP { get; }
public string PrivateIP { get; }
public InstanceInfo(Instance instance, string name)
{
InstanceId = instance.InstanceId;
Name = name;
switch(instance.State.Code)
{
case 0: Status = "Pending"; break;
case 16: Status = "Running"; break;
case 32: Status = "Shutdown"; break;
case 48: Status = "Terminated"; break;
case 64: Status = "Stopping"; break;
case 80: Status = "Stopped"; break;
default: Status = "Unknown"; break;
};
PublicIP = instance.PublicIpAddress ?? "";
PrivateIP = instance.PrivateIpAddress ?? "";
}
}
}

View File

@@ -1,28 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<UseWindowsForms>True</UseWindowsForms>
<Platforms>x64</Platforms>
<Configurations>Debug;Release;Debug Portable;Release Portable</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.Core" Version="3.7.11.2" />
<PackageReference Include="AWSSDK.EC2" Version="3.7.72" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<Compile Update="AWS\AWSConnectionForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="TSS\SSConnectionForm.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>

View File

@@ -1,280 +0,0 @@
namespace ExternalConnectors.TSS
{
partial class SSConnectionForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SSConnectionForm));
this.tbSSURL = new System.Windows.Forms.TextBox();
this.tbUsername = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.tbPassword = new System.Windows.Forms.TextBox();
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label5 = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.cbUseSSO = new System.Windows.Forms.CheckBox();
this.label6 = new System.Windows.Forms.Label();
this.tbOTP = new System.Windows.Forms.TextBox();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.label4 = new System.Windows.Forms.Label();
this.tableLayoutPanel1.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.SuspendLayout();
//
// tbSSURL
//
this.tbSSURL.Dock = System.Windows.Forms.DockStyle.Fill;
this.tbSSURL.Location = new System.Drawing.Point(260, 4);
this.tbSSURL.Margin = new System.Windows.Forms.Padding(4);
this.tbSSURL.Name = "tbSSURL";
this.tbSSURL.Size = new System.Drawing.Size(536, 23);
this.tbSSURL.TabIndex = 0;
//
// tbUsername
//
this.tbUsername.Dock = System.Windows.Forms.DockStyle.Fill;
this.tbUsername.Location = new System.Drawing.Point(260, 35);
this.tbUsername.Margin = new System.Windows.Forms.Padding(4);
this.tbUsername.Name = "tbUsername";
this.tbUsername.Size = new System.Drawing.Size(536, 23);
this.tbUsername.TabIndex = 2;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Dock = System.Windows.Forms.DockStyle.Fill;
this.label3.Location = new System.Drawing.Point(4, 62);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(248, 31);
this.label3.TabIndex = 5;
this.label3.Text = "Password";
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// tbPassword
//
this.tbPassword.Dock = System.Windows.Forms.DockStyle.Fill;
this.tbPassword.Location = new System.Drawing.Point(260, 66);
this.tbPassword.Margin = new System.Windows.Forms.Padding(4);
this.tbPassword.Name = "tbPassword";
this.tbPassword.Size = new System.Drawing.Size(536, 23);
this.tbPassword.TabIndex = 4;
this.tbPassword.UseSystemPasswordChar = true;
//
// btnOK
//
this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(296, 12);
this.btnOK.Margin = new System.Windows.Forms.Padding(4);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(88, 26);
this.btnOK.TabIndex = 6;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
//
// btnCancel
//
this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(415, 12);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(88, 26);
this.btnCancel.TabIndex = 11;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32.06997F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67.93003F));
this.tableLayoutPanel1.Controls.Add(this.label5, 1, 4);
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.tbSSURL, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.cbUseSSO, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.tbUsername, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.tbPassword, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.label6, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.tbOTP, 1, 3);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 5;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(800, 159);
this.tableLayoutPanel1.TabIndex = 12;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Dock = System.Windows.Forms.DockStyle.Fill;
this.label5.Location = new System.Drawing.Point(260, 124);
this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(536, 35);
this.label5.TabIndex = 15;
this.label5.Text = "For SSO to work, additional IIS configuration is required!";
this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.Location = new System.Drawing.Point(4, 0);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(248, 31);
this.label1.TabIndex = 2;
this.label1.Text = "Secret Server URL";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Dock = System.Windows.Forms.DockStyle.Fill;
this.label2.Location = new System.Drawing.Point(4, 31);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(248, 31);
this.label2.TabIndex = 4;
this.label2.Text = "DOMAIN\\Username";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// cbUseSSO
//
this.cbUseSSO.AutoSize = true;
this.cbUseSSO.Location = new System.Drawing.Point(4, 128);
this.cbUseSSO.Margin = new System.Windows.Forms.Padding(4, 4, 4, 0);
this.cbUseSSO.Name = "cbUseSSO";
this.cbUseSSO.Size = new System.Drawing.Size(69, 19);
this.cbUseSSO.TabIndex = 14;
this.cbUseSSO.Text = "Use SSO";
this.cbUseSSO.UseVisualStyleBackColor = true;
this.cbUseSSO.CheckedChanged += new System.EventHandler(this.cbUseSSO_CheckedChanged);
//
// label6
//
this.label6.AutoSize = true;
this.label6.Dock = System.Windows.Forms.DockStyle.Fill;
this.label6.Location = new System.Drawing.Point(3, 93);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(250, 31);
this.label6.TabIndex = 15;
this.label6.Text = "2FA OTP (Optional)";
//
// tbOTP
//
this.tbOTP.Dock = System.Windows.Forms.DockStyle.Fill;
this.tbOTP.Location = new System.Drawing.Point(259, 96);
this.tbOTP.Name = "tbOTP";
this.tbOTP.Size = new System.Drawing.Size(538, 23);
this.tbOTP.TabIndex = 5;
//
// tableLayoutPanel2
//
this.tableLayoutPanel2.ColumnCount = 5;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 23F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
this.tableLayoutPanel2.Controls.Add(this.btnOK, 1, 0);
this.tableLayoutPanel2.Controls.Add(this.btnCancel, 3, 0);
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 225);
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(4);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.RowCount = 1;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(800, 50);
this.tableLayoutPanel2.TabIndex = 13;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Dock = System.Windows.Forms.DockStyle.Fill;
this.label4.Location = new System.Drawing.Point(0, 159);
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(341, 15);
this.label4.TabIndex = 14;
this.label4.Text = "URL is the base URL, like https://cred.domain.local/SecretServer";
this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// SSConnectionForm
//
this.AcceptButton = this.btnOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 275);
this.Controls.Add(this.label4);
this.Controls.Add(this.tableLayoutPanel2);
this.Controls.Add(this.tableLayoutPanel1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(4);
this.Name = "SSConnectionForm";
this.Text = "Secret Server API Login Data";
this.Activated += new System.EventHandler(this.SSConnectionForm_Activated);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label3;
public System.Windows.Forms.TextBox tbSSURL;
public System.Windows.Forms.TextBox tbUsername;
public System.Windows.Forms.TextBox tbPassword;
private System.Windows.Forms.Button btnOK;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
public System.Windows.Forms.CheckBox cbUseSSO;
private System.Windows.Forms.Label label4;
private Label label5;
private Label label6;
public TextBox tbOTP;
}
}

View File

@@ -1,35 +0,0 @@
namespace ExternalConnectors.TSS
{
public partial class SSConnectionForm : Form
{
public SSConnectionForm()
{
InitializeComponent();
}
private void SSConnectionForm_Activated(object sender, EventArgs e)
{
SetVisibility();
if (cbUseSSO.Checked)
btnOK.Focus();
else
{
if (tbPassword.Text.Length == 0)
tbPassword.Focus();
else
tbOTP.Focus();
}
}
private void cbUseSSO_CheckedChanged(object sender, EventArgs e)
{
SetVisibility();
}
private void SetVisibility()
{
bool ch = cbUseSSO.Checked;
tbPassword.Enabled = !ch;
tbUsername.Enabled = !ch;
}
}
}

View File

@@ -1,615 +0,0 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAQAQEAAAAEAIAAoQAAARgAAACAgAAABACAAKBAAAG5AAAAYGAAAAQAgACgJAACWUAAAEBAAAAEA
IAAoBAAAvlkAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9/f3e/f397vz9
/e79/f3u7ezr7p6Xju5XTEHuNScV7i8hCe4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHAjuKx8I7jYoFu5XTUHumJGO7u3r6+79/f3u/f397v39
/e79/f3e/f399P//////////u7Wy/0M1KP8jEwD/KRgA/ywdBv8tHwj/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8sHAT/KRcA/yAR
AP9DNSX/ubOt/////////////f399Pv8/e7/////m5aK/xoMAP8lFAD/LSAI/y0gCv8uIAr/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cv8uIAr/LB4J/ysfCf8uIQj/JBYA/xoLAP+elYz///////z9/O79/f3uvLWw/xsNAP8rHAX/LiAK/y0f
Cf8tHwj/LR8I/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCf8tHwn/LR8J/y4gCv8pGgP/GgsA/7y3sP/9/f3u6unp7kM0
I/8nFwH/LiEL/yweCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAr/LR8J/yIU
AP9DNSL/6+nq7pqUju4fEQD/LiAI/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8uIAf/HxAA/5qTju5YTT7uJRYA/ywgCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Kx8I/ygXAP9YTkDuOCkV7iweBv8sHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHQX/NykW7ioc
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8uIAn/Lh8I/yocCO4qHAjuLyEK/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8K/y4gCf8rHAjuKx8I7i4gCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0fCP8tHwn/LR8J/y0f
CP8tHwn/LR8J/y0fCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
CP8tHwj/LR8J/y0fCf8tHwj/LR8J/y0fCf8tHwn/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8uIAj/Kx8I7isfCO4uIAn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8I/y8fCP8uHwj/LR8G/yweCP8uHgn/LR4H/yweB/8rHgf/Kx4H/yweCP8sHgj/Kx4I/yse
CP8sHgj/LB4I/yweCP8sHgn/LB4J/yoeCP8qHgj/LB4I/y4fCP8sIQj/LB8H/y0eCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysf
CO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tIAn/LiAK/y0WAf8sFgH/LRsG/ywbBv8uGwX/MR0B/y8cAv8uGwX/LRkG/ysZ
Bf8qGQX/KhkF/ygaBP8oGgX/JxoF/ycaBf8pGgX/KRkG/ykZBv8nGQT/JxkF/ygZCv8jFAb/IBUG/ywe
Cf8tHwf/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Lh0I/y0XA/84Qyf/P31P/0aSXP9KkVv/UpJT/2OV
Sv93mUf/jJ9A/6CjPP+vpjv/rac4/6ilN/+ppDn/qaQ4/62jOP+uozj/sKI2/7KiNv+yojX/s6Az/7Wi
NP+0oDP/mIUs/1pIFv8lFQb/Kh4I/y4gCf8sHgn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR4I/ygWA/83b0X/SsOE/0zC
f/9QvHf/Ubp0/1e4b/9YtGz/WrJl/2KwZP96tWD/lL9X/7fNUf/b2E//3NhP/9nWTP/X00v/29NL/9vS
Sv/d0Ur/39FJ/9/RSP/j0Ej/5tBD/+3VRP/r0EX/jHYl/yMUBv8sHwb/LB4I/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ysV
AP8zWTb/ScmH/0e8ef9Ltnb/TrRx/1Cyb/9VsGr/Wa1m/1mrYf9dqV3/XqRc/1yfWf9ooVL/g6tQ/7XD
Tf/R0Ur/ys1H/87LR//Oykb/0MlG/9PKRf/Uy0X/18pF/9jJQf/byEH/4MtD/+7VRv9rWxz/HxMC/y8h
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/Kx8I/y0cBv8vHQj/QKFs/0fEgP9IuHX/TLZ0/0+0cP9UsW3/Vq9o/1isZP9cqWD/X6hc/2Ok
Wf9loVX/aKBT/2iZUP9um0j/obZJ/87RTP/SzUz/yspI/8zLSP/Qykj/0stH/9XJR//VyUT/2clE/9fH
RP/izkn/vao2/ywdBv8rHAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tGwX/MCoV/0azfP9Fvnv/Sbh2/0y2c/9PtG//VbFs/1av
Z/9YrGT/XKlg/2KnXP9kpVr/ZaJX/2mhU/9unkz/bZtK/2qVR/+asUj/zdJM/8nOSf/JzUv/zcxJ/8/L
R//Rykf/0cpE/9XKRP/WykX/3c5G/9DBQv86Kw//KRoH/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwr/LRoE/ywsGP9Dt3//R7x6/0q3
dv9OtHL/ULNu/1Wwa/9Xrmb/Watj/12pXv9hplv/ZaRY/2eiVP9qoFH/bp5M/3OcSf9ymUb/b5BC/6m5
SP/L0k7/yM5M/8vNS//LzEn/z8xJ/87LR//Rykf/08tH/9nNSP/RxkT/PS8R/ygZBv8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LB8K/y4a
A/8uLRj/RLZ9/0e8ev9LtnX/ULNy/1Gybf9VsGr/Wa5l/1mrYv9dqVz/YKZa/2WkVv9oolL/aqBQ/2+e
S/9zm0n/dJpE/3SUQ/+Dmj7/wMxM/8fPTP/IzUz/yc5L/8zNS//LzEr/z8tK/8/LSP/UzUj/0MZG/z0u
EP8oGgX/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8uIAj/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/ywfCv8uGgP/Li0Y/0W2fP9Iu3n/TLZ0/1Czcf9Rsm3/VbBq/1muZf9aq2L/Xqlc/2Gm
Wv9lpFb/aKJS/2ugUP9wnkv/c5tI/3WaQ/95l0H/do07/6m6Rf/F007/xM5N/8XOTP/IzUz/yM5L/8zN
S//MzEn/0c9J/83HR/89LhD/KBoG/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwr/LRsE/zAsGP9HtHr/S7p3/061cv9Qs2//U7Fr/1Wv
af9arGP/XKlh/2GnW/9lpFn/ZaNV/2igUf9unk7/cpxK/3WbRv91mEH/e5Y//3mPOP+YrEH/wtVT/7/O
Uf/CzlD/xc1Q/8TOTP/IzUz/yc5L/87QTP/HyEn/PS8P/ykZBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8K/y0bBP8wLBj/R7R6/0u6
d/9OtXL/ULNv/1Oxa/9Vr2n/Wqxj/1ypYf9hp1v/ZaRZ/2WjVf9ooFH/bp5O/3KcSv91m0b/dZhB/3yW
P/97kDj/j6I9/7/VV/+90VH/wNBR/8PPUf/Bz0z/xM5M/8XOS//K0Uz/xclJ/z0vD/8pGQb/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tGwT/MCwa/0qzef9MuXb/TrRx/1Kybv9Tr2n/V61k/12qYP9fqF3/YKZZ/2SkVv9qoVL/bKBP/26d
TP9ym0b/dJpE/3iWQf98lD7/fY43/42gPf+111v/t9RU/7nSU/+/z1L/vtBQ/8HPUP/Cz03/xtFN/8HJ
TP88MA//KhsF/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LRsE/zAsGv9Ks3n/TLl2/060cf9Ssm7/U69p/1etZP9dqmD/X6hd/2Cm
Wf9kpFb/aqFS/2ygT/9unUz/cptG/3SaRP94lkH/fJQ+/36NNv+LpkP/qNxh/6zYWv+v1Ff/ttFV/7zS
Uf/A0VL/wNFP/8TUT//Ayk3/PDAP/yobBP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4cA/8wLBn/SrN2/024dP9Ps3D/VLFs/1eu
Z/9Zq2T/XKlf/2KnXf9jpVj/ZaNV/2qgUf9unk7/cJ1K/3KbRf92mUP/e5U+/3mUPP9/jDP/ibRM/5zi
aP+g2mD/pdhe/6zVW/+01Ff/utNU/7vSUv/D1VH/vctN/zswEf8pGgb/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8uHQL/MCwY/0qz
df9NuHP/T7Nv/1Wwa/9arWb/Wqtj/1qpX/9jplz/ZKRY/2WiVf9qoFH/b55N/3CdSf9ym0T/d5hC/3yW
Pf98kjn/g441/4nRZP+N4mv/ld5n/5rcZf+g22D/p9hd/63XWf+y1Fb/utZU/7jMTv86MBH/KBoG/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysf
CO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/Lh0D/zAsF/9LsnT/Trdz/1Gybv9WsGr/WK1l/1uqYv9dqV3/YqZb/2WkVv9nolT/aqBQ/2+e
TP9ynEn/c5pD/3aXQf97lT3/gokz/3uvT/986nr/g+Rw/4nibf+O32v/lN5n/5rcY/+h22D/ptdd/6/Y
Wv+xzlX/Oi8R/ygZBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4cBf8wLRb/TbFz/1C2cv9TsWz/V7Bo/1etZP9cqmH/Yahb/2Gl
Wv9mo1T/aKFS/2ugTv9wnUr/dJpI/3WZQ/98kz3/gYo3/3WsUP9s7ID/but8/3fneP995XT/geJy/4fh
bf+O32r/lN5m/5rbZP+j3GL/pdJd/zkvEv8pFwX/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHAX/MC0W/02xc/9QtnL/U7Fs/1ev
aP9YrWT/XKpg/1+oW/9hpVr/ZqNV/2mhUv9sn03/cJxJ/3WVRP95kT3/eJZA/2TAYf9Y8Yj/XPOI/2fs
gv9r637/cel7/3bneP995XT/g+Nw/4nibP+P32r/l+Bo/5jXYf85MBL/KhgG/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/KhsE/zAs
FP9NsXH/UbVv/1awav9arWf/Wqxi/12oXv9jo1v/Y6FU/2mcUP9tm0r/bptI/2+cSv9rp1P/YMNo/1Do
hf9G/ZX/SfaO/1PyjP9b8Ir/X+6F/2btgv9s637/cul6/3fmeP995XT/g+Nw/4vlbv+O22n/NjAU/yca
BP8uHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/yobBP8wLBT/TbJy/1G1b/9WsGr/Wq1n/1uoX/9dp13/YKlf/2CvXv9gt2X/XcBq/1XQ
dv9P4YP/SPCO/0T3lP9J85L/R/SR/0n1kf9L9ZD/TfSP/1Tzi/9b8Yj/Yu+E/2ftgP9r637/cel6/3jn
dv+A6XT/geBs/zgxFf8pGwX/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y8fCf8rHAL/LisU/02ucv9Vs23/VLBp/1ipYP9XuGr/TtOA/0fg
if9H5Ir/RuqO/0Xukf9G7pL/Ru2Q/0Ttjf9J7I7/R+6N/0nwj/9L8ZD/S/KR/0nzkP9I85D/TPSN/1X0
if9d8If/Ye6D/2fsf/9s6nz/cux7/3Xic/81MRb/KxkE/ywfCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/Lh0D/y4gCP9PnWP/Vbdx/1eq
ZP9TwXP/RuSM/0fmjv9F4ov/Q+SJ/0bliv9F5or/RuaM/0bojP9G6or/R+uM/0jtjP9J7o3/SO+O/0nw
j/9I8o//RvKP/0f0j/9K947/S/WN/1Tyif9b8YT/YO6D/2n2hP9kz27/KyEM/y4dBv8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB8J/y4e
B/8sFgH/PGU2/1i9dP9Utm3/RN6J/0Leif9J3on/SN+K/0Xgif9H4ov/R+KL/0fjjP9I5Yz/R+eL/0jo
jP9I6o3/SeuO/0jsjf9J7Y7/SO+O/0jwjv9J8Y//S/OQ/0r0kf9M9ZD/T/SO/1Pziv9h/5X/SIJE/yoP
Af8sHwf/LR8K/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwn/LBwH/ywdCf9IfEf/Tsx8/0Ddif9F14X/RduH/0bdiP9E3on/Rd+K/0fl
jf9I6JD/SOqQ/0jsj/9J7ZD/SO6R/0nvkv9H8JD/SPKR/0n0kf9K84//SPCP/0fxkP9J8pH/SvWS/0r3
lP9M/pn/RKxh/y0YBv8sHAf/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sGgX/KhUE/0GsbP9E4Y7/QteG/0TZ
h/9F2oj/RdyI/0Teiv9Ez3//RMp7/0XNe/9Eznr/RM97/0TQff9F0H3/Q9J7/0PTfP9E0n3/SNyC/0zq
iv9M6Yr/TeqL/0zsiv9P8Iz/S7Vp/ywWB/8sGgT/LiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/Lh8G/ywR
BP87m2b/ReCR/0PUiP9E1oj/RtiJ/0bZh/9D5o//Nlk1/ywZB/8vJg3/LyYM/y8mDP8vJQ3/LyUN/ywm
DP8sJgz/KhgH/z9sO/9c2Hr/Vslx/1fJcf9YyXL/XdF3/0yRUv8pEwT/LyEJ/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LB8J/y0eBv8sEwX/PJ1n/0Tej/9D0of/Q9SG/0TWiP9F2Ib/QuWO/zVPLv8sDgD/MBsI/y8b
B/8vGwb/LhsE/y4bBP8sGgb/KxoG/ywKAP87Zjr/U+CD/1HQe/9R0Hv/UtB7/1Pcg/9En17/KxMD/y4g
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4fCf8sHwj/LhUD/0CbZP9F2o7/RM+H/0TQhv9F0of/Q9WG/0bk
kf8yVzL/KxEA/y4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tDgD/O3ZF/0zslP9I2ob/SdqG/0na
hv9L5o7/QaNm/ywVAv8uHgf/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwj/LiAJ/ywUAf89jlf/Q9qO/0LN
hf9Dz4T/RNCG/0LRg/9H4I//O4dQ/yoOAP8tIAj/LR8J/y0fCf8tHwn/LR8J/y0fCP8tHgj/LBMC/z+n
ZP9H6pH/RN+K/0Tfiv9E34r/RuyT/z2bXv8uEwH/LR8H/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB8J/y0f
Cf8rDwD/NXNF/0PZjP9DyYX/RcuF/0XNhv9EzYT/RdSJ/0TFfv8uKRH/LBYC/ywfCP8tHwn/LR8J/ywf
Cf8sHwj/KBAA/zZAIf9H34n/R+GL/0Xdif9F3Yn/Rd2I/0nvlv86gU3/Kg4A/y0eCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysf
CO4rHwjuLiAI/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCf8rHwj/LRMB/zZRLv9H0ov/QsmE/0LKhP9Ey4X/Q8uE/0XMhP9H3I7/Pppj/ysa
Bv8sEQD/Lh0I/y0fB/8tGgX/Kg4A/y0lD/9AuXH/R+qQ/0Lci/9D34n/Rd6J/0PdiP9C65H/M1wx/y8U
Av8uIAf/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y4gCP8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB4I/y4bBv8uJQ//QLV4/0LNh/9Bx4P/Q8iE/0PJ
g/9Ey4P/RMuG/0Xajv9EpGn/MEoo/ysfC/8uFgr/LiUN/zNULf8+vHf/SOWQ/0TYh/9F3Ij/Rt6J/0fe
i/9E44z/Rc9//y0qDv8vGQX/LSAH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCf8tHwn/KxEA/zt2
Sv9F047/QcOC/0PFg/9Cx4L/RMmD/0PKgv9EzIP/RdaL/0fUif8/t3P/Pqpv/0C+dv9H3I//Rt6O/0HW
hv9E1oj/RdiI/0Tah/9G3In/R+6U/zuET/8sDwD/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0aBP8wKhH/QLV4/0HKhv9BxIP/QsWB/0LHgv9CyIL/QsmD/0XKg/9FzoT/Q9KH/0LY
iP9G14r/Q9CH/0XRhf9F0oX/RNSI/0LXiP9C14X/ReGM/0XOgv8uKxT/KxgF/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwf/LRYA/zRTL/9Dy4v/QcWG/0HCgf9CxIL/QsSC/0PE
g/9CxoP/Q8iC/0TKhP9Dy4T/RsyF/0PNhf9Cz4b/RNCH/0XSiP9H0oj/QtiJ/0jikP8yWjX/KxAA/ywg
B/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LSAH/y8gBv8vEgD/OWlC/0PL
if8+xYP/P8KA/0DCgf9Bw4L/QsaC/0LHgf9DyIL/QsqC/0PLg/9CzIT/Q82F/0TOhf9Fz4X/RdaK/0fg
lf85dkf/KhEA/yseB/8uHwj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8vHwn/Kh4I/y8WAP82Yzj/P7+C/0LLiP9CwoL/QMGC/0HFhP9BxIH/Q8WD/0PGhP9Ex4X/RMmE/0bK
hf9DzYT/QtyN/0nTiP86aj3/LRIA/y4eCP8sIAr/Lh8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8rIAj/KhQA/zU/IP9Bl2X/RseI/0LLiv8/x4b/QMaD/0DF
g/9CxoT/Q8mG/0LOh/9G1o3/RtSK/0WjaP8yQiD/LBEA/y8eCP8sIAn/LR8I/y0fCP8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/LR4I/y4gCv8sFQD/KxoE/zRJ
Kf9Cg1P/Q6tv/0O6ev9EwoL/QsOC/0a+fv9FsHD/QoZS/zZJJ/8sGgT/KxMB/y4fCf8wIAj/Lh8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAI/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0f
Cf8tHwj/LR8J/y0dCP8rEwL/LBQA/zEjCf8yLxP/Njce/zQ3H/8yMBT/MiMJ/y8UAP8rEgD/LB0H/y4g
Cf8tHwn/LB8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y4gCP8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAI/yseB/8rHQj/LBoF/ysXAv8sGAP/KxsF/ygb
Bv8wHgf/LR8J/y4fCv8uIAr/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
CO4uIAj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8I/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LB8J/y0fCf8tHwj/LR8I/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LiAJ/ysfCO4rHwjuLyEJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuLh8J7i8hCP8tIAj/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwj/LyEJ7jgqFu4sHgX/LR8H/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LB4G/zUn
FO5YTT7uJhUA/y4gCv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LSAL/yoZAP9XTD7um5SO7iMRAP8vIAn/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwn/LR8I/ywfBv8iEgD/npeO7urq6e5ENSX/JBQA/y0hCP8tIAf/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Kx0H/y4gCv8lFQD/QzYm/+vr6e79/f3uvLax/xoM
AP8pGwL/LiEI/y0fCf8tHwj/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHQX/Gw0A/723
tP/9/f3u+vv87v////+ak4n/GQsA/yQUAP8uHwn/LR8J/y0fCP8uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCf8sHgj/LB8J/y4g
B/8mFwD/Gw0A/5qViv//////+/z87v39/fT//////////7q1sP9DNCb/IxEA/ycWAP8rHgX/LiAH/y8h
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/zAh
Cv8uHwj/LB0G/yQWAP8fEQD/QzQm/7q0r/////////////39/fT9/f3e/f397vz9/e79/f3u6+rp7puU
ju5YTj/uOCkW7i4fCu4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
CO4rHwjuKx8I7isfCO4rHwjuKyAI7jcpFu5YTj7umpOO7uvr6u79/f3u/Pz97v39/e79/f3eKAAAACAA
AABAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP39/dzt6+vtdGte7TQmFu0rHQjtLR8I7S0f
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7SsdBu00Jhbtc2le7e3r6e39/f3c6ubl81ZKOP8dDgD/LB4G/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LyAK/ywdBf8dDgD/VUk4/+rn5vN0a13tHg4A/y4g
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8dDQD/dWtd7TQm
Eu0rHQb/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywd
Bf80JhLtKx0G7S8gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LiAJ/ysdBu0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwj/LBcC/ysT
AP8pFAH/JhMC/yISA/8hEQP/IBID/yASA/8gEgT/HxIE/x8RBP8jFgb/LB4I/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR4I/ywY
BP81QiP/Olw1/0RbLP9WXSb/bGMj/3RnIP9vZCD/bmIg/3FiIP9yYh7/dWMf/1ZFFv8lFwb/LB4I/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8rFgL/N2c//0vDgf9SwHr/W7pv/2a2Zf9+t13/p8RT/9bYT//g20z/3tZL/+LWSv/p10f/69RF/39r
Iv8iFQT/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LBwG/y4lD/9Esnb/Sr55/1Kybv9XrWf/Xahe/1+iWP9imlH/faJJ/8DHSv/Pz0r/z8tI/9PK
Rf/f0Uf/z75A/zQmC/8pGwj/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8sGgX/Ly0W/0S0ef9NuHb/U7Fs/1itZP9ep1z/ZqNW/2yfTv9slUb/hqFE/8fP
TP/Lzkv/zcxJ/9TOSf/Ow0T/PS4P/ygaB/8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywbBf8wLBX/RrF1/0+4dP9UsGv/Wqxj/2CmWv9molT/b55N/3WZ
RP93kTz/r8BI/8fST//Hzkz/zdFM/8bERv87LQ7/KRoH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0f
CO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBsF/zAsFf9Jr3T/ULdz/1WvaP9dqmD/YqVZ/2mg
Uv9wnEv/dplD/3mOOf+iuEn/wNZV/8HPT//H0k7/v8ZJ/zstDv8pGgf/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sGwX/MCwV/0mvc/9RtnH/Vq1m/16p
Xv9jpFf/bKBQ/3CcSP93mEH/fIw2/5bCU/+q3F//tdNW/8HWUv+7yEv/Oi4O/ykaB/8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywbBf8wLRT/S65w/1O1
bv9ZrGT/YKdc/2ajVv9sn0//cpxG/3uPOf+AmT7/hd5t/5Hhav+e22L/rdtc/6/KUf85LQ//KRoH/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBsG/zAs
E/9NrW7/VrRt/1qrYf9ipFj/aZ5R/3GXR/94kj//dKFI/2bddv9t74H/eeZ2/4Xib/+U4mr/mdFe/zgt
EP8qGQb/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8sGwb/LiwU/06ubf9ZsGn/Xahf/2CqXv9jr13/YLtj/1jRdP9K8o7/SvqT/1byi/9j7oP/b+p8/3zq
d/+D22r/NjAS/yoaBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/ywcBv8vJw7/Ualp/1e3bP9N0n//R+GJ/0bojf9E75H/RPKR/0jwj/9J8ZD/R/OR/0z1
jv9W8on/ZfWF/2vhd/8yKg//KxoG/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8I/ysXAv9CaTr/TNSC/0PijP9F34n/RueP/0n0lv9J95b/SvqY/0n9
mP9J+5b/R/OR/0j4lP9Q/pb/RI5N/ysTAv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0f
CO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LRwG/ywbB/9Aunf/RN6M/0bijf9At3D/OXJC/zp7
Rf86fEb/NnhD/z+SUv9S3H//U+KD/068bf8sGgj/LRsH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHQf/LRwI/0CydP9E2oz/RuWR/zqO
Vv8qAAD/LA8A/ywOAP8pAgD/Mzkb/0/bhP9Q24T/SLRs/y0dCP8tHQf/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8tGQT/PqZp/0TW
jP9F1or/Qbh0/ywbBv8sFQL/LBwG/ykGAP86eEX/Ru6U/0Xpkf9At3H/LRkE/y0eB/8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ysR
AP86fk//RNeP/0TJg/9G14z/PI1Y/y4pEP8tHQn/M1ky/0TWhv9F4Yz/R/CV/zqOU/8sEAD/LR8I/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LBcD/zE5HP9CxoX/QsqF/0PIgv9F1ov/QsR+/0C7d/9F2oz/RNmK/0Xdiv9G34z/MT0d/ywW
A/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LBQA/zdmPf9C0Iz/QcmG/0LFgv9DzIX/RNGI/0POhv9F14v/R+WU/zdv
Qv8qEQD/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBUB/zVeNv9Ct3v/Q8+M/0LRjP9E1I7/RteO/0TE
fv84ZDr/LRQB/y0eCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBMA/y8qEf84Xjb/O3lM/zt6
Tf86YDf/MCoR/ysSAP8tHgj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0f
CO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBoF/ysT
AP8sEQD/KxEA/ysSAP8sGgX/LSAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8uIAn/LR8I7SweBu0vIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y4gCf8rHQbtNCYT7SwdBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LB4G/zQmEu11a17tHQ4A/y4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0gCf8dDgD/dmxe7ern5vNVSTf/Hg4A/ywdBv8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8rHQX/Hg4A/1ZKOP/q5+fz/f393Ozr6e10aV7tNSYU7Swe
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLB4I7TQmFO1zaV7t7evp7f39/dwoAAAAGAAAADAA
AAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f393JSPhO00JhbtKx0G7S0fCO0tHwjtLR8I7S0f
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0rHQbtMyUW7ZSO
g+39/f3ckouC8yESAP8qHAT/LyAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8vIAn/KhwE/yERAP+Si4LzMyUR7SocBP8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/yocBP8zJRLtKx0F7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8sGgX/KxoG/yka
B/8oGgf/KBoH/ygaB/8nGgf/KBoH/yweCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHQXtLR8I7S4g
Cf8tHwn/LR8J/y0fCf8tHwn/LRwH/y0aBf8wLRP/Ni8Q/0ExDv9FMw3/PjAN/z4vDv8/MA3/PS4N/ycZ
B/8qHAj/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHQf/LB8K/z6E
VP9NsnL/Wapi/3GoVv+bskv/x8VG/87FRP/PwkL/2cZB/6GMLf8tHwj/Kx0I/y0fCf8tHwn/LR8J/y4g
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8sFgH/MkYn/0nIhP9Rt3L/WK9m/16nXf9jnlP/iKlK/8nO
S//T0Er/2M9H/+jYSv9XSRf/IxUF/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
Cf8sFQH/M0os/0vBfv9Tsm3/Wqxi/2OlWP9sn07/bJJD/5uuRP/M0k3/y81L/9nWTP9cTxv/IhQF/y0f
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8rFQH/NEkq/02+e/9UsGr/Xalg/2Wj
Vv9unkz/dJRB/4ufPv+/01P/xM9O/8/XT/9YThr/IxQE/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
Cf8tHwn/LR8J/y0fCf8rFgH/NUkp/068ef9Xrmf/X6dc/2iiVP9wnUn/eI86/4ilRP+k3mL/tNVX/8ba
U/9WTxv/JBQE/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8rFgH/Nkgo/1K7
dv9ZrWT/YqRZ/2ycTf93kj//epU+/3bVa/+D53T/lN5n/63hYv9RTx//JRMD/y0fCf8tHwn/LR8J/y4g
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8qFQL/NUop/1a6c/9cqWH/Yatd/2SwW/9gv2b/UeeE/1L4
kP9k7oL/dOh5/4vvdf9JViX/JxIC/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
Cf8rFwP/NT4e/1a5cP9N0H3/R+GI/0fwkv9F+5n/SPuY/0j7lv9J95H/VfaM/2f6iP88SyH/KhQC/y0f
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHgj/LBUC/z6ETv9E6JL/Rd+L/0LG
ef9DwHP/QsR1/0LHdP9L4oX/T/aS/0KaV/8rFgT/LR4I/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
Cf8tHwn/LR8J/y0fCf8tHwn/Kw8A/zVgOv9F4pL/Rd+N/zJGJP8tEAD/LRoF/ywaBv9Lum3/VOeL/zle
M/8qDwD/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/KxMA/zVX
Mf9F2I7/RtuO/zl3R/8qBQD/KQQA/zJDIv9F4Iv/RvCW/zVfNf8rEQD/LR8J/y0fCf8tHwn/LR8J/y4g
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LBoF/zAtE/9Cv3//RNCJ/0TNhf86g1D/N29C/0LF
fP9G6JH/RNeF/y8wFP8sGQT/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/ywTAP82Yzv/QtKN/0PPif9F2I3/Rd2Q/0XekP9H5ZP/N2w//ysRAP8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8sFQH/NVs1/0Cu
dP9CxoT/RMqG/0O3dv83YTj/LBQB/y0eCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBMA/y4iC/8zPB7/Mj0e/y8hCv8rEgD/LR4I/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtKx0F7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/ywcB/8sFwP/LBcD/ywcB/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
Cf8rHQbtNCUS7SocBP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/yocBP80JhLtkouD8yERAP8qHAT/LyAJ/y4g
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
Cf8vIAn/KhwE/yERAP+VjITz/f393JSOhO00JhbtKx0G7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0rHQbtMyUW7ZSOg+39/f3cKAAAABAA
AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL65stw/MR/tKBkF7S0fCO0tHwjtLR8I7S0f
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0oGQXtPzEf7b65stw8MR3zJxgA/y8gCf8uIAn/LiAJ/y4g
Cf8uHwn/Lh8J/y4fCf8uHwn/LiAJ/y4gCf8uIAn/LyAJ/ycYAP88MR3zKBoE7S8gCf8tHwn/LSAJ/ywa
Bf8rFAH/LBcD/ysXBP8kFQT/IxUF/yATBP8oGgf/LiAJ/y0fCf8vIQn/KBkE7S0fCO0uIAn/LR8J/ywa
Bf8wMxj/QYNR/1OFSP94iTr/npg0/6aYM/+hji7/RDQP/ycaB/8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0f
Cf8rEQD/OW9F/1LNhP9dtGj/aahY/5u2TP/Z2U3/7+ZP/4V2KP8eEAT/LR8J/y4gCf8tHwjtLR8I7S4g
Cf8tHwn/KxIA/ztvRf9Tv3f/Xqhd/2ufT/91kj7/tMNK/9fgU/+AeSr/IBAD/y0fCf8uIAn/LR8I7S0f
CO0uIAn/LR8J/ysTAP89bUL/Vrxz/2OjWP9ylkX/fZQ7/5XTX/+952D/d3ot/yEQA/8tHwn/LiAJ/y0f
CO0tHwjtLiAJ/y0fCf8qEwD/P29C/1y6bv9iqlv/Z7Fa/1zad/9l8oX/ifZ6/2OGPP8lDgH/LR8J/y4g
Cf8tHwjtLR8I7S4gCf8tHwn/KhQB/zxULf9P0oD/SOSL/0b1lf9F/Jn/SPaS/1j7kP9DbTb/KRAB/y0f
Cf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0cB/8uIwz/QcuC/0HCef8zRSL/MD0e/0SmYP9M0n7/LSMN/ywc
Bv8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHQj/LRsG/0Cxc/9Ez4b/MDca/yweCf9Cxnr/Qsd6/y0b
Bv8tHQj/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/ywUAf81XTf/RNiR/0TTiv9E1In/R+2Y/zZn
Ov8rEgH/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHgj/LBcC/zRWMv8+nWb/P6Jo/zZb
NP8sFgL/LR4I/y0fCf8tHwn/LiAJ/y0fCO0oGgTtLyEJ/y0fCf8tHwn/LR8J/y0fCP8sFAH/LBYC/ywW
Av8rEwD/LR4I/y0fCf8tHwn/LR8J/y8hCf8oGQTtPTEd8ycYAP8vIAn/LiAJ/y4gCf8uIAn/LiAJ/y4f
CP8uHwn/LiAJ/y4gCf8uIAn/LiAJ/y8gCf8nGAD/PTEd87u5stw/MR/tKBkF7S0fCO0tHwjtLR8I7S0f
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0oGQXtPzEf7b65stwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</value>
</data>
</root>

View File

@@ -1,383 +0,0 @@
//----------------------
// <auto-generated>
// Generated using the NSwag toolchain v13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
namespace SecretServerAuthentication.TSS
{
using System = global::System;
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class OAuth2ServiceClient
{
private string _baseUrl = "";
private System.Net.Http.HttpClient _httpClient;
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
public OAuth2ServiceClient(string baseUrl, System.Net.Http.HttpClient httpClient)
{
BaseUrl = baseUrl;
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
{
var settings = new Newtonsoft.Json.JsonSerializerSettings();
UpdateJsonSerializerSettings(settings);
return settings;
}
public string BaseUrl
{
get { return _baseUrl; }
set { _baseUrl = value; }
}
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
/// <summary>Retrieve or Refresh Access Token</summary>
/// <param name="grant_type">Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</param>
/// <param name="username">Secret Server authentication username. Required when authenticating.</param>
/// <param name="password">Secret Server authentication password. Required when authenticating.</param>
/// <param name="refresh_token">The refresh token. Required when refreshing a token.</param>
/// <returns>Successful retrieval of an access token</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public System.Threading.Tasks.Task<TokenResponse> AuthorizeAsync(Grant_type grant_type, string username, string password, string refresh_token, string OTP)
{
return AuthorizeAsync(grant_type, username, password, refresh_token, System.Threading.CancellationToken.None, OTP);
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>Retrieve or Refresh Access Token</summary>
/// <param name="grant_type">Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</param>
/// <param name="username">Secret Server authentication username. Required when authenticating.</param>
/// <param name="password">Secret Server authentication password. Required when authenticating.</param>
/// <param name="refresh_token">The refresh token. Required when refreshing a token.</param>
/// <returns>Successful retrieval of an access token</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public async System.Threading.Tasks.Task<TokenResponse> AuthorizeAsync(Grant_type grant_type, string username, string password, string refresh_token, System.Threading.CancellationToken cancellationToken, string OTP)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/oauth2/token");
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var keyValues_ = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string, string>>();
if (grant_type == null)
throw new System.ArgumentNullException("grant_type");
else
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("grant_type", ConvertToString(grant_type, System.Globalization.CultureInfo.InvariantCulture)));
if (username != null)
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("username", ConvertToString(username, System.Globalization.CultureInfo.InvariantCulture)));
if (password != null)
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("password", ConvertToString(password, System.Globalization.CultureInfo.InvariantCulture)));
if (refresh_token != null)
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("refresh_token", ConvertToString(refresh_token, System.Globalization.CultureInfo.InvariantCulture)));
if (OTP != null)
request_.Headers.Add("OTP", ConvertToString(OTP, System.Globalization.CultureInfo.InvariantCulture));
request_.Content = new System.Net.Http.FormUrlEncodedContent(keyValues_);
request_.Method = new System.Net.Http.HttpMethod("POST");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<TokenResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
if (status_ == 400)
{
var objectResponse_ = await ReadObjectResponseAsync<TokenErrorResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
throw new ApiException<TokenErrorResponse>("An error occurred", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
protected struct ObjectResponseResult<T>
{
public ObjectResponseResult(T responseObject, string responseText)
{
this.Object = responseObject;
this.Text = responseText;
}
public T Object { get; }
public string Text { get; }
}
public bool ReadResponseAsString { get; set; }
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Threading.CancellationToken cancellationToken)
{
if (response == null || response.Content == null)
{
return new ObjectResponseResult<T>(default(T), string.Empty);
}
if (ReadResponseAsString)
{
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
return new ObjectResponseResult<T>(typedBody, responseText);
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
}
}
else
{
try
{
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var streamReader = new System.IO.StreamReader(responseStream))
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
{
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
var typedBody = serializer.Deserialize<T>(jsonTextReader);
return new ObjectResponseResult<T>(typedBody, string.Empty);
}
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
}
}
}
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null)
{
return "";
}
if (value is System.Enum)
{
var name = System.Enum.GetName(value.GetType(), value);
if (name != null)
{
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
return attribute.Value != null ? attribute.Value : name;
}
}
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
return converted == null ? string.Empty : converted;
}
}
else if (value is bool)
{
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[])value);
}
else if (value.GetType().IsArray)
{
var array = System.Linq.Enumerable.OfType<object>((System.Array)value);
return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
}
var result = System.Convert.ToString(value, cultureInfo);
return result == null ? "" : result;
}
}
/// <summary>API access token response</summary>
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
public partial class TokenResponse
{
/// <summary>Authentication token</summary>
[Newtonsoft.Json.JsonProperty("access_token", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
public string Access_token { get; set; }
/// <summary>Authentication token type</summary>
[Newtonsoft.Json.JsonProperty("token_type", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public TokenResponseToken_type Token_type { get; set; }
private string _Expires_in;
/// <summary>Authentication token expiration time, in seconds</summary>
[Newtonsoft.Json.JsonProperty("expires_in", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
// public string Expires_in { get; set; }
public string Expires_in
{
get { return _Expires_in; }
set
{
_Expires_in = value;
Expires_on = DateTime.UtcNow.AddSeconds(Double.Parse(value) - 60);
}
}
/// <summary>Authentication token expiration time in UTC</summary>
public DateTime Expires_on { get; set; }
/// <summary>Refresh token. This is only provided when the server is set to allow refresh tokens for web services and when the session timeout duration is not set to Unlimited.</summary>
[Newtonsoft.Json.JsonProperty("refresh_token", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Refresh_token { get; set; }
}
/// <summary>API access token error response</summary>
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
public partial class TokenErrorResponse
{
/// <summary>Authentication token</summary>
[Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
public string Message { get; set; }
}
/// <summary>Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</summary>
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
public enum Grant_type
{
[System.Runtime.Serialization.EnumMember(Value = @"password")]
Password = 0,
[System.Runtime.Serialization.EnumMember(Value = @"refresh_token")]
Refresh_token = 1,
}
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
public enum TokenResponseToken_type
{
[System.Runtime.Serialization.EnumMember(Value = @"bearer")]
Bearer = 0,
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class ApiException : System.Exception
{
public int StatusCode { get; private set; }
public string Response { get; private set; }
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> Headers { get; private set; }
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Exception innerException)
: base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException)
{
StatusCode = statusCode;
Response = response;
Headers = headers;
}
public override string ToString()
{
return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString());
}
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class ApiException<TResult> : ApiException
{
public TResult Result { get; private set; }
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, TResult result, System.Exception innerException)
: base(message, statusCode, response, headers, innerException)
{
Result = result;
}
}
}
#pragma warning restore 1591
#pragma warning restore 1573
#pragma warning restore 472
#pragma warning restore 114
#pragma warning restore 108
#pragma warning restore 3016

View File

@@ -1,254 +0,0 @@
using Microsoft.Win32;
using SecretServerAuthentication.TSS;
using SecretServerRestClient.TSS;
namespace ExternalConnectors.TSS
{
public class SecretServerInterface
{
private static class SSConnectionData
{
public static string ssUsername = "";
public static string ssPassword = "";
public static string ssUrl = "";
public static string ssOTP = "";
public static bool ssSSO = false;
public static bool initdone = false;
//token
public static string ssTokenBearer = "";
public static DateTime ssTokenExpiresOn = DateTime.UtcNow;
public static string ssTokenRefresh = "";
public static void Init()
{
if (initdone == true)
return;
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteSSInterface");
try
{
// display gui and ask for data
SSConnectionForm f = new SSConnectionForm();
string? un = key.GetValue("Username") as string;
f.tbUsername.Text = un ?? "";
f.tbPassword.Text = SSConnectionData.ssPassword; // in OTP refresh cases, this value might already be filled
string? url = key.GetValue("URL") as string;
if (url == null || !url.Contains("://"))
url = "https://cred.domain.local/SecretServer";
f.tbSSURL.Text = url;
var b = key.GetValue("SSO");
if (b == null || (string)b != "True")
ssSSO = false;
else
ssSSO = true;
f.cbUseSSO.Checked = ssSSO;
// show dialog
while (true)
{
_ = f.ShowDialog();
if (f.DialogResult != DialogResult.OK)
return;
// store values to memory
ssUsername = f.tbUsername.Text;
ssPassword = f.tbPassword.Text;
ssUrl = f.tbSSURL.Text;
ssSSO = f.cbUseSSO.Checked;
ssOTP = f.tbOTP.Text;
// check connection first
try
{
if (TestCredentials() == true)
{
initdone = true;
break;
}
}
catch (Exception)
{
MessageBox.Show("Test Credentials failed - please check your credentials");
}
}
// write values to registry
key.SetValue("Username", ssUsername);
key.SetValue("URL", ssUrl);
key.SetValue("SSO", ssSSO);
}
catch (Exception)
{
throw;
}
finally
{
key.Close();
}
}
}
private static bool TestCredentials()
{
if (SSConnectionData.ssSSO)
{
// checking creds doesn't really make sense here, as we can't modify them anyway if something is wrong
return true;
}
else
{
if (!String.IsNullOrEmpty(GetToken()))
{
return true;
}
else
{
return false;
}
}
}
private static void FetchSecret(int secretID, out string secretUsername, out string secretPassword, out string secretDomain)
{
string baseURL = SSConnectionData.ssUrl;
SecretModel secret;
if (SSConnectionData.ssSSO)
{
// REQUIRES IIS CONFIG! https://docs.thycotic.com/ss/11.0.0/api-scripting/webservice-iwa-powershell
var handler = new HttpClientHandler() { UseDefaultCredentials = true };
using (var httpClient = new HttpClient(handler))
{
// Call REST API:
var client = new SecretsServiceClient($"{baseURL}/winauthwebservices/api", httpClient);
secret = client.GetSecretAsync(false, true, secretID, null).Result;
}
}
else
{
using (var httpClient = new HttpClient())
{
var token = GetToken();
// Set credentials (token):
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
// Call REST API:
var client = new SecretsServiceClient($"{baseURL}/api", httpClient);
secret = client.GetSecretAsync(false, true, secretID, null).Result;
}
}
// clear return variables
secretDomain = "";
secretUsername = "";
secretPassword = "";
// parse data and extract what we need
foreach (var item in secret.Items)
{
if (item.FieldName.ToLower().Equals("domain"))
secretDomain = item.ItemValue;
else if (item.FieldName.ToLower().Equals("username"))
secretUsername = item.ItemValue;
else if (item.FieldName.ToLower().Equals("password"))
secretPassword = item.ItemValue;
}
}
private static string GetToken()
{
// if there is no token, fetch a fresh one
if (String.IsNullOrEmpty(SSConnectionData.ssTokenBearer))
{
return GetTokenFresh();
}
// if there is a token, check if it is valid
if (SSConnectionData.ssTokenExpiresOn >= DateTime.UtcNow)
{
return SSConnectionData.ssTokenBearer;
}
else
{
// try using refresh token
using (var httpClient = new HttpClient())
{
var tokenClient = new OAuth2ServiceClient(SSConnectionData.ssUrl, httpClient);
TokenResponse token = new();
try
{
token = tokenClient.AuthorizeAsync(Grant_type.Refresh_token, null, null, SSConnectionData.ssTokenRefresh, null).Result;
var tokenResult = token.Access_token;
SSConnectionData.ssTokenBearer = tokenResult;
SSConnectionData.ssTokenRefresh = token.Refresh_token;
SSConnectionData.ssTokenExpiresOn = token.Expires_on;
return tokenResult;
}
catch (Exception)
{
// refresh token failed. clean memory and start fresh
SSConnectionData.ssTokenBearer = "";
SSConnectionData.ssTokenRefresh = "";
SSConnectionData.ssTokenExpiresOn = DateTime.Now;
// if OTP is required we need to ask user for a new OTP
if (!String.IsNullOrEmpty(SSConnectionData.ssOTP))
{
SSConnectionData.initdone = false;
// the call below executes a connection test, which fetches a valid token
SSConnectionData.Init();
// we now have a fresh token in memory. return it to caller
return SSConnectionData.ssTokenBearer;
}
else
{
// no user interaction required. get a fresh token and return it to caller
return GetTokenFresh();
}
}
}
}
}
static string GetTokenFresh()
{
using (var httpClient = new HttpClient())
{
// Authenticate:
var tokenClient = new OAuth2ServiceClient(SSConnectionData.ssUrl, httpClient);
// call below will throw an exception if the creds are invalid
var token = tokenClient.AuthorizeAsync(Grant_type.Password, SSConnectionData.ssUsername, SSConnectionData.ssPassword, null, SSConnectionData.ssOTP).Result;
// here we can be sure the creds are ok - return success state
var tokenResult = token.Access_token;
SSConnectionData.ssTokenBearer = tokenResult;
SSConnectionData.ssTokenRefresh = token.Refresh_token;
SSConnectionData.ssTokenExpiresOn = token.Expires_on;
return tokenResult;
}
}
// input must be in form "SSAPI:xxxx" where xxx is the secret id to fetch
public static void FetchSecretFromServer(string input, out string username, out string password, out string domain)
{
// get secret id
if (!input.StartsWith("SSAPI:"))
throw new Exception("calling this function requires SSAPI: input");
int secretID = Int32.Parse(input.Substring(6));
// init connection credentials, display popup if necessary
SSConnectionData.Init();
// get the secret
FetchSecret(secretID, out username, out password, out domain);
}
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -2,7 +2,7 @@
<br/><br/>
<p align="center">
<img width="450" src="https://github.com/mRemoteNG/mRemoteNG/blob/mRemoteNGProjectFiles/Header_dark.png">
<img width="500" src="https://github.com/mRemoteNG/mRemoteNG/blob/develop/mRemoteNGProjectFiles/Header_dark.png">
</p>
<p align="center">
@@ -23,9 +23,7 @@
<a href="https://gitter.im/mRemoteNG/PublicChat">
<img alt="Gitter" src="https://img.shields.io/gitter/room/mRemoteNG/PublicChat?label=Join%20the%20Chat&logo=Gitter&style=flat-square">
</a>
</p>
<p align="center">
<a href="https://www.paypal.com/paypalme/mremoteng">
<a href="https://www.paypal.me/DavidSparer">
<img alt="PayPal" src="https://img.shields.io/badge/%24-PayPal-blue.svg?label=Donate&logo=PayPal&style=flat-square">
</a>
<a href="bitcoin:16fUnHUM3k7W9Fvpc6dug7TAdfeGEcLbSg">
@@ -47,11 +45,11 @@
---
| Channel | Build Status | Downloads |
| Update Channel | Build Status | Downloads |
| ---------------|--------------|-----------|
| Stable | ![Build status](https://ci.appveyor.com/api/projects/status/rqwxjxldail7btcf?svg=true) | [![Github Releases (by Release)](https://img.shields.io/github/downloads/mRemoteNG/mRemoteNG/v1.76.20/total.svg)](https://github.com/mRemoteNG/mRemoteNG/releases/tag/v1.76.20) |
| Preview | ![Build status](https://ci.appveyor.com/api/projects/status/rqwxjxldail7btcf/branch/preview?svg=true) | [![Github Releases (by Release)](https://img.shields.io/github/downloads/mRemoteNG/mRemoteNG/v1.77.1/total.svg)](https://github.com/mRemoteNG/mRemoteNG/releases/tag/v1.77.1) |
| Nightly | ![Build status](https://ci.appveyor.com/api/projects/status/rqwxjxldail7btcf/branch/develop?svg=true) | [![Github Releases (by Release)](https://img.shields.io/github/downloads/mRemoteNG/mRemoteNG/2022.01.07-1.77.2-nb/total.svg)](https://github.com/mRemoteNG/mRemoteNG/releases/tag/2022.01.07-1.77.2-nb) |
| Stable | [![Build status](https://ci.appveyor.com/api/projects/status/k0sdbxmq90fgdmj6/branch/master?svg=true)](https://ci.appveyor.com/project/mremoteng/mremoteng/branch/master) | [![Github Releases (by Release)](https://img.shields.io/github/downloads/mRemoteNG/mRemoteNG/v1.76.20/total.svg)](https://github.com/mRemoteNG/mRemoteNG/releases/tag/v1.76.20) |
| Prerelease | [![Build status](https://ci.appveyor.com/api/projects/status/k0sdbxmq90fgdmj6/branch/develop?svg=true)](https://ci.appveyor.com/project/mremoteng/mremoteng/branch/develop) | [![Github Releases (by Release)](https://img.shields.io/github/downloads/mRemoteNG/mRemoteNG/v1.77.1/total.svg)](https://github.com/mRemoteNG/mRemoteNG/releases/tag/v1.77.1) |
| Nightly build | [![Build status](https://ci.appveyor.com/api/projects/status/k0sdbxmq90fgdmj6/branch/develop?svg=true)](https://ci.appveyor.com/project/mremoteng/mremoteng/branch/develop) | [![Github Releases (by Release)](https://img.shields.io/github/downloads/mRemoteNG/mRemoteNG/v1.77.2-nb/total.svg)](https://github.com/mRemoteNG/mRemoteNG/releases/tag/v1.77.2-nb) |
## Features
@@ -75,11 +73,24 @@ For a detailed feature list and general usage support, refer to the [Documentati
- [Windows 11](https://en.wikipedia.org/wiki/Windows_11)
- [Windows 10](https://en.wikipedia.org/wiki/Windows_10)
- [Windows 8.1](https://en.wikipedia.org/wiki/Windows_8.1)
- [Windows Server 2022](https://en.wikipedia.org/wiki/Windows_Server_2022)
- [Windows Server 2019](https://en.wikipedia.org/wiki/Windows_Server_2019)
- [Windows Server 2016](https://en.wikipedia.org/wiki/Windows_Server_2016)
- [Windows Server 2012 R2](https://en.wikipedia.org/wiki/Windows_Server_2012_R2)
### Packaging
Downloads are provided in three different packages.
#### Binary package
The binary package of mRemoteNG is a compiled version of mRemoteNG which comes in an MSI installer.
This is the most common way to install mRemoteNG and get up and running.
#### Portable package
The portable package contains a modified version of the executable which stores and loads all your settings from files in the application's directory.
This package can be used to run mRemoteNG from a USB stick and preserve your configuration wherever you go.
#### Source package
This contains the source code from which mRemoteNG is build.
@@ -87,15 +98,13 @@ You will need to compile it yourself using Visual Studio.
### Minimum Requirements
* [Microsoft Visual C++ Redistributable for Visual Studio 2015 - 2022](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads)
* [Microsoft .NET 6.0 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/6.0)
* [Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads)
* [Microsoft .NET Framework 4.0](https://www.microsoft.com/en-us/download/details.aspx?id=17851)
* Microsoft Terminal Service Client 6.0 or later
* Needed if you use RDP. mstscax.dll and/or msrdp.ocx be registered.
### Download
> :star: Starting Windows 11 you can use winget to install mRemoteNG. Just run `winget install -e --id mRemoteNG.mRemoteNG`
mRemoteNG is available as a redistributable MSI package or as a portable ZIP package and can be downloaded from the following locations:
* [GitHub](https://github.com/mRemoteNG/mRemoteNG/releases)
* [Project Website](https://mremoteng.org/download)
@@ -104,31 +113,52 @@ mRemoteNG is available as a redistributable MSI package or as a portable ZIP pac
The MSI package of mRemoteNG can be installed using the command line:
`msiexec /i [/qn] C:\Path\To\mRemoteNG-Installer.exe [INSTALLDIR=value] [IGNOREPREREQUISITES=value] [/lv* <log path>]`
`msiexec /i C:\Path\To\mRemoteNG-Installer.exe [INSTALLDIR=value] [IGNOREPREREQUISITES=value]`
| Argument/Property | Value | Description |
| Property | Value | Description |
|-|-|-|
| /qn | `Silent Installation` | Will run the installer silently in the background. |
| /lv* | `Silent Installation` | Will write a logfile to the specified location. (For paths that contain spaces, enclose the path in double quotes) |
| INSTALLDIR | `folder path` | Allows you to set the installation directory from the command line. (For paths that contain spaces, enclose the path in double quotes) |
| INSTALLDIR | `folder path` | This allows you to set the installation directory from the command line. For paths that contain spaces, enclose the path in double quotes (""). This overrides any value found in the registry. |
| IGNOREPREREQUISITES | `0` or `1` | When set to `1`, the installer will not be halted if any prerequisite check is not met. You must still run the installer as administrator. |
## Manual Uninstall
#### Examples
**Install to a custom folder**
`msiexec /i C:\Path\To\mRemoteNG-Installer.msi INSTALLDIR="D:\Work Apps\mRemoteNG"`
**Ignore prerequisites during a normal install**
`msiexec /i C:\Path\To\mRemoteNG-Installer.msi IGNOREPREREQUISITES=1`
**Ignore prerequisites during a silent install**
`msiexec /i C:\Path\To\mRemoteNG-Installer.msi /qn IGNOREPREREQUISITES=1`
### Troubleshooting installation
Turn on verbose logging by using the `/lv* <log path>` argument at the command line.
`msiexec /i C:\Path\To\mRemoteNG-Installer.msi /l*v C:\mremoteng_install.log`
## Uninstall
### Standard Uninstall
mRemoteNG basic binary package can be uninstalled with Windows Control Panel. If for some reason it does not work please
follow information provided below for Manual Uninstall.
### Manual Uninstall
_If you are using the Portable version, simply deleting the folder that contains mRemoteNG should be sufficient. These uninstall instructions are only necessary for the normal binary .MSI installed version of mRemoteNG_
* Delete the folder where mRemoteNG was installed. By default, this is:
`%PROGRAMFILES%\mRemoteNG` (for versions before 1.77 on a x64 Windows its `%programfiles(x86)%\mRemoteNG`)
`%PROGRAMFILES%\mRemoteNG`
* Delete the mRemoteNG install entry from the following location. You may search for "mRemoteNG" in the DisplayName field:
* x86 Windows or mRemoteNG starting with v1.77: `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\`
* x64 Windows and mRemoteNG before 1.77: `HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\`
* Remove the following registry key: `HKLM\SOFTWARE\mRemoteNG` (on x64 Windows with mRemoteNG before 1.77 it's `HKLM\SOFTWARE\WOW6432Node\mRemoteNG`)
* Delete the mRemoteNG install entry from one of the following locations. Search for "mRemoteNG" in the DisplayName field:
* x86: ``HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\``
* x64: ``HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\``
* (Optional) If you would also like to delete user data remove `%LOCALAPPDATA%\mRemoteNG`
* (Optional) If you would also like to remove the connection configuration, delete `%APPDATA%\mRemoteNG`
* (Optional) If no other software uses it, the "Microsoft Windows Desktop Runtime" may be uninstalled too.
## Featured Projects
@@ -147,5 +177,5 @@ Check out the [Wiki page](https://github.com/mRemoteNG/mRemoteNG/wiki) on how to
</br>
<p align="center">
<img alt="Developed with ReSharper" src="https://github.com/mRemoteNG/mRemoteNG/blob/mRemoteNGProjectFiles/icon_ReSharper.png">
<img alt="Developed with ReSharper" src="https://github.com/mRemoteNG/mRemoteNG/blob/develop/mRemoteNGProjectFiles/icon_ReSharper.png">
</p>

View File

@@ -0,0 +1,21 @@
param (
[string]
$SourcePath,
[string]
$DestinationDir
)
Write-Host $SourcePath
Write-Host $DestinationDir
if (!(Test-Path -Path $DestinationDir))
{
New-Item -Path $DestinationDir -ItemType "directory"
}
$sourceFiles = Get-ChildItem -Path $SourcePath -Recurse | ?{$_.Extension -match "exe|msi"}
foreach ($item in $sourceFiles)
{
Copy-Item -Path $item.FullName -Destination $DestinationDir -Force
}

View File

@@ -25,9 +25,9 @@ param (
$ExcludeFromSigning
)
Write-Output "+===========================================================================================+"
Write-Output "| Beginning mRemoteNG Installer Post Build |"
Write-Output "+===========================================================================================+"
Write-Output "+=================================================================+"
Write-Output "| Beginning mRemoteNG Installer Post Build |"
Write-Output "+=================================================================+"
Format-Table -AutoSize -Wrap -InputObject @{
"SolutionDir" = $SolutionDir
"TargetDir" = $TargetDir
@@ -40,4 +40,5 @@ Format-Table -AutoSize -Wrap -InputObject @{
& "$PSScriptRoot\sign_binaries.ps1" -TargetDir $TargetDir -CertificatePath $CertificatePath -CertificatePassword $CertificatePassword -ConfigurationName $ConfigurationName -Exclude $ExcludeFromSigning -SolutionDir $SolutionDir
& "$PSScriptRoot\verify_binary_signatures.ps1" -TargetDir $TargetDir -ConfigurationName $ConfigurationName -CertificatePath $CertificatePath -SolutionDir $SolutionDir
& "$PSScriptRoot\rename_and_copy_installer.ps1" -SolutionDir $SolutionDir
& "$PSScriptRoot\rename_installer_with_version.ps1" -SolutionDir $SolutionDir
& "$PSScriptRoot\copy_release_installer.ps1" -SourcePath $TargetDir -DestinationDir (Join-Path -Path $SolutionDir -ChildPath "Release")

View File

@@ -25,9 +25,9 @@ param (
$ExcludeFromSigning
)
Write-Output "+===========================================================================================+"
Write-Output "| Beginning mRemoteNG Post Build |"
Write-Output "+===========================================================================================+"
Write-Output "+=================================================================+"
Write-Output "| Beginning mRemoteNG Post Build |"
Write-Output "+=================================================================+"
Format-Table -AutoSize -Wrap -InputObject @{
"SolutionDir" = $SolutionDir
"TargetDir" = $TargetDir
@@ -37,25 +37,9 @@ Format-Table -AutoSize -Wrap -InputObject @{
"ExcludeFromSigning" = $ExcludeFromSigning
}
# Move dlls resurses into folder
#Remove-Item -Path "$TargetDir\libs" -Recurse -ErrorAction Ignore
#New-Item -ItemType "directory" -Force -Path $TargetDir -Name "libs"
#Move-Item -Path "$TargetDir\*.dll" -Destination "$TargetDir\libs" -force
###
# Move lang resurses into folder
#Remove-Item -Path "$TargetDir\languages" -Recurse -ErrorAction Ignore
#New-Item -ItemType "directory" -Force -Path $TargetDir -Name "languages"
#"cs-CZ,de,el,en-US,es-AR,es,fr,hu,it,lt,ja-JP,ko-KR,nb-NO,nl,pt,pt-BR,pl,ru,uk,tr-TR,zh-CN,zh-TW,fi-FI".Split(",") | ForEach {
# Move-Item -Path "$TargetDir\$_" -Destination "$TargetDir\languages" -force
# }
###
& "$PSScriptRoot\set_LargeAddressAware.ps1" -TargetDir $TargetDir -TargetFileName $TargetFileName
& "$PSScriptRoot\verify_LargeAddressAware.ps1" -TargetDir $TargetDir -TargetFileName $TargetFileName
& "$PSScriptRoot\tidy_files_for_release.ps1" -TargetDir $TargetDir -ConfigurationName $ConfigurationName
& "$PSScriptRoot\sign_binaries.ps1" -TargetDir $TargetDir -CertificatePath $CertificatePath -CertificatePassword $CertificatePassword -ConfigurationName $ConfigurationName -Exclude $ExcludeFromSigning -SolutionDir $SolutionDir
& "$PSScriptRoot\verify_binary_signatures.ps1" -TargetDir $TargetDir -ConfigurationName $ConfigurationName -CertificatePath $CertificatePath -SolutionDir $SolutionDir
& "$PSScriptRoot\zip_files.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir -ConfigurationName $ConfigurationName
& "$PSScriptRoot\zip_files.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir -ConfigurationName $ConfigurationName

View File

@@ -1,13 +0,0 @@
param (
[string]
$SolutionDir
)
$targetVersionedFile = "$SolutionDir\mRemoteNG\bin\x64\Release\mRemoteNG.exe"
$version = &"$SolutionDir\Tools\exes\sigcheck.exe" /accepteula -q -n $targetVersionedFile
$src = $SolutionDir + "mRemoteNGInstaller\Installer\bin\Release\en-US\mRemoteNG-Installer.msi"
$dst = $SolutionDir + "mRemoteNG\bin\x64\Release\mRemoteNG-Installer-" + $version + ".msi"
# Copy file
Copy-Item $src -Destination $dst -Force

View File

@@ -0,0 +1,29 @@
param (
[string]
$SolutionDir
)
$renameTarget = $SolutionDir + "mRemoteNGInstaller\Installer\bin\Release\en-US\mRemoteNG-Installer.msi"
Write-Host $SolutionDir
Write-Host $renameTarget
$targetVersionedFile = "$SolutionDir\mRemoteNG\bin\Release\mRemoteNG.exe"
$version = &"$SolutionDir\Tools\exes\sigcheck.exe" /accepteula -q -n $targetVersionedFile
$renameTargetFileObject = Get-Item -Path $renameTarget -ErrorAction SilentlyContinue
if ($renameTargetFileObject)
{
# Build the new file name
$oldFileName = $renameTargetFileObject.Name
$newFileName = $oldFileName -replace "$("\"+$renameTargetFileObject.Extension)",$("-"+$version+$renameTargetFileObject.Extension)
Write-Host $oldFileName
Write-Host $newFileName
# Delete any items that already exist with the new name (effectively an overwrite)
Remove-Item -Path "$($renameTargetFileObject.Directory.FullName)\$newFileName" -ErrorAction SilentlyContinue
# Rename file
Rename-Item -Path $renameTarget -NewName $newFileName -ErrorAction SilentlyContinue
}

View File

@@ -7,15 +7,15 @@ param (
[Parameter(Mandatory=$true)]
$ConfigurationName
)
Write-Output ""
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
# Remove unnecessary files from Release versions
if ($ConfigurationName -match "Release")
{
if ($ConfigurationName -match "Release") {
Write-Output "Removing unnecessary files from Release versions"
$test = Join-Path -Path $TargetDir -ChildPath "app.publish"
if (Test-Path $test -PathType Container)
{
if (Test-Path $test -PathType Container) {
Remove-Item -Path (Join-Path -Path $TargetDir -ChildPath "app.publish") -Recurse -Force
}
@@ -29,18 +29,10 @@ if ($ConfigurationName -match "Release")
) -Exclude @(
"mRemoteNG.VisualElementsManifest.xml"
)
if ($filesToDelete)
{
Write-Output "Unnecessary files are detected and will be removed"
Remove-Item -Path $filesToDelete.FullName
Write-Output $filesToDelete.FullName
} else
{
Write-Output " No unnecessary files are detected"
}
Remove-Item -Path $filesToDelete.FullName
Write-Output $filesToDelete.FullName
}
else
{
else {
Write-Output "We will not remove anything - this is not a release build."
}

View File

@@ -20,7 +20,6 @@ param (
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
# validate release versions and if the certificate value was passed
if ($ConfigurationName -match "Release" -And ($CertificatePath)) {

View File

@@ -12,7 +12,6 @@
$ConfigurationName
)
Write-Output ""
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
$ConfigurationName = $ConfigurationName.Trim()
@@ -47,13 +46,8 @@ if ($ConfigurationName -match "Release") {
}
# Local build
else {
if (!(Test-Path -Path $debugFile -PathType Leaf))
{
$outputZipPath = "$($SolutionDir)Release\$zipFilePrefix-$($version).zip"
Compress-Archive $debugFile $outputZipPath -Force
} else {
write-host "File do not exist:" $debugFile", nothing to compress"
}
$outputZipPath = "$($SolutionDir)Release\$zipFilePrefix-$($version).zip"
Compress-Archive $debugFile $outputZipPath -Force
}
Remove-Item $debugFile
@@ -70,13 +64,8 @@ if ($ConfigurationName -eq "Release Portable") {
}
# Local build
else {
if ($Source)
{
$outputZipPath="$($SolutionDir)\Release\mRemoteNG-Portable-$($version).zip"
Compress-Archive $Source $outputZipPath -Force
} else {
write-host "File do not exist:" $Source", nothing to compress"
}
$outputZipPath="$($SolutionDir)\Release\mRemoteNG-Portable-$($version).zip"
Compress-Archive $Source $outputZipPath -Force
}
}

View File

@@ -1,11 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31912.275
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.352
MinimumVisualStudioVersion = 14.0.25420.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mRemoteNG", "mRemoteNG\mRemoteNG.csproj", "{4934A491-40BC-4E5B-9166-EA1169A220F6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mRemoteNG", "mRemoteNG\mRemoteNG.csproj", "{4934A491-40BC-4E5B-9166-EA1169A220F6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mRemoteNGTests", "mRemoteNGTests\mRemoteNGTests.csproj", "{1453B37F-8621-499E-B0B2-6091F76DC0BB}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mRemoteNGTests", "mRemoteNGTests\mRemoteNGTests.csproj", "{1453B37F-8621-499E-B0B2-6091F76DC0BB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mRemoteNGInstaller", "mRemoteNGInstaller", "{4FE795BE-646E-4F1B-BAD0-A68EA26394DD}"
EndProject
@@ -16,63 +16,108 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Installer", "mRemoteNGInsta
{5423D985-CB48-4344-B47F-E8C6D60C8B04} = {5423D985-CB48-4344-B47F-E8C6D60C8B04}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mRemoteNGSpecs", "mRemoteNGSpecs\mRemoteNGSpecs.csproj", "{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExternalConnectors", "ExternalConnectors\ExternalConnectors.csproj", "{A56A2029-79B8-492A-ABE5-D2BFE05801BD}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mRemoteNGSpecs", "mRemoteNGSpecs\mRemoteNGSpecs.csproj", "{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug Portable|x64 = Debug Portable|x64
Debug|x64 = Debug|x64
Release Installer|x64 = Release Installer|x64
Release Portable|x64 = Release Portable|x64
Release|x64 = Release|x64
Debug Portable|Any CPU = Debug Portable|Any CPU
Debug Portable|x86 = Debug Portable|x86
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release Installer|Any CPU = Release Installer|Any CPU
Release Installer|x86 = Release Installer|x86
Release Portable|Any CPU = Release Portable|Any CPU
Release Portable|x86 = Release Portable|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug Portable|x64.ActiveCfg = Debug Portable|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug Portable|x64.Build.0 = Debug Portable|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug|x64.ActiveCfg = Debug|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug|x64.Build.0 = Debug|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Installer|x64.ActiveCfg = Release|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Installer|x64.Build.0 = Release|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Portable|x64.ActiveCfg = Release Portable|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Portable|x64.Build.0 = Release Portable|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release|x64.ActiveCfg = Release|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release|x64.Build.0 = Release|x64
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug Portable|x64.ActiveCfg = Debug Portable|x64
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug|x64.ActiveCfg = Debug|x64
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|x64.ActiveCfg = Release|x64
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Portable|x64.ActiveCfg = Release Portable|x64
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Portable|x64.Build.0 = Release Portable|x64
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release|x64.ActiveCfg = Release|x64
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release|x64.Build.0 = Release|x64
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug Portable|x64.ActiveCfg = Debug Portable|x64
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug|x64.ActiveCfg = Debug|x64
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Installer|x64.ActiveCfg = Release|x64
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Installer|x64.Build.0 = Release|x64
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Portable|x64.ActiveCfg = Release Portable|x64
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release|x64.ActiveCfg = Release|x64
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Debug Portable|x64.ActiveCfg = Debug Portable|x64
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Debug|x64.ActiveCfg = Debug|x64
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Installer|x64.ActiveCfg = Release|x64
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Installer|x64.Build.0 = Release|x64
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Portable|x64.ActiveCfg = Release Portable|x64
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release|x64.ActiveCfg = Release|x64
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug Portable|x64.ActiveCfg = Debug Portable|x64
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug|x64.ActiveCfg = Debug|x64
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|x64.ActiveCfg = Release|x64
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Portable|x64.ActiveCfg = Release Portable|x64
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release|x64.ActiveCfg = Release|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Debug Portable|x64.ActiveCfg = Debug Portable|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Debug Portable|x64.Build.0 = Debug Portable|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Debug|x64.ActiveCfg = Debug|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Debug|x64.Build.0 = Debug|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Release Installer|x64.ActiveCfg = Release|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Release Installer|x64.Build.0 = Release|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Release Portable|x64.ActiveCfg = Release Portable|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Release Portable|x64.Build.0 = Release Portable|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Release|x64.ActiveCfg = Release|x64
{A56A2029-79B8-492A-ABE5-D2BFE05801BD}.Release|x64.Build.0 = Release|x64
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug Portable|Any CPU.ActiveCfg = Debug Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug Portable|Any CPU.Build.0 = Debug Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug Portable|x86.ActiveCfg = Debug Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug Portable|x86.Build.0 = Debug Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug|Any CPU.ActiveCfg = Debug|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug|Any CPU.Build.0 = Debug|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug|x86.ActiveCfg = Debug|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Debug|x86.Build.0 = Debug|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Installer|Any CPU.ActiveCfg = Release|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Installer|Any CPU.Build.0 = Release|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Installer|x86.ActiveCfg = Release|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Installer|x86.Build.0 = Release|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Portable|Any CPU.ActiveCfg = Release Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Portable|Any CPU.Build.0 = Release Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Portable|x86.ActiveCfg = Release Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release Portable|x86.Build.0 = Release Portable|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release|Any CPU.ActiveCfg = Release|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release|Any CPU.Build.0 = Release|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release|x86.ActiveCfg = Release|x86
{4934A491-40BC-4E5B-9166-EA1169A220F6}.Release|x86.Build.0 = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug Portable|Any CPU.ActiveCfg = Debug Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug Portable|Any CPU.Build.0 = Debug Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug Portable|x86.ActiveCfg = Debug Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug Portable|x86.Build.0 = Debug Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug|Any CPU.ActiveCfg = Debug|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug|Any CPU.Build.0 = Debug|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug|x86.ActiveCfg = Debug|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug|x86.Build.0 = Debug|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|Any CPU.ActiveCfg = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|Any CPU.Build.0 = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|x86.ActiveCfg = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|x86.Build.0 = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Portable|Any CPU.ActiveCfg = Release Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Portable|Any CPU.Build.0 = Release Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Portable|x86.ActiveCfg = Release Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Portable|x86.Build.0 = Release Portable|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release|Any CPU.ActiveCfg = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release|Any CPU.Build.0 = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release|x86.ActiveCfg = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release|x86.Build.0 = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug Portable|Any CPU.ActiveCfg = Debug|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug Portable|Any CPU.Build.0 = Debug|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug Portable|x86.ActiveCfg = Debug|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug|Any CPU.ActiveCfg = Debug|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug|x86.ActiveCfg = Debug|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Debug|x86.Build.0 = Debug|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Installer|Any CPU.ActiveCfg = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Installer|Any CPU.Build.0 = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Installer|x86.ActiveCfg = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Installer|x86.Build.0 = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Portable|Any CPU.ActiveCfg = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release Portable|x86.ActiveCfg = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release|Any CPU.ActiveCfg = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release|Any CPU.Build.0 = Release|x86
{5423D985-CB48-4344-B47F-E8C6D60C8B04}.Release|x86.ActiveCfg = Release|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Debug Portable|Any CPU.ActiveCfg = Debug Portable|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Debug Portable|Any CPU.Build.0 = Debug Portable|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Debug Portable|x86.ActiveCfg = Debug Portable|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Debug|Any CPU.ActiveCfg = Debug|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Debug|x86.ActiveCfg = Debug|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Installer|Any CPU.ActiveCfg = Release|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Installer|Any CPU.Build.0 = Release|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Installer|x86.ActiveCfg = Release|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Installer|x86.Build.0 = Release|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Portable|Any CPU.ActiveCfg = Release Portable|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release Portable|x86.ActiveCfg = Release Portable|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release|Any CPU.ActiveCfg = Release|x86
{F0168B9F-6815-40DF-BA53-46CEE7683B68}.Release|x86.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug Portable|Any CPU.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug Portable|Any CPU.Build.0 = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug Portable|x86.ActiveCfg = Debug|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug Portable|x86.Build.0 = Debug|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug|Any CPU.ActiveCfg = Debug|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug|x86.ActiveCfg = Debug|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug|x86.Build.0 = Debug|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|Any CPU.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|Any CPU.Build.0 = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|x86.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|x86.Build.0 = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Portable|Any CPU.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Portable|Any CPU.Build.0 = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Portable|x86.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Portable|x86.Build.0 = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release|Any CPU.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release|x86.ActiveCfg = Release|x86
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -7,7 +7,6 @@ using mRemoteNG.Messages;
using mRemoteNG.Properties;
using mRemoteNG.UI.Forms;
using mRemoteNG.UI.TaskDialog;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.App
{
@@ -37,9 +36,14 @@ namespace mRemoteNG.App
messageCollector.AddMessage(MessageClass.ErrorMsg, errorText, true);
//About to pop up a message, let's not block it...
FrmSplashScreenNew.GetInstance().Close();
FrmSplashScreen.getInstance().Close();
var ShouldIStayOrShouldIGo = CTaskDialog.MessageBox(Application.ProductName, Language.CompatibilityProblemDetected, errorText, "", "", Language.CheckboxDoNotShowThisMessageAgain, ETaskDialogButtons.OkCancel, ESysIcons.Warning, ESysIcons.Warning);
var ShouldIStayOrShouldIGo = CTaskDialog.MessageBox(Application.ProductName,
Language.CompatibilityProblemDetected, errorText, "",
"",
Language.CheckboxDoNotShowThisMessageAgain,
ETaskDialogButtons.OkCancel, ESysIcons.Warning,
ESysIcons.Warning);
if (CTaskDialog.VerificationChecked && ShouldIStayOrShouldIGo == DialogResult.OK)
{
messageCollector.AddMessage(MessageClass.ErrorMsg, "User requests that FIPS check be overridden", true);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Windows.Forms;
using mRemoteNG.Config.Connections;
@@ -19,7 +19,7 @@ namespace mRemoteNG.App
{
public static class Export
{
public static void ExportToFile(ConnectionInfo selectedNode, ConnectionTreeModel connectionTreeModel)
public static void ExportToFile(ConnectionInfo selectedNode, IConnectionTreeModel connectionTreeModel)
{
try
{
@@ -93,8 +93,7 @@ namespace mRemoteNG.App
serializer = new XmlConnectionsSerializer(cryptographyProvider, connectionNodeSerializer);
break;
case SaveFormat.mRCSV:
serializer =
new CsvConnectionsSerializerMremotengFormat(saveFilter, Runtime.CredentialProviderCatalog);
serializer = new CsvConnectionsSerializerMremotengFormat(saveFilter, Runtime.CredentialService.RepositoryList);
break;
default:
throw new ArgumentOutOfRangeException(nameof(saveFormat), saveFormat, null);

View File

@@ -7,11 +7,10 @@ using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.App
{
public static class Import
public static class Import
{
public static void ImportFromFile(ContainerInfo importDestinationContainer)
{
@@ -38,8 +37,8 @@ namespace mRemoteNG.App
return;
HeadlessFileImport(
openFileDialog.FileNames,
importDestinationContainer,
openFileDialog.FileNames,
importDestinationContainer,
Runtime.ConnectionsService,
fileName => MessageBox.Show(string.Format(Language.ImportFileFailedContent, fileName), Language.AskUpdatesMainInstruction,
MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1));
@@ -51,40 +50,9 @@ namespace mRemoteNG.App
}
}
public static void ImportFromRemoteDesktopManagerCsv(ContainerInfo importDestinationContainer)
{
try
{
using (Runtime.ConnectionsService.BatchedSavingContext())
{
using (var openFileDialog = new OpenFileDialog())
{
openFileDialog.CheckFileExists = true;
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
openFileDialog.Multiselect = false;
var fileTypes = new List<string>();
fileTypes.AddRange(new[] {Language.FiltermRemoteRemoteDesktopManagerCSV, "*.csv"});
openFileDialog.Filter = string.Join("|", fileTypes.ToArray());
if (openFileDialog.ShowDialog() != DialogResult.OK)
return;
var importer = new RemoteDesktopManagerImporter();
importer.Import(openFileDialog.FileName, importDestinationContainer);
}
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionMessage("App.Import.ImportFromRemoteDesktopManagerCsv() failed.", ex);
}
}
public static void HeadlessFileImport(
IEnumerable<string> filePaths,
ContainerInfo importDestinationContainer,
IEnumerable<string> filePaths,
ContainerInfo importDestinationContainer,
ConnectionsService connectionsService,
Action<string> exceptionAction = null)
{

View File

@@ -1,12 +1,10 @@
using System;
namespace mRemoteNG.App.Info
namespace mRemoteNG.App.Info
{
public static class ConnectionsFileInfo
{
public static readonly string DefaultConnectionsPath = SettingsFileInfo.SettingsPath;
public static readonly string DefaultConnectionsFile = "confCons.xml";
public static readonly string DefaultConnectionsFileNew = "confConsNew.xml";
public static readonly Version ConnectionFileVersion = new Version(2, 9);
public static readonly double ConnectionFileVersion = 2.8;
}
}

View File

@@ -8,9 +8,13 @@ namespace mRemoteNG.App.Info
{
public static class SettingsFileInfo
{
private static readonly string ExePath = Path.GetDirectoryName(Assembly.GetAssembly(typeof(ConnectionInfo))?.Location);
private static readonly string ExePath =
Path.GetDirectoryName(Assembly.GetAssembly(typeof(ConnectionInfo))?.Location);
public static string SettingsPath => Runtime.IsPortableEdition ? ExePath : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + Application.ProductName;
public static string SettingsPath =>
Runtime.IsPortableEdition
? ExePath
: Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + Application.ProductName;
public static string LayoutFileName { get; } = "pnlLayout.xml";
public static string ExtAppsFilesName { get; } = "extApps.xml";

View File

@@ -22,7 +22,7 @@ namespace mRemoteNG.App.Info
public static Uri GetUpdateChannelInfo()
{
var channel = IsValidChannel(Properties.OptionsUpdatesPage.Default.UpdateChannel) ? Properties.OptionsUpdatesPage.Default.UpdateChannel : STABLE;
var channel = IsValidChannel(Settings.Default.UpdateChannel) ? Settings.Default.UpdateChannel : STABLE;
return GetUpdateTxtUri(channel);
}
@@ -65,7 +65,7 @@ namespace mRemoteNG.App.Info
private static Uri GetUpdateTxtUri(string channel)
{
return new Uri(new Uri(Properties.OptionsUpdatesPage.Default.UpdateAddress),
return new Uri(new Uri(Settings.Default.UpdateAddress),
new Uri(GetChannelFileName(channel), UriKind.Relative));
}

View File

@@ -1,19 +1,38 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
using mRemoteNG.Config.Connections;
using mRemoteNG.Connection;
using mRemoteNG.Credential;
using mRemoteNG.Tools;
using mRemoteNG.Properties;
namespace mRemoteNG.App.Initialization
{
public class CredsAndConsSetup
{
public void LoadCredsAndCons()
public void LoadCredsAndCons(ConnectionsService connectionsService, CredentialService credentialService, SaveConnectionsOnEdit connectionsOnEdit)
{
new SaveConnectionsOnEdit(Runtime.ConnectionsService);
connectionsOnEdit.Subscribe(connectionsService);
if (Properties.App.Default.FirstStart && !Properties.OptionsBackupPage.Default.LoadConsFromCustomLocation && !File.Exists(Runtime.ConnectionsService.GetStartupConnectionFileName()))
Runtime.ConnectionsService.NewConnectionsFile(Runtime.ConnectionsService.GetStartupConnectionFileName());
if (Settings.Default.FirstStart && !Settings.Default.LoadConsFromCustomLocation &&
!File.Exists(connectionsService.GetStartupConnectionFileName()))
connectionsService.NewConnectionsFile(connectionsService.GetStartupConnectionFileName());
credentialService.LoadRepositoryList();
LoadDefaultConnectionCredentials(credentialService);
Runtime.LoadConnections();
}
private void LoadDefaultConnectionCredentials(CredentialService credentialService)
{
var defaultCredId = Settings.Default.ConDefaultCredentialRecord;
var matchedCredentials = credentialService
.GetCredentialRecords()
.Where(record => record.Id.Equals(defaultCredId))
.ToArray();
DefaultConnectionInfo.Instance.CredentialRecordId = Optional<Guid>.FromNullable(matchedCredentials.FirstOrDefault()?.Id);
}
}
}

View File

@@ -3,7 +3,6 @@ using System.Management;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.App.Initialization
{

View File

@@ -1,6 +1,5 @@
using System;
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using log4net;
using log4net.Appender;
@@ -27,16 +26,15 @@ namespace mRemoteNG.App
private void Initialize()
{
XmlConfigurator.Configure(LogManager.CreateRepository("mRemoteNG"));
if (string.IsNullOrEmpty(Properties.OptionsNotificationsPage.Default.LogFilePath))
Properties.OptionsNotificationsPage.Default.LogFilePath = BuildLogFilePath();
if (string.IsNullOrEmpty(Settings.Default.LogFilePath))
Settings.Default.LogFilePath = BuildLogFilePath();
SetLogPath(Properties.OptionsNotificationsPage.Default.LogToApplicationDirectory ? DefaultLogPath : Properties.OptionsNotificationsPage.Default.LogFilePath);
SetLogPath(Settings.Default.LogToApplicationDirectory ? DefaultLogPath : Settings.Default.LogFilePath);
}
public void SetLogPath(string path)
{
var repository = LogManager.GetRepository("mRemoteNG");
XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));
var appenders = repository.GetAppenders();
foreach (var appender in appenders)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Drawing;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
@@ -71,12 +71,6 @@ namespace mRemoteNG.App
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
internal static extern bool ChangeClipboardChain(
IntPtr hWndRemove, // handle to window to remove
IntPtr hWndNewNext // handle to next window
);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
@@ -112,6 +106,7 @@ namespace mRemoteNG.App
internal static extern bool SetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("kernel32", SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern bool CloseHandle(IntPtr handle);
#endregion

View File

@@ -1,8 +1,6 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using mRemoteNG.Properties;
using mRemoteNG.UI.Forms;
@@ -19,7 +17,7 @@ namespace mRemoteNG.App
[STAThread]
public static void Main(string[] args)
{
if (Properties.OptionsStartupExitPage.Default.SingleInstance)
if (Settings.Default.SingleInstance)
StartApplicationAsSingleInstance();
else
StartApplication();
@@ -28,23 +26,11 @@ namespace mRemoteNG.App
private static void StartApplication()
{
CatchAllUnhandledExceptions();
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
var frmSplashScreen = FrmSplashScreenNew.GetInstance();
Screen targetScreen = (Screen.AllScreens.Length > 1) ? Screen.AllScreens[1] : Screen.AllScreens[0];
Rectangle viewport = targetScreen.WorkingArea;
frmSplashScreen.Top = viewport.Top;
frmSplashScreen.Left = viewport.Left;
// normaly it should be screens[1] however due DPI apply 1 size "same" as default with 100%
frmSplashScreen.Left = viewport.Left + (targetScreen.Bounds.Size.Width / 2) - (frmSplashScreen.Width / 2);
frmSplashScreen.Top = viewport.Top + (targetScreen.Bounds.Size.Height / 2) - (frmSplashScreen.Height / 2);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var frmSplashScreen = FrmSplashScreen.getInstance();
frmSplashScreen.Show();
System.Windows.Forms.Application.Run(FrmMain.Default);
Application.Run(FrmMain.Default);
}
public static void CloseSingletonInstanceMutex()
@@ -92,15 +78,15 @@ namespace mRemoteNG.App
private static void CatchAllUnhandledExceptions()
{
System.Windows.Forms.Application.ThreadException += ApplicationOnThreadException;
System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += ApplicationOnThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
}
private static void ApplicationOnThreadException(object sender, ThreadExceptionEventArgs e)
{
// if (PresentationSource.FromVisual(FrmSplashScreenNew))
FrmSplashScreenNew.GetInstance().Close();
if (!FrmSplashScreen.getInstance().IsDisposed)
FrmSplashScreen.getInstance().Close();
if (FrmMain.Default.IsDisposed) return;
@@ -111,9 +97,8 @@ namespace mRemoteNG.App
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
//TODO: Check if splash closed properly
//if (!FrmSplashScreenNew.GetInstance().IsDisposed)
// FrmSplashScreenNew.GetInstance().Close();
if (!FrmSplashScreen.getInstance().IsDisposed)
FrmSplashScreen.getInstance().Close();
var window = new FrmUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
window.ShowDialog(FrmMain.Default);

View File

@@ -2,7 +2,6 @@
using mRemoteNG.Config.Putty;
using mRemoteNG.Connection;
using mRemoteNG.Credential;
using mRemoteNG.Credential.Repositories;
using mRemoteNG.Messages;
using mRemoteNG.Security;
using mRemoteNG.Tools;
@@ -16,7 +15,6 @@ using System.Security;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.App
{
@@ -37,7 +35,7 @@ namespace mRemoteNG.App
/// <summary>
/// Feature flag to enable the credential manager feature
/// </summary>
public static bool UseCredentialManager => false;
public static bool UseCredentialManager => true;
public static WindowList WindowList { get; set; }
public static MessageCollector MessageCollector { get; } = new MessageCollector();
@@ -47,12 +45,9 @@ namespace mRemoteNG.App
public static SecureString EncryptionKey { get; set; } =
new RootNodeInfo(RootNodeType.Connection).PasswordString.ConvertToSecureString();
public static ICredentialRepositoryList CredentialProviderCatalog { get; } = new CredentialRepositoryList();
public static CredentialService CredentialService { get; } = new CredentialServiceFactory().Build();
public static ConnectionInitiator ConnectionInitiator { get; set; } = new ConnectionInitiator();
public static ConnectionsService ConnectionsService { get; } =
new ConnectionsService(PuttySessionsManager.Instance);
public static ConnectionsService ConnectionsService { get; } = new ConnectionsService(PuttySessionsManager.Instance, CredentialService);
#region Connections Loading/Saving
@@ -91,23 +86,19 @@ namespace mRemoteNG.App
return;
connectionFileName = loadDialog.FileName;
Properties.OptionsDBsPage.Default.UseSQLServer = false;
Properties.OptionsDBsPage.Default.Save();
Settings.Default.UseSQLServer = false;
Settings.Default.Save();
}
else if (!Properties.OptionsDBsPage.Default.UseSQLServer)
else if (!Settings.Default.UseSQLServer)
{
connectionFileName = ConnectionsService.GetStartupConnectionFileName();
}
ConnectionsService.LoadConnections(Properties.OptionsDBsPage.Default.UseSQLServer, false, connectionFileName);
ConnectionsService.LoadConnections(Settings.Default.UseSQLServer, connectionFileName);
if (Properties.OptionsDBsPage.Default.UseSQLServer)
if (Settings.Default.UseSQLServer)
{
ConnectionsService.LastSqlUpdate = DateTime.Now;
}
else
{
ConnectionsService.LastFileUpdate = System.IO.File.GetLastWriteTime(connectionFileName);
}
// re-enable sql update checking after updates are loaded
@@ -115,20 +106,26 @@ namespace mRemoteNG.App
}
catch (Exception ex)
{
FrmSplashScreenNew.GetInstance().Close();
FrmSplashScreen.getInstance().Close();
if (Properties.OptionsDBsPage.Default.UseSQLServer)
if (Settings.Default.UseSQLServer)
{
MessageCollector.AddExceptionMessage(Language.LoadFromSqlFailed, ex);
var commandButtons = string.Join("|", Language._TryAgain, Language.CommandOpenConnectionFile, string.Format(Language.CommandExitProgram, Application.ProductName));
CTaskDialog.ShowCommandBox(Application.ProductName, Language.LoadFromSqlFailed, Language.LoadFromSqlFailedContent, MiscTools.GetExceptionMessageRecursive(ex), "", "", commandButtons, false, ESysIcons.Error, ESysIcons.Error);
var commandButtons = string.Join("|", Language._TryAgain,
Language.CommandOpenConnectionFile,
string.Format(Language.CommandExitProgram,
Application.ProductName));
CTaskDialog.ShowCommandBox(Application.ProductName, Language.LoadFromSqlFailed,
Language.LoadFromSqlFailedContent,
MiscTools.GetExceptionMessageRecursive(ex), "", "",
commandButtons, false, ESysIcons.Error, ESysIcons.Error);
switch (CTaskDialog.CommandButtonResult)
{
case 0:
LoadConnections(withDialog);
return;
case 1:
Properties.OptionsDBsPage.Default.UseSQLServer = false;
Settings.Default.UseSQLServer = false;
LoadConnections(true);
return;
default:
@@ -157,7 +154,14 @@ namespace mRemoteNG.App
{
try
{
CTaskDialog.ShowTaskDialogBox(GeneralAppInfo.ProductName, Language.ConnectionFileNotFound, "", "", "", "", "", string.Join(" | ", commandButtons), ETaskDialogButtons.None, ESysIcons.Question, ESysIcons.Question);
CTaskDialog.ShowTaskDialogBox(
GeneralAppInfo.ProductName,
Language.ConnectionFileNotFound,
"", "", "", "", "",
string.Join(" | ", commandButtons),
ETaskDialogButtons.None,
ESysIcons.Question,
ESysIcons.Question);
switch (CTaskDialog.CommandButtonResult)
{
@@ -182,7 +186,11 @@ namespace mRemoteNG.App
}
catch (Exception exc)
{
MessageCollector.AddExceptionMessage(string.Format(Language.ConnectionsFileCouldNotBeLoadedNew, connectionFileName), exc, MessageClass.InformationMsg);
MessageCollector.AddExceptionMessage(
string
.Format(Language.ConnectionsFileCouldNotBeLoadedNew,
connectionFileName), exc,
MessageClass.InformationMsg);
}
}
@@ -198,7 +206,12 @@ namespace mRemoteNG.App
}
else
{
MessageBox.Show(FrmMain.Default, string.Format(Language.ErrorStartupConnectionFileLoad, Environment.NewLine, Application.ProductName, ConnectionsService.GetStartupConnectionFileName(), MiscTools.GetExceptionMessageRecursive(ex)), @"Could not load startup file.", MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show(FrmMain.Default,
string.Format(Language.ErrorStartupConnectionFileLoad, Environment.NewLine,
Application.ProductName,
ConnectionsService.GetStartupConnectionFileName(),
MiscTools.GetExceptionMessageRecursive(ex)),
@"Could not load startup file.", MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
}

View File

@@ -2,12 +2,10 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
using mRemoteNG.Config.Connections;
using mRemoteNG.Config.Putty;
using mRemoteNG.Properties;
using mRemoteNG.UI.Controls;
using mRemoteNG.UI.Forms;
using mRemoteNG.Resources.Language;
// ReSharper disable ArrangeAccessorOwnerBody
@@ -61,34 +59,8 @@ namespace mRemoteNG.App
private static void SaveConnections()
{
DateTime lastUpdate;
DateTime updateDate;
DateTime currentDate = DateTime.Now;
//OBSOLETE: Settings.Default.SaveConsOnExit is obsolete and should be removed in a future release
if (Properties.OptionsStartupExitPage.Default.SaveConnectionsOnExit || (Properties.OptionsBackupPage.Default.SaveConnectionsFrequency == (int)ConnectionsBackupFrequencyEnum.OnExit))
{
if (Settings.Default.SaveConsOnExit)
Runtime.ConnectionsService.SaveConnections();
return;
}
lastUpdate = Runtime.ConnectionsService.UsingDatabase ? Runtime.ConnectionsService.LastSqlUpdate : Runtime.ConnectionsService.LastFileUpdate;
switch (Properties.OptionsBackupPage.Default.SaveConnectionsFrequency)
{
case (int)ConnectionsBackupFrequencyEnum.Daily:
updateDate = lastUpdate.AddDays(1);
break;
case (int)ConnectionsBackupFrequencyEnum.Weekly:
updateDate = lastUpdate.AddDays(7);
break;
default:
return;
}
if (currentDate >= updateDate)
{
Runtime.ConnectionsService.SaveConnections();
}
}
private static void SaveSettings(Control quickConnectToolStrip,
@@ -120,7 +92,7 @@ namespace mRemoteNG.App
private static void RunUpdateFile()
{
if (UpdatePending)
Process.Start(new ProcessStartInfo(_updateFilePath) { UseShellExecute = true });
Process.Start(_updateFilePath);
}
}
}

View File

@@ -2,7 +2,6 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using mRemoteNG.App.Info;
using mRemoteNG.App.Initialization;
using mRemoteNG.App.Update;
@@ -38,7 +37,8 @@ namespace mRemoteNG.App
public void InitializeProgram(MessageCollector messageCollector)
{
Debug.Print("---------------------------" + Environment.NewLine + "[START] - " + Convert.ToString(DateTime.Now, CultureInfo.InvariantCulture));
Debug.Print("---------------------------" + Environment.NewLine + "[START] - " +
Convert.ToString(DateTime.Now, CultureInfo.InvariantCulture));
var startupLogger = new StartupDataLogger(messageCollector);
startupLogger.LogStartupData();
CompatibilityChecker.CheckCompatibility(messageCollector);
@@ -58,13 +58,14 @@ namespace mRemoteNG.App
public void CreateConnectionsProvider(MessageCollector messageCollector)
{
messageCollector.AddMessage(MessageClass.DebugMsg, "Determining if we need a database syncronizer");
if (!Properties.OptionsDBsPage.Default.UseSQLServer) return;
if (!Settings.Default.UseSQLServer) return;
messageCollector.AddMessage(MessageClass.DebugMsg, "Creating database syncronizer");
Runtime.ConnectionsService.RemoteConnectionsSyncronizer = new RemoteConnectionsSyncronizer(new SqlConnectionsUpdateChecker());
Runtime.ConnectionsService.RemoteConnectionsSyncronizer =
new RemoteConnectionsSyncronizer(new SqlConnectionsUpdateChecker());
Runtime.ConnectionsService.RemoteConnectionsSyncronizer.Enable();
}
public async Task CheckForUpdate()
public void CheckForUpdate()
{
if (_appUpdate == null)
{
@@ -76,15 +77,42 @@ namespace mRemoteNG.App
}
var nextUpdateCheck =
Convert.ToDateTime(Properties.OptionsUpdatesPage.Default.CheckForUpdatesLastCheck.Add(TimeSpan.FromDays(Convert.ToDouble(Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays))));
if (!Properties.OptionsUpdatesPage.Default.UpdatePending && DateTime.UtcNow < nextUpdateCheck)
Convert.ToDateTime(Settings.Default.CheckForUpdatesLastCheck.Add(
TimeSpan
.FromDays(Convert.ToDouble(Settings
.Default
.CheckForUpdatesFrequencyDays))));
if (!Settings.Default.UpdatePending && DateTime.UtcNow < nextUpdateCheck)
{
return;
}
_appUpdate.GetUpdateInfoCompletedEvent += GetUpdateInfoCompleted;
_appUpdate.GetUpdateInfoAsync();
}
private void GetUpdateInfoCompleted(object sender, AsyncCompletedEventArgs e)
{
if (_frmMain.InvokeRequired)
{
_frmMain.Invoke(new AsyncCompletedEventHandler(GetUpdateInfoCompleted), sender, e);
return;
}
try
{
await _appUpdate.GetUpdateInfoAsync();
_appUpdate.GetUpdateInfoCompletedEvent -= GetUpdateInfoCompleted;
if (e.Cancelled)
{
return;
}
if (e.Error != null)
{
throw e.Error;
}
if (_appUpdate.IsUpdateAvailable())
{
Windows.Show(WindowType.Update);
@@ -92,7 +120,7 @@ namespace mRemoteNG.App
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionMessage("CheckForUpdate() failed.", ex);
Runtime.MessageCollector.AddExceptionMessage("GetUpdateInfoCompleted() failed.", ex);
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
@@ -22,7 +22,7 @@ namespace mRemoteNG.App
private SupportedCultures()
{
foreach (var CultureName in Properties.AppUI.Default.SupportedUICultures.Split(','))
foreach (var CultureName in Settings.Default.SupportedUICultures.Split(','))
{
try
{

View File

@@ -1,12 +1,12 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.ComponentModel;
using System.Threading;
using System.Reflection;
using mRemoteNG.App.Info;
using mRemoteNG.Security.SymmetricEncryption;
using System.Security.Cryptography;
using System.Threading.Tasks;
using mRemoteNG.Properties;
#if !PORTABLE
using mRemoteNG.Tools;
@@ -21,30 +21,29 @@ namespace mRemoteNG.App.Update
{
public class AppUpdater
{
private const int _bufferLength = 8192;
private WebProxy _webProxy;
private HttpClient _httpClient;
private CancellationTokenSource _changeLogCancelToken;
private CancellationTokenSource _getUpdateInfoCancelToken;
private Thread _getUpdateInfoThread;
private Thread _getChangeLogThread;
#region Public Properties
public UpdateInfo CurrentUpdateInfo { get; private set; }
public string ChangeLog { get; private set; }
public bool IsGetUpdateInfoRunning
{
get
{
return _getUpdateInfoCancelToken != null;
}
get { return _getUpdateInfoThread != null && _getUpdateInfoThread.IsAlive; }
}
private bool IsGetChangeLogRunning
{
get
{
return _changeLogCancelToken != null;
}
get { return _getChangeLogThread != null && _getChangeLogThread.IsAlive; }
}
public bool IsDownloadUpdateRunning
{
get { return _downloadUpdateWebClient != null; }
}
#endregion
@@ -53,35 +52,39 @@ namespace mRemoteNG.App.Update
public AppUpdater()
{
SetDefaultProxySettings();
SetProxySettings();
}
private void SetDefaultProxySettings()
private void SetProxySettings()
{
var shouldWeUseProxy = Properties.OptionsUpdatesPage.Default.UpdateUseProxy;
var proxyAddress = Properties.OptionsUpdatesPage.Default.UpdateProxyAddress;
var port = Properties.OptionsUpdatesPage.Default.UpdateProxyPort;
var useAuthentication = Properties.OptionsUpdatesPage.Default.UpdateProxyUseAuthentication;
var username = Properties.OptionsUpdatesPage.Default.UpdateProxyAuthUser;
var shouldWeUseProxy = Settings.Default.UpdateUseProxy;
var proxyAddress = Settings.Default.UpdateProxyAddress;
var port = Settings.Default.UpdateProxyPort;
var useAuthentication = Settings.Default.UpdateProxyUseAuthentication;
var username = Settings.Default.UpdateProxyAuthUser;
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
var password = cryptographyProvider.Decrypt(Properties.OptionsUpdatesPage.Default.UpdateProxyAuthPass, Runtime.EncryptionKey);
var password = cryptographyProvider.Decrypt(Settings.Default.UpdateProxyAuthPass, Runtime.EncryptionKey);
SetProxySettings(shouldWeUseProxy, proxyAddress, port, useAuthentication, username, password);
}
public void SetProxySettings(bool useProxy, string address, int port, bool useAuthentication, string username, string password)
public void SetProxySettings(bool useProxy,
string address,
int port,
bool useAuthentication,
string username,
string password)
{
if (useProxy && !string.IsNullOrEmpty(address))
{
_webProxy = port != 0 ? new WebProxy(address, port) : new WebProxy(address);
_webProxy.Credentials = useAuthentication ? new NetworkCredential(username, password) : null;
}
else
{
_webProxy = null;
}
UpdateHttpClient();
}
public bool IsUpdateAvailable()
@@ -93,24 +96,54 @@ namespace mRemoteNG.App.Update
return CurrentUpdateInfo.Version > GeneralAppInfo.GetApplicationVersion();
}
public async Task DownloadUpdateAsync(IProgress<int> progress)
public void GetUpdateInfoAsync()
{
if (IsGetUpdateInfoRunning)
{
_getUpdateInfoCancelToken.Cancel();
_getUpdateInfoCancelToken.Dispose();
_getUpdateInfoCancelToken = null;
_getUpdateInfoThread.Abort();
}
_getUpdateInfoThread = new Thread(GetUpdateInfo);
_getUpdateInfoThread.SetApartmentState(ApartmentState.STA);
_getUpdateInfoThread.IsBackground = true;
_getUpdateInfoThread.Start();
}
public void GetChangeLogAsync()
{
if (CurrentUpdateInfo == null || !CurrentUpdateInfo.IsValid)
{
throw new InvalidOperationException(
"CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling GetChangeLogAsync().");
}
if (IsGetChangeLogRunning)
{
_getChangeLogThread.Abort();
}
_getChangeLogThread = new Thread(GetChangeLog);
_getChangeLogThread.SetApartmentState(ApartmentState.STA);
_getChangeLogThread.IsBackground = true;
_getChangeLogThread.Start();
}
public void DownloadUpdateAsync()
{
if (_downloadUpdateWebClient != null)
{
throw new InvalidOperationException("A previous call to DownloadUpdateAsync() is still in progress.");
}
if (CurrentUpdateInfo == null || !CurrentUpdateInfo.IsValid)
{
throw new InvalidOperationException("CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling DownloadUpdateAsync().");
throw new InvalidOperationException(
"CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling DownloadUpdateAsync().");
}
#if !PORTABLE
CurrentUpdateInfo.UpdateFilePath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetRandomFileName(), "msi"));
CurrentUpdateInfo.UpdateFilePath =
Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetRandomFileName(), "msi"));
#else
var sfd = new SaveFileDialog
{
@@ -127,38 +160,135 @@ namespace mRemoteNG.App.Update
return;
}
#endif
try
DownloadUpdateWebClient.DownloadFileAsync(CurrentUpdateInfo.DownloadAddress,
CurrentUpdateInfo.UpdateFilePath);
}
#endregion
#region Private Properties
private WebClient _downloadUpdateWebClient;
private WebClient DownloadUpdateWebClient
{
get
{
_getUpdateInfoCancelToken = new CancellationTokenSource();
using var response = await _httpClient.GetAsync(CurrentUpdateInfo.DownloadAddress, HttpCompletionOption.ResponseHeadersRead, _getUpdateInfoCancelToken.Token);
var buffer = new byte[_bufferLength];
var totalBytes = response.Content.Headers.ContentLength ?? 0;
var readBytes = 0L;
await using (var httpStream = await response.Content.ReadAsStreamAsync(_getUpdateInfoCancelToken.Token))
if (_downloadUpdateWebClient != null)
{
await using var fileStream = new FileStream(CurrentUpdateInfo.UpdateFilePath, FileMode.Create,
FileAccess.Write, FileShare.None, _bufferLength, true);
while (readBytes <= totalBytes || !_getUpdateInfoCancelToken.IsCancellationRequested)
{
var bytesRead =
await httpStream.ReadAsync(buffer, 0, _bufferLength, _getUpdateInfoCancelToken.Token);
if (bytesRead == 0)
{
progress.Report(100);
break;
}
await fileStream.WriteAsync(buffer, 0, bytesRead, _getUpdateInfoCancelToken.Token);
readBytes += bytesRead;
var percentComplete = (int)(readBytes * 100 / totalBytes);
progress.Report(percentComplete);
}
return _downloadUpdateWebClient;
}
_downloadUpdateWebClient = CreateWebClient();
_downloadUpdateWebClient.DownloadProgressChanged += DownloadUpdateProgressChanged;
_downloadUpdateWebClient.DownloadFileCompleted += DownloadUpdateCompleted;
return _downloadUpdateWebClient;
}
}
#endregion
#region Private Methods
private WebClient CreateWebClient()
{
var webClient = new WebClient();
webClient.Headers.Add("user-agent", GeneralAppInfo.UserAgent);
webClient.Proxy = _webProxy;
return webClient;
}
private static DownloadStringCompletedEventArgs NewDownloadStringCompletedEventArgs(string result,
Exception exception,
bool cancelled,
object userToken)
{
var type = typeof(DownloadStringCompletedEventArgs);
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
Type[] argumentTypes = {typeof(string), typeof(Exception), typeof(bool), typeof(object)};
var constructor = type.GetConstructor(bindingFlags, null, argumentTypes, null);
object[] arguments = {result, exception, cancelled, userToken};
if (constructor == null)
return null;
return (DownloadStringCompletedEventArgs)constructor.Invoke(arguments);
}
private DownloadStringCompletedEventArgs DownloadString(Uri address)
{
var webClient = CreateWebClient();
var result = string.Empty;
Exception exception = null;
var cancelled = false;
try
{
result = webClient.DownloadString(address);
}
catch (ThreadAbortException)
{
cancelled = true;
}
catch (Exception ex)
{
exception = ex;
}
return NewDownloadStringCompletedEventArgs(result, exception, cancelled, null);
}
private void GetUpdateInfo()
{
var e = DownloadString(UpdateChannelInfo.GetUpdateChannelInfo());
if (!e.Cancelled && e.Error == null)
{
try
{
CurrentUpdateInfo = UpdateInfo.FromString(e.Result);
Settings.Default.CheckForUpdatesLastCheck = DateTime.UtcNow;
if (!Settings.Default.UpdatePending)
{
Settings.Default.UpdatePending = IsUpdateAvailable();
}
}
catch (Exception ex)
{
e = NewDownloadStringCompletedEventArgs(e.Result, ex, e.Cancelled, null);
}
}
GetUpdateInfoCompletedEventEvent?.Invoke(this, e);
}
private void GetChangeLog()
{
var e = DownloadString(CurrentUpdateInfo.ChangeLogAddress);
if (!e.Cancelled && e.Error == null)
{
ChangeLog = e.Result;
}
GetChangeLogCompletedEventEvent?.Invoke(this, e);
}
private void DownloadUpdateProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
DownloadUpdateProgressChangedEventEvent?.Invoke(sender, e);
}
private void DownloadUpdateCompleted(object sender, AsyncCompletedEventArgs e)
{
var raiseEventArgs = e;
if (!e.Cancelled && e.Error == null)
{
try
{
#if !PORTABLE
var updateAuthenticode = new Authenticode(CurrentUpdateInfo.UpdateFilePath)
{
@@ -177,85 +307,101 @@ namespace mRemoteNG.App.Update
}
#endif
using var checksum = SHA512.Create();
await using var stream = File.OpenRead(CurrentUpdateInfo.UpdateFilePath);
var hash = await checksum.ComputeHashAsync(stream);
var hashString = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
if (!hashString.Equals(CurrentUpdateInfo.Checksum))
throw new Exception("SHA512 Hashes didn't match!");
} finally{
_getUpdateInfoCancelToken?.Dispose();
_getUpdateInfoCancelToken = null;
using (var cksum = SHA512.Create())
{
using (var stream = File.OpenRead(CurrentUpdateInfo.UpdateFilePath))
{
var hash = cksum.ComputeHash(stream);
var hashString = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
if (!hashString.Equals(CurrentUpdateInfo.Checksum))
throw new Exception("SHA512 Hashes didn't match!");
}
}
}
catch (Exception ex)
{
raiseEventArgs = new AsyncCompletedEventArgs(ex, false, null);
}
}
if (raiseEventArgs.Cancelled || raiseEventArgs.Error != null)
{
File.Delete(CurrentUpdateInfo.UpdateFilePath);
}
DownloadUpdateCompletedEventEvent?.Invoke(this, raiseEventArgs);
_downloadUpdateWebClient.Dispose();
_downloadUpdateWebClient = null;
}
#endregion
#region Private Methods
#region Events
private void UpdateHttpClient()
private AsyncCompletedEventHandler GetUpdateInfoCompletedEventEvent;
public event AsyncCompletedEventHandler GetUpdateInfoCompletedEvent
{
if (_httpClient != null)
add
{
_httpClient.Dispose();
GetUpdateInfoCompletedEventEvent =
(AsyncCompletedEventHandler)Delegate.Combine(GetUpdateInfoCompletedEventEvent, value);
}
var httpClientHandler = new HttpClientHandler();
if (_webProxy != null)
remove
{
httpClientHandler.UseProxy = true;
httpClientHandler.Proxy = _webProxy;
}
_httpClient = new HttpClient(httpClientHandler);
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(GeneralAppInfo.UserAgent);
}
public async Task GetUpdateInfoAsync()
{
if (IsGetUpdateInfoRunning)
{
_getUpdateInfoCancelToken.Cancel();
_getUpdateInfoCancelToken.Dispose();
_getUpdateInfoCancelToken = null;
}
try
{
_getUpdateInfoCancelToken = new CancellationTokenSource();
var updateInfo = await _httpClient.GetStringAsync(UpdateChannelInfo.GetUpdateChannelInfo(), _getUpdateInfoCancelToken.Token);
CurrentUpdateInfo = UpdateInfo.FromString(updateInfo);
Properties.OptionsUpdatesPage.Default.CheckForUpdatesLastCheck = DateTime.UtcNow;
if (!Properties.OptionsUpdatesPage.Default.UpdatePending)
{
Properties.OptionsUpdatesPage.Default.UpdatePending = IsUpdateAvailable();
}
}
finally
{
_getUpdateInfoCancelToken?.Dispose();
_getUpdateInfoCancelToken = null;
GetUpdateInfoCompletedEventEvent =
(AsyncCompletedEventHandler)Delegate.Remove(GetUpdateInfoCompletedEventEvent, value);
}
}
public async Task<string> GetChangeLogAsync()
{
if (IsGetChangeLogRunning)
{
_changeLogCancelToken.Cancel();
_changeLogCancelToken.Dispose();
_changeLogCancelToken = null;
}
private AsyncCompletedEventHandler GetChangeLogCompletedEventEvent;
try
public event AsyncCompletedEventHandler GetChangeLogCompletedEvent
{
add
{
_changeLogCancelToken = new CancellationTokenSource();
return await _httpClient.GetStringAsync(CurrentUpdateInfo.ChangeLogAddress, _changeLogCancelToken.Token);
GetChangeLogCompletedEventEvent =
(AsyncCompletedEventHandler)Delegate.Combine(GetChangeLogCompletedEventEvent, value);
}
finally
remove
{
_changeLogCancelToken?.Dispose();
_changeLogCancelToken = null;
GetChangeLogCompletedEventEvent =
(AsyncCompletedEventHandler)Delegate.Remove(GetChangeLogCompletedEventEvent, value);
}
}
private DownloadProgressChangedEventHandler DownloadUpdateProgressChangedEventEvent;
public event DownloadProgressChangedEventHandler DownloadUpdateProgressChangedEvent
{
add
{
DownloadUpdateProgressChangedEventEvent =
(DownloadProgressChangedEventHandler)Delegate.Combine(DownloadUpdateProgressChangedEventEvent,
value);
}
remove
{
DownloadUpdateProgressChangedEventEvent =
(DownloadProgressChangedEventHandler)Delegate.Remove(DownloadUpdateProgressChangedEventEvent,
value);
}
}
private AsyncCompletedEventHandler DownloadUpdateCompletedEventEvent;
public event AsyncCompletedEventHandler DownloadUpdateCompletedEvent
{
add
{
DownloadUpdateCompletedEventEvent =
(AsyncCompletedEventHandler)Delegate.Combine(DownloadUpdateCompletedEventEvent, value);
}
remove
{
DownloadUpdateCompletedEventEvent =
(AsyncCompletedEventHandler)Delegate.Remove(DownloadUpdateCompletedEventEvent, value);
}
}

View File

@@ -1,9 +0,0 @@
namespace mRemoteNG.Config.ACL
{
public enum ACLPermissions
{
Hidden = 0,
ReadOnly = 1,
WriteAllow = 2
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using mRemoteNG.Credential;
namespace mRemoteNG.Config
{
public class ConnectionToCredentialMap : Dictionary<Guid, ICredentialRecord>
{
private readonly IEqualityComparer<ICredentialRecord> _credentialComparer = new CredentialDomainUserPasswordComparer();
public IEnumerable<ICredentialRecord> DistinctCredentialRecords => Values.Distinct(_credentialComparer);
}
}

View File

@@ -1,13 +0,0 @@
namespace mRemoteNG.Config.Connections
{
public enum ConnectionsBackupFrequencyEnum
{
Unassigned = 0,
Never = 1,
OnEdit = 2,
OnExit = 3,
Daily = 4,
Weekly = 5,
Custom = 6
}
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using mRemoteNG.Connection;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
@@ -10,7 +12,7 @@ namespace mRemoteNG.Config.Connections
/// The previous <see cref="ConnectionTreeModel"/> that is being
/// unloaded.
/// </summary>
public Optional<ConnectionTreeModel> PreviousConnectionTreeModel { get; }
public List<ConnectionInfo> RemovedConnections { get; }
/// <summary>
/// True if the previous <see cref="ConnectionTreeModel"/> was loaded from
@@ -21,7 +23,7 @@ namespace mRemoteNG.Config.Connections
/// <summary>
/// The new <see cref="ConnectionTreeModel"/> that is being loaded.
/// </summary>
public ConnectionTreeModel NewConnectionTreeModel { get; }
public List<ConnectionInfo> AddedConnections { get; }
/// <summary>
/// True if the new <see cref="ConnectionTreeModel"/> was loaded from
@@ -36,24 +38,18 @@ namespace mRemoteNG.Config.Connections
/// </summary>
public string NewSourcePath { get; }
public ConnectionsLoadedEventArgs(Optional<ConnectionTreeModel> previousTreeModelModel,
ConnectionTreeModel newTreeModelModel,
bool previousSourceWasDatabase,
bool newSourceIsDatabase,
string newSourcePath)
{
if (previousTreeModelModel == null)
throw new ArgumentNullException(nameof(previousTreeModelModel));
if (newTreeModelModel == null)
throw new ArgumentNullException(nameof(newTreeModelModel));
if (newSourcePath == null)
throw new ArgumentNullException(nameof(newSourcePath));
public IConnectionTreeModel NewConnectionTreeModel { get; set; } = new ConnectionTreeModel();
PreviousConnectionTreeModel = previousTreeModelModel;
public ConnectionsLoadedEventArgs(
List<ConnectionInfo> removedConnections, List<ConnectionInfo> addedConnections,
bool previousSourceWasDatabase, bool newSourceIsDatabase,
string newSourcePath)
{
RemovedConnections = removedConnections.ThrowIfNull(nameof(removedConnections));
PreviousSourceWasDatabase = previousSourceWasDatabase;
NewConnectionTreeModel = newTreeModelModel;
AddedConnections = addedConnections.ThrowIfNull(nameof(addedConnections));
NewSourceIsDatabase = newSourceIsDatabase;
NewSourcePath = newSourcePath;
NewSourcePath = newSourcePath.ThrowIfNull(nameof(newSourcePath));
}
}
}
}

View File

@@ -5,12 +5,12 @@ namespace mRemoteNG.Config.Connections
{
public class ConnectionsSavedEventArgs
{
public ConnectionTreeModel ModelThatWasSaved { get; }
public IConnectionTreeModel ModelThatWasSaved { get; }
public bool PreviouslyUsingDatabase { get; }
public bool UsingDatabase { get; }
public string ConnectionFileName { get; }
public ConnectionsSavedEventArgs(ConnectionTreeModel modelThatWasSaved,
public ConnectionsSavedEventArgs(IConnectionTreeModel modelThatWasSaved,
bool previouslyUsingDatabase,
bool usingDatabase,
string connectionFileName)
@@ -24,4 +24,4 @@ namespace mRemoteNG.Config.Connections
ConnectionFileName = connectionFileName;
}
}
}
}

View File

@@ -25,11 +25,10 @@ namespace mRemoteNG.Config.Connections
public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
{
var csvConnectionsSerializer =
new CsvConnectionsSerializerMremotengFormat(_saveFilter, Runtime.CredentialProviderCatalog);
var csvConnectionsSerializer = new CsvConnectionsSerializerMremotengFormat(_saveFilter, Runtime.CredentialService.RepositoryList);
var dataProvider = new FileDataProvider(_connectionFileName);
var csvContent = csvConnectionsSerializer.Serialize(connectionTreeModel);
dataProvider.Save(csvContent);
}
}
}
}

View File

@@ -1,9 +1,9 @@
using mRemoteNG.Tree;
using mRemoteNG.Config.Serializers;
namespace mRemoteNG.Config.Connections
{
public interface IConnectionsLoader
{
ConnectionTreeModel Load();
SerializationResult Load();
}
}

View File

@@ -8,7 +8,7 @@ namespace mRemoteNG.Config.Connections.Multiuser
{
public class RemoteConnectionsSyncronizer : IConnectionsUpdateChecker
{
private readonly System.Timers.Timer _updateTimer;
private readonly Timer _updateTimer;
private readonly IConnectionsUpdateChecker _updateChecker;
public double TimerIntervalInMilliseconds
@@ -19,7 +19,7 @@ namespace mRemoteNG.Config.Connections.Multiuser
public RemoteConnectionsSyncronizer(IConnectionsUpdateChecker updateChecker)
{
_updateChecker = updateChecker;
_updateTimer = new System.Timers.Timer(3000);
_updateTimer = new Timer(3000);
SetEventListeners();
}
@@ -35,7 +35,7 @@ namespace mRemoteNG.Config.Connections.Multiuser
private void Load(object sender, ConnectionsUpdateAvailableEventArgs args)
{
Runtime.ConnectionsService.LoadConnections(true, false, "");
Runtime.ConnectionsService.LoadConnections(true, "");
args.Handled = true;
}

View File

@@ -1,58 +1,47 @@
using System;
using System.Collections.Specialized;
using System.Collections.Specialized;
using System.ComponentModel;
using mRemoteNG.Connection;
using mRemoteNG.UI.Forms;
using mRemoteNG.Properties;
using mRemoteNG.Tools;
namespace mRemoteNG.Config.Connections
{
public class SaveConnectionsOnEdit
{
private readonly ConnectionsService _connectionsService;
private IConnectionsService _connectionsService;
public SaveConnectionsOnEdit(ConnectionsService connectionsService)
public void Subscribe(IConnectionsService connectionsService)
{
if (connectionsService == null)
throw new ArgumentNullException(nameof(connectionsService));
_connectionsService = connectionsService;
connectionsService.ConnectionsLoaded += ConnectionsServiceOnConnectionsLoaded;
_connectionsService = connectionsService.ThrowIfNull(nameof(connectionsService));
connectionsService.ConnectionTreeModel.CollectionChanged += ConnectionTreeModelOnCollectionChanged;
connectionsService.ConnectionTreeModel.PropertyChanged += ConnectionTreeModelOnPropertyChanged;
}
private void ConnectionsServiceOnConnectionsLoaded(object sender, ConnectionsLoadedEventArgs connectionsLoadedEventArgs)
public void Unsubscribe()
{
connectionsLoadedEventArgs.NewConnectionTreeModel.CollectionChanged +=
ConnectionTreeModelOnCollectionChanged;
connectionsLoadedEventArgs.NewConnectionTreeModel.PropertyChanged += ConnectionTreeModelOnPropertyChanged;
foreach (var oldTree in connectionsLoadedEventArgs.PreviousConnectionTreeModel)
{
oldTree.CollectionChanged -= ConnectionTreeModelOnCollectionChanged;
oldTree.PropertyChanged -= ConnectionTreeModelOnPropertyChanged;
}
_connectionsService.ConnectionTreeModel.CollectionChanged -= ConnectionTreeModelOnCollectionChanged;
_connectionsService.ConnectionTreeModel.PropertyChanged -= ConnectionTreeModelOnPropertyChanged;
_connectionsService = null;
}
private void ConnectionTreeModelOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
private void ConnectionTreeModelOnPropertyChanged(object sender,
PropertyChangedEventArgs propertyChangedEventArgs)
{
SaveConnectionOnEdit(propertyChangedEventArgs.PropertyName);
}
private void ConnectionTreeModelOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
private void ConnectionTreeModelOnCollectionChanged(object sender,
NotifyCollectionChangedEventArgs
notifyCollectionChangedEventArgs)
{
SaveConnectionOnEdit();
}
private void SaveConnectionOnEdit(string propertyName = "")
{
//OBSOLETE: mRemoteNG.Settings.Default.SaveConnectionsAfterEveryEdit is obsolete and should be removed in a future release
if (Properties.OptionsBackupPage.Default.SaveConnectionsAfterEveryEdit || (Properties.OptionsBackupPage.Default.SaveConnectionsFrequency == (int)ConnectionsBackupFrequencyEnum.OnEdit))
{
if (FrmMain.Default.IsClosing)
return;
if (!Properties.Settings.Default.SaveConnectionsAfterEveryEdit)
return;
_connectionsService.SaveConnectionsAsync(propertyName);
}
_connectionsService?.SaveConnectionsAsync(propertyName);
}
}
}
}

View File

@@ -12,7 +12,6 @@ using mRemoteNG.Security;
using mRemoteNG.Security.Authentication;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Connections
@@ -36,7 +35,7 @@ namespace mRemoteNG.Config.Connections
_dataProvider = dataProvider.ThrowIfNull(nameof(dataProvider));
}
public ConnectionTreeModel Load()
public SerializationResult Load()
{
var connector = DatabaseConnectorFactory.DatabaseConnectorFromSettings();
var dataProvider = new SqlDataProvider(connector);
@@ -54,9 +53,9 @@ namespace mRemoteNG.Config.Connections
databaseVersionVerifier.VerifyDatabaseVersion(metaData.ConfVersion);
var dataTable = dataProvider.Load();
var deserializer = new DataTableDeserializer(cryptoProvider, decryptionKey.First());
var connectionTree = deserializer.Deserialize(dataTable);
ApplyLocalConnectionProperties(connectionTree.RootNodes.First(i => i is RootNodeInfo));
return connectionTree;
var serializationResult = deserializer.Deserialize(dataTable);
ApplyLocalConnectionProperties(serializationResult.ConnectionRecords.OfType<RootNodeInfo>().First());
return serializationResult;
}
private Optional<SecureString> GetDecryptionKey(SqlConnectionListMetaData metaData)

View File

@@ -17,18 +17,19 @@ using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.Resources.Language;
using mRemoteNG.Properties;
namespace mRemoteNG.Config.Connections
{
public class SqlConnectionsSaver : ISaver<ConnectionTreeModel>
public class SqlConnectionsSaver : ISaver<IConnectionTreeModel>
{
private readonly SaveFilter _saveFilter;
private readonly ISerializer<IEnumerable<LocalConnectionPropertiesModel>, string> _localPropertiesSerializer;
private readonly IDataProvider<string> _dataProvider;
public SqlConnectionsSaver(SaveFilter saveFilter, ISerializer<IEnumerable<LocalConnectionPropertiesModel>, string> localPropertieSerializer, IDataProvider<string> localPropertiesDataProvider)
public SqlConnectionsSaver(SaveFilter saveFilter,
ISerializer<IEnumerable<LocalConnectionPropertiesModel>, string>
localPropertieSerializer,
IDataProvider<string> localPropertiesDataProvider)
{
if (saveFilter == null)
throw new ArgumentNullException(nameof(saveFilter));
@@ -37,7 +38,7 @@ namespace mRemoteNG.Config.Connections
_dataProvider = localPropertiesDataProvider.ThrowIfNull(nameof(localPropertiesDataProvider));
}
public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
public void Save(IConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
{
var rootTreeNode = connectionTreeModel.RootNodes.OfType<RootNodeInfo>().First();
@@ -45,13 +46,15 @@ namespace mRemoteNG.Config.Connections
if (PropertyIsLocalOnly(propertyNameTrigger))
{
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Property {propertyNameTrigger} is local only. Not saving to database.");
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
$"Property {propertyNameTrigger} is local only. Not saving to database.");
return;
}
if (SqlUserIsReadOnly())
{
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, "Trying to save connection tree but the SQL read only checkbox is checked, aborting!");
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg,
"Trying to save connection tree but the SQL read only checkbox is checked, aborting!");
return;
}
@@ -64,7 +67,8 @@ namespace mRemoteNG.Config.Connections
if (!databaseVersionVerifier.VerifyDatabaseVersion(metaData.ConfVersion))
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.ErrorConnectionListSaveFailed);
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg,
Language.ErrorConnectionListSaveFailed);
return;
}
@@ -135,9 +139,9 @@ namespace mRemoteNG.Config.Connections
{
dbQuery =
databaseConnector.DbCommand(
"INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES('" +
MiscTools.PrepareValueForDB(rootTreeNode.Name) + "', 0, '" + strProtected + "','" +
ConnectionsFileInfo.ConnectionFileVersion.ToString() + "')");
"INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES(\'" +
MiscTools.PrepareValueForDB(rootTreeNode.Name) + "\', 0, \'" + strProtected + "\'," +
ConnectionsFileInfo.ConnectionFileVersion.ToString(CultureInfo.InvariantCulture) + ")");
dbQuery.ExecuteNonQuery();
}
else
@@ -167,13 +171,13 @@ namespace mRemoteNG.Config.Connections
{
var dbQuery = databaseConnector.DbCommand("DELETE FROM tblUpdate");
dbQuery.ExecuteNonQuery();
dbQuery = databaseConnector.DbCommand("INSERT INTO tblUpdate (LastUpdate) VALUES('" + MiscTools.DBDate(DateTime.Now) + "')");
dbQuery = databaseConnector.DbCommand("INSERT INTO tblUpdate (LastUpdate) VALUES(\'" + MiscTools.DBDate(DateTime.Now) + "\')");
dbQuery.ExecuteNonQuery();
}
private bool SqlUserIsReadOnly()
{
return Properties.OptionsDBsPage.Default.SQLReadOnly;
return Properties.Settings.Default.SQLReadOnly;
}
}
}

View File

@@ -1,18 +1,25 @@
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using System;
using System.IO;
using System.Security;
using mRemoteNG.Config.Serializers.ConnectionSerializers.Xml;
using mRemoteNG.App.Info;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Connection;
using mRemoteNG.Credential;
using mRemoteNG.UI.Forms;
namespace mRemoteNG.Config.Connections
{
public class XmlConnectionsLoader : IConnectionsLoader
{
private readonly string _credentialFilePath = Path.Combine(CredentialsFileInfo.CredentialsPath, CredentialsFileInfo.CredentialsFile);
private readonly string _connectionFilePath;
private readonly ConnectionsService _connectionsService;
private readonly ICredentialService _credentialService;
public XmlConnectionsLoader(string connectionFilePath)
public XmlConnectionsLoader(string connectionFilePath, ICredentialService credentialService, ConnectionsService connectionsService)
{
if (string.IsNullOrEmpty(connectionFilePath))
throw new ArgumentException($"{nameof(connectionFilePath)} cannot be null or empty");
@@ -21,14 +28,26 @@ namespace mRemoteNG.Config.Connections
throw new FileNotFoundException($"{connectionFilePath} does not exist");
_connectionFilePath = connectionFilePath;
_connectionsService = connectionsService.ThrowIfNull(nameof(connectionsService));
_credentialService = credentialService.ThrowIfNull(nameof(credentialService));
}
public ConnectionTreeModel Load()
public SerializationResult Load()
{
var dataProvider = new FileDataProvider(_connectionFilePath);
var xmlString = dataProvider.Load();
var deserializer = new XmlConnectionsDeserializer(PromptForPassword);
return deserializer.Deserialize(xmlString);
var deserializer = new CredentialManagerUpgradeForm
{
ConnectionFilePath = _connectionFilePath,
NewCredentialRepoPath = _credentialFilePath,
ConnectionsService = _connectionsService,
CredentialService = _credentialService,
ConnectionDeserializer = new XmlConnectionsDeserializer(PromptForPassword)
};
var serializationResult = deserializer.Deserialize(xmlString);
return serializationResult;
}
private Optional<SecureString> PromptForPassword()

View File

@@ -7,11 +7,10 @@ using mRemoteNG.Security;
using mRemoteNG.Security.Factories;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.Properties;
namespace mRemoteNG.Config.Connections
{
public class XmlConnectionsSaver : ISaver<ConnectionTreeModel>
public class XmlConnectionsSaver : ISaver<IConnectionTreeModel>
{
private readonly string _connectionFileName;
private readonly SaveFilter _saveFilter;
@@ -27,7 +26,7 @@ namespace mRemoteNG.Config.Connections
_saveFilter = saveFilter;
}
public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
public void Save(IConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
{
try
{
@@ -38,7 +37,7 @@ namespace mRemoteNG.Config.Connections
cryptographyProvider,
connectionTreeModel,
_saveFilter,
Properties.OptionsSecurityPage.Default.EncryptCompleteConnectionsFile);
Properties.Settings.Default.EncryptCompleteConnectionsFile);
var rootNode = connectionTreeModel.RootNodes.OfType<RootNodeInfo>().First();
var xml = xmlConnectionsSerializer.Serialize(rootNode);

View File

@@ -0,0 +1,39 @@
using System.Collections.Generic;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers.CredentialProviderSerializer;
using mRemoteNG.Credential;
using mRemoteNG.Credential.Repositories;
using mRemoteNG.Tools;
namespace mRemoteNG.Config
{
public class CredentialRepositoryListPersistor : ISaver<IEnumerable<ICredentialRepository>>, ILoader<IEnumerable<ICredentialRepository>>
{
private readonly IReadOnlyCollection<ICredentialRepositoryFactory> _repositoryFactories;
private readonly IDataProvider<string> _dataProvider;
private readonly CredentialRepositoryListDeserializer _deserializer;
private readonly CredentialRepositoryListSerializer _serializer;
public CredentialRepositoryListPersistor(
IDataProvider<string> dataProvider,
IReadOnlyCollection<ICredentialRepositoryFactory> repositoryFactories)
{
_repositoryFactories = repositoryFactories.ThrowIfNull(nameof(repositoryFactories));
_dataProvider = dataProvider.ThrowIfNull(nameof(dataProvider));
_deserializer = new CredentialRepositoryListDeserializer();
_serializer = new CredentialRepositoryListSerializer();
}
public IEnumerable<ICredentialRepository> Load()
{
var data = _dataProvider.Load();
return _deserializer.Deserialize(data, _repositoryFactories);
}
public void Save(IEnumerable<ICredentialRepository> repositories, string propertyNameTrigger = "")
{
var data = _serializer.Serialize(repositories);
_dataProvider.Save(data);
}
}
}

View File

@@ -2,7 +2,6 @@
using System.IO;
using mRemoteNG.App;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Config.DataProviders
{
@@ -16,7 +15,7 @@ namespace mRemoteNG.Config.DataProviders
return;
var backupFileName =
string.Format(Properties.OptionsBackupPage.Default.BackupFileNameFormat, fileName, DateTime.Now);
string.Format(Properties.Settings.Default.BackupFileNameFormat, fileName, DateTime.Now);
File.Copy(fileName, backupFileName);
}
catch (Exception ex)
@@ -39,7 +38,7 @@ namespace mRemoteNG.Config.DataProviders
private bool FeatureIsTurnedOff()
{
return Properties.OptionsBackupPage.Default.BackupFileKeepCount == 0;
return Properties.Settings.Default.BackupFileKeepCount == 0;
}
}
}

View File

@@ -13,7 +13,7 @@ namespace mRemoteNG.Config.DataProviders
if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(directoryName))
return;
var searchPattern = string.Format(Properties.OptionsBackupPage.Default.BackupFileNameFormat, fileName, "*");
var searchPattern = string.Format(Properties.Settings.Default.BackupFileNameFormat, fileName, "*");
var files = Directory.GetFiles(directoryName, searchPattern);
if (files.Length <= maxBackupsToKeep)

View File

@@ -2,7 +2,6 @@
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Messages;
using mRemoteNG.App;
using mRemoteNG.Properties;
using MySql.Data.MySqlClient;
using System.Data.SqlClient;
@@ -109,7 +108,7 @@ namespace mRemoteNG.Config.DataProviders
private bool DbUserIsReadOnly()
{
return Properties.OptionsDBsPage.Default.SQLReadOnly;
return Properties.Settings.Default.SQLReadOnly;
}
}
}

View File

@@ -1,5 +1,4 @@
using mRemoteNG.App;
using mRemoteNG.Properties;
using mRemoteNG.Security.SymmetricEncryption;
namespace mRemoteNG.Config.DatabaseConnectors
@@ -8,12 +7,12 @@ namespace mRemoteNG.Config.DatabaseConnectors
{
public static IDatabaseConnector DatabaseConnectorFromSettings()
{
var sqlType = Properties.OptionsDBsPage.Default.SQLServerType;
var sqlHost = Properties.OptionsDBsPage.Default.SQLHost;
var sqlCatalog = Properties.OptionsDBsPage.Default.SQLDatabaseName;
var sqlUsername = Properties.OptionsDBsPage.Default.SQLUser;
var sqlType = Properties.Settings.Default.SQLServerType;
var sqlHost = Properties.Settings.Default.SQLHost;
var sqlCatalog = Properties.Settings.Default.SQLDatabaseName;
var sqlUsername = Properties.Settings.Default.SQLUser;
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
var sqlPassword = cryptographyProvider.Decrypt(Properties.OptionsDBsPage.Default.SQLPass, Runtime.EncryptionKey);
var sqlPassword = cryptographyProvider.Decrypt(Properties.Settings.Default.SQLPass, Runtime.EncryptionKey);
return DatabaseConnector(sqlType, sqlHost, sqlCatalog, sqlUsername, sqlPassword);
}

View File

@@ -1,10 +1,11 @@
using System.IO;
using System.Linq;
using mRemoteNG.App;
using mRemoteNG.App;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers.ConnectionSerializers.Csv;
using mRemoteNG.Container;
using mRemoteNG.Messages;
using System.IO;
using System.Linq;
using mRemoteNG.UI.Forms;
namespace mRemoteNG.Config.Import
{
@@ -25,11 +26,18 @@ namespace mRemoteNG.Config.Import
var dataProvider = new FileDataProvider(filePath);
var xmlString = dataProvider.Load();
var xmlConnectionsDeserializer = new CsvConnectionsDeserializerMremotengFormat();
var connectionTreeModel = xmlConnectionsDeserializer.Deserialize(xmlString);
var serializationResult = xmlConnectionsDeserializer.Deserialize(xmlString);
var credentialImportForm = new CredentialImportForm
{
ImportedCredentialRecords = serializationResult.ConnectionToCredentialMap.DistinctCredentialRecords.ToList(),
CredentialService = Runtime.CredentialService
};
credentialImportForm.ShowDialog();
var rootImportContainer = new ContainerInfo {Name = Path.GetFileNameWithoutExtension(filePath)};
rootImportContainer.AddChildRange(connectionTreeModel.RootNodes.First().Children.ToArray());
rootImportContainer.AddChildRange(serializationResult.ConnectionRecords);
destinationContainer.AddChild(rootImportContainer);
}
}
}
}

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Linq;
using mRemoteNG.App;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers.ConnectionSerializers.Xml;
@@ -27,10 +26,10 @@ namespace mRemoteNG.Config.Import
var dataProvider = new FileDataProvider(fileName);
var xmlString = dataProvider.Load();
var xmlConnectionsDeserializer = new XmlConnectionsDeserializer();
var connectionTreeModel = xmlConnectionsDeserializer.Deserialize(xmlString, true);
var serializationResult = xmlConnectionsDeserializer.Deserialize(xmlString, true);
var rootImportContainer = new ContainerInfo {Name = Path.GetFileNameWithoutExtension(fileName)};
rootImportContainer.AddChildRange(connectionTreeModel.RootNodes.First().Children.ToArray());
rootImportContainer.AddChildRange(serializationResult.ConnectionRecords);
destinationContainer.AddChild(rootImportContainer);
}
}

View File

@@ -1,5 +1,4 @@
using System.Linq;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers.MiscSerializers;
using mRemoteNG.Container;
@@ -14,12 +13,9 @@ namespace mRemoteNG.Config.Import
var xmlContent = dataProvider.Load();
var deserializer = new PuttyConnectionManagerDeserializer();
var connectionTreeModel = deserializer.Deserialize(xmlContent);
var serializationResult = deserializer.Deserialize(xmlContent);
var importedRootNode = connectionTreeModel.RootNodes.First();
if (importedRootNode == null) return;
var childrenToAdd = importedRootNode.Children.ToArray();
destinationContainer.AddChildRange(childrenToAdd);
destinationContainer.AddChildRange(serializationResult.ConnectionRecords);
}
}
}

View File

@@ -15,9 +15,9 @@ namespace mRemoteNG.Config.Import
var content = dataProvider.Load();
var deserializer = new RemoteDesktopConnectionDeserializer();
var connectionTreeModel = deserializer.Deserialize(content);
var serializationResult = deserializer.Deserialize(content);
var importedConnection = connectionTreeModel.RootNodes.First().Children.First();
var importedConnection = serializationResult.ConnectionRecords.FirstOrDefault();
if (importedConnection == null) return;
importedConnection.Name = Path.GetFileNameWithoutExtension(fileName);

View File

@@ -1,5 +1,4 @@
using System.Linq;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers.MiscSerializers;
using mRemoteNG.Container;
@@ -14,12 +13,9 @@ namespace mRemoteNG.Config.Import
var fileContent = dataProvider.Load();
var deserializer = new RemoteDesktopConnectionManagerDeserializer();
var connectionTreeModel = deserializer.Deserialize(fileContent);
var serializationResult = deserializer.Deserialize(fileContent);
var importedRootNode = connectionTreeModel.RootNodes.First();
if (importedRootNode == null) return;
var childrenToAdd = importedRootNode.Children.ToArray();
destinationContainer.AddChildRange(childrenToAdd);
destinationContainer.AddChildRange(serializationResult.ConnectionRecords);
}
}
}

View File

@@ -1,47 +0,0 @@
#region
using System.IO;
using Castle.Core.Internal;
using mRemoteNG.App;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers.ConnectionSerializers.Csv.RemoteDesktopManager;
using mRemoteNG.Container;
using mRemoteNG.Messages;
#endregion
namespace mRemoteNG.Config.Import;
public class RemoteDesktopManagerImporter : IConnectionImporter<string>
{
public void Import(string filePath, ContainerInfo destinationContainer)
{
if (string.IsNullOrEmpty(filePath))
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, "Unable to import file. File path is null.");
return;
}
if (!File.Exists(filePath))
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg,
$"Unable to import file. File does not exist. Path: {filePath}");
var dataProvider = new FileDataProvider(filePath);
var csvString = dataProvider.Load();
if (string.IsNullOrEmpty(csvString))
{
var csvDeserializer = new CsvConnectionsDeserializerRdmFormat();
var connectionTreeModel = csvDeserializer.Deserialize(csvString);
var rootContainer = new ContainerInfo { Name = Path.GetFileNameWithoutExtension(filePath) };
rootContainer.AddChildRange(connectionTreeModel.RootNodes);
destinationContainer.AddChild(rootContainer);
}
else
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, "Unable to import file. File is empty.");
return;
}
}
}

View File

@@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.Management;
using System.Net;
using System.Security.Principal;
using Microsoft.Win32;
using Microsoft.Win32;
using mRemoteNG.App;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Messages;
using System;
using System.Collections.Generic;
using System.Management;
using System.Net;
using System.Security.Principal;
namespace mRemoteNG.Config.Putty
@@ -55,6 +55,7 @@ namespace mRemoteNG.Config.Putty
PuttySession = sessionName,
Name = sessionName,
Hostname = sessionKey.GetValue("HostName")?.ToString() ?? "",
// TODO: this should create a temp putty credential
Username = sessionKey.GetValue("UserName")?.ToString() ?? ""
};

View File

@@ -1,20 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using mRemoteNG.Config.Serializers.CredentialSerializer;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.Http;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Container;
using mRemoteNG.Security;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
{
public class CsvConnectionsDeserializerMremotengFormat : IDeserializer<string, ConnectionTreeModel>
public class CsvConnectionsDeserializerMremotengFormat
{
public ConnectionTreeModel Deserialize(string serializedData)
public SerializationResult Deserialize(string serializedData)
{
var lines = serializedData.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.RemoveEmptyEntries);
var csvHeaders = new List<string>();
@@ -29,26 +32,39 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
else
{
var connectionInfo = ParseConnectionInfo(csvHeaders, line);
parentMapping.Add(connectionInfo, line[csvHeaders.IndexOf("Parent")]);
var parentFieldIndex = csvHeaders.IndexOf("Parent");
if (parentFieldIndex > 0)
parentMapping.Add(connectionInfo, line[parentFieldIndex]);
}
}
var root = CreateTreeStructure(parentMapping);
var connectionTreeModel = new ConnectionTreeModel();
connectionTreeModel.AddRootNode(root);
return connectionTreeModel;
var harvestedCredentials = new CredentialHarvester()
.Harvest(new HarvestConfig<string[]>
{
ItemEnumerator = () => lines.Skip(1).Select(s => s.Split(';')),
ConnectionGuidSelector = line => csvHeaders.Contains("Id") ? Guid.Parse(line[csvHeaders.IndexOf("Id")]) : Guid.NewGuid(),
TitleSelector = line => "",
UsernameSelector = line => csvHeaders.Contains("Username") ? line[csvHeaders.IndexOf("Username")] : "",
DomainSelector = line => csvHeaders.Contains("Domain") ? line[csvHeaders.IndexOf("Domain")] : "",
PasswordSelector = line => csvHeaders.Contains("Password") ? line[csvHeaders.IndexOf("Password")].ConvertToSecureString() : new SecureString()
});
var result = new SerializationResult(root, harvestedCredentials);
return result;
}
private RootNodeInfo CreateTreeStructure(Dictionary<ConnectionInfo, string> parentMapping)
private List<ConnectionInfo> CreateTreeStructure(Dictionary<ConnectionInfo, string> parentMapping)
{
var root = new RootNodeInfo(RootNodeType.Connection);
var root = new List<ConnectionInfo>();
foreach (var node in parentMapping)
{
// no parent mapped, add to root
if (string.IsNullOrEmpty(node.Value))
{
root.AddChild(node.Key);
root.Add(node.Key);
continue;
}
@@ -64,7 +80,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
}
else
{
root.AddChild(node.Key);
root.Add(node.Key);
}
}
@@ -85,65 +101,29 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
? new ConnectionInfo(nodeId)
: new ContainerInfo(nodeId);
connectionRecord.Name = headers.Contains("Name")
? connectionCsv[headers.IndexOf("Name")]
: "";
connectionRecord.Name = headers.Contains("Name") ? connectionCsv[headers.IndexOf("Name")] : "";
connectionRecord.Description =
headers.Contains("Description") ? connectionCsv[headers.IndexOf("Description")] : "";
connectionRecord.Icon = headers.Contains("Icon") ? connectionCsv[headers.IndexOf("Icon")] : "";
connectionRecord.Panel = headers.Contains("Panel") ? connectionCsv[headers.IndexOf("Panel")] : "";
connectionRecord.Description = headers.Contains("Description")
? connectionCsv[headers.IndexOf("Description")]
: "";
var hasCredRecordId = Guid.TryParse(
headers.Contains("CredentialRecordId") ? connectionCsv[headers.IndexOf("CredentialRecordId")] : "",
out var credRecordId);
connectionRecord.CredentialRecordId = hasCredRecordId ? credRecordId : Optional<Guid>.Empty;
connectionRecord.Icon = headers.Contains("Icon")
? connectionCsv[headers.IndexOf("Icon")]
: "";
connectionRecord.Panel = headers.Contains("Panel")
? connectionCsv[headers.IndexOf("Panel")]
: "";
connectionRecord.Username = headers.Contains("UserViaAPI")
? connectionCsv[headers.IndexOf("UserViaAPI")]
: "";
connectionRecord.Username = headers.Contains("Username")
? connectionCsv[headers.IndexOf("Username")]
: "";
connectionRecord.Password = headers.Contains("Password")
? connectionCsv[headers.IndexOf("Password")]
: "";
connectionRecord.Domain = headers.Contains("Domain")
? connectionCsv[headers.IndexOf("Domain")]
: "";
connectionRecord.Hostname = headers.Contains("Hostname")
? connectionCsv[headers.IndexOf("Hostname")]
: "";
connectionRecord.VmId = headers.Contains("VmId")
? connectionCsv[headers.IndexOf("VmId")] : "";
connectionRecord.SSHOptions =headers.Contains("SSHOptions")
? connectionCsv[headers.IndexOf("SSHOptions")]
: "";
connectionRecord.SSHTunnelConnectionName = headers.Contains("SSHTunnelConnectionName")
? connectionCsv[headers.IndexOf("SSHTunnelConnectionName")]
: "";
connectionRecord.PuttySession = headers.Contains("PuttySession")
? connectionCsv[headers.IndexOf("PuttySession")]
: "";
// TODO: harvest
connectionRecord.Username = headers.Contains("Username") ? connectionCsv[headers.IndexOf("Username")] : "";
connectionRecord.Password = headers.Contains("Password") ? connectionCsv[headers.IndexOf("Password")] : "";
connectionRecord.Domain = headers.Contains("Domain") ? connectionCsv[headers.IndexOf("Domain")] : "";
connectionRecord.Hostname = headers.Contains("Hostname") ? connectionCsv[headers.IndexOf("Hostname")] : "";
connectionRecord.PuttySession =
headers.Contains("PuttySession") ? connectionCsv[headers.IndexOf("PuttySession")] : "";
connectionRecord.LoadBalanceInfo = headers.Contains("LoadBalanceInfo")
? connectionCsv[headers.IndexOf("LoadBalanceInfo")]
: "";
connectionRecord.OpeningCommand = headers.Contains("OpeningCommand")
? connectionCsv[headers.IndexOf("OpeningCommand")]
: "";
connectionRecord.PreExtApp = headers.Contains("PreExtApp")
? connectionCsv[headers.IndexOf("PreExtApp")]
: "";
@@ -194,13 +174,11 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
? connectionCsv[headers.IndexOf("RDGatewayHostname")]
: "";
connectionRecord.RDPStartProgram = headers.Contains("RDPStartProgram")
? connectionCsv[headers.IndexOf("RDPStartProgram")]
: "";
connectionRecord.RDPStartProgramWorkDir = headers.Contains("RDPStartProgramWorkDir")
? connectionCsv[headers.IndexOf("RDPStartProgramWorkDir")]
: "";
if (headers.Contains("CredentialId"))
{
if (Guid.TryParse(connectionCsv[headers.IndexOf("CredentialId")], out var credId))
connectionRecord.CredentialRecordId = credId;
}
if (headers.Contains("Protocol"))
{
@@ -226,18 +204,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
connectionRecord.UseCredSsp = value;
}
if (headers.Contains("UseRestrictedAdmin"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("UseRestrictedAdmin")], out bool value))
connectionRecord.UseRestrictedAdmin = value;
}
if (headers.Contains("UseRCG"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("UseRCG")], out bool value))
connectionRecord.UseRCG = value;
}
if (headers.Contains("UseVmId"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("UseVmId")], out bool value))
@@ -564,12 +530,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
connectionRecord.Inheritance.SSHTunnelConnectionName = value;
}
if (headers.Contains("InheritOpeningCommand"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritOpeningCommand")], out bool value))
connectionRecord.Inheritance.OpeningCommand = value;
}
if (headers.Contains("InheritSSHOptions"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritSSHOptions")], out bool value))
@@ -648,19 +608,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
connectionRecord.Inheritance.UseCredSsp = value;
}
if (headers.Contains("InheritUseRestrictedAdmin"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritUseRestrictedAdmin")], out bool value))
connectionRecord.Inheritance.UseRestrictedAdmin = value;
}
if (headers.Contains("InheritUseRCG"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritUseRCG")], out bool value))
connectionRecord.Inheritance.UseRCG = value;
}
if (headers.Contains("InheritUseVmId"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritUseVmId")], out bool value))
@@ -679,12 +626,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
connectionRecord.Inheritance.RenderingEngine = value;
}
if (headers.Contains("InheritUserViaAPI"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritUserViaAPI")], out bool value))
connectionRecord.Inheritance.UserViaAPI = value;
}
if (headers.Contains("InheritUsername"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritUsername")], out bool value))
@@ -709,12 +650,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
connectionRecord.Inheritance.LoadBalanceInfo = value;
}
if (headers.Contains("InheritOpeningCommand"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritOpeningCommand")], out bool value))
connectionRecord.Inheritance.OpeningCommand = value;
}
if (headers.Contains("InheritPreExtApp"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritPreExtApp")], out bool value))
@@ -884,9 +819,16 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
connectionRecord.Inheritance.RdpVersion = value;
}
if (headers.Contains("InheritCredentialRecord"))
{
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritCredentialRecord")], out bool value))
connectionRecord.Inheritance.CredentialId = value;
}
#endregion
return connectionRecord;
}
}
}
}

View File

@@ -1,267 +1,247 @@
using System;
using System.Linq;
using System.Text;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Credential;
using mRemoteNG.Security;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
{
public class CsvConnectionsSerializerMremotengFormat : ISerializer<ConnectionInfo, string>
{
private readonly SaveFilter _saveFilter;
private readonly ICredentialRepositoryList _credentialRepositoryList;
public Version Version { get; } = new Version(2, 7);
public CsvConnectionsSerializerMremotengFormat(SaveFilter saveFilter,
ICredentialRepositoryList credentialRepositoryList)
{
saveFilter.ThrowIfNull(nameof(saveFilter));
credentialRepositoryList.ThrowIfNull(nameof(credentialRepositoryList));
_saveFilter = saveFilter;
_credentialRepositoryList = credentialRepositoryList;
}
public string Serialize(ConnectionTreeModel connectionTreeModel)
{
connectionTreeModel.ThrowIfNull(nameof(connectionTreeModel));
var rootNode = connectionTreeModel.RootNodes.First(node => node is RootNodeInfo);
return Serialize(rootNode);
}
public string Serialize(ConnectionInfo serializationTarget)
{
serializationTarget.ThrowIfNull(nameof(serializationTarget));
var sb = new StringBuilder();
WriteCsvHeader(sb);
SerializeNodesRecursive(serializationTarget, sb);
return sb.ToString();
}
private void WriteCsvHeader(StringBuilder sb)
{
sb.Append("Name;Id;Parent;NodeType;Description;Icon;Panel;");
if (_saveFilter.SaveUsername)
sb.Append("Username;");
if (_saveFilter.SavePassword)
sb.Append("Password;");
if (_saveFilter.SaveDomain)
sb.Append("Domain;");
sb.Append("Hostname;Port;VmId;Protocol;SSHTunnelConnectionName;OpeningCommand;SSHOptions;PuttySession;ConnectToConsole;UseCredSsp;UseRestrictedAdmin;UseRCG;UseVmId;UseEnhancedMode;RenderingEngine;RDPAuthenticationLevel;" +
"LoadBalanceInfo;Colors;Resolution;AutomaticResize;DisplayWallpaper;DisplayThemes;EnableFontSmoothing;EnableDesktopComposition;DisableFullWindowDrag;DisableMenuAnimations;DisableCursorShadow;DisableCursorBlinking;" +
"CacheBitmaps;RedirectDiskDrives;RedirectPorts;RedirectPrinters;RedirectClipboard;RedirectSmartCards;RedirectSound;RedirectKeys;" +
"PreExtApp;PostExtApp;MacAddress;UserField;ExtApp;Favorite;VNCCompression;VNCEncoding;VNCAuthMode;VNCProxyType;VNCProxyIP;" +
"VNCProxyPort;VNCProxyUsername;VNCProxyPassword;VNCColors;VNCSmartSizeMode;VNCViewOnly;RDGatewayUsageMethod;RDGatewayHostname;" +
"RDGatewayUseConnectionCredentials;RDGatewayUsername;RDGatewayPassword;RDGatewayDomain;RedirectAudioCapture;RdpVersion;RDPStartProgram;RDPStartProgramWorkDir;UserViaAPI;EC2InstanceId;EC2Region;");
if (_saveFilter.SaveInheritance)
sb.Append("InheritCacheBitmaps;InheritColors;InheritDescription;InheritDisplayThemes;InheritDisplayWallpaper;" +
"InheritEnableFontSmoothing;InheritEnableDesktopComposition;InheritDisableFullWindowDrag;InheritDisableMenuAnimations;InheritDisableCursorShadow;InheritDisableCursorBlinking;InheritDomain;InheritIcon;InheritPanel;InheritPassword;InheritPort;" +
"InheritProtocol;InheritSSHTunnelConnectionName;InheritOpeningCommand;InheritSSHOptions;InheritPuttySession;InheritRedirectDiskDrives;InheritRedirectKeys;InheritRedirectPorts;InheritRedirectPrinters;" +
"InheritRedirectClipboard;InheritRedirectSmartCards;InheritRedirectSound;InheritResolution;InheritAutomaticResize;" +
"InheritUseConsoleSession;InheritUseCredSsp;InheritUseRestrictedAdmin;InheritUseRCG;InheritUseVmId;InheritUseEnhancedMode;InheritVmId;InheritRenderingEngine;InheritUsername;" +
"InheritRDPAuthenticationLevel;InheritLoadBalanceInfo;InheritPreExtApp;InheritPostExtApp;InheritMacAddress;InheritUserField;" +
"InheritFavorite;InheritExtApp;InheritVNCCompression;InheritVNCEncoding;InheritVNCAuthMode;InheritVNCProxyType;InheritVNCProxyIP;" +
"InheritVNCProxyPort;InheritVNCProxyUsername;InheritVNCProxyPassword;InheritVNCColors;InheritVNCSmartSizeMode;InheritVNCViewOnly;" +
"InheritRDGatewayUsageMethod;InheritRDGatewayHostname;InheritRDGatewayUseConnectionCredentials;InheritRDGatewayUsername;" +
"InheritRDGatewayPassword;InheritRDGatewayDomain;InheritRDPAlertIdleTimeout;InheritRDPMinutesToIdleTimeout;InheritSoundQuality;InheritUserViaAPI;" +
"InheritRedirectAudioCapture;InheritRdpVersion");
}
private void SerializeNodesRecursive(ConnectionInfo node, StringBuilder sb)
{
var nodeAsContainer = node as ContainerInfo;
if (nodeAsContainer != null)
{
foreach (var child in nodeAsContainer.Children)
{
SerializeNodesRecursive(child, sb);
}
}
// dont serialize the root node
if (node is RootNodeInfo)
return;
SerializeConnectionInfo(node, sb);
}
private void SerializeConnectionInfo(ConnectionInfo con, StringBuilder sb)
{
sb.AppendLine();
sb.Append(FormatForCsv(con.Name))
.Append(FormatForCsv(con.ConstantID))
.Append(FormatForCsv(con.Parent?.ConstantID ?? ""))
.Append(FormatForCsv(con.GetTreeNodeType()))
.Append(FormatForCsv(con.Description))
.Append(FormatForCsv(con.Icon))
.Append(FormatForCsv(con.Panel));
if (_saveFilter.SaveUsername)
sb.Append(FormatForCsv(con.Username));
if (_saveFilter.SavePassword)
sb.Append(FormatForCsv(con.Password));
if (_saveFilter.SaveDomain)
sb.Append(FormatForCsv(con.Domain));
sb.Append(FormatForCsv(con.Hostname))
.Append(FormatForCsv(con.Port))
.Append(FormatForCsv(con.VmId))
.Append(FormatForCsv(con.Protocol))
.Append(FormatForCsv(con.SSHTunnelConnectionName))
.Append(FormatForCsv(con.OpeningCommand))
.Append(FormatForCsv(con.SSHOptions))
.Append(FormatForCsv(con.PuttySession))
.Append(FormatForCsv(con.UseConsoleSession))
.Append(FormatForCsv(con.UseCredSsp))
.Append(FormatForCsv(con.UseRestrictedAdmin))
.Append(FormatForCsv(con.UseRCG))
.Append(FormatForCsv(con.UseVmId))
.Append(FormatForCsv(con.UseEnhancedMode))
.Append(FormatForCsv(con.RenderingEngine))
.Append(FormatForCsv(con.RDPAuthenticationLevel))
.Append(FormatForCsv(con.LoadBalanceInfo))
.Append(FormatForCsv(con.Colors))
.Append(FormatForCsv(con.Resolution))
.Append(FormatForCsv(con.AutomaticResize))
.Append(FormatForCsv(con.DisplayWallpaper))
.Append(FormatForCsv(con.DisplayThemes))
.Append(FormatForCsv(con.EnableFontSmoothing))
.Append(FormatForCsv(con.EnableDesktopComposition))
.Append(FormatForCsv(con.DisableFullWindowDrag))
.Append(FormatForCsv(con.DisableMenuAnimations))
.Append(FormatForCsv(con.DisableCursorShadow))
.Append(FormatForCsv(con.DisableCursorBlinking))
.Append(FormatForCsv(con.CacheBitmaps))
.Append(FormatForCsv(con.RedirectDiskDrives))
.Append(FormatForCsv(con.RedirectPorts))
.Append(FormatForCsv(con.RedirectPrinters))
.Append(FormatForCsv(con.RedirectClipboard))
.Append(FormatForCsv(con.RedirectSmartCards))
.Append(FormatForCsv(con.RedirectSound))
.Append(FormatForCsv(con.RedirectKeys))
.Append(FormatForCsv(con.PreExtApp))
.Append(FormatForCsv(con.PostExtApp))
.Append(FormatForCsv(con.MacAddress))
.Append(FormatForCsv(con.UserField))
.Append(FormatForCsv(con.ExtApp))
.Append(FormatForCsv(con.Favorite))
.Append(FormatForCsv(con.VNCCompression))
.Append(FormatForCsv(con.VNCEncoding))
.Append(FormatForCsv(con.VNCAuthMode))
.Append(FormatForCsv(con.VNCProxyType))
.Append(FormatForCsv(con.VNCProxyIP))
.Append(FormatForCsv(con.VNCProxyPort))
.Append(FormatForCsv(con.VNCProxyUsername))
.Append(FormatForCsv(con.VNCProxyPassword))
.Append(FormatForCsv(con.VNCColors))
.Append(FormatForCsv(con.VNCSmartSizeMode))
.Append(FormatForCsv(con.VNCViewOnly))
.Append(FormatForCsv(con.RDGatewayUsageMethod))
.Append(FormatForCsv(con.RDGatewayHostname))
.Append(FormatForCsv(con.RDGatewayUseConnectionCredentials))
.Append(FormatForCsv(con.RDGatewayUsername))
.Append(FormatForCsv(con.RDGatewayPassword))
.Append(FormatForCsv(con.RDGatewayDomain))
.Append(FormatForCsv(con.RedirectAudioCapture))
.Append(FormatForCsv(con.RdpVersion))
.Append(FormatForCsv(con.RDPStartProgram))
.Append(FormatForCsv(con.RDPStartProgramWorkDir))
.Append(FormatForCsv(con.UserViaAPI))
.Append(FormatForCsv(con.EC2InstanceId))
.Append(FormatForCsv(con.EC2Region));
if (!_saveFilter.SaveInheritance)
return;
sb.Append(FormatForCsv(con.Inheritance.CacheBitmaps))
.Append(FormatForCsv(con.Inheritance.Colors))
.Append(FormatForCsv(con.Inheritance.Description))
.Append(FormatForCsv(con.Inheritance.DisplayThemes))
.Append(FormatForCsv(con.Inheritance.DisplayWallpaper))
.Append(FormatForCsv(con.Inheritance.EnableFontSmoothing))
.Append(FormatForCsv(con.Inheritance.EnableDesktopComposition))
.Append(FormatForCsv(con.Inheritance.DisableFullWindowDrag))
.Append(FormatForCsv(con.Inheritance.DisableMenuAnimations))
.Append(FormatForCsv(con.Inheritance.DisableCursorShadow))
.Append(FormatForCsv(con.Inheritance.DisableCursorBlinking))
.Append(FormatForCsv(con.Inheritance.Domain))
.Append(FormatForCsv(con.Inheritance.Icon))
.Append(FormatForCsv(con.Inheritance.Panel))
.Append(FormatForCsv(con.Inheritance.Password))
.Append(FormatForCsv(con.Inheritance.Port))
.Append(FormatForCsv(con.Inheritance.Protocol))
.Append(FormatForCsv(con.Inheritance.SSHTunnelConnectionName))
.Append(FormatForCsv(con.Inheritance.OpeningCommand))
.Append(FormatForCsv(con.Inheritance.SSHOptions))
.Append(FormatForCsv(con.Inheritance.PuttySession))
.Append(FormatForCsv(con.Inheritance.RedirectDiskDrives))
.Append(FormatForCsv(con.Inheritance.RedirectKeys))
.Append(FormatForCsv(con.Inheritance.RedirectPorts))
.Append(FormatForCsv(con.Inheritance.RedirectPrinters))
.Append(FormatForCsv(con.Inheritance.RedirectClipboard))
.Append(FormatForCsv(con.Inheritance.RedirectSmartCards))
.Append(FormatForCsv(con.Inheritance.RedirectSound))
.Append(FormatForCsv(con.Inheritance.Resolution))
.Append(FormatForCsv(con.Inheritance.AutomaticResize))
.Append(FormatForCsv(con.Inheritance.UseConsoleSession))
.Append(FormatForCsv(con.Inheritance.UseCredSsp))
.Append(FormatForCsv(con.Inheritance.UseRestrictedAdmin))
.Append(FormatForCsv(con.Inheritance.UseRCG))
.Append(FormatForCsv(con.Inheritance.UseVmId))
.Append(FormatForCsv(con.Inheritance.UseEnhancedMode))
.Append(FormatForCsv(con.Inheritance.VmId))
.Append(FormatForCsv(con.Inheritance.RenderingEngine))
.Append(FormatForCsv(con.Inheritance.Username))
.Append(FormatForCsv(con.Inheritance.RDPAuthenticationLevel))
.Append(FormatForCsv(con.Inheritance.LoadBalanceInfo))
.Append(FormatForCsv(con.Inheritance.PreExtApp))
.Append(FormatForCsv(con.Inheritance.PostExtApp))
.Append(FormatForCsv(con.Inheritance.MacAddress))
.Append(FormatForCsv(con.Inheritance.UserField))
.Append(FormatForCsv(con.Inheritance.Favorite))
.Append(FormatForCsv(con.Inheritance.ExtApp))
.Append(FormatForCsv(con.Inheritance.VNCCompression))
.Append(FormatForCsv(con.Inheritance.VNCEncoding))
.Append(FormatForCsv(con.Inheritance.VNCAuthMode))
.Append(FormatForCsv(con.Inheritance.VNCProxyType))
.Append(FormatForCsv(con.Inheritance.VNCProxyIP))
.Append(FormatForCsv(con.Inheritance.VNCProxyPort))
.Append(FormatForCsv(con.Inheritance.VNCProxyUsername))
.Append(FormatForCsv(con.Inheritance.VNCProxyPassword))
.Append(FormatForCsv(con.Inheritance.VNCColors))
.Append(FormatForCsv(con.Inheritance.VNCSmartSizeMode))
.Append(FormatForCsv(con.Inheritance.VNCViewOnly))
.Append(FormatForCsv(con.Inheritance.RDGatewayUsageMethod))
.Append(FormatForCsv(con.Inheritance.RDGatewayHostname))
.Append(FormatForCsv(con.Inheritance.RDGatewayUseConnectionCredentials))
.Append(FormatForCsv(con.Inheritance.RDGatewayUsername))
.Append(FormatForCsv(con.Inheritance.RDGatewayPassword))
.Append(FormatForCsv(con.Inheritance.RDGatewayDomain))
.Append(FormatForCsv(con.Inheritance.RDPAlertIdleTimeout))
.Append(FormatForCsv(con.Inheritance.RDPMinutesToIdleTimeout))
.Append(FormatForCsv(con.Inheritance.SoundQuality))
.Append(FormatForCsv(con.Inheritance.RedirectAudioCapture))
.Append(FormatForCsv(con.Inheritance.RdpVersion))
.Append(FormatForCsv(con.Inheritance.UserViaAPI));
}
private string FormatForCsv(object value)
{
var cleanedString = value.ToString().Replace(";", "");
return cleanedString + ";";
}
}
using System;
using System.Linq;
using System.Text;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Credential;
using mRemoteNG.Security;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
{
public class CsvConnectionsSerializerMremotengFormat : ISerializer<ConnectionInfo, string>
{
private readonly SaveFilter _saveFilter;
private readonly ICredentialRepositoryList _credentialRepositoryList;
public Version Version { get; } = new Version(2, 7);
public CsvConnectionsSerializerMremotengFormat(SaveFilter saveFilter,
ICredentialRepositoryList credentialRepositoryList)
{
saveFilter.ThrowIfNull(nameof(saveFilter));
credentialRepositoryList.ThrowIfNull(nameof(credentialRepositoryList));
_saveFilter = saveFilter;
_credentialRepositoryList = credentialRepositoryList;
}
public string Serialize(ConnectionTreeModel connectionTreeModel)
{
connectionTreeModel.ThrowIfNull(nameof(connectionTreeModel));
var rootNode = connectionTreeModel.RootNodes.First(node => node is RootNodeInfo);
return Serialize(rootNode);
}
public string Serialize(ConnectionInfo serializationTarget)
{
serializationTarget.ThrowIfNull(nameof(serializationTarget));
var sb = new StringBuilder();
WriteCsvHeader(sb);
SerializeNodesRecursive(serializationTarget, sb);
return sb.ToString();
}
private void WriteCsvHeader(StringBuilder sb)
{
sb.Append("Name;Id;Parent;NodeType;Description;Icon;Panel;");
if (_saveFilter.SaveUsername)
sb.Append("Username;");
if (_saveFilter.SavePassword)
sb.Append("Password;");
if (_saveFilter.SaveDomain)
sb.Append("Domain;");
sb.Append("Hostname;Port;VmId;Protocol;SSHTunnelConnectionName;SSHOptions;PuttySession;ConnectToConsole;UseCredSsp;UseVmId;UseEnhancedMode;RenderingEngine;RDPAuthenticationLevel;" +
"LoadBalanceInfo;Colors;Resolution;AutomaticResize;DisplayWallpaper;DisplayThemes;EnableFontSmoothing;EnableDesktopComposition;DisableFullWindowDrag;DisableMenuAnimations;DisableCursorShadow;DisableCursorBlinking;" +
"CacheBitmaps;RedirectDiskDrives;RedirectPorts;RedirectPrinters;RedirectClipboard;RedirectSmartCards;RedirectSound;RedirectKeys;" +
"PreExtApp;PostExtApp;MacAddress;UserField;ExtApp;Favorite;VNCCompression;VNCEncoding;VNCAuthMode;VNCProxyType;VNCProxyIP;" +
"VNCProxyPort;VNCProxyUsername;VNCProxyPassword;VNCColors;VNCSmartSizeMode;VNCViewOnly;RDGatewayUsageMethod;RDGatewayHostname;" +
"RDGatewayUseConnectionCredentials;RDGatewayUsername;RDGatewayPassword;RDGatewayDomain;RedirectAudioCapture;RdpVersion;CredentialId");
if (_saveFilter.SaveInheritance)
sb.Append("InheritCacheBitmaps;InheritColors;InheritDescription;InheritDisplayThemes;InheritDisplayWallpaper;" +
"InheritEnableFontSmoothing;InheritEnableDesktopComposition;InheritDisableFullWindowDrag;InheritDisableMenuAnimations;InheritDisableCursorShadow;InheritDisableCursorBlinking;InheritDomain;InheritIcon;InheritPanel;InheritPassword;InheritPort;" +
"InheritProtocol;InheritSSHTunnelConnectionName;InheritSSHOptions;InheritPuttySession;InheritRedirectDiskDrives;InheritRedirectKeys;InheritRedirectPorts;InheritRedirectPrinters;" +
"InheritRedirectClipboard;InheritRedirectSmartCards;InheritRedirectSound;InheritResolution;InheritAutomaticResize;" +
"InheritUseConsoleSession;InheritUseCredSsp;InheritUseVmId;InheritUseEnhancedMode;InheritVmId;InheritRenderingEngine;InheritUsername;" +
"InheritRDPAuthenticationLevel;InheritLoadBalanceInfo;InheritPreExtApp;InheritPostExtApp;InheritMacAddress;InheritUserField;" +
"InheritFavorite;InheritExtApp;InheritVNCCompression;InheritVNCEncoding;InheritVNCAuthMode;InheritVNCProxyType;InheritVNCProxyIP;" +
"InheritVNCProxyPort;InheritVNCProxyUsername;InheritVNCProxyPassword;InheritVNCColors;InheritVNCSmartSizeMode;InheritVNCViewOnly;" +
"InheritRDGatewayUsageMethod;InheritRDGatewayHostname;InheritRDGatewayUseConnectionCredentials;InheritRDGatewayUsername;" +
"InheritRDGatewayPassword;InheritRDGatewayDomain;InheritRDPAlertIdleTimeout;InheritRDPMinutesToIdleTimeout;InheritSoundQuality;" +
"InheritRedirectAudioCapture;InheritRdpVersion;InheritCredentialRecord");
}
private void SerializeNodesRecursive(ConnectionInfo node, StringBuilder sb)
{
var nodeAsContainer = node as ContainerInfo;
if (nodeAsContainer != null)
{
foreach (var child in nodeAsContainer.Children)
{
SerializeNodesRecursive(child, sb);
}
}
// dont serialize the root node
if (node is RootNodeInfo)
return;
SerializeConnectionInfo(node, sb);
}
private void SerializeConnectionInfo(ConnectionInfo con, StringBuilder sb)
{
sb.AppendLine();
sb.Append(FormatForCsv(con.Name))
.Append(FormatForCsv(con.ConstantID))
.Append(FormatForCsv(con.Parent?.ConstantID ?? ""))
.Append(FormatForCsv(con.GetTreeNodeType()))
.Append(FormatForCsv(con.Description))
.Append(FormatForCsv(con.Icon))
.Append(FormatForCsv(con.Panel))
.Append(FormatForCsv(con.Hostname))
.Append(FormatForCsv(con.Port))
.Append(FormatForCsv(con.VmId))
.Append(FormatForCsv(con.Protocol))
.Append(FormatForCsv(con.SSHTunnelConnectionName))
.Append(FormatForCsv(con.SSHOptions))
.Append(FormatForCsv(con.PuttySession))
.Append(FormatForCsv(con.UseConsoleSession))
.Append(FormatForCsv(con.UseCredSsp))
.Append(FormatForCsv(con.UseVmId))
.Append(FormatForCsv(con.UseEnhancedMode))
.Append(FormatForCsv(con.RenderingEngine))
.Append(FormatForCsv(con.RDPAuthenticationLevel))
.Append(FormatForCsv(con.LoadBalanceInfo))
.Append(FormatForCsv(con.Colors))
.Append(FormatForCsv(con.Resolution))
.Append(FormatForCsv(con.AutomaticResize))
.Append(FormatForCsv(con.DisplayWallpaper))
.Append(FormatForCsv(con.DisplayThemes))
.Append(FormatForCsv(con.EnableFontSmoothing))
.Append(FormatForCsv(con.EnableDesktopComposition))
.Append(FormatForCsv(con.DisableFullWindowDrag))
.Append(FormatForCsv(con.DisableMenuAnimations))
.Append(FormatForCsv(con.DisableCursorShadow))
.Append(FormatForCsv(con.DisableCursorBlinking))
.Append(FormatForCsv(con.CacheBitmaps))
.Append(FormatForCsv(con.RedirectDiskDrives))
.Append(FormatForCsv(con.RedirectPorts))
.Append(FormatForCsv(con.RedirectPrinters))
.Append(FormatForCsv(con.RedirectClipboard))
.Append(FormatForCsv(con.RedirectSmartCards))
.Append(FormatForCsv(con.RedirectSound))
.Append(FormatForCsv(con.RedirectKeys))
.Append(FormatForCsv(con.PreExtApp))
.Append(FormatForCsv(con.PostExtApp))
.Append(FormatForCsv(con.MacAddress))
.Append(FormatForCsv(con.UserField))
.Append(FormatForCsv(con.ExtApp))
.Append(FormatForCsv(con.Favorite))
.Append(FormatForCsv(con.VNCCompression))
.Append(FormatForCsv(con.VNCEncoding))
.Append(FormatForCsv(con.VNCAuthMode))
.Append(FormatForCsv(con.VNCProxyType))
.Append(FormatForCsv(con.VNCProxyIP))
.Append(FormatForCsv(con.VNCProxyPort))
.Append(FormatForCsv(con.VNCProxyUsername))
.Append(FormatForCsv(con.VNCProxyPassword))
.Append(FormatForCsv(con.VNCColors))
.Append(FormatForCsv(con.VNCSmartSizeMode))
.Append(FormatForCsv(con.VNCViewOnly))
.Append(FormatForCsv(con.RDGatewayUsageMethod))
.Append(FormatForCsv(con.RDGatewayHostname))
.Append(FormatForCsv(con.RDGatewayUseConnectionCredentials))
.Append(FormatForCsv(con.RDGatewayUsername))
.Append(FormatForCsv(con.RDGatewayPassword))
.Append(FormatForCsv(con.RDGatewayDomain))
.Append(FormatForCsv(con.RedirectAudioCapture))
.Append(FormatForCsv(con.RdpVersion))
.Append(FormatForCsv(con.CredentialRecordId));
if (!_saveFilter.SaveInheritance)
return;
sb.Append(FormatForCsv(con.Inheritance.CacheBitmaps))
.Append(FormatForCsv(con.Inheritance.Colors))
.Append(FormatForCsv(con.Inheritance.Description))
.Append(FormatForCsv(con.Inheritance.DisplayThemes))
.Append(FormatForCsv(con.Inheritance.DisplayWallpaper))
.Append(FormatForCsv(con.Inheritance.EnableFontSmoothing))
.Append(FormatForCsv(con.Inheritance.EnableDesktopComposition))
.Append(FormatForCsv(con.Inheritance.DisableFullWindowDrag))
.Append(FormatForCsv(con.Inheritance.DisableMenuAnimations))
.Append(FormatForCsv(con.Inheritance.DisableCursorShadow))
.Append(FormatForCsv(con.Inheritance.DisableCursorBlinking))
.Append(FormatForCsv(con.Inheritance.Domain))
.Append(FormatForCsv(con.Inheritance.Icon))
.Append(FormatForCsv(con.Inheritance.Panel))
.Append(FormatForCsv(con.Inheritance.Password))
.Append(FormatForCsv(con.Inheritance.Port))
.Append(FormatForCsv(con.Inheritance.Protocol))
.Append(FormatForCsv(con.Inheritance.SSHTunnelConnectionName))
.Append(FormatForCsv(con.Inheritance.SSHOptions))
.Append(FormatForCsv(con.Inheritance.PuttySession))
.Append(FormatForCsv(con.Inheritance.RedirectDiskDrives))
.Append(FormatForCsv(con.Inheritance.RedirectKeys))
.Append(FormatForCsv(con.Inheritance.RedirectPorts))
.Append(FormatForCsv(con.Inheritance.RedirectPrinters))
.Append(FormatForCsv(con.Inheritance.RedirectClipboard))
.Append(FormatForCsv(con.Inheritance.RedirectSmartCards))
.Append(FormatForCsv(con.Inheritance.RedirectSound))
.Append(FormatForCsv(con.Inheritance.Resolution))
.Append(FormatForCsv(con.Inheritance.AutomaticResize))
.Append(FormatForCsv(con.Inheritance.UseConsoleSession))
.Append(FormatForCsv(con.Inheritance.UseCredSsp))
.Append(FormatForCsv(con.Inheritance.UseVmId))
.Append(FormatForCsv(con.Inheritance.UseEnhancedMode))
.Append(FormatForCsv(con.Inheritance.VmId))
.Append(FormatForCsv(con.Inheritance.RenderingEngine))
.Append(FormatForCsv(con.Inheritance.Username))
.Append(FormatForCsv(con.Inheritance.RDPAuthenticationLevel))
.Append(FormatForCsv(con.Inheritance.LoadBalanceInfo))
.Append(FormatForCsv(con.Inheritance.PreExtApp))
.Append(FormatForCsv(con.Inheritance.PostExtApp))
.Append(FormatForCsv(con.Inheritance.MacAddress))
.Append(FormatForCsv(con.Inheritance.UserField))
.Append(FormatForCsv(con.Inheritance.Favorite))
.Append(FormatForCsv(con.Inheritance.ExtApp))
.Append(FormatForCsv(con.Inheritance.VNCCompression))
.Append(FormatForCsv(con.Inheritance.VNCEncoding))
.Append(FormatForCsv(con.Inheritance.VNCAuthMode))
.Append(FormatForCsv(con.Inheritance.VNCProxyType))
.Append(FormatForCsv(con.Inheritance.VNCProxyIP))
.Append(FormatForCsv(con.Inheritance.VNCProxyPort))
.Append(FormatForCsv(con.Inheritance.VNCProxyUsername))
.Append(FormatForCsv(con.Inheritance.VNCProxyPassword))
.Append(FormatForCsv(con.Inheritance.VNCColors))
.Append(FormatForCsv(con.Inheritance.VNCSmartSizeMode))
.Append(FormatForCsv(con.Inheritance.VNCViewOnly))
.Append(FormatForCsv(con.Inheritance.RDGatewayUsageMethod))
.Append(FormatForCsv(con.Inheritance.RDGatewayHostname))
.Append(FormatForCsv(con.Inheritance.RDGatewayUseConnectionCredentials))
.Append(FormatForCsv(con.Inheritance.RDGatewayUsername))
.Append(FormatForCsv(con.Inheritance.RDGatewayPassword))
.Append(FormatForCsv(con.Inheritance.RDGatewayDomain))
.Append(FormatForCsv(con.Inheritance.RDPAlertIdleTimeout))
.Append(FormatForCsv(con.Inheritance.RDPMinutesToIdleTimeout))
.Append(FormatForCsv(con.Inheritance.SoundQuality))
.Append(FormatForCsv(con.Inheritance.RedirectAudioCapture))
.Append(FormatForCsv(con.Inheritance.RdpVersion))
.Append(FormatForCsv(con.Inheritance.CredentialId));
}
private string FormatForCsv(object value)
{
var cleanedString = value.ToString().Replace(";", "");
return cleanedString + ";";
}
}
}

View File

@@ -1,226 +0,0 @@
#region
using System;
using System.Collections.Generic;
using System.Linq;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Tree;
#endregion
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv.RemoteDesktopManager;
/// <summary>
/// Import of connections from the Remote Desktop Manager (RDM) in a CSV file format
/// </summary>
public partial class CsvConnectionsDeserializerRdmFormat : IDeserializer<string, ConnectionTreeModel>
{
private readonly List<RemoteDesktopManagerConnectionInfo> _connectionTypes;
private readonly HashSet<string> _groups;
public CsvConnectionsDeserializerRdmFormat()
{
_connectionTypes = new List<RemoteDesktopManagerConnectionInfo>
{
new(ProtocolType.RDP, "RDP (Microsoft Remote Desktop)", 3389, "Remote Desktop"),
new(ProtocolType.SSH2, "SSH Shell", 22, "SSH")
};
_groups = new HashSet<string>();
Containers = new List<ContainerInfo>();
}
private List<ContainerInfo> Containers { get; }
/// <summary>
/// Deserializes the CSV file into a <see cref="ConnectionTreeModel" />
/// </summary>
/// <param name="serializedData">Data from the CSV file</param>
/// <returns></returns>
public ConnectionTreeModel Deserialize(string serializedData)
{
var lines = serializedData.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
var csvHeaders = new List<string>();
var connections = new List<(ConnectionInfo, string)>(); // (ConnectionInfo, group)
for (var lineNumber = 0; lineNumber < lines.Length; lineNumber++)
{
var line = lines[lineNumber].Split(',');
if (lineNumber == 0)
{
csvHeaders = line.ToList();
}
else
{
var (connectionInfo, group) = ParseConnectionInfo(csvHeaders, line);
if (connectionInfo == default) continue;
connections.Add((connectionInfo, group));
}
}
var connectionTreeModel = new ConnectionTreeModel();
var unsortedConnections = new ContainerInfo { Name = "Unsorted" };
foreach (var containerInfo in Containers) connectionTreeModel.AddRootNode(containerInfo);
var allChildren = Containers.SelectMany(x => x.GetRecursiveChildList().Select(y => (ContainerInfo)y)).ToList();
foreach (var (connection, path) in connections)
if (string.IsNullOrEmpty(path))
{
unsortedConnections.AddChild(connection);
}
else
{
var container = allChildren.FirstOrDefault(x => x.ConstantID == path);
if (container == default) continue;
container.AddChild(connection);
}
if (unsortedConnections.HasChildren())
connectionTreeModel.AddRootNode(unsortedConnections);
return connectionTreeModel;
}
/// <summary>
/// Parses a line from the CSV file and returns <see cref="ConnectionInfo" />
/// </summary>
/// <param name="headers">CSV Headers</param>
/// <param name="connectionCsv">CSV Columns</param>
/// <returns></returns>
private (ConnectionInfo connectionInfo, string) ParseConnectionInfo(IList<string> headers, IReadOnlyList<string> connectionCsv)
{
if (headers.Count != connectionCsv.Count) return default;
var hostString = connectionCsv[headers.IndexOf("Host")].Trim();
if (string.IsNullOrEmpty(hostString)) return default;
var hostType = Uri.CheckHostName(hostString);
if (hostType == UriHostNameType.Unknown) return default;
var connectionTypeString = connectionCsv[headers.IndexOf("ConnectionType")];
if (string.IsNullOrEmpty(connectionTypeString)) return default;
var connectionType = _connectionTypes.FirstOrDefault(x => x.Name == connectionTypeString);
if (connectionType == default) return default;
var portString = connectionCsv[headers.IndexOf("Port")] ?? connectionType.Port.ToString();
if (!int.TryParse(portString, out var port)) port = connectionType.Port;
var name = connectionCsv[headers.IndexOf("Name")];
var description = connectionCsv[headers.IndexOf("Description")];
var group = connectionCsv[headers.IndexOf("Group")];
var username = connectionCsv[headers.IndexOf("CredentialUserName")];
var domain = connectionCsv[headers.IndexOf("CredentialDomain")];
var password = connectionCsv[headers.IndexOf("CredentialPassword")];
var connectionInfo = new ConnectionInfo(Guid.NewGuid().ToString())
{
Name = name,
Hostname = hostString,
Port = port,
Username = username,
Password = password,
Domain = domain,
Icon = connectionType.IconName ?? "mRemoteNG",
Description = description,
Protocol = connectionType.Protocol
};
if (!string.IsNullOrEmpty(group))
if (group.Contains('\\'))
{
var groupParts = group.Split('\\').ToList();
var parentContainerName = groupParts[0];
var parentContainer = Containers.FirstOrDefault(x => x.Name == parentContainerName);
if (parentContainer == default)
{
parentContainer = new ContainerInfo(group) { Name = parentContainerName };
Containers.Add(parentContainer);
}
groupParts.RemoveAt(0);
AddChildrenRecursive(group, groupParts, parentContainer);
}
return string.IsNullOrEmpty(group) ? (connectionInfo, default) : (connectionInfo, group);
}
/// <summary>
/// Adds a child to a container recursively
/// </summary>
/// <param name="group">Full path of the RDM Grouping</param>
/// <param name="groupParts">Segements of the group path</param>
/// <param name="parentContainer">Parent container to add children to</param>
private void AddChildrenRecursive(string group, IList<string> groupParts, ContainerInfo parentContainer)
{
if (_groups.Contains(group)) return;
var groupCount = groupParts.Count;
while (groupCount > 0)
{
var childName = groupParts[0];
var newContainer = new ContainerInfo(group) { Name = childName };
var childrenNames = parentContainer.GetRecursiveChildList().Select(x => x.Name).ToList();
if (!childrenNames.Any())
{
groupCount = AddChild(parentContainer, newContainer, groupCount);
_groups.Add(group);
continue;
}
if (groupParts.Count > 1)
{
var childContainer = (ContainerInfo)parentContainer.Children.FirstOrDefault(x => x.Name == childName);
if (childContainer == default)
{
groupCount = AddChild(parentContainer, newContainer, groupCount);
continue;
}
AddChildrenRecursive(group, groupParts.Skip(1).ToList(), childContainer);
}
else
{
parentContainer.AddChild(newContainer);
_groups.Add(group);
}
groupCount--;
}
}
/// <summary>
/// Adds a child to a container and returns the remaining group count
/// </summary>
/// <param name="parentContainer">Parent container</param>
/// <param name="newContainer">New child container</param>
/// <param name="groupCount">Remaining group count</param>
/// <returns></returns>
private static int AddChild(ContainerInfo parentContainer, ContainerInfo newContainer, int groupCount)
{
parentContainer.AddChild(newContainer);
groupCount--;
return groupCount;
}
}
/// <summary>
/// Record of supported connection types
/// </summary>
/// <param name="Protocol">Procotol</param>
/// <param name="Name">Display Name</param>
/// <param name="Port">Default Port</param>
/// <param name="IconName">Icon Name</param>
internal sealed record RemoteDesktopManagerConnectionInfo(ProtocolType Protocol, string Name, int Port, string IconName);

View File

@@ -1,18 +0,0 @@
#region
using System;
using mRemoteNG.Connection;
#endregion
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv.RemoteDesktopManager;
public partial class CsvConnectionsDeserializerRdmFormat : ISerializer<ConnectionInfo, string>
{
public string Serialize(ConnectionInfo model)
{
throw new NotImplementedException();
}
public Version Version { get; }
}

View File

@@ -1,267 +1,274 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security;
using mRemoteNG.App;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.Http;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Container;
using mRemoteNG.Security;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
{
public class DataTableDeserializer : IDeserializer<DataTable, ConnectionTreeModel>
{
private readonly ICryptographyProvider _cryptographyProvider;
private readonly SecureString _decryptionKey;
public DataTableDeserializer(ICryptographyProvider cryptographyProvider, SecureString decryptionKey)
{
_cryptographyProvider = cryptographyProvider.ThrowIfNull(nameof(cryptographyProvider));
_decryptionKey = decryptionKey.ThrowIfNull(nameof(decryptionKey));
}
public ConnectionTreeModel Deserialize(DataTable table)
{
var connectionList = CreateNodesFromTable(table);
var connectionTreeModel = CreateNodeHierarchy(connectionList, table);
Runtime.ConnectionsService.IsConnectionsFileLoaded = true;
return connectionTreeModel;
}
private List<ConnectionInfo> CreateNodesFromTable(DataTable table)
{
var nodeList = new List<ConnectionInfo>();
foreach (DataRow row in table.Rows)
{
// ReSharper disable once SwitchStatementMissingSomeCases
switch ((string)row["Type"])
{
case "Connection":
nodeList.Add(DeserializeConnectionInfo(row));
break;
case "Container":
nodeList.Add(DeserializeContainerInfo(row));
break;
}
}
return nodeList;
}
private ConnectionInfo DeserializeConnectionInfo(DataRow row)
{
var connectionId = row["ConstantID"] as string ?? Guid.NewGuid().ToString();
var connectionInfo = new ConnectionInfo(connectionId);
PopulateConnectionInfoFromDatarow(row, connectionInfo);
return connectionInfo;
}
private ContainerInfo DeserializeContainerInfo(DataRow row)
{
var containerId = row["ConstantID"] as string ?? Guid.NewGuid().ToString();
var containerInfo = new ContainerInfo(containerId);
PopulateConnectionInfoFromDatarow(row, containerInfo);
return containerInfo;
}
private void PopulateConnectionInfoFromDatarow(DataRow dataRow, ConnectionInfo connectionInfo)
{
connectionInfo.Name = (string)dataRow["Name"];
// This throws a NPE - Parent is a connectionInfo object which will be null at this point.
// The Parent object is linked properly later in CreateNodeHierarchy()
//connectionInfo.Parent.ConstantID = (string)dataRow["ParentID"];
connectionInfo.Description = (string)dataRow["Description"];
connectionInfo.Icon = (string)dataRow["Icon"];
connectionInfo.Panel = (string)dataRow["Panel"];
connectionInfo.Username = (string)dataRow["Username"];
connectionInfo.Domain = (string)dataRow["Domain"];
connectionInfo.Password = DecryptValue((string)dataRow["Password"]);
connectionInfo.Hostname = (string)dataRow["Hostname"];
connectionInfo.VmId = (string)dataRow["VmId"];
connectionInfo.UseEnhancedMode = (bool)dataRow["UseEnhancedMode"];
connectionInfo.Protocol = (ProtocolType)Enum.Parse(typeof(ProtocolType), (string)dataRow["Protocol"]);
connectionInfo.SSHTunnelConnectionName = (string)dataRow["SSHTunnelConnectionName"];
connectionInfo.OpeningCommand = (string)dataRow["OpeningCommand"];
connectionInfo.SSHOptions = (string)dataRow["SSHOptions"];
connectionInfo.PuttySession = (string)dataRow["PuttySession"];
connectionInfo.Port = (int)dataRow["Port"];
connectionInfo.UseConsoleSession = (bool)dataRow["ConnectToConsole"];
connectionInfo.UseCredSsp = (bool)dataRow["UseCredSsp"];
connectionInfo.UseRestrictedAdmin = (bool)dataRow["UseRestrictedAdmin"];
connectionInfo.UseRCG = (bool)dataRow["UseRCG"];
connectionInfo.UseVmId = (bool)dataRow["UseVmId"];
connectionInfo.RenderingEngine = (HTTPBase.RenderingEngine)Enum.Parse(typeof(HTTPBase.RenderingEngine), (string)dataRow["RenderingEngine"]);
connectionInfo.RDPAuthenticationLevel = (AuthenticationLevel)Enum.Parse(typeof(AuthenticationLevel), (string)dataRow["RDPAuthenticationLevel"]);
connectionInfo.RDPMinutesToIdleTimeout = (int)dataRow["RDPMinutesToIdleTimeout"];
connectionInfo.RDPAlertIdleTimeout = (bool)dataRow["RDPAlertIdleTimeout"];
connectionInfo.LoadBalanceInfo = (string)dataRow["LoadBalanceInfo"];
connectionInfo.Colors = (RDPColors)Enum.Parse(typeof(RDPColors), (string)dataRow["Colors"]);
connectionInfo.Resolution = (RDPResolutions)Enum.Parse(typeof(RDPResolutions), (string)dataRow["Resolution"]);
connectionInfo.AutomaticResize = (bool)dataRow["AutomaticResize"];
connectionInfo.DisplayWallpaper = (bool)dataRow["DisplayWallpaper"];
connectionInfo.DisplayThemes = (bool)dataRow["DisplayThemes"];
connectionInfo.EnableFontSmoothing = (bool)dataRow["EnableFontSmoothing"];
connectionInfo.EnableDesktopComposition = (bool)dataRow["EnableDesktopComposition"];
connectionInfo.DisableFullWindowDrag = (bool)dataRow["DisableFullWindowDrag"];
connectionInfo.DisableMenuAnimations = (bool)dataRow["DisableMenuAnimations"];
connectionInfo.DisableCursorShadow = (bool)dataRow["DisableCursorShadow"];
connectionInfo.DisableCursorBlinking = (bool)dataRow["DisableCursorBlinking"];
connectionInfo.CacheBitmaps = (bool)dataRow["CacheBitmaps"];
connectionInfo.RedirectDiskDrives = (bool)dataRow["RedirectDiskDrives"];
connectionInfo.RedirectPorts = (bool)dataRow["RedirectPorts"];
connectionInfo.RedirectPrinters = (bool)dataRow["RedirectPrinters"];
connectionInfo.RedirectClipboard = (bool)dataRow["RedirectClipboard"];
connectionInfo.RedirectSmartCards = (bool)dataRow["RedirectSmartCards"];
connectionInfo.RedirectSound = (RDPSounds)Enum.Parse(typeof(RDPSounds), (string)dataRow["RedirectSound"]);
connectionInfo.SoundQuality = (RDPSoundQuality)Enum.Parse(typeof(RDPSoundQuality), (string)dataRow["SoundQuality"]);
connectionInfo.RedirectAudioCapture = (bool)dataRow["RedirectAudioCapture"];
connectionInfo.RDPStartProgram = (string)dataRow["StartProgram"];
connectionInfo.RDPStartProgramWorkDir = (string)dataRow["StartProgramWorkDir"];
connectionInfo.RedirectKeys = (bool)dataRow["RedirectKeys"];
connectionInfo.OpeningCommand = (string)dataRow["OpeningCommand"];
connectionInfo.PreExtApp = (string)dataRow["PreExtApp"];
connectionInfo.PostExtApp = (string)dataRow["PostExtApp"];
connectionInfo.MacAddress = (string)dataRow["MacAddress"];
connectionInfo.UserField = (string)dataRow["UserField"];
connectionInfo.ExtApp = (string)dataRow["ExtApp"];
connectionInfo.VNCCompression = (ProtocolVNC.Compression)Enum.Parse(typeof(ProtocolVNC.Compression), (string)dataRow["VNCCompression"]);
connectionInfo.VNCEncoding = (ProtocolVNC.Encoding)Enum.Parse(typeof(ProtocolVNC.Encoding), (string)dataRow["VNCEncoding"]);
connectionInfo.VNCAuthMode = (ProtocolVNC.AuthMode)Enum.Parse(typeof(ProtocolVNC.AuthMode), (string)dataRow["VNCAuthMode"]);
connectionInfo.VNCProxyType = (ProtocolVNC.ProxyType)Enum.Parse(typeof(ProtocolVNC.ProxyType), (string)dataRow["VNCProxyType"]);
connectionInfo.VNCProxyIP = (string)dataRow["VNCProxyIP"];
connectionInfo.VNCProxyPort = (int)dataRow["VNCProxyPort"];
connectionInfo.VNCProxyUsername = (string)dataRow["VNCProxyUsername"];
connectionInfo.VNCProxyPassword = DecryptValue((string)dataRow["VNCProxyPassword"]);
connectionInfo.VNCColors = (ProtocolVNC.Colors)Enum.Parse(typeof(ProtocolVNC.Colors), (string)dataRow["VNCColors"]);
connectionInfo.VNCSmartSizeMode = (ProtocolVNC.SmartSizeMode)Enum.Parse(typeof(ProtocolVNC.SmartSizeMode), (string)dataRow["VNCSmartSizeMode"]);
connectionInfo.VNCViewOnly = (bool)dataRow["VNCViewOnly"];
connectionInfo.RDGatewayUsageMethod = (RDGatewayUsageMethod)Enum.Parse(typeof(RDGatewayUsageMethod), (string)dataRow["RDGatewayUsageMethod"]);
connectionInfo.RDGatewayHostname = (string)dataRow["RDGatewayHostname"];
connectionInfo.RDGatewayUseConnectionCredentials = (RDGatewayUseConnectionCredentials)Enum.Parse(typeof(RDGatewayUseConnectionCredentials), (string)dataRow["RDGatewayUseConnectionCredentials"]);
connectionInfo.RDGatewayUsername = (string)dataRow["RDGatewayUsername"];
connectionInfo.RDGatewayPassword = DecryptValue((string)dataRow["RDGatewayPassword"]);
connectionInfo.RDGatewayDomain = (string)dataRow["RDGatewayDomain"];
if (!dataRow.IsNull("RdpVersion")) // table allows null values which must be handled
if (Enum.TryParse((string)dataRow["RdpVersion"], true, out RdpVersion rdpVersion))
connectionInfo.RdpVersion = rdpVersion;
connectionInfo.Inheritance.CacheBitmaps = (bool)dataRow["InheritCacheBitmaps"];
connectionInfo.Inheritance.Colors = (bool)dataRow["InheritColors"];
connectionInfo.Inheritance.Description = (bool)dataRow["InheritDescription"];
connectionInfo.Inheritance.DisplayThemes = (bool)dataRow["InheritDisplayThemes"];
connectionInfo.Inheritance.DisplayWallpaper = (bool)dataRow["InheritDisplayWallpaper"];
connectionInfo.Inheritance.EnableFontSmoothing = (bool)dataRow["InheritEnableFontSmoothing"];
connectionInfo.Inheritance.EnableDesktopComposition = (bool)dataRow["InheritEnableDesktopComposition"];
connectionInfo.Inheritance.DisableFullWindowDrag = (bool)dataRow["InheritDisableFullWindowDrag"];
connectionInfo.Inheritance.DisableMenuAnimations = (bool)dataRow["InheritDisableMenuAnimations"];
connectionInfo.Inheritance.DisableCursorShadow = (bool)dataRow["InheritDisableCursorShadow"];
connectionInfo.Inheritance.DisableCursorBlinking = (bool)dataRow["InheritDisableCursorBlinking"];
connectionInfo.Inheritance.Domain = (bool)dataRow["InheritDomain"];
connectionInfo.Inheritance.Icon = (bool)dataRow["InheritIcon"];
connectionInfo.Inheritance.Panel = (bool)dataRow["InheritPanel"];
connectionInfo.Inheritance.Password = (bool)dataRow["InheritPassword"];
connectionInfo.Inheritance.Port = (bool)dataRow["InheritPort"];
connectionInfo.Inheritance.Protocol = (bool)dataRow["InheritProtocol"];
connectionInfo.Inheritance.SSHTunnelConnectionName = (bool)dataRow["InheritSSHTunnelConnectionName"];
connectionInfo.Inheritance.OpeningCommand = (bool)dataRow["InheritOpeningCommand"];
connectionInfo.Inheritance.SSHOptions = (bool)dataRow["InheritSSHOptions"];
connectionInfo.Inheritance.PuttySession = (bool)dataRow["InheritPuttySession"];
connectionInfo.Inheritance.RedirectDiskDrives = (bool)dataRow["InheritRedirectDiskDrives"];
connectionInfo.Inheritance.RedirectKeys = (bool)dataRow["InheritRedirectKeys"];
connectionInfo.Inheritance.RedirectPorts = (bool)dataRow["InheritRedirectPorts"];
connectionInfo.Inheritance.RedirectPrinters = (bool)dataRow["InheritRedirectPrinters"];
connectionInfo.Inheritance.RedirectClipboard = (bool)dataRow["InheritRedirectClipboard"];
connectionInfo.Inheritance.RedirectSmartCards = (bool)dataRow["InheritRedirectSmartCards"];
connectionInfo.Inheritance.RedirectSound = (bool)dataRow["InheritRedirectSound"];
connectionInfo.Inheritance.SoundQuality = (bool)dataRow["InheritSoundQuality"];
connectionInfo.Inheritance.RedirectAudioCapture = (bool)dataRow["InheritRedirectAudioCapture"];
connectionInfo.Inheritance.Resolution = (bool)dataRow["InheritResolution"];
connectionInfo.Inheritance.AutomaticResize = (bool)dataRow["InheritAutomaticResize"];
connectionInfo.Inheritance.UseConsoleSession = (bool)dataRow["InheritUseConsoleSession"];
connectionInfo.Inheritance.UseCredSsp = (bool)dataRow["InheritUseCredSsp"];
connectionInfo.Inheritance.UseRestrictedAdmin = (bool)dataRow["InheritUseRestrictedAdmin"];
connectionInfo.Inheritance.UseRCG = (bool)dataRow["InheritUseRCG"];
connectionInfo.Inheritance.UseVmId = (bool)dataRow["InheritUseVmId"];
connectionInfo.Inheritance.UseEnhancedMode = (bool)dataRow["InheritUseEnhancedMode"];
connectionInfo.Inheritance.VmId = (bool)dataRow["InheritVmId"];
connectionInfo.Inheritance.RenderingEngine = (bool)dataRow["InheritRenderingEngine"];
connectionInfo.Inheritance.Username = (bool)dataRow["InheritUsername"];
connectionInfo.Inheritance.RDPAuthenticationLevel = (bool)dataRow["InheritRDPAuthenticationLevel"];
connectionInfo.Inheritance.RDPAlertIdleTimeout = (bool)dataRow["InheritRDPAlertIdleTimeout"];
connectionInfo.Inheritance.RDPMinutesToIdleTimeout = (bool)dataRow["InheritRDPMinutesToIdleTimeout"];
connectionInfo.Inheritance.LoadBalanceInfo = (bool)dataRow["InheritLoadBalanceInfo"];
connectionInfo.Inheritance.OpeningCommand = (bool)dataRow["InheritOpeningCommand"];
connectionInfo.Inheritance.PreExtApp = (bool)dataRow["InheritPreExtApp"];
connectionInfo.Inheritance.PostExtApp = (bool)dataRow["InheritPostExtApp"];
connectionInfo.Inheritance.MacAddress = (bool)dataRow["InheritMacAddress"];
connectionInfo.Inheritance.UserField = (bool)dataRow["InheritUserField"];
connectionInfo.Inheritance.ExtApp = (bool)dataRow["InheritExtApp"];
connectionInfo.Inheritance.VNCCompression = (bool)dataRow["InheritVNCCompression"];
connectionInfo.Inheritance.VNCEncoding = (bool)dataRow["InheritVNCEncoding"];
connectionInfo.Inheritance.VNCAuthMode = (bool)dataRow["InheritVNCAuthMode"];
connectionInfo.Inheritance.VNCProxyType = (bool)dataRow["InheritVNCProxyType"];
connectionInfo.Inheritance.VNCProxyIP = (bool)dataRow["InheritVNCProxyIP"];
connectionInfo.Inheritance.VNCProxyPort = (bool)dataRow["InheritVNCProxyPort"];
connectionInfo.Inheritance.VNCProxyUsername = (bool)dataRow["InheritVNCProxyUsername"];
connectionInfo.Inheritance.VNCProxyPassword = (bool)dataRow["InheritVNCProxyPassword"];
connectionInfo.Inheritance.VNCColors = (bool)dataRow["InheritVNCColors"];
connectionInfo.Inheritance.VNCSmartSizeMode = (bool)dataRow["InheritVNCSmartSizeMode"];
connectionInfo.Inheritance.VNCViewOnly = (bool)dataRow["InheritVNCViewOnly"];
connectionInfo.Inheritance.RDGatewayUsageMethod = (bool)dataRow["InheritRDGatewayUsageMethod"];
connectionInfo.Inheritance.RDGatewayHostname = (bool)dataRow["InheritRDGatewayHostname"];
connectionInfo.Inheritance.RDGatewayUseConnectionCredentials = (bool)dataRow["InheritRDGatewayUseConnectionCredentials"];
connectionInfo.Inheritance.RDGatewayUsername = (bool)dataRow["InheritRDGatewayUsername"];
connectionInfo.Inheritance.RDGatewayPassword = (bool)dataRow["InheritRDGatewayPassword"];
connectionInfo.Inheritance.RDGatewayDomain = (bool)dataRow["InheritRDGatewayDomain"];
connectionInfo.Inheritance.RdpVersion = (bool)dataRow["InheritRdpVersion"];
}
private string DecryptValue(string cipherText)
{
try
{
return _cryptographyProvider.Decrypt(cipherText, _decryptionKey);
}
catch (EncryptionException)
{
// value may not be encrypted
return cipherText;
}
}
private ConnectionTreeModel CreateNodeHierarchy(List<ConnectionInfo> connectionList, DataTable dataTable)
{
var connectionTreeModel = new ConnectionTreeModel();
var rootNode = new RootNodeInfo(RootNodeType.Connection, "0")
{
PasswordString = _decryptionKey.ConvertToUnsecureString()
};
connectionTreeModel.AddRootNode(rootNode);
foreach (DataRow row in dataTable.Rows)
{
var id = (string)row["ConstantID"];
var connectionInfo = connectionList.First(node => node.ConstantID == id);
var parentId = (string)row["ParentID"];
if (parentId == "0" || connectionList.All(node => node.ConstantID != parentId))
rootNode.AddChild(connectionInfo);
else
(connectionList.First(node => node.ConstantID == parentId) as ContainerInfo)?.AddChild(
connectionInfo);
}
return connectionTreeModel;
}
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.Http;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Container;
using mRemoteNG.Security;
using mRemoteNG.Tools;
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
{
public class DataTableDeserializer
{
private readonly ICryptographyProvider _cryptographyProvider;
private readonly SecureString _decryptionKey;
public DataTableDeserializer(ICryptographyProvider cryptographyProvider, SecureString decryptionKey)
{
_cryptographyProvider = cryptographyProvider.ThrowIfNull(nameof(cryptographyProvider));
_decryptionKey = decryptionKey.ThrowIfNull(nameof(decryptionKey));
}
public SerializationResult Deserialize(DataTable table)
{
var connectionList = CreateNodesFromTable(table);
var rootNodes = CreateNodeHierarchy(connectionList, table);
var serializationResult = new SerializationResult(rootNodes, new ConnectionToCredentialMap());
return serializationResult;
}
private List<ConnectionInfo> CreateNodesFromTable(DataTable table)
{
var nodeList = new List<ConnectionInfo>();
foreach (DataRow row in table.Rows)
{
// ReSharper disable once SwitchStatementMissingSomeCases
switch ((string)row["Type"])
{
case "Connection":
nodeList.Add(DeserializeConnectionInfo(row));
break;
case "Container":
nodeList.Add(DeserializeContainerInfo(row));
break;
}
}
return nodeList;
}
private ConnectionInfo DeserializeConnectionInfo(DataRow row)
{
var connectionId = row["ConstantID"] as string ?? Guid.NewGuid().ToString();
var connectionInfo = new ConnectionInfo(connectionId);
PopulateConnectionInfoFromDataRow(row, connectionInfo);
return connectionInfo;
}
private ContainerInfo DeserializeContainerInfo(DataRow row)
{
var containerId = row["ConstantID"] as string ?? Guid.NewGuid().ToString();
var containerInfo = new ContainerInfo(containerId);
PopulateConnectionInfoFromDataRow(row, containerInfo);
return containerInfo;
}
private void PopulateConnectionInfoFromDataRow(DataRow dataRow, ConnectionInfo connectionInfo)
{
connectionInfo.Name = (string)dataRow["Name"];
// This throws a NPE - Parent is a connectionInfo object which will be null at this point.
// The Parent object is linked properly later in CreateNodeHierarchy()
//connectionInfo.Parent.ConstantID = (string)dataRow["ParentID"];
connectionInfo.Description = (string)dataRow["Description"];
connectionInfo.Icon = (string)dataRow["Icon"];
connectionInfo.Panel = (string)dataRow["Panel"];
// TODO: harvest
if (dataRow.Table.Columns.Contains("Username"))
connectionInfo.Username = (string)dataRow["Username"];
if (dataRow.Table.Columns.Contains("DomainName"))
connectionInfo.Domain = (string)dataRow["DomainName"];
if (dataRow.Table.Columns.Contains("Password"))
connectionInfo.Password = DecryptValue((string)dataRow["Password"]);
connectionInfo.Hostname = (string)dataRow["Hostname"];
connectionInfo.VmId = (string)dataRow["VmId"];
connectionInfo.UseEnhancedMode = (bool)dataRow["UseEnhancedMode"];
connectionInfo.Protocol = (ProtocolType)Enum.Parse(typeof(ProtocolType), (string)dataRow["Protocol"]);
connectionInfo.SSHTunnelConnectionName = (string)dataRow["SSHTunnelConnectionName"];
connectionInfo.SSHOptions = (string)dataRow["SSHOptions"];
connectionInfo.PuttySession = (string)dataRow["PuttySession"];
connectionInfo.Port = (int)dataRow["Port"];
connectionInfo.UseConsoleSession = (bool)dataRow["ConnectToConsole"];
connectionInfo.UseCredSsp = (bool)dataRow["UseCredSsp"];
connectionInfo.UseVmId = (bool)dataRow["UseVmId"];
connectionInfo.RenderingEngine = (HTTPBase.RenderingEngine)Enum.Parse(typeof(HTTPBase.RenderingEngine),
(string)dataRow["RenderingEngine"]);
connectionInfo.RDPAuthenticationLevel =
(AuthenticationLevel)Enum.Parse(typeof(AuthenticationLevel),
(string)dataRow["RDPAuthenticationLevel"]);
connectionInfo.RDPMinutesToIdleTimeout = (int)dataRow["RDPMinutesToIdleTimeout"];
connectionInfo.RDPAlertIdleTimeout = (bool)dataRow["RDPAlertIdleTimeout"];
connectionInfo.LoadBalanceInfo = (string)dataRow["LoadBalanceInfo"];
connectionInfo.Colors =
(RDPColors)Enum.Parse(typeof(RDPColors), (string)dataRow["Colors"]);
connectionInfo.Resolution =
(RDPResolutions)Enum.Parse(typeof(RDPResolutions),
(string)dataRow["Resolution"]);
connectionInfo.AutomaticResize = (bool)dataRow["AutomaticResize"];
connectionInfo.DisplayWallpaper = (bool)dataRow["DisplayWallpaper"];
connectionInfo.DisplayThemes = (bool)dataRow["DisplayThemes"];
connectionInfo.EnableFontSmoothing = (bool)dataRow["EnableFontSmoothing"];
connectionInfo.EnableDesktopComposition = (bool)dataRow["EnableDesktopComposition"];
connectionInfo.DisableFullWindowDrag = (bool)dataRow["DisableFullWindowDrag"];
connectionInfo.DisableMenuAnimations = (bool)dataRow["DisableMenuAnimations"];
connectionInfo.DisableCursorShadow = (bool)dataRow["DisableCursorShadow"];
connectionInfo.DisableCursorBlinking = (bool)dataRow["DisableCursorBlinking"];
connectionInfo.CacheBitmaps = (bool)dataRow["CacheBitmaps"];
connectionInfo.RedirectDiskDrives = (bool)dataRow["RedirectDiskDrives"];
connectionInfo.RedirectPorts = (bool)dataRow["RedirectPorts"];
connectionInfo.RedirectPrinters = (bool)dataRow["RedirectPrinters"];
connectionInfo.RedirectClipboard = (bool)dataRow["RedirectClipboard"];
connectionInfo.RedirectSmartCards = (bool)dataRow["RedirectSmartCards"];
connectionInfo.RedirectSound =
(RDPSounds)Enum.Parse(typeof(RDPSounds), (string)dataRow["RedirectSound"]);
connectionInfo.SoundQuality = (RDPSoundQuality)Enum.Parse(typeof(RDPSoundQuality),
(string)dataRow["SoundQuality"]);
connectionInfo.RedirectAudioCapture = (bool)dataRow["RedirectAudioCapture"];
connectionInfo.RedirectKeys = (bool)dataRow["RedirectKeys"];
connectionInfo.PreExtApp = (string)dataRow["PreExtApp"];
connectionInfo.PostExtApp = (string)dataRow["PostExtApp"];
connectionInfo.MacAddress = (string)dataRow["MacAddress"];
connectionInfo.UserField = (string)dataRow["UserField"];
connectionInfo.ExtApp = (string)dataRow["ExtApp"];
connectionInfo.VNCCompression = (ProtocolVNC.Compression)Enum.Parse(typeof(ProtocolVNC.Compression),
(string)dataRow["VNCCompression"]);
connectionInfo.VNCEncoding =
(ProtocolVNC.Encoding)Enum.Parse(typeof(ProtocolVNC.Encoding), (string)dataRow["VNCEncoding"]);
connectionInfo.VNCAuthMode =
(ProtocolVNC.AuthMode)Enum.Parse(typeof(ProtocolVNC.AuthMode), (string)dataRow["VNCAuthMode"]);
connectionInfo.VNCProxyType =
(ProtocolVNC.ProxyType)Enum.Parse(typeof(ProtocolVNC.ProxyType), (string)dataRow["VNCProxyType"]);
connectionInfo.VNCProxyIP = (string)dataRow["VNCProxyIP"];
connectionInfo.VNCProxyPort = (int)dataRow["VNCProxyPort"];
connectionInfo.VNCProxyUsername = (string)dataRow["VNCProxyUsername"];
connectionInfo.VNCProxyPassword = DecryptValue((string)dataRow["VNCProxyPassword"]);
connectionInfo.VNCColors =
(ProtocolVNC.Colors)Enum.Parse(typeof(ProtocolVNC.Colors), (string)dataRow["VNCColors"]);
connectionInfo.VNCSmartSizeMode = (ProtocolVNC.SmartSizeMode)Enum.Parse(typeof(ProtocolVNC.SmartSizeMode),
(string)dataRow
["VNCSmartSizeMode"]);
connectionInfo.VNCViewOnly = (bool)dataRow["VNCViewOnly"];
connectionInfo.RDGatewayUsageMethod =
(RDGatewayUsageMethod)Enum.Parse(typeof(RDGatewayUsageMethod),
(string)dataRow["RDGatewayUsageMethod"]);
connectionInfo.RDGatewayHostname = (string)dataRow["RDGatewayHostname"];
connectionInfo.RDGatewayUseConnectionCredentials =
(RDGatewayUseConnectionCredentials)Enum.Parse(typeof(RDGatewayUseConnectionCredentials),
(string)dataRow["RDGatewayUseConnectionCredentials"]);
connectionInfo.RDGatewayUsername = (string)dataRow["RDGatewayUsername"];
connectionInfo.RDGatewayPassword = DecryptValue((string)dataRow["RDGatewayPassword"]);
connectionInfo.RDGatewayDomain = (string)dataRow["RDGatewayDomain"];
if (!dataRow.IsNull("RdpVersion")) // table allows null values which must be handled
if (Enum.TryParse((string)dataRow["RdpVersion"], true, out RdpVersion rdpVersion))
connectionInfo.RdpVersion = rdpVersion;
connectionInfo.Inheritance.CacheBitmaps = (bool)dataRow["InheritCacheBitmaps"];
connectionInfo.Inheritance.Colors = (bool)dataRow["InheritColors"];
connectionInfo.Inheritance.Description = (bool)dataRow["InheritDescription"];
connectionInfo.Inheritance.DisplayThemes = (bool)dataRow["InheritDisplayThemes"];
connectionInfo.Inheritance.DisplayWallpaper = (bool)dataRow["InheritDisplayWallpaper"];
connectionInfo.Inheritance.EnableFontSmoothing = (bool)dataRow["InheritEnableFontSmoothing"];
connectionInfo.Inheritance.EnableDesktopComposition = (bool)dataRow["InheritEnableDesktopComposition"];
connectionInfo.Inheritance.DisableFullWindowDrag = (bool)dataRow["InheritDisableFullWindowDrag"];
connectionInfo.Inheritance.DisableMenuAnimations = (bool)dataRow["InheritDisableMenuAnimations"];
connectionInfo.Inheritance.DisableCursorShadow = (bool)dataRow["InheritDisableCursorShadow"];
connectionInfo.Inheritance.DisableCursorBlinking = (bool)dataRow["InheritDisableCursorBlinking"];
connectionInfo.Inheritance.Domain = (bool)dataRow["InheritDomain"];
connectionInfo.Inheritance.Icon = (bool)dataRow["InheritIcon"];
connectionInfo.Inheritance.Panel = (bool)dataRow["InheritPanel"];
connectionInfo.Inheritance.Port = (bool)dataRow["InheritPort"];
connectionInfo.Inheritance.Protocol = (bool)dataRow["InheritProtocol"];
connectionInfo.Inheritance.SSHTunnelConnectionName = (bool)dataRow["InheritSSHTunnelConnectionName"];
connectionInfo.Inheritance.SSHOptions = (bool)dataRow["InheritSSHOptions"];
connectionInfo.Inheritance.PuttySession = (bool)dataRow["InheritPuttySession"];
connectionInfo.Inheritance.RedirectDiskDrives = (bool)dataRow["InheritRedirectDiskDrives"];
connectionInfo.Inheritance.RedirectKeys = (bool)dataRow["InheritRedirectKeys"];
connectionInfo.Inheritance.RedirectPorts = (bool)dataRow["InheritRedirectPorts"];
connectionInfo.Inheritance.RedirectPrinters = (bool)dataRow["InheritRedirectPrinters"];
connectionInfo.Inheritance.RedirectClipboard = (bool)dataRow["InheritRedirectClipboard"];
connectionInfo.Inheritance.RedirectSmartCards = (bool)dataRow["InheritRedirectSmartCards"];
connectionInfo.Inheritance.RedirectSound = (bool)dataRow["InheritRedirectSound"];
connectionInfo.Inheritance.SoundQuality = (bool)dataRow["InheritSoundQuality"];
connectionInfo.Inheritance.RedirectAudioCapture = (bool)dataRow["InheritRedirectAudioCapture"];
connectionInfo.Inheritance.Resolution = (bool)dataRow["InheritResolution"];
connectionInfo.Inheritance.AutomaticResize = (bool)dataRow["InheritAutomaticResize"];
connectionInfo.Inheritance.UseConsoleSession = (bool)dataRow["InheritUseConsoleSession"];
connectionInfo.Inheritance.UseCredSsp = (bool)dataRow["InheritUseCredSsp"];
connectionInfo.Inheritance.UseVmId = (bool)dataRow["InheritUseVmId"];
connectionInfo.Inheritance.UseEnhancedMode = (bool)dataRow["InheritUseEnhancedMode"];
connectionInfo.Inheritance.VmId = (bool)dataRow["InheritVmId"];
connectionInfo.Inheritance.RenderingEngine = (bool)dataRow["InheritRenderingEngine"];
connectionInfo.Inheritance.Username = (bool)dataRow["InheritUsername"];
connectionInfo.Inheritance.RDPAuthenticationLevel = (bool)dataRow["InheritRDPAuthenticationLevel"];
connectionInfo.Inheritance.RDPAlertIdleTimeout = (bool)dataRow["InheritRDPAlertIdleTimeout"];
connectionInfo.Inheritance.RDPMinutesToIdleTimeout = (bool)dataRow["InheritRDPMinutesToIdleTimeout"];
connectionInfo.Inheritance.LoadBalanceInfo = (bool)dataRow["InheritLoadBalanceInfo"];
connectionInfo.Inheritance.PreExtApp = (bool)dataRow["InheritPreExtApp"];
connectionInfo.Inheritance.PostExtApp = (bool)dataRow["InheritPostExtApp"];
connectionInfo.Inheritance.MacAddress = (bool)dataRow["InheritMacAddress"];
connectionInfo.Inheritance.UserField = (bool)dataRow["InheritUserField"];
connectionInfo.Inheritance.ExtApp = (bool)dataRow["InheritExtApp"];
connectionInfo.Inheritance.VNCCompression = (bool)dataRow["InheritVNCCompression"];
connectionInfo.Inheritance.VNCEncoding = (bool)dataRow["InheritVNCEncoding"];
connectionInfo.Inheritance.VNCAuthMode = (bool)dataRow["InheritVNCAuthMode"];
connectionInfo.Inheritance.VNCProxyType = (bool)dataRow["InheritVNCProxyType"];
connectionInfo.Inheritance.VNCProxyIP = (bool)dataRow["InheritVNCProxyIP"];
connectionInfo.Inheritance.VNCProxyPort = (bool)dataRow["InheritVNCProxyPort"];
connectionInfo.Inheritance.VNCProxyUsername = (bool)dataRow["InheritVNCProxyUsername"];
connectionInfo.Inheritance.VNCProxyPassword = (bool)dataRow["InheritVNCProxyPassword"];
connectionInfo.Inheritance.VNCColors = (bool)dataRow["InheritVNCColors"];
connectionInfo.Inheritance.VNCSmartSizeMode = (bool)dataRow["InheritVNCSmartSizeMode"];
connectionInfo.Inheritance.VNCViewOnly = (bool)dataRow["InheritVNCViewOnly"];
connectionInfo.Inheritance.RDGatewayUsageMethod = (bool)dataRow["InheritRDGatewayUsageMethod"];
connectionInfo.Inheritance.RDGatewayHostname = (bool)dataRow["InheritRDGatewayHostname"];
connectionInfo.Inheritance.RDGatewayUseConnectionCredentials =
(bool)dataRow["InheritRDGatewayUseConnectionCredentials"];
connectionInfo.Inheritance.RDGatewayUsername = (bool)dataRow["InheritRDGatewayUsername"];
connectionInfo.Inheritance.RDGatewayPassword = (bool)dataRow["InheritRDGatewayPassword"];
connectionInfo.Inheritance.RDGatewayDomain = (bool)dataRow["InheritRDGatewayDomain"];
connectionInfo.Inheritance.RdpVersion = (bool)dataRow["InheritRdpVersion"];
}
private string DecryptValue(string cipherText)
{
try
{
return _cryptographyProvider.Decrypt(cipherText, _decryptionKey);
}
catch (EncryptionException)
{
// value may not be encrypted
return cipherText;
}
}
private List<ConnectionInfo> CreateNodeHierarchy(IReadOnlyCollection<ConnectionInfo> connectionList, DataTable dataTable)
{
var rootNodes = new List<ConnectionInfo>();
foreach (DataRow row in dataTable.Rows)
{
var id = (string) row["ConstantID"];
var connectionInfo = connectionList.First(node => node.ConstantID == id);
var parentId = (string) row["ParentID"];
if (parentId == "0" || connectionList.All(node => node.ConstantID != parentId))
rootNodes.Add(connectionInfo);
else
(connectionList.First(node => node.ConstantID == parentId) as ContainerInfo)?.AddChild(connectionInfo);
}
return rootNodes;
}
}
}

View File

@@ -80,9 +80,9 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
if (rootTreeNode != null)
{
cmd = databaseConnector.DbCommand(
"INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES('" +
MiscTools.PrepareValueForDB(rootTreeNode.Name) + "', 0, '" + strProtected + "','" +
ConnectionsFileInfo.ConnectionFileVersion.ToString() + "')");
"INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES(\'" +
MiscTools.PrepareValueForDB(rootTreeNode.Name) + "\', 0, \'" + strProtected + "\'," +
ConnectionsFileInfo.ConnectionFileVersion.ToString(CultureInfo.InvariantCulture) + ")");
cmd.ExecuteNonQuery();
}
else

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Security;
using System.Xml.Linq;
using mRemoteNG.Connection;
@@ -66,7 +66,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("Hostname", connectionInfo.Hostname));
element.Add(new XAttribute("Protocol", connectionInfo.Protocol));
element.Add(new XAttribute("SSHTunnelConnectionName", connectionInfo.SSHTunnelConnectionName));
element.Add(new XAttribute("OpeningCommand", connectionInfo.OpeningCommand));
element.Add(new XAttribute("SSHOptions", connectionInfo.SSHOptions));
element.Add(new XAttribute("PuttySession", connectionInfo.PuttySession));
element.Add(new XAttribute("Port", connectionInfo.Port));
@@ -99,7 +98,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("MacAddress", connectionInfo.MacAddress));
element.Add(new XAttribute("UserField", connectionInfo.UserField));
element.Add(new XAttribute("ExtApp", connectionInfo.ExtApp));
element.Add(new XAttribute("StartProgram", connectionInfo.RDPStartProgram));
element.Add(new XAttribute("StartProgram", connectionInfo.StartProgram));
element.Add(new XAttribute("VNCCompression", connectionInfo.VNCCompression));
element.Add(new XAttribute("VNCEncoding", connectionInfo.VNCEncoding));
element.Add(new XAttribute("VNCAuthMode", connectionInfo.VNCAuthMode));
@@ -169,8 +168,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("InheritProtocol", inheritance.Protocol.ToString().ToLowerInvariant()));
if (inheritance.SSHTunnelConnectionName)
element.Add(new XAttribute("InheritSSHTunnelConnectionName", inheritance.SSHTunnelConnectionName.ToString().ToLowerInvariant()));
if (inheritance.OpeningCommand)
element.Add(new XAttribute("InheritOpeningCommand", inheritance.OpeningCommand.ToString().ToLowerInvariant()));
if (inheritance.SSHOptions)
element.Add(new XAttribute("InheritSSHOptions", inheritance.SSHOptions.ToString().ToLowerInvariant()));
if (inheritance.PuttySession)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Security;
using System.Xml.Linq;
using mRemoteNG.App;
@@ -76,7 +76,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("Protocol", connectionInfo.Protocol));
element.Add(new XAttribute("RdpVersion", connectionInfo.RdpVersion.ToString().ToLowerInvariant()));
element.Add(new XAttribute("SSHTunnelConnectionName", connectionInfo.SSHTunnelConnectionName));
element.Add(new XAttribute("OpeningCommand", connectionInfo.OpeningCommand));
element.Add(new XAttribute("SSHOptions", connectionInfo.SSHOptions));
element.Add(new XAttribute("PuttySession", connectionInfo.PuttySession));
element.Add(new XAttribute("Port", connectionInfo.Port));
@@ -115,8 +114,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("UserField", connectionInfo.UserField));
element.Add(new XAttribute("Favorite", connectionInfo.Favorite));
element.Add(new XAttribute("ExtApp", connectionInfo.ExtApp));
element.Add(new XAttribute("StartProgram", connectionInfo.RDPStartProgram));
element.Add(new XAttribute("StartProgramWorkDir", connectionInfo.RDPStartProgramWorkDir));
element.Add(new XAttribute("StartProgram", connectionInfo.StartProgram));
element.Add(new XAttribute("VNCCompression", connectionInfo.VNCCompression));
element.Add(new XAttribute("VNCEncoding", connectionInfo.VNCEncoding));
element.Add(new XAttribute("VNCAuthMode", connectionInfo.VNCAuthMode));
@@ -150,13 +148,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(_saveFilter.SaveDomain
? new XAttribute("RDGatewayDomain", connectionInfo.RDGatewayDomain)
: new XAttribute("RDGatewayDomain", ""));
element.Add(new XAttribute("UseRCG", connectionInfo.UseRCG));
element.Add(new XAttribute("UseRestrictedAdmin", connectionInfo.UseRestrictedAdmin));
element.Add(new XAttribute("UserViaAPI", connectionInfo.UserViaAPI));
element.Add(new XAttribute("EC2InstanceId", connectionInfo.EC2InstanceId));
element.Add(new XAttribute("EC2Region", connectionInfo.EC2Region));
}
private void SetInheritanceAttributes(XContainer element, IInheritable connectionInfo)
@@ -203,8 +194,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("InheritRdpVersion", inheritance.RdpVersion.ToString().ToLowerInvariant()));
if (inheritance.SSHTunnelConnectionName)
element.Add(new XAttribute("InheritSSHTunnelConnectionName", inheritance.SSHTunnelConnectionName.ToString().ToLowerInvariant()));
if (inheritance.OpeningCommand)
element.Add(new XAttribute("InheritOpeningCommand", inheritance.OpeningCommand.ToString().ToLowerInvariant()));
if (inheritance.SSHOptions)
element.Add(new XAttribute("InheritSSHOptions", inheritance.SSHOptions.ToString().ToLowerInvariant()));
if (inheritance.PuttySession)
@@ -299,12 +288,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
element.Add(new XAttribute("InheritUseVmId", inheritance.UseVmId.ToString().ToLowerInvariant()));
if (inheritance.UseEnhancedMode)
element.Add(new XAttribute("InheritUseEnhancedMode", inheritance.UseEnhancedMode.ToString().ToLowerInvariant()));
if (inheritance.UserViaAPI)
element.Add(new XAttribute("InheritUserViaAPI", inheritance.UserViaAPI.ToString().ToLowerInvariant()));
if (inheritance.UseRCG)
element.Add(new XAttribute("InheritUseRCG", inheritance.UseRCG.ToString().ToLowerInvariant()));
if (inheritance.UseRestrictedAdmin)
element.Add(new XAttribute("InheritUseRestrictedAdmin", inheritance.UseRestrictedAdmin.ToString().ToLowerInvariant()));
}
}
}

View File

@@ -10,7 +10,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
{
public ISerializer<ConnectionInfo, string> Build(
ICryptographyProvider cryptographyProvider,
ConnectionTreeModel connectionTreeModel,
IConnectionTreeModel connectionTreeModel,
SaveFilter saveFilter = null,
bool useFullEncryption = false)
{

View File

@@ -12,8 +12,7 @@ using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
{
public class XmlConnectionsSerializer : ISerializer<ConnectionTreeModel, string>,
ISerializer<ConnectionInfo, string>
public class XmlConnectionsSerializer : ISerializer<IConnectionTreeModel,string>, ISerializer<ConnectionInfo, string>
{
private readonly ICryptographyProvider _cryptographyProvider;
private readonly ISerializer<ConnectionInfo, XElement> _connectionNodeSerializer;
@@ -28,7 +27,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
_connectionNodeSerializer = connectionNodeSerializer;
}
public string Serialize(ConnectionTreeModel connectionTreeModel)
public string Serialize(IConnectionTreeModel connectionTreeModel)
{
var rootNode = (RootNodeInfo)connectionTreeModel.RootNodes.First(node => node is RootNodeInfo);
return SerializeConnectionsData(rootNode);

View File

@@ -9,29 +9,38 @@ namespace mRemoteNG.Config.Serializers.CredentialProviderSerializer
{
public class CredentialRepositoryListDeserializer
{
private readonly ISecureSerializer<IEnumerable<ICredentialRecord>, string> _serializer;
private readonly ISecureDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
public CredentialRepositoryListDeserializer(
ISecureSerializer<IEnumerable<ICredentialRecord>, string> serializer,
ISecureDeserializer<string, IEnumerable<ICredentialRecord>> deserializer)
public IEnumerable<ICredentialRepository> Deserialize(string xml, IEnumerable<ICredentialRepositoryFactory> factories)
{
if (serializer == null)
throw new ArgumentNullException(nameof(serializer));
if (deserializer == null)
throw new ArgumentNullException(nameof(deserializer));
if (string.IsNullOrEmpty(xml))
return new ICredentialRepository[0];
_serializer = serializer;
_deserializer = deserializer;
}
public IEnumerable<ICredentialRepository> Deserialize(string xml)
{
if (string.IsNullOrEmpty(xml)) return new ICredentialRepository[0];
var xdoc = XDocument.Parse(xml);
var repoEntries = xdoc.Descendants("CredentialRepository");
var xmlRepoFactory = new XmlCredentialRepositoryFactory(_serializer, _deserializer);
return repoEntries.Select(xmlRepoFactory.Build);
return repoEntries
.Select(ParseConfigEntries)
.Select(config =>
factories
.FirstOrDefault(f => string.Equals(f.SupportsConfigType, config.TypeName))?
.Build(config));
}
public ICredentialRepositoryConfig ParseConfigEntries(XElement repositoryXElement)
{
var stringId = repositoryXElement.Attribute("Id")?.Value;
Guid.TryParse(stringId, out var id);
if (id.Equals(Guid.Empty))
id = Guid.NewGuid();
var config = new CredentialRepositoryConfig(id)
{
TypeName = repositoryXElement.Attribute("TypeName")?.Value,
Title = repositoryXElement.Attribute("Title")?.Value,
Source = repositoryXElement.Attribute("Source")?.Value
};
return config;
}
}
}

View File

@@ -0,0 +1,51 @@
using mRemoteNG.Connection;
using mRemoteNG.Credential;
using System;
using System.Collections.Generic;
using System.Linq;
namespace mRemoteNG.Config.Serializers.CredentialSerializer
{
public class CredentialHarvester
{
private readonly IEqualityComparer<ICredentialRecord> _credentialComparer = new CredentialDomainUserPasswordComparer();
/// <summary>
/// Maps a <see cref="ConnectionInfo"/> (by its id) to the <see cref="ICredentialRecord"/>
/// object that was harvested
/// </summary>
/// <param name="harvestConfig"></param>
public ConnectionToCredentialMap Harvest<T>(HarvestConfig<T> harvestConfig)
{
if (harvestConfig == null)
throw new ArgumentNullException(nameof(harvestConfig));
var credentialMap = new ConnectionToCredentialMap();
foreach (var element in harvestConfig.ItemEnumerator())
{
var newCredential = new CredentialRecord
{
Title = harvestConfig.TitleSelector(element),
Username = harvestConfig.UsernameSelector(element),
Domain = harvestConfig.DomainSelector(element),
Password = harvestConfig.PasswordSelector(element)
};
if (!EntryHasSomeCredentialData(newCredential))
continue;
var connectionId = harvestConfig.ConnectionGuidSelector(element);
var existingCredential = credentialMap.Values.FirstOrDefault(record => _credentialComparer.Equals(newCredential, record));
credentialMap.Add(connectionId, existingCredential ?? newCredential);
}
return credentialMap;
}
private bool EntryHasSomeCredentialData(ICredentialRecord e)
{
return !_credentialComparer.Equals(e, new NullCredentialRecord());
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Security;
namespace mRemoteNG.Config.Serializers.CredentialSerializer
{
/// <summary>
/// Configuration for the <see cref="CredentialHarvester"/> to allow it to
/// iterate over and select values from any arbitrary data type. Each element
/// of type <see cref="T"/> represents an object that contains intermixed
/// connection and credential data.
/// </summary>
/// <typeparam name="T"></typeparam>
public class HarvestConfig<T>
{
/// <summary>
/// This will be called to produce a list of all objects
/// that should be iterated over.
/// </summary>
public Func<IEnumerable<T>> ItemEnumerator { get; set; }
/// <summary>
/// Given an item of type <see cref="T"/>, return
/// the <see cref="Guid"/> that represents the connection's unique ID
/// within mRemoteNG.
/// </summary>
public Func<T, Guid> ConnectionGuidSelector { get; set; }
/// <summary>
/// Given an item of type <see cref="T"/>, return a <see cref="string"/>
/// that represents what the associated credential's title should be.
/// </summary>
public Func<T, string> TitleSelector { get; set; }
/// <summary>
/// Given an item of type <see cref="T"/>, return a <see cref="string"/>
/// that represents what the associated credential's username should be.
/// </summary>
public Func<T, string> UsernameSelector { get; set; }
/// <summary>
/// Given an item of type <see cref="T"/>, return a <see cref="string"/>
/// that represents what the associated credential's domain should be.
/// </summary>
public Func<T, string> DomainSelector { get; set; }
/// <summary>
/// Given an item of type <see cref="T"/>, return a <see cref="SecureString"/>
/// that represents what the associated credential's password should be.
/// </summary>
public Func<T, SecureString> PasswordSelector { get; set; }
}
}

View File

@@ -9,7 +9,6 @@ using mRemoteNG.Container;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Config.Serializers.MiscSerializers
{

View File

@@ -1,59 +1,68 @@
using System;
using System.IO;
using System.Xml;
using mRemoteNG.Connection;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security;
using System.Xml;
using mRemoteNG.Credential;
using mRemoteNG.Security;
namespace mRemoteNG.Config.Serializers.MiscSerializers
{
public class PuttyConnectionManagerDeserializer : IDeserializer<string, ConnectionTreeModel>
public class PuttyConnectionManagerDeserializer
{
public ConnectionTreeModel Deserialize(string puttycmConnectionsXml)
public SerializationResult Deserialize(string puttycmConnectionsXml)
{
var connectionTreeModel = new ConnectionTreeModel();
var root = new RootNodeInfo(RootNodeType.Connection);
connectionTreeModel.AddRootNode(root);
var result = new SerializationResult(new List<ConnectionInfo>(), new ConnectionToCredentialMap());
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(puttycmConnectionsXml);
var configurationNode = xmlDocument.SelectSingleNode("/configuration");
var rootNodes = configurationNode?.SelectNodes("./root");
if (rootNodes == null) return connectionTreeModel;
foreach (XmlNode rootNode in rootNodes)
var rootXmlNode = configurationNode?.SelectSingleNode("./root");
if (rootXmlNode == null)
return result;
var rootContainer = ReadContainerProperties(rootXmlNode);
result.ConnectionRecords.Add(rootContainer);
foreach (XmlNode node in rootXmlNode.ChildNodes)
{
ImportRootOrContainer(rootNode, root);
rootContainer.AddChild(ImportRecursive(node, result.ConnectionToCredentialMap));
}
return connectionTreeModel;
return result;
}
private void ImportRootOrContainer(XmlNode xmlNode, ContainerInfo parentContainer)
private ContainerInfo ImportRecursive(XmlNode xmlNode, ConnectionToCredentialMap credentialMap)
{
VerifyNodeType(xmlNode);
var newContainer = ImportContainer(xmlNode, parentContainer);
var newContainer = ReadContainerProperties(xmlNode);
var childNodes = xmlNode.SelectNodes("./*");
if (childNodes == null) return;
if (childNodes == null)
return newContainer;
foreach (XmlNode childNode in childNodes)
{
switch (childNode.Name)
{
case "container":
ImportRootOrContainer(childNode, newContainer);
newContainer.AddChild(ImportRecursive(childNode, credentialMap));
break;
case "connection":
ImportConnection(childNode, newContainer);
newContainer.AddChild(ImportConnection(childNode, credentialMap));
break;
default:
throw (new FileFormatException($"Unrecognized child node ({childNode.Name})."));
throw new FileFormatException($"Unrecognized child node ({childNode.Name}).");
}
}
return newContainer;
}
private void VerifyNodeType(XmlNode xmlNode)
@@ -82,25 +91,28 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
}
}
private ContainerInfo ImportContainer(XmlNode containerNode, ContainerInfo parentContainer)
private ContainerInfo ReadContainerProperties(XmlNode containerNode)
{
var containerInfo = new ContainerInfo
{
Name = containerNode.Attributes?["name"].Value,
IsExpanded = bool.Parse(containerNode.Attributes?["expanded"].InnerText ?? "false")
};
parentContainer.AddChild(containerInfo);
return containerInfo;
}
private void ImportConnection(XmlNode connectionNode, ContainerInfo parentContainer)
private ConnectionInfo ImportConnection(XmlNode connectionNode, ConnectionToCredentialMap credentialMap)
{
var connectionNodeType = connectionNode.Attributes?["type"].Value;
if (string.Compare(connectionNodeType, "PuTTY", StringComparison.OrdinalIgnoreCase) != 0)
throw (new FileFormatException($"Unrecognized connection node type ({connectionNodeType})."));
var connectionInfo = ConnectionInfoFromXml(connectionNode);
parentContainer.AddChild(connectionInfo);
var cred = CredentialFromXml(connectionNode);
connectionInfo.CredentialRecordId = cred.Id;
credentialMap.Add(Guid.Parse(connectionInfo.ConstantID), cred);
return connectionInfo;
}
private ConnectionInfo ConnectionInfoFromXml(XmlNode xmlNode)
@@ -129,9 +141,6 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
// ./commandline
connectionInfo.Description = connectionInfoNode.SelectSingleNode("./description")?.InnerText;
var loginNode = xmlNode.SelectSingleNode("./login");
connectionInfo.Username = loginNode?.SelectSingleNode("login")?.InnerText;
connectionInfo.Password = loginNode?.SelectSingleNode("password")?.InnerText;
// ./prompt
// ./timeout/connectiontimeout
@@ -151,5 +160,19 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
return connectionInfo;
}
private ICredentialRecord CredentialFromXml(XmlNode xmlNode)
{
var loginNode = xmlNode.SelectSingleNode("./login");
var username = loginNode?.SelectSingleNode("login")?.InnerText ?? "";
return new CredentialRecord
{
Title = username,
Username = username,
Domain = "",
Password = loginNode?.SelectSingleNode("password")?.InnerText.ConvertToSecureString() ?? new SecureString()
};
}
}
}

View File

@@ -1,21 +1,21 @@
using System;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using System.Collections.Generic;
using mRemoteNG.Credential;
namespace mRemoteNG.Config.Serializers.MiscSerializers
{
public class RemoteDesktopConnectionDeserializer : IDeserializer<string, ConnectionTreeModel>
public class RemoteDesktopConnectionDeserializer
{
// .rdp file schema: https://technet.microsoft.com/en-us/library/ff393699(v=ws.10).aspx
public ConnectionTreeModel Deserialize(string rdcFileContent)
public SerializationResult Deserialize(string rdcFileContent)
{
var connectionTreeModel = new ConnectionTreeModel();
var root = new RootNodeInfo(RootNodeType.Connection);
connectionTreeModel.AddRootNode(root);
var connectionInfo = new ConnectionInfo();
var username = "";
var domain = "";
foreach (var line in rdcFileContent.Split(Environment.NewLine.ToCharArray()))
{
var parts = line.Split(new[] { ':' }, 3);
@@ -24,21 +24,42 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
continue;
}
var key = parts[0].Trim();
var propertyName = parts[0].Trim().ToLowerInvariant();
var value = parts[2].Trim();
SetConnectionInfoParameter(connectionInfo, key, value);
SetConnectionInfoParameter(connectionInfo, propertyName, value);
if (propertyName.Equals("username"))
username = value;
if (propertyName.Equals("domain"))
domain = value;
}
root.AddChild(connectionInfo);
var serializationResult = new SerializationResult(new List<ConnectionInfo>(), new ConnectionToCredentialMap());
serializationResult.ConnectionRecords.Add(connectionInfo);
return connectionTreeModel;
if (username.Length > 0 || domain.Length > 0)
{
var cred = new CredentialRecord
{
Title = domain.Length > 0 ? $"{domain}\\" : "" + username,
Domain = domain,
Username = username
};
serializationResult.ConnectionToCredentialMap.Add(Guid.Parse(connectionInfo.ConstantID), cred);
connectionInfo.CredentialRecordId = cred.Id;
}
return serializationResult;
}
private void SetConnectionInfoParameter(ConnectionInfo connectionInfo, string key, string value)
{
switch (key.ToLower())
switch (key)
{
case "full address":
var uri = new Uri("dummyscheme" + Uri.SchemeDelimiter + value);
@@ -50,12 +71,6 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
case "server port":
connectionInfo.Port = Convert.ToInt32(value);
break;
case "username":
connectionInfo.Username = value;
break;
case "domain":
connectionInfo.Domain = value;
break;
case "session bpp":
switch (value)
{
@@ -152,7 +167,7 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
connectionInfo.RDGatewayHostname = value;
break;
case "alternate shell":
connectionInfo.RDPStartProgram = value;
connectionInfo.StartProgram = value;
break;
}
}

View File

@@ -1,41 +1,44 @@
using System;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Container;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.Resources.Language;
using mRemoteNG.Credential;
using mRemoteNG.Security;
using mRemoteNG.Tools;
namespace mRemoteNG.Config.Serializers.MiscSerializers
{
public class RemoteDesktopConnectionManagerDeserializer : IDeserializer<string, ConnectionTreeModel>
public class RemoteDesktopConnectionManagerDeserializer
{
private static int _schemaVersion; /* 1 = RDCMan v2.2
3 = RDCMan v2.7 */
// 1 = RDCMan v2.2
// 3 = RDCMan v2.7
private static int _schemaVersion;
public ConnectionTreeModel Deserialize(string rdcmConnectionsXml)
public SerializationResult Deserialize(string rdcmConnectionsXml)
{
var connectionTreeModel = new ConnectionTreeModel();
var root = new RootNodeInfo(RootNodeType.Connection);
var serializationResult = new SerializationResult(new List<ConnectionInfo>(), new ConnectionToCredentialMap());
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(rdcmConnectionsXml);
var rdcManNode = xmlDocument.SelectSingleNode("/RDCMan");
VerifySchemaVersion(rdcManNode);
VerifyFileVersion(rdcManNode);
var fileNode = rdcManNode?.SelectSingleNode("./file");
ImportFileOrGroup(fileNode, root);
var importedItem = ImportFileOrGroup(fileNode, serializationResult.ConnectionToCredentialMap);
connectionTreeModel.AddRootNode(root);
return connectionTreeModel;
serializationResult.ConnectionRecords.Add(importedItem);
return serializationResult;
}
private static void VerifySchemaVersion(XmlNode rdcManNode)
@@ -57,7 +60,7 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
if (versionAttribute != null)
{
var version = new Version(versionAttribute);
if (!(version == new Version(2, 7)) && !(version == new Version(2, 83)))
if (!(version == new Version(2, 7)))
{
throw new FileFormatException($"Unsupported file version ({version}).");
}
@@ -80,28 +83,46 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
}
}
private static void ImportFileOrGroup(XmlNode xmlNode, ContainerInfo parentContainer)
private static ContainerInfo ImportFileOrGroup(XmlNode xmlNode, ConnectionToCredentialMap credentialMap)
{
var newContainer = ImportContainer(xmlNode, parentContainer);
var newContainer = ImportContainer(xmlNode);
var childNodes = xmlNode.SelectNodes("./group|./server");
if (childNodes == null) return;
if (childNodes == null)
return newContainer;
foreach (XmlNode childNode in childNodes)
{
ConnectionInfo newChild = null;
// ReSharper disable once SwitchStatementMissingSomeCases
switch (childNode.Name)
{
case "group":
ImportFileOrGroup(childNode, newContainer);
newChild = ImportFileOrGroup(childNode, credentialMap);
break;
case "server":
ImportServer(childNode, newContainer);
newChild = ConnectionInfoFromXml(childNode);
break;
}
if (newChild == null)
return newContainer;
newContainer.AddChild(newChild);
var cred = ParseCredentials(childNode);
if (!cred.Any())
continue;
newChild.CredentialRecordId = cred.First().Id;
credentialMap.Add(Guid.Parse(newChild.ConstantID), cred.First());
}
return newContainer;
}
private static ContainerInfo ImportContainer(XmlNode containerPropertiesNode, ContainerInfo parentContainer)
private static ContainerInfo ImportContainer(XmlNode containerPropertiesNode)
{
if (_schemaVersion == 1)
{
@@ -121,16 +142,9 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
newContainer.Name = containerPropertiesNode?.SelectSingleNode("./name")?.InnerText ?? Language.NewFolder;
if (bool.TryParse(containerPropertiesNode?.SelectSingleNode("./expanded")?.InnerText, out var expanded))
newContainer.IsExpanded = expanded;
parentContainer.AddChild(newContainer);
return newContainer;
}
private static void ImportServer(XmlNode serverNode, ContainerInfo parentContainer)
{
var newConnectionInfo = ConnectionInfoFromXml(serverNode);
parentContainer.AddChild(newConnectionInfo);
}
private static ConnectionInfo ConnectionInfoFromXml(XmlNode xmlNode)
{
var connectionInfo = new ConnectionInfo {Protocol = ProtocolType.RDP};
@@ -153,25 +167,7 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
connectionInfo.Description = propertiesNode?.SelectSingleNode("./comment")?.InnerText ?? string.Empty;
var logonCredentialsNode = xmlNode.SelectSingleNode("./logonCredentials");
if (logonCredentialsNode?.Attributes?["inherit"]?.Value == "None")
{
connectionInfo.Username = logonCredentialsNode.SelectSingleNode("userName")?.InnerText ?? string.Empty;
var passwordNode = logonCredentialsNode.SelectSingleNode("./password");
if (_schemaVersion == 1) // Version 2.2 allows clear text passwords
{
connectionInfo.Password = passwordNode?.Attributes?["storeAsClearText"]?.Value == "True"
? passwordNode.InnerText
: DecryptRdcManPassword(passwordNode?.InnerText);
}
else
{
connectionInfo.Password = DecryptRdcManPassword(passwordNode?.InnerText);
}
connectionInfo.Domain = logonCredentialsNode.SelectSingleNode("./domain")?.InnerText ?? string.Empty;
}
else
if (logonCredentialsNode?.Attributes?["inherit"]?.Value != "None")
{
connectionInfo.Inheritance.Username = true;
connectionInfo.Inheritance.Password = true;
@@ -183,8 +179,8 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
{
if (bool.TryParse(connectionSettingsNode.SelectSingleNode("./connectToConsole")?.InnerText, out var useConsole))
connectionInfo.UseConsoleSession = useConsole;
connectionInfo.RDPStartProgram = connectionSettingsNode.SelectSingleNode("./startProgram")?.InnerText ?? string.Empty;
connectionInfo.RDPStartProgramWorkDir = connectionSettingsNode.SelectSingleNode("./startProgramWorkDir")?.InnerText ?? string.Empty;
connectionInfo.StartProgram = connectionSettingsNode.SelectSingleNode("./startProgram")?.InnerText;
// ./workingDir
if (int.TryParse(connectionSettingsNode.SelectSingleNode("./port")?.InnerText, out var port))
connectionInfo.Port = port;
}
@@ -205,9 +201,9 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
connectionInfo.RDGatewayUsername = gatewaySettingsNode.SelectSingleNode("./userName")?.InnerText ?? string.Empty;
var passwordNode = gatewaySettingsNode.SelectSingleNode("./password");
connectionInfo.RDGatewayPassword = passwordNode?.Attributes?["storeAsClearText"]?.Value == "True"
? passwordNode.InnerText
: DecryptRdcManPassword(passwordNode?.InnerText);
connectionInfo.RDGatewayPassword = passwordNode?.Attributes?["storeAsClearText"]?.Value == "True"
? passwordNode.InnerText
: DecryptRdcManPassword(passwordNode?.InnerText).ConvertToUnsecureString();
connectionInfo.RDGatewayDomain = gatewaySettingsNode.SelectSingleNode("./domain")?.InnerText ?? string.Empty;
// ./logonMethod
@@ -349,22 +345,45 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
return connectionInfo;
}
private static string DecryptRdcManPassword(string ciphertext)
private static Optional<ICredentialRecord> ParseCredentials(XmlNode xmlNode)
{
var logonCredentialsNode = xmlNode.SelectSingleNode("./logonCredentials");
if (logonCredentialsNode?.Attributes?["inherit"]?.Value != "None")
return Optional<ICredentialRecord>.Empty;
var username = logonCredentialsNode.SelectSingleNode("userName")?.InnerText ?? "";
var domain = logonCredentialsNode.SelectSingleNode("./domain")?.InnerText ?? "";
var passwordNode = logonCredentialsNode.SelectSingleNode("./password");
var creds = new CredentialRecord
{
Title = domain.Length > 0 ? $"{domain}\\{username}" : $"{username}",
Username = username,
Domain = domain,
Password = passwordNode?.Attributes?["storeAsClearText"]?.Value == "True"
? passwordNode.InnerText.ConvertToSecureString()
: DecryptRdcManPassword(passwordNode?.InnerText)
};
return creds;
}
private static SecureString DecryptRdcManPassword(string ciphertext)
{
if (string.IsNullOrEmpty(ciphertext))
return string.Empty;
return new SecureString();
try
{
var plaintextData = ProtectedData.Unprotect(Convert.FromBase64String(ciphertext), new byte[] { },
DataProtectionScope.LocalMachine);
var charArray = Encoding.Unicode.GetChars(plaintextData);
return new string(charArray);
return Encoding.Unicode.GetString(plaintextData).ConvertToSecureString();
}
catch (Exception /*ex*/)
{
//Runtime.MessageCollector.AddExceptionMessage("RemoteDesktopConnectionManager.DecryptPassword() failed.", ex, logOnly: true);
return string.Empty;
return new SecureString();
}
}
}

View File

@@ -0,0 +1,23 @@
using mRemoteNG.Connection;
using mRemoteNG.Tools;
using System.Collections.Generic;
namespace mRemoteNG.Config.Serializers
{
/// <summary>
/// Represents the connections and credentials found during a deserialization.
/// </summary>
public class SerializationResult
{
public List<ConnectionInfo> ConnectionRecords { get; }
public ConnectionToCredentialMap ConnectionToCredentialMap { get; }
public SerializationResult(
List<ConnectionInfo> connectionRecords,
ConnectionToCredentialMap connectionToCredentialMap)
{
ConnectionRecords = connectionRecords.ThrowIfNull(nameof(connectionRecords));
ConnectionToCredentialMap = connectionToCredentialMap.ThrowIfNull(nameof(connectionToCredentialMap));
}
}
}

View File

@@ -2,15 +2,12 @@
using mRemoteNG.App.Info;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
using System;
namespace mRemoteNG.Config.Serializers.Versioning
{
public class SqlDatabaseVersionVerifier
{
protected readonly Version currentSupportedVersion = new Version(2, 9);
{
private readonly IDatabaseConnector _databaseConnector;
public SqlDatabaseVersionVerifier(IDatabaseConnector DatabaseConnector)
@@ -41,7 +38,6 @@ namespace mRemoteNG.Config.Serializers.Versioning
new SqlVersion25To26Upgrader(_databaseConnector),
new SqlVersion26To27Upgrader(_databaseConnector),
new SqlVersion27To28Upgrader(_databaseConnector),
new SqlVersion28To29Upgrader(_databaseConnector),
};
foreach (var upgrader in dbUpgraders)
@@ -53,7 +49,7 @@ namespace mRemoteNG.Config.Serializers.Versioning
}
// DB is at the highest current supported version
if (databaseVersion.CompareTo(currentSupportedVersion) == 0)
if (databaseVersion.CompareTo(new Version(2, 8)) == 0)
isVerified = true;
if (isVerified == false)

View File

@@ -1,80 +0,0 @@
using mRemoteNG.App;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Messages;
using System;
using System.Data.Common;
namespace mRemoteNG.Config.Serializers.Versioning
{
public class SqlVersion28To29Upgrader : IVersionUpgrader
{
private readonly Version version = new Version(2, 9);
private readonly IDatabaseConnector _databaseConnector;
public SqlVersion28To29Upgrader(IDatabaseConnector databaseConnector)
{
_databaseConnector = databaseConnector ?? throw new ArgumentNullException(nameof(databaseConnector));
}
public bool CanUpgrade(Version currentVersion)
{
return currentVersion == new Version(2, 8) ||
// Support upgrading during dev revisions, 2.9.1, 2.9.2, etc...
(currentVersion <= new Version(2, 9) &&
currentVersion < version);
}
public Version Upgrade()
{
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg,
string.Format("Upgrading database to version {0}.", version));
const string mySqlAlter = @"
ALTER TABLE tblCons ADD COLUMN StartProgram varchar(512) DEFAULT NULL;
ALTER TABLE tblCons ADD COLUMN StartProgramWorkDir varchar(512) DEFAULT NULL;
ALTER TABLE tblRoot CHANGE COLUMN ConfVersion ConfVersion VARCHAR(15) NOT NULL;";
const string mySqlUpdate = @"UPDATE tblRoot SET ConfVersion=?;";
const string msSqlAlter = @"
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[tblCons]') AND name = 'StartProgram')
BEGIN
ALTER TABLE tblCons ADD StartProgram varchar(512), StartProgramWorkDir varchar(512);
END;GO;
ALTER TABLE tblRoot MODIFY COLUMN ConfVersion varchar(15);GO;";
const string msSqlUpdate = @"UPDATE tblRoot SET ConfVersion=@confVersion;";
using (var sqlTran = _databaseConnector.DbConnection().BeginTransaction(System.Data.IsolationLevel.Serializable))
{
DbCommand dbCommand;
if (_databaseConnector.GetType() == typeof(MSSqlDatabaseConnector))
{
dbCommand = _databaseConnector.DbCommand(msSqlAlter);
dbCommand.Transaction = sqlTran;
dbCommand.ExecuteNonQuery();
dbCommand = _databaseConnector.DbCommand(msSqlUpdate);
dbCommand.Transaction = sqlTran;
}
else if (_databaseConnector.GetType() == typeof(MySqlDatabaseConnector))
{
dbCommand = _databaseConnector.DbCommand(mySqlAlter);
dbCommand.Transaction = sqlTran;
dbCommand.ExecuteNonQuery();
dbCommand = _databaseConnector.DbCommand(mySqlUpdate);
dbCommand.Transaction = sqlTran;
}
else
{
throw new Exception("Unknown database back-end");
}
var pConfVersion = dbCommand.CreateParameter();
pConfVersion.ParameterName = "confVersion";
pConfVersion.Value = version.ToString();
pConfVersion.DbType = System.Data.DbType.String;
pConfVersion.Direction = System.Data.ParameterDirection.Input;
dbCommand.Parameters.Add(pConfVersion);
dbCommand.ExecuteNonQuery();
sqlTran.Commit();
}
return version;
}
}
}

View File

@@ -0,0 +1,109 @@
using mRemoteNG.App;
using mRemoteNG.Config.Serializers.CredentialSerializer;
using mRemoteNG.Connection;
using mRemoteNG.Credential;
using mRemoteNG.Security;
using mRemoteNG.Security.Authentication;
using mRemoteNG.Security.Factories;
using mRemoteNG.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using mRemoteNG.Config.Serializers.ConnectionSerializers.Xml;
namespace mRemoteNG.Config.Serializers.Versioning
{
public class XmlCredentialManagerUpgrader
{
private readonly XmlConnectionsDeserializer _deserializer;
public XmlCredentialManagerUpgrader(XmlConnectionsDeserializer decoratedDeserializer)
{
_deserializer = decoratedDeserializer.ThrowIfNull(nameof(decoratedDeserializer));
}
public SerializationResult Deserialize(string serializedData, ConnectionToCredentialMap upgradeMap)
{
var serializedDataAsXDoc = EnsureConnectionXmlElementsHaveIds(serializedData);
var serializedDataWithIds = $"{serializedDataAsXDoc.Declaration}{serializedDataAsXDoc}";
var serializationResult = _deserializer.Deserialize(serializedDataWithIds);
if (upgradeMap != null)
ApplyCredentialMapping(upgradeMap, serializationResult.ConnectionRecords.FlattenConnectionTree());
return serializationResult;
}
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 ConnectionToCredentialMap UpgradeUserFilesForCredentialManager(XDocument xdoc)
{
if (!CredentialManagerUpgradeNeeded(xdoc))
{
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 keyForOldConnectionFile = auth.LastAuthenticatedPassword;
var preCredManagerXmlHarvestConfig = new HarvestConfig<XElement>
{
ItemEnumerator = () => xdoc.Descendants("Node"),
ConnectionGuidSelector = e =>
{
Guid.TryParse(e.Attribute("Id")?.Value, out var connectionId);
return connectionId;
},
UsernameSelector = e => e.Attribute("Username")?.Value,
DomainSelector = e => e.Attribute("Domain")?.Value,
PasswordSelector = e => cryptoProvider.Decrypt(e.Attribute("Password")?.Value, keyForOldConnectionFile).ConvertToSecureString(),
TitleSelector = e => $"{e.Attribute("Domain")?.Value}{(string.IsNullOrEmpty(e.Attribute("Domain")?.Value) ? "" : "\\")}{e.Attribute("Username")?.Value}"
};
var credentialHarvester = new CredentialHarvester();
var harvestedCredentials = credentialHarvester.Harvest(preCredManagerXmlHarvestConfig);
return harvestedCredentials;
}
/// <summary>
/// If any connections in the xml contain a Username/Domain/Password field, we need to upgrade
/// it to be compatible with the credential manager.
/// </summary>
/// <param name="xdoc"></param>
public static bool CredentialManagerUpgradeNeeded(XContainer xdoc)
{
return xdoc
.Descendants("Node")
.Any(n =>
n.Attribute("Username") != null ||
n.Attribute("Domain") != null ||
n.Attribute("Password") != null);
}
private void ApplyCredentialMapping(IDictionary<Guid, ICredentialRecord> map, IEnumerable<AbstractConnectionRecord> connectionRecords)
{
foreach (var connectionInfo in connectionRecords)
{
Guid.TryParse(connectionInfo.ConstantID, out var id);
if (map.ContainsKey(id))
connectionInfo.CredentialRecordId = map[id].Id.ToOptional();
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Drawing;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
@@ -27,7 +27,12 @@ namespace mRemoteNG.Config.Settings
private FrmMain MainForm { get; }
public SettingsLoader(FrmMain mainForm, MessageCollector messageCollector, QuickConnectToolStrip quickConnectToolStrip, ExternalToolsToolStrip externalToolsToolStrip, MultiSshToolStrip multiSshToolStrip, MenuStrip mainMenu)
public SettingsLoader(FrmMain mainForm,
MessageCollector messageCollector,
QuickConnectToolStrip quickConnectToolStrip,
ExternalToolsToolStrip externalToolsToolStrip,
MultiSshToolStrip multiSshToolStrip,
MenuStrip mainMenu)
{
if (mainForm == null)
throw new ArgumentNullException(nameof(mainForm));
@@ -69,7 +74,7 @@ namespace mRemoteNG.Config.Settings
LoadExternalAppsFromXml();
SetAlwaysShowPanelTabs();
if (Properties.App.Default.ResetToolbars)
if (Properties.Settings.Default.ResetToolbars)
SetToolbarsDefault();
else
LoadToolbarsFromSettings();
@@ -82,37 +87,40 @@ namespace mRemoteNG.Config.Settings
private static void SetAlwaysShowPanelTabs()
{
if (Properties.OptionsTabsPanelsPage.Default.AlwaysShowPanelTabs)
if (Properties.Settings.Default.AlwaysShowPanelTabs)
FrmMain.Default.pnlDock.DocumentStyle = DocumentStyle.DockingWindow;
}
private void SetSupportedCulture()
{
if (Properties.Settings.Default.OverrideUICulture == "" || !SupportedCultures.IsNameSupported(Properties.Settings.Default.OverrideUICulture)) return;
if (Properties.Settings.Default.OverrideUICulture == "" ||
!SupportedCultures.IsNameSupported(Properties.Settings.Default.OverrideUICulture)) return;
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.OverrideUICulture);
_messageCollector.AddMessage(MessageClass.InformationMsg, $"Override Culture: {Thread.CurrentThread.CurrentUICulture.Name}/{Thread.CurrentThread.CurrentUICulture.NativeName}", true);
_messageCollector.AddMessage(MessageClass.InformationMsg,
$"Override Culture: {Thread.CurrentThread.CurrentUICulture.Name}/{Thread.CurrentThread.CurrentUICulture.NativeName}",
true);
}
private void SetApplicationWindowPositionAndSize()
{
MainForm.WindowState = FormWindowState.Normal;
if (Properties.App.Default.MainFormState == FormWindowState.Normal)
if (Properties.Settings.Default.MainFormState == FormWindowState.Normal)
{
if (!Properties.App.Default.MainFormLocation.IsEmpty)
MainForm.Location = Properties.App.Default.MainFormLocation;
if (!Properties.App.Default.MainFormSize.IsEmpty)
MainForm.Size = Properties.App.Default.MainFormSize;
if (!Properties.Settings.Default.MainFormLocation.IsEmpty)
MainForm.Location = Properties.Settings.Default.MainFormLocation;
if (!Properties.Settings.Default.MainFormSize.IsEmpty)
MainForm.Size = Properties.Settings.Default.MainFormSize;
}
else
{
if (!Properties.App.Default.MainFormRestoreLocation.IsEmpty)
MainForm.Location = Properties.App.Default.MainFormRestoreLocation;
if (!Properties.App.Default.MainFormRestoreSize.IsEmpty)
MainForm.Size = Properties.App.Default.MainFormRestoreSize;
if (!Properties.Settings.Default.MainFormRestoreLocation.IsEmpty)
MainForm.Location = Properties.Settings.Default.MainFormRestoreLocation;
if (!Properties.Settings.Default.MainFormRestoreSize.IsEmpty)
MainForm.Size = Properties.Settings.Default.MainFormRestoreSize;
}
if (Properties.App.Default.MainFormState == FormWindowState.Maximized)
if (Properties.Settings.Default.MainFormState == FormWindowState.Maximized)
{
MainForm.WindowState = FormWindowState.Maximized;
}
@@ -137,31 +145,33 @@ namespace mRemoteNG.Config.Settings
private void SetAutoSave()
{
if (Properties.OptionsConnectionsPage.Default.AutoSaveEveryMinutes <= 0) return;
MainForm.tmrAutoSave.Interval = Properties.OptionsConnectionsPage.Default.AutoSaveEveryMinutes * 60000;
if (Properties.Settings.Default.AutoSaveEveryMinutes <= 0) return;
MainForm.tmrAutoSave.Interval = Properties.Settings.Default.AutoSaveEveryMinutes * 60000;
MainForm.tmrAutoSave.Enabled = true;
}
private void SetKioskMode()
{
if (!Properties.App.Default.MainFormKiosk) return;
if (!Properties.Settings.Default.MainFormKiosk) return;
MainForm.Fullscreen.Value = true;
}
private static void SetShowSystemTrayIcon()
{
if (Properties.OptionsAppearancePage.Default.ShowSystemTrayIcon)
if (Properties.Settings.Default.ShowSystemTrayIcon)
Runtime.NotificationAreaIcon = new NotificationAreaIcon();
}
private static void SetPuttyPath()
{
PuttyBase.PuttyPath = Properties.OptionsAdvancedPage.Default.UseCustomPuttyPath ? Properties.OptionsAdvancedPage.Default.CustomPuttyPath : GeneralAppInfo.PuttyPath;
PuttyBase.PuttyPath = Properties.Settings.Default.UseCustomPuttyPath
? Properties.Settings.Default.CustomPuttyPath
: GeneralAppInfo.PuttyPath;
}
private void EnsureSettingsAreSavedInNewestVersion()
{
if (Properties.App.Default.DoUpgrade)
if (Properties.Settings.Default.DoUpgrade)
UpgradeSettingsVersion();
}
@@ -177,12 +187,12 @@ namespace mRemoteNG.Config.Settings
_messageCollector.AddExceptionMessage("Settings.Upgrade() failed", ex);
}
Properties.App.Default.DoUpgrade = false;
Properties.Settings.Default.DoUpgrade = false;
// Clear pending update flag
// This is used for automatic updates, not for settings migration, but it
// needs to be cleared here because we know that we just updated.
Properties.OptionsUpdatesPage.Default.UpdatePending = false;
Properties.Settings.Default.UpdatePending = false;
}
private void SetToolbarsDefault()
@@ -249,7 +259,9 @@ namespace mRemoteNG.Config.Settings
private void SetToolstripGripStyle(ToolStrip toolbar)
{
toolbar.GripStyle = Properties.Settings.Default.LockToolbars ? ToolStripGripStyle.Hidden : ToolStripGripStyle.Visible;
toolbar.GripStyle = Properties.Settings.Default.LockToolbars
? ToolStripGripStyle.Hidden
: ToolStripGripStyle.Visible;
}
private ToolStripPanel ToolStripPanelFromString(string panel)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Windows.Forms;
using mRemoteNG.App;
using mRemoteNG.App.Info;
@@ -11,7 +11,10 @@ namespace mRemoteNG.Config.Settings
{
public static class SettingsSaver
{
public static void SaveSettings(Control quickConnectToolStrip, ExternalToolsToolStrip externalToolsToolStrip, MultiSshToolStrip multiSshToolStrip, FrmMain frmMain)
public static void SaveSettings(Control quickConnectToolStrip,
ExternalToolsToolStrip externalToolsToolStrip,
MultiSshToolStrip multiSshToolStrip,
FrmMain frmMain)
{
try
{
@@ -22,46 +25,31 @@ namespace mRemoteNG.Config.Settings
frmMain.WindowState = FormWindowState.Maximized;
}
Properties.App.Default.MainFormLocation = frmMain.Location;
Properties.App.Default.MainFormSize = frmMain.Size;
Properties.Settings.Default.MainFormLocation = frmMain.Location;
Properties.Settings.Default.MainFormSize = frmMain.Size;
if (frmMain.WindowState != FormWindowState.Normal)
{
Properties.App.Default.MainFormRestoreLocation = frmMain.RestoreBounds.Location;
Properties.App.Default.MainFormRestoreSize = frmMain.RestoreBounds.Size;
Properties.Settings.Default.MainFormRestoreLocation = frmMain.RestoreBounds.Location;
Properties.Settings.Default.MainFormRestoreSize = frmMain.RestoreBounds.Size;
}
Properties.App.Default.MainFormState = frmMain.WindowState;
Properties.Settings.Default.MainFormState = frmMain.WindowState;
if (frmMain.Fullscreen != null)
{
Properties.App.Default.MainFormKiosk = frmMain.Fullscreen.Value;
Properties.Settings.Default.MainFormKiosk = frmMain.Fullscreen.Value;
}
Properties.App.Default.FirstStart = false;
Properties.App.Default.ResetPanels = false;
Properties.App.Default.ResetToolbars = false;
Properties.Settings.Default.FirstStart = false;
Properties.Settings.Default.ResetPanels = false;
Properties.Settings.Default.ResetToolbars = false;
Properties.Settings.Default.NoReconnect = false;
SaveExternalAppsToolbarLocation(externalToolsToolStrip);
SaveQuickConnectToolbarLocation(quickConnectToolStrip);
SaveMultiSshToolbarLocation(multiSshToolStrip);
Properties.App.Default.Save();
Properties.AppUI.Default.Save();
Properties.OptionsAdvancedPage.Default.Save();
Properties.OptionsAppearancePage.Default.Save();
Properties.OptionsBackupPage.Default.Save();
Properties.OptionsConnectionsPage.Default.Save();
Properties.OptionsCredentialsPage.Default.Save();
Properties.OptionsDBsPage.Default.Save();
Properties.OptionsNotificationsPage.Default.Save();
Properties.OptionsSecurityPage.Default.Save();
Properties.OptionsStartupExitPage.Default.Save();
Properties.OptionsTabsPanelsPage.Default.Save();
Properties.OptionsThemePage.Default.Save();
Properties.OptionsUpdatesPage.Default.Save();
Properties.Settings.Default.Save();
SaveDockPanelLayout();

File diff suppressed because it is too large Load Diff

View File

@@ -16,8 +16,6 @@ using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Container;
using mRemoteNG.Properties;
using mRemoteNG.Tree;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Connection
@@ -47,10 +45,10 @@ namespace mRemoteNG.Connection
[Browsable(false)] public ContainerInfo Parent { get; internal set; }
[Browsable(false)]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public bool IsQuickConnect { get; set; }
[Browsable(false)]
public bool PleaseConnect { get; set; }
[Browsable(false)] public bool PleaseConnect { get; set; }
#endregion
@@ -67,7 +65,6 @@ namespace mRemoteNG.Connection
SetTreeDisplayDefaults();
SetConnectionDefaults();
SetProtocolDefaults();
SetRemoteDesktopServicesDefaults();
SetRdGatewayDefaults();
SetAppearanceDefaults();
SetRedirectDefaults();
@@ -138,22 +135,24 @@ namespace mRemoteNG.Connection
return filteredProperties;
}
public virtual IEnumerable<PropertyInfo> GetSerializableProperties()
{
var excludedProperties = new[]
{
"Parent", "Name", "Hostname", "Port", "Inheritance", "OpenConnections",
"IsContainer", "IsDefault", "PositionID", "ConstantID", "TreeNode", "IsQuickConnect", "PleaseConnect"
};
public virtual IEnumerable<PropertyInfo> GetSerializableProperties()
{
var excludedProperties = new[] {
nameof(Parent), nameof(Name), nameof(Hostname), nameof(Port),
nameof(Username), nameof(Domain), nameof(Password),
nameof(Inheritance), nameof(OpenConnections),
nameof(IsContainer), nameof(IsDefault), nameof(ConstantID),
nameof(IsQuickConnect), nameof(PleaseConnect), nameof(CredentialRecord)
};
return GetProperties(excludedProperties);
}
return GetProperties(excludedProperties);
}
public virtual void SetParent(ContainerInfo newParent)
{
public virtual void SetParent(ContainerInfo newParent)
{
RemoveParent();
newParent?.AddChild(this);
}
newParent?.AddChild(this);
}
public void RemoveParent()
{
@@ -201,10 +200,7 @@ namespace mRemoteNG.Connection
private bool ShouldThisPropertyBeInherited(string propertyName)
{
return
Inheritance.InheritanceActive &&
ParentIsValidInheritanceTarget() &&
IsInheritanceTurnedOnForThisProperty(propertyName);
return ParentIsValidInheritanceTarget() && IsInheritanceTurnedOnForThisProperty(propertyName);
}
private bool ParentIsValidInheritanceTarget()
@@ -296,7 +292,6 @@ namespace mRemoteNG.Connection
private void SetConnectionDefaults()
{
Hostname = string.Empty;
EC2Region = Settings.Default.ConDefaultEC2Region;
}
private void SetProtocolDefaults()
@@ -312,18 +307,10 @@ namespace mRemoteNG.Connection
LoadBalanceInfo = Settings.Default.ConDefaultLoadBalanceInfo;
RenderingEngine = (HTTPBase.RenderingEngine)Enum.Parse(typeof(HTTPBase.RenderingEngine), Settings.Default.ConDefaultRenderingEngine);
UseCredSsp = Settings.Default.ConDefaultUseCredSsp;
UseRestrictedAdmin = Settings.Default.ConDefaultUseRestrictedAdmin;
UseRCG = Settings.Default.ConDefaultUseRCG;
UseVmId = Settings.Default.ConDefaultUseVmId;
UseEnhancedMode = Settings.Default.ConDefaultUseEnhancedMode;
}
private void SetRemoteDesktopServicesDefaults()
{
RDPStartProgram = string.Empty;
RDPStartProgramWorkDir = string.Empty;
}
private void SetRdGatewayDefaults()
{
RDGatewayUsageMethod = (RDGatewayUsageMethod)Enum.Parse(typeof(RDGatewayUsageMethod), Settings.Default.ConDefaultRDGatewayUsageMethod);
@@ -370,9 +357,7 @@ namespace mRemoteNG.Connection
MacAddress = Settings.Default.ConDefaultMacAddress;
UserField = Settings.Default.ConDefaultUserField;
Favorite = Settings.Default.ConDefaultFavorite;
RDPStartProgram = Settings.Default.ConDefaultRDPStartProgram;
RDPStartProgramWorkDir = Settings.Default.ConDefaultRDPStartProgramWorkDir;
OpeningCommand = Settings.Default.OpeningCommand;
StartProgram = Settings.Default.ConDefaultStartProgram;
}
private void SetVncDefaults()

View File

@@ -3,8 +3,6 @@ using System.ComponentModel;
using System.Linq;
using System.Reflection;
using mRemoteNG.Tools;
using mRemoteNG.Tree.Root;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Connection
{
@@ -50,13 +48,13 @@ namespace mRemoteNG.Connection
#endregion
#region Connection
#region
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 3),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.UserViaAPI)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionUserViaAPI)),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.Credentials)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionCredentials)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
[Browsable(true)]
public bool UserViaAPI { get; set; }
public bool CredentialId { get; set; }
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 3),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.Username)),
@@ -99,15 +97,6 @@ namespace mRemoteNG.Connection
[Browsable(true)]
public bool SSHTunnelConnectionName { get; set; }
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 3),
LocalizedAttributes.LocalizedDisplayNameInheritAttribute(nameof(Language.OpeningCommand)),
LocalizedAttributes.LocalizedDescriptionInheritAttribute(nameof(Language.PropertyDescriptionOpeningCommand)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
[Browsable(true)]
public bool OpeningCommand { get; set; }
#endregion
#region Protocol
@@ -184,18 +173,6 @@ namespace mRemoteNG.Connection
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
public bool UseCredSsp { get; set; }
[LocalizedAttributes.LocalizedCategory(nameof(Language.Protocol), 4),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.UseRestrictedAdmin)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionUseRestrictedAdmin)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
public bool UseRestrictedAdmin { get; set; }
[LocalizedAttributes.LocalizedCategory(nameof(Language.Protocol), 4),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.UseRCG)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionUseRCG)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
public bool UseRCG { get; set; }
[LocalizedAttributes.LocalizedCategory(nameof(Language.Protocol), 4),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.UseVmId)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionUseVmId)),
@@ -474,16 +451,7 @@ namespace mRemoteNG.Connection
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]public bool VNCViewOnly {get; set;}
#endregion
[Browsable(false)]
public ConnectionInfo Parent { get; private set; }
/// <summary>
/// Indicates whether this inheritance object is enabled.
/// When false, users of this object should not respect inheritance
/// settings for individual properties.
/// </summary>
[Browsable(false)]
public bool InheritanceActive => !(Parent is RootNodeInfo || Parent?.Parent is RootNodeInfo);
[Browsable(false)] public ConnectionInfo Parent { get; private set; }
#endregion
@@ -500,6 +468,7 @@ namespace mRemoteNG.Connection
{
var newInheritance = (ConnectionInfoInheritance)MemberwiseClone();
newInheritance.Parent = parent;
newInheritance._tempInheritanceStorage = null;
return newInheritance;
}
@@ -557,22 +526,15 @@ namespace mRemoteNG.Connection
/// <returns></returns>
public IEnumerable<string> GetEnabledInheritanceProperties()
{
return InheritanceActive
? GetProperties()
.Where(property => (bool)property.GetValue(this))
.Select(property => property.Name)
.ToList()
: Enumerable.Empty<string>();
return GetProperties()
.Where(property => (bool)property.GetValue(this))
.Select(property => property.Name)
.ToList();
}
private bool FilterProperty(PropertyInfo propertyInfo)
{
var exclusions = new[]
{
nameof(EverythingInherited),
nameof(Parent),
nameof(InheritanceActive)
};
var exclusions = new[] {"EverythingInherited", "Parent"};
var valueShouldNotBeFiltered = !exclusions.Contains(propertyInfo.Name);
return valueShouldNotBeFiltered;
}

View File

@@ -8,10 +8,10 @@ using mRemoteNG.Messages;
using mRemoteNG.Properties;
using mRemoteNG.UI.Forms;
using mRemoteNG.UI.Panels;
using mRemoteNG.Credential;
using mRemoteNG.UI.Tabs;
using mRemoteNG.UI.Window;
using WeifenLuo.WinFormsUI.Docking;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Connection
@@ -20,6 +20,12 @@ namespace mRemoteNG.Connection
{
private readonly PanelAdder _panelAdder = new PanelAdder();
private readonly List<string> _activeConnections = new List<string>();
private readonly CredentialService _credentialService;
public ConnectionInitiator(CredentialService credentialService)
{
_credentialService = credentialService;
}
public IEnumerable<string> ActiveConnections => _activeConnections;
@@ -62,19 +68,6 @@ namespace mRemoteNG.Connection
try
{
if (!string.IsNullOrEmpty(connectionInfo.EC2InstanceId))
{
try
{
string host = await ExternalConnectors.AWS.EC2FetchDataService.GetEC2InstanceDataAsync("AWSAPI:" + connectionInfo.EC2InstanceId, connectionInfo.EC2Region);
if (!string.IsNullOrEmpty(host))
connectionInfo.Hostname = host;
}
catch
{
}
}
if (connectionInfo.Hostname == "" && connectionInfo.Protocol != ProtocolType.IntApp)
{
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
@@ -299,7 +292,9 @@ namespace mRemoteNG.Connection
private static string SetConnectionPanel(ConnectionInfo connectionInfo, ConnectionInfo.Force force)
{
if (connectionInfo.Panel != "" && !force.HasFlag(ConnectionInfo.Force.OverridePanel) && !Properties.OptionsTabsPanelsPage.Default.AlwaysShowPanelSelectionDlg)
if (connectionInfo.Panel != "" &&
!force.HasFlag(ConnectionInfo.Force.OverridePanel) &&
!Settings.Default.AlwaysShowPanelSelectionDlg)
return connectionInfo.Panel;
var frmPnl = new FrmChoosePanel();

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.App;
@@ -11,6 +13,7 @@ using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Putty;
using mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Credential;
using mRemoteNG.Messages;
using mRemoteNG.Properties;
using mRemoteNG.Security;
@@ -18,11 +21,10 @@ using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Connection
{
public class ConnectionsService
public class ConnectionsService : IConnectionsService
{
private static readonly object SaveLock = new object();
private readonly PuttySessionsManager _puttySessionsManager;
@@ -31,26 +33,26 @@ namespace mRemoteNG.Connection
private bool _batchingSaves = false;
private bool _saveRequested = false;
private bool _saveAsyncRequested = false;
private readonly ICredentialService _credentialService;
public bool IsConnectionsFileLoaded { get; set; }
public bool UsingDatabase { get; private set; }
public string ConnectionFileName { get; private set; }
public RemoteConnectionsSyncronizer RemoteConnectionsSyncronizer { get; set; }
public DateTime LastSqlUpdate { get; set; }
public DateTime LastFileUpdate { get; set; }
public ConnectionTreeModel ConnectionTreeModel { get; private set; }
public IConnectionTreeModel ConnectionTreeModel { get; private set; } = new ConnectionTreeModel();
public ConnectionsService(PuttySessionsManager puttySessionsManager)
public ConnectionsService(PuttySessionsManager puttySessionsManager, ICredentialService credentialService)
{
if (puttySessionsManager == null)
throw new ArgumentNullException(nameof(puttySessionsManager));
_puttySessionsManager = puttySessionsManager;
_puttySessionsManager = puttySessionsManager.ThrowIfNull(nameof(puttySessionsManager));
_credentialService = credentialService.ThrowIfNull(nameof(credentialService));
var path = SettingsFileInfo.SettingsPath;
_localConnectionPropertiesDataProvider =
new FileDataProvider(Path.Combine(path, "LocalConnectionProperties.xml"));
_localConnectionPropertiesSerializer = new LocalConnectionPropertiesXmlSerializer();
_puttySessionsManager.RootPuttySessionsNodes.ForEach(node => ConnectionTreeModel.AddRootNode(node));
}
public void NewConnectionsFile(string filename)
@@ -58,10 +60,9 @@ namespace mRemoteNG.Connection
try
{
filename.ThrowIfNullOrEmpty(nameof(filename));
var newConnectionsModel = new ConnectionTreeModel();
newConnectionsModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection));
SaveConnections(newConnectionsModel, false, new SaveFilter(), filename, true);
LoadConnections(false, false, filename);
ConnectionTreeModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection));
SaveConnections(ConnectionTreeModel, false, new SaveFilter(), filename, true);
LoadConnections(false, filename);
}
catch (Exception ex)
{
@@ -78,30 +79,23 @@ namespace mRemoteNG.Connection
if (connectionString.Contains("@"))
{
var x = connectionString.Split('@');
string[] x = connectionString.Split('@');
uriBuilder.UserName = x[0];
connectionString = x[1];
uriBuilder.Host = x[1];
}
if (connectionString.Contains(":"))
{
var x = connectionString.Split(':');
connectionString = x[0];
uriBuilder.Port = Convert.ToInt32(x[1]);
}
uriBuilder.Host = connectionString;
else
uriBuilder.Host = connectionString;
var newConnectionInfo = new ConnectionInfo();
newConnectionInfo.CopyFrom(DefaultConnectionInfo.Instance);
newConnectionInfo.Name = Properties.OptionsTabsPanelsPage.Default.IdentifyQuickConnectTabs
newConnectionInfo.Name = Settings.Default.IdentifyQuickConnectTabs
? string.Format(Language.Quick, uriBuilder.Host)
: uriBuilder.Host;
newConnectionInfo.Protocol = protocol;
newConnectionInfo.Hostname = uriBuilder.Host;
newConnectionInfo.Username = uriBuilder.UserName;
if (uriBuilder.Port == -1)
{
newConnectionInfo.SetDefaultPort();
@@ -132,23 +126,24 @@ namespace mRemoteNG.Connection
/// <param name="useDatabase"></param>
/// <param name="import"></param>
/// <param name="connectionFileName"></param>
public void LoadConnections(bool useDatabase, bool import, string connectionFileName)
public void LoadConnections(bool useDatabase, string connectionFileName)
{
var oldConnectionTreeModel = ConnectionTreeModel;
var oldIsUsingDatabaseValue = UsingDatabase;
var connectionLoader = useDatabase
? (IConnectionsLoader)new SqlConnectionsLoader(_localConnectionPropertiesSerializer, _localConnectionPropertiesDataProvider)
: new XmlConnectionsLoader(connectionFileName);
? (IConnectionsLoader)new SqlConnectionsLoader(_localConnectionPropertiesSerializer,
_localConnectionPropertiesDataProvider)
: new XmlConnectionsLoader(connectionFileName, _credentialService, this);
var newConnectionTreeModel = connectionLoader.Load();
var serializationResult = connectionLoader.Load();
if (useDatabase)
LastSqlUpdate = DateTime.Now;
if (newConnectionTreeModel == null)
if (serializationResult == null)
{
DialogFactory.ShowLoadConnectionsFailedDialog(connectionFileName, "Decrypting connection file failed", IsConnectionsFileLoaded);
DialogFactory.ShowLoadConnectionsFailedDialog(connectionFileName, "Decrypting connection file failed",
IsConnectionsFileLoaded);
return;
}
@@ -156,16 +151,19 @@ namespace mRemoteNG.Connection
ConnectionFileName = connectionFileName;
UsingDatabase = useDatabase;
if (!import)
{
_puttySessionsManager.AddSessions();
newConnectionTreeModel.RootNodes.AddRange(_puttySessionsManager.RootPuttySessionsNodes);
}
if (ConnectionTreeModel.RootNodes.Any())
ConnectionTreeModel.RemoveRootNode(ConnectionTreeModel.RootNodes.First());
var rootNode = new RootNodeInfo(RootNodeType.Connection);
rootNode.AddChildRange(serializationResult.ConnectionRecords);
ConnectionTreeModel.AddRootNode(rootNode);
ConnectionTreeModel = newConnectionTreeModel;
UpdateCustomConsPathSetting(connectionFileName);
RaiseConnectionsLoadedEvent(oldConnectionTreeModel, newConnectionTreeModel, oldIsUsingDatabaseValue, useDatabase, connectionFileName);
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Connections loaded using {connectionLoader.GetType().Name}");
// TODO: fix this call
RaiseConnectionsLoadedEvent(new List<ConnectionInfo>(), new List<ConnectionInfo>(),
oldIsUsingDatabaseValue, useDatabase, connectionFileName);
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
$"Connections loaded using {connectionLoader.GetType().Name}");
}
/// <summary>
@@ -229,7 +227,13 @@ namespace mRemoteNG.Connection
/// Optional. The name of the property that triggered
/// this save.
/// </param>
public void SaveConnections(ConnectionTreeModel connectionTreeModel, bool useDatabase, SaveFilter saveFilter, string connectionFileName, bool forceSave = false, string propertyNameTrigger = "")
public void SaveConnections(
IConnectionTreeModel connectionTreeModel,
bool useDatabase,
SaveFilter saveFilter,
string connectionFileName,
bool forceSave = false,
string propertyNameTrigger = "")
{
if (connectionTreeModel == null)
return;
@@ -251,7 +255,8 @@ namespace mRemoteNG.Connection
var previouslyUsingDatabase = UsingDatabase;
var saver = useDatabase
? (ISaver<ConnectionTreeModel>)new SqlConnectionsSaver(saveFilter, _localConnectionPropertiesSerializer, _localConnectionPropertiesDataProvider)
? (ISaver<IConnectionTreeModel>)new SqlConnectionsSaver(saveFilter, _localConnectionPropertiesSerializer,
_localConnectionPropertiesDataProvider)
: new XmlConnectionsSaver(connectionFileName, saveFilter);
saver.Save(connectionTreeModel, propertyNameTrigger);
@@ -267,7 +272,9 @@ namespace mRemoteNG.Connection
}
catch (Exception ex)
{
Runtime.MessageCollector?.AddExceptionMessage(string.Format(Language.ConnectionsFileCouldNotSaveAs, connectionFileName), ex, logOnly: false);
Runtime.MessageCollector?.AddExceptionMessage(
string.Format(Language.ConnectionsFileCouldNotSaveAs,
connectionFileName), ex, logOnly: false);
}
finally
{
@@ -294,7 +301,12 @@ namespace mRemoteNG.Connection
{
lock (SaveLock)
{
SaveConnections(ConnectionTreeModel, UsingDatabase, new SaveFilter(), ConnectionFileName, propertyNameTrigger: propertyNameTrigger);
SaveConnections(
ConnectionTreeModel,
UsingDatabase,
new SaveFilter(),
ConnectionFileName,
propertyNameTrigger: propertyNameTrigger);
}
});
t.SetApartmentState(ApartmentState.STA);
@@ -303,45 +315,37 @@ namespace mRemoteNG.Connection
public string GetStartupConnectionFileName()
{
/*
if (Properties.OptionsBackupPage.Default.LoadConsFromCustomLocation == true && Properties.OptionsBackupPage.Default.BackupLocation != "")
{
return Properties.OptionsBackupPage.Default.BackupLocation;
} else {
return GetDefaultStartupConnectionFileName();
}
*/
if (Properties.OptionsConnectionsPage.Default.ConnectrionFilePath != "")
{
return Properties.OptionsConnectionsPage.Default.ConnectrionFilePath;
}
else
{
return GetDefaultStartupConnectionFileName();
}
return Settings.Default.LoadConsFromCustomLocation == false
? GetDefaultStartupConnectionFileName()
: Settings.Default.CustomConsPath;
}
public string GetDefaultStartupConnectionFileName()
{
return Runtime.IsPortableEdition ? GetDefaultStartupConnectionFileNamePortableEdition() : GetDefaultStartupConnectionFileNameNormalEdition();
return Runtime.IsPortableEdition
? GetDefaultStartupConnectionFileNamePortableEdition()
: GetDefaultStartupConnectionFileNameNormalEdition();
}
private void UpdateCustomConsPathSetting(string filename)
{
if (filename == GetDefaultStartupConnectionFileName())
{
Properties.OptionsBackupPage.Default.LoadConsFromCustomLocation = false;
Settings.Default.LoadConsFromCustomLocation = false;
}
else
{
Properties.OptionsBackupPage.Default.LoadConsFromCustomLocation = true;
Properties.OptionsBackupPage.Default.BackupLocation = filename;
Settings.Default.LoadConsFromCustomLocation = true;
Settings.Default.CustomConsPath = filename;
}
}
private string GetDefaultStartupConnectionFileNameNormalEdition()
{
var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.ProductName, ConnectionsFileInfo.DefaultConnectionsFile);
var appDataPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Application.ProductName,
ConnectionsFileInfo.DefaultConnectionsFile);
return File.Exists(appDataPath) ? appDataPath : GetDefaultStartupConnectionFileNamePortableEdition();
}
@@ -355,16 +359,22 @@ namespace mRemoteNG.Connection
public event EventHandler<ConnectionsLoadedEventArgs> ConnectionsLoaded;
public event EventHandler<ConnectionsSavedEventArgs> ConnectionsSaved;
private void RaiseConnectionsLoadedEvent(Optional<ConnectionTreeModel> previousTreeModel, ConnectionTreeModel newTreeModel, bool previousSourceWasDatabase, bool newSourceIsDatabase, string newSourcePath)
private void RaiseConnectionsLoadedEvent(List<ConnectionInfo> removedConnections, List<ConnectionInfo> addedConnections,
bool previousSourceWasDatabase, bool newSourceIsDatabase,
string newSourcePath)
{
ConnectionsLoaded?.Invoke(this, new ConnectionsLoadedEventArgs(previousTreeModel, newTreeModel, previousSourceWasDatabase, newSourceIsDatabase, newSourcePath));
ConnectionsLoaded?.Invoke(this, new ConnectionsLoadedEventArgs(
removedConnections,
addedConnections,
previousSourceWasDatabase,
newSourceIsDatabase,
newSourcePath));
}
private void RaiseConnectionsSavedEvent(ConnectionTreeModel modelThatWasSaved, bool previouslyUsingDatabase, bool usingDatabase, string connectionFileName)
private void RaiseConnectionsSavedEvent(IConnectionTreeModel modelThatWasSaved, bool previouslyUsingDatabase, bool usingDatabase, string connectionFileName)
{
ConnectionsSaved?.Invoke(this, new ConnectionsSavedEventArgs(modelThatWasSaved, previouslyUsingDatabase, usingDatabase, connectionFileName));
}
#endregion
}
}

View File

@@ -67,8 +67,11 @@ namespace mRemoteNG.Connection
throw new SettingsPropertyNotFoundException($"No property with name '{expectedPropertyName}' found.");
// ensure value is of correct type
var value = Convert.ChangeType(property.GetValue(Instance, null),
propertyFromDestination.PropertyType);
var value = property.PropertyType == propertyFromDestination.PropertyType
? property.GetValue(Instance, null)
: propertyFromDestination.PropertyType == typeof(string)
? property.GetValue(Instance, null).ToString()
: Convert.ChangeType(property.GetValue(Instance, null), propertyFromDestination.PropertyType);
propertyFromDestination.SetValue(destinationInstance, value, null);
}

View File

@@ -28,7 +28,9 @@ namespace mRemoteNG.Connection
var propertyFromSettings = typeof(TSource).GetProperty(propertyNameMutator(property.Name));
if (propertyFromSettings == null)
{
Runtime.MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, $"DefaultConInherit-LoadFrom: Could not load {property.Name}", true);
Runtime.MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg,
$"DefaultConInherit-LoadFrom: Could not load {property.Name}",
true);
continue;
}
@@ -48,7 +50,9 @@ namespace mRemoteNG.Connection
var localValue = property.GetValue(Instance, null);
if (propertyFromSettings == null)
{
Runtime.MessageCollector?.AddMessage(Messages.MessageClass.ErrorMsg, $"DefaultConInherit-SaveTo: Could not load {property.Name}", true);
Runtime.MessageCollector?.AddMessage(Messages.MessageClass.ErrorMsg,
$"DefaultConInherit-SaveTo: Could not load {property.Name}",
true);
continue;
}

View File

@@ -0,0 +1,78 @@
using System;
using mRemoteNG.Config.Connections;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Security;
using mRemoteNG.Tree;
namespace mRemoteNG.Connection
{
public interface IConnectionsService
{
IConnectionTreeModel ConnectionTreeModel { get; }
void NewConnectionsFile(string filename);
ConnectionInfo CreateQuickConnect(string connectionString, ProtocolType protocol);
/// <summary>
/// Load connections from a source. <see cref="connectionFileName"/> is ignored if
/// <see cref="useDatabase"/> is true.
/// </summary>
/// <param name="useDatabase"></param>
/// <param name="import"></param>
/// <param name="connectionFileName"></param>
void LoadConnections(bool useDatabase, string connectionFileName);
/// <summary>
/// When turned on, calls to <see cref="ConnectionsService.SaveConnections()"/> or
/// <see cref="ConnectionsService.SaveConnectionsAsync"/> will not immediately execute.
/// Instead, they will be deferred until <see cref="ConnectionsService.EndBatchingSaves"/>
/// is called.
/// </summary>
void BeginBatchingSaves();
/// <summary>
/// Immediately executes a single <see cref="ConnectionsService.SaveConnections()"/> or
/// <see cref="ConnectionsService.SaveConnectionsAsync"/> if one has been requested
/// since calling <see cref="ConnectionsService.BeginBatchingSaves"/>.
/// </summary>
void EndBatchingSaves();
/// <summary>
/// Saves the currently loaded <see cref="ConnectionsService.ConnectionTreeModel"/> with
/// no <see cref="SaveFilter"/>.
/// </summary>
void SaveConnections();
/// <summary>
/// Saves the given <see cref="ConnectionsService.ConnectionTreeModel"/>.
/// If <see cref="useDatabase"/> is true, <see cref="connectionFileName"/> is ignored
/// </summary>
/// <param name="connectionTreeModel"></param>
/// <param name="useDatabase"></param>
/// <param name="saveFilter"></param>
/// <param name="connectionFileName"></param>
/// <param name="forceSave">Bypasses safety checks that prevent saving if a connection file isn't loaded.</param>
/// <param name="propertyNameTrigger">
/// Optional. The name of the property that triggered
/// this save.
/// </param>
void SaveConnections(
IConnectionTreeModel connectionTreeModel,
bool useDatabase,
SaveFilter saveFilter,
string connectionFileName,
bool forceSave = false,
string propertyNameTrigger = "");
/// <summary>
/// Save the currently loaded connections asynchronously
/// </summary>
/// <param name="propertyNameTrigger">
/// Optional. The name of the property that triggered
/// this save.
/// </param>
void SaveConnectionsAsync(string propertyNameTrigger = "");
event EventHandler<ConnectionsLoadedEventArgs> ConnectionsLoaded;
event EventHandler<ConnectionsSavedEventArgs> ConnectionsSaved;
}
}

Some files were not shown because too many files have changed in this diff Show More