Compare commits

..

1 Commits

Author SHA1 Message Date
Faryan Rezagholi
141b326a20 added SharedLibraryNG project 2021-07-26 01:48:23 +02:00
744 changed files with 12485 additions and 19019 deletions

View File

@@ -1,14 +0,0 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: mRemoteNGDocumentation/conf.py
# Optionally build your docs in additional formats such as PDF
formats:
- pdf

View File

@@ -14,18 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- #283: Support for native PowerShell remoting as new protocol
- #1850: Minify config xml
### Changed
- #2022: Replaced CefSharp with WebView2
- #2014: Revised icons
- #2013: Removed components check
- #2011: Removed screenshot manager
- #2010: Redesigned menus
- #2005: Removed in-app documentation
- #1777: Cleaned up VisualStudio project structure
- #1767: Turned about window into a simple popup form
- #1766: Converted components check page into options page
- #1690: Replaced GeckoFX (Firefox) with CefSharp (Chromium)
- #1325: Language resource files cleanup
### Fixed
- #1884: Allow setting Port when using MSSQL
- #1783: Added missing inheritance properties to SQL scripts
- #1773: Connection issue with mysql - Missing fields in
- #1756: Cannot type any character on MultiSSH toolbar

View File

@@ -90,6 +90,10 @@ 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

@@ -2,7 +2,7 @@
<br/><br/>
<p align="center">
<img width="500" src="https://github.com/mRemoteNG/mRemoteNG/blob/develop/mRemoteNGProjectFiles/Header_dark.png">
<img width="500" src="https://raw.githubusercontent.com/mRemoteNG/mRemoteNG/develop/Tools/img/logo.png">
</p>
<p align="center">
@@ -177,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/develop/mRemoteNGProjectFiles/icon_ReSharper.png">
<img alt="Developed with ReSharper" src="https://raw.githubusercontent.com/mRemoteNG/mRemoteNG/develop/Tools/img/icon_ReSharper.png">
</p>

View File

@@ -0,0 +1,331 @@
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
//
// Hotkey selection control, written by serenity@exscape.org, 2006-08-03
// Please mail me if you find a bug.
//
namespace SharedLibraryNG
{
/// <summary>
/// A simple control that allows the user to select pretty much any valid hotkey combination
/// </summary>
public class HotkeyControl : TextBox
{
private const string KeySeparator = " + ";
// These variables store the current hotkey and modifier(s)
private Keys _keyCode = Keys.None;
private Keys _modifiers = Keys.None;
// ArrayLists used to enforce the use of proper modifiers.
// Shift+A isn't a valid hotkey, for instance, as it would screw up when the user is typing.
private readonly ArrayList _needNonShiftModifier;
private readonly ArrayList _needNonAltGrModifier;
private readonly ContextMenu _emptyContextMenu = new ContextMenu();
/// <summary>
/// Used to make sure that there is no right-click menu available
/// </summary>
public override ContextMenu ContextMenu
{
get
{
return _emptyContextMenu;
}
// ReSharper disable once ValueParameterNotUsed
set
{
base.ContextMenu = _emptyContextMenu;
}
}
/// <summary>
/// Forces the control to be non-multiline
/// </summary>
public override bool Multiline
{
get
{
return base.Multiline;
}
// ReSharper disable once ValueParameterNotUsed
set
{
// Ignore what the user wants; force Multiline to false
base.Multiline = false;
}
}
/// <summary>
/// Creates a new HotkeyControl
/// </summary>
public HotkeyControl()
{
// Handle events that occurs when keys are pressed
KeyUp += HotkeyControl_KeyUp;
// Fill the ArrayLists that contain all invalid hotkey combinations
_needNonShiftModifier = new ArrayList();
_needNonAltGrModifier = new ArrayList();
PopulateModifierLists();
}
protected override void OnCreateControl()
{
base.OnCreateControl();
ContextMenu = _emptyContextMenu; // Disable right-clicking
Multiline = false;
Text = "None";
}
/// <summary>
/// Populates the ArrayLists specifying disallowed hotkeys
/// such as Shift+A, Ctrl+Alt+4 (would produce a dollar sign) etc
/// </summary>
private void PopulateModifierLists()
{
// Shift + 0 - 9, A - Z
for (var k = Keys.D0; k <= Keys.Z; k++)
_needNonShiftModifier.Add((int)k);
// Shift + Numpad keys
for (var k = Keys.NumPad0; k <= Keys.NumPad9; k++)
_needNonShiftModifier.Add((int)k);
// Shift + Misc (,;<./ etc)
for (var k = Keys.Oem1; k <= Keys.OemBackslash; k++)
_needNonShiftModifier.Add((int)k);
// Misc keys that we can't loop through
_needNonShiftModifier.Add((int)Keys.Insert);
_needNonShiftModifier.Add((int)Keys.Help);
_needNonShiftModifier.Add((int)Keys.Multiply);
_needNonShiftModifier.Add((int)Keys.Add);
_needNonShiftModifier.Add((int)Keys.Subtract);
_needNonShiftModifier.Add((int)Keys.Divide);
_needNonShiftModifier.Add((int)Keys.Decimal);
_needNonShiftModifier.Add((int)Keys.Return);
_needNonShiftModifier.Add((int)Keys.Escape);
_needNonShiftModifier.Add((int)Keys.NumLock);
_needNonShiftModifier.Add((int)Keys.Scroll);
_needNonShiftModifier.Add((int)Keys.Pause);
// Ctrl+Alt + 0 - 9
for (var k = Keys.D0; k <= Keys.D9; k++)
_needNonAltGrModifier.Add((int)k);
}
/// <summary>
/// Fires when all keys are released. If the current hotkey isn't valid, reset it.
/// Otherwise, do nothing and keep the text and hotkey as it was.
/// </summary>
void HotkeyControl_KeyUp(object sender, KeyEventArgs e)
{
if (_keyCode == Keys.None && ModifierKeys == Keys.None) ResetHotkey();
}
/// <summary>
/// Handles some misc keys, such as Ctrl+Delete and Shift+Insert
/// </summary>
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
var keyCode = keyData & Keys.KeyCode;
var modifiers = keyData & Keys.Modifiers;
if (keyData == Keys.Back || keyData == Keys.Delete)
{
ResetHotkey();
return true;
}
_keyCode = keyCode;
_modifiers = modifiers;
Redraw();
return true;
}
/// <summary>
/// Clears the current hotkey and resets the TextBox
/// </summary>
public void ResetHotkey()
{
_keyCode = Keys.None;
_modifiers = Keys.None;
Text = "None";
}
/// <summary>
/// Used to get/set the hotkey (e.g. Keys.A)
/// </summary>
public Keys KeyCode
{
get
{
return _keyCode;
}
set
{
_keyCode = value;
Redraw(false);
}
}
/// <summary>
/// Used to get/set the modifier keys (e.g. Keys.Alt | Keys.Control)
/// </summary>
public Keys HotkeyModifiers
{
get
{
return _modifiers;
}
set
{
_modifiers = value;
Redraw(false);
}
}
/// <summary>
/// Redraws the TextBox when necessary.
/// </summary>
/// <param name="validate">Specifies whether this function was called by the Hotkey/HotkeyModifiers properties or by the user.</param>
private void Redraw(bool validate = true)
{
// Only validate input if it comes from the user
if (validate)
{
// No modifier or shift only, AND a hotkey that needs another modifier
if ((_modifiers == Keys.Shift || _modifiers == Keys.None) &&
_needNonShiftModifier.Contains((int) _keyCode))
{
if (_modifiers == Keys.None)
{
// Set Ctrl+Alt as the modifier unless Ctrl+Alt+<key> won't work...
if (_needNonAltGrModifier.Contains((int) _keyCode) == false)
_modifiers = Keys.Alt | Keys.Control;
else // ... in that case, use Shift+Alt instead.
_modifiers = Keys.Alt | Keys.Shift;
}
else
{
// User pressed Shift and an invalid key (e.g. a letter or a number),
// that needs another set of modifier keys
_keyCode = Keys.None;
Text = _modifiers + " + Invalid Key";
return;
}
}
// Check all Ctrl+Alt keys
if ((_modifiers == (Keys.Alt | Keys.Control)) &&
_needNonAltGrModifier.Contains((int) _keyCode))
{
// Ctrl+Alt+4 etc won't work; reset hotkey and tell the user
_keyCode = Keys.None;
Text = _modifiers + " + Invalid Key";
return;
}
}
// Don't allow modifiers keys for _keyCode
if (_keyCode == Keys.ShiftKey ||
_keyCode == Keys.LShiftKey ||
_keyCode == Keys.RShiftKey ||
_keyCode == Keys.ControlKey ||
_keyCode == Keys.LControlKey ||
_keyCode == Keys.RControlKey ||
_keyCode == Keys.Menu ||
_keyCode == Keys.LMenu ||
_keyCode == Keys.RMenu ||
_keyCode == Keys.LWin ||
_keyCode == Keys.RWin)
_keyCode = Keys.None;
if (_modifiers == Keys.None)
{
if (_keyCode == Keys.None)
{
ResetHotkey();
return;
}
// We get here if we've got a hotkey that is valid without a modifier,
// like F1-F12, Media-keys etc.
Text = _keyCode.ToString();
return;
}
Text = string.Join(KeySeparator, new[] { KeysToString(_modifiers), KeysToString(_keyCode) });
}
public static string KeysToString(Keys keys)
{
if (keys == Keys.None) return "None";
var modifiers = (keys & Keys.Modifiers);
var keyCode = (keys & Keys.KeyCode);
var strings = new List<string>();
if (modifiers != 0)
{
var modifierStrings = new List<string>(modifiers.ToString().Replace(", ", ",").Split(','));
modifierStrings.Sort(new KeyModifierComparer());
strings.AddRange(modifierStrings);
}
if (keyCode != 0)
{
var keyString = keyCode.ToString();
var keyHashtable = new Dictionary<string, string>
{
{"Next", "PageDown"},
{"Oemcomma", ","},
{"OemMinus", "-"},
{"OemOpenBrackets", "["},
{"OemPeriod", "."},
{"Oemplus", "="},
{"OemQuestion", "/"},
{"Oemtilde", "`"},
{"D0", "0"},
{"D1", "1"},
{"D2", "2"},
{"D3", "3"},
{"D4", "4"},
{"D5", "5"},
{"D6", "6"},
{"D7", "7"},
{"D8", "8"},
{"D9", "9"},
};
if (keyHashtable.ContainsKey(keyString)) keyString = keyHashtable[keyString];
strings.Add(keyString);
}
return string.Join(KeySeparator, strings.ToArray());
}
private class KeyModifierComparer : IComparer<string>
{
private static readonly List<string> ModifierOrder = new List<string>
{
"control",
"alt",
"shift",
};
public int Compare(string x, string y)
{
var xIndex = ModifierOrder.IndexOf(x.ToLowerInvariant());
var yIndex = ModifierOrder.IndexOf(y.ToLowerInvariant());
return xIndex - yIndex;
}
}
}
}

View File

@@ -0,0 +1,258 @@
//
// Based on code from Stephen Toub's MSDN blog at
// http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx
//
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace SharedLibraryNG
{
public class KeyboardHook
{
// ReSharper disable InconsistentNaming
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PostMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, HookKeyMsgData lParam);
// ReSharper restore InconsistentNaming
[Flags]
public enum ModifierKeys
{
None = 0x0000,
Shift = 0x0001,
LeftShift = 0x002,
RightShift = 0x004,
Control = 0x0008,
LeftControl = 0x010,
RightControl = 0x20,
Alt = 0x0040,
LeftAlt = 0x0080,
RightAlt = 0x0100,
Win = 0x0200,
LeftWin = 0x0400,
RightWin = 0x0800,
}
protected class KeyNotificationEntry
: IEquatable<KeyNotificationEntry>
{
public IntPtr WindowHandle;
public Int32 KeyCode;
public ModifierKeys ModifierKeys;
public Boolean Block;
public bool Equals(KeyNotificationEntry obj)
{
return (WindowHandle == obj.WindowHandle &&
KeyCode == obj.KeyCode &&
ModifierKeys == obj.ModifierKeys &&
Block == obj.Block);
}
}
public const string HookKeyMsgName = "HOOKKEYMSG-{56BE0940-34DA-11E1-B308-C6714824019B}";
private static Int32 _hookKeyMsg;
public static Int32 HookKeyMsg
{
get
{
if (_hookKeyMsg == 0)
{
_hookKeyMsg = Win32.RegisterWindowMessage(HookKeyMsgName).ToInt32();
if (_hookKeyMsg == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return _hookKeyMsg;
}
}
// this is a custom structure that will be passed to
// the requested hWnd via a WM_APP_HOOKKEYMSG message
[StructLayout(LayoutKind.Sequential)]
public class HookKeyMsgData
{
public Int32 KeyCode;
public ModifierKeys ModifierKeys;
public Boolean WasBlocked;
}
private static int _referenceCount;
private static IntPtr _hook;
private static readonly Win32.LowLevelKeyboardProcDelegate LowLevelKeyboardProcStaticDelegate = LowLevelKeyboardProc;
private static readonly List<KeyNotificationEntry> NotificationEntries = new List<KeyNotificationEntry>();
public KeyboardHook()
{
_referenceCount++;
SetHook();
}
~KeyboardHook()
{
_referenceCount--;
if (_referenceCount < 1) UnsetHook();
}
private static void SetHook()
{
if (_hook != IntPtr.Zero) return;
var curProcess = Process.GetCurrentProcess();
var curModule = curProcess.MainModule;
var hook = Win32.SetWindowsHookEx(Win32.WH_KEYBOARD_LL, LowLevelKeyboardProcStaticDelegate, Win32.GetModuleHandle(curModule.ModuleName), 0);
if (hook == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
_hook = hook;
}
private static void UnsetHook()
{
if (_hook == IntPtr.Zero) return;
Win32.UnhookWindowsHookEx(_hook);
_hook = IntPtr.Zero;
}
private static IntPtr LowLevelKeyboardProc(Int32 nCode, IntPtr wParam, Win32.KBDLLHOOKSTRUCT lParam)
{
var wParamInt = wParam.ToInt32();
var result = 0;
if (nCode == Win32.HC_ACTION)
{
switch (wParamInt)
{
case Win32.WM_KEYDOWN:
case Win32.WM_SYSKEYDOWN:
case Win32.WM_KEYUP:
case Win32.WM_SYSKEYUP:
result = OnKey(wParamInt, lParam);
break;
}
}
if (result != 0) return new IntPtr(result);
return Win32.CallNextHookEx(_hook, nCode, wParam, lParam);
}
private static int OnKey(Int32 msg, Win32.KBDLLHOOKSTRUCT key)
{
var result = 0;
foreach (var notificationEntry in NotificationEntries)
if (GetFocusWindow() == notificationEntry.WindowHandle && notificationEntry.KeyCode == key.vkCode)
{
var modifierKeys = GetModifierKeyState();
if (!ModifierKeysMatch(notificationEntry.ModifierKeys, modifierKeys)) continue;
var wParam = new IntPtr(msg);
var lParam = new HookKeyMsgData
{
KeyCode = key.vkCode,
ModifierKeys = modifierKeys,
WasBlocked = notificationEntry.Block,
};
if (!PostMessage(notificationEntry.WindowHandle, HookKeyMsg, wParam, lParam))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (notificationEntry.Block) result = 1;
}
return result;
}
private static IntPtr GetFocusWindow()
{
var guiThreadInfo = new Win32.GUITHREADINFO();
if (!Win32.GetGUIThreadInfo(0, guiThreadInfo))
throw new Win32Exception(Marshal.GetLastWin32Error());
return Win32.GetAncestor(guiThreadInfo.hwndFocus, Win32.GA_ROOT);
}
protected static Dictionary<Int32, ModifierKeys> ModifierKeyTable = new Dictionary<Int32, ModifierKeys>
{
{ Win32.VK_SHIFT, ModifierKeys.Shift },
{ Win32.VK_LSHIFT, ModifierKeys.LeftShift },
{ Win32.VK_RSHIFT, ModifierKeys.RightShift },
{ Win32.VK_CONTROL, ModifierKeys.Control },
{ Win32.VK_LCONTROL, ModifierKeys.LeftControl },
{ Win32.VK_RCONTROL, ModifierKeys.RightControl },
{ Win32.VK_MENU, ModifierKeys.Alt },
{ Win32.VK_LMENU, ModifierKeys.LeftAlt },
{ Win32.VK_RMENU, ModifierKeys.RightAlt },
{ Win32.VK_LWIN, ModifierKeys.LeftWin },
{ Win32.VK_RWIN, ModifierKeys.RightWin },
};
public static ModifierKeys GetModifierKeyState()
{
var modifierKeyState = ModifierKeys.None;
foreach (KeyValuePair<Int32, ModifierKeys> pair in ModifierKeyTable)
{
if ((Win32.GetAsyncKeyState(pair.Key) & Win32.KEYSTATE_PRESSED) != 0) modifierKeyState |= pair.Value;
}
if ((modifierKeyState & ModifierKeys.LeftWin) != 0) modifierKeyState |= ModifierKeys.Win;
if ((modifierKeyState & ModifierKeys.RightWin) != 0) modifierKeyState |= ModifierKeys.Win;
return modifierKeyState;
}
public static Boolean ModifierKeysMatch(ModifierKeys requestedKeys, ModifierKeys pressedKeys)
{
if ((requestedKeys & ModifierKeys.Shift) != 0) pressedKeys &= ~(ModifierKeys.LeftShift | ModifierKeys.RightShift);
if ((requestedKeys & ModifierKeys.Control) != 0) pressedKeys &= ~(ModifierKeys.LeftControl | ModifierKeys.RightControl);
if ((requestedKeys & ModifierKeys.Alt) != 0) pressedKeys &= ~(ModifierKeys.LeftAlt | ModifierKeys.RightAlt);
if ((requestedKeys & ModifierKeys.Win) != 0) pressedKeys &= ~(ModifierKeys.LeftWin | ModifierKeys.RightWin);
return requestedKeys == pressedKeys;
}
public static void RequestKeyNotification(IntPtr windowHandle, Int32 keyCode, Boolean block)
{
RequestKeyNotification(windowHandle, keyCode, ModifierKeys.None, block);
}
public static void RequestKeyNotification(IntPtr windowHandle, Int32 keyCode, ModifierKeys modifierKeys = ModifierKeys.None, Boolean block = false)
{
var newNotificationEntry = new KeyNotificationEntry
{
WindowHandle = windowHandle,
KeyCode = keyCode,
ModifierKeys = modifierKeys,
Block = block,
};
foreach (var notificationEntry in NotificationEntries)
if (notificationEntry == newNotificationEntry) return;
NotificationEntries.Add(newNotificationEntry);
}
public static void CancelKeyNotification(IntPtr windowHandle, Int32 keyCode, Boolean block)
{
CancelKeyNotification(windowHandle, keyCode, ModifierKeys.None, block);
}
public static void CancelKeyNotification(IntPtr windowHandle, Int32 keyCode, ModifierKeys modifierKeys = ModifierKeys.None, Boolean block = false)
{
var notificationEntry = new KeyNotificationEntry
{
WindowHandle = windowHandle,
KeyCode = keyCode,
ModifierKeys = modifierKeys,
Block = block,
};
NotificationEntries.Remove(notificationEntry);
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SharedLibraryNG")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SharedLibraryNG")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f4470853-f933-4203-9d2a-b3c731e225c7")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{0F615504-5F30-4CF2-8341-1DE7FEC95A23}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SharedLibraryNG</RootNamespace>
<AssemblyName>SharedLibraryNG</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="HotkeyControl.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="KeyboardHook.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Win32.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

171
SharedLibraryNG/Win32.cs Normal file
View File

@@ -0,0 +1,171 @@
using System;
using System.Runtime.InteropServices;
namespace SharedLibraryNG
{
// ReSharper disable InconsistentNaming
public static class Win32
{
#region Functions
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProcDelegate lpfn, IntPtr hMod, Int32 dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, KBDLLHOOKSTRUCT lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr RegisterWindowMessage(string lpString);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetGUIThreadInfo(Int32 idThread, GUITHREADINFO lpgui);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetAncestor(IntPtr hwnd, UInt32 gaFlags);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Int16 GetAsyncKeyState(Int32 vKey);
#endregion
#region Delegates
public delegate IntPtr LowLevelKeyboardProcDelegate(Int32 nCode, IntPtr wParam, KBDLLHOOKSTRUCT lParam);
#endregion
#region Structures
[StructLayout(LayoutKind.Sequential)]
public class KBDLLHOOKSTRUCT
{
public Int32 vkCode;
public Int32 scanCode;
public Int32 flags;
public Int32 time;
public IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
public class GUITHREADINFO
{
public GUITHREADINFO()
{
cbSize = Convert.ToInt32(Marshal.SizeOf(this));
}
public Int32 cbSize;
public Int32 flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public RECT rcCaret;
}
#endregion
#region Constants
// GetAncestor
public const int GA_ROOT = 2;
// SetWindowsHookEx
public const int WH_KEYBOARD_LL = 13;
// LowLevelKeyboardProcDelegate
public const int HC_ACTION = 0;
// SendMessage
public const int WM_KEYDOWN = 0x0100;
public const int WM_KEYUP = 0x0101;
public const int WM_SYSKEYDOWN = 0x0104;
public const int WM_SYSKEYUP = 0x0105;
// GetAsyncKeyState
public const int KEYSTATE_PRESSED = 0x8000;
#region Virtual Keys
public const int VK_CANCEL = 0x0003;
public const int VK_BACK = 0x0008;
public const int VK_TAB = 0x0009;
public const int VK_CLEAR = 0x000C;
public const int VK_RETURN = 0x000D;
public const int VK_PAUSE = 0x0013;
public const int VK_ESCAPE = 0x001B;
public const int VK_SNAPSHOT = 0x002C;
public const int VK_INSERT = 0x002D;
public const int VK_DELETE = 0x002E;
public const int VK_HOME = 0x0024;
public const int VK_END = 0x0023;
public const int VK_PRIOR = 0x0021;
public const int VK_NEXT = 0x0022;
public const int VK_LEFT = 0x0025;
public const int VK_UP = 0x0026;
public const int VK_RIGHT = 0x0027;
public const int VK_DOWN = 0x0028;
public const int VK_SELECT = 0x0029;
public const int VK_PRINT = 0x002A;
public const int VK_EXECUTE = 0x002B;
public const int VK_HELP = 0x002F;
public const int VK_LWIN = 0x005B;
public const int VK_RWIN = 0x005C;
public const int VK_APPS = 0x005D;
public const int VK_F1 = 0x0070;
public const int VK_F2 = 0x0071;
public const int VK_F3 = 0x0072;
public const int VK_F4 = 0x0073;
public const int VK_F5 = 0x0074;
public const int VK_F6 = 0x0075;
public const int VK_F7 = 0x0076;
public const int VK_F8 = 0x0077;
public const int VK_F9 = 0x0078;
public const int VK_F10 = 0x0079;
public const int VK_F11 = 0x007A;
public const int VK_F12 = 0x007B;
public const int VK_SHIFT = 0x0010;
public const int VK_LSHIFT = 0x00A0;
public const int VK_RSHIFT = 0x00A1;
public const int VK_CONTROL = 0x0011;
public const int VK_LCONTROL = 0x00A2;
public const int VK_RCONTROL = 0x00A3;
public const int VK_MENU = 0x0012;
public const int VK_LMENU = 0x00A4;
public const int VK_RMENU = 0x00A5;
public const int VK_OEM_1 = 0x00BA;
public const int VK_OEM_2 = 0x00BF;
public const int VK_OEM_3 = 0x00C0;
public const int VK_OEM_4 = 0x00DB;
public const int VK_OEM_5 = 0x00DC;
public const int VK_OEM_6 = 0x00DD;
public const int VK_OEM_7 = 0x00DE;
public const int VK_OEM_8 = 0x00DF;
public const int VK_OEM_102 = 0x00E2;
#endregion
#endregion
// ReSharper restore InconsistentNaming
}
}

BIN
Tools/7zip/7za.dll Normal file

Binary file not shown.

BIN
Tools/7zip/7za.exe Normal file

Binary file not shown.

BIN
Tools/7zip/7zxa.dll Normal file

Binary file not shown.

31
Tools/7zip/License.txt Normal file
View File

@@ -0,0 +1,31 @@
7-Zip Extra
~~~~~~~~~~~
License for use and distribution
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (C) 1999-2019 Igor Pavlov.
7-Zip Extra files are under the GNU LGPL license.
Notes:
You can use 7-Zip Extra on any computer, including a computer in a commercial
organization. You don't need to register or pay for 7-Zip.
GNU LGPL information
--------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You can receive a copy of the GNU Lesser General Public License from
http://www.gnu.org/

111
Tools/7zip/history.txt Normal file
View File

@@ -0,0 +1,111 @@
7-Zip Extra history
-------------------
This file contains only information about changes related to that package exclusively.
The full history of changes is listed in history.txt in main 7-Zip program.
19.00 2019-02-21
-------------------------
- Encryption strength for 7z archives was increased:
the size of random initialization vector was increased from 64-bit to 128-bit,
and the pseudo-random number generator was improved.
- Some bugs were fixed.
18.06 2018-12-30
-------------------------
- The speed for LZMA/LZMA2 compressing was increased by 3-10%,
and there are minor changes in compression ratio.
- Some bugs were fixed.
18.05 2018-04-30
-------------------------
- The speed for LZMA/LZMA2 compressing was increased
by 8% for fastest/fast compression levels and
by 3% for normal/maximum compression levels.
18.03 beta 2018-03-04
-------------------------
- The speed for single-thread LZMA/LZMA2 decoding
was increased by 30% in x64 version and by 3% in x86 version.
- 7-Zip now can use multi-threading for 7z/LZMA2 decoding,
if there are multiple independent data chunks in LZMA2 stream.
9.35 beta 2014-12-07
------------------------------
- SFX modules were moved to LZMA SDK package.
9.34 alpha 2014-06-22
------------------------------
- Minimum supported system now is Windows 2000 for EXE and DLL files.
- all EXE and DLL files use msvcrt.dll.
- 7zr.exe now support AES encryption.
9.18 2010-11-02
------------------------------
- New small SFX module for installers.
9.17 2010-10-04
------------------------------
- New 7-Zip plugin for FAR Manager x64.
9.10 2009-12-30
------------------------------
- 7-Zip for installers now supports LZMA2.
9.09 2009-12-12
------------------------------
- LZMA2 compression method support.
- Some bugs were fixed.
4.65 2009-02-03
------------------------------
- Some bugs were fixed.
4.38 beta 2006-04-13
------------------------------
- SFX for installers now supports new properties in config file:
Progress, Directory, ExecuteFile, ExecuteParameters.
4.34 beta 2006-02-27
------------------------------
- ISetProperties::SetProperties:
it's possible to specify desirable number of CPU threads:
PROPVARIANT: name=L"mt", vt = VT_UI4, ulVal = NumberOfThreads
If "mt" is not defined, 7za.dll will check number of processors in system to set
number of desirable threads.
Now 7za.dll can use:
2 threads for LZMA compressing
N threads for BZip2 compressing
4 threads for BZip2 decompressing
Other codecs use only one thread.
Note: 7za.dll can use additional "small" threads with low CPU load.
- It's possible to call ISetProperties::SetProperties to specify "mt" property for decoder.
4.33 beta 2006-02-05
------------------------------
- Compressing speed and Memory requirements were increased.
Default dictionary size was increased: Fastest: 64 KB, Fast: 1 MB,
Normal: 4 MB, Max: 16 MB, Ultra: 64 MB.
- 7z/LZMA now can use only these match finders: HC4, BT2, BT3, BT4
4.27 2005-09-21
------------------------------
- Some GUIDs/interfaces were changed.
IStream.h:
ISequentialInStream::Read now works as old ReadPart
ISequentialOutStream::Write now works as old WritePart

124
Tools/7zip/readme.txt Normal file
View File

@@ -0,0 +1,124 @@
7-Zip Extra 19.00
-----------------
7-Zip Extra is package of extra modules of 7-Zip.
7-Zip Copyright (C) 1999-2019 Igor Pavlov.
7-Zip is free software. Read License.txt for more information about license.
Source code of binaries can be found at:
http://www.7-zip.org/
This package contains the following files:
7za.exe - standalone console version of 7-Zip with reduced formats support.
7za.dll - library for working with 7z archives
7zxa.dll - library for extracting from 7z archives
License.txt - license information
readme.txt - this file
Far\ - plugin for Far Manager
x64\ - binaries for x64
All 32-bit binaries can work in:
Windows 2000 / 2003 / 2008 / XP / Vista / 7 / 8 / 10
and in any Windows x64 version with WoW64 support.
All x64 binaries can work in any Windows x64 version.
All binaries use msvcrt.dll.
7za.exe
-------
7za.exe - is a standalone console version of 7-Zip with reduced formats support.
Extra: 7za.exe : support for only some formats of 7-Zip.
7-Zip: 7z.exe with 7z.dll : support for all formats of 7-Zip.
7za.exe and 7z.exe from 7-Zip have same command line interface.
7za.exe doesn't use external DLL files.
You can read Help File (7-zip.chm) from 7-Zip package for description
of all commands and switches for 7za.exe and 7z.exe.
7za.exe features:
- High compression ratio in 7z format
- Supported formats:
- Packing / unpacking: 7z, xz, ZIP, GZIP, BZIP2 and TAR
- Unpacking only: Z, lzma, CAB.
- Highest compression ratio for ZIP and GZIP formats.
- Fast compression and decompression
- Strong AES-256 encryption in 7z and ZIP formats.
Note: LZMA SDK contains 7zr.exe - more reduced version of 7za.exe.
But you can use 7zr.exe as "public domain" code.
DLL files
---------
7za.dll and 7zxa.dll are reduced versions of 7z.dll from 7-Zip.
7za.dll and 7zxa.dll support only 7z format.
Note: 7z.dll is main DLL file that works with all archive types in 7-Zip.
7za.dll and 7zxa.dll support the following decoding methods:
- LZMA, LZMA2, PPMD, BCJ, BCJ2, COPY, 7zAES, BZip2, Deflate.
7za.dll also supports 7z encoding with the following encoding methods:
- LZMA, LZMA2, PPMD, BCJ, BCJ2, COPY, 7zAES.
7za.dll and 7zxa.dll work via COM interfaces.
But these DLLs don't use standard COM interfaces for objects creating.
Look also example code that calls DLL functions (in source code of 7-Zip):
7zip\UI\Client7z
Another example of binary that uses these interface is 7-Zip itself.
The following binaries from 7-Zip use 7z.dll:
- 7z.exe (console version)
- 7zG.exe (GUI version)
- 7zFM.exe (7-Zip File Manager)
Note: The source code of LZMA SDK also contains the code for similar DLLs
(DLLs without BZip2, Deflate support). And these files from LZMA SDK can be
used as "public domain" code. If you use LZMA SDK files, you don't need to
follow GNU LGPL rules, if you want to change the code.
License FAQ
-----------
Can I use the EXE or DLL files from 7-Zip in a commercial application?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes, but you are required to specify in documentation for your application:
(1) that you used parts of the 7-Zip program,
(2) that 7-Zip is licensed under the GNU LGPL license and
(3) you must give a link to www.7-zip.org, where the source code can be found.
Can I use the source code of 7-Zip in a commercial application?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since 7-Zip is licensed under the GNU LGPL you must follow the rules of that license.
In brief, it means that any LGPL'ed code must remain licensed under the LGPL.
For instance, you can change the code from 7-Zip or write a wrapper for some
code from 7-Zip and compile it into a DLL; but, the source code of that DLL
(including your modifications / additions / wrapper) must be licensed under
the LGPL or GPL.
Any other code in your application can be licensed as you wish. This scheme allows
users and developers to change LGPL'ed code and recompile that DLL. That is the
idea of free software. Read more here: http://www.gnu.org/.
Note: You can look also LZMA SDK, which is available under a more liberal license.
---
End of document

15
Tools/copy_puttyng.ps1 Normal file
View File

@@ -0,0 +1,15 @@
param (
[string]
[Parameter(Mandatory=$true)]
$SolutionDir,
[string]
[Parameter(Mandatory=$true)]
$TargetDir
)
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
Write-Output "Copying PUTTYNG to correct directory"
Copy-Item -Path (Join-Path -Path $SolutionDir -ChildPath "mRemoteNG\Resources\PuTTYNG.exe") -Destination $TargetDir -Force
Write-Output ""

19
Tools/copy_themes.ps1 Normal file
View File

@@ -0,0 +1,19 @@
param (
[string]
[Parameter(Mandatory=$true)]
$SolutionDir,
[string]
[Parameter(Mandatory=$true)]
$TargetDir
)
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
Write-Output "Copying THEMES folder to output"
$sourceFiles = [io.path]::combine($SolutionDir , 'mRemoteNG\Resources\Themes' )
$DestinationDir = [io.path]::combine($TargetDir , 'Themes')
robocopy $sourceFiles $DestinationDir *.vstheme /s
Write-Output ""

17
Tools/copy_tiles.ps1 Normal file
View File

@@ -0,0 +1,17 @@
param (
[string]
[Parameter(Mandatory=$true)]
$SolutionDir,
[string]
[Parameter(Mandatory=$true)]
$TargetDir
)
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
Write-Output "Copying TILES folder to output"
$sourceFiles = [io.path]::combine($SolutionDir , 'mRemoteNG\Resources\Tiles' )
robocopy $sourceFiles $TargetDir *.*
Write-Output ""

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
Tools/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -37,9 +37,16 @@ Format-Table -AutoSize -Wrap -InputObject @{
"ExcludeFromSigning" = $ExcludeFromSigning
}
& "$PSScriptRoot\copy_puttyng.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir
& "$PSScriptRoot\copy_themes.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir
& "$PSScriptRoot\copy_tiles.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir
& "$PSScriptRoot\sphinx_docs.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir
& "$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_symbols.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir -ConfigurationName $ConfigurationName
& "$PSScriptRoot\zip_portable_files.ps1" -SolutionDir $SolutionDir -TargetDir $TargetDir -ConfigurationName $ConfigurationName

View File

@@ -3,7 +3,7 @@
$SolutionDir
)
$renameTarget = $SolutionDir + "mRemoteNGInstaller\Installer\bin\Release\en-US\mRemoteNG-Installer.msi"
$renameTarget = $SolutionDir + "InstallerProjects\Installer\bin\Release\en-US\mRemoteNG-Installer.msi"
Write-Host $SolutionDir
Write-Host $renameTarget

33
Tools/sphinx_docs.ps1 Normal file
View File

@@ -0,0 +1,33 @@
param (
[string]
[Parameter(Mandatory=$true)]
$SolutionDir,
[string]
[Parameter(Mandatory=$true)]
$TargetDir
)
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
Write-Output "Building HTML-Documentation with Sphinx"
$path_HelpFilesDir = Join-Path -Path $TargetDir -ChildPath "Help"
$path_SphinxSourceDir = Join-Path -Path $SolutionDir -ChildPath "mRemoteNG\Documentation"
# Remove stale Help files, if they exist
if (Test-Path -Path $path_HelpFilesDir) {
Remove-Item -Path $path_HelpFilesDir -Recurse -Force
}
# Build docs
sphinx-build $path_SphinxSourceDir $path_HelpFilesDir
# Place dummy html file if build failed
if (-Not (Test-Path $path_HelpFilesDir\index.html -PathType Leaf)) {
New-Item -Path $path_HelpFilesDir -ItemType "directory"
New-Item $path_HelpFilesDir\index.html
Set-Content $path_HelpFilesDir\index.html 'Welcome to mRemoteNG!'
}
Write-Output ""

View File

@@ -1,72 +0,0 @@
param (
[string]
[Parameter(Mandatory=$true)]
$SolutionDir,
[string]
[Parameter(Mandatory=$true)]
$TargetDir,
[string]
[Parameter(Mandatory=$true)]
$ConfigurationName
)
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
$ConfigurationName = $ConfigurationName.Trim()
Write-Output "Config Name (trimmed): '$($ConfigurationName)'"
$exe = Join-Path -Path $TargetDir -ChildPath $TargetFileName
$Version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($exe).FileVersion
Write-Output "Version is $($version)"
# Fix for AppVeyor
if(!([string]::IsNullOrEmpty($Env:APPVEYOR_BUILD_FOLDER))) {
if(!(test-path "Release")) {
New-Item -ItemType Directory -Force -Path "Release" | Out-Null
}
}
# Package debug symbols zip file
if ($ConfigurationName -match "Release") {
Write-Output "Packaging debug symbols"
if ($ConfigurationName -match "Portable") {
$zipFilePrefix = "mRemoteNG-Portable-symbols"
} else {
$zipFilePrefix = "mRemoteNG-symbols"
}
$debugFile = Join-Path -Path $TargetDir -ChildPath "mRemoteNG.pdb"
# AppVeyor build
if(!([string]::IsNullOrEmpty($Env:APPVEYOR_BUILD_FOLDER))) {
$outputZipPath = Join-Path -Path $SolutionDir -ChildPath "Release\$zipFilePrefix-$($version).zip"
7z a $outputZipPath $debugFile
}
# Local build
else {
$outputZipPath = "$($SolutionDir)Release\$zipFilePrefix-$($version).zip"
Compress-Archive $debugFile $outputZipPath -Force
}
Remove-Item $debugFile
}
# Package portable release zip file
if ($ConfigurationName -eq "Release Portable") {
Write-Output "Packaging portable ZIP file"
# AppVeyor build
if(!([string]::IsNullOrEmpty($Env:APPVEYOR_BUILD_FOLDER))) {
$outputZipPath = Join-Path -Path $SolutionDir -ChildPath "Release\mRemoteNG-Portable-$($version).zip"
7z a -bt -bd -bb1 -mx=9 -tzip -y -r $outputZipPath $TargetDir\*
}
# Local build
else {
$outputZipPath="$($SolutionDir)\Release\mRemoteNG-Portable-$($version).zip"
Compress-Archive $Source $outputZipPath -Force
}
}
Write-Output ""

View File

@@ -0,0 +1,64 @@
param (
[string]
[Parameter(Mandatory=$true)]
$SolutionDir,
[string]
[Parameter(Mandatory=$true)]
$TargetDir,
[string]
[Parameter(Mandatory=$true)]
$ConfigurationName
)
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
if(-not [string]::IsNullOrEmpty($Env:APPVEYOR_BUILD_FOLDER)) {
Write-Output "Too early to run via Appveyor - artifacts don't get generated properly. Exiting"
Exit
}
Write-Output "Solution Dir: '$($SolutionDir)'"
Write-Output "Target Dir: '$($TargetDir)'"
$ConfigurationName = $ConfigurationName.Trim()
Write-Output "Config Name (tirmmed): '$($ConfigurationName)'"
# Windows Sysinternals Sigcheck from http://technet.microsoft.com/en-us/sysinternals/bb897441
$SIGCHECK="$($SolutionDir)Tools\exes\sigcheck.exe"
$SEVENZIP="$($SolutionDir)Tools\7zip\7za.exe"
# Package Zip
if ($ConfigurationName -eq "Release Portable") {
Write-Output "Packaging Release Portable ZIP"
$version = & $SIGCHECK /accepteula -q -n "$($SolutionDir)mRemoteNG\bin\$($ConfigurationName)\mRemoteNG.exe"
Write-Output "Version is $($version)"
$PortableZip="$($SolutionDir)Release\mRemoteNG-Portable-$($version).zip"
$tempFolderPath = Join-Path -Path $SolutionDir -ChildPath "mRemoteNG\bin\package"
Remove-Item -Recurse $tempFolderPath -ErrorAction SilentlyContinue | Out-Null
New-Item $tempFolderPath -ItemType "directory" | Out-Null
Copy-Item "$($SolutionDir)mRemoteNG\Resources\PuTTYNG.exe" -Destination $tempFolderPath
#Write-Output "$($SolutionDir)mRemoteNG\bin\$ConfigurationName"
#Write-Output "$($SolutionDir)mRemoteNG\bin\package"
Copy-Item "$($SolutionDir)mRemoteNG\bin\$ConfigurationName\*" -Destination $tempFolderPath -Recurse -Force
# Delete any PDB files that accidentally get copied into the temp folder
Get-ChildItem -Path $tempFolderPath -Filter "*.pdb" | Remove-Item
Copy-Item "$($SolutionDir)*.txt" -Destination $tempFolderPath
Write-Output "Creating portable ZIP file $($PortableZip)"
Remove-Item -Force $PortableZip -ErrorAction SilentlyContinue
& $SEVENZIP a -bt -bd -bb1 -mx=9 -tzip -y -r $PortableZip (Join-Path -Path $tempFolderPath -ChildPath "*.*")
#& $SEVENZIP a -bt -mx=9 -tzip -y $PortableZip "$($SolutionDir)*.TXT"
}
else {
Write-Output "We will not zip anything - this isnt a portable release build."
}
Write-Output ""

56
Tools/zip_symbols.ps1 Normal file
View File

@@ -0,0 +1,56 @@
param (
[string]
[Parameter(Mandatory=$true)]
$SolutionDir,
[string]
[Parameter(Mandatory=$true)]
$TargetDir,
[string]
[Parameter(Mandatory=$true)]
$ConfigurationName
)
Write-Output "===== Beginning $($PSCmdlet.MyInvocation.MyCommand) ====="
if(-not [string]::IsNullOrEmpty($Env:APPVEYOR_BUILD_FOLDER)) {
Write-Output "Too early to run via Appveyor - artifacts don't get generated properly. Exiting"
Exit
}
Write-Output "Solution Dir: '$($SolutionDir)'"
Write-Output "Target Dir: '$($TargetDir)'"
$ConfigurationName = $ConfigurationName.Trim()
Write-Output "Config Name (trimmed): '$($ConfigurationName)'"
# Windows Sysinternals Sigcheck from http://technet.microsoft.com/en-us/sysinternals/bb897441
$SIGCHECK="$($SolutionDir)Tools\exes\sigcheck.exe"
$SEVENZIP="$($SolutionDir)Tools\7zip\7za.exe"
# Package Zip
if ($ConfigurationName -match "Release") {
Write-Output "Packaging debug symbols"
$version = & $SIGCHECK /accepteula -q -n "$($SolutionDir)mRemoteNG\bin\$($ConfigurationName)\mRemoteNG.exe"
Write-Output "Version is $($version)"
if ($ConfigurationName -match "Portable") {
$zipFilePrefix = "mRemoteNG-Portable-symbols"
} else {
$zipFilePrefix = "mRemoteNG-symbols"
}
$outputZipPath="$($SolutionDir)Release\$zipFilePrefix-$($version).zip"
Write-Output "Creating debug symbols ZIP file $($outputZipPath)"
Remove-Item -Force $outputZipPath -ErrorAction SilentlyContinue
& $SEVENZIP a -bt -bd -bb1 -mx=9 -tzip -y -r $outputZipPath (Join-Path -Path $TargetDir -ChildPath "*.pdb")
}
else {
Write-Output "We will not package debug symbols - this isnt a release build."
}
Write-Output ""

View File

@@ -18,6 +18,8 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Installer", "mRemoteNGInsta
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mRemoteNGSpecs", "mRemoteNGSpecs\mRemoteNGSpecs.csproj", "{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedLibraryNG", "SharedLibraryNG\SharedLibraryNG.csproj", "{0F615504-5F30-4CF2-8341-1DE7FEC95A23}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug Portable|Any CPU = Debug Portable|Any CPU
@@ -118,6 +120,26 @@ Global
{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
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug Portable|Any CPU.ActiveCfg = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug Portable|Any CPU.Build.0 = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug Portable|x86.ActiveCfg = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug Portable|x86.Build.0 = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug|x86.ActiveCfg = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Debug|x86.Build.0 = Debug|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Installer|Any CPU.ActiveCfg = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Installer|Any CPU.Build.0 = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Installer|x86.ActiveCfg = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Installer|x86.Build.0 = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Portable|Any CPU.ActiveCfg = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Portable|Any CPU.Build.0 = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Portable|x86.ActiveCfg = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Portable|x86.Build.0 = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release|Any CPU.Build.0 = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release|x86.ActiveCfg = Release|Any CPU
{0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -5,6 +5,7 @@ using Microsoft.Win32;
using mRemoteNG.App.Info;
using mRemoteNG.Messages;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
using mRemoteNG.UI.Forms;
using mRemoteNG.UI.TaskDialog;

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, IConnectionTreeModel connectionTreeModel)
public static void ExportToFile(ConnectionInfo selectedNode, ConnectionTreeModel connectionTreeModel)
{
try
{
@@ -93,7 +93,8 @@ namespace mRemoteNG.App
serializer = new XmlConnectionsSerializer(cryptographyProvider, connectionNodeSerializer);
break;
case SaveFormat.mRCSV:
serializer = new CsvConnectionsSerializerMremotengFormat(saveFilter, Runtime.CredentialService.RepositoryList);
serializer =
new CsvConnectionsSerializerMremotengFormat(saveFilter, Runtime.CredentialProviderCatalog);
break;
default:
throw new ArgumentOutOfRangeException(nameof(saveFormat), saveFormat, null);

View File

@@ -6,11 +6,12 @@ using mRemoteNG.Config.Import;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.App
{
public static class Import
public static class Import
{
public static void ImportFromFile(ContainerInfo importDestinationContainer)
{

View File

@@ -15,7 +15,6 @@ namespace mRemoteNG.App.Info
public const string UrlDonate = "https://mremoteng.org/contribute";
public const string UrlForum = "https://www.reddit.com/r/mRemoteNG";
public const string UrlBugs = "https://bugs.mremoteng.org";
public const string UrlDocumentation = "https://mremoteng.readthedocs.io/en/latest/";
public static string ApplicationVersion = Application.ProductVersion;
public static readonly string ProductName = Application.ProductName;

View File

@@ -8,16 +8,17 @@ namespace mRemoteNG.App.Info
public static class UpdateChannelInfo
{
public const string STABLE = "Stable";
public const string PREVIEW = "Preview";
public const string NIGHTLY = "Nightly";
public const string BETA = "Beta";
public const string DEV = "Development";
/* no #if here since they are used for unit tests as well */
public const string STABLE_PORTABLE = "update-portable.txt";
public const string PREVIEW_PORTABLE = "preview-update-portable.txt";
public const string NIGHTLY_PORTABLE = "nightly-update-portable.txt";
public const string BETA_PORTABLE = "beta-update-portable.txt";
public const string DEV_PORTABLE = "dev-update-portable.txt";
public const string STABLE_MSI = "update.txt";
public const string PREVIEW_MSI = "preview-update.txt";
public const string NIGHTLY_MSI = "nightly-update.txt";
public const string BETA_MSI = "beta-update.txt";
public const string DEV_MSI = "dev-update.txt";
public static Uri GetUpdateChannelInfo()
@@ -39,10 +40,10 @@ namespace mRemoteNG.App.Info
{
case STABLE:
return STABLE_MSI;
case PREVIEW:
return PREVIEW_MSI;
case NIGHTLY:
return NIGHTLY_MSI;
case BETA:
return BETA_MSI;
case DEV:
return DEV_MSI;
default:
return STABLE_MSI;
}
@@ -54,10 +55,10 @@ namespace mRemoteNG.App.Info
{
case STABLE:
return STABLE_PORTABLE;
case PREVIEW:
return PREVIEW_PORTABLE;
case NIGHTLY:
return NIGHTLY_PORTABLE;
case BETA:
return BETA_PORTABLE;
case DEV:
return DEV_PORTABLE;
default:
return STABLE_PORTABLE;
}
@@ -71,7 +72,7 @@ namespace mRemoteNG.App.Info
private static bool IsValidChannel(string s)
{
return s.Equals(STABLE) || s.Equals(PREVIEW) || s.Equals(NIGHTLY);
return s.Equals(STABLE) || s.Equals(BETA) || s.Equals(DEV);
}
}
}

View File

@@ -1,38 +1,21 @@
using System;
using System.IO;
using System.Linq;
using System.IO;
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(ConnectionsService connectionsService, CredentialService credentialService, SaveConnectionsOnEdit connectionsOnEdit)
public void LoadCredsAndCons()
{
connectionsOnEdit.Subscribe(connectionsService);
new SaveConnectionsOnEdit(Runtime.ConnectionsService);
if (Settings.Default.FirstStart && !Settings.Default.LoadConsFromCustomLocation &&
!File.Exists(connectionsService.GetStartupConnectionFileName()))
connectionsService.NewConnectionsFile(connectionsService.GetStartupConnectionFileName());
if (Settings.Default.FirstStart && !Settings.Default.LoadConsFromCustomLocation &&
!File.Exists(Runtime.ConnectionsService.GetStartupConnectionFileName()))
Runtime.ConnectionsService.NewConnectionsFile(Runtime.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,6 +3,7 @@ using System.Management;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.App.Initialization
{

View File

@@ -2,6 +2,7 @@
using mRemoteNG.Config.Putty;
using mRemoteNG.Connection;
using mRemoteNG.Credential;
using mRemoteNG.Credential.Repositories;
using mRemoteNG.Messages;
using mRemoteNG.Security;
using mRemoteNG.Tools;
@@ -15,6 +16,7 @@ using System.Security;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.App
{
@@ -35,7 +37,7 @@ namespace mRemoteNG.App
/// <summary>
/// Feature flag to enable the credential manager feature
/// </summary>
public static bool UseCredentialManager => true;
public static bool UseCredentialManager => false;
public static WindowList WindowList { get; set; }
public static MessageCollector MessageCollector { get; } = new MessageCollector();
@@ -45,9 +47,10 @@ namespace mRemoteNG.App
public static SecureString EncryptionKey { get; set; } =
new RootNodeInfo(RootNodeType.Connection).PasswordString.ConvertToSecureString();
public static CredentialService CredentialService { get; } = new CredentialServiceFactory().Build();
public static ICredentialRepositoryList CredentialProviderCatalog { get; } = new CredentialRepositoryList();
public static ConnectionsService ConnectionsService { get; } = new ConnectionsService(PuttySessionsManager.Instance, CredentialService);
public static ConnectionsService ConnectionsService { get; } =
new ConnectionsService(PuttySessionsManager.Instance);
#region Connections Loading/Saving
@@ -94,7 +97,7 @@ namespace mRemoteNG.App
connectionFileName = ConnectionsService.GetStartupConnectionFileName();
}
ConnectionsService.LoadConnections(Settings.Default.UseSQLServer, connectionFileName);
ConnectionsService.LoadConnections(Settings.Default.UseSQLServer, false, connectionFileName);
if (Settings.Default.UseSQLServer)
{

View File

@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Windows.Forms;
using mRemoteNG.Config.Putty;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
using mRemoteNG.UI.Controls;
using mRemoteNG.UI.Forms;

View File

@@ -8,8 +8,10 @@ namespace mRemoteNG.App
public static class Windows
{
private static ActiveDirectoryImportWindow _adimportForm;
private static HelpWindow _helpForm;
private static ExternalToolsWindow _externalappsForm;
private static PortScanWindow _portscanForm;
private static ScreenshotManagerWindow _screenshotmanagerForm;
private static UltraVNCWindow _ultravncscForm;
private static ConnectionTreeWindow _treeForm;
@@ -21,6 +23,7 @@ namespace mRemoteNG.App
internal static ConfigWindow ConfigForm { get; set; } = new ConfigWindow();
internal static ErrorAndInfoWindow ErrorsForm { get; set; } = new ErrorAndInfoWindow();
internal static ScreenshotManagerWindow ScreenshotForm { get; set; } = new ScreenshotManagerWindow();
private static UpdateWindow UpdateForm { get; set; } = new UpdateWindow();
internal static SSHTransferWindow SshtransferForm { get; private set; } = new SSHTransferWindow();
@@ -55,6 +58,11 @@ namespace mRemoteNG.App
UpdateForm = new UpdateWindow();
UpdateForm.Show(dockPanel);
break;
case WindowType.Help:
if (_helpForm == null || _helpForm.IsDisposed)
_helpForm = new HelpWindow();
_helpForm.Show(dockPanel);
break;
case WindowType.ExternalApps:
if (_externalappsForm == null || _externalappsForm.IsDisposed)
_externalappsForm = new ExternalToolsWindow();
@@ -64,6 +72,10 @@ namespace mRemoteNG.App
_portscanForm = new PortScanWindow();
_portscanForm.Show(dockPanel);
break;
case WindowType.ScreenshotManager:
_screenshotmanagerForm = new ScreenshotManagerWindow();
_screenshotmanagerForm.Show(dockPanel);
break;
case WindowType.UltraVNCSC:
if (_ultravncscForm == null || _ultravncscForm.IsDisposed)
_ultravncscForm = new UltraVNCWindow();

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,7 +35,7 @@ namespace mRemoteNG.Config.Connections.Multiuser
private void Load(object sender, ConnectionsUpdateAvailableEventArgs args)
{
Runtime.ConnectionsService.LoadConnections(true, "");
Runtime.ConnectionsService.LoadConnections(true, false, "");
args.Handled = true;
}

View File

@@ -1,26 +1,36 @@
using System.Collections.Specialized;
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using mRemoteNG.Connection;
using mRemoteNG.Tools;
using mRemoteNG.UI.Forms;
namespace mRemoteNG.Config.Connections
{
public class SaveConnectionsOnEdit
{
private IConnectionsService _connectionsService;
private readonly ConnectionsService _connectionsService;
public void Subscribe(IConnectionsService connectionsService)
public SaveConnectionsOnEdit(ConnectionsService connectionsService)
{
_connectionsService = connectionsService.ThrowIfNull(nameof(connectionsService));
connectionsService.ConnectionTreeModel.CollectionChanged += ConnectionTreeModelOnCollectionChanged;
connectionsService.ConnectionTreeModel.PropertyChanged += ConnectionTreeModelOnPropertyChanged;
if (connectionsService == null)
throw new ArgumentNullException(nameof(connectionsService));
_connectionsService = connectionsService;
connectionsService.ConnectionsLoaded += ConnectionsServiceOnConnectionsLoaded;
}
public void Unsubscribe()
private void ConnectionsServiceOnConnectionsLoaded(object sender,
ConnectionsLoadedEventArgs connectionsLoadedEventArgs)
{
_connectionsService.ConnectionTreeModel.CollectionChanged -= ConnectionTreeModelOnCollectionChanged;
_connectionsService.ConnectionTreeModel.PropertyChanged -= ConnectionTreeModelOnPropertyChanged;
_connectionsService = null;
connectionsLoadedEventArgs.NewConnectionTreeModel.CollectionChanged +=
ConnectionTreeModelOnCollectionChanged;
connectionsLoadedEventArgs.NewConnectionTreeModel.PropertyChanged += ConnectionTreeModelOnPropertyChanged;
foreach (var oldTree in connectionsLoadedEventArgs.PreviousConnectionTreeModel)
{
oldTree.CollectionChanged -= ConnectionTreeModelOnCollectionChanged;
oldTree.PropertyChanged -= ConnectionTreeModelOnPropertyChanged;
}
}
private void ConnectionTreeModelOnPropertyChanged(object sender,
@@ -40,8 +50,10 @@ namespace mRemoteNG.Config.Connections
{
if (!Properties.Settings.Default.SaveConnectionsAfterEveryEdit)
return;
if (FrmMain.Default.IsClosing)
return;
_connectionsService?.SaveConnectionsAsync(propertyName);
_connectionsService.SaveConnectionsAsync(propertyName);
}
}
}
}

View File

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

View File

@@ -12,6 +12,7 @@ using mRemoteNG.Config.Serializers.Versioning;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
using mRemoteNG.Security;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
@@ -20,7 +21,7 @@ using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Connections
{
public class SqlConnectionsSaver : ISaver<IConnectionTreeModel>
public class SqlConnectionsSaver : ISaver<ConnectionTreeModel>
{
private readonly SaveFilter _saveFilter;
private readonly ISerializer<IEnumerable<LocalConnectionPropertiesModel>, string> _localPropertiesSerializer;
@@ -38,7 +39,7 @@ namespace mRemoteNG.Config.Connections
_dataProvider = localPropertiesDataProvider.ThrowIfNull(nameof(localPropertiesDataProvider));
}
public void Save(IConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
{
var rootTreeNode = connectionTreeModel.RootNodes.OfType<RootNodeInfo>().First();

View File

@@ -1,25 +1,18 @@
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, ICredentialService credentialService, ConnectionsService connectionsService)
public XmlConnectionsLoader(string connectionFilePath)
{
if (string.IsNullOrEmpty(connectionFilePath))
throw new ArgumentException($"{nameof(connectionFilePath)} cannot be null or empty");
@@ -28,26 +21,14 @@ namespace mRemoteNG.Config.Connections
throw new FileNotFoundException($"{connectionFilePath} does not exist");
_connectionFilePath = connectionFilePath;
_connectionsService = connectionsService.ThrowIfNull(nameof(connectionsService));
_credentialService = credentialService.ThrowIfNull(nameof(credentialService));
}
public SerializationResult Load()
public ConnectionTreeModel Load()
{
var dataProvider = new FileDataProvider(_connectionFilePath);
var xmlString = dataProvider.Load();
var deserializer = new CredentialManagerUpgradeForm
{
ConnectionFilePath = _connectionFilePath,
NewCredentialRepoPath = _credentialFilePath,
ConnectionsService = _connectionsService,
CredentialService = _credentialService,
ConnectionDeserializer = new XmlConnectionsDeserializer(PromptForPassword)
};
var serializationResult = deserializer.Deserialize(xmlString);
return serializationResult;
var deserializer = new XmlConnectionsDeserializer(PromptForPassword);
return deserializer.Deserialize(xmlString);
}
private Optional<SecureString> PromptForPassword()

View File

@@ -10,7 +10,7 @@ using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Connections
{
public class XmlConnectionsSaver : ISaver<IConnectionTreeModel>
public class XmlConnectionsSaver : ISaver<ConnectionTreeModel>
{
private readonly string _connectionFileName;
private readonly SaveFilter _saveFilter;
@@ -26,7 +26,7 @@ namespace mRemoteNG.Config.Connections
_saveFilter = saveFilter;
}
public void Save(IConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "")
{
try
{

View File

@@ -1,39 +0,0 @@
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,6 +2,7 @@
using System.IO;
using mRemoteNG.App;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Config.DataProviders
{

View File

@@ -53,26 +53,12 @@ namespace mRemoteNG.Config.DatabaseConnectors
private void BuildDbConnectionStringWithCustomCredentials()
{
string[] hostParts = _dbHost.Split(new char[] { ':' }, 2);
var _dbPort = (hostParts.Length == 2) ? hostParts[1] : "1433";
_dbConnectionString = new SqlConnectionStringBuilder
{
DataSource = $"{hostParts[0]},{_dbPort}",
InitialCatalog = _dbCatalog,
UserID = _dbUsername,
Password = _dbPassword,
}.ToString();
_dbConnectionString = $"Data Source={_dbHost};Initial Catalog={_dbCatalog};User Id={_dbUsername};Password={_dbPassword}";
}
private void BuildDbConnectionStringWithDefaultCredentials()
{
_dbConnectionString = new SqlConnectionStringBuilder
{
DataSource = _dbHost,
InitialCatalog = _dbCatalog,
IntegratedSecurity = true
}.ToString();
_dbConnectionString = $"Data Source={_dbHost};Initial Catalog={_dbCatalog};Integrated Security=True";
}
public void Connect()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
using Microsoft.Win32;
using mRemoteNG.App;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Messages;
using System;
using System;
using System.Collections.Generic;
using System.Management;
using System.Net;
using System.Security.Principal;
using Microsoft.Win32;
using mRemoteNG.App;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Messages;
namespace mRemoteNG.Config.Putty
@@ -55,7 +55,6 @@ 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,23 +1,20 @@
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
public class CsvConnectionsDeserializerMremotengFormat : IDeserializer<string, ConnectionTreeModel>
{
public SerializationResult Deserialize(string serializedData)
public ConnectionTreeModel Deserialize(string serializedData)
{
var lines = serializedData.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.RemoveEmptyEntries);
var csvHeaders = new List<string>();
@@ -32,39 +29,26 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
else
{
var connectionInfo = ParseConnectionInfo(csvHeaders, line);
var parentFieldIndex = csvHeaders.IndexOf("Parent");
if (parentFieldIndex > 0)
parentMapping.Add(connectionInfo, line[parentFieldIndex]);
parentMapping.Add(connectionInfo, line[csvHeaders.IndexOf("Parent")]);
}
}
var root = CreateTreeStructure(parentMapping);
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;
var connectionTreeModel = new ConnectionTreeModel();
connectionTreeModel.AddRootNode(root);
return connectionTreeModel;
}
private List<ConnectionInfo> CreateTreeStructure(Dictionary<ConnectionInfo, string> parentMapping)
private RootNodeInfo CreateTreeStructure(Dictionary<ConnectionInfo, string> parentMapping)
{
var root = new List<ConnectionInfo>();
var root = new RootNodeInfo(RootNodeType.Connection);
foreach (var node in parentMapping)
{
// no parent mapped, add to root
if (string.IsNullOrEmpty(node.Value))
{
root.Add(node.Key);
root.AddChild(node.Key);
continue;
}
@@ -80,7 +64,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
}
else
{
root.Add(node.Key);
root.AddChild(node.Key);
}
}
@@ -101,25 +85,53 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
? new ConnectionInfo(nodeId)
: new ContainerInfo(nodeId);
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.Name = headers.Contains("Name")
? connectionCsv[headers.IndexOf("Name")]
: "";
var hasCredRecordId = Guid.TryParse(
headers.Contains("CredentialRecordId") ? connectionCsv[headers.IndexOf("CredentialRecordId")] : "",
out var credRecordId);
connectionRecord.CredentialRecordId = hasCredRecordId ? credRecordId : Optional<Guid>.Empty;
connectionRecord.Description = headers.Contains("Description")
? connectionCsv[headers.IndexOf("Description")]
: "";
// 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.Icon = headers.Contains("Icon")
? connectionCsv[headers.IndexOf("Icon")]
: "";
connectionRecord.Panel = headers.Contains("Panel")
? connectionCsv[headers.IndexOf("Panel")]
: "";
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")]
: "";
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")]
: "";
@@ -174,12 +186,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
? connectionCsv[headers.IndexOf("RDGatewayHostname")]
: "";
if (headers.Contains("CredentialId"))
{
if (Guid.TryParse(connectionCsv[headers.IndexOf("CredentialId")], out var credId))
connectionRecord.CredentialRecordId = credId;
}
if (headers.Contains("Protocol"))
{
if (Enum.TryParse(connectionCsv[headers.IndexOf("Protocol")], out ProtocolType protocolType))
@@ -819,16 +825,9 @@ 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

@@ -61,7 +61,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
"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");
"RDGatewayUseConnectionCredentials;RDGatewayUsername;RDGatewayPassword;RDGatewayDomain;RedirectAudioCapture;RdpVersion;");
if (_saveFilter.SaveInheritance)
sb.Append("InheritCacheBitmaps;InheritColors;InheritDescription;InheritDisplayThemes;InheritDisplayWallpaper;" +
@@ -74,7 +74,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
"InheritVNCProxyPort;InheritVNCProxyUsername;InheritVNCProxyPassword;InheritVNCColors;InheritVNCSmartSizeMode;InheritVNCViewOnly;" +
"InheritRDGatewayUsageMethod;InheritRDGatewayHostname;InheritRDGatewayUseConnectionCredentials;InheritRDGatewayUsername;" +
"InheritRDGatewayPassword;InheritRDGatewayDomain;InheritRDPAlertIdleTimeout;InheritRDPMinutesToIdleTimeout;InheritSoundQuality;" +
"InheritRedirectAudioCapture;InheritRdpVersion;InheritCredentialRecord");
"InheritRedirectAudioCapture;InheritRdpVersion");
}
private void SerializeNodesRecursive(ConnectionInfo node, StringBuilder sb)
@@ -104,8 +104,18 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
.Append(FormatForCsv(con.GetTreeNodeType()))
.Append(FormatForCsv(con.Description))
.Append(FormatForCsv(con.Icon))
.Append(FormatForCsv(con.Panel))
.Append(FormatForCsv(con.Hostname))
.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))
@@ -162,8 +172,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
.Append(FormatForCsv(con.RDGatewayPassword))
.Append(FormatForCsv(con.RDGatewayDomain))
.Append(FormatForCsv(con.RedirectAudioCapture))
.Append(FormatForCsv(con.RdpVersion))
.Append(FormatForCsv(con.CredentialRecordId));
.Append(FormatForCsv(con.RdpVersion));
if (!_saveFilter.SaveInheritance)
@@ -234,8 +243,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv
.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));
.Append(FormatForCsv(con.Inheritance.RdpVersion));
}
private string FormatForCsv(object value)

View File

@@ -3,6 +3,7 @@ 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;
@@ -11,10 +12,12 @@ 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
public class DataTableDeserializer : IDeserializer<DataTable, ConnectionTreeModel>
{
private readonly ICryptographyProvider _cryptographyProvider;
private readonly SecureString _decryptionKey;
@@ -25,13 +28,12 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
_decryptionKey = decryptionKey.ThrowIfNull(nameof(decryptionKey));
}
public SerializationResult Deserialize(DataTable table)
public ConnectionTreeModel Deserialize(DataTable table)
{
var connectionList = CreateNodesFromTable(table);
var rootNodes = CreateNodeHierarchy(connectionList, table);
var serializationResult = new SerializationResult(rootNodes, new ConnectionToCredentialMap());
return serializationResult;
var connectionTreeModel = CreateNodeHierarchy(connectionList, table);
Runtime.ConnectionsService.IsConnectionsFileLoaded = true;
return connectionTreeModel;
}
private List<ConnectionInfo> CreateNodesFromTable(DataTable table)
@@ -58,7 +60,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
{
var connectionId = row["ConstantID"] as string ?? Guid.NewGuid().ToString();
var connectionInfo = new ConnectionInfo(connectionId);
PopulateConnectionInfoFromDataRow(row, connectionInfo);
PopulateConnectionInfoFromDatarow(row, connectionInfo);
return connectionInfo;
}
@@ -66,11 +68,11 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
{
var containerId = row["ConstantID"] as string ?? Guid.NewGuid().ToString();
var containerInfo = new ContainerInfo(containerId);
PopulateConnectionInfoFromDataRow(row, containerInfo);
PopulateConnectionInfoFromDatarow(row, containerInfo);
return containerInfo;
}
private void PopulateConnectionInfoFromDataRow(DataRow dataRow, ConnectionInfo connectionInfo)
private void PopulateConnectionInfoFromDatarow(DataRow dataRow, ConnectionInfo connectionInfo)
{
connectionInfo.Name = (string)dataRow["Name"];
@@ -81,15 +83,9 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
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.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"];
@@ -188,6 +184,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
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"];
@@ -254,21 +251,28 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
}
}
private List<ConnectionInfo> CreateNodeHierarchy(IReadOnlyCollection<ConnectionInfo> connectionList, DataTable dataTable)
private ConnectionTreeModel CreateNodeHierarchy(List<ConnectionInfo> connectionList, DataTable dataTable)
{
var rootNodes = new List<ConnectionInfo>();
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 id = (string)row["ConstantID"];
var connectionInfo = connectionList.First(node => node.ConstantID == id);
var parentId = (string) row["ParentID"];
var parentId = (string)row["ParentID"];
if (parentId == "0" || connectionList.All(node => node.ConstantID != parentId))
rootNodes.Add(connectionInfo);
rootNode.AddChild(connectionInfo);
else
(connectionList.First(node => node.ConstantID == parentId) as ContainerInfo)?.AddChild(connectionInfo);
(connectionList.First(node => node.ConstantID == parentId) as ContainerInfo)?.AddChild(
connectionInfo);
}
return rootNodes;
return connectionTreeModel;
}
}
}

View File

@@ -105,6 +105,9 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
dataTable.Columns.Add("Description", typeof(string));
dataTable.Columns.Add("Icon", typeof(string));
dataTable.Columns.Add("Panel", typeof(string));
dataTable.Columns.Add("Username", typeof(string));
dataTable.Columns.Add("Domain", typeof(string));
dataTable.Columns.Add("Password", typeof(string));
dataTable.Columns.Add("Hostname", typeof(string));
dataTable.Columns.Add("Port", typeof(int));
dataTable.Columns.Add("Protocol", typeof(string));
@@ -499,10 +502,16 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
dataRow["ParentID"] = connectionInfo.Parent?.ConstantID ?? "";
dataRow["PositionID"] = _currentNodeIndex;
dataRow["LastChange"] = MiscTools.DBTimeStampNow();
dataRow["Expanded"] =
false; // TODO: this column can eventually be removed. we now save this property locally
dataRow["Description"] = connectionInfo.Description;
dataRow["Icon"] = connectionInfo.Icon;
dataRow["Panel"] = connectionInfo.Panel;
dataRow["Username"] = _saveFilter.SaveUsername ? connectionInfo.Username : "";
dataRow["Domain"] = _saveFilter.SaveDomain ? connectionInfo.Domain : "";
dataRow["Password"] = _saveFilter.SavePassword
? _cryptographyProvider.Encrypt(connectionInfo.Password, _encryptionKey)
: "";
dataRow["Hostname"] = connectionInfo.Hostname;
dataRow["VmId"] = connectionInfo.VmId;
dataRow["Protocol"] = connectionInfo.Protocol;
@@ -540,6 +549,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
dataRow["SoundQuality"] = connectionInfo.SoundQuality;
dataRow["RedirectAudioCapture"] = connectionInfo.RedirectAudioCapture;
dataRow["RedirectKeys"] = connectionInfo.RedirectKeys;
dataRow["Connected"] = false; // TODO: this column can eventually be removed. we now save this property locally
dataRow["PreExtApp"] = connectionInfo.PreExtApp;
dataRow["PostExtApp"] = connectionInfo.PostExtApp;
dataRow["MacAddress"] = connectionInfo.MacAddress;
@@ -582,6 +592,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
dataRow["InheritDomain"] = connectionInfo.Inheritance.Domain;
dataRow["InheritIcon"] = connectionInfo.Inheritance.Icon;
dataRow["InheritPanel"] = connectionInfo.Inheritance.Panel;
dataRow["InheritPassword"] = connectionInfo.Inheritance.Password;
dataRow["InheritPort"] = connectionInfo.Inheritance.Port;
dataRow["InheritProtocol"] = connectionInfo.Inheritance.Protocol;
dataRow["InheritSSHTunnelConnectionName"] = connectionInfo.Inheritance.SSHTunnelConnectionName;
@@ -649,6 +660,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql
dataRow["InheritDomain"] = false;
dataRow["InheritIcon"] = false;
dataRow["InheritPanel"] = false;
dataRow["InheritPassword"] = false;
dataRow["InheritPort"] = false;
dataRow["InheritProtocol"] = false;
dataRow["InheritSSHTunnelConnectionName"] = false;

View File

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

View File

@@ -11,25 +11,24 @@ using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Container;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
using mRemoteNG.Security;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI.Forms;
using mRemoteNG.UI.TaskDialog;
using System.Collections.Generic;
using mRemoteNG.Credential;
namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
{
public class XmlConnectionsDeserializer
public class XmlConnectionsDeserializer : IDeserializer<string, ConnectionTreeModel>
{
private XmlDocument _xmlDocument;
private double _confVersion;
private XmlConnectionsDecryptor _decryptor;
private string ConnectionFileName = "";
private const double MaxSupportedConfVersion = 2.7;
private readonly CredentialDomainUserPasswordComparer _credentialComparer = new CredentialDomainUserPasswordComparer();
private const double MaxSupportedConfVersion = 2.8;
private readonly RootNodeInfo _rootNodeInfo = new RootNodeInfo(RootNodeType.Connection);
public Func<Optional<SecureString>> AuthenticationRequestor { get; set; }
@@ -38,12 +37,12 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
AuthenticationRequestor = authenticationRequestor;
}
public SerializationResult Deserialize(string xml)
public ConnectionTreeModel Deserialize(string xml)
{
return Deserialize(xml, false);
}
public SerializationResult Deserialize(string xml, bool import)
public ConnectionTreeModel Deserialize(string xml, bool import)
{
try
{
@@ -51,13 +50,17 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
ValidateConnectionFileVersion();
var rootXmlElement = _xmlDocument.DocumentElement;
var rootNodeInfo = InitializeRootNode(rootXmlElement);
_decryptor = CreateDecryptor(rootNodeInfo, rootXmlElement);
InitializeRootNode(rootXmlElement);
CreateDecryptor(_rootNodeInfo, rootXmlElement);
var connectionTreeModel = new ConnectionTreeModel();
connectionTreeModel.AddRootNode(_rootNodeInfo);
if (_confVersion > 1.3)
{
var protectedString = _xmlDocument.DocumentElement?.Attributes["Protected"].Value;
if (!_decryptor.ConnectionsFileIsAuthentic(protectedString, rootNodeInfo.PasswordString.ConvertToSecureString()))
if (!_decryptor.ConnectionsFileIsAuthentic(protectedString,
_rootNodeInfo.PasswordString.ConvertToSecureString()))
{
return null;
}
@@ -73,11 +76,12 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
}
}
var credentialMap = new ConnectionToCredentialMap();
var rootNodes = AddNodesFromXmlRecursive(_xmlDocument.DocumentElement, credentialMap);
var serializationResult = new SerializationResult(rootNodes, credentialMap);
AddNodesFromXmlRecursive(_xmlDocument.DocumentElement, _rootNodeInfo);
return serializationResult;
if (!import)
Runtime.ConnectionsService.IsConnectionsFileLoaded = true;
return connectionTreeModel;
}
catch (Exception ex)
{
@@ -89,8 +93,8 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
private void LoadXmlConnectionData(string connections)
{
var legacyDecryptor = CreateDecryptor(new RootNodeInfo(RootNodeType.Connection));
connections = legacyDecryptor.LegacyFullFileDecrypt(connections);
CreateDecryptor(new RootNodeInfo(RootNodeType.Connection));
connections = _decryptor.LegacyFullFileDecrypt(connections);
_xmlDocument = new XmlDocument();
if (connections != "")
_xmlDocument.LoadXml(connections);
@@ -131,16 +135,13 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
);
}
private RootNodeInfo InitializeRootNode(XmlElement connectionsRootElement)
private void InitializeRootNode(XmlElement connectionsRootElement)
{
var rootNodeName = connectionsRootElement?.Attributes["Name"].Value.Trim();
return new RootNodeInfo(RootNodeType.Connection)
{
Name = rootNodeName
};
_rootNodeInfo.Name = rootNodeName;
}
private XmlConnectionsDecryptor CreateDecryptor(RootNodeInfo rootNodeInfo, XmlElement connectionsRootElement = null)
private void CreateDecryptor(RootNodeInfo rootNodeInfo, XmlElement connectionsRootElement = null)
{
if (_confVersion >= 2.6)
{
@@ -148,27 +149,24 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
var mode = connectionsRootElement.GetAttributeAsEnum<BlockCipherModes>("BlockCipherMode");
var keyDerivationIterations = connectionsRootElement.GetAttributeAsInt("KdfIterations");
return new XmlConnectionsDecryptor(engine, mode, rootNodeInfo)
_decryptor = new XmlConnectionsDecryptor(engine, mode, rootNodeInfo)
{
AuthenticationRequestor = AuthenticationRequestor,
KeyDerivationIterations = keyDerivationIterations
};
}
return new XmlConnectionsDecryptor(rootNodeInfo)
else
{
AuthenticationRequestor = AuthenticationRequestor
};
_decryptor = new XmlConnectionsDecryptor(_rootNodeInfo)
{AuthenticationRequestor = AuthenticationRequestor};
}
}
private List<ConnectionInfo> AddNodesFromXmlRecursive(XmlNode parentXmlNode, ConnectionToCredentialMap credentialMap)
private void AddNodesFromXmlRecursive(XmlNode parentXmlNode, ContainerInfo parentContainer)
{
try
{
if (!parentXmlNode.HasChildNodes)
return new List<ConnectionInfo>();
var children = new List<ConnectionInfo>();
if (!parentXmlNode.HasChildNodes) return;
foreach (XmlNode xmlNode in parentXmlNode.ChildNodes)
{
var nodeType = xmlNode.GetAttributeAsEnum("Type", TreeNodeType.Connection);
@@ -177,27 +175,24 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
switch (nodeType)
{
case TreeNodeType.Connection:
var connectionInfo = GetConnectionInfoFromXml(xmlNode, credentialMap);
children.Add(connectionInfo);
var connectionInfo = GetConnectionInfoFromXml(xmlNode);
parentContainer.AddChild(connectionInfo);
break;
case TreeNodeType.Container:
var containerInfo = new ContainerInfo();
if (_confVersion >= 0.9)
containerInfo.CopyFrom(GetConnectionInfoFromXml(xmlNode, credentialMap));
containerInfo.CopyFrom(GetConnectionInfoFromXml(xmlNode));
if (_confVersion >= 0.8)
{
containerInfo.IsExpanded = xmlNode.GetAttributeAsBool("Expanded");
}
var subChildren = AddNodesFromXmlRecursive(xmlNode, credentialMap);
subChildren.ForEach(info => containerInfo.AddChild(info));
children.Add(containerInfo);
parentContainer.AddChild(containerInfo);
AddNodesFromXmlRecursive(xmlNode, containerInfo);
break;
}
}
return children;
}
catch (Exception ex)
{
@@ -206,7 +201,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
}
}
private ConnectionInfo GetConnectionInfoFromXml(XmlNode xmlnode, ConnectionToCredentialMap credentialMap)
private ConnectionInfo GetConnectionInfoFromXml(XmlNode xmlnode)
{
if (xmlnode?.Attributes == null)
return null;
@@ -234,21 +229,13 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
: RDPResolutions.FitToWindow;
}
if (_confVersion <= 2.6) // 0.2 - 2.6
if (!Runtime.UseCredentialManager || _confVersion <= 2.6) // 0.2 - 2.6
{
var username = xmlnode.GetAttributeAsString("Username");
var domain = xmlnode.GetAttributeAsString("Domain");
var cred = new CredentialRecord
{
Title = domain.Length > 0 ? $"{domain}\\{username}" : username,
Username = username,
Domain = domain,
Password = _decryptor.Decrypt(xmlnode.GetAttributeAsString("Password")).ConvertToSecureString()
};
if (!_credentialComparer.Equals(cred, new NullCredentialRecord()))
connectionInfo.CredentialRecordId = cred.Id;
#pragma warning disable 618
connectionInfo.Username = xmlnode.GetAttributeAsString("Username");
connectionInfo.Password = _decryptor.Decrypt(xmlnode.GetAttributeAsString("Password"));
connectionInfo.Domain = xmlnode.GetAttributeAsString("Domain");
#pragma warning restore 618
}
}
@@ -570,12 +557,6 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml
connectionInfo.Inheritance.DisableMenuAnimations = xmlnode.GetAttributeAsBool("InheritDisableMenuAnimations");
connectionInfo.Inheritance.DisableCursorShadow = xmlnode.GetAttributeAsBool("InheritDisableCursorShadow");
connectionInfo.Inheritance.DisableCursorBlinking = xmlnode.GetAttributeAsBool("InheritDisableCursorBlinking");
connectionInfo.CredentialRecordId = Guid.TryParse(xmlnode.Attributes?["CredentialId"]?.Value, out var credId)
? credId
: Optional<Guid>.Empty;
connectionInfo.Inheritance.CredentialId = xmlnode.GetAttributeAsBool("InheritCredentialId");
}
}
catch (Exception ex)

View File

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

View File

@@ -9,38 +9,29 @@ namespace mRemoteNG.Config.Serializers.CredentialProviderSerializer
{
public class CredentialRepositoryListDeserializer
{
public IEnumerable<ICredentialRepository> Deserialize(string xml, IEnumerable<ICredentialRepositoryFactory> factories)
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)
{
if (string.IsNullOrEmpty(xml))
return new ICredentialRepository[0];
if (serializer == null)
throw new ArgumentNullException(nameof(serializer));
if (deserializer == null)
throw new ArgumentNullException(nameof(deserializer));
var xdoc = XDocument.Parse(xml);
var repoEntries = xdoc.Descendants("CredentialRepository");
return repoEntries
.Select(ParseConfigEntries)
.Select(config =>
factories
.FirstOrDefault(f => string.Equals(f.SupportsConfigType, config.TypeName))?
.Build(config));
_serializer = serializer;
_deserializer = deserializer;
}
public ICredentialRepositoryConfig ParseConfigEntries(XElement repositoryXElement)
public IEnumerable<ICredentialRepository> Deserialize(string xml)
{
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;
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);
}
}
}

View File

@@ -1,51 +0,0 @@
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

@@ -1,53 +0,0 @@
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

@@ -6,6 +6,7 @@ using mRemoteNG.Config.Import;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;

View File

@@ -1,68 +1,59 @@
using mRemoteNG.Connection;
using System;
using System.IO;
using System.Xml;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security;
using System.Xml;
using mRemoteNG.Credential;
using mRemoteNG.Security;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.MiscSerializers
{
public class PuttyConnectionManagerDeserializer
public class PuttyConnectionManagerDeserializer : IDeserializer<string, ConnectionTreeModel>
{
public SerializationResult Deserialize(string puttycmConnectionsXml)
public ConnectionTreeModel Deserialize(string puttycmConnectionsXml)
{
var result = new SerializationResult(new List<ConnectionInfo>(), new ConnectionToCredentialMap());
var connectionTreeModel = new ConnectionTreeModel();
var root = new RootNodeInfo(RootNodeType.Connection);
connectionTreeModel.AddRootNode(root);
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(puttycmConnectionsXml);
var configurationNode = xmlDocument.SelectSingleNode("/configuration");
var rootXmlNode = configurationNode?.SelectSingleNode("./root");
if (rootXmlNode == null)
return result;
var rootContainer = ReadContainerProperties(rootXmlNode);
result.ConnectionRecords.Add(rootContainer);
foreach (XmlNode node in rootXmlNode.ChildNodes)
var rootNodes = configurationNode?.SelectNodes("./root");
if (rootNodes == null) return connectionTreeModel;
foreach (XmlNode rootNode in rootNodes)
{
rootContainer.AddChild(ImportRecursive(node, result.ConnectionToCredentialMap));
ImportRootOrContainer(rootNode, root);
}
return result;
return connectionTreeModel;
}
private ContainerInfo ImportRecursive(XmlNode xmlNode, ConnectionToCredentialMap credentialMap)
private void ImportRootOrContainer(XmlNode xmlNode, ContainerInfo parentContainer)
{
VerifyNodeType(xmlNode);
var newContainer = ReadContainerProperties(xmlNode);
var newContainer = ImportContainer(xmlNode, parentContainer);
var childNodes = xmlNode.SelectNodes("./*");
if (childNodes == null)
return newContainer;
if (childNodes == null) return;
foreach (XmlNode childNode in childNodes)
{
switch (childNode.Name)
{
case "container":
newContainer.AddChild(ImportRecursive(childNode, credentialMap));
ImportRootOrContainer(childNode, newContainer);
break;
case "connection":
newContainer.AddChild(ImportConnection(childNode, credentialMap));
ImportConnection(childNode, newContainer);
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)
@@ -91,28 +82,25 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
}
}
private ContainerInfo ReadContainerProperties(XmlNode containerNode)
private ContainerInfo ImportContainer(XmlNode containerNode, ContainerInfo parentContainer)
{
var containerInfo = new ContainerInfo
{
Name = containerNode.Attributes?["name"].Value,
IsExpanded = bool.Parse(containerNode.Attributes?["expanded"].InnerText ?? "false")
};
parentContainer.AddChild(containerInfo);
return containerInfo;
}
private ConnectionInfo ImportConnection(XmlNode connectionNode, ConnectionToCredentialMap credentialMap)
private void ImportConnection(XmlNode connectionNode, ContainerInfo parentContainer)
{
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);
var cred = CredentialFromXml(connectionNode);
connectionInfo.CredentialRecordId = cred.Id;
credentialMap.Add(Guid.Parse(connectionInfo.ConstantID), cred);
return connectionInfo;
parentContainer.AddChild(connectionInfo);
}
private ConnectionInfo ConnectionInfoFromXml(XmlNode xmlNode)
@@ -141,6 +129,9 @@ 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
@@ -160,19 +151,5 @@ 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 System.Collections.Generic;
using mRemoteNG.Credential;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.MiscSerializers
{
public class RemoteDesktopConnectionDeserializer
public class RemoteDesktopConnectionDeserializer : IDeserializer<string, ConnectionTreeModel>
{
// .rdp file schema: https://technet.microsoft.com/en-us/library/ff393699(v=ws.10).aspx
public SerializationResult Deserialize(string rdcFileContent)
public ConnectionTreeModel 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,42 +24,21 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
continue;
}
var propertyName = parts[0].Trim().ToLowerInvariant();
var key = parts[0].Trim();
var value = parts[2].Trim();
SetConnectionInfoParameter(connectionInfo, propertyName, value);
if (propertyName.Equals("username"))
username = value;
if (propertyName.Equals("domain"))
domain = value;
SetConnectionInfoParameter(connectionInfo, key, value);
}
var serializationResult = new SerializationResult(new List<ConnectionInfo>(), new ConnectionToCredentialMap());
serializationResult.ConnectionRecords.Add(connectionInfo);
root.AddChild(connectionInfo);
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;
return connectionTreeModel;
}
private void SetConnectionInfoParameter(ConnectionInfo connectionInfo, string key, string value)
{
switch (key)
switch (key.ToLower())
{
case "full address":
var uri = new Uri("dummyscheme" + Uri.SchemeDelimiter + value);
@@ -71,6 +50,12 @@ 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)
{

View File

@@ -1,44 +1,41 @@
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.Credential;
using mRemoteNG.Security;
using mRemoteNG.Tools;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Container;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.MiscSerializers
{
public class RemoteDesktopConnectionManagerDeserializer
public class RemoteDesktopConnectionManagerDeserializer : IDeserializer<string, ConnectionTreeModel>
{
// 1 = RDCMan v2.2
// 3 = RDCMan v2.7
private static int _schemaVersion;
private static int _schemaVersion; /* 1 = RDCMan v2.2
3 = RDCMan v2.7 */
public SerializationResult Deserialize(string rdcmConnectionsXml)
public ConnectionTreeModel Deserialize(string rdcmConnectionsXml)
{
var serializationResult = new SerializationResult(new List<ConnectionInfo>(), new ConnectionToCredentialMap());
var connectionTreeModel = new ConnectionTreeModel();
var root = new RootNodeInfo(RootNodeType.Connection);
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(rdcmConnectionsXml);
var rdcManNode = xmlDocument.SelectSingleNode("/RDCMan");
VerifySchemaVersion(rdcManNode);
VerifyFileVersion(rdcManNode);
var fileNode = rdcManNode?.SelectSingleNode("./file");
var importedItem = ImportFileOrGroup(fileNode, serializationResult.ConnectionToCredentialMap);
ImportFileOrGroup(fileNode, root);
serializationResult.ConnectionRecords.Add(importedItem);
return serializationResult;
connectionTreeModel.AddRootNode(root);
return connectionTreeModel;
}
private static void VerifySchemaVersion(XmlNode rdcManNode)
@@ -83,46 +80,28 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
}
}
private static ContainerInfo ImportFileOrGroup(XmlNode xmlNode, ConnectionToCredentialMap credentialMap)
private static void ImportFileOrGroup(XmlNode xmlNode, ContainerInfo parentContainer)
{
var newContainer = ImportContainer(xmlNode);
var newContainer = ImportContainer(xmlNode, parentContainer);
var childNodes = xmlNode.SelectNodes("./group|./server");
if (childNodes == null)
return newContainer;
if (childNodes == null) return;
foreach (XmlNode childNode in childNodes)
{
ConnectionInfo newChild = null;
// ReSharper disable once SwitchStatementMissingSomeCases
switch (childNode.Name)
{
case "group":
newChild = ImportFileOrGroup(childNode, credentialMap);
ImportFileOrGroup(childNode, newContainer);
break;
case "server":
newChild = ConnectionInfoFromXml(childNode);
ImportServer(childNode, newContainer);
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)
private static ContainerInfo ImportContainer(XmlNode containerPropertiesNode, ContainerInfo parentContainer)
{
if (_schemaVersion == 1)
{
@@ -142,9 +121,16 @@ 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};
@@ -167,7 +153,25 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
connectionInfo.Description = propertiesNode?.SelectSingleNode("./comment")?.InnerText ?? string.Empty;
var logonCredentialsNode = xmlNode.SelectSingleNode("./logonCredentials");
if (logonCredentialsNode?.Attributes?["inherit"]?.Value != "None")
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
{
connectionInfo.Inheritance.Username = true;
connectionInfo.Inheritance.Password = true;
@@ -201,9 +205,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).ConvertToUnsecureString();
connectionInfo.RDGatewayPassword = passwordNode?.Attributes?["storeAsClearText"]?.Value == "True"
? passwordNode.InnerText
: DecryptRdcManPassword(passwordNode?.InnerText);
connectionInfo.RDGatewayDomain = gatewaySettingsNode.SelectSingleNode("./domain")?.InnerText ?? string.Empty;
// ./logonMethod
@@ -345,45 +349,22 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers
return connectionInfo;
}
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)
private static string DecryptRdcManPassword(string ciphertext)
{
if (string.IsNullOrEmpty(ciphertext))
return new SecureString();
return string.Empty;
try
{
var plaintextData = ProtectedData.Unprotect(Convert.FromBase64String(ciphertext), new byte[] { },
DataProtectionScope.LocalMachine);
return Encoding.Unicode.GetString(plaintextData).ConvertToSecureString();
var charArray = Encoding.Unicode.GetChars(plaintextData);
return new string(charArray);
}
catch (Exception /*ex*/)
{
//Runtime.MessageCollector.AddExceptionMessage("RemoteDesktopConnectionManager.DecryptPassword() failed.", ex, logOnly: true);
return new SecureString();
return string.Empty;
}
}
}

View File

@@ -1,23 +0,0 @@
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

@@ -3,6 +3,7 @@ using mRemoteNG.App.Info;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Messages;
using System;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Config.Serializers.Versioning
{

View File

@@ -1,109 +0,0 @@
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

@@ -77,6 +77,9 @@ namespace mRemoteNG.Config.Settings
if (persistString == typeof(ErrorAndInfoWindow).ToString())
return Windows.ErrorsForm;
if (persistString == typeof(ScreenshotManagerWindow).ToString())
return Windows.ScreenshotForm;
}
catch (Exception ex)
{

View File

@@ -1,18 +1,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Linq;
using mRemoteNG.App;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.Http;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Credential;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
using mRemoteNG.Tools.Attributes;
using mRemoteNG.UI.Controls.Adapters;
namespace mRemoteNG.Connection
@@ -27,7 +22,6 @@ namespace mRemoteNG.Connection
private string _panel;
private string _hostname;
private Optional<Guid> _credentialRecordId = new Optional<Guid>();
private string _username = "";
private string _password = "";
private string _domain = "";
@@ -166,7 +160,6 @@ namespace mRemoteNG.Connection
set => SetField(ref _port, value, "Port");
}
[Browsable(false)]
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 2),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Username)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionUsername)),
@@ -177,7 +170,6 @@ namespace mRemoteNG.Connection
set => SetField(ref _username, Settings.Default.DoNotTrimUsername ? value : value?.Trim(), "Username");
}
[Browsable(false)]
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 2),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Password)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionPassword)),
@@ -189,7 +181,6 @@ namespace mRemoteNG.Connection
set => SetField(ref _password, value, "Password");
}
[Browsable(false)]
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 2),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Domain)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionDomain)),
@@ -220,26 +211,6 @@ namespace mRemoteNG.Connection
get => GetPropertyValue("SSHTunnelConnectionName", _sshTunnelConnectionName).Trim();
set => SetField(ref _sshTunnelConnectionName, value?.Trim(), "SSHTunnelConnectionName");
}
[Browsable(false)]
public virtual Optional<Guid> CredentialRecordId
{
get => GetPropertyValue(nameof(CredentialRecordId), _credentialRecordId);
set => SetField(ref _credentialRecordId, value ?? Optional<Guid>.Empty, nameof(CredentialRecordId));
}
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 2),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Credentials)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionCredentials)),
AttributeUsedInAllProtocolsExcept()]
[Editor(typeof(CredentialRecordListAdaptor), typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
public virtual ICredentialRecord CredentialRecord
{
// TODO: this static ref to the cred service makes testing difficult. refactor it to allow easy mocking
get => Runtime.CredentialService.GetEffectiveCredentialRecord(CredentialRecordId, false).FirstOrDefault();
set => CredentialRecordId = Optional<Guid>.FromNullable(value?.Id);
}
#endregion
#region Protocol
@@ -618,7 +589,7 @@ namespace mRemoteNG.Connection
}
[LocalizedAttributes.LocalizedCategory(nameof(Language.Redirect), 6),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.DiskDrives)),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Redirect)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionRedirectDrives)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter)),
AttributeUsedInProtocol(ProtocolType.RDP)]
@@ -663,7 +634,7 @@ namespace mRemoteNG.Connection
}
[LocalizedAttributes.LocalizedCategory(nameof(Language.Redirect), 6),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.SmartCard)),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Redirect)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionRedirectSmartCards)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter)),
AttributeUsedInProtocol(ProtocolType.RDP)]

View File

@@ -15,6 +15,7 @@ using mRemoteNG.Connection.Protocol.Telnet;
using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Container;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tree;
@@ -135,24 +136,22 @@ namespace mRemoteNG.Connection
return filteredProperties;
}
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)
};
public virtual IEnumerable<PropertyInfo> GetSerializableProperties()
{
var excludedProperties = new[]
{
"Parent", "Name", "Hostname", "Port", "Inheritance", "OpenConnections",
"IsContainer", "IsDefault", "PositionID", "ConstantID", "TreeNode", "IsQuickConnect", "PleaseConnect"
};
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()
{

View File

@@ -2,6 +2,7 @@
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection
@@ -48,13 +49,7 @@ namespace mRemoteNG.Connection
#endregion
#region
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 3),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.Credentials)),
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionCredentials)),
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
public bool CredentialId { get; set; }
#region Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 3),
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.Username)),

View File

@@ -6,9 +6,9 @@ using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Messages;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
using mRemoteNG.UI.Forms;
using mRemoteNG.UI.Panels;
using mRemoteNG.Credential;
using mRemoteNG.UI.Tabs;
using mRemoteNG.UI.Window;
using WeifenLuo.WinFormsUI.Docking;
@@ -20,12 +20,6 @@ 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;

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.App;
@@ -13,9 +11,9 @@ 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.Resources.Language;
using mRemoteNG.Security;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
@@ -24,7 +22,7 @@ using mRemoteNG.UI;
namespace mRemoteNG.Connection
{
public class ConnectionsService : IConnectionsService
public class ConnectionsService
{
private static readonly object SaveLock = new object();
private readonly PuttySessionsManager _puttySessionsManager;
@@ -33,7 +31,6 @@ 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; }
@@ -41,18 +38,18 @@ namespace mRemoteNG.Connection
public RemoteConnectionsSyncronizer RemoteConnectionsSyncronizer { get; set; }
public DateTime LastSqlUpdate { get; set; }
public IConnectionTreeModel ConnectionTreeModel { get; private set; } = new ConnectionTreeModel();
public ConnectionTreeModel ConnectionTreeModel { get; private set; }
public ConnectionsService(PuttySessionsManager puttySessionsManager, ICredentialService credentialService)
public ConnectionsService(PuttySessionsManager puttySessionsManager)
{
_puttySessionsManager = puttySessionsManager.ThrowIfNull(nameof(puttySessionsManager));
_credentialService = credentialService.ThrowIfNull(nameof(credentialService));
if (puttySessionsManager == null)
throw new ArgumentNullException(nameof(puttySessionsManager));
_puttySessionsManager = puttySessionsManager;
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)
@@ -60,9 +57,10 @@ namespace mRemoteNG.Connection
try
{
filename.ThrowIfNullOrEmpty(nameof(filename));
ConnectionTreeModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection));
SaveConnections(ConnectionTreeModel, false, new SaveFilter(), filename, true);
LoadConnections(false, filename);
var newConnectionsModel = new ConnectionTreeModel();
newConnectionsModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection));
SaveConnections(newConnectionsModel, false, new SaveFilter(), filename, true);
LoadConnections(false, false, filename);
}
catch (Exception ex)
{
@@ -126,21 +124,22 @@ namespace mRemoteNG.Connection
/// <param name="useDatabase"></param>
/// <param name="import"></param>
/// <param name="connectionFileName"></param>
public void LoadConnections(bool useDatabase, string connectionFileName)
public void LoadConnections(bool useDatabase, bool import, string connectionFileName)
{
var oldConnectionTreeModel = ConnectionTreeModel;
var oldIsUsingDatabaseValue = UsingDatabase;
var connectionLoader = useDatabase
? (IConnectionsLoader)new SqlConnectionsLoader(_localConnectionPropertiesSerializer,
_localConnectionPropertiesDataProvider)
: new XmlConnectionsLoader(connectionFileName, _credentialService, this);
: new XmlConnectionsLoader(connectionFileName);
var serializationResult = connectionLoader.Load();
var newConnectionTreeModel = connectionLoader.Load();
if (useDatabase)
LastSqlUpdate = DateTime.Now;
if (serializationResult == null)
if (newConnectionTreeModel == null)
{
DialogFactory.ShowLoadConnectionsFailedDialog(connectionFileName, "Decrypting connection file failed",
IsConnectionsFileLoaded);
@@ -151,17 +150,16 @@ namespace mRemoteNG.Connection
ConnectionFileName = connectionFileName;
UsingDatabase = useDatabase;
if (ConnectionTreeModel.RootNodes.Any())
ConnectionTreeModel.RemoveRootNode(ConnectionTreeModel.RootNodes.First());
var rootNode = new RootNodeInfo(RootNodeType.Connection);
rootNode.AddChildRange(serializationResult.ConnectionRecords);
ConnectionTreeModel.AddRootNode(rootNode);
if (!import)
{
_puttySessionsManager.AddSessions();
newConnectionTreeModel.RootNodes.AddRange(_puttySessionsManager.RootPuttySessionsNodes);
}
ConnectionTreeModel = newConnectionTreeModel;
UpdateCustomConsPathSetting(connectionFileName);
// TODO: fix this call
RaiseConnectionsLoadedEvent(new List<ConnectionInfo>(), new List<ConnectionInfo>(),
oldIsUsingDatabaseValue, useDatabase, connectionFileName);
RaiseConnectionsLoadedEvent(oldConnectionTreeModel, newConnectionTreeModel, oldIsUsingDatabaseValue,
useDatabase, connectionFileName);
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
$"Connections loaded using {connectionLoader.GetType().Name}");
}
@@ -227,13 +225,12 @@ namespace mRemoteNG.Connection
/// Optional. The name of the property that triggered
/// this save.
/// </param>
public void SaveConnections(
IConnectionTreeModel connectionTreeModel,
bool useDatabase,
SaveFilter saveFilter,
string connectionFileName,
bool forceSave = false,
string propertyNameTrigger = "")
public void SaveConnections(ConnectionTreeModel connectionTreeModel,
bool useDatabase,
SaveFilter saveFilter,
string connectionFileName,
bool forceSave = false,
string propertyNameTrigger = "")
{
if (connectionTreeModel == null)
return;
@@ -255,8 +252,9 @@ namespace mRemoteNG.Connection
var previouslyUsingDatabase = UsingDatabase;
var saver = useDatabase
? (ISaver<IConnectionTreeModel>)new SqlConnectionsSaver(saveFilter, _localConnectionPropertiesSerializer,
_localConnectionPropertiesDataProvider)
? (ISaver<ConnectionTreeModel>)new SqlConnectionsSaver(saveFilter,
_localConnectionPropertiesSerializer,
_localConnectionPropertiesDataProvider)
: new XmlConnectionsSaver(connectionFileName, saveFilter);
saver.Save(connectionTreeModel, propertyNameTrigger);
@@ -359,22 +357,31 @@ namespace mRemoteNG.Connection
public event EventHandler<ConnectionsLoadedEventArgs> ConnectionsLoaded;
public event EventHandler<ConnectionsSavedEventArgs> ConnectionsSaved;
private void RaiseConnectionsLoadedEvent(List<ConnectionInfo> removedConnections, List<ConnectionInfo> addedConnections,
bool previousSourceWasDatabase, bool newSourceIsDatabase,
string newSourcePath)
private void RaiseConnectionsLoadedEvent(Optional<ConnectionTreeModel> previousTreeModel,
ConnectionTreeModel newTreeModel,
bool previousSourceWasDatabase,
bool newSourceIsDatabase,
string newSourcePath)
{
ConnectionsLoaded?.Invoke(this, new ConnectionsLoadedEventArgs(
removedConnections,
addedConnections,
previousTreeModel,
newTreeModel,
previousSourceWasDatabase,
newSourceIsDatabase,
newSourcePath));
}
private void RaiseConnectionsSavedEvent(IConnectionTreeModel modelThatWasSaved, bool previouslyUsingDatabase, bool usingDatabase, string connectionFileName)
private void RaiseConnectionsSavedEvent(ConnectionTreeModel modelThatWasSaved,
bool previouslyUsingDatabase,
bool usingDatabase,
string connectionFileName)
{
ConnectionsSaved?.Invoke(this, new ConnectionsSavedEventArgs(modelThatWasSaved, previouslyUsingDatabase, usingDatabase, connectionFileName));
ConnectionsSaved?.Invoke(this,
new ConnectionsSavedEventArgs(modelThatWasSaved, previouslyUsingDatabase,
usingDatabase,
connectionFileName));
}
#endregion
}
}

View File

@@ -67,11 +67,8 @@ namespace mRemoteNG.Connection
throw new SettingsPropertyNotFoundException($"No property with name '{expectedPropertyName}' found.");
// ensure value is of correct type
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);
var value = Convert.ChangeType(property.GetValue(Instance, null),
propertyFromDestination.PropertyType);
propertyFromDestination.SetValue(destinationInstance, value, null);
}

View File

@@ -1,78 +0,0 @@
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;
}
}

View File

@@ -0,0 +1,34 @@
// Copyright © 2013 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
using CefSharp;
using System;
namespace mRemoteNG.Connection.Protocol.Http
{
public class DownloadHandler : IDownloadHandler
{
public event EventHandler<DownloadItem> OnBeforeDownloadFired;
public event EventHandler<DownloadItem> OnDownloadUpdatedFired;
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
{
OnBeforeDownloadFired?.Invoke(this, downloadItem);
if (!callback.IsDisposed)
{
using (callback)
{
callback.Continue(downloadItem.SuggestedFileName, showDialog: true);
}
}
}
public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
{
OnDownloadUpdatedFired?.Invoke(this, downloadItem);
}
}
}

View File

@@ -0,0 +1,68 @@
using CefSharp;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
namespace mRemoteNG.Connection.Protocol.Http
{
partial class RequestHandler : IRequestHandler
{
public bool GetAuthCredentials(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
{
return false;
}
public IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
{
return null;
}
public bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect)
{
if (request.Url.StartsWith(Cef.CefCommitHash))
{
return false;
}
else
{
Process.Start(request.Url);
return true;
}
}
public void OnDocumentAvailableInMainFrame(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
}
public bool OnCertificateError(IWebBrowser chromiumWebBrowser, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback)
{
return false;
}
public bool OnOpenUrlFromTab(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture)
{
return false;
}
public void OnPluginCrashed(IWebBrowser chromiumWebBrowser, IBrowser browser, string pluginPath)
{
}
public bool OnQuotaRequest(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, long newSize, IRequestCallback callback)
{
return true;
}
public void OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status)
{
}
public void OnRenderViewReady(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
}
public bool OnSelectClientCertificate(IWebBrowser chromiumWebBrowser, IBrowser browser, bool isProxy, string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback)
{
return true;
}
}
}

View File

@@ -1,8 +1,10 @@
using System;
using System.Windows.Forms;
using Microsoft.Web.WebView2.WinForms;
using CefSharp;
using CefSharp.WinForms;
using mRemoteNG.Tools;
using mRemoteNG.App;
using mRemoteNG.Resources.Language;
using mRemoteNG.UI.Tabs;
@@ -12,22 +14,24 @@ namespace mRemoteNG.Connection.Protocol.Http
{
#region Private Properties
private Control _wBrowser;
private string _tabTitle;
private Control wBrowser;
protected string httpOrS;
protected int defaultPort;
private string tabTitle;
private bool browserInitialised = false;
private bool connectCalled = false;
#endregion
#region Public Methods
protected HTTPBase(RenderingEngine renderingEngine)
protected HTTPBase(RenderingEngine RenderingEngine)
{
try
{
if (renderingEngine == RenderingEngine.EdgeChromium)
if (RenderingEngine == RenderingEngine.CEF)
{
Control = new WebView2()
Control = new ChromiumWebBrowser("about:blank")
{
Dock = DockStyle.Fill,
};
@@ -49,26 +53,33 @@ namespace mRemoteNG.Connection.Protocol.Http
try
{
if (InterfaceControl.Parent is ConnectionTab objConnectionTab) _tabTitle = objConnectionTab.TabText;
if (InterfaceControl.Parent is ConnectionTab objConnectionTab) tabTitle = objConnectionTab.TabText;
}
catch (Exception)
{
_tabTitle = "";
tabTitle = "";
}
try
{
_wBrowser = Control;
wBrowser = Control;
if (InterfaceControl.Info.RenderingEngine == RenderingEngine.EdgeChromium)
if (InterfaceControl.Info.RenderingEngine == RenderingEngine.CEF)
{
var edge = (WebView2)_wBrowser;
edge.CoreWebView2InitializationCompleted += Edge_CoreWebView2InitializationCompleted;
var CEFBrowser = (ChromiumWebBrowser)wBrowser;
if (CEFBrowser != null)
{
CEFBrowser.LoadingStateChanged += CefBrowser_LoadingStateChanged;
CEFBrowser.TitleChanged += WBrowser_DocumentTitleChanged;
}
else
{
throw new Exception("Failed to initialize CEF Rendering Engine.");
}
}
else
{
var objWebBrowser = (WebBrowser)_wBrowser;
var objWebBrowser = (WebBrowser)wBrowser;
objWebBrowser.ScrollBarsEnabled = true;
// http://stackoverflow.com/questions/4655662/how-to-ignore-script-errors-in-webbrowser
@@ -76,6 +87,7 @@ namespace mRemoteNG.Connection.Protocol.Http
objWebBrowser.Navigated += WBrowser_Navigated;
objWebBrowser.DocumentTitleChanged += WBrowser_DocumentTitleChanged;
browserInitialised = true;
}
return true;
@@ -91,16 +103,20 @@ namespace mRemoteNG.Connection.Protocol.Http
{
try
{
if (InterfaceControl.Info.RenderingEngine == RenderingEngine.EdgeChromium)
if (InterfaceControl.Info.RenderingEngine == RenderingEngine.CEF)
{
((WebView2)_wBrowser).Source = new Uri(GetUrl());
if (browserInitialised)
{
((ChromiumWebBrowser)wBrowser).Load(GetURL());
}
}
else
{
((WebBrowser)_wBrowser).Navigate(GetUrl());
((WebBrowser)wBrowser).Navigate(GetURL());
}
base.Connect();
connectCalled = true;
return true;
}
catch (Exception ex)
@@ -114,12 +130,22 @@ namespace mRemoteNG.Connection.Protocol.Http
#region Private Methods
private string GetUrl()
private string GetURL()
{
try
{
var strHost = InterfaceControl.Info.Hostname;
/*
* Commenting out since this codes doesn't actually do anything at this time...
* Possibly related to MR-221 and/or MR-533 ????
*
string strAuth = "";
if (((int)Force & (int)ConnectionInfo.Force.NoCredentials) != (int)ConnectionInfo.Force.NoCredentials && !string.IsNullOrEmpty(InterfaceControl.Info.Username) && !string.IsNullOrEmpty(InterfaceControl.Info.Password))
{
strAuth = "Authorization: Basic " + Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(InterfaceControl.Info.Username + ":" + InterfaceControl.Info.Password)) + Environment.NewLine;
}
*/
if (InterfaceControl.Info.Port != defaultPort)
{
if (strHost.EndsWith("/"))
@@ -135,7 +161,6 @@ namespace mRemoteNG.Connection.Protocol.Http
if (strHost.Contains(httpOrS + "://") == false)
strHost = httpOrS + "://" + strHost;
}
return strHost;
}
catch (Exception ex)
@@ -149,17 +174,26 @@ namespace mRemoteNG.Connection.Protocol.Http
#region Events
private void Edge_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
private void CefBrowser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
{
if (!e.IsSuccess)
browserInitialised = !e.IsLoading;
if (browserInitialised)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.HttpFailedUrlBuild, e.InitializationException);
// Unhook the loading state changes now, as navigation is done by the user on links in the control
((ChromiumWebBrowser)wBrowser).LoadingStateChanged -= CefBrowser_LoadingStateChanged;
// If this Connection has already been asked to connect but the browser hadn't finished initalising
// then the connect wouldn't have been allowed to take place, so now we can call it!
if (connectCalled)
{
Connect();
}
}
}
private void WBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
if (!(_wBrowser is WebBrowser objWebBrowser)) return;
if (!(wBrowser is WebBrowser objWebBrowser)) return;
// This can only be set once the WebBrowser control is shown, it will throw a COM exception otherwise.
objWebBrowser.AllowWebBrowserDrop = false;
@@ -173,18 +207,33 @@ namespace mRemoteNG.Connection.Protocol.Http
{
if (!(InterfaceControl.Parent is ConnectionTab tabP)) return;
string shortTitle;
if (((WebBrowser)_wBrowser).DocumentTitle.Length >= 15)
if (InterfaceControl.Info.RenderingEngine == RenderingEngine.CEF)
{
shortTitle = ((WebBrowser)_wBrowser).DocumentTitle.Substring(0, 10) + "...";
if (((TitleChangedEventArgs)e).Title.Length >= 15)
{
shortTitle = ((TitleChangedEventArgs)e).Title.Substring(0, 10) + "...";
}
else
{
shortTitle = ((CefSharp.TitleChangedEventArgs)e).Title;
}
}
else
{
shortTitle = ((WebBrowser)_wBrowser).DocumentTitle;
if (((WebBrowser)wBrowser).DocumentTitle.Length >= 15)
{
shortTitle = ((WebBrowser)wBrowser).DocumentTitle.Substring(0, 10) + "...";
}
else
{
shortTitle = ((WebBrowser)wBrowser).DocumentTitle;
}
}
if (!string.IsNullOrEmpty(_tabTitle))
if (!string.IsNullOrEmpty(tabTitle))
{
tabP.TabText = _tabTitle + @" - " + shortTitle;
tabP.TabText = tabTitle + @" - " + shortTitle;
}
else
{
@@ -197,6 +246,38 @@ namespace mRemoteNG.Connection.Protocol.Http
}
}
private void geckoBrowser_DocumentTitleChanged(object sender, EventArgs e)
{
try
{
if (!(InterfaceControl.Parent is ConnectionTab tabP)) return;
string shortTitle;
if (((WebBrowser)wBrowser).DocumentTitle.Length >= 15)
{
shortTitle = ((WebBrowser)wBrowser).DocumentTitle.Substring(0, 10) + "...";
}
else
{
shortTitle = ((WebBrowser)wBrowser).DocumentTitle;
}
if (!string.IsNullOrEmpty(tabTitle))
{
tabP.TabText = tabTitle + @" - " + shortTitle;
}
else
{
tabP.TabText = shortTitle;
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.HttpDocumentTileChangeFailed, ex);
}
}
#endregion
#region Enums
@@ -207,7 +288,7 @@ namespace mRemoteNG.Connection.Protocol.Http
IE = 1,
[LocalizedAttributes.LocalizedDescription(nameof(Language.HttpCEF))]
EdgeChromium = 2
CEF = 2
}
#endregion

View File

@@ -6,6 +6,7 @@ using System.Windows.Forms;
using mRemoteNG.App;
using mRemoteNG.Messages;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol
@@ -63,7 +64,7 @@ namespace mRemoteNG.Connection.Protocol
return false;
}
var argParser = new ExternalToolArgumentParser(_externalTool.ConnectionInfo, Runtime.CredentialService);
var argParser = new ExternalToolArgumentParser(_externalTool.ConnectionInfo);
_process = new Process
{
StartInfo =

View File

@@ -3,6 +3,7 @@ using System.Drawing;
using System.Windows.Forms;
using mRemoteNG.App;
using mRemoteNG.Messages;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Connection.Protocol.PowerShell
{

View File

@@ -8,6 +8,7 @@ using mRemoteNG.Connection.Protocol.VNC;
using System;
using mRemoteNG.Connection.Protocol.PowerShell;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Connection.Protocol
{

View File

@@ -1,4 +1,5 @@
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol
{

View File

@@ -1,15 +1,16 @@
using mRemoteNG.App;
using mRemoteNG.Messages;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tools.Cmdline;
using mRemoteNG.UI;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.Security;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
// ReSharper disable ArrangeAccessorOwnerBody
@@ -78,20 +79,45 @@ namespace mRemoteNG.Connection.Protocol
if (PuttyProtocol == Putty_Protocol.ssh)
{
var username = "";
var password = "";
if (!string.IsNullOrEmpty(InterfaceControl.Info?.Username))
{
username = InterfaceControl.Info.Username;
}
else
{
// ReSharper disable once SwitchStatementMissingSomeCases
switch (Settings.Default.EmptyCredentials)
{
case "windows":
username = Environment.UserName;
break;
case "custom":
username = Settings.Default.DefaultUsername;
break;
}
}
if (!string.IsNullOrEmpty(InterfaceControl.Info?.Password))
{
password = InterfaceControl.Info.Password;
}
else
{
if (Settings.Default.EmptyCredentials == "custom")
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
password = cryptographyProvider.Decrypt(Settings.Default.DefaultPassword,
Runtime.EncryptionKey);
}
}
arguments.Add("-" + (int)PuttySSHVersion);
if (!Force.HasFlag(ConnectionInfo.Force.NoCredentials))
{
var cred = Runtime.CredentialService.GetEffectiveCredentialRecord(InterfaceControl?.Info.CredentialRecordId
.FirstOrDefault()).FirstOrDefault();
var username = cred.Username;
var password = cred.Password.ConvertToUnsecureString();
if (!string.IsNullOrEmpty(username))
{
arguments.Add("-l", username);
@@ -124,11 +150,11 @@ namespace mRemoteNG.Connection.Protocol
PuttyProcess.Exited += ProcessExited;
PuttyProcess.Start();
PuttyProcess.WaitForInputIdle(Properties.Settings.Default.MaxPuttyWaitTime * 1000);
PuttyProcess.WaitForInputIdle(Settings.Default.MaxPuttyWaitTime * 1000);
var startTicks = Environment.TickCount;
while (PuttyHandle.ToInt32() == 0 &
Environment.TickCount < startTicks + Properties.Settings.Default.MaxPuttyWaitTime * 1000)
Environment.TickCount < startTicks + Settings.Default.MaxPuttyWaitTime * 1000)
{
if (_isPuttyNg)
{

View File

@@ -1,4 +1,5 @@
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol.RDP
{

View File

@@ -1,4 +1,5 @@
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol.RDP
{

View File

@@ -1,4 +1,5 @@
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol.RDP
{

View File

@@ -1,4 +1,5 @@
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol.RDP
{

View File

@@ -1,4 +1,5 @@
using System.ComponentModel;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol.RDP

View File

@@ -1,4 +1,5 @@
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol.RDP
{

View File

@@ -1,4 +1,5 @@
using mRemoteNG.Tools;
using mRemoteNG.Resources.Language;
using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol.RDP
{

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections;
using mRemoteNG.App;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Connection.Protocol.RDP
{

View File

@@ -8,13 +8,13 @@ using AxMSTSCLib;
using mRemoteNG.App;
using mRemoteNG.Messages;
using mRemoteNG.Properties;
using mRemoteNG.Resources.Language;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.UI;
using mRemoteNG.UI.Forms;
using mRemoteNG.UI.Tabs;
using MSTSCLib;
using System.Linq;
using mRemoteNG.Security;
namespace mRemoteNG.Connection.Protocol.RDP
{
@@ -379,9 +379,9 @@ namespace mRemoteNG.Connection.Protocol.RDP
{
if (connectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.Yes)
{
_rdpClient.TransportSettings2.GatewayUsername = connectionInfo.CredentialRecord.Username;
_rdpClient.TransportSettings2.GatewayPassword = connectionInfo.CredentialRecord.Password.ConvertToUnsecureString();
_rdpClient.TransportSettings2.GatewayDomain = connectionInfo.CredentialRecord.Domain;
_rdpClient.TransportSettings2.GatewayUsername = connectionInfo.Username;
_rdpClient.TransportSettings2.GatewayPassword = connectionInfo.Password;
_rdpClient.TransportSettings2.GatewayDomain = connectionInfo?.Domain;
}
else if (connectionInfo.RDGatewayUseConnectionCredentials ==
RDGatewayUseConnectionCredentials.SmartCard)
@@ -457,15 +457,58 @@ namespace mRemoteNG.Connection.Protocol.RDP
return;
}
var cred = Runtime.CredentialService.GetEffectiveCredentialRecord(connectionInfo.CredentialRecordId
.FirstOrDefault()).FirstOrDefault();
var userName = connectionInfo?.Username ?? "";
var password = connectionInfo?.Password ?? "";
var domain = connectionInfo?.Domain ?? "";
_rdpClient.UserName = cred.Username ?? "";
_rdpClient.AdvancedSettings2.ClearTextPassword = cred.Password?.ConvertToUnsecureString() ?? "";
_rdpClient.Domain = cred.Domain ?? "";
if (string.IsNullOrEmpty(userName))
{
if (Settings.Default.EmptyCredentials == "windows")
{
_rdpClient.UserName = Environment.UserName;
}
else if (Settings.Default.EmptyCredentials == "custom")
{
_rdpClient.UserName = Settings.Default.DefaultUsername;
}
}
else
{
_rdpClient.UserName = userName;
}
if (string.IsNullOrEmpty(password))
{
if (Settings.Default.EmptyCredentials == "custom")
{
if (Settings.Default.DefaultPassword != "")
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
_rdpClient.AdvancedSettings2.ClearTextPassword =
cryptographyProvider.Decrypt(Settings.Default.DefaultPassword, Runtime.EncryptionKey);
}
}
}
else
{
_rdpClient.AdvancedSettings2.ClearTextPassword = password;
}
if (string.IsNullOrEmpty(domain))
{
if (Settings.Default.EmptyCredentials == "windows")
{
_rdpClient.Domain = Environment.UserDomainName;
}
else if (Settings.Default.EmptyCredentials == "custom")
{
_rdpClient.Domain = Settings.Default.DefaultDomain;
}
}
else
{
_rdpClient.Domain = domain;
}
}
catch (Exception ex)
{
@@ -774,4 +817,4 @@ namespace mRemoteNG.Connection.Protocol.RDP
#endregion
}
}
}

View File

@@ -3,6 +3,7 @@ using mRemoteNG.App;
using MSTSCLib;
using System;
using System.Windows.Forms;
using mRemoteNG.Resources.Language;
namespace mRemoteNG.Connection.Protocol.RDP
{

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