From 770d1821de43cf1d0a93c79025995bdd812a76ee Mon Sep 17 00:00:00 2001 From: Codeusa Date: Mon, 23 Oct 2017 04:28:07 -0400 Subject: [PATCH] Stability patch, fixes broken GPU detection code, updates the websocket framework to be real-time, removes crappy agent spawning system, removes IPC, fixes HTTP server traversal, --- .gitignore | 1 + AgentInterface/AgentInterface.csproj | 163 --- AgentInterface/Api/Win32/Display.cs | 910 ----------------- AgentInterface/Api/Win32/ProcessStarter.cs | 247 ----- AgentInterface/FodyWeavers.xml | 4 - AgentInterface/FrameContract.cs | 20 - AgentInterface/InputContract.cs | 52 - AgentInterface/Properties/AssemblyInfo.cs | 36 - AgentInterface/packages.config | 11 - Deps/OpenHardwareMonitorLib.dll | Bin 0 -> 266752 bytes .../Api/Network/Messages/MessageBuilder.cs | 12 +- .../Api/Network/Models/SystemInformation.cs | 2 +- .../PacketHandlers/AccountPacketHandler.cs | 1 - .../PacketHandlers/DisplayPacketHandler.cs | 14 +- .../PacketHandlers/FilePacketHandler.cs | 2 +- .../PacketHandlers/GpuPacketHandler.cs | 5 +- .../PacketHandlers/ProcessPacketHandler.cs | 29 +- .../ScreenSharePacketHandler.cs | 217 +--- .../PacketHandlers/ServerPacketHandler.cs | 2 +- .../PacketHandlers/SettingsPacketHandler.cs | 2 +- .../Api/Network/UlteriusAgentClient.cs | 313 ------ .../Api/Services/LocalSystem/SystemService.cs | 42 +- .../Api/Services/Network/NetworkService.cs | 2 +- .../Api/Services/Update/UpdateService.cs | 9 +- .../Api}/SystemData.cs | 198 ++-- RemoteTaskServer/Api/UlteriusApiServer.cs | 492 ++++----- .../Api/Win32/Desktop.cs | 4 +- RemoteTaskServer/Api/Win32/DesktopWatcher.cs | 61 ++ RemoteTaskServer/Api/Win32/Display.cs | 477 +++++++++ .../DesktopDuplicationException.cs | 2 +- .../DesktopDuplication/DesktopDuplicator.cs | 707 ++++++------- .../DesktopDuplication/DesktopFrame.cs | 13 +- .../DesktopDuplication/FinishedRegions.cs | 9 +- .../DesktopDuplication/MovedRegion.cs | 2 +- .../Api/Win32}/ScreenShare/ImageUtils.cs | 0 .../ScreenShare}/Models/DisplayInformation.cs | 2 +- .../ScreenShare}/Models/FrameInformation.cs | 15 +- .../Models/ResolutionInformation.cs | 2 +- .../Api/Win32}/ScreenShare/ScreenData.cs | 14 +- .../Api/Win32/SessionInfo.cs | 2 +- .../Api/Win32/Win32Exception.cs | 2 +- .../Api/Win32/WinApi.cs | 2 +- .../Api/Win32/WindowsIdentityImpersonator.cs | 2 +- RemoteTaskServer/App.config | 142 +-- RemoteTaskServer/FodyWeavers.xml | 10 - .../Forms/Utilities/UlteriusTray.cs | 1 - RemoteTaskServer/Program.cs | 54 +- RemoteTaskServer/Properties/AssemblyInfo.cs | 4 +- .../Handlers/InputTerminalRequestHandler.cs | 2 +- .../Messaging/WebSocketHandler.cs | 5 +- .../Messaging/WebSocketQueueServer.cs | 25 +- RemoteTaskServer/Ulterius.cs | 37 +- RemoteTaskServer/UlteriusAgent.cs | 82 -- RemoteTaskServer/UlteriusServer.csproj | 896 ++++++++--------- .../UlteriusServer.csproj.DotSettings | 2 + .../Utilities}/AppEnvironment.cs | 2 +- .../Utilities}/Config.cs | 2 +- .../Utilities/ExceptionHandler.cs | 1 - .../Utilities/Extensions/ProcessExtensions.cs | 209 ++++ .../Utilities/Extensions/StringExtensions.cs | 930 ++++++++++-------- .../Utilities/Security/AuthUtils.cs | 9 +- RemoteTaskServer/Utilities/Tools.cs | 50 +- RemoteTaskServer/Utilities/Trace.cs | 1 - .../Utilities/Usage/HardwareSurvey.cs | 151 --- RemoteTaskServer/WebCams/WebCamManager.cs | 2 +- RemoteTaskServer/WebServer/HttpServer.cs | 13 +- .../WebSocketAPI/WebSocketEventListener.cs | 39 +- RemoteTaskServer/packages.config | 55 +- UlteriusAgent/App.config | 44 +- UlteriusAgent/FodyWeavers.xml | 10 - UlteriusAgent/Networking/FrameAgent.cs | 87 -- UlteriusAgent/Networking/InputAgent.cs | 159 --- UlteriusAgent/Networking/Tools.cs | 34 - UlteriusAgent/Program.cs | 107 +- UlteriusAgent/UlteriusAgent.cs | 107 ++ UlteriusAgent/UlteriusAgent.csproj | 190 ++-- UlteriusAgent/packages.config | 5 + UlteriusServer.sln | 93 +- 78 files changed, 2976 insertions(+), 4686 deletions(-) delete mode 100644 AgentInterface/AgentInterface.csproj delete mode 100644 AgentInterface/Api/Win32/Display.cs delete mode 100644 AgentInterface/Api/Win32/ProcessStarter.cs delete mode 100644 AgentInterface/FodyWeavers.xml delete mode 100644 AgentInterface/FrameContract.cs delete mode 100644 AgentInterface/InputContract.cs delete mode 100644 AgentInterface/Properties/AssemblyInfo.cs delete mode 100644 AgentInterface/packages.config create mode 100644 Deps/OpenHardwareMonitorLib.dll delete mode 100644 RemoteTaskServer/Api/Network/UlteriusAgentClient.cs rename {AgentInterface/Api/System => RemoteTaskServer/Api}/SystemData.cs (70%) rename {AgentInterface => RemoteTaskServer}/Api/Win32/Desktop.cs (99%) create mode 100644 RemoteTaskServer/Api/Win32/DesktopWatcher.cs create mode 100644 RemoteTaskServer/Api/Win32/Display.cs rename {AgentInterface/Api => RemoteTaskServer/Api/Win32}/ScreenShare/DesktopDuplication/DesktopDuplicationException.cs (73%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32}/ScreenShare/DesktopDuplication/DesktopDuplicator.cs (83%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32}/ScreenShare/DesktopDuplication/DesktopFrame.cs (90%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32}/ScreenShare/DesktopDuplication/FinishedRegions.cs (65%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32}/ScreenShare/DesktopDuplication/MovedRegion.cs (91%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32}/ScreenShare/ImageUtils.cs (100%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32/ScreenShare}/Models/DisplayInformation.cs (95%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32/ScreenShare}/Models/FrameInformation.cs (56%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32/ScreenShare}/Models/ResolutionInformation.cs (85%) rename {AgentInterface/Api => RemoteTaskServer/Api/Win32}/ScreenShare/ScreenData.cs (97%) rename {AgentInterface => RemoteTaskServer}/Api/Win32/SessionInfo.cs (99%) rename {AgentInterface => RemoteTaskServer}/Api/Win32/Win32Exception.cs (99%) rename {AgentInterface => RemoteTaskServer}/Api/Win32/WinApi.cs (99%) rename {AgentInterface => RemoteTaskServer}/Api/Win32/WindowsIdentityImpersonator.cs (98%) delete mode 100644 RemoteTaskServer/FodyWeavers.xml delete mode 100644 RemoteTaskServer/UlteriusAgent.cs create mode 100644 RemoteTaskServer/UlteriusServer.csproj.DotSettings rename {AgentInterface/Settings => RemoteTaskServer/Utilities}/AppEnvironment.cs (99%) rename {AgentInterface/Settings => RemoteTaskServer/Utilities}/Config.cs (99%) create mode 100644 RemoteTaskServer/Utilities/Extensions/ProcessExtensions.cs delete mode 100644 RemoteTaskServer/Utilities/Usage/HardwareSurvey.cs delete mode 100644 UlteriusAgent/FodyWeavers.xml delete mode 100644 UlteriusAgent/Networking/FrameAgent.cs delete mode 100644 UlteriusAgent/Networking/InputAgent.cs delete mode 100644 UlteriusAgent/Networking/Tools.cs create mode 100644 UlteriusAgent/UlteriusAgent.cs create mode 100644 UlteriusAgent/packages.config diff --git a/.gitignore b/.gitignore index dd701dc..a736671 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ TestResults [Dd]ebug/ [Rr]elease/ x64/ +.vs/ *_i.c *_p.c *.ilk diff --git a/AgentInterface/AgentInterface.csproj b/AgentInterface/AgentInterface.csproj deleted file mode 100644 index 3b092db..0000000 --- a/AgentInterface/AgentInterface.csproj +++ /dev/null @@ -1,163 +0,0 @@ - - - - - Debug - AnyCPU - {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466} - Library - Properties - AgentInterface - AgentInterface - v4.5.2 - 512 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - true - - - true - - - - ..\packages\Cassia.2.0.0.60\lib\2.0\Cassia.dll - True - - - ..\packages\Newtonsoft.Json.10.0.1-beta1\lib\net45\Newtonsoft.Json.dll - - - ..\packages\OpenHardwareMonitor.0.7.1\lib\net40\OpenHardwareMonitorLib.dll - True - - - - - ..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll - True - - - ..\packages\SharpDX.Direct3D11.3.1.1\lib\net45\SharpDX.Direct3D11.dll - True - - - ..\packages\SharpDX.DXGI.3.1.1\lib\net45\SharpDX.DXGI.dll - True - - - ..\packages\SharpDX.Mathematics.3.1.1\lib\net45\SharpDX.Mathematics.dll - True - - - - - - - - - ..\packages\TurboJpegWrapper.1.4.2.6\lib\net35\TurboJpegWrapper.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - - - - - - - - - - - - - - (); -var attribute = config.Attribute("ExcludeAssemblies"); -if (attribute != null) - foreach (var item in attribute.Value.Split('|').Select(x => x.Trim()).Where(x => x != string.Empty)) - excludedAssemblies.Add(item); -var element = config.Element("ExcludeAssemblies"); -if (element != null) - foreach (var item in element.Value.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Where(x => x != string.Empty)) - excludedAssemblies.Add(item); - -var filesToCleanup = Files.Select(f => f.ItemSpec).Where(f => !excludedAssemblies.Contains(Path.GetFileNameWithoutExtension(f), StringComparer.InvariantCultureIgnoreCase)); - -foreach (var item in filesToCleanup) - File.Delete(item); -]]> - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/AgentInterface/Api/Win32/Display.cs b/AgentInterface/Api/Win32/Display.cs deleted file mode 100644 index c58d49d..0000000 --- a/AgentInterface/Api/Win32/Display.cs +++ /dev/null @@ -1,910 +0,0 @@ -#region - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Globalization; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using System.Windows.Forms; -using AgentInterface.Api.Models; -using AgentInterface.Api.ScreenShare; - -#endregion - -namespace AgentInterface.Api.Win32 -{ - public class Display - { - - - [Flags] - public enum ChangeDisplaySettingsFlags : uint - { - CDS_NONE = 0, - CDS_UPDATEREGISTRY = 0x00000001, - CDS_TEST = 0x00000002, - CDS_FULLSCREEN = 0x00000004, - CDS_GLOBAL = 0x00000008, - CDS_SET_PRIMARY = 0x00000010, - CDS_VIDEOPARAMETERS = 0x00000020, - CDS_ENABLE_UNSAFE_MODES = 0x00000100, - CDS_DISABLE_UNSAFE_MODES = 0x00000200, - CDS_RESET = 0x40000000, - CDS_RESET_EX = 0x20000000, - CDS_NORESET = 0x10000000 - } - - [Flags] - public enum DisplayDeviceStateFlags - { - /// The device is part of the desktop. - AttachedToDesktop = 0x1, - MultiDriver = 0x2, - - /// The device is part of the desktop. - PrimaryDevice = 0x4, - - /// Represents a pseudo device used to mirror application drawing for remoting or other purposes. - MirroringDriver = 0x8, - - /// The device is VGA compatible. - VgaCompatible = 0x10, - - /// The device is removable; it cannot be the primary display. - Removable = 0x20, - - /// The device has more display modes than its output devices support. - ModesPruned = 0x8000000, - Remote = 0x4000000, - Disconnect = 0x2000000 - } - - public const int DMDO_DEFAULT = 0; - public const int DMDO_90 = 1; - public const int DMDO_180 = 2; - public const int DMDO_270 = 3; - - public const int ErrorSuccess = 0; - - public const int ENUM_CURRENT_SETTINGS = -1; - public const int CDS_UPDATEREGISTRY = 0x01; - public const int CDS_TEST = 0x02; - - - private const int ENUM_REGISTRY_SETTINGS = -2; - - public static int EnumCurrentSettings { get; } = -1; - - public static int EnumRegistrySettings { get; } = -2; - - private static string MonitorFriendlyName(Luid adapterId, uint targetId) - { - var deviceName = new DisplayconfigTargetDeviceName - { - header = - { - size = (uint) Marshal.SizeOf(typeof(DisplayconfigTargetDeviceName)), - adapterId = adapterId, - id = targetId, - type = DisplayconfigDeviceInfoType.DisplayconfigDeviceInfoGetTargetName - } - }; - var error = DisplayConfigGetDeviceInfo(ref deviceName); - if (error != ErrorSuccess) - throw new Win32Exception(error); - return deviceName.monitorFriendlyDeviceName; - } - - private static IEnumerable GetAllMonitorsFriendlyNames() - { - uint pathCount, modeCount; - var error = GetDisplayConfigBufferSizes(QueryDeviceConfigFlags.QdcOnlyActivePaths, out pathCount, - out modeCount); - if (error != ErrorSuccess) - throw new Win32Exception(error); - - var displayPaths = new DisplayconfigPathInfo[pathCount]; - var displayModes = new DisplayconfigModeInfo[modeCount]; - error = QueryDisplayConfig(QueryDeviceConfigFlags.QdcOnlyActivePaths, - ref pathCount, displayPaths, ref modeCount, displayModes, IntPtr.Zero); - if (error != ErrorSuccess) - throw new Win32Exception(error); - - for (var i = 0; i < modeCount; i++) - if (displayModes[i].infoType == DisplayconfigModeInfoType.DisplayconfigModeInfoTypeTarget) - yield return MonitorFriendlyName(displayModes[i].adapterId, displayModes[i].id); - } - - public static List DeviceFriendlyName() - { - return GetAllMonitorsFriendlyNames().ToList(); - } - - [DllImport("user32.dll")] - public static extern int ChangeDisplaySettings( - ref Devmode devMode, int flags); - - [DllImport("user32.dll")] - public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, ref Devmode lpDevMode, - IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam); - - [DllImport("user32.dll")] - public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, IntPtr lpDevMode, IntPtr hwnd, - ChangeDisplaySettingsFlags dwflags, IntPtr lParam); - - [DllImport("user32.dll")] - public static extern bool EnumDisplaySettings( - string deviceName, int modeNum, ref Devmode devMode); - - private static List UpdateDisplays() - { - var monitors = new List(); - var d = new DisplayDevice(); - d.cb = Marshal.SizeOf(d); - try - { - for (uint id = 0; EnumDisplayDevices(null, id, ref d, 0); id++) - { - if (d.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop)) - { - var device = d.DeviceName; - - var vDevMode = new Devmode(); - var i = 0; - var supportedResolutions = new Dictionary>(); - while (EnumDisplaySettings(device, i, ref vDevMode)) - { - var width = vDevMode.dmPelsWidth; - var height = vDevMode.dmPelsHeight; - var bpp = vDevMode.dmBitsPerPel; - var orientation = vDevMode.dmDisplayOrientation.ToString(); - var freq = vDevMode.dmDisplayFrequency; - var resolutionKey = $"{width}x{height}"; - var resolution = new ResolutionInformation - { - BitsPerPixel = bpp, - Frequency = freq, - Height = height, - Width = width, - Orientation = orientation - }; - if (supportedResolutions.ContainsKey(resolutionKey)) - { - supportedResolutions[resolutionKey].Add(resolution); - } - else - { - supportedResolutions.Add(resolutionKey, new List()); - supportedResolutions[resolutionKey].Add(resolution); - } - i++; - } - var cDevMode = new Devmode(); - EnumDisplaySettings(device, ENUM_CURRENT_SETTINGS, ref cDevMode); - - var currentResolution = new ResolutionInformation - { - BitsPerPixel = cDevMode.dmBitsPerPel, - Frequency = cDevMode.dmDisplayFrequency, - Height = cDevMode.dmPelsHeight, - Width = cDevMode.dmPelsWidth, - Orientation = cDevMode.dmDisplayOrientation.ToString(), - X = cDevMode.dmPositionX, - Y = cDevMode.dmPositionY - }; - var monitor = new DisplayInformation - { - Primary = d.StateFlags.HasFlag(DisplayDeviceStateFlags.PrimaryDevice), - Attached = d.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop), - Removable = d.StateFlags.HasFlag(DisplayDeviceStateFlags.Removable), - VgaCompatible = d.StateFlags.HasFlag(DisplayDeviceStateFlags.VgaCompatible), - MirroringDriver = d.StateFlags.HasFlag(DisplayDeviceStateFlags.MirroringDriver), - MultiDriver = d.StateFlags.HasFlag(DisplayDeviceStateFlags.MultiDriver), - ModesPruned = d.StateFlags.HasFlag(DisplayDeviceStateFlags.ModesPruned), - Remote = d.StateFlags.HasFlag(DisplayDeviceStateFlags.Remote), - Disconnect = d.StateFlags.HasFlag(DisplayDeviceStateFlags.Disconnect), - FriendlyName = $"{GetAllMonitorsFriendlyNames().ElementAt((int)id)} on {d.DeviceString}", - SupportedResolutions = supportedResolutions, - CurrentResolution = currentResolution, - DeviceName = device - }; - monitors.Add(monitor); - d.cb = Marshal.SizeOf(d); - EnumDisplayDevices(d.DeviceName, 0, ref d, 0); - } - d.cb = Marshal.SizeOf(d); - } - return monitors; - } - catch - { - - } - string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; - Console.WriteLine(errorMessage); - return monitors; - } - - public static List DisplayInformation() - { - return UpdateDisplays(); - } - - public static string SetPrimary(string deviceName) - { - var id = int.Parse(Regex.Match(deviceName, @"\d+").Value) - 1; - var originalMode = new Devmode(); - originalMode.dmSize = (short) Marshal.SizeOf(originalMode); - EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, ref originalMode); - var offsetx = originalMode.dmPositionX; - var offsety = originalMode.dmPositionY; - originalMode.dmPositionX = 0; - originalMode.dmPositionY = 0; - - ChangeDisplaySettingsEx(deviceName, ref originalMode, (IntPtr) null, - ChangeDisplaySettingsFlags.CDS_SET_PRIMARY | ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | - ChangeDisplaySettingsFlags.CDS_NORESET, IntPtr.Zero); - var device = new DisplayDevice(); - device.cb = Marshal.SizeOf(device); - - // Update remaining devices - for (uint otherid = 0; EnumDisplayDevices(null, otherid, ref device, 0); otherid++) - { - if (device.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop) && otherid != id) - { - device.cb = Marshal.SizeOf(device); - var otherDeviceMode = new Devmode(); - - EnumDisplaySettings(device.DeviceName, -1, ref otherDeviceMode); - - otherDeviceMode.dmPositionX -= offsetx; - otherDeviceMode.dmPositionY -= offsety; - - ChangeDisplaySettingsEx( - device.DeviceName, - ref otherDeviceMode, - (IntPtr) null, - ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET, - IntPtr.Zero); - } - - device.cb = Marshal.SizeOf(device); - } - - // Apply settings - return - GetMessageForCode(ChangeDisplaySettingsEx(null, IntPtr.Zero, (IntPtr) null, - ChangeDisplaySettingsFlags.CDS_NONE, (IntPtr) null)); - } - [DllImport("kernel32.dll")] - public static extern uint GetLastError(); - - public static string Rotate(int angle, int width, int height, string deviceName) - { - var originalMode = new Devmode(); - originalMode.dmSize = (short) Marshal.SizeOf(originalMode); - EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, ref originalMode); - - // swap height and width - var temp = originalMode.dmPelsHeight; - originalMode.dmPelsHeight = originalMode.dmPelsWidth; - originalMode.dmPelsWidth = temp; - - originalMode.dmPelsWidth = width; - originalMode.dmPelsHeight = height; - switch (angle) - { - case 0: - originalMode.dmDisplayOrientation = ScreenOrientation.Angle0; - break; - case 90: - originalMode.dmDisplayOrientation = ScreenOrientation.Angle90; - break; - case 180: - originalMode.dmDisplayOrientation = ScreenOrientation.Angle180; - break; - case 270: - originalMode.dmDisplayOrientation = ScreenOrientation.Angle270; - break; - } - return GetMessageForCode(ChangeDisplaySettingsEx(deviceName, ref originalMode, IntPtr.Zero, - ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY, IntPtr.Zero)); - } - - public static string ChangeResolution(string deviceName, int width, int height, int bbp, int freq) - { - var originalMode = new Devmode(); - originalMode.dmSize = (short) Marshal.SizeOf(originalMode); - EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, ref originalMode); - var newMode = originalMode; - newMode.dmDeviceName = deviceName; - newMode.dmPelsWidth = width; - newMode.dmPelsHeight = height; - newMode.dmBitsPerPel = bbp; - newMode.dmDisplayFrequency = freq; - return GetMessageForCode(ChangeDisplaySettingsEx(deviceName, ref newMode, IntPtr.Zero, - ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY, IntPtr.Zero)); - } - - private static string GetMessageForCode(DISP_CHANGE code) - { - string message; - switch (code) - { - case DISP_CHANGE.Successful: - message = "Resolution updated."; - break; - case DISP_CHANGE.Restart: - message = "A restart is required for this resolution to take effect."; - break; - case DISP_CHANGE.BadMode: - message = $"resolution is not valid."; - break; - case DISP_CHANGE.BadDualView: - message = "The settings change was unsuccessful because system is DualView capable."; - break; - case DISP_CHANGE.BadFlags: - message = "An invalid set of flags was passed in."; - break; - case DISP_CHANGE.BadParam: - message = - "An invalid parameter was passed in. This can include an invalid flag or combination of flags."; - break; - case DISP_CHANGE.Failed: - message = "Resolution failed to update."; - break; - case DISP_CHANGE.NotUpdated: - message = "Unable to write settings to the registry."; - break; - default: - message = "Unknown return value from ChangeDisplaySettings API."; - break; - } - return message; - } - - [DllImport("user32.dll")] - private static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DisplayDevice lpDisplayDevice, - uint dwFlags); - - public enum DISP_CHANGE - { - Successful = 0, - Restart = 1, - Failed = -1, - BadMode = -2, - NotUpdated = -3, - BadFlags = -4, - BadParam = -5, - BadDualView = -6 - } - - [StructLayout(LayoutKind.Sequential)] - public struct Devmode - { - private const int Cchdevicename = 0x20; - private const int Cchformname = 0x20; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] public string dmDeviceName; - public short dmSpecVersion; - public short dmDriverVersion; - public short dmSize; - public short dmDriverExtra; - public int dmFields; - public int dmPositionX; - public int dmPositionY; - public ScreenOrientation dmDisplayOrientation; - public int dmDisplayFixedOutput; - public short dmColor; - public short dmDuplex; - public short dmYResolution; - public short dmTTOption; - public short dmCollate; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] public string dmFormName; - public short dmLogPixels; - public int dmBitsPerPel; - public int dmPelsWidth; - public int dmPelsHeight; - public int dmDisplayFlags; - public int dmDisplayFrequency; - public int dmICMMethod; - public int dmICMIntent; - public int dmMediaType; - public int dmDitherType; - public int dmReserved1; - public int dmReserved2; - public int dmPanningWidth; - public int dmPanningHeight; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - public struct DisplayDevice - { - [MarshalAs(UnmanagedType.U4)] public int cb; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string DeviceName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceString; - [MarshalAs(UnmanagedType.U4)] public DisplayDeviceStateFlags StateFlags; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceID; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceKey; - } - - #region enums - - public enum QueryDeviceConfigFlags : uint - { - QdcAllPaths = 0x00000001, - QdcOnlyActivePaths = 0x00000002, - QdcDatabaseCurrent = 0x00000004 - } - - public enum DisplayconfigVideoOutputTechnology : uint - { - DisplayconfigOutputTechnologyOther = 0xFFFFFFFF, - DisplayconfigOutputTechnologyHd15 = 0, - DisplayconfigOutputTechnologySvideo = 1, - DisplayconfigOutputTechnologyCompositeVideo = 2, - DisplayconfigOutputTechnologyComponentVideo = 3, - DisplayconfigOutputTechnologyDvi = 4, - DisplayconfigOutputTechnologyHdmi = 5, - DisplayconfigOutputTechnologyLvds = 6, - DisplayconfigOutputTechnologyDJpn = 8, - DisplayconfigOutputTechnologySdi = 9, - DisplayconfigOutputTechnologyDisplayportExternal = 10, - DisplayconfigOutputTechnologyDisplayportEmbedded = 11, - DisplayconfigOutputTechnologyUdiExternal = 12, - DisplayconfigOutputTechnologyUdiEmbedded = 13, - DisplayconfigOutputTechnologySdtvdongle = 14, - DisplayconfigOutputTechnologyMiracast = 15, - DisplayconfigOutputTechnologyInternal = 0x80000000, - DisplayconfigOutputTechnologyForceUint32 = 0xFFFFFFFF - } - - public enum DisplayconfigScanlineOrdering : uint - { - DisplayconfigScanlineOrderingUnspecified = 0, - DisplayconfigScanlineOrderingProgressive = 1, - DisplayconfigScanlineOrderingInterlaced = 2, - DisplayconfigScanlineOrderingInterlacedUpperfieldfirst = DisplayconfigScanlineOrderingInterlaced, - DisplayconfigScanlineOrderingInterlacedLowerfieldfirst = 3, - DisplayconfigScanlineOrderingForceUint32 = 0xFFFFFFFF - } - - public enum DisplayconfigRotation : uint - { - DisplayconfigRotationIdentity = 1, - DisplayconfigRotationRotate90 = 2, - DisplayconfigRotationRotate180 = 3, - DisplayconfigRotationRotate270 = 4, - DisplayconfigRotationForceUint32 = 0xFFFFFFFF - } - - public enum DisplayconfigScaling : uint - { - DisplayconfigScalingIdentity = 1, - DisplayconfigScalingCentered = 2, - DisplayconfigScalingStretched = 3, - DisplayconfigScalingAspectratiocenteredmax = 4, - DisplayconfigScalingCustom = 5, - DisplayconfigScalingPreferred = 128, - DisplayconfigScalingForceUint32 = 0xFFFFFFFF - } - [DllImport("user32")] - private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData); - - private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData); - - [StructLayout(LayoutKind.Sequential)] - private struct Rect - { - public int left; - public int top; - public int right; - public int bottom; - } - - public enum DisplayconfigPixelformat : uint - { - DisplayconfigPixelformat8Bpp = 1, - DisplayconfigPixelformat16Bpp = 2, - DisplayconfigPixelformat24Bpp = 3, - DisplayconfigPixelformat32Bpp = 4, - DisplayconfigPixelformatNongdi = 5, - DisplayconfigPixelformatForceUint32 = 0xffffffff - } - - public enum DisplayconfigModeInfoType : uint - { - DisplayconfigModeInfoTypeSource = 1, - DisplayconfigModeInfoTypeTarget = 2, - DisplayconfigModeInfoTypeForceUint32 = 0xFFFFFFFF - } - - public enum DisplayconfigDeviceInfoType : uint - { - DisplayconfigDeviceInfoGetSourceName = 1, - DisplayconfigDeviceInfoGetTargetName = 2, - DisplayconfigDeviceInfoGetTargetPreferredMode = 3, - DisplayconfigDeviceInfoGetAdapterName = 4, - DisplayconfigDeviceInfoSetTargetPersistence = 5, - DisplayconfigDeviceInfoGetTargetBaseType = 6, - DisplayconfigDeviceInfoForceUint32 = 0xFFFFFFFF - } - - #endregion - - #region structs - - [StructLayout(LayoutKind.Sequential)] - public struct Luid - { - public uint LowPart; - public int HighPart; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigPathSourceInfo - { - public Luid adapterId; - public uint id; - public uint modeInfoIdx; - public uint statusFlags; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigPathTargetInfo - { - public Luid adapterId; - public uint id; - public uint modeInfoIdx; - private readonly DisplayconfigVideoOutputTechnology outputTechnology; - private readonly DisplayconfigRotation rotation; - private readonly DisplayconfigScaling scaling; - private readonly DisplayconfigRational refreshRate; - private readonly DisplayconfigScanlineOrdering scanLineOrdering; - public bool targetAvailable; - public uint statusFlags; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigRational - { - public uint Numerator; - public uint Denominator; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigPathInfo - { - public DisplayconfigPathSourceInfo sourceInfo; - public DisplayconfigPathTargetInfo targetInfo; - public uint flags; - } - - [StructLayout(LayoutKind.Sequential)] - public struct Displayconfig2Dregion - { - public uint cx; - public uint cy; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigVideoSignalInfo - { - public ulong pixelRate; - public DisplayconfigRational hSyncFreq; - public DisplayconfigRational vSyncFreq; - public Displayconfig2Dregion activeSize; - public Displayconfig2Dregion totalSize; - public uint videoStandard; - public DisplayconfigScanlineOrdering scanLineOrdering; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigTargetMode - { - public DisplayconfigVideoSignalInfo targetVideoSignalInfo; - } - - [StructLayout(LayoutKind.Sequential)] - public struct Pointl - { - private readonly int x; - private readonly int y; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigSourceMode - { - public uint width; - public uint height; - public DisplayconfigPixelformat pixelFormat; - public Pointl position; - } - - [StructLayout(LayoutKind.Explicit)] - public struct DisplayconfigModeInfoUnion - { - [FieldOffset(0)] public DisplayconfigTargetMode targetMode; - - [FieldOffset(0)] public DisplayconfigSourceMode sourceMode; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigModeInfo - { - public DisplayconfigModeInfoType infoType; - public uint id; - public Luid adapterId; - public DisplayconfigModeInfoUnion modeInfo; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigTargetDeviceNameFlags - { - public uint value; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DisplayconfigDeviceInfoHeader - { - public DisplayconfigDeviceInfoType type; - public uint size; - public Luid adapterId; - public uint id; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct DisplayconfigTargetDeviceName - { - public DisplayconfigDeviceInfoHeader header; - public DisplayconfigTargetDeviceNameFlags flags; - public DisplayconfigVideoOutputTechnology outputTechnology; - public ushort edidManufactureId; - public ushort edidProductCodeId; - public uint connectorInstance; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string monitorFriendlyDeviceName; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string monitorDevicePath; - } - [StructLayout(LayoutKind.Sequential)] - public struct RECT - { - public int Left, Top, Right, Bottom; - - public RECT(int left, int top, int right, int bottom) - { - Left = left; - Top = top; - Right = right; - Bottom = bottom; - } - - public RECT(Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { } - - public int X - { - get { return Left; } - set { Right -= (Left - value); Left = value; } - } - - public int Y - { - get { return Top; } - set { Bottom -= (Top - value); Top = value; } - } - - public int Height - { - get { return Bottom - Top; } - set { Bottom = value + Top; } - } - - public int Width - { - get { return Right - Left; } - set { Right = value + Left; } - } - - - - public Size Size - { - get { return new Size(Width, Height); } - set { Width = value.Width; Height = value.Height; } - } - - public static implicit operator Rectangle(RECT r) - { - return new Rectangle(r.Left, r.Top, r.Width, r.Height); - } - - public static implicit operator RECT(Rectangle r) - { - return new RECT(r); - } - - public static bool operator ==(RECT r1, RECT r2) - { - return r1.Equals(r2); - } - - public static bool operator !=(RECT r1, RECT r2) - { - return !r1.Equals(r2); - } - - public bool Equals(RECT r) - { - return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; - } - - public override bool Equals(object obj) - { - if (obj is RECT) - return Equals((RECT)obj); - else if (obj is Rectangle) - return Equals(new RECT((Rectangle)obj)); - return false; - } - - public override int GetHashCode() - { - return ((Rectangle)this).GetHashCode(); - } - - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom); - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct CURSORINFO - { - public Int32 cbSize; // Specifies the size, in bytes, of the structure. - public Int32 flags; // Specifies the cursor state. This parameter can be one of the following values: - public IntPtr hCursor; // Handle to the cursor. - public POINT ptScreenPos; // A POINT structure that receives the screen coordinates of the cursor. - } - - [StructLayout(LayoutKind.Sequential)] - public struct ICONINFO - { - public bool fIcon; // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies - public Int32 xHotspot; // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot - public Int32 yHotspot; // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot - public IntPtr hbmMask; // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, - public IntPtr hbmColor; // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this - } - - [StructLayout(LayoutKind.Sequential)] - public struct POINT - { - public Int32 x; - public Int32 y; - } - public const int Width = 0; - public const int Height = 1; - public struct ScreenSize - { - public int Width; - public int Height; - } - public const Int32 CURSOR_SHOWING = 0x00000001; - - [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")] - public static extern IntPtr GetDesktopWindow(); - - [DllImport("user32.dll", EntryPoint = "GetDC")] - public static extern IntPtr GetDesktopContext(IntPtr ptr); - - [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")] - public static extern int GetSystemMetrics(int abc); - - [DllImport("user32.dll", EntryPoint = "GetWindowDC")] - public static extern IntPtr GetWindowDesktopContext(Int32 ptr); - - [DllImport("user32.dll", EntryPoint = "ReleaseDC")] - public static extern IntPtr ReleaseDesktopContext(IntPtr hWnd, IntPtr hDc); - - [DllImport("user32.dll", EntryPoint = "GetCursorInfo")] - public static extern bool GetCursorInfo(out CursorInfo pci); - - [DllImport("user32.dll", EntryPoint = "CopyIcon")] - public static extern IntPtr CopyIcon(IntPtr hIcon); - - [DllImport("user32.dll", EntryPoint = "GetIconInfo")] - public static extern bool GetIconInfo(IntPtr hIcon, out IconInfo piconinfo); - - [DllImport("user32.dll", EntryPoint = "DestroyIcon")] - public static extern bool DestroyIcon(IntPtr hIcon); - #endregion - [StructLayout(LayoutKind.Sequential)] - public struct IconInfo - { - public bool IsIcon; // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies - public Int32 Xcoord; // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot - public Int32 Ycoord; // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot - public IntPtr Bitmask; // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, - public IntPtr Color; // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this - } - - [StructLayout(LayoutKind.Sequential)] - public struct Point - { - public Int32 X; - public Int32 Y; - } - - [StructLayout(LayoutKind.Sequential)] - public struct CursorInfo - { - public Int32 Size; // Specifies the size, in bytes, of the structure. - public Int32 State; // Specifies the cursor state. This parameter can be one of the following values: - public IntPtr Handle; // Handle to the cursor. - public Point Coordinates; // A POINT structure that receives the screen coordinates of the cursor. - } - - #region DLL-Imports - - [DllImport("user32.dll")] - public static extern int GetDisplayConfigBufferSizes( - QueryDeviceConfigFlags flags, out uint numPathArrayElements, out uint numModeInfoArrayElements); - - [DllImport("user32.dll")] - public static extern int QueryDisplayConfig( - QueryDeviceConfigFlags flags, - ref uint numPathArrayElements, [Out] DisplayconfigPathInfo[] pathInfoArray, - ref uint numModeInfoArrayElements, [Out] DisplayconfigModeInfo[] modeInfoArray, - IntPtr currentTopologyId - ); - - [DllImport("user32.dll")] - public static extern int DisplayConfigGetDeviceInfo(ref DisplayconfigTargetDeviceName deviceName); - - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); - - public static Rectangle GetWindowRectangle() - { - RECT scBounds = new RECT(); - GetWindowRect(GetDesktopWindow(), ref scBounds); - return scBounds; - } - - public const int SRCCOPY = 13369376; - - [DllImport("gdi32.dll", EntryPoint = "CreateDC")] - public static extern IntPtr CreateDesktopContext(IntPtr lpszDriver, string lpszDevice, IntPtr lpszOutput, IntPtr lpInitData); - - [DllImport("gdi32.dll", EntryPoint = "DeleteDC")] - public static extern IntPtr DeleteDesktopContext(IntPtr hDc); - - [DllImport("gdi32.dll", EntryPoint = "DeleteObject")] - public static extern IntPtr DeleteObject(IntPtr hDc); - - [DllImport("gdi32.dll", EntryPoint = "BitBlt")] - public static extern bool BitBlt(IntPtr hdcDest, int xDest, - int yDest, int wDest, - int hDest, IntPtr hdcSource, - int xSrc, int ySrc, int rasterOp); - - [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] - public static extern IntPtr CreateCompatibleBitmap - (IntPtr hdc, int nWidth, int nHeight); - - [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")] - public static extern IntPtr CreateCompatibleDesktopContext(IntPtr hdc); - - [DllImport("gdi32.dll", EntryPoint = "SelectObject")] - public static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp); - - #endregion - } - - -} \ No newline at end of file diff --git a/AgentInterface/Api/Win32/ProcessStarter.cs b/AgentInterface/Api/Win32/ProcessStarter.cs deleted file mode 100644 index f264db5..0000000 --- a/AgentInterface/Api/Win32/ProcessStarter.cs +++ /dev/null @@ -1,247 +0,0 @@ -#region - -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Security; -using System.Security.Principal; -using Cassia; - -#endregion - -namespace AgentInterface.Api.Win32 -{ - /// - /// Class that allows running applications with full admin rights. In - /// addition the application launched will bypass the Vista UAC prompt. - /// - public class ProcessStarter - { - - - - private static int GetUserSessionId(string username) - { - try - { - ITerminalServicesManager manager = new TerminalServicesManager(); - using (ITerminalServer server = manager.GetLocalServer()) - { - server.Open(); - foreach (ITerminalServicesSession session in server.GetSessions()) - { - - NTAccount account = session.UserAccount; - if (account == null) continue; - var userName = account.Value.Split('\\')[1]; - if (userName.ToLower().Equals(username.ToLower())) return session.SessionId; - } - } - return -1; - } - catch (Exception) - { - - return -1; - } - } - - - /// - /// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt - /// - /// The name of the application to launch - /// Process information regarding the launched application that gets returned to the caller - /// - public static bool StartProcessAndBypassUAC(string applicationName, out PROCESS_INFORMATION procInfo) - { - uint winlogonPid = 0; - IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero; - procInfo = new PROCESS_INFORMATION(); - - - // obtain the currently active session id; every logged on user in the system has a unique session id - var dwSessionId = -1; - - dwSessionId = (int) WTSGetActiveConsoleSessionId(); - - // obtain the process id of the winlogon process that is running within the currently active session - var processes = Process.GetProcessesByName("winlogon"); - foreach (var p in processes) - { - if ((uint) p.SessionId == dwSessionId) - { - winlogonPid = (uint) p.Id; - } - } - - // obtain a handle to the winlogon process - hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid); - - // obtain a handle to the access token of the winlogon process - if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken)) - { - CloseHandle(hProcess); - return false; - } - - // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser - // I would prefer to not have to use a security attribute variable and to just - // simply pass null and inherit (by default) the security attributes - // of the existing token. However, in C# structures are value types and therefore - // cannot be assigned the null value. - var sa = new SECURITY_ATTRIBUTES(); - sa.Length = Marshal.SizeOf(sa); - - // copy the access token of the winlogon process; the newly created token will be a primary token - if ( - !DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, - (int) SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int) TOKEN_TYPE.TokenPrimary, - ref hUserTokenDup)) - { - CloseHandle(hProcess); - CloseHandle(hPToken); - return false; - } - - // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning - // the window station has a desktop that is invisible and the process is incapable of receiving - // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user - // interaction with the new process. - var si = new STARTUPINFO(); - si.cb = Marshal.SizeOf(si); - si.lpDesktop = @"winsta0\default"; - // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop - - // flags that specify the priority and creation method of the process - var dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; - - // create a new process in the current user's logon session - var result = CreateProcessAsUser(hUserTokenDup, // client's access token - null, // file to execute - applicationName, // command line - ref sa, // pointer to process SECURITY_ATTRIBUTES - ref sa, // pointer to thread SECURITY_ATTRIBUTES - false, // handles are not inheritable - dwCreationFlags, // creation flags - IntPtr.Zero, // pointer to new environment block - null, // name of current directory - ref si, // pointer to STARTUPINFO structure - out procInfo // receives information about new process - ); - - // invalidate the handles - CloseHandle(hProcess); - CloseHandle(hPToken); - CloseHandle(hUserTokenDup); - - return result; // return the result - } - - #region Structures - - [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_ATTRIBUTES - { - public int Length; - public IntPtr lpSecurityDescriptor; - public bool bInheritHandle; - } - - [StructLayout(LayoutKind.Sequential)] - public struct STARTUPINFO - { - public int cb; - public string lpReserved; - public string lpDesktop; - public string lpTitle; - public uint dwX; - public uint dwY; - public uint dwXSize; - public uint dwYSize; - public uint dwXCountChars; - public uint dwYCountChars; - public uint dwFillAttribute; - public uint dwFlags; - public short wShowWindow; - public short cbReserved2; - public IntPtr lpReserved2; - public IntPtr hStdInput; - public IntPtr hStdOutput; - public IntPtr hStdError; - } - - [StructLayout(LayoutKind.Sequential)] - public struct PROCESS_INFORMATION - { - public IntPtr hProcess; - public IntPtr hThread; - public uint dwProcessId; - public uint dwThreadId; - } - - #endregion - - #region Enumerations - - private enum TOKEN_TYPE - { - TokenPrimary = 1, - TokenImpersonation = 2 - } - - private enum SECURITY_IMPERSONATION_LEVEL - { - SecurityAnonymous = 0, - SecurityIdentification = 1, - SecurityImpersonation = 2, - SecurityDelegation = 3 - } - - #endregion - - #region Constants - - public const int TOKEN_DUPLICATE = 0x0002; - public const uint MAXIMUM_ALLOWED = 0x2000000; - public const int CREATE_NEW_CONSOLE = 0x00000010; - - public const int IDLE_PRIORITY_CLASS = 0x40; - public const int NORMAL_PRIORITY_CLASS = 0x20; - public const int HIGH_PRIORITY_CLASS = 0x80; - public const int REALTIME_PRIORITY_CLASS = 0x100; - - #endregion - - #region Win32 API Imports - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool CloseHandle(IntPtr hSnapshot); - - [DllImport("kernel32.dll")] - private static extern uint WTSGetActiveConsoleSessionId(); - - [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, - CallingConvention = CallingConvention.StdCall)] - public static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, - ref SECURITY_ATTRIBUTES lpProcessAttributes, - ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment, - string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); - - [DllImport("kernel32.dll")] - private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId); - - [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] - public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, - ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, - int ImpersonationLevel, ref IntPtr DuplicateTokenHandle); - - [DllImport("kernel32.dll")] - private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); - - [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurity] - private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle); - - #endregion - } -} \ No newline at end of file diff --git a/AgentInterface/FodyWeavers.xml b/AgentInterface/FodyWeavers.xml deleted file mode 100644 index 52e39c5..0000000 --- a/AgentInterface/FodyWeavers.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/AgentInterface/FrameContract.cs b/AgentInterface/FrameContract.cs deleted file mode 100644 index ac79e4d..0000000 --- a/AgentInterface/FrameContract.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ServiceModel; -using AgentInterface.Api.Models; - -namespace AgentInterface -{ - [ServiceContract(Namespace = "https://ulterius.io/")] - public interface IFrameContract - { - - [OperationContract] - FrameInformation GetCleanFrame(); - - [OperationContract] - FrameInformation GetFullFrame(); - - [OperationContract] - bool KeepAlive(); - - } -} \ No newline at end of file diff --git a/AgentInterface/InputContract.cs b/AgentInterface/InputContract.cs deleted file mode 100644 index 69e0401..0000000 --- a/AgentInterface/InputContract.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel; -using System.Text; -using System.Threading.Tasks; -using AgentInterface.Api.Models; - -namespace AgentInterface -{ - [ServiceContract(Namespace = "https://ulterius.io/")] - public interface IInputContract - { - - [OperationContract(IsOneWay = true)] - void HandleRightMouseDown(); - - [OperationContract(IsOneWay = true)] - void HandleRightMouseUp(); - - [OperationContract(IsOneWay = true)] - void MoveMouse(int x, int y); - - [OperationContract(IsOneWay = true)] - void MouseScroll(bool positive); - - [OperationContract(IsOneWay = true)] - void HandleLeftMouseDown(); - - [OperationContract(IsOneWay = true)] - void HandleLeftMouseUp(); - - [OperationContract(IsOneWay = true)] - void HandleKeyDown(List keyCodes); - - [OperationContract(IsOneWay = true)] - void HandleKeyUp(List keyCodes); - - [OperationContract(IsOneWay = true)] - void HandleRightClick(); - - [OperationContract] - float GetGpuTemp(string gpuName); - - [OperationContract] - List GetDisplayInformation(); - - [OperationContract] - List GetCpuTemps(); - - } -} diff --git a/AgentInterface/Properties/AssemblyInfo.cs b/AgentInterface/Properties/AssemblyInfo.cs deleted file mode 100644 index 327ea23..0000000 --- a/AgentInterface/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -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("AgentInterface")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Octopodal Solutions")] -[assembly: AssemblyProduct("Ulterius")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[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("5c3b0b17-cbb7-4b4b-b527-1fab2bb96466")] - -// 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")] diff --git a/AgentInterface/packages.config b/AgentInterface/packages.config deleted file mode 100644 index 4fbf902..0000000 --- a/AgentInterface/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Deps/OpenHardwareMonitorLib.dll b/Deps/OpenHardwareMonitorLib.dll new file mode 100644 index 0000000000000000000000000000000000000000..e059e78d5829e092528025da010b702f92455ffa GIT binary patch literal 266752 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%0Af?&$cZ7nmR*TcZJn8 z=q3s;8lC@g1;11J3Dnp5)YW2cWb`FU51>TjXF)#K<4%;XL3D=r5_IVamh``YcU%WL z9Ix4~g2uzhJZc#x4mX$^$1zdDjf>-0J>w^1HH^%?F)swCsSfJZmRR4kOl*yWh4KhP>>H@^83{>ouZ-9ayA`Qd>UZ53c|2h;&1MOv@TKJBG zQxnFNHf6<%xI}LqMEJugCtAe$bm0$DVVKocqlWm0hJbHgY1-@X;Ea@p<`B{qemQVA z;}MG%z!85Yp0j{^X8_Ol<4yw}gOjZn6GkzH>eLTxUyOI&!&c$g4Db3(>1J&7HY0Xh zKaQCfEOC$&WP`tq7Ihe0Vln!_c3?IReyg2#%!Rf4!E-%zK@`=1dsxO}aQQAdTNT#( z=p5MY;ouQImR>7%)U$x$2mCDHYa$RTMO-s;Ry@$BxLN1g;U2s>H^xR{dP&4w)DnkZ ze5_r*X5{az;kf2gaoOkn8o5$NkvA8>bQ#$!u1^_!Ph9R2_-FJp3#BO0`n6>zNZ6A9P~IDjf z;0vZgz?aOM3HYLU2LWF;A1B}o=bs4p(s_tLR^;GFdXhDzD$K;&ldY(r z%WjY9DH<(~AFGEd0k*Mtn&GzMN#SY3lTi;B?6^aj!?U2_=>^QEaTlcs+#)=Kcz7&e z7*Aojlh5Qk@}2pve0RPlKPNvopN;3URz91^XOsDCQ$E|A&$i^Vt@*5-&!+O(bUxdb z&$jbF7_falixT+l^J(}|+$C(EHs%`H6IA>BJ*48-M7*!oK8N7<>){Oe{c5-te!mvp z4!>UspMc-5gGb@_tKdoa{Tlcw{MGjPI|6?DH2MMf?K4BbZ=Z_@`0aBQ0l$5|f`H#X zcM|a1=UxJS`}{lszkPn2fZslUMZj;LEsFs7?K4Zjm&y@m0@e~3R=_I>ELXtg1Xd_O z=z^63fG%LKP@SWy3uu%_*Pei`#GMH)+d*6gpjjba@rP-mVbw<4f@dq9OYvNRXD6Orc&^5C4W8HFxgJji z&m^8V;<*XW9z6T-+=AzAc;12M-FW^1&-?Iv0MCc;d<4(k>KXnhJpX(3 z?aXJp^4acuwkMyRlg~ndW%JowK07a;ouAJx$Y+nqXBXzPz4`31`K+7I9+%JN^Vvc^ zTg+$s^4b1;c2Pb%kk2m8X9x4yp?r2pK6`vVyELC&md_67v&-|@75wjCln9*{!3&?7 zwgGihyYInrzX%hfYp8kRY1jmb3A}U_P8M<#-zH>s;fA5yBnmtfn|MZqm=oWlixa#s z6h?PUJ_QJ52}w3GAMK*_F0>_nyBK$;mX-{`YDmG)6@t=bgwli}@zU;U5bP@3EPa@8 z8v@(BcKqIT4r~>m@0$TV2K?cf@chy&UJH$vepY3UwF)~{>RK`b^H^!=4Cpb*ByLCA z)J4nu*!=_=qs5ouLih}QW`gD3_a+H-*iqm*?KpnB+QkcE@O3-B&%=QO>n=u}d=k+h zUGfqv_=RSRGj%OH;<~V=1Vz-DAR&azPfA+?IE=AM9YyZ~?plJA{|YJ*>MjLG(C-*^ zm%*2|VvcR0hD3bZI*dvWMkYA<9Yn@p$ab+8RXmDz(Iz;yiJ(9m3ABUrYqgt*)@nBy zE1g48vTmE=ZR+A=RJkmPl3Tb=Zebyp-n=NlM7IzG+M0VD#L&xi*eKsq<^03Ob+o)> z3^T+XLk_=h(j0LXCe}w6CfCOnHm#4Zck&Y~4i5w~&|63z>SnS`UTazW5I8|GEk6B$ zpX5$YJSai!!o9zF<8NB`u8r+IJ9$CNp^Nrk`@$Y`@7mTquL|!uC${IJR=fyq0C#Al6PxtCQvDJI*%@1i@#~l~ku%E29LXL;( z6<29YvUlQ;6KXxQ@bJTe%dmSD!u8TNpm4irc>J3<4O007vVxT`I_lJ4iQ!qxC-@IZ zKt~bW41!FJ0ISC2Kt~Ydtxoyg?)#CS&Bzbe^f6%QDeW+w+S?uXR{n%YDCQy*6YJwL z8}oTuS^T85@{1tAF%tOxEPR^-$ExsJy@)P?6fMr2AnN2fb*E04wJNX1;Cg+{fe!b(1-;IJ)j({&-QxpKU4NLbxFyZkc3Ueqb194v^R^$a8`@OE^!2gxxFOZy+ zH}eA=ruTZ(jhK)_rwslkzvl3(eNl-kziC6Wy_^O2Rfz8%k5s%IT8V!L`7vhcF{ICr zQe&d-VqC!e?M+j;+N8X9N_@ZEYMM&)PT>_51BkYKJRcfCQkV*$Nm^i!zqB?FcXeRU z5Ajx?*ecE7nuPGL(G0X#>0hfE7|d~>2lo7dLFL+|roZzvJziW&Y#ys;|H@BXZyW{K$-GtqG3UV0IwHF4r1Q1@H{O#QPK{_9B5e&>m^n%-tEWx zG$0VmD`GND>D?Xc;~PR9Xg-sse=Mrpn86ZMD2_hVBuzRQ4j7eQ1qb28dnfs6&#u_g8%VB-O7 zp;h9a0$#BxlPE`?@=<>6nvlACTgYBwoKcNH>~XN_+pSx8elTjiMu*- zoV4>xw+hD&Ii=;7s8*Tx31+s~DjaV}6qXoK z_~J2A2BO1s%q9=?haQ&aGA;F5Uys0FmU@IbaScLW&3v+5drOdrvv|ngd{__JN#Uy{ zGEErlO)ZA=ZeZRcat!!wB|ql04@0W(=#IA(aoBeqi$ke3h22h>x472Qp+#;L zGKGJR7Qv!N=p4xa9gd8=Wr&S4&bZGN{4c4>HD);L!w&2rXNQt6PV0nqae)AYjItLJ zxPU0Pt$o*)tJWK)aG)hP9vC=NC3!`pxdn4d9L>ac9mE~4RXR@2wpG)FdgGvqLSj(W zE6qqSrHNa}j9p_DTs|W;!9)x`b05jPvrxlGl=sWZsmN6-ZF!O^J zqIef0M(37dL`ap1px`1A>^q~JMkNgwfwDDYZyf26BrIAqZ4X0CVlS`fr-X(Q2xe`H zS-Wt<6>Ny8oa5asa3r0S8fA7a@`s78D!XEZ3=39RltsQWl@8$=SI3C~eS3z?*-f$xpHqV^ikw$N zvhhjE`86clfkX}gnUOqM+Y-4H$PS5Qtx4p0HA;$DBG0c;Qc4oJx<<(Y@{{Li_4wp@ z1>eRJc5ah~8I-mi@@s3$t-@-ByAI9KGR3rv-_^E?@o;GjUHv?KZ$K|Ip8UwH2ZnzSm4p?NvhR`L+`mGBwia5082p@9*mC~Zzk z>E#u;q3 zL$Nyfh@J}F@GmGq`5*D)Zbdjks>sh)k(vB_!@UB*SPSEP>~}~Wi`n)#Hrp+$^fh#X z(huu<>O$kVZerIX+d_xD5k-bG0EsbmbNUz+;U@!Z{@8%o7^ zG|1h+9LZ0b<)blq3_!&iMUXS_&pfsu-z_tZ#XlS02rPCU*j66{pX-pGr0?3Vi`sBX z*Fe^X;u)#X4#hi4HwZ*$cptP#=uq6<1Rfp^o>_R0a1u=P+#~pPuR_yEM7!m!30!F{ zy@3&N1flLzGj_P6bU+4l;w0yJFooQU!OX>3RNR>u?kc@ghPtmJb_K%GbXLMCrefX+ za5Eknf#t2jk35ypz3O$SO8M=RFz;mL1`EGk;O;`6@M*6;yyOQH*vifyny*ez;JaE1 z(`shq>4=Vv9!|+xc`)J_l>r|tZwB~atQZ49QIF;w(nPecf~F=zxjNqzpFxI)>Sfqf z`lvv3N9yHSxVBE1J%5WZ?>VY4MLKpE3E{G)X%fZAEzY%c z>EYYEMmRfP)O|#`E>5eeP?WzLx@w)F;cXwtN;KWdV`#Q|>2t`K{NxbdtanO}iHzDA ztiQ4)iFeGii|)XScU!^Fy97UnY&*CZYgE2MPDW09C(Qb}nelC)sjyS1Mt&(^>CHBY zGo|bh-ettfb;+_lHh|1JXwMd5zOYYs|463?tiiL?0`b_2t-29jwnP% zAd-4^T^*uHH{~1D50jtre-(b7{`w%ql$~dXM{(-Pj$}`EJ>-iQw2&C@!$QS%sRiXj zKJp+b&lj1hf5B14kuNLbn<-TeJv9yzd2s%tk34b`*{qXnHq~8dtBAy+w0};>Ty-IO zk{*>s`I^Xnw6H6?*pA|mt&VGwRo?NUEJJapTinL?HsosgArhhQelf^+; zhT@*9R=Xu2bcsBe-A39AM4G|ZX;D-~A&aVBZ;cP$fu?y@hg(9E=*2A-kAkH%y&Qck9pmBZ_ko_D#KIR@6&0_{;lOMO zs-`%MA?}@~!x?dRZXt`yjt;Wf-$Jy|seKV1a%=Wii^W%UpRps$F+J6y{O)LW0M`R1 zvbp~G^!HysjD>s+-jS6bhaBKpxAF(@4C7!O?u9+Dy>o=C@EC<|mmTWB_7iEa_`S=y zJ+l#6$lM<6TdFgOFwu7~R^1-FyLtE?rUmXDBZq{K+}VJ^{Wif}v}PmhhN68kZomFk>+Gv$^%H$}4>;Obxtv+ccC#d!-r|=jYt(&^RS57uE~tdki(}OpYti zCh%1oX|+iE<0En`{VdXh*-;!SU1 zu>~ktew};S^|sw9`v2Ih^Q1(-7PROFeohVc3y$Dw8{#OFV4EvI}cNKhT@m=6^UI~@5nDQqj zcae(6!A{Qu?d<~QTH5J6t6EI)kg5k%UyMFsr3b$ye0N<2mQfRtCH#%h| zMIlVOQLuypRZy&;wJ0hm0xF^imPHmtEJcN{N=5VwWl>T20#;ZxoQh9Pns z9x}TYzY6@g&jdq*G*RBryc;Irp9UN)P3G!v0p^3Fgox(fNicr~IK<im)8rU}g2+CQnK`u)G}hV(dMr~QYa__S8;2+gc7iN<7-TXavn2IV;3;|7#js`N zVVA&V$-}T1>t7zm{rA(6$8N@%=#uD0yAzj6Z0a;SW?)}^z=(|E;4G{3m16ow?aObhxiJRlIM#RKH0w;dLu*A^Elf+@>xgYf?I~(N=z^zAMya{Ua6mhw zG(1s2NV&^zb+sjyeiAg6y_?xNaXD>q84lHgB!=9GojDO>I?jt2M;jyo#LfCU4kl&` zzk}jo$3_gky3eSHU4?9m%1%KxL0Rx1pa5zca6yu3gm9NMshg1iD~1KXL98C}bVAjKl>$x&SYW+&5;iJcL~_t3 z@Dj;|Qqf7%bPN5}JQv3}u$gs&`}eWSHEn7U8nzLwz!u23RYuh9f;r3xwfqfj-G~l= zXUh<=(x(MGL0U5+hgXpS%z;L9iUn7omUj@O851{9j=`>Z%K))5{+5^M^<{Bu7;?s) zYDB&0pNyplX3MLR6@prRO4PKVy3n$ex~e+?TnX401&>j*jEA!syC6ty%61ji=BVLQC<@%tBcWOLmdlw6Hop)$x(Bql0}a544!r^q%{QU}n#h|AW(yY7@FJ)M zvJkELH{uS#t?~u|xAr)G5Dqdnfg@shI}^JUTJs&^k%Y&%(MEJ+CLj)zXxHLbEB+T| z0uF+kha8$Q!XQQst+53=AB-GXZHd#1IWa^R!njE=k$vWOz!%|vobm#w#UScvakqa1 z?utyP7IGxbA#qdZ1Vd9PL(9EF2!dL^K$O3TTXZqpB39AG@OgSs7M>h*A|KKoFW$byGp5a#`F`t7-6-+tcRMst(DjCzWR)?vCdRB>2pL7Ztu zjs6@IL%L%Fl*7e~%$xfa3eN7*;HZulRVi%p;XY-~-A9%QY<&K$L*c&6xiJ6gW1sgNn^7m` zYC_HY80|OZTvK${ERzeF&*1EgVr?+zn=fP-_WO+`@$oqUb%gYE9Eg1jH7eIMvoZWF z6NB2oDj)p=Gt)9%Mam~pwrPTL?lw~Xcs2%st)N}@`Z+G1FIME2QLkyJF^D(%SP$wJ z`7kz=?A39+ToQ>I(Coy#L7R~ck=NJV=HgXu_=>1dp1065|r6Z`x4hS!5;v{cy@jT~dk~IOoIhpygV8V1I)4%kTk9>m_5{=7a;q)ZF-y zZ>qsBK9n5qAb!FKv>Bi!L+~O&9EYUCOCsg4k{cepOzzA1UD4->5ne9N=M zO8KB#qKiJ_I#M*(sQs)0*in^^s$5JN-*o6}QsWyuMzOmWE>o@HoHP~ynI*VaQ3<`~*QM-<2D*o9-YkR=$PHX47dREmiVIRlHlMr~lw{IW6MXU!J1$#Z&>e91%?uTzSf>bw@}WjDv4# z9+zMw`(PZJd4VN|1t9O>4R~$34f^FBrrHCVtOK4F<#2^J*>FB0ISZMH`=J`*Xne$X za`*_}B#PEdkdd65Uku8dPP8Wsb;Yh2R$4~T7x0O~9m>S|Owqb{ZVuj7!hJ9;ODYfxUof#|J=APw@+kyk zME)XA((94qybBLt$D9$L4|%1tt{HMP&f6}Tx7p0iIn-rSj>366>!qTXlk^JA=%I2H zVJGZGF2Rp9lNHDC&SBMW?83`dj>iyiDD;~RCSvmFv8tP4V?-u% zPqY`DiZz)R58M2sFU-Jh+oJsGoL$olV@~Iw068EP9j0?W)hL@6+_2$cA1jC6U8r2i zAztCF`zT%j@_VaB9ivBzEqE#59btbdu9GdIE}+3ns3!+_#i7yAHYI7Bge#U4tX6(Y zRDO&7C4FaD17cUKe`+e1xr@v)X1E4%K>v$^CGD+o?gR8`oHnH*dyb zEZqbgHQQz1qV{1!7xyHZ-vqx3-qXFi*c8F@m`ZX3`rBTpDl%p9o}d@cIyHZXxX>xQ zyTq;J?xht$r#HD7$RaRR&sRgZuAZ-ULd!JYfwY!j;%bn5QSs1WyhH zWM#%DNeSBkb12=-Xkp?nUo|-MSY2B<$9$=!39Zm!e;X-MSO4os_*V9)R7t z5j`CC%zg2(bXfPrr_y2F7jLJ-x-Wh%9oBvEb#z$w#V?`5x-Whu9oBvE-E>&@#c!p< zx-b4+I;{KRPtsxC7k_~c>%RCKbXZg|romy6rG^eg7hE(ZIZdkiivuM}>p-uLIxA^| zP)LA^GoqSwT-D^_Ti*hr& z*D+Zy_kdh4Pu7ca0DGw4qX=dWDNVC`9msl7@Mib=cGioMJiFJ8Suctr>|Q8$CN1zl zb}yXzu)HWpvwKl&X6BF_mE8-%70atC&#N-)MSja3>M&~6jF9R@?OwmmdNo4WW%qhG z>qTLg-Rmn^FABfxURP(mD5kS{#V<1R5Z#jtKi7m)R%gdQ*@RQLX@@tMaIQ7%aA>fR zhDYJ24UUI!7IO9J$Ikxb6aEIZ+~&CrKOR4t+O zw7CuU?&@%L>QT%wdqs?cU}JvkOir~TD$%hauOec$#3d26FrrdML>m+|XCumJ!$Cxg ztcV~`;kf`66-%a^64wcg3vAsWy^B^n1chHVK8tH(_%Xk-R*#A*{mC40VUuD<%!wc7 z`{z#WSk%W3)CZ(lbHM?SsK)Ntb7&C|QIYhfEDOuBiG}ZMi^Sz%CD!X%RK*xzUs=5= zKo1oAW}GNz0wlIaC4iF+tP&*^675%BCF^^N9D_Q_kxd|rY9(Mwb?6}p;CcFaNI`1A zsgj`XE!GuOfeD|7+f9gDy%e71Y!8M|02`W>fOS?&c_NL^V}C4$O2NI<+7q=(4Ah`r zr&jw}%l@o3@`x59ocmBXQ_Qo)=yxQmXqy5L;^Zyc1IUIIJ^J>TLy9u$)J}jeiS5Q| zUcoXsOuq|gFRJ3Ht>ggCgn4ilNTGom6p;CO@$^M?E(wDo@Ao79nMgmm0ZrN3R42e` zcO+XqBb<-b3n7(9~pxqGE^+M%Il_TwJ1hWZQPl7ReHdOFJmHHt1TpIGmfs%Q#QO?iG^N>hpUj!?O4Zs#Dp}g zZ=S0Vqk91E0GRA{!G{2)~Qme55K7- z^P7tO&HMhEidiZY>N{4}!?;|*d6iS&P832-;*o|19-B$LDV;kiT^J%4PODt)jXMbz zp1P3vPflX&CIu9*h2xu05db-Gfr#nJpeou%DoFO&1*Qz16Vu`yvK3GqUnH3>j?a~B6~|{vmWtz3B?HCr ziBYafi}6?>(=CqI%GN+}e3+zG9Iuh>3%rglQhsPHmFKD_A4bkwQCWr>H9tjoP0pxUk~?UYIwHe_lfkpn2ZByT!lq*!19JieUAdvK z8#~l7*hE>T#@q~w>Be}=40*gwI7BD|oY$kqw>rGY+vo7>KHBhGFkIP(-~QSl`P!x+ zHi;zzIBofl8!KAt}vK$~=q9`on{gmtx@oWlNEUn~ZSe zw401HgPU;Z1;P~|cQ|tTP3|=FV4mHbz+{VRt%Dob4vhT{ASIN=N}s7Dc5tOHWtxmU z>|!3I&6r9uMyWe()bn&0^w?{JW81+B>@})P6MG8G;$B0pwBg>@o6ubj0JbVK&pFYJ z4NGeu+w@kO%?dknROj#q4DFKFp+HL%2j%GNoKJiEvGBp-;JDh!^I)EG>a4TQfjNBS z5D&wQI5y%fhUvl`Q|$k1(V@eR89riU`y389**}-dr5qW5(@)&(bEK_p1sC;k($YNA zvk(-p>}T2Ri){9@m0h-l7c2V$)Oz|HoBdqcEw_(FKdcsvV zdeTb?wf3!BGsw%b;8JhRz#F~k&3<5X_r!ejf3t0h!gNI*) zZ9)OZ2q+vDvr0`2%5jJT7&L#O2Eu{fwbj`J8>O>k>27Yn|p5M^S_k-A8 z5vlnIjf&K39&k7%6Iey6;M_tRJn%-pF@&G=QR6iq4w7iChl8f)#;U+%Sq36L_aJ^U zk>YrP*(;>|NM_a8KO98-1p8xjGi;I5rA6Sen2>4hcnXJYZ(hs;_!vVR+C*So$L17X z;%q2R8Yf#vt8q{TtDniEi<6Twu<#l{mMrr*Lya!oIB!X6PI>3#tUpI=IrM-lS#*;G{7=;Mdq|Tj6A7GrNuPVm<}@Ce2|vONt0=6GwP?J zXjM{5zHmXR8@sHy1YGI~FR3q6Bmy&YzGIU`F$oNGv=F!<@3@92Q}b82NAu&aIh#q0z(q?mkd?MvUXe;N=}`ZgR4% z^=!&$hVeC)aihet_`%~LVPu#08`svO_p_2Sd(T-xWE>~PY+fqs)=fR-h1}2Nj_n%R z*NhMGd)Ywo<_u%>sjRQ>z}*sa=Ro;@h4{JO%QsL<e1&628RgmJXa!otfs2;|6~1$L|smW3g;;ih%DzqGF{>Qa9>wy62S37faS zL`h`_WGR4P`%o#Pi~VJTM;Yc`acQJu6ab;o1e^sa5k^}xk9c<;Z|21Qh$^7yDw9tj z=EBXa0-nJxfy5M2EtMD0^(C|li(E*?AD`;?N+H&O7Z2djB}ZS{gCP0}khmD%Vt>FN zYzSnkz`x5MjJ=HP@P%`^jXml+)&@7%TBl;Ig*DO#KFh=Y+b3UcB(o9`1CLz+?%Q@}B^EtV1k6@kF_u1a#a{}ri^KdUaT;35M z5%|bCChKtT!}dctgXL^*xU~6OlAC21mAzRtGDYBbB~9PaNejVl~QhVzKGjVTKgX>8P#x z79&#G6vX8f#+)ELy&5USr|ZU5yD_NBEhL+-C^AqeBTN*z?V~^eWJH2xVLF(=!^nox zAj|PTl)TfyqaCi)6@Uw--o-={xJP27*T9y&hzLft!Q+*Ays27G?MI081;Eg|_)Q#u zy99fGssEN~zXhAv5l* zcHQ-8(580XT3?8&n5_M$8*F3ER$%3vyT>@e_K_%KB}(tnyGRCHB~_LLE*_aa(HZS0 zO-X(dQN(Z$oXUVJ+$n4(7$taO1~=+5uYpWM`%rtA%tI6MiuDuLizIvg*)|qh1<|)dW0T5y=*vpdIjMY@x7#b(ZbbxNe(@62WTYqI22|0w3M0jAi>H- zSFa;+E^0ysJns6vT6?9ra0>54wd?5%fvtD(8*(^PZy~z95?>VYp_8Ic=x3_{oVdwG z_ZpiK>VP=ZLsTR>qCdlMO7m!qE~lojoB0C}viM*S&k{r55bVFID28H7d^FJ;sm>>q zYu+k#rx3|&BcU%EQz7mc8`|H&*WM`c4np8;C#Kd}OTGM85C&Jlf50MDq#uEi+{3n5 zf~H%XJZM={s$?M3LNs zkEm?omX|1P3YOx?Ss4^5l_?7POO<}6s`zYwRaS|U=*LL@0l}4$rm0*4Gj@M@(mBI~ zevuLZa3pfV!_;VPX0!LjFK(81gE}!2xpl9`D7Q8Mu1&sB+15X3TuUNib}|F*KP7^#@dNf2BX9?xf0H`fN%z)X$oz{vfv4uwvG< zGW?qHTZZ4W_+enuu>0n3;cBD#hxq8&ehm66{FQR^J~hy4Lu9GhmJ&Nb_~!2vVn(b+ zNlZ3Qg?UXO=15|))f1DZV8&PFB^@W)k4!Y03T;Ym2AhEu4C541IB_*{hBFLdg@==> zxtL*KikE*tO{PLeai4Wl>u+(ZTgNqV?5X>to9KRT;#xY`n>kMNY=y4EOPSM0$&`X z&m{Clf$y-aua%G=ASM)=nJr+(U0+OlH%t;p5U) z-}oo?jUVj~e?s5*C-;Xxv2XlU{ozj{zDbEofs{iDIYOsPQL!L?RTjh?6en;OPmPcA zmL49WygWZF4%5xBFM!B5AM3?adQLoP3*GnTQ+fvWUhGflnJhG((tFxm+sJZfox)#t z3}yY zdQB_@6osGcjwng}~itwj2TzEjwXZ9BsNXK+pAaE^875kK!pFI;Y$C4 z2J-w%29UhY??12Po_%SFz|63MNL*&F6-1&nv#ubL0CL38%o)SbBqmMqC#_cZmA)c^ z#1yM%W$HL`Pf94zCjY&;p1BfL~hxu>gS6KPnEf0Pq_NAQqwR#eJ2{UXt_+2!z7oFsw zUjd3~eKl)+Eo=QftD7gp2AmsqcTO@OeWn7M^;UP*E%UtTo z8%O$(M3sIS2K#G33ls(PfJ49Z5A?Y7kMez8zJHSMpXK`(d}GsC*?8i}h@E8G+_9;q z%@dnq+Ptw=*idApEW|R&Gp$0oKZkNV68J(}XB&7Z`Qri;6CiFYcFn?0O4q6Q=v}k% z4HT){!R|ya#SaDkj>P8?)w*NOlRszcq62boSYaPj35A>L-(NWDomznW>=FJBuRn2n z$dP8QT!~MDuDJXYfj^Yv;YfwgSHa6XsWXX)i3a;eEQH@>bZJfqT38IfOG@K6q-cpF zD9hEWPxvK@Y;=K>wEQg{gT-zqRvOUT!HY~Ou$e}Q$6H_KcO5YnB9#<2ZJ|Cn0frv4 znX|r}9%oq|6bL1-T!mPKWaeIg5E`q0Z76;^e(@P*R?LhAyo$`=h)L&oZLYWHC{%_r zTC{PnxooBG1kPOfbhoMFQ8fHa{28WktoCeLdckZ@!BU$!3FF5zU%W9ROgG#@f0&;K ziCqaC+Vqvgn>`6!q`_^0t|Lgtk@&liaMVzv>+f(YsHIsG%Omz4l^G}_#xPsI&#v(o z>05e+rk$!}0@Q8VjWqX4nxBz0J3I+oP+^+yNSZjIifS7*!|3{(q)BKMo;~^riUM7* z*U(HZabtbmFjU)s%7gla`$3y6^zb@Um|J_Y(1FhLw`DY}3eq?m z2(9=SU2o?iR4IggM0ipV{+f&6Oo4r1Q8~2Pi#l-zM94X%wd&#iAE>fg!acf|9&%dXF=Z!q%lBY!-lBr@&>l|jnMViqDl9q4*NxIOpRKS& zvl4U?GCDcQH-(eB&mA{0%Ayu!Lr|9b>4 z1+e)Qr}!k{Gp-3J+2ehW0Oc|GzNk{l9`C~y5skm*+f`T|d}z7HOkoep%ZPU=PaXor zu|;+8K?=^(5iLB8ZUS2%anYKWDRiESXt~t%M9`Lxk#^LM0&G#QMA|hz1fTVacA{YE zQes6E7LS#-pi?r0s93__tTpTORILi!dV86(y$oNYz06xyTvif08BG)ilBLz<_{XNx z$JL&kej4tW4ujb3B>AMy1w3&x8Y}$f!!O*AUkCiE`trlcwBg7`asv>sMV{RfGg2Ll z8+#5u**dp6gBpUz{0LXDh9mPygxG*ZV>X~WwUhy|CF7IDq!m!j7h`@@Sisl}mM&Z1 z*sMJFY`T%w4M;1LTFmIpGJh42a5#024fG&DRk@&N0jfsnJyY?SH8qE7FTk%R7yWI3 z2KIxlpN`A9*({KBR}N>cGSnid1X$r*P!yml8wkZ|04S8D7O6Q+0%izE(0mD)0Tq%D z?I$*~UkYttjJf3_IoKG49G+ZqTn_XMsq*};gCV7&mW>MXgjW0GmD*e=PoR!^4xvW=48S|QUI4gx1% zL3|KPWCMCr%h>2}C&(w_3M-)EEcn;9^*(F z?fL!EMhMYSSH3S19CFR#tfcNCMSRN^Esq$cy+yaP` zH+ZVEH;B##=0pzB9M)aQ8jxj(y&LBDLQ=0xl`U^r%k(6Zdgi!vpw?9|z9feunQ%54 zSF#a+>S4P-{a>wUve7BFktEp+uNpvKpHkp86<&k;4)v}D0l80;Tx2Q{#o{oLGjb%) z6P!%hfO8ojRoT{!;vFUPA6_qZBp8yWAb8##HBsH!QT%NxgK?lMoA zH+G7Q`i0H*UyGoW0-F@&af$2@3fV}eQCkNobZc<>F|Sh}Yj$DYpSFc_Wt+8ivpO?I z{kYc2*DfM{rG^P?r*<62jp;haje@pUHYcx#v&&cg<6V9T;vHy@rU3vO1_{$iTo3y4d|MhH8bKZO(`6pnC0vYb@% z+ErmB`2!v<4`upi&RprYtpPG;P;LFwo$a4F42R#g1hCXqWcw#|pdVYNMn7ojvG;3F zmZzc*1!oyfg8iD;Z7a`ei-KS$-nPPFR;?63>vuowzckeEUN9B>JqSqyVZ@yY zj$?-}_s8`cfAqJ0`8Z7TTCB#KFGJuy#uL^@4#)6*$CJUs&O7hHDZ`qY968)YUZtTa za{$O~?rhxlA)zlmfol%i0MJ)=|5q=;jN7K&rBk_cT_%-+uQm zLYxnKc==x}rZZV1^^)aY`7 z&YD2FYYrrj*(qEld|2WYs9n#jHgW{2ey>d$03W`NWS0i`va1c;Y_mxNeAy8%bLY*H zSVD?WoLz0;Ei7BO;_R5`H($xJtv34L?Y|1iYK|!V|5Q#DEXfiQ7ujqDE3pyMy5eSA zwv}cHS(+2B)J917P=}L!*;bY%Bpy_>rB`Mnr1{jMjgZ!UvSo7Rh6NVy#y~dg)HcKX z5@o|xqo9iKbZ$B4Zy_u2-~`SqP{Ua4DxN3s@f~#TNPB9l<;}wp`)43<>vmL~bzOrm;856J9AU{MQf1rM&drxPxAkS@OYdq&v=SyS2Su5LXJ!(%W1E?X zQlh^EH%aFJ>$>V?9S0IcCDH5b+cK#lhm>Mxcr|?*f zZ%)XEt&fN=lz?h3pU--5)2LgMT8aV|yS-^r7#&VTZ<#UOm7IwvY%`aVY1DNu zd&KClcKu5{bKD`JW|_-<#K3m0+V`IW;_?oPrahsR{X%m;F|UQQ3rN z-7FNw+i2lF!42@@K3N}LvI6bP%M@za>dTZ3y#MK^aF)F+w?;6_at|<+Gn2fRQ8BN~ zC`X>;>x0cUdGsobwqL5r_K=eD=ZL&)5-)Rm$lNRA=9(qt&k=UnWL@U&nzf-M`vOYN zl_Tb|Nx1+o$%?oRK*U7_cq7kh3$k^YzXLG&`v?H}KNm()>u2{nh~{m<>mcfCP9m@? zNlC7K16@XpDhQk2>Z37oYu7HpE|AXVkv}`!nEP}6G}pcbSMRp8&TE`#iUoxsPs0Nc z8wv}<&AES%au#y*d7aRdo!G|3zGy*?FFdT`_Mh~%>;hb-?{PpyKE_bP=5Abpnv|m$WgGycWIA+O}$OlRyf~1 zq;;pxbreoiM=Dx7$f+LXT&r|!a&;&0@Fp%}WRJWiYtdhMkg*T1vGC$5Yae;R+)&fl z%lq~s|HG4?vzS(E&IT+w$U%U1OqEr%JsZ~X#==9nYHf{B1zvqF+ZG~c_EAD@^1d5q zTsT!NBVDq>9E>FWpBq;6wGVn9-r*~rQ0Dr!fEw8aLxI;nW2N?gLLgnPlgTmvh5(MU zv3=z%tUnO7cot2qYxCBe8Ft59b7t1Hoaoi8oj1{E<|cEZx7M|grLa$L#fgfqN}t9t zjq29FLZ0WO%r@dxO6+;I)B0V6PQCyu&>Ad7fib4#A)QCEWKCLdB#S+b9lnuWck^U> zPoo^6?~}_S4D0*gvdF&J;aeGDYpXp%Yl1I43Z3ltU~->bmnXjM9aRaoBkyYK>9aiWK^i5d8af5GC}EcPJMpf%-9S2v6-Qm{5SlK%BM-&W{{Vh(NvN+`mv|wZ3X&%ggsuM0w z?KcXYvN=nU)ARJ&V_UZ(aim2_N&$D}?0?T(^p6Y5nJpNhAW6>e@gG{ZW+#5PW|}8@`u|50 z{MYK$juOvc)yAm7n?FyaTmi;$jRHV(2U=bqZH=vlQqkrq!+vwg8#bxvyG}euX`!5x z`e>maS~JX-fc7<7<~2i%mgMhqM@#beb~)Tp=p{m5EvEy%%S8SECcXas=J0>5uH8oy z{0~V@JwWKgd0K4V6A$DIQ*R>`Cf4E^I%-Tz=r}ly{U^SO{xyt~U+287d>lMdE$Yq^ zU&1ltpr^S8BP!Gaf0>7l^)9McG!oF#X(Z0T2X9WtuEq)R32$J`HQYh3I|w!QP}*r! zCGY~2FL49n3l;}_f>mDqu z0wtjR)6fmk4G7A71(cci3+e_e3wVx%=7X+E7rfGtr|4K2UmK_tBI2DKYNzyMiC7wN zNaA(VJ>Fo*BjmFaAsh%x7RZ6bvuR@k8bF)N+f+|bZ@wPy< zWQng#QCBAPEoN#q=nA-0t|HXsFJ%Vg8q^qOJ53u8#r;l|ShY&*E+kf+n;2Cos>EvI zBLg)mF|;qLK*k0#oN zANNW5M>r2ed=%!hdGNo$Js0pcAm!l0%{MkaVS}@OK&!uYhxCt3VW1xf&ED(~>`$sB zyht&G7b!&bBrv2LKSMc+a>^0ggsxJn^aYk^qgqrpYM}%;6r%<%XeV}L)Mjp;JPaAo zTrui5sGX@a1q&#oKnF7VkEtmU8hoiL5gL4{3y~Upsq#=SbETm~4W^N%4#nC9nLtzk zi9qkGiM5EpXLL^idJJB^x}gqZKj=3wHkKd^ZoZ9levzLo{M6!xi%%Zx>GRFLe74c&?tCA{ zb{T!}kU?&2^x00I@8tGt;G%CYqBnN?-Y1FCD z*hC`6(5U0l*rE2KO;^$xf%9Ns^pJMkqrqYHd1e!FQW zLx5QE=;yBf!;_oXFk(aLEdA+yyEk1;Cx{)78rT1E?51nzJciE5$G`gDo35pEIGvT- zI+kqOMdt`QXMFDG12=t~&XIKf{=mVGO`o9iSUP_l*FZ`=~Hxq6!GX&UzqdPO*ha9io~N|oa`#v#FiKXW#ZBGcmKM6 z)2HbKb>iX8AA^-+5#L+Tv|i0Wpvb`30=mE+Dl%qR_CZC)EZA$hRfr&Tw2pvG49&x@ zJ7Zr5$xKzqc=|4sJQ?)giw&CcqSzeNLJUzt1F~CbKz3Rhkar_-^v2K7aHVgg5xbME zT@PqobKrb_=ToTs^i8lrF+bj$(cm!`F84JM#_1RGQv$u}rvpSCAy5f|g}Md`OQ$S_ z34LQ?9}Ls)8PXI>E<)Z)<0F%pCtwaqV*G#^O0E}pS@K#jjpUbU>Rn9S?R8h-RTnC4 zg^Ez>j@^q(TjH}@CJ-e}76_E(pNUbTn{{0`P7G(jnYaRh>PJFN^q8T#L>;POT+roi zq(2K(#79G7gML^k_vZ-j;x{C@4TY^Bdlfh=nGffKtCDvh?^RhaH7Hdg1xDp}sVryC z(Fzn=<9=-=yGN4c*G#tBmMp>H>Y{3?RLiF()e)AaJDEiZuNN zfsO^V$_Bkzpz{H(wn4urP^j(Q5E_(;;PV8=?gAc{3T8qiL-ZJtRf-Cr zv@s_x!AHvdXyGxu(}?GulNX_grndLd=q9zv%K@nM)!=<5r_9|LX#D~0 zNF|5wfY!YkNu}HI324=6_7a#o*%Q+28QmPN>Ox}ZUF=$s@_?532-2$a1T@@<1Bh=) zIkoA?3+N$nbI3;A9Hi50@Cj&t;8RyAhKe+VI6sw%lN4v14s9mlL|hW* zohnXFBk42n324zM29}*`F^;4c0MN|=ux^kTDq6(aL#iOBUBtli(4?DF`#HuMOqh6Y zN(~Uw)qJ+X zGY7hK)sf26V+*XuzJaWjdMDh7GMfEwH{U~@=pAzXMdquc!T@?BL$0rzWcFO5I3^O5 zgQI+_%n0&*<1Mz(NFa37z-76<0q2oQPTK)zHz(>e)RAKZ8nP2Sg`S2UWoI{Mnz~Lg z%u+iO*O2ZV94Y1moLxloyRKo7E@)GC){WP@*!3}bQ?NF^DEfYH4T0nJR`mw-%!vkBczlOPrF=*K5HE?s2=m8IlAC+88y0$$4eg^ zS>U#fD=Pr8XGh^kT-54L<|p9oCcBfqkb%7eRk4$BbaMuu@MmdSoC$;Ff%4dG;O!0qU1TS&It_!1 z&CjISz`#I7ObpjrH@Tw;P{@xL7miet@eCAolhsV8@DX1#*r325Me3r&&1`<_jpQTD zlzL7oP}J3l0Ff1o6SsrJ$j6~RJkJdd4h&Ad1|P)=eV;u=MWCJ>GLnDJ33d_U%qey^ z`Lzk4j3zc~oC-Js&VVa%8#2a65hi{L2u3n+W;TC%((TGg_Dn^tIvb9oADme>9A`f` zYiu~KzHw9$Wj30C*S@rX+nmSTwt38L&SUQUc}x`*6PQ~ju%O3CUdc37IaJ(!&%^;THW~H65G^I$m`f>RUAsC>&>{z1A=OEaG#v%E2GIu)p|c z_lbXLzxbS<3gf@OUwpE;SK*|!J^y~3$>w3}H7F~m_VtqwLyi-VK;sYMF&moSbJeW~ zCTq_e;>pxI;bC?K-;qk0uzlUx3ES7#gl)DzpUl+3Vk7B0E5AyL>rcb;((#D_pUmR^ zlIug`k2R82K=hYL7tQ+Q6TojOsxUDyQGX}DG{yauW)}!hx7I$Jm&$AUsmf?Sr> z*BHqK%u{Lpu#ul)S$`>VCY|sU=?CGSOuFT^Nw++I(lu#UF0(CUe#y>)Tn(sS62gPi zrv}Q^B<#0M!i{)qi~02{8s=hfBDR%7S~p&vUPyhhW@I4dm-CA@pueKYJp2HQ2I-^B z*#X;}9VnQy&9Vh#rVa*;}~p&-_)6a%Ic_{e${3k*HIL0nxlLe~)DNZf$_xjKD8 zzDGE{9-wYY(yR`zB|ZaoY9w{l>)LThuLS8i5}!f@wl}hH--!}3ypa{V2k4M{$0!g0 z8!P!d73K%XXC!)*{P4X&%JKQ9gbQ_3GSY~wkmAe8Xa<}WQeg5o!SW7OouM+WD>**T zzTj;i4V*CEL4yumU;0a=iM*$p{xYrdfLLrb3RzzSwMm2}r)8Rj0w{J2a*dTl!+xD$ z-7(1GLKMJ!Ff-D+6I&8`ard=|DudkKXOy*qIq)WqzeVkmi zu!Z)_)TZu0X@bsfN@*vc7PW2)Xypk@taL-To!m{16uXDI;P?%&nq5c*cT#a+9elN` zezBR?php7pt!FH~HnEWC1-|%LY%Y2HzH3;X3;9XhiA3we`C#l0-31RzGuaxycm0@$ z{jLdJkQSN+`ZCKeYXb!c=vncpDjl_$P4he5{^D$Ufs({G5HUm+tTumfjwjx3{w6#f zsoRm^#J89MQ^lEQ%(ruBg`HJ~PuvY0ZnLubgkxm89*8%;$HTmrZA9UH8+gTWYz@Wm z*bYSS32q9(^&PnCUsiiYD2ut1h)AscqJhV%{h~#9mhdY`1R~&DX}Hq2!Qf6Wp)?7m zDR9jJInZ|Gfq&-ik2C#MhIFLAM#Gi9LkvCkFbg}4Z36nVjnI*z^w;SFar6W5N#9OK z7eVzCT{i6GXRu!4gEdC<7p|^7X-Zuj@Ak61$R_Tn>`$HBwh_PkW4hDGL+(KJYS*eRLo> zW~KHKJ*0}`Fbg83&X(B{x?{Zb@!6hiG%P%$VaaQM2(r=Qs5@3S)3gyMFHSyhV$>Es zOn(=%Y}8ItyLuIP-H_7p+LO5zs2(UVV*6F9v8QN)MnSyuz8@y$Cg?WPQ{P4D)Au4L znM(Zz9j^2@Xcf%~h%kpJ-`Ym1*+KEVXAFh`TGTdc`TV6uDkTQC`%$<4= ziR~4!Jdnp6)Q#A^Oy=7Xl{@`CnvV3H%Bja7Uo^ryQG-QzJuV3uLPm-D!vH}McW5Bj`yhSz-D-RG&pYlQ7+8oxT|bJrPeYolK%Vxt2Wm7(N8 zHd`oUWYZK<7zrPul$U>{Z5{NXLJSoN`*XDnLI(-wTivd_@ge_Pq{YNPgvAK)2c;8O;F0q6%Q6UHHF zT1>ZU%q-LOL%4jHq}g;FO_1tJ$FpIcmG}`*cq&Gr1Qm+InZkQ=M&b#0f&*YE3-n`x zFsiV7(qwm4{oJLUg1q=8FZ4}pL?6kFi3;r#!Ijt!;Y=`t27XMQ{reW9bCmJNvmkpV2L!CK1;#@ zh$cr3n)lE|+75YvXdlv+dh{VlMZqUQ3e3Y9x`#%C*HX@(m{eu2j=g~lW8*TmAHZW! z4E()5x$(Ogxvz##-CTV{*ZoKf?rY#)n5F};@52Z$Ow-WCQPYI6TTqpI?b(KERA^+H zrhs{Z$x6~1I;>6BdhO1 zYdq&L8YbiwX!oqI#8V`Ayv&^dcZXrv!KZ^U+@aO@>R+*seGG5$CN(3~ID{1MV~Tt_ zSu9C4IXUW!ldoY+Okp$JcL_ZAW%Bh-2JFxiOE?+GbXj(;g>8Y+=@`n9*u~6e-{)nU zjvy}&q72<{0z-E=u3n8Tu@IGG8tdU$)8L5sYH{#{xMAQvMBKNDtKJVz3;}MvZs3V> z!HEI~;yr)&VTBV86eUIirNmS3bcY*^6!yw}mvHaSr46^z{;MtRVNBay?=h1%0{~yw z*l;oC_+z6GN1_oQpHb7`F`}E{frBkkl)N@MkzeFR*GT6d5Fe+YtaBUHIp;QXb!G!S zAIp(A&(}D>cOw7tmuyE=Of0_n56EG1Go45_zWI-I-byFhKz#G-bbgo4F?9Zk&L`+> zqVvymzC!0%I{!lFkR@=Aqw@_qr_u>_H@^9Q=ng-+H0=;qV5IyCJO&_N%fw)uPEq1PxoPJTP}LCRi4dzG^5w3|2%n;oxd zX5cQH-LToQ34q1i0d$A7ht}v|P(GOGbs6pd+G35fMYGh+Ofr^)6P5-&I3JM@86?D-Iw)Go$<%IZK9u}Xpd~+!&oFKCk(09T5Bp0@nrH54OTCT^I5*ld4( zck&NR$DOS(zk87GlEW=L1tk2H!bQ*@^4*_BdKa6e-(yk+s&p;Nl!pBt8)ZtXlAfic zSZOujS;~|OWorDMh1sOgG~}rui5p4n*1@BLqcl=u@k!%JAn@;_Vb#^4j0JiZdxRtL zG>AMBVw?*(Y#+9uKLxa|(sEfiN#f{T^RWr(Nc@Z_nEh2WKZlEBom-=xn@7>d!;Z+5 zMGJj;7kio!IdS2x=n)KN4MnwBw{)m#b5t#IFWikt1ZVI<_rhJF(GxtJ!YA8-YZnz9 ztSvk@8+YrT1hXh>pQ-(?3M&T6wx8dQyW2=fNV+09OQ84CP>mlZFWi2Vt3 zkFth#MOHKfyE%p{jBug`O=BeLme~#OiejmtHI!S<$imj2VdPfMXaE(wOYh<^hsxBu zIC@^ax;_YMF!a@{ZG);n6iO)Z4B7~8G^fmX{(v+LR(QJ_$Bf;!dXJjEYtdae$Qiry zPgdI-Kn928h?gNJW*9aHrNr18HMgSmSvhc`B+}%N1CE03oE(_G%z^SedJasSOb$$U zra@W*?U=0??yVfW2^x4z8gTgZqKpJObaoi-mUW4kk!{A%`l)Kv-Vn2}i~b(%y? zMACV1A|($~CMonC(l|SlF+9k6C$`J8UHA@7%`(cBk{!#ys?I_9*dlyP&*WjqCY2ps^d#VV zI`H&>Ilzr-P{PYhCOPhTuOTy;d;zeDPLRk8+w)?WEJqyMN%cTg;bVV9reujR>W~c*ZUTeDOvhel zrj4|)o>WPIr|gf;iA z%4wB-t8T6qv5bBb3D-fKMW5tdnO1gi{K1lu2S<-Q*f8>7eB{BH@%+wbG0YWrb5)5` zr3u_OG3s#2T*7I~t=3sPY%yMGC;5%VYqk@ws$N<3M4E5(7fYL#Ts4@vI?!jXq|Uvnx|^>? zvTCo6OODdW3h6OwRpFJ!R`2q=tH!*j9HB|%7rfHSA}d-sr1TjmItcNOLdbz*expKi zTM;O`NEShOWW|kP?DhE}3iheUZ?rb@{9Z5Y0goe6z9hVm@Lwa&(t{Ni>*9Bz!F?h1JfN3&YcJ8e zegm5$@dCbedI-FXa{m_YaBa8cq<6i|`*(1U3`5Xr3(jp{22T7pKG?)lju+v$YB?y= z2gC~S5(CWCQ-@%t8HE~pA@6c(=YwWXf@Xmteg7~-qpqO645aP=+5m^xSKt_F)DO=3 z>#^x*gC>TNcohhatS{!81`sRu8o>FN-_Vx4+AByC%&NcJj}($y(RL)Q#P1Ox%k(Dx z0H;q+{+w+q2z#+R@kbaXo=^g(A$+CYodcwnxhAdPB~lNs&%6$lz1H0C9rKdmklZ*D ze}YFe6vdM^?!=#A_d6Y3b*$8`zrdBie2=XsPtb$Is_{A;8u0EmuZ{i>e8LN{i}xl& zxf5>@{cU_kTIW0cXeVQyaCFrpx;pGXMK_~_iN6BHp!#56?fo&F;qWDYhoYq3fi29( zvwX=hD^WBk$n$L`B~rb#k?OS*&D~Z<8bVwj+W}3-&Dc>HSd00YuB%tG%4zFFPac@S zM~(&%!l$da?UTYyY-uY3#3q!lb2X#D5BnsSA(#8vUqWk|9HBL}eang`ht?E_+*bj) zDAtS|#%AIJDe!zWfjR~Y>3N>zTI>wV(~WaDRR~amc|3V3%9|RDd|ncp2Lw-SF1|4* z^GF%Lrk$&K&(})sL{jaL;m%%ktp*lCbK=*Fj&~1H@YgEeqkq~lca}xITc&-}`{SYa z6wUha(D|!Yr=lB@iSv_7FNiK*x_*74GrDYLG`VqoboKh^wA1HBFG#Fd*-%>Q8-Dbt z|Ks~lqQFy?*S>vy=aJj2-(7cYyX}+W?!9dr%qPzL{cYF6yz8z{-1Z4Ex88Q0n3sz= zclGjB3~Ob#y$w3!iwy1V5k1(HRDaKFdQowaN84PgX`9^&w%rdq8(lfwMcQV3`9trM zp9?1cHdiaWveSsgKLnC?jE|TZLwp}YyeGi6xdOY)Y}n=>h2=3lF( zO>aMlnqs z8m*c(wIR83T>^+WnTr4#Gj;GEN2iOqcN)td5ew>sP5 z^3YXMez$U!z-5bEm8ExM1?sYQV@c0!d;OCIJY0@ zF?O`QU4GT4;NDbxB9GXXZ@m(ZrhPp>am;=@_~5pei?%(hZQr-)BbR!%{I$2YT>Hoc z^VVo*&h6d!c5h`P%lmB4$)4$tw3Ta}@#&q_)2DYDa1^~7@4cu5q?*@TITl#24$3&2 z=`77SPk{4GV70>BoG#bS29evU*bq?1ueziZOVGuvo*=c25_+sUJ;oy2090;oUr#~{~GxCTTdN>n0)70!6h`B)|dv(*6!`Cv+#w~{*?todbA6bUpeb~7a0o%45o<_5F%i+i1$B5WF%eQ}`Afic#sORlh(>b{cgZ8h! zoCAttf&1#uw;Y!4{JkxQJ)cLs1GgNW4s+w=98?K?8>;C0GPn%eb|T5q+ZpaHrRCLf z3t;qO5rOXBJLW*olRLVrdMjgSg*}fhIrr>y9`x9H=C;==I6^Ksv*$!k1FIgES>cWL z-e=IYcDB9KzD8Tc>1kE#jo8j_djoDv4zqx|eP7zUO_J?>_2g|Mn4EUcjULp_$;~@X zZryS626Kvet^LuH$6k&tp5A8|!OphVFzgLyc3$89X4>1k@f|hW^gP1eU}o-#ReOqy zv=zNLE`bV~HhjR2b;Aej#4rB*yai`|UPEf5+umvVLHmxChtOkt+TJmz9(4PiZLhDm znc*Jlnf>~T9UzsaowWo-@A>a@<~$_xS?)*myw@t`^_)|(peN;N8prt0>@l{zedLk0 zUd&!^qj1{Jt^pX_Pjfnc(p1uXD1q+La~gV$N4b~K-R60-)p=g|_7{MT(R}9_ukD!k zPS4pT+x~WBN84*VX20IKWakWRr}ONdS(jIAe|+b(%LnXed!t9&(dOx_=y~SQH;~Tu zeMqzD($-C$Vwk%*_Dp^<{S(tbTA_rvQZB^2TkZYFw)yI2g zmmJzAUL_M2mUISIsnR{&^Vp%w6&Qp6j_D=&(eKQQJ}t;^^i22kJo9oCj_-1=pD?ea z^GY+yA02|6M0=T=NEU@rTRroK4?q=G^voJwb;jKGIdfKRD$UfbRAtk{JEjfytk^Mv z83t-&&q5Ed>zb;2Hk52Ta-_2Xag5z|_(-R-<yl z;0Sn<=g?Cu@qH_<5!QCw!&RWgLx{=w))Xfg1dX$o*4cz4JSaxfV~4&1_ZdB_s(Lq+ z^lrd7TXE=AWPaPjCFckZa=t^y^$WD6GZ2lZ=eXVr2lZZ9)q7#=&@dAbJkl8p&Yait zO4GikUp5_Rdb6pw_o=g&oO{kgJ+JiqspkO!KMs%H-WS1vfgo!%^r{GY+MGjJ#cJ%7 zh<@F0&)hk^ox>{*JqEfm3sz_7-LG10ODSt^qyCqNUS|1(#L{->?3n$|8m)Jn6W-`D z{BC?_)my;ntz3wnW>!Y;i1~0Vh72sBrSM&;U8Jpsta&9|i?uUhU$4!T@A+DzwghM0 zmf+Oq64-fK)Q((t!U7T9HZuPRotDBm~FFtei`ng1C*sx-m`nwcYnpWb4+hS^ z;>W+f{Aap~wA1kG&;MrleaZ9}r2m7@X)m_kduR3cr~Mx!olR0Yii|ef_kD*s!dUXa z#vwhGb?6iKu+Kbj$kU|XEM3gq^MF|1+J7_q)HyHzJ1~TvB3zj9;tzJHB_4>7#${lk% z*UebJy7P?ntCvGkG_RA*6>xp_1!t_kVCjY#iFq5AGKSanxouOXEt%VP%8YsQ+vYBr zddA5o!#Lxl=~L!v+MoOJoj3jTx%0I@_2WD1^wUn!w9}E+Y+Fj7LSD~1DRIFD7GUnm z3pcJzb!ysBKu=1pT-v!Zw*&(-aI37P`l&1DuU@|Pq{PPco!a`&L~3nv{kn#gA4Mq^ zXi|!HR=<{gbc&@bIZ?R)6U<fDvP>J0tJT0<&u9u}VZs_ATgir9fUrq~&W0I{l*ZuKh#wi| ziCQV}NuN%^U5|XOv*k8_rf)!O%rE~wtDT6jvk>ll2}SB{fG=};KIn#SDHP3xMYR~9 z$ANNVkc&p`c&I~Dwr0SNMwARDo4DjyST>8U zbg`xj>1Z$G7^#bP#KHRChQKv90(;X{ykQ*kyCi3fn6J8N|#8RYW^H~$U~FVuYFzKQ((!}FuNnW*@t2Q( zaD43eyN-YM_?M1<^Y|mj&zf-d#1#|QP24!~vWd4$ylvuB6Ms4JcN1Tk`1V9cOR!~F z%ea=gEqAqCd%|x|@SiyN#L7uCC#{~8n3SG$<)rUT-p9^7KtugOM%>uixVW*S@zTc4 zjXk43JNo9)UmJb*==(;)>!12ZnY58QXU2YQpH4e4K>8#i@dqeeg7u@vpoTcsG{OCQhTchZ-yBRU9lmeHnRD@j7YB=%I}f!X3dXTaX--#v$R-#^W0&j2Sz+aa`lL zaf{$=wmBQ;*)lP+eRNG@m5R}SS|Yjn;*D!pM`zDCYs#W2r_LB1YaE#=fVU`Ay=wKv z3I1A_CofKQE?sxQ(&XBP)$7koG;S>#W-lzxPCB+0m7~6jeEQa+BJGjy{a4jbRz7vb z30JS`t?_zd-MKZjSqA$?zs5=l`G2Y0=F!_W8UMJ+)z~c zu}81CZ_BmyUz>mGtdd{M{>c@ef9i}UAMg6@SARHT)V;%o4J}>&#wG-xgo8_QpqRUi{t9AAjeymj>@ze@lG%@qg%gD{Jmhk9KP}tg%h(UNvL##jmZFf17zzyX;NUkNh7+z0Yg7t(#M-8nQ!tspQai z-qSwAN+0oHhvQZyCtD<1Xo)0&88R;8Q*VwF2>3jKriD6e0YeRrt3$8FZEV<5Ahpm< zYI<(STX%E!HN}sHefLlN@VcW-f1l+RwseLTg3cBe79E$jU*0yO_2~Hc=^X1ON)O+!aZr>J9 z`7vZ)8}(i;$GckDx1INO{HPC3<|CI)Uht#S^M31pG_9=}r0$vaw;MqQT$vC~>vcDA&(w1RDF z0iD*ei-l$9ZkCp)7BSctvFEq=)9iR?@C=HL9~T-MM;Byh(VQ+wMY}02{U5^r$5^9^mAR8lly)1fLX^|H!BUeSCp^p!f!ic_5+EhpLcTY zs=TdNnzT*+aENcvGo>u{)a!NR@_XOK%6(ogEO5-9wfql>-T7xL(UD>k_&(Cz?{fJgZ58t+M$IOe0Z}kqzUQ*T|ra- zwC6ix);tQXx#lIf^yOi#%-iiWd^%x&Zg0`nqV;m~<)#_G{e6F?8x?yp{UoSAN(4>` z(|1vucRb6p)9->)U5e_yeRu!U&Wl}rXs3>&`kHTlw9~$8&l}76FZ>sNcJAh_YY|#i z-G8M<&hA~iM$LNU@jR#Cz{At)ew!G->GG%e1=~NK&epcMSa>pL{E<#J+q!QKI-oLr z!9A0aAuVll(mtf;e4J2MY$K|jRxTT{^W`9OQJC5yeS+EpMy% zazk|uWBXrh8{Vh7dDyfO+_=|W^Tyup_@mgRMz__@BiV}E6>;ur#4LoDR;lp^6vHW0f?3pk!B0O$1 z+!de)1o)7k+4$P68XY?R*&P3}@2^~x&~}}6sU*f!dTWJfWSb^SuP;rAdwk1(V0fXn z|A03~_i=+>m}kVQ1isfh;>RDjXpiQx6&KLSSFhd$kY?7ljwSBbaHljQcx)UCYN zo{B!*ci6~x)uV>*o7Y$A_`VxCeYT!@9XcePD|R1y^VF=4?>!%{TD-)3{#ff^M}D+( z?TEW(36ng3AF)ET(pA;3pik5Dd9E3w7d&e9ZtnAij$R)NFHD|!$9dl97=F#5sQo)M zd%si*OK{D0E<9%ezbzR2(RmRUecV54@t}EaTQ$q>7~>u3x^Z1QjYM5p$WiYvqGm)X z{<6z<)%Oz%;vOr%thgOJ(@1NB(ca;wKkpzfJylcDU3=lN zi5(LA{nz#V<-I$Wj9=F~Cc~khYri4p#l`WT?X!xeOx^vua^CF}+XYLUegvji_ue^V zdd~Y{@AlTM*K{t5IbZeqH^(;%SFd><{N=&3(7t*WOR}qWeZOvhC*u1>@1{!MB27MtJ&{XAb~QzAyw2fO!rpVw?!C8nz4-7P&*smsw7kxH3(n07wphFL zbj!jY8#|Va$&SB%(_}@{LthX2Y|*qx5okc)|M*ki*UW#GQt;=#|KIRwD?e-R45#e2 zKOGgAT0~iD!ESS#^9*Pe|ZQvb)tEk;kvq8hd1|0DJO68dTJ(JPI$QT zcJYi)W8)6VK3_>oxfXR`^Fe>_1zopXI&|-D^@UyQ=6J>mlHMH~tMm5T)ETaFdmat0Y??$Hbq?5Ijzd}yogrTrlVN}oTk9c-KD-MxLQPiL>6 z*IyWruDO#0nf0*KZgy15WBl459ziDq&Q&;Bm<-i%kPL{sQ(mw-G;H>@^F|a6{rTfxDXGz? z{T4%C75ClY?Pi*GaY`PhwsB+U!NwN$u*u-;-NwS&qRaBm%PlfG{_{_JdJ0#6f`^~{ z+Oz;OfvSbFNDSAxLZK30{L@9lU!G+Ad@+ir10jo)j&C~YXw>ahRF}m&A0L-}zWVgb z$7UUe{-M_+PV4#LYu~4sSO%5ldkxtdRy;MN=7;8vn4*146S^%P5%uP(pj*lFGX7ov z-4g=}1{qGC+Dm%bqx1gHMvq#$B;LH86Phx_<$FkF)Zxodn>ZS(+7ACv`04Per7h~b zGQ-Eu&z`lTRc4C*rJj>F7Wd!X>+yvy=gL>x`t;luKSigTh40#DdrVJu61zKvyzaXt zXYpg-h;7%d_t;wXTiPMYojO>40IbvA!tJCKfwNa5&et5Y{_d-wK zStp0x-g50mOG)P`os)G(DvM4`KGVfySCsXxt*1{cY}3`D*OEa~>;6cY|E1(&*72@4 zPMjJjI)xu4YQ`UTkrZHyTvSzLtzDZE$;QR?R&3z z^ls;I*F`h>MVdXH-{ktf}%F$Zr|gtLwCFqO}hH%L`JRPnDqL~ z*zLyLK-sN(R(IN;TC9{fw@<5_%$zQ&Yo`u1F8^I=d)(NNndavfU4A?%bZt@IiujDS zLS9OF+SE6bPW5ctPGdnEqo*@x=x-hKbY+;0dgz@$YATPl$jq@F|FT(1g?7%o)4`69 zCcodYD?;b6jrR6ADkDXiH|{tmYOeP*%?`XS{5WJy(`{4yV^s>D9{eVZ&JA5EG+2E@ zX^Z>7o0YbsdK`(jND;zuf^^ZycXo z9CL45R(F3}nH_-3sycks%-P@jj8s4INv$i>D`;}GwQN4TR3yigzT-?S{UbTl=zIx zJw2sc{GRAOdB1rD>rUFdb+eB!}Bw_cfb(92ji?^s(an9}P zto~`27avy2SexpDT=cz0|^wF1&@HzlDcI-{rF9t{Kja zPGchLcjk_8GTAA5Y&g9&*$JMo2uTQzkL#q^m!07IYuu9+w>AEJ2!i$0J`yopDY56k zxy*Vs*bIss0!;iZrds$svt3GJd`I@jnnYxQ! zZq+~jN1k??$<>A9j9*Y;by*1o%bDH)W*u-QqRH`)02<*TyERS z>Ox1osp3JA*ZS)%SpRgy+l5PJdUYQ>bbqJy;;xH)*InzX@2zXqrTeNR!I~rCU7rDJQYI`ItFyCjh<{8gA%W#+Rfz*wfpY)bq4xV&j^VUv5)}Q;j7g<%Q$2xv~ zy)}2*X4h9YZKotGcWfFUFzmgrPiBd>y6}r(ZBkBMbmfE^_v^0eE%jqB9=!Qu!{+co zJ--cgy?-%zVfMJtfC>7Y*W|9<6*4E}yOPhbWPy>GtHe43-*VbrmA zNlnvsr>j>4Xg2yLN!5}EUzX}M zd`ZH$RBaR%!2KD;1((1Q?*6?`jJOx{<+9F;(w>6li^nWKKKWX57B@R=gj8QX7my_E8H^arlH5WlcEtDl=BlitC+e&jk60bo?Me8N%$}{jYV)ite;c{FA3x;1b&I)+lb*GP- zO|hN%mMRKS;^5O=5fNHy;FodTI>T*#D+??5NdEKw(N=c3=AMgEZ0)Q!VP{IGxjQU3 zX=0JQOQgcS^OR`vR4w6(GkKLo@^XvhB?9<8{Nm_Y%FfH&UZRIoq*!)XY3NzgyO%mg^?3@QUtWxzA+C`kA}i)`pI`a@Z(MxZ*%~%xqJY zC8Juumd+U3Y}wLJv8T)f+Ptud{uCzp`f+_b*DaZkceida=1RP()`pc<CON%tS$-3DxX=J%VYtUHW#R=m#I{Kv)>-iDI?qvHy@b|3dGW(e8R@W*ZW z9o&D(lr8c2{`;0_e2?4`i|;Yar?U5n_)f>S(J~Y($0q1Fw~!1GDK~Ez=T-|pOQXVr zu|}$p3C4fXm)H{k&m7RFE$~IB;h7-%#Bco{rbh#z8;bSh&pM&d0M6TJLg9@<5DxK_ zk9#A5FfxKZ2j#%VtLe+fy+I1TqK*IgxHp!q;SKWBs{P%oe%xCnT^U+8I@Ib>Nkbnuu zdO1LO_2c7yqvd$QAFW%X=OV#qb$%KjN81qd>B4)Xm&5<(Z!o;sb&kUza_5{&KGWDNc+IMr+wOh9SE5PZ|op=D<|Ma!~MlW0m->TL`N zbD>Yjay(2uAv(HF2gW>s`kFdmTAfY-mYiKPNnMsb(=-X@snZF9wCuyO)QZ3%4I?vT zP(!hdj`)ye#hXe8!n#?=awglzvfs$E^GoQ$Qd^Ov?qo0Qa4&yCivp8n_m?_n_twnT(nwng zGt*dFK2DTtr2E0rUu1DlvLWH!lO8aOJbzztwzsB7cDXETs;TkgZ;<{32J!!8a3c-E zZ~y&ea4-$hK@haqY~fpY;3&a#C+YAejkJ3(qa7XL;6z#c z?{+wPDcp_@UeZYSg#xn&Yo>k`#|C&n&0wpg9{%K*Epf^A61b(7i=DH}v&+k$h#l#` z!`n(4yQ&@O;KSQO_|;xQoK?zTPiXC&uYePPU`q6%H!X*zG6?d-dc1WSrqBc===}oqe*VID3qk z)-7Q-s~7Bdt^Z}cSe;Vf=$c-Zpaehr4r3toQ@_(ek)L2tB;6BlKa=!$9Ip&NDok!e zJ)gN{b$aIXSKHl&HdD>BHrc}N@S_5$$LgT+`uy@|n%TlOiUdh&8J8v01E-@*WH792 zxJ8|g7gRt7Lq}w??08d8=t6m@R zExT5h{aGWO_QY&QRgEo<;vO{`DXE~cY>6!SGd+=*IwAY9EXSFvliAUMpoybhtjc8B z7wdG+!wE+A0I^Hw`!HNIzAZ&F81ou0blkx`GtVBND$D*M%dUO9=9!*C!oXA%_I5QC zB+EWpr?U*oa!I?NphDZhGfS!%14=g;G){ym9N*$$<7H2Ut(jV3>^QWcHS0%H(f6>h zuw4AhmGw~j45uCmTC(zT&9INq~5SuRj-uBMI??znRAH2L~ZKHY#l0L$}=?pPd$s zRDW9CX%hS|0RNV;Q9+R*)?NN#a1=a-O9Ug~$F`s>e>hj9WuAp`2ruA|PSf|sv1Q<; zEJoiO$3}yvvlzaM;J4Aec@~_WGSQ2d5WNsF(eqUzdR;_Bud_}W*HlcJhA5M!z7o>3 z3ru@!Mss2w5pNUFv*!O9FWLzIM_-*+-s-TR9sE|-FynJ!7vy8-{x3C!7_P<6z+b!x zj2VP?BiOeCVcHblN5G#0Ow)aS4*Z$I^kR5F3V&jlrhVuu{5>~@%P<{6?!cc6(py6x z8vuXzA^q3u{~s+N%ND-AU03I}r@lkT(#zt7ZZN#DlShv55`<(W#&F7<^bw}AiJMiK zTL}y;g%=@iEz8_AoZ$jQxS8hrcowEM!PJv=2XPR0iDyF_RKVwg)R>Uvjna7ZGXm27 z>h)j!`81aISEC5GDMkzaJc_VLkzPNF@BpO9$58&9Q%CsMBPRDL^4FIzh5zRVQHU1jc_-kC_uW^lyMis~hCjT1O_-kC_uW^mP#x*wlU;%w_6Gj=*r;mS) zYy35?@z=PVuP^lMz>uW^mP#x?#MejV~ZYh2^D!t=@@QQ3DRcXjMGAR+5;$%1(< ze=)9cMUxxCkb~pXJYy%_S=pUm<7V9FhHN))z!CAfZ|Cs2s`j2f#N@r}2y_~A1KHYxexay{x9Ey@h7vDa2py!kDr03ot z)gMyNyZWmRUeo*KhpXmYy?PH={l&iMW7pm7HXfQA9x>ji`rCK0XFJsaO`oR3>@Icx z!Rynl*z(BKJ-3coc6V|y{l77;(dHk=HSUf2Vt(hDbN)WRnDD9#Dl4kIrda=P8rN7c z=gZ!X+TlxIe|@uan%U{WySw^n9*UR|Z)9{z>34&g(!s$> z6H0s%oE9|o3Tv{j^~js+whfMH(~V1(j!U~Bqq=3qBf$d>!S}t-xv6%oT^*jPxh#r&W(4*gVu-N%t?iEXyKM8F!ryoJkkfMA!#ihJW`?M3Sg(|EKXhT_l9re5 zKiD?DNsgZ04ZFVd8D0A7?bEk+{m49cq?nBF+|i9mypd_Den^`;PxWQIbTpAYE*Panf0k)&lGKR0K1cLrZ<3Ems-J%iI1Jj1Lx zux^*Vqy@+qGb_TLFWw3&;Rx{(5;77-Z$!a+2S|he;E~Y(k1-pjTf?6c{8_-?S@@$v z^VHyv{`|2g%$pB?FdGhW3x{`j=pKF%TmH;4Jc>t`O=iNMI{fv3KgbR*W%e#2D>WBs zipXZ@J#=fzKCcV()8Yj zG-WVyvSGBLi2QCu)Aw7`=}A2)d3Kbpjx;^6?|{A{6725fE+QK}W_u`;+rDRgMP!T* zjXsD!9WNrr30#7RTt=2oq|>L65tHe3)fAdjG>w+f0_lPrikyU;kKBqpj(mV5iF7W# zL|RUVL|RU8qEn)Xq#{c&{WS6l=B&j$s!23wGo&T5ZxSsb4yOx5G@66cYmkqUXqmiZ znx|PZjrImAliA79$;zYvxd{0O@+eS5&SK8nNR1RamvIVhtDZPLD#bfRL^5%D7IG=D z2N?!iTST^F%2DJwDIiAe1LRG zqa}<%s;ARwbL5Kj^mN$D$V-^=DxH=r$?$SlCJq@LV7zU32F)Lh%&~n{+DK;JXtSC5V?DPs8%$O}jjMTA!9C%B`k?laSs+P*_T>0ic@l_4LbGeae`7oW zK9L5I6tWz0YC(*^|0F;^8#IX&ViK@fxPeR{W&%G8pO0mB#hjs}5OemzGDFD{k_2VC zqD7L8SWjR0pfH1_Z6>L7`+-SF3>e*hWBG}EA^hS_G1_vpiD0yzJHR5y6fjzsO0dNw zgY3n6UIK#wkfaP_H^3xdM{#-2Az}ixyoPnD!&pAKg*lZ4iTpMY8w72xNsQ6vkRfPY zz!sCaWH_1wS^)_}b3$0kRI4 zULiOmhydHf(H5))dk3HEFr*fi{t@gGxjd=fAT`9O}+E=QVxnUPQA z494^@_KjH!G&SxPrwXOCf|COw(dHh|0ZybJm>JOnOM{qvOB;jH<%Pg}?}#z?kfVJp z5_4K}^#6X-B#FZ5LJ4sOTMp}91O`v^bMRkvLu@O?+&BUK{V1{r=L_d_(2k-_V|E#> zfZ0Q|Enu{icS24iA(deC7}6R}bA+(2dRR&)Ff(!zjOOevqB*O<=$`B*qAg#|8Sv;9 z^MsrxG3YJR+7Ji13}z;8^LkwLss zX!>Zwn3u zOahjUv1p8);pL*Gf|-#syt%v;(EoD52)P1AOX&oAmKoR*9{prq1Ti6b2u5?R!<>(K z%h9$l+d}_CBvK-oE;J*LdGvEhlk5kR5H-HsDw*xX*eNtUemU9&G%NmD>;X67qDn%# z@XurH0mkh3x6z)V_2WNAdn2L!b~OJZ+BdWqzL-zzqN2=+%t$O>6-|nkMGb0Zq)gl8 z2pA8{48{yYEAgA6bpng$RrB@H?9tBijnKT%YWQYoVQAO+?a-#6-R0ZB@-#^%EDwJ2 zn{Usj{b)YKOvrn_FWLrXgZQ++?*WrQY$#ee+E;!c+9|Z}{2;Vyv^xGsT%$|Mx0KBY zCkRI4t5EC8tQndU82tol1tui!%q-A6nYp0(F!Mw6WfqR+$1E0YAhRiG0nF0ThBBLl zHk{c!v@y&!qD3;>juyr22wDuYb7*m3u+xzzU=lJBjJD@180c?877E6oslo*}wQ*=> z%wplE0pb69Vm2OaG_%QQvzSdo+s!Nm?Gm#Lw2#bYpy|TJGF{#*G%IHLXakrPqQx;= zh*rRCDcVkEE77hoTZ_hrOKZA>4Y;?qQop4f0k#><5==tY2}*I>+NsmNxn6JpW8PpA zvPp0Vtu=l=Y!#e_CetKCF;*scjQh(N^>}qNQZ9Ibwg~Mwvo&ZpnC(Qn3r6>)17L70 zV0IMk5zPrpxDF;EFTrTb-@|qIDtIqnBlxit6Y^E?8O@m4cQgxT@Np0GIWiNX4Pqui zo6JlNZ7wq{w2jPk(DpIYLp#OH5bZ8A6SOzX=>Pq#Nkp3Q>Jd=SHUhezX~HR!2~ihz z!B`83Nx*Cb^wYPaCT(j?VNbyd*gx$w+XFR)cJKkDN&17)m_6nkggLc@eJ~bPUA zj3r{MsnAQ%@EO-s=ncz*|8p*b9vO6n{sL{N3vW98%yDOS^d}Q!HjYL9WJi87*Q~}l zSNvp;e=_|UjdQyHWcfc?I9xb1)U0A=V|(|LndLN&`Tu0of3l>!#yJb}>x~aBe-%uE zEkW%Cnul;8Y%w@@n?qy$g@e($&80R#I07vk%~Kcz+re9_HGD`6(rOPZ(i#fftQ7>@ zjx0g0X_5~q#Z7|XZikgNr46N~i*_+g_r(-nWDqhMqmyVwtf z$OXs^$i2wZ$cxAu$Y)5RL+8>%c1HRlW0CWa`;j-1M3?4o1=J#4b)N$J>%N3rmSc6_ z0Mm3o0N3b#0q)WL0lc8g<8;Y0T@g^dnKH0LGj*U_vnIgsX1c&x&Gb1fvY}Zqlx84x zg6Xm(C(eW<=&j(Hk|e!i*mmhaQ2IRb?{|@+B{~M4l-2|7&kvYw6_#Bzv+zz-Fxf}Vr zz7M1X0Y#z-2DD`o4JPpYlhTk`K(C}(26P__mF(ujy&r>8V6_3AzDB9}&|p7IyEdgM z@;t9F{WGL{C2@wdoplT=VJSw2#jr-53{UV)NLRzNK)OG3q$lLzNMFPAkY}{vbzqv| zW8iGV8lEMjn+dc?Vp=qCJMs~bBV&vxQ(EkXoJ(8K7T(d~I;5O!K}&muDJsSfAjQJ? zG4QSx&1ur57{;OXHS8vo^B+t8=jrv)Zt1i>v{v#Ij>x*y@U+NqNUv|nAzf^sB}0+p zfE*d$#X(4;4nqH=DP0Bu(~vpHdBFPUvMwVbx(ZnYgsZ-;qkwe(;mDn?Q9_P9?n>A0 z1@Z?{Vog(;B3mH4Aoq1W33D9>ni7X@TZN{?wVMO5AJPXov|AADpJRcr=X84@Fd?&% ztB?n~mB7;P0y&~=vlnP+a{y>=L(?5>%7J}sDuDxR=pGwjb3*8qJK7`Y@xg>-+UXse^MrL39=M<5!p(ptK^lW6IhhZ$-T+&4931^rl-s0O*yp z*@>=oCGwgR%~R_{qdaFyC1;vn)A=jZS;v|FNQj{`-A_6=3t`$C>4cOay>PB@WRkNK z^5h~{BllqXNoRUYyX0&D|4;fy=l#$EYA*X>DZjbU{8lb>xqXl$F+Bm(vydxXj3Lhs zoIdShDyA*A8;(nMt~O%-Bxj^M(i=G(8H$WR#vvyolaZOo*~miVCS)nH3V8u}7g>w^ z0c=?M2wXa)yDOz?A4+{>Yos;O8R?A-M2ilVajsBgA#xM46j_D5fV_*WMgBleg%aRw zp%2~PMjK8Pi%1VRQqgZdb6~o<4?WYn(#KA5=CDUO2kKUiJcYc~Cr7MBzV|uH*CHia zdxg40EL$$NB{s7s7xYaqoOQ#v6XfV2hZF>|2oHIz9V8HS8NPDCanX8`N- z7s~V`{z*%b8JIH6q_sFkEzMGzemP3zznr@w;96g}?{D&ifZZ$lPj75$I zipU~2_%tUgk$meX z^p!p|zs!T?_rz#VLwbA*@CYJaN#lSVndw2#uSz{$VEfRT-}X>du5a_V9=eKYNk3y{ z|D+bc`e^rlW{}bkITD$GoB^!Qv#4KN$Wx3wg1ic>kG}5L38JF@lm^JINH63VWCn5t z@*wgC@*7e*fX>?s82}WK2?OX_rz7Vg*CBT!tC2S;*?#zBKrbla1F$|t;3^d`2fRbB2u$wkO^ zUUb?DSRd`?^#W2>b)hUmUh#OLi1ze)1L+RPe#pVdP+)xtHi8sKq#rUE z8H1dToQqtE+>JbiypOB}iU{XJsq91da9yM!(iA!0i>9~tf#tF*lrBDWyW9KpfD|{H zN7J<_y?*h-{6URUYy9Uy1p=`olSuXi+KeO&%Y^miY3h-UgU6N$(Yq-#?;y#=TD zBQGIqDK&|&R4md|^QHCC_oX?_kk*u%eQ3JoAdH4!dIB;9IUBhcxrI{m5aw)L^IEZ& zihMsbAyvM#M?dpDqypEQzI2~7f~zn%d-0=ma;E2CGNd(9+Yisok@QT`gc$qLYs^df zx1j`jz3G*-*iWcRuR7r~f*nQQNSsxn=lWBsBby*~k;X`oCXK3L&Q_38Kkpu8KSVxTdZ|A`@ed^#yHIC#CrdLe=d|e6#r>U8c)yUmIucSkRY5mV5pCZW+nxa31 zp4GR*X-{MXG80*ZEI}Rxnvi=#7DLJMdo0g~&@=F_K(C~}LuolfhyJ3pp3L8o2{`40#245BULEhm?%Kr6Y}zZINA(y^u1bFLER@2APb^ zM=nQhL6#s7A= z6S69h?muh$l)`jjKpAjdUMQoa;< z$tN-8GUjm#anykODj}}G4$kgCYorrWhV%>Zh4dg~3~~}OJtP3qb39#HO9~q43O5q`gU_0qt?Vf zsXuZEG8j1)8Hb#R{2iHsoPnH+T!LH?Tn-eG<8&@^aST1H zypMc~ti$LSJlia20#VJ@qGnA@h-fUm?@dXc`K9R_{NXMZ$@_u zYceUqL1<0#kt>i}k!8plmMD5IMDO;SkUfx-V|R(^Hzp?J2u9CG(ev9&`Z6f_ zW|Rla`y|RA_yJi5guATK!+`Q{4cbM8K~y7}mQTNPfume>6_4)o^ol+_dYz7c(j;US zashH9axd~Y@+z>tr#y|`1as9Qc`K)9;t-??sGE zD268|TTC}Mf#)Zu8@1HtOfo%yS(!_KOORWUcQaQ)$_J!!7M(WA3W9k%BZITnLCOT= zOyq&AO^|XPr-d_i!?g8`eZbK(%7MQlx6G)7=?a{_GJ}@zat1AvpM5~6%}J3RvT3b) z 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

|%Z!$)+WZ#+U4hQA=G5aVog)6l+MZz!l}-_# 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 dt3C+1bKEWSkK+{UrLJ - { - try - { - ProcessStarter.PROCESS_INFORMATION procInfo; - ProcessStarter.StartProcessAndBypassUAC(path, out procInfo); - } - catch (Exception) - { - //continue - } - }); - processStarted = task.Wait(TimeSpan.FromSeconds(5)); - } - else - { - var processStartInfo = new ProcessStartInfo(path); - var process = new Process {StartInfo = processStartInfo}; - processStarted = process.Start(); - processId = process.Id; - } + var processStartInfo = new ProcessStartInfo(path); + var process = new Process { StartInfo = processStartInfo }; + processStarted = process.Start(); + processId = process.Id; } catch (Exception ex) { diff --git a/RemoteTaskServer/Api/Network/PacketHandlers/ScreenSharePacketHandler.cs b/RemoteTaskServer/Api/Network/PacketHandlers/ScreenSharePacketHandler.cs index 0cacf9e..69921a7 100644 --- a/RemoteTaskServer/Api/Network/PacketHandlers/ScreenSharePacketHandler.cs +++ b/RemoteTaskServer/Api/Network/PacketHandlers/ScreenSharePacketHandler.cs @@ -10,10 +10,10 @@ using System.Windows.Forms; using WindowsInput; using WindowsInput.Native; -using AgentInterface.Api.ScreenShare; -using AgentInterface.Api.ScreenShare.DesktopDuplication; -using AgentInterface.Api.Win32; using UlteriusServer.Api.Network.Messages; +using UlteriusServer.Api.Win32; +using UlteriusServer.Api.Win32.ScreenShare; +using UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication; using UlteriusServer.WebSocketAPI.Authentication; using vtortola.WebSockets; using static UlteriusServer.Api.UlteriusApiServer; @@ -125,9 +125,7 @@ public void StartScreenShare() return; } _authClient.ShutDownScreenShare = false; - var stream = RunningAsService - ? new Thread(GetScreenAgentFrame) { IsBackground = true } - : new Thread(GetScreenFrame) { IsBackground = true }; + var stream = new Thread(GetScreenFrame) { IsBackground = true }; ScreenShareService.Streams[_authClient] = stream; var data = new { @@ -148,41 +146,6 @@ public void StartScreenShare() } } - - private void GetScreenAgentFrame() - { - try - { - while (_client != null && _client.IsConnected && _authClient != null && - !_authClient.ShutDownScreenShare) - { - try - { - var image = AgentClient.GetCleanFrame(); - if (image != null) - { - if (image.UsingGpu) - { - SendGpuFrame(image.FinishedRegions); - } - else - { - SendPolledFrame(image.ScreenImage, image.Bounds); - } - } - } - catch (Exception e) - { - // Console.WriteLine(e.Message + " " + e.StackTrace); - } - } - Console.WriteLine("Screen Share Died"); - } - catch (Exception) - { - } - } - private void SendGpuFrame(FinishedRegions[] gpuFrame) { if (gpuFrame == null) @@ -208,6 +171,10 @@ private void GetScreenFrame() while (_client != null && _client.IsConnected && _authClient != null && !_authClient.ShutDownScreenShare) { + if (RunningAsService && DesktopWatcher.CurrentDesktop != null) + { + Desktop.SetCurrent(DesktopWatcher.CurrentDesktop); + } try { var image = ScreenData.DesktopCapture(); @@ -243,6 +210,10 @@ private void SendPolledFrame(Bitmap screenImage, Rectangle bounds) public override void HandlePacket(Packet packet) { + if (RunningAsService && DesktopWatcher.CurrentDesktop != null) + { + Desktop.SetCurrent(DesktopWatcher.CurrentDesktop); + } _client = packet.Client; _authClient = packet.AuthClient; _packet = packet; @@ -276,14 +247,7 @@ public override void HandlePacket(Packet packet) HandleKeyUp(); break; case PacketManager.EndPoints.FullFrame: - if (RunningAsService) - { - HandleAgentFullFrame(); - } - else - { - HandleFullFrame(); - } + HandleFullFrame(); break; case PacketManager.EndPoints.RightClick: HandleRightClick(); @@ -316,68 +280,12 @@ private void HandleCtrlAltDel() private void RightUp() { - if (!ScreenShareService.Streams.ContainsKey(_authClient)) return; - if (RunningAsService) - { - AgentClient.HandleRightMouseUp(); - } - else - { - new InputSimulator().Mouse.RightButtonUp(); - } + new InputSimulator().Mouse.RightButtonUp(); } private void RightDown() { - if (!ScreenShareService.Streams.ContainsKey(_authClient)) return; - if (RunningAsService) - { - AgentClient.HandleRightMouseDown(); - } - else - { - new InputSimulator().Mouse.RightButtonDown(); - } - } - - private void HandleAgentFullFrame() - { - try - { - var fullFrameData = AgentClient.GetFullFrame(); - if (fullFrameData?.ScreenImage == null) throw new InvalidOperationException("Frame was null"); - var bounds = fullFrameData.Bounds; - var image = ScreenData.ImageToByteArray(fullFrameData.ScreenImage); - var frameData = new - { - screenBounds = new - { - top = bounds.Top, - bottom = bounds.Bottom, - left = bounds.Left, - right = bounds.Right, - height = bounds.Height, - width = bounds.Width, - x = bounds.X, - y = bounds.Y, - empty = bounds.IsEmpty, - location = bounds.Location, - size = bounds.Size - }, - frameData = image.Select(b => (int)b).ToArray() - }; - _builder.WriteMessage(frameData); - } - catch (Exception ex) - { - var data = new - { - frameFailed = true, - message = ex.Message - }; - Console.WriteLine(ex.Message + "Fuck"); - _builder.WriteMessage(data); - } + new InputSimulator().Mouse.RightButtonDown(); } private void HandleFullFrame() @@ -427,7 +335,6 @@ private void HandleFullFrame() private void HandleKeyUp() { - if (!ScreenShareService.Streams.ContainsKey(_authClient)) return; var keyCodes = ((IEnumerable)_packet.Args[0]).Cast() .Select(x => x.ToString()) .ToList(); @@ -436,17 +343,10 @@ private void HandleKeyUp() .Select(hexString => Convert.ToInt32(hexString, 16)) .ToList(); - if (RunningAsService) - { - AgentClient.HandleKeyUp(codes); - } - else + foreach (var code in codes) { - foreach (var code in codes) - { - var virtualKey = (VirtualKeyCode)code; - new InputSimulator().Keyboard.KeyUp(virtualKey); - } + var virtualKey = (VirtualKeyCode)code; + new InputSimulator().Keyboard.KeyUp(virtualKey); } } @@ -458,7 +358,6 @@ private string ToHex(int value) private void HandleKeyDown() { - if (!ScreenShareService.Streams.ContainsKey(_authClient)) return; var keyCodes = ((IEnumerable)_packet.Args[0]).Cast() .Select(x => x.ToString()) .ToList(); @@ -466,35 +365,20 @@ private void HandleKeyDown() keyCodes.Select(code => ToHex(int.Parse(code.ToString()))) .Select(hexString => Convert.ToInt32(hexString, 16)) .ToList(); - if (RunningAsService) + foreach (var code in codes) { - AgentClient.HandleKeyDown(codes); - } - else - { - foreach (var code in codes) - { - var virtualKey = (VirtualKeyCode)code; - new InputSimulator().Keyboard.KeyDown(virtualKey); - } + var virtualKey = (VirtualKeyCode)code; + new InputSimulator().Keyboard.KeyDown(virtualKey); } } private void HandleScroll() { - if (!ScreenShareService.Streams.ContainsKey(_authClient)) return; var delta = Convert.ToInt32(_packet.Args[0], CultureInfo.InvariantCulture); delta = ~delta; var positive = delta > 0; var direction = positive ? 10 : -10; - if (RunningAsService) - { - AgentClient.ScrollMouse(positive); - } - else - { - new InputSimulator().Mouse.VerticalScroll(direction); - } + new InputSimulator().Mouse.VerticalScroll(direction); } private static Point Translate(Point point, Size from, Size to) @@ -510,77 +394,36 @@ private static Point GetRelativeCoordinates(Point absoluteCoordinates) private void HandleMoveMouse() { - if (!ScreenShareService.Streams.ContainsKey(_authClient)) return; try { int y = Convert.ToInt16(_packet.Args[0], CultureInfo.InvariantCulture); int x = Convert.ToInt16(_packet.Args[1], CultureInfo.InvariantCulture); - - if (RunningAsService) - { - - AgentClient.MoveMouse(x, y); - } - else - { - var bounds = Display.GetWindowRectangle(); - x = checked((int)Math.Round(x * (65535 / (double)bounds.Width))); - y = checked((int)Math.Round(y * (65535 / (double)bounds.Height))); - new InputSimulator().Mouse.MoveMouseTo(x, y); - } + var bounds = Display.GetWindowRectangle(); + x = checked((int)Math.Round(x * (65535 / (double)bounds.Width))); + y = checked((int)Math.Round(y * (65535 / (double)bounds.Height))); + new InputSimulator().Mouse.MoveMouseTo(x, y); } catch { - Console.WriteLine("Error moving mouse"); + //Console.WriteLine("Error moving mouse"); } } private void HandleRightClick() { - if (ScreenShareService.Streams.ContainsKey(_authClient)) - { - if (RunningAsService) - { - AgentClient.HandleRightClick(); - } - else - { - new InputSimulator().Mouse.RightButtonClick(); - } - } + new InputSimulator().Mouse.RightButtonClick(); } private void HandleMouseUp() { - if (ScreenShareService.Streams.ContainsKey(_authClient)) - { - if (RunningAsService) - { - AgentClient.HandleLeftMouseUp(); - } - else - { - new InputSimulator().Mouse.LeftButtonUp(); - } - } + new InputSimulator().Mouse.LeftButtonUp(); } private void HandleMouseDown() { - if (ScreenShareService.Streams.ContainsKey(_authClient)) - { - if (RunningAsService) - { - AgentClient.HandleLeftMouseDown(); - } - else - - { - new InputSimulator().Mouse.LeftButtonDown(); - } - } + new InputSimulator().Mouse.LeftButtonDown(); } } } \ No newline at end of file diff --git a/RemoteTaskServer/Api/Network/PacketHandlers/ServerPacketHandler.cs b/RemoteTaskServer/Api/Network/PacketHandlers/ServerPacketHandler.cs index aad95d1..e1fce93 100644 --- a/RemoteTaskServer/Api/Network/PacketHandlers/ServerPacketHandler.cs +++ b/RemoteTaskServer/Api/Network/PacketHandlers/ServerPacketHandler.cs @@ -8,8 +8,8 @@ using System.Reflection; using System.Text; using System.Windows.Forms; -using AgentInterface.Settings; using UlteriusServer.Api.Network.Messages; +using UlteriusServer.Utilities; using UlteriusServer.Utilities.Security; using UlteriusServer.WebSocketAPI.Authentication; using vtortola.WebSockets; diff --git a/RemoteTaskServer/Api/Network/PacketHandlers/SettingsPacketHandler.cs b/RemoteTaskServer/Api/Network/PacketHandlers/SettingsPacketHandler.cs index be097e3..494706a 100644 --- a/RemoteTaskServer/Api/Network/PacketHandlers/SettingsPacketHandler.cs +++ b/RemoteTaskServer/Api/Network/PacketHandlers/SettingsPacketHandler.cs @@ -3,9 +3,9 @@ using System; using System.IO; using System.Text; -using AgentInterface.Settings; using Newtonsoft.Json; using UlteriusServer.Api.Network.Messages; +using UlteriusServer.Utilities; using UlteriusServer.WebSocketAPI.Authentication; using vtortola.WebSockets; diff --git a/RemoteTaskServer/Api/Network/UlteriusAgentClient.cs b/RemoteTaskServer/Api/Network/UlteriusAgentClient.cs deleted file mode 100644 index 9e765a8..0000000 --- a/RemoteTaskServer/Api/Network/UlteriusAgentClient.cs +++ /dev/null @@ -1,313 +0,0 @@ -#region - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Net.Security; -using System.ServiceModel; -using System.Threading; -using System.Threading.Tasks; -using AgentInterface; -using AgentInterface.Api.Models; -using UlteriusServer.Utilities; - -#endregion - -namespace UlteriusServer.Api.Network -{ - public class UlteriusAgentClient - { - private IInputContract InputChannel { get; set; } - - private IFrameContract FrameChannel { get; set; } - - public void Start(bool keepAlive = true) - { - var inputAddress = "net.tcp://localhost/ulterius/agent/input/"; - var frameAddress = "net.pipe://localhost/ulterius/agent/frames/"; - - var inputBinding = new NetTcpBinding - { - Security = new NetTcpSecurity - { - Transport = { ProtectionLevel = ProtectionLevel.None }, - Mode = SecurityMode.None - }, - MaxReceivedMessageSize = int.MaxValue - }; - var ep = new EndpointAddress(inputAddress); - - InputChannel = ChannelFactory.CreateChannel(inputBinding, ep); - - - var frameBinding = new NetNamedPipeBinding - { - Security = new NetNamedPipeSecurity - { - Transport = { ProtectionLevel = ProtectionLevel.None }, - Mode = NetNamedPipeSecurityMode.None - }, - MaxReceivedMessageSize = int.MaxValue - }; - var epf = new EndpointAddress(frameAddress); - FrameChannel = ChannelFactory.CreateChannel(frameBinding, epf); - - - if (!keepAlive) return; - var task = new Task(KeepAlive); - task.Start(); - } - - private void KeepAlive() - { - while (true) - { - Tools.RestartDaemon(); - var alive = ChannelActive(); - if (!alive) - { - var agentList = Process.GetProcessesByName("UlteriusAgent"); - if (agentList.Length == 0) - { - Tools.RestartAgent(); - } - Start(false); - } - Thread.Sleep(1000); - } - } - - public void ScrollMouse(bool positive) - { - try - { - InputChannel?.MouseScroll(positive); - } - catch (EndpointNotFoundException) - { - // - } - catch (CommunicationException) - { - // - } - } - - public bool ChannelActive() - { - try - { - return FrameChannel != null && FrameChannel.KeepAlive(); - } - catch (EndpointNotFoundException) - { - return false; - } - catch (CommunicationException) - { - return false; - } - } - - public FrameInformation GetCleanFrame() - { - try - { - return FrameChannel.GetCleanFrame(); - } - catch (EndpointNotFoundException) - { - return null; - } - catch (TimeoutException) - { - return null; - } - catch (CommunicationException) - { - return null; - } - } - - public FrameInformation GetFullFrame() - { - try - { - return FrameChannel.GetFullFrame(); - } - catch (EndpointNotFoundException) - { - return null; - } - catch (CommunicationException) - { - return null; - } - } - - public void HandleRightMouseDown() - { - try - { - InputChannel.HandleRightMouseDown(); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException) - { - } - } - - public void HandleRightMouseUp() - { - try - { - InputChannel.HandleRightMouseUp(); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException) - { - } - } - - public void MoveMouse(int x, int y) - { - try - { - InputChannel.MoveMouse(x, y); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException ex) - { - Console.WriteLine(ex.Message); - Console.Write(ex.StackTrace); - } - } - - public void HandleLeftMouseDown() - { - try - { - InputChannel.HandleLeftMouseDown(); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException) - { - } - } - - public void HandleLeftMouseUp() - { - try - { - InputChannel.HandleLeftMouseUp(); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException) - { - } - } - - public void HandleKeyDown(List keyCodes) - { - try - { - InputChannel.HandleKeyDown(keyCodes); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException) - { - } - } - - public void HandleKeyUp(List keyCodes) - { - try - { - InputChannel.HandleKeyUp(keyCodes); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException) - { - } - } - - public void HandleRightClick() - { - try - { - InputChannel.HandleRightClick(); - } - catch (EndpointNotFoundException) - { - } - catch (CommunicationException) - { - } - } - - public float GetGpuTemp(string gpuName) - { - try - { - var temp = InputChannel.GetGpuTemp(gpuName); - return temp; - } - catch (EndpointNotFoundException) - { - return -1; - } - catch (CommunicationException) - { - return -1; - } - } - - public List GetDisplayInformation() - { - try - { - return InputChannel.GetDisplayInformation(); - } - catch (EndpointNotFoundException) - { - return null; - } - catch (CommunicationException) - { - return null; - } - } - - public List GetCpuTemps() - { - try - { - return InputChannel.GetCpuTemps(); - } - catch (EndpointNotFoundException) - { - return null; - } - catch (CommunicationException) - { - return null; - } - } - - - } -} \ No newline at end of file diff --git a/RemoteTaskServer/Api/Services/LocalSystem/SystemService.cs b/RemoteTaskServer/Api/Services/LocalSystem/SystemService.cs index 46e1918..019dd61 100644 --- a/RemoteTaskServer/Api/Services/LocalSystem/SystemService.cs +++ b/RemoteTaskServer/Api/Services/LocalSystem/SystemService.cs @@ -13,12 +13,13 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using AgentInterface.Api.Models; -using AgentInterface.Api.System; +using System.Windows.Forms; using UlteriusServer.Api.Network.Models; using UlteriusServer.Api.Services.Network; +using UlteriusServer.Api.Win32; +using UlteriusServer.Api.Win32.ScreenShare.Models; using UlteriusServer.Utilities.Drive; -using static AgentInterface.Api.Win32.Display; +using SystemInformation = UlteriusServer.Api.Network.Models.SystemInformation; #endregion @@ -199,18 +200,31 @@ private async void Updater() private List GetDisplayInformation() { - var displayList = DisplayInformation(); - if (!UlteriusApiServer.RunningAsService) return displayList; - var displayInfo = UlteriusApiServer.AgentClient.GetDisplayInformation(); - return displayInfo ?? displayList; + var displays = new List(); + for (var i = 0; i < Screen.AllScreens.Length; i++) + { + var friendly = Display.GetDeviceFriendlyName(i); + var display = new DisplayInformation {FriendlyName = friendly}; + var currentScreen = Screen.AllScreens[i]; + display.CurrentResolution = new ResolutionInformation + { + BitsPerPixel = currentScreen.BitsPerPixel, + Frequency = 60, + Height = currentScreen.Bounds.Height, + Width = currentScreen.Bounds.Width, + Orientation = "Unknown", + X = currentScreen.Bounds.X, + Y = currentScreen.Bounds.Y + }; + displays.Add(display); + } + return displays; } private List GetCpuTemps() { - return UlteriusApiServer.RunningAsService - ? UlteriusApiServer.AgentClient.GetCpuTemps() - : SystemData.GetCpuTemps(); + return SystemData.GetCpuTemps(); } @@ -231,7 +245,7 @@ public string GetMotherBoard() _motherBoard = "Board Unknown"; } } - return _motherBoard; + return string.IsNullOrEmpty(_motherBoard) ? "Board Unknown" : _motherBoard; } @@ -242,14 +256,14 @@ private static object GetNetworkInfo() foreach (var networkInterface in NetworkInterface.GetAllNetworkInterfaces()) { - totalBytesReceived += networkInterface.GetIPv4Statistics().BytesReceived; - totalBytesSent += networkInterface.GetIPv4Statistics().BytesSent; + totalBytesReceived += networkInterface?.GetIPv4Statistics()?.BytesReceived ?? 0; + totalBytesSent += networkInterface?.GetIPv4Statistics()?.BytesSent ?? 0; } var data = new { - totalNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces().Length, + totalNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces()?.Length ?? 0, networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(), totalBytesReceived, totalBytesSent diff --git a/RemoteTaskServer/Api/Services/Network/NetworkService.cs b/RemoteTaskServer/Api/Services/Network/NetworkService.cs index e095a25..8b4fce9 100644 --- a/RemoteTaskServer/Api/Services/Network/NetworkService.cs +++ b/RemoteTaskServer/Api/Services/Network/NetworkService.cs @@ -7,7 +7,7 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Runtime.InteropServices; -using AgentInterface.Settings; +using UlteriusServer.Utilities; #endregion diff --git a/RemoteTaskServer/Api/Services/Update/UpdateService.cs b/RemoteTaskServer/Api/Services/Update/UpdateService.cs index 76bead6..018f1fb 100644 --- a/RemoteTaskServer/Api/Services/Update/UpdateService.cs +++ b/RemoteTaskServer/Api/Services/Update/UpdateService.cs @@ -1,12 +1,13 @@ #region using System; +using System.Diagnostics; using System.IO; using System.Net.Http; using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; -using AgentInterface.Api.Win32; +using UlteriusServer.Api.Win32; using UlteriusServer.Utilities; #endregion @@ -52,10 +53,8 @@ private async void Updater() private void CheckForServerUpdates() { - var file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), - "Ulterius Updater.exe /silentall -nofreqcheck"); - ProcessStarter.PROCESS_INFORMATION procInfo; - ProcessStarter.StartProcessAndBypassUAC(file, out procInfo); + var file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Ulterius Updater.exe /silentall -nofreqcheck"); + Process.Start(file); } diff --git a/AgentInterface/Api/System/SystemData.cs b/RemoteTaskServer/Api/SystemData.cs similarity index 70% rename from AgentInterface/Api/System/SystemData.cs rename to RemoteTaskServer/Api/SystemData.cs index db38aa8..48fd0c0 100644 --- a/AgentInterface/Api/System/SystemData.cs +++ b/RemoteTaskServer/Api/SystemData.cs @@ -1,99 +1,99 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.ExceptionServices; -using OpenHardwareMonitor.Hardware; - -namespace AgentInterface.Api.System -{ - public class SystemData - { - - [HandleProcessCorruptedStateExceptions] - public static float GetGpuTemp(string gpuName) - { - try - { - var myComputer = new Computer(); - myComputer.Open(); - //possible fix for gpu temps on laptops - myComputer.GPUEnabled = true; - float temp = -1; - foreach (var hardwareItem in myComputer.Hardware) - { - hardwareItem.Update(); - switch (hardwareItem.HardwareType) - { - case HardwareType.GpuNvidia: - foreach ( - var sensor in - hardwareItem.Sensors.Where( - sensor => - sensor.SensorType == SensorType.Temperature && - hardwareItem.Name.Contains(gpuName))) - { - if (sensor.Value != null) - { - temp = (float)sensor.Value; - } - } - break; - case HardwareType.GpuAti: - foreach ( - var sensor in - hardwareItem.Sensors.Where( - sensor => - sensor.SensorType == SensorType.Temperature && - hardwareItem.Name.Contains(gpuName))) - { - if (sensor.Value != null) - { - temp = (float)sensor.Value; - } - } - break; - } - } - myComputer.Close(); - return temp; - } - catch (AccessViolationException) - { - return -1; - } - } - - public static List GetCpuTemps() - { - var myComputer = new Computer(); - myComputer.Open(); - myComputer.CPUEnabled = true; - var tempTemps = new List(); - var procCount = Environment.ProcessorCount; - for (var i = 0; i < procCount; i++) - { - tempTemps.Add(-1); - } - try - { - var temps = (from hardwareItem in myComputer.Hardware - where hardwareItem.HardwareType == HardwareType.CPU - from sensor in hardwareItem.Sensors - where sensor.SensorType == SensorType.Temperature - let value = sensor.Value - where value != null - where value != null - select (float)value).ToList(); - if (temps.Count != 0) return temps; - myComputer.Close(); - return tempTemps; - } - catch (Exception) - { - myComputer.Close(); - return tempTemps; - } - } - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.ExceptionServices; +using OpenHardwareMonitor.Hardware; + +namespace UlteriusServer.Api +{ + public class SystemData + { + + [HandleProcessCorruptedStateExceptions] + public static float GetGpuTemp(string gpuName) + { + try + { + var myComputer = new Computer(); + myComputer.Open(); + //possible fix for gpu temps on laptops + myComputer.GPUEnabled = true; + float temp = -1; + foreach (var hardwareItem in myComputer.Hardware) + { + hardwareItem.Update(); + switch (hardwareItem.HardwareType) + { + case HardwareType.GpuNvidia: + foreach ( + var sensor in + hardwareItem.Sensors.Where( + sensor => + sensor.SensorType == SensorType.Temperature && + hardwareItem.Name.Contains(gpuName))) + { + if (sensor.Value != null) + { + temp = (float)sensor.Value; + } + } + break; + case HardwareType.GpuAti: + foreach ( + var sensor in + hardwareItem.Sensors.Where( + sensor => + sensor.SensorType == SensorType.Temperature && + hardwareItem.Name.Contains(gpuName))) + { + if (sensor.Value != null) + { + temp = (float)sensor.Value; + } + } + break; + } + } + myComputer.Close(); + return temp; + } + catch (AccessViolationException) + { + return -1; + } + } + + public static List GetCpuTemps() + { + var myComputer = new Computer(); + myComputer.Open(); + myComputer.CPUEnabled = true; + var tempTemps = new List(); + var procCount = Environment.ProcessorCount; + for (var i = 0; i < procCount; i++) + { + tempTemps.Add(-1); + } + try + { + var temps = (from hardwareItem in myComputer.Hardware + where hardwareItem.HardwareType == HardwareType.CPU + from sensor in hardwareItem.Sensors + where sensor.SensorType == SensorType.Temperature + let value = sensor.Value + where value != null + where value != null + select (float)value).ToList(); + if (temps.Count != 0) return temps; + myComputer.Close(); + return tempTemps; + } + catch (Exception) + { + myComputer.Close(); + return tempTemps; + } + } + + } +} \ No newline at end of file diff --git a/RemoteTaskServer/Api/UlteriusApiServer.cs b/RemoteTaskServer/Api/UlteriusApiServer.cs index 34c5ceb..07d355a 100644 --- a/RemoteTaskServer/Api/UlteriusApiServer.cs +++ b/RemoteTaskServer/Api/UlteriusApiServer.cs @@ -1,238 +1,256 @@ -#region - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using AgentInterface.Settings; -using Newtonsoft.Json; -using UlteriusServer.Api.Network; -using UlteriusServer.Api.Network.Messages; -using UlteriusServer.Api.Services.LocalSystem; -using UlteriusServer.Api.Services.Network; -using UlteriusServer.Api.Services.Update; -using UlteriusServer.Forms.Utilities; -using UlteriusServer.Utilities.Security; -using UlteriusServer.WebSocketAPI; -using UlteriusServer.WebSocketAPI.Authentication; -using vtortola.WebSockets; -using ScreenShareService = UlteriusServer.Api.Services.LocalSystem.ScreenShareService; - -#endregion - -namespace UlteriusServer.Api -{ - internal class UlteriusApiServer - { - public static ConcurrentDictionary AllClients { get; set; } - public static bool RunningAsService { get; set; } - public static ScreenShareService ScreenShareService { get; set; } - public static FileSearchService FileSearchService { get; set; } - public static CronJobService CronJobService { get; set; } - - public static UlteriusAgentClient AgentClient { get; set; } - - /// - /// Start the API Server - /// - public static void Start() - { - PacketLoader.LoadPackets(); - var config = Config.Load(); - var clientUpdateService = new UpdateService(); - clientUpdateService.Start(); - FileSearchService = new FileSearchService(Path.Combine(AppEnvironment.DataPath, "fileIndex.db")); - FileSearchService.Start(); - CronJobService = new CronJobService(Path.Combine(AppEnvironment.DataPath, "jobs.json"), Path.Combine(AppEnvironment.DataPath, "scripts")); - CronJobService.ConfigureJobs(); - var apiPort = config.TaskServer.TaskServerPort; - AllClients = new ConcurrentDictionary(); - ScreenShareService = new ScreenShareService(); - var address = NetworkService.GetAddress(); - var webCamPort = config.Webcams.WebcamPort; - var screenSharePort = config.ScreenShareService.ScreenSharePort; - var endPoints = new List - { - new IPEndPoint(address, apiPort), - new IPEndPoint(address, webCamPort), - new IPEndPoint(address, screenSharePort) - }; - var server = new WebSocketEventListener(endPoints, new WebSocketListenerOptions - { - PingTimeout = TimeSpan.FromSeconds(2), - NegotiationTimeout = TimeSpan.FromSeconds(2), - WebSocketSendTimeout = TimeSpan.FromSeconds(2), - WebSocketReceiveTimeout = TimeSpan.FromSeconds(2), - ParallelNegotiations = Environment.ProcessorCount*2, - NegotiationQueueCapacity = 256, - TcpBacklog = 1000, - OnHttpNegotiation = (request, response) => - { - if (request.Cookies["ConnectionId"] == null) - response.Cookies.Add(new Cookie("ConnectionId", Guid.NewGuid().ToString())); - } - }); - server.OnConnect += HandleConnect; - server.OnDisconnect += HandleDisconnect; - server.OnPlainTextMessage += HandlePlainTextMessage; - server.OnEncryptedMessage += HandleEncryptedMessage; - server.OnError += HandleError; - server.Start(); - Log("Api Server started at " + address); - if (RunningAsService) - { - AgentClient = new UlteriusAgentClient(); - AgentClient.Start(); - } - } - - - - - /// - /// Handles encrypted binary messages - /// - /// - /// - private static void HandleEncryptedMessage(WebSocket clientSocket, byte[] message) - { - var connectionId = CookieManager.GetConnectionId(clientSocket); - AuthClient authClient; - if (AllClients.TryGetValue(connectionId, out authClient)) - { - var packetManager = new PacketManager(authClient, clientSocket, message); - var packet = packetManager.GetPacket(); - packet?.HandlePacket(); - } - } - - private static void HandleError(WebSocket websocket, Exception error) - { - Console.WriteLine(error.StackTrace + " " + error.Message); - } - - /// - /// Handles plaintext JSON packets. - /// - /// - /// - private static void HandlePlainTextMessage(WebSocket clientSocket, string message) - { - var connectionId = CookieManager.GetConnectionId(clientSocket); - AuthClient authClient; - if (AllClients.TryGetValue(connectionId, out authClient)) - { - var packetManager = new PacketManager(authClient, clientSocket, message); - var packet = packetManager.GetPacket(); - packet?.HandlePacket(); - } - } - - - /// - /// Remove a client when it disconnects - /// - /// - private static void HandleDisconnect(WebSocket clientSocket) - { - var connectionId = CookieManager.GetConnectionId(clientSocket); - AuthClient temp = null; - if (AllClients.TryRemove(connectionId, out temp)) - { - Console.WriteLine("Disconnection from " + clientSocket.RemoteEndpoint); - var userCount = AllClients.Count; - var extra = userCount < 1 ? "s" : string.Empty; - UlteriusTray.ShowMessage($"There are now {userCount} user{extra} connected.", "A user disconnected!"); - } - } - - /// - /// When a client connects, assign them a unique RSA keypair for handshake. - /// - /// - private static async void HandleConnect(WebSocket clientSocket) - { - var connectionId = CookieManager.GetConnectionId(clientSocket); - AuthClient authClient; - AllClients.TryGetValue(connectionId, out authClient); - if (authClient != null) - { - if (RunningAsService) - { - MessageQueueManager agentManager; - if (!authClient.MessageQueueManagers.TryGetValue(22005, out agentManager)) - { - if (authClient.MessageQueueManagers.TryAdd(22005, - new MessageQueueManager())) - { - Console.WriteLine("Service Manager Started"); - } - } - } - MessageQueueManager manager; - //check if a manager for that port exist, if not, create one - if (!authClient.MessageQueueManagers.TryGetValue(clientSocket.LocalEndpoint.Port, out manager)) - { - if (authClient.MessageQueueManagers.TryAdd(clientSocket.LocalEndpoint.Port, - new MessageQueueManager())) - { - Console.WriteLine("Manager started for " + clientSocket.LocalEndpoint.Port); - } - } - return; - } - Console.WriteLine("Connection from " + clientSocket.RemoteEndpoint); - var rsa = new Rsa(); - rsa.GenerateKeyPairs(); - var client = new AuthClient - { - PublicKey = rsa.PublicKey, - PrivateKey = rsa.PrivateKey, - MessageQueueManagers = new ConcurrentDictionary() - }; - client.MessageQueueManagers.TryAdd(clientSocket.LocalEndpoint.Port, new MessageQueueManager()); - AllClients.AddOrUpdate(connectionId, client, (key, value) => value); - await SendWelcomeMessage(client, clientSocket); - } - - /// - /// Sends a new user their unique RSA keypair - /// - /// - /// - private static async Task SendWelcomeMessage(AuthClient client, WebSocket clientSocket) - { - var welcomeMessage = JsonConvert.SerializeObject(new - { - endpoint = "connectedToUlterius", - results = new - { - message = "Ulterius server online!", - publicKey = Rsa.SecureStringToString(client.PublicKey) - } - }); - if (clientSocket != null) - { - try - { - await clientSocket.WriteStringAsync(welcomeMessage, CancellationToken.None); - var userCount = AllClients.Count; - var extra = userCount > 1 ? "s" : string.Empty; - UlteriusTray.ShowMessage($"There are now {userCount} user{extra} connected.", "A new user connected!"); - } - catch (Exception ex) - { - Console.WriteLine("Failed to write welcome message " + ex.Message ); - } - } - } - - private static void Log(string message) - { - Console.WriteLine(message); - } - } +#region + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using UlteriusServer.Api.Network; +using UlteriusServer.Api.Network.Messages; +using UlteriusServer.Api.Services.LocalSystem; +using UlteriusServer.Api.Services.Network; +using UlteriusServer.Api.Services.Update; +using UlteriusServer.Forms.Utilities; +using UlteriusServer.Utilities; +using UlteriusServer.Utilities.Security; +using UlteriusServer.WebSocketAPI; +using UlteriusServer.WebSocketAPI.Authentication; +using vtortola.WebSockets; +using vtortola.WebSockets.Http; +using ScreenShareService = UlteriusServer.Api.Services.LocalSystem.ScreenShareService; + +#endregion + +namespace UlteriusServer.Api +{ + internal class UlteriusApiServer + { + public static ConcurrentDictionary AllClients { get; set; } + public static bool RunningAsService { get; set; } + public static ScreenShareService ScreenShareService { get; set; } + public static FileSearchService FileSearchService { get; set; } + public static CronJobService CronJobService { get; set; } + private static int _bufferSize = 1024 * 8; + private static int _bufferPoolSize; + + /// + /// Start the API Server + /// + public static void Start() + { + _bufferPoolSize = 100 * _bufferSize; + PacketLoader.LoadPackets(); + var config = Config.Load(); + var clientUpdateService = new UpdateService(); + clientUpdateService.Start(); + FileSearchService = new FileSearchService(Path.Combine(AppEnvironment.DataPath, "fileIndex.db")); + FileSearchService.Start(); + CronJobService = new CronJobService(Path.Combine(AppEnvironment.DataPath, "jobs.json"), Path.Combine(AppEnvironment.DataPath, "scripts")); + CronJobService.ConfigureJobs(); + var apiPort = config.TaskServer.TaskServerPort; + AllClients = new ConcurrentDictionary(); + ScreenShareService = new ScreenShareService(); + var address = NetworkService.GetAddress(); + var webCamPort = config.Webcams.WebcamPort; + var screenSharePort = config.ScreenShareService.ScreenSharePort; + var listenEndPoints = new Uri[] { + new Uri($"ws://{address}:{apiPort}"), + new Uri($"ws://{address}:{webCamPort}"), + new Uri($"ws://{address}:{screenSharePort}") + }; + var options = new WebSocketListenerOptions + { + PingMode = PingMode.LatencyControl, + NegotiationTimeout = TimeSpan.FromSeconds(30), + PingTimeout = TimeSpan.FromSeconds(5), + ParallelNegotiations = 16, + NegotiationQueueCapacity = 256, + BufferManager = BufferManager.CreateBufferManager(_bufferPoolSize, _bufferSize), + Logger = NullLogger.Instance, + HttpAuthenticationHandler = async (request, response) => + { + await Task.Delay(TimeSpan.FromMilliseconds(1)); + if (request.Cookies["ConnectionId"] == null) + response.Cookies.Add(new Cookie("ConnectionId", Guid.NewGuid().ToString())); + return true; + } + }; + options.Transports.ConfigureTcp(tcp => + { + tcp.BacklogSize = 1000; // max pending connections waiting to be accepted + tcp.ReceiveBufferSize = _bufferSize; + tcp.SendBufferSize = _bufferSize; + tcp.LingerState = new LingerOption(true, 0); + tcp.NoDelay = true; + tcp.IsAsync = true; + + tcp.ReceiveTimeout = TimeSpan.FromSeconds(1); + tcp.SendTimeout = TimeSpan.FromSeconds(3); + }); + + var server = new WebSocketEventListener(listenEndPoints, options); + server.OnConnect += HandleConnect; + server.OnDisconnect += HandleDisconnect; + server.OnPlainTextMessage += HandlePlainTextMessage; + server.OnEncryptedMessage += HandleEncryptedMessage; + server.OnError += HandleError; + server.Start(); + Log("Api Server started at " + address); + } + + + + + /// + /// Handles encrypted binary messages + /// + /// + /// + private static void HandleEncryptedMessage(WebSocket clientSocket, byte[] message) + { + var connectionId = CookieManager.GetConnectionId(clientSocket); + AuthClient authClient; + if (AllClients.TryGetValue(connectionId, out authClient)) + { + var packetManager = new PacketManager(authClient, clientSocket, message); + var packet = packetManager.GetPacket(); + packet?.HandlePacket(); + } + } + + private static void HandleError(WebSocket websocket, Exception error) + { + Console.WriteLine(error.StackTrace + " " + error.Message); + } + + /// + /// Handles plaintext JSON packets. + /// + /// + /// + private static void HandlePlainTextMessage(WebSocket clientSocket, string message) + { + var connectionId = CookieManager.GetConnectionId(clientSocket); + AuthClient authClient; + if (AllClients.TryGetValue(connectionId, out authClient)) + { + var packetManager = new PacketManager(authClient, clientSocket, message); + var packet = packetManager.GetPacket(); + packet?.HandlePacket(); + } + } + + + /// + /// Remove a client when it disconnects + /// + /// + private static void HandleDisconnect(WebSocket clientSocket) + { + var connectionId = CookieManager.GetConnectionId(clientSocket); + AuthClient temp = null; + if (AllClients.TryRemove(connectionId, out temp)) + { + Console.WriteLine("Disconnection from " + clientSocket.RemoteEndpoint); + var userCount = AllClients.Count; + var extra = userCount < 1 ? "s" : string.Empty; + UlteriusTray.ShowMessage($"There are now {userCount} user{extra} connected.", "A user disconnected!"); + } + } + + /// + /// When a client connects, assign them a unique RSA keypair for handshake. + /// + /// + private static async void HandleConnect(WebSocket clientSocket) + { + var connectionId = CookieManager.GetConnectionId(clientSocket); + AuthClient authClient; + AllClients.TryGetValue(connectionId, out authClient); + var host = new Uri($"ws://{clientSocket.HttpRequest.Headers[RequestHeader.Host]}", UriKind.Absolute); + + if (authClient != null) + { + + if (RunningAsService) + { + MessageQueueManager agentManager; + if (!authClient.MessageQueueManagers.TryGetValue(22005, out agentManager)) + { + if (authClient.MessageQueueManagers.TryAdd(22005, + new MessageQueueManager())) + { + Console.WriteLine("Service Manager Started"); + } + } + } + MessageQueueManager manager; + //check if a manager for that port exist, if not, create one + + if (!authClient.MessageQueueManagers.TryGetValue(host.Port, out manager)) + { + if (authClient.MessageQueueManagers.TryAdd(host.Port, + new MessageQueueManager())) + { + Console.WriteLine($"Manager started for {host.Port}"); + } + } + return; + } + Console.WriteLine("Connection from " + clientSocket.RemoteEndpoint); + var rsa = new Rsa(); + rsa.GenerateKeyPairs(); + var client = new AuthClient + { + PublicKey = rsa.PublicKey, + PrivateKey = rsa.PrivateKey, + MessageQueueManagers = new ConcurrentDictionary() + }; + + client.MessageQueueManagers.TryAdd(host.Port, new MessageQueueManager()); + AllClients.AddOrUpdate(connectionId, client, (key, value) => value); + await SendWelcomeMessage(client, clientSocket); + } + + /// + /// Sends a new user their unique RSA keypair + /// + /// + /// + private static async Task SendWelcomeMessage(AuthClient client, WebSocket clientSocket) + { + var welcomeMessage = JsonConvert.SerializeObject(new + { + endpoint = "connectedToUlterius", + results = new + { + message = "Ulterius server online!", + publicKey = Rsa.SecureStringToString(client.PublicKey) + } + }); + if (clientSocket != null) + { + try + { + await clientSocket.WriteStringAsync(welcomeMessage, CancellationToken.None); + var userCount = AllClients.Count; + var extra = userCount > 1 ? "s" : string.Empty; + UlteriusTray.ShowMessage($"There are now {userCount} user{extra} connected.", "A new user connected!"); + } + catch (Exception ex) + { + Console.WriteLine("Failed to write welcome message " + ex.Message ); + } + } + } + + private static void Log(string message) + { + Console.WriteLine(message); + } + } } \ No newline at end of file diff --git a/AgentInterface/Api/Win32/Desktop.cs b/RemoteTaskServer/Api/Win32/Desktop.cs similarity index 99% rename from AgentInterface/Api/Win32/Desktop.cs rename to RemoteTaskServer/Api/Win32/Desktop.cs index a1a586f..eb01019 100644 --- a/AgentInterface/Api/Win32/Desktop.cs +++ b/RemoteTaskServer/Api/Win32/Desktop.cs @@ -9,7 +9,7 @@ #endregion -namespace AgentInterface.Api.Win32 +namespace UlteriusServer.Api.Win32 { /// /// Encapsulates the Desktop API. @@ -61,7 +61,7 @@ public override string ToString() [DllImport("kernel32.dll")] private static extern int GetProcessId(IntPtr process); [DllImport("kernel32.dll")] - static extern int GetCurrentThreadId(); + public static extern int GetCurrentThreadId(); // // Imported winAPI functions. // diff --git a/RemoteTaskServer/Api/Win32/DesktopWatcher.cs b/RemoteTaskServer/Api/Win32/DesktopWatcher.cs new file mode 100644 index 0000000..3b856ea --- /dev/null +++ b/RemoteTaskServer/Api/Win32/DesktopWatcher.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace UlteriusServer.Api.Win32 +{ + public class DesktopWatcher + { + private static string _lastDesktop; + private static Desktop _lastDesktopInput; + public static void Start() + { + var backgroundThread = new Thread(WatchDesktop) { IsBackground = true }; + backgroundThread.Start(); + Console.WriteLine($"Desktop Task is running on thread: {Desktop.GetCurrentThreadId()}"); + } + + private static void WatchDesktop() + { + while (true) + { + try + { + using (var inputDesktop = new Desktop()) + { + inputDesktop.OpenInput(); + if (!inputDesktop.DesktopName.Equals(_lastDesktop)) + { + if (inputDesktop.Show() && Desktop.SetCurrent(inputDesktop)) + { + Console.WriteLine($"Desktop switched from {_lastDesktop} to {inputDesktop.DesktopName} on thread {Desktop.GetCurrentThreadId()}"); + _lastDesktop = inputDesktop.DesktopName; + _lastDesktopInput = inputDesktop; + CurrentDesktop = inputDesktop; + } + else + { + var errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; + Console.WriteLine(errorMessage); + _lastDesktopInput?.Close(); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message, ex); + } + Thread.Sleep(1000); + } + } + + public static string CurrentDesktopName { get; set; } + + public static Desktop CurrentDesktop { get; set; } + } +} diff --git a/RemoteTaskServer/Api/Win32/Display.cs b/RemoteTaskServer/Api/Win32/Display.cs new file mode 100644 index 0000000..f3bdc35 --- /dev/null +++ b/RemoteTaskServer/Api/Win32/Display.cs @@ -0,0 +1,477 @@ +#region + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.Linq; +using System.Runtime.InteropServices; + +#endregion + +namespace UlteriusServer.Api.Win32 +{ + public class Monitor + { + public Rectangle Rect { get; set; } + public Rectangle WorkRect { get; set; } + public bool Primary { get; set; } + public string DeviceName { get; set; } + } + + public class Display + { + + + private static IEnumerable GetAllMonitorsFriendlyNames() + { + uint pathCount, modeCount; + var error = GetDisplayConfigBufferSizes(QueryDeviceConfigFlags.QdcOnlyActivePaths, out pathCount, + out modeCount); + if (error != ErrorSuccess) + throw new System.ComponentModel.Win32Exception(error); + + var displayPaths = new DisplayconfigPathInfo[pathCount]; + var displayModes = new DisplayconfigModeInfo[modeCount]; + error = QueryDisplayConfig(QueryDeviceConfigFlags.QdcOnlyActivePaths, + ref pathCount, displayPaths, ref modeCount, displayModes, IntPtr.Zero); + if (error != ErrorSuccess) + throw new System.ComponentModel.Win32Exception(error); + + for (var i = 0; i < modeCount; i++) + if (displayModes[i].infoType == DisplayconfigModeInfoType.DisplayconfigModeInfoTypeTarget) + yield return MonitorFriendlyName(displayModes[i].adapterId, displayModes[i].id); + } + + public static string GetDeviceFriendlyName(int screenIndex) + { + var allFriendlyNames = GetAllMonitorsFriendlyNames().ToList(); + if (screenIndex > allFriendlyNames.Count) + { + return null; + } + return allFriendlyNames.ElementAtOrDefault(screenIndex) ?? "Unknown"; + } + + private static string MonitorFriendlyName(Luid adapterId, uint targetId) + { + var deviceName = new DisplayconfigTargetDeviceName + { + header = + { + size = (uint) Marshal.SizeOf(typeof(DisplayconfigTargetDeviceName)), + adapterId = adapterId, + id = targetId, + type = DisplayconfigDeviceInfoType.DisplayconfigDeviceInfoGetTargetName + } + }; + var error = DisplayConfigGetDeviceInfo(ref deviceName); + if (error != ErrorSuccess) + throw new System.ComponentModel.Win32Exception(error); + return deviceName.monitorFriendlyDeviceName; + } + + public const int ErrorSuccess = 0; + [DllImport("user32.dll")] + public static extern int GetDisplayConfigBufferSizes( + QueryDeviceConfigFlags flags, out uint numPathArrayElements, out uint numModeInfoArrayElements); + + [DllImport("user32.dll")] + public static extern int QueryDisplayConfig( + QueryDeviceConfigFlags flags, + ref uint numPathArrayElements, [Out] DisplayconfigPathInfo[] pathInfoArray, + ref uint numModeInfoArrayElements, [Out] DisplayconfigModeInfo[] modeInfoArray, + IntPtr currentTopologyId + ); + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigTargetDeviceNameFlags + { + public uint value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigDeviceInfoHeader + { + public DisplayconfigDeviceInfoType type; + public uint size; + public Luid adapterId; + public uint id; + } + + public enum DisplayconfigDeviceInfoType : uint + { + DisplayconfigDeviceInfoGetSourceName = 1, + DisplayconfigDeviceInfoGetTargetName = 2, + DisplayconfigDeviceInfoGetTargetPreferredMode = 3, + DisplayconfigDeviceInfoGetAdapterName = 4, + DisplayconfigDeviceInfoSetTargetPersistence = 5, + DisplayconfigDeviceInfoGetTargetBaseType = 6, + DisplayconfigDeviceInfoForceUint32 = 0xFFFFFFFF + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DisplayconfigTargetDeviceName + { + public DisplayconfigDeviceInfoHeader header; + public DisplayconfigTargetDeviceNameFlags flags; + public DisplayconfigVideoOutputTechnology outputTechnology; + public ushort edidManufactureId; + public ushort edidProductCodeId; + public uint connectorInstance; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string monitorFriendlyDeviceName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string monitorDevicePath; + } + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigModeInfo + { + public DisplayconfigModeInfoType infoType; + public uint id; + public Luid adapterId; + public DisplayconfigModeInfoUnion modeInfo; + } + [DllImport("user32.dll")] + public static extern int DisplayConfigGetDeviceInfo(ref DisplayconfigTargetDeviceName deviceName); + + public enum QueryDeviceConfigFlags : uint + { + QdcAllPaths = 0x00000001, + QdcOnlyActivePaths = 0x00000002, + QdcDatabaseCurrent = 0x00000004 + } + public enum DisplayconfigModeInfoType : uint + { + DisplayconfigModeInfoTypeSource = 1, + DisplayconfigModeInfoTypeTarget = 2, + DisplayconfigModeInfoTypeForceUint32 = 0xFFFFFFFF + } + [StructLayout(LayoutKind.Explicit)] + public struct DisplayconfigModeInfoUnion + { + [FieldOffset(0)] public DisplayconfigTargetMode targetMode; + + [FieldOffset(0)] public DisplayconfigSourceMode sourceMode; + } + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigTargetMode + { + public DisplayconfigVideoSignalInfo targetVideoSignalInfo; + } + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigPathInfo + { + public DisplayconfigPathSourceInfo sourceInfo; + public DisplayconfigPathTargetInfo targetInfo; + public uint flags; + } + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigSourceMode + { + public uint width; + public uint height; + public DisplayconfigPixelformat pixelFormat; + public Pointl position; + } + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigPathSourceInfo + { + public Luid adapterId; + public uint id; + public uint modeInfoIdx; + public uint statusFlags; + } + + public static Rectangle GetWindowRectangle() + { + var scBounds = new RECT(); + GetWindowRect(GetDesktopWindow(), ref scBounds); + return scBounds; + } + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); + + + + [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")] + public static extern IntPtr GetDesktopWindow(); + + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left, Top, Right, Bottom; + + public RECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public RECT(Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) + { + } + + public int X + { + get => Left; + set + { + Right -= Left - value; + Left = value; + } + } + + public int Y + { + get => Top; + set + { + Bottom -= Top - value; + Top = value; + } + } + + public int Height + { + get => Bottom - Top; + set => Bottom = value + Top; + } + + public int Width + { + get => Right - Left; + set => Right = value + Left; + } + + + public Size Size + { + get => new Size(Width, Height); + set + { + Width = value.Width; + Height = value.Height; + } + } + + public static implicit operator Rectangle(RECT r) + { + return new Rectangle(r.Left, r.Top, r.Width, r.Height); + } + + public static implicit operator RECT(Rectangle r) + { + return new RECT(r); + } + + public static bool operator ==(RECT r1, RECT r2) + { + return r1.Equals(r2); + } + + public static bool operator !=(RECT r1, RECT r2) + { + return !r1.Equals(r2); + } + + public bool Equals(RECT r) + { + return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; + } + + public override bool Equals(object obj) + { + if (obj is RECT) + return Equals((RECT)obj); + if (obj is Rectangle) + return Equals(new RECT((Rectangle)obj)); + return false; + } + + public override int GetHashCode() + { + return ((Rectangle)this).GetHashCode(); + } + + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, + Right, Bottom); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct Displayconfig2Dregion + { + public uint cx; + public uint cy; + } + [StructLayout(LayoutKind.Sequential)] + public struct Pointl + { + private readonly int x; + private readonly int y; + } + public enum DisplayconfigPixelformat : uint + { + DisplayconfigPixelformat8Bpp = 1, + DisplayconfigPixelformat16Bpp = 2, + DisplayconfigPixelformat24Bpp = 3, + DisplayconfigPixelformat32Bpp = 4, + DisplayconfigPixelformatNongdi = 5, + DisplayconfigPixelformatForceUint32 = 0xffffffff + } + public struct DisplayconfigVideoSignalInfo + { + public ulong pixelRate; + public DisplayconfigRational hSyncFreq; + public DisplayconfigRational vSyncFreq; + public Displayconfig2Dregion activeSize; + public Displayconfig2Dregion totalSize; + public uint videoStandard; + public DisplayconfigScanlineOrdering scanLineOrdering; + } + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigPathTargetInfo + { + public Luid adapterId; + public uint id; + public uint modeInfoIdx; + private readonly DisplayconfigVideoOutputTechnology outputTechnology; + private readonly DisplayconfigRotation rotation; + private readonly DisplayconfigScaling scaling; + private readonly DisplayconfigRational refreshRate; + private readonly DisplayconfigScanlineOrdering scanLineOrdering; + public bool targetAvailable; + public uint statusFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct Luid + { + public uint LowPart; + public int HighPart; + } + + + public enum DisplayconfigScanlineOrdering : uint + { + DisplayconfigScanlineOrderingUnspecified = 0, + DisplayconfigScanlineOrderingProgressive = 1, + DisplayconfigScanlineOrderingInterlaced = 2, + DisplayconfigScanlineOrderingInterlacedUpperfieldfirst = DisplayconfigScanlineOrderingInterlaced, + DisplayconfigScanlineOrderingInterlacedLowerfieldfirst = 3, + DisplayconfigScanlineOrderingForceUint32 = 0xFFFFFFFF + } + + public enum DisplayconfigVideoOutputTechnology : uint + { + DisplayconfigOutputTechnologyOther = 0xFFFFFFFF, + DisplayconfigOutputTechnologyHd15 = 0, + DisplayconfigOutputTechnologySvideo = 1, + DisplayconfigOutputTechnologyCompositeVideo = 2, + DisplayconfigOutputTechnologyComponentVideo = 3, + DisplayconfigOutputTechnologyDvi = 4, + DisplayconfigOutputTechnologyHdmi = 5, + DisplayconfigOutputTechnologyLvds = 6, + DisplayconfigOutputTechnologyDJpn = 8, + DisplayconfigOutputTechnologySdi = 9, + DisplayconfigOutputTechnologyDisplayportExternal = 10, + DisplayconfigOutputTechnologyDisplayportEmbedded = 11, + DisplayconfigOutputTechnologyUdiExternal = 12, + DisplayconfigOutputTechnologyUdiEmbedded = 13, + DisplayconfigOutputTechnologySdtvdongle = 14, + DisplayconfigOutputTechnologyMiracast = 15, + DisplayconfigOutputTechnologyInternal = 0x80000000, + DisplayconfigOutputTechnologyForceUint32 = 0xFFFFFFFF + } + [StructLayout(LayoutKind.Sequential)] + public struct DisplayconfigRational + { + public uint Numerator; + public uint Denominator; + } + + public enum DisplayconfigRotation : uint + { + DisplayconfigRotationIdentity = 1, + DisplayconfigRotationRotate90 = 2, + DisplayconfigRotationRotate180 = 3, + DisplayconfigRotationRotate270 = 4, + DisplayconfigRotationForceUint32 = 0xFFFFFFFF + } + + public enum DisplayconfigScaling : uint + { + DisplayconfigScalingIdentity = 1, + DisplayconfigScalingCentered = 2, + DisplayconfigScalingStretched = 3, + DisplayconfigScalingAspectratiocenteredmax = 4, + DisplayconfigScalingCustom = 5, + DisplayconfigScalingPreferred = 128, + DisplayconfigScalingForceUint32 = 0xFFFFFFFF + } + public const int MONITORINFOF_PRIMARY = 0x1; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct MONITORINFOEX + { + public int cbSize; + public RECT rcMonitor; + public RECT rcWork; + public int dwFlags; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string szDevice; + } + public delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData); + [DllImport("User32.dll", CharSet = CharSet.Unicode)] + public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi); + + [DllImport("User32.dll", CharSet = CharSet.Unicode)] + public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData); + + private static List GetMonitors() + { + List monitors = new List(); + + EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, + (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) => + { + MONITORINFOEX monitorInfo = new MONITORINFOEX(); + monitorInfo.cbSize = Marshal.SizeOf(typeof(MONITORINFOEX)); + + GetMonitorInfo(hMonitor, ref monitorInfo); + + monitors.Add(new Monitor() + { + Primary = (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) != 0, + DeviceName = monitorInfo.szDevice, + Rect = Rectangle.FromLTRB(monitorInfo.rcMonitor.Left, + monitorInfo.rcMonitor.Top, + monitorInfo.rcMonitor.Right, + monitorInfo.rcMonitor.Bottom), + WorkRect = Rectangle.FromLTRB(monitorInfo.rcWork.Left, + monitorInfo.rcWork.Top, + monitorInfo.rcWork.Right, + monitorInfo.rcWork.Bottom) + }); + + return true; + }, + IntPtr.Zero + ); + + return monitors; + } + public static int X { get; set; } + + public static int Y { get; set; } + + public static int Height { get; set; } + + public static int Width { get; set; } + } +} \ No newline at end of file diff --git a/AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopDuplicationException.cs b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopDuplicationException.cs similarity index 73% rename from AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopDuplicationException.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopDuplicationException.cs index ceedd64..9c5e800 100644 --- a/AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopDuplicationException.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopDuplicationException.cs @@ -1,6 +1,6 @@ using System; -namespace AgentInterface.Api.ScreenShare.DesktopDuplication +namespace UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication { public class DesktopDuplicationException : Exception { diff --git a/AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopDuplicator.cs b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopDuplicator.cs similarity index 83% rename from AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopDuplicator.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopDuplicator.cs index d92c739..24d2281 100644 --- a/AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopDuplicator.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopDuplicator.cs @@ -1,354 +1,355 @@ -#region - -using System; -using System.Drawing; -using System.Drawing.Imaging; -using System.Runtime.InteropServices; -using SharpDX; -using SharpDX.Direct3D; -using SharpDX.Direct3D11; -using SharpDX.DXGI; -using SharpDX.Mathematics.Interop; -using Device = SharpDX.Direct3D11.Device; -using MapFlags = SharpDX.Direct3D11.MapFlags; -using Point = System.Drawing.Point; -using Rectangle = SharpDX.Rectangle; -using Resource = SharpDX.DXGI.Resource; -using ResultCode = SharpDX.DXGI.ResultCode; - -#endregion - -namespace AgentInterface.Api.ScreenShare.DesktopDuplication -{ - /// - /// Provides access to frame-by-frame updates of a particular desktop (i.e. one monitor), with image and cursor - /// information. - /// - public class DesktopDuplicator - { - private readonly OutputDuplication _mDeskDupl; - private readonly Device _mDevice; - private readonly Texture2DDescription _mTextureDesc; - private readonly int _mWhichOutputDevice; - private Texture2D _desktopImageTexture; - - private Bitmap _finalImage1, _finalImage2; - private OutputDuplicateFrameInformation _frameInfo; - private bool _isFinalImage1; - private OutputDescription _mOutputDesc; - - - /// - /// Duplicates the output of the specified monitor on the specified graphics adapter. - /// - /// The adapter which contains the desired outputs. - /// - /// The output device to duplicate (i.e. monitor). Begins with zero, which seems to - /// correspond to the primary monitor. - /// - public DesktopDuplicator() - { - - - Adapter1 adapter; - try - { - adapter = GetBestAdapter(out int bestAdapaterIndex); - if (adapter == null) throw new SharpDXException(); - Console.WriteLine($"Using the {adapter.Description.Description} for screen share."); - } - catch (SharpDXException) - { - ScreenData.CanUseGpuAcceleration = false; - throw new DesktopDuplicationException("Could not find the specified graphics card adapter."); - } - DeviceCreationFlags flags = DeviceCreationFlags.BgraSupport; - _mDevice = new Device(adapter, flags, FeatureLevel.Level_11_0); - Output output; - try - { - output = adapter.GetOutput(0); - } - catch (SharpDXException) - { - ScreenData.CanUseGpuAcceleration = false; - throw new DesktopDuplicationException("Could not find the specified output device."); - } - var output1 = output.QueryInterface(); - _mOutputDesc = output.Description; - _mTextureDesc = new Texture2DDescription - { - CpuAccessFlags = CpuAccessFlags.Read, - BindFlags = BindFlags.None, - Format = Format.B8G8R8A8_UNorm, - Width = ((Rectangle) _mOutputDesc.DesktopBounds).Width, - Height = ((Rectangle) _mOutputDesc.DesktopBounds).Height, - OptionFlags = ResourceOptionFlags.None, - MipLevels = 1, - ArraySize = 1, - SampleDescription = {Count = 1, Quality = 0}, - Usage = ResourceUsage.Staging - }; - - try - { - _mDeskDupl = output1.DuplicateOutput(_mDevice); - } - catch (SharpDXException ex) - { - if (ex.ResultCode.Code == ResultCode.NotCurrentlyAvailable.Result.Code) - throw new DesktopDuplicationException( - "There is already the maximum number of applications using the Desktop Duplication API running, please close one of the applications and try again."); - } - } - - private Bitmap FinalImage - { - get { return _isFinalImage1 ? _finalImage1 : _finalImage2; } - set - { - if (_isFinalImage1) - { - _finalImage2 = value; - _finalImage1?.Dispose(); - } - else - { - _finalImage1 = value; - _finalImage2?.Dispose(); - } - _isFinalImage1 = !_isFinalImage1; - } - } - - /// - /// Finds the most capable graphics card. - /// - /// - private static Adapter1 GetBestAdapter(out int bestAdapterIndex) - { - using (var dxgiFactory = new Factory1()) - { - Adapter1 bestAdapter = null; - bestAdapterIndex = -1; - var adapterIndex = -1; - long bestVideoMemory = 0; - long bestSystemMemory = 0; - - foreach (var dxgiAdapter in dxgiFactory.Adapters1) - { - adapterIndex++; - - // not skip the render only WARP device - if (dxgiAdapter.Description.VendorId != 0x1414 || dxgiAdapter.Description.DeviceId != 0x8c) - if (dxgiAdapter.Outputs == null || dxgiAdapter.Outputs.Length == 0) - continue; - - var level = Device.GetSupportedFeatureLevel(dxgiAdapter); - - if (level < FeatureLevel.Level_11_0) - continue; - long videoMemory = (uint) dxgiAdapter.Description.DedicatedVideoMemory; - long systemMemory = (uint) dxgiAdapter.Description.DedicatedSystemMemory; - - if (bestAdapter != null && videoMemory <= bestVideoMemory && - (videoMemory != bestVideoMemory || systemMemory <= bestSystemMemory)) continue; - bestAdapter = dxgiAdapter; - bestAdapterIndex = adapterIndex; - bestVideoMemory = videoMemory; - bestSystemMemory = systemMemory; - } - - return bestAdapter; - } - } - - /// - /// Retrieves the latest desktop image and associated metadata. - /// - public DesktopFrame GetLatestFrame() - { - var frame = new DesktopFrame(); - // Try to get the latest frame; this may timeout - var retrievalTimedOut = RetrieveFrame(); - if (retrievalTimedOut) - return null; - try - { - RetrieveFrameMetadata(frame); - //we don't need cursor info - //RetrieveCursorMetadata(frame); - //we dont need a full frame - //ProcessFrame(frame); - } - catch - { - ReleaseFrame(); - } - try - { - ReleaseFrame(); - } - catch - { - // throw new DesktopDuplicationException("Couldn't release frame."); - } - return frame; - } - - private bool RetrieveFrame() - { - if (_desktopImageTexture == null) - _desktopImageTexture = new Texture2D(_mDevice, _mTextureDesc); - Resource desktopResource = null; - _frameInfo = new OutputDuplicateFrameInformation(); - try - { - _mDeskDupl.AcquireNextFrame(500, out _frameInfo, out desktopResource); - } - catch (SharpDXException ex) - { - if (ex.ResultCode.Code == ResultCode.WaitTimeout.Result.Code) - return true; - if (ex.ResultCode.Failure) - { - //return true; - desktopResource?.Dispose(); - throw new DesktopDuplicationException("Failed to acquire next frame."); - } - } - using (var tempTexture = desktopResource?.QueryInterface()) - { - _mDevice.ImmediateContext.CopyResource(tempTexture, _desktopImageTexture); - } - desktopResource?.Dispose(); - return false; - } - - private void RetrieveFrameMetadata(DesktopFrame frame) - { - if (_frameInfo.TotalMetadataBufferSize > 0) - { - // Get moved regions - var movedRegionsLength = 0; - var movedRectangles = new OutputDuplicateMoveRectangle[_frameInfo.TotalMetadataBufferSize]; - _mDeskDupl.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out movedRegionsLength); - frame.MovedRegions = - new MovedRegion[movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle))]; - for (var i = 0; i < frame.MovedRegions.Length; i++) - { - var destRect = (Rectangle) movedRectangles[i].DestinationRect; - frame.MovedRegions[i] = new MovedRegion - { - Source = new Point(movedRectangles[i].SourcePoint.X, movedRectangles[i].SourcePoint.Y), - Destination = - new global::System.Drawing.Rectangle(destRect.X, destRect.Y, destRect.Width, destRect.Height) - }; - } - - // Get dirty regions - var dirtyRegionsLength = 0; - var dirtyRectangles = new RawRectangle[_frameInfo.TotalMetadataBufferSize]; - _mDeskDupl.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out dirtyRegionsLength); - frame.UpdatedRegions = - new global::System.Drawing.Rectangle[dirtyRegionsLength / Marshal.SizeOf(typeof(Rectangle))]; - frame.FinishedRegions = new FinishedRegions[frame.UpdatedRegions.Length]; - for (var i = 0; i < frame.UpdatedRegions.Length; i++) - { - var dirtyRect = (Rectangle) dirtyRectangles[i]; - var rect = new global::System.Drawing.Rectangle(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, - dirtyRect.Height); - - - frame.FinishedRegions[i] = new FinishedRegions - { - Destination = rect, - Frame = ExtractRect(rect.X, rect.Y, rect.Width, rect.Height) - }; - } - } - else - { - frame.MovedRegions = new MovedRegion[0]; - frame.UpdatedRegions = new global::System.Drawing.Rectangle[0]; - } - } - - - private Bitmap ExtractRect(int originX, int originY, int width, int height) - { - // Get the desktop capture screenTexture - var mapSource = _mDevice.ImmediateContext.MapSubresource(_desktopImageTexture, 0, MapMode.Read, - MapFlags.None); - - // Create Drawing.Bitmap - - var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); //不能是ARGB - var boundsRect = new global::System.Drawing.Rectangle(0, 0, width, height); - - // Copy pixels from screen capture Texture to GDI bitmap - var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); - var sourcePtr = mapSource.DataPointer; - var destPtr = mapDest.Scan0; - - sourcePtr = IntPtr.Add(sourcePtr, originY * mapSource.RowPitch + originX * 4); - for (var y = 0; y < height; y++) - { - // Copy a single line - - Utilities.CopyMemory(destPtr, sourcePtr, width * 4); - - // Advance pointers - if (y != height - 1) - sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); - destPtr = IntPtr.Add(destPtr, mapDest.Stride); - } - - // Release source and dest locks - bitmap.UnlockBits(mapDest); - _mDevice.ImmediateContext.UnmapSubresource(_desktopImageTexture, 0); - return bitmap; - } - - private void ProcessFrame(DesktopFrame frame) - { - // Get the desktop capture texture - var mapSource = _mDevice.ImmediateContext.MapSubresource(_desktopImageTexture, 0, MapMode.Read, - MapFlags.None); - var bounds = (Rectangle) _mOutputDesc.DesktopBounds; - FinalImage = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); - var boundsRect = new global::System.Drawing.Rectangle(0, 0, bounds.Width, bounds.Height); - // Copy pixels from screen capture Texture to GDI bitmap - var mapDest = FinalImage.LockBits(boundsRect, ImageLockMode.WriteOnly, FinalImage.PixelFormat); - var sourcePtr = mapSource.DataPointer; - var destPtr = mapDest.Scan0; - for (var y = 0; y < bounds.Height; y++) - { - // Copy a single line - Utilities.CopyMemory(destPtr, sourcePtr, bounds.Width * 4); - - // Advance pointers - sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); - destPtr = IntPtr.Add(destPtr, mapDest.Stride); - } - - // Release source and dest locks - FinalImage.UnlockBits(mapDest); - _mDevice.ImmediateContext.UnmapSubresource(_desktopImageTexture, 0); - frame.DesktopImage = (Bitmap) FinalImage.Clone(); - } - - private void ReleaseFrame() - { - try - { - _mDeskDupl.ReleaseFrame(); - } - catch (SharpDXException ex) - { - if (ex.ResultCode.Failure) - throw new DesktopDuplicationException("Failed to release frame."); - } - } - } +#region + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using SharpDX; +using SharpDX.Direct3D; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using SharpDX.Mathematics.Interop; +using Device = SharpDX.Direct3D11.Device; +using MapFlags = SharpDX.Direct3D11.MapFlags; +using Point = System.Drawing.Point; +using Rectangle = SharpDX.Rectangle; +using Resource = SharpDX.DXGI.Resource; +using ResultCode = SharpDX.DXGI.ResultCode; + +#endregion + +namespace UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication +{ + /// + /// Provides access to frame-by-frame updates of a particular desktop (i.e. one monitor), with image and cursor + /// information. + /// + public class DesktopDuplicator + { + private readonly OutputDuplication _mDeskDupl; + private readonly Device _mDevice; + private readonly Texture2DDescription _mTextureDesc; + private readonly int _mWhichOutputDevice; + private Texture2D _desktopImageTexture; + + private Bitmap _finalImage1, _finalImage2; + private OutputDuplicateFrameInformation _frameInfo; + private bool _isFinalImage1; + private OutputDescription _mOutputDesc; + + /// + /// Duplicates the output of the specified monitor. + /// + /// The output device to duplicate (i.e. monitor). Begins with zero, which seems to correspond to the primary monitor. + public DesktopDuplicator(int whichMonitor) + : this(0, whichMonitor) { } + + /// + /// Duplicates the output of the specified monitor on the specified graphics adapter. + /// + /// The adapter which contains the desired outputs. + /// + /// The output device to duplicate (i.e. monitor). Begins with zero, which seems to + /// correspond to the primary monitor. + /// + public DesktopDuplicator(int whichGraphicsCardAdapter, int whichOutputDevice) + { + _mWhichOutputDevice = whichOutputDevice; + Adapter1 adapter; + try + { + adapter = new Factory1().GetAdapter1(whichGraphicsCardAdapter); + } + catch (SharpDXException) + { + throw new DesktopDuplicationException("Could not find the specified graphics card adapter."); + } + _mDevice = new Device(adapter); + Output output = null; + try + { + output = adapter.GetOutput(whichOutputDevice); + } + catch (SharpDXException) + { + throw new DesktopDuplicationException("Could not find the specified output device."); + } + var output1 = output.QueryInterface(); + _mOutputDesc = output.Description; + _mTextureDesc = new Texture2DDescription() + { + CpuAccessFlags = CpuAccessFlags.Read, + BindFlags = BindFlags.None, + Format = Format.B8G8R8A8_UNorm, + Width = ((Rectangle)_mOutputDesc.DesktopBounds).Width, + Height = ((Rectangle)_mOutputDesc.DesktopBounds).Height, + OptionFlags = ResourceOptionFlags.None, + MipLevels = 1, + ArraySize = 1, + SampleDescription = { Count = 1, Quality = 0 }, + Usage = ResourceUsage.Staging + }; + + try + { + _mDeskDupl = output1.DuplicateOutput(_mDevice); + } + catch (SharpDXException ex) + { + if (ex.ResultCode.Code == ResultCode.NotCurrentlyAvailable.Result.Code) + { + throw new DesktopDuplicationException("There is already the maximum number of applications using the Desktop Duplication API running, please close one of the applications and try again."); + } + } + } + + private Bitmap FinalImage + { + get => _isFinalImage1 ? _finalImage1 : _finalImage2; + set + { + if (_isFinalImage1) + { + _finalImage2 = value; + _finalImage1?.Dispose(); + } + else + { + _finalImage1 = value; + _finalImage2?.Dispose(); + } + _isFinalImage1 = !_isFinalImage1; + } + } + + /// + /// Finds the most capable graphics card. + /// + /// + private static Adapter1 GetBestAdapter(out int bestAdapterIndex) + { + using (var dxgiFactory = new Factory1()) + { + Adapter1 bestAdapter = null; + bestAdapterIndex = -1; + var adapterIndex = -1; + long bestVideoMemory = 0; + long bestSystemMemory = 0; + + foreach (var dxgiAdapter in dxgiFactory.Adapters1) + { + adapterIndex++; + + // not skip the render only WARP device + if (dxgiAdapter.Description.VendorId != 0x1414 || dxgiAdapter.Description.DeviceId != 0x8c) + if (dxgiAdapter.Outputs == null || dxgiAdapter.Outputs.Length == 0) + continue; + + var level = Device.GetSupportedFeatureLevel(dxgiAdapter); + + if (level < FeatureLevel.Level_11_0) + continue; + long videoMemory = (uint) dxgiAdapter.Description.DedicatedVideoMemory; + long systemMemory = (uint) dxgiAdapter.Description.DedicatedSystemMemory; + + if (bestAdapter != null && videoMemory <= bestVideoMemory && + (videoMemory != bestVideoMemory || systemMemory <= bestSystemMemory)) continue; + bestAdapter = dxgiAdapter; + bestAdapterIndex = adapterIndex; + bestVideoMemory = videoMemory; + bestSystemMemory = systemMemory; + } + + return bestAdapter; + } + } + + /// + /// Retrieves the latest desktop image and associated metadata. + /// + public DesktopFrame GetLatestFrame() + { + var frame = new DesktopFrame(); + // Try to get the latest frame; this may timeout + var retrievalTimedOut = RetrieveFrame(); + if (retrievalTimedOut) + return null; + try + { + RetrieveFrameMetadata(frame); + //we don't need cursor info + //RetrieveCursorMetadata(frame); + //we dont need a full frame + //ProcessFrame(frame); + } + catch + { + ReleaseFrame(); + } + try + { + ReleaseFrame(); + } + catch + { + // throw new DesktopDuplicationException("Couldn't release frame."); + } + return frame; + } + + private bool RetrieveFrame() + { + if (_desktopImageTexture == null) + _desktopImageTexture = new Texture2D(_mDevice, _mTextureDesc); + Resource desktopResource = null; + _frameInfo = new OutputDuplicateFrameInformation(); + try + { + _mDeskDupl.AcquireNextFrame(500, out _frameInfo, out desktopResource); + } + catch (SharpDXException ex) + { + if (ex.ResultCode.Code == ResultCode.WaitTimeout.Result.Code) + return true; + if (ex.ResultCode.Failure) + { + //return true; + desktopResource?.Dispose(); + throw new DesktopDuplicationException("Failed to acquire next frame."); + } + } + using (var tempTexture = desktopResource?.QueryInterface()) + { + _mDevice.ImmediateContext.CopyResource(tempTexture, _desktopImageTexture); + } + desktopResource?.Dispose(); + return false; + } + + private void RetrieveFrameMetadata(DesktopFrame frame) + { + if (_frameInfo.TotalMetadataBufferSize > 0) + { + // Get moved regions + var movedRegionsLength = 0; + var movedRectangles = new OutputDuplicateMoveRectangle[_frameInfo.TotalMetadataBufferSize]; + _mDeskDupl.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out movedRegionsLength); + frame.MovedRegions = + new MovedRegion[movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle))]; + for (var i = 0; i < frame.MovedRegions.Length; i++) + { + var destRect = (Rectangle) movedRectangles[i].DestinationRect; + frame.MovedRegions[i] = new MovedRegion + { + Source = new Point(movedRectangles[i].SourcePoint.X, movedRectangles[i].SourcePoint.Y), + Destination = + new System.Drawing.Rectangle(destRect.X, destRect.Y, destRect.Width, destRect.Height) + }; + } + + // Get dirty regions + var dirtyRegionsLength = 0; + var dirtyRectangles = new RawRectangle[_frameInfo.TotalMetadataBufferSize]; + _mDeskDupl.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out dirtyRegionsLength); + frame.UpdatedRegions = + new System.Drawing.Rectangle[dirtyRegionsLength / Marshal.SizeOf(typeof(Rectangle))]; + frame.FinishedRegions = new FinishedRegions[frame.UpdatedRegions.Length]; + for (var i = 0; i < frame.UpdatedRegions.Length; i++) + { + var dirtyRect = (Rectangle) dirtyRectangles[i]; + var rect = new System.Drawing.Rectangle(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, + dirtyRect.Height); + + + frame.FinishedRegions[i] = new FinishedRegions + { + Destination = rect, + Frame = ExtractRect(rect.X, rect.Y, rect.Width, rect.Height) + }; + } + } + else + { + frame.MovedRegions = new MovedRegion[0]; + frame.UpdatedRegions = new System.Drawing.Rectangle[0]; + } + } + + + private Bitmap ExtractRect(int originX, int originY, int width, int height) + { + // Get the desktop capture screenTexture + var mapSource = _mDevice.ImmediateContext.MapSubresource(_desktopImageTexture, 0, MapMode.Read, + MapFlags.None); + + // Create Drawing.Bitmap + + var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); //不能是ARGB + var boundsRect = new System.Drawing.Rectangle(0, 0, width, height); + + // Copy pixels from screen capture Texture to GDI bitmap + var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); + var sourcePtr = mapSource.DataPointer; + var destPtr = mapDest.Scan0; + + sourcePtr = IntPtr.Add(sourcePtr, originY * mapSource.RowPitch + originX * 4); + for (var y = 0; y < height; y++) + { + // Copy a single line + + SharpDX.Utilities.CopyMemory(destPtr, sourcePtr, width * 4); + + // Advance pointers + if (y != height - 1) + sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); + destPtr = IntPtr.Add(destPtr, mapDest.Stride); + } + + // Release source and dest locks + bitmap.UnlockBits(mapDest); + _mDevice.ImmediateContext.UnmapSubresource(_desktopImageTexture, 0); + return bitmap; + } + + private void ProcessFrame(DesktopFrame frame) + { + // Get the desktop capture texture + var mapSource = _mDevice.ImmediateContext.MapSubresource(_desktopImageTexture, 0, MapMode.Read, + MapFlags.None); + var bounds = (Rectangle) _mOutputDesc.DesktopBounds; + FinalImage = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); + var boundsRect = new System.Drawing.Rectangle(0, 0, bounds.Width, bounds.Height); + // Copy pixels from screen capture Texture to GDI bitmap + var mapDest = FinalImage.LockBits(boundsRect, ImageLockMode.WriteOnly, FinalImage.PixelFormat); + var sourcePtr = mapSource.DataPointer; + var destPtr = mapDest.Scan0; + for (var y = 0; y < bounds.Height; y++) + { + // Copy a single line + SharpDX.Utilities.CopyMemory(destPtr, sourcePtr, bounds.Width * 4); + + // Advance pointers + sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); + destPtr = IntPtr.Add(destPtr, mapDest.Stride); + } + + // Release source and dest locks + FinalImage.UnlockBits(mapDest); + _mDevice.ImmediateContext.UnmapSubresource(_desktopImageTexture, 0); + frame.DesktopImage = (Bitmap) FinalImage.Clone(); + } + + private void ReleaseFrame() + { + try + { + _mDeskDupl.ReleaseFrame(); + } + catch (SharpDXException ex) + { + if (ex.ResultCode.Failure) + throw new DesktopDuplicationException("Failed to release frame."); + } + } + } } \ No newline at end of file diff --git a/AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopFrame.cs b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopFrame.cs similarity index 90% rename from AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopFrame.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopFrame.cs index 1175568..4f3a903 100644 --- a/AgentInterface/Api/ScreenShare/DesktopDuplication/DesktopFrame.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/DesktopFrame.cs @@ -1,24 +1,21 @@ #region using System.Drawing; -using System.Runtime.Serialization; #endregion -namespace AgentInterface.Api.ScreenShare.DesktopDuplication +namespace UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication { /// /// Provides image data, cursor data, and image metadata about the retrieved desktop frame. /// - [DataContract] public class DesktopFrame { /// /// Gets the bitmap representing the last retrieved desktop frame. This image spans the entire bounds of the specified /// monitor. /// - [DataMember] public Bitmap DesktopImage { get; set; } @@ -30,7 +27,6 @@ public class DesktopFrame /// To produce a visually accurate copy of the desktop, an application must first process all moved regions before it /// processes updated regions. /// - [DataMember] public MovedRegion[] MovedRegions { get; set; } @@ -40,7 +36,6 @@ public class DesktopFrame /// /// To produce a visually accurate copy of the desktop, an application must first process all moved regions before it processes updated regions. /// - [DataMember] public Rectangle[] UpdatedRegions { get; set; } @@ -48,36 +43,30 @@ public class DesktopFrame /// The number of frames that the operating system accumulated in the desktop image surface since the last retrieved /// frame. /// - [DataMember] public int AccumulatedFrames { get; set; } /// /// Gets the location of the top-left-hand corner of the cursor. This is not necessarily the same position as the /// cursor's hot spot, which is the location in the cursor that interacts with other elements on the screen. /// - [DataMember] public Point CursorLocation { get; set; } /// /// Gets whether the cursor on the last retrieved desktop image was visible. /// - [DataMember] public bool CursorVisible { get; set; } /// /// Gets whether the desktop image contains protected content that was already blacked out in the desktop image. /// - [DataMember] public bool ProtectedContentMaskedOut { get; set; } /// /// Gets whether the operating system accumulated updates by coalescing updated regions. If so, the updated regions /// might contain unmodified pixels. /// - [DataMember] public bool RectanglesCoalesced { get; set; } - [DataMember] public FinishedRegions[] FinishedRegions { get; set; } } } \ No newline at end of file diff --git a/AgentInterface/Api/ScreenShare/DesktopDuplication/FinishedRegions.cs b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/FinishedRegions.cs similarity index 65% rename from AgentInterface/Api/ScreenShare/DesktopDuplication/FinishedRegions.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/FinishedRegions.cs index 06aeceb..1305fbe 100644 --- a/AgentInterface/Api/ScreenShare/DesktopDuplication/FinishedRegions.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/FinishedRegions.cs @@ -1,19 +1,14 @@ -using System; -using System.Drawing; -using System.Runtime.Serialization; +using System.Drawing; -namespace AgentInterface.Api.ScreenShare.DesktopDuplication +namespace UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication { - [DataContract] public class FinishedRegions { - [DataMember] public Bitmap Frame { get; internal set; } /// /// Gets the target region to where the operating system moved the image region. /// - [DataMember] public Rectangle Destination { get; internal set; } public void Dispose() diff --git a/AgentInterface/Api/ScreenShare/DesktopDuplication/MovedRegion.cs b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/MovedRegion.cs similarity index 91% rename from AgentInterface/Api/ScreenShare/DesktopDuplication/MovedRegion.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/MovedRegion.cs index 72f8e56..8bf8976 100644 --- a/AgentInterface/Api/ScreenShare/DesktopDuplication/MovedRegion.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/DesktopDuplication/MovedRegion.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace AgentInterface.Api.ScreenShare.DesktopDuplication +namespace UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication { /// /// Describes the movement of an image rectangle within a desktop frame. diff --git a/AgentInterface/Api/ScreenShare/ImageUtils.cs b/RemoteTaskServer/Api/Win32/ScreenShare/ImageUtils.cs similarity index 100% rename from AgentInterface/Api/ScreenShare/ImageUtils.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/ImageUtils.cs diff --git a/AgentInterface/Api/Models/DisplayInformation.cs b/RemoteTaskServer/Api/Win32/ScreenShare/Models/DisplayInformation.cs similarity index 95% rename from AgentInterface/Api/Models/DisplayInformation.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/Models/DisplayInformation.cs index 062492e..1031b98 100644 --- a/AgentInterface/Api/Models/DisplayInformation.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/Models/DisplayInformation.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace AgentInterface.Api.Models +namespace UlteriusServer.Api.Win32.ScreenShare.Models { public class DisplayInformation { diff --git a/AgentInterface/Api/Models/FrameInformation.cs b/RemoteTaskServer/Api/Win32/ScreenShare/Models/FrameInformation.cs similarity index 56% rename from AgentInterface/Api/Models/FrameInformation.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/Models/FrameInformation.cs index 2f9f057..6461e3a 100644 --- a/AgentInterface/Api/Models/FrameInformation.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/Models/FrameInformation.cs @@ -1,23 +1,22 @@ #region using System.Drawing; -using System.Runtime.Serialization; -using AgentInterface.Api.ScreenShare.DesktopDuplication; +using UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication; #endregion -namespace AgentInterface.Api.Models +namespace UlteriusServer.Api.Win32.ScreenShare.Models { - [DataContract] + public class FrameInformation { - [DataMember] + public Rectangle Bounds { get; set; } - [DataMember] + public Bitmap ScreenImage { get; set; } - [DataMember] + public bool UsingGpu { get; set; } - [DataMember] + public FinishedRegions[] FinishedRegions { get; set; } } } \ No newline at end of file diff --git a/AgentInterface/Api/Models/ResolutionInformation.cs b/RemoteTaskServer/Api/Win32/ScreenShare/Models/ResolutionInformation.cs similarity index 85% rename from AgentInterface/Api/Models/ResolutionInformation.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/Models/ResolutionInformation.cs index 9cdf86f..2e20c2e 100644 --- a/AgentInterface/Api/Models/ResolutionInformation.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/Models/ResolutionInformation.cs @@ -1,4 +1,4 @@ -namespace AgentInterface.Api.Models +namespace UlteriusServer.Api.Win32.ScreenShare.Models { public class ResolutionInformation { diff --git a/AgentInterface/Api/ScreenShare/ScreenData.cs b/RemoteTaskServer/Api/Win32/ScreenShare/ScreenData.cs similarity index 97% rename from AgentInterface/Api/ScreenShare/ScreenData.cs rename to RemoteTaskServer/Api/Win32/ScreenShare/ScreenData.cs index 2bff67a..83f43b1 100644 --- a/AgentInterface/Api/ScreenShare/ScreenData.cs +++ b/RemoteTaskServer/Api/Win32/ScreenShare/ScreenData.cs @@ -4,15 +4,14 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; -using AgentInterface.Api.Models; -using AgentInterface.Api.ScreenShare.DesktopDuplication; -using AgentInterface.Api.Win32; -using AgentInterface.Settings; using TurboJpegWrapper; +using UlteriusServer.Api.Win32.ScreenShare.DesktopDuplication; +using UlteriusServer.Api.Win32.ScreenShare.Models; +using UlteriusServer.Utilities; #endregion -namespace AgentInterface.Api.ScreenShare +namespace UlteriusServer.Api.Win32.ScreenShare { public static class ScreenData { @@ -257,7 +256,7 @@ private static DesktopFrame GetScreenPicDxgi() } catch (Exception) { - _desktopDuplicator = new DesktopDuplicator(); + _desktopDuplicator = new DesktopDuplicator(0); } return frame; } @@ -375,7 +374,7 @@ public static void SetupDuplication() } try { - _desktopDuplicator = new DesktopDuplicator(); + _desktopDuplicator = new DesktopDuplicator(0); _nullFrame = true; CanUseGpuAcceleration = true; Console.WriteLine("desktop duplication setup"); @@ -383,6 +382,7 @@ public static void SetupDuplication() catch (Exception ex) { CanUseGpuAcceleration = false; + Console.WriteLine("desktop duplication failed"); Console.WriteLine(ex.Message); } } diff --git a/AgentInterface/Api/Win32/SessionInfo.cs b/RemoteTaskServer/Api/Win32/SessionInfo.cs similarity index 99% rename from AgentInterface/Api/Win32/SessionInfo.cs rename to RemoteTaskServer/Api/Win32/SessionInfo.cs index cb87d07..508048d 100644 --- a/AgentInterface/Api/Win32/SessionInfo.cs +++ b/RemoteTaskServer/Api/Win32/SessionInfo.cs @@ -5,7 +5,7 @@ #endregion -namespace AgentInterface.Api.Win32 +namespace UlteriusServer.Api.Win32 { public static class SessionInfo { diff --git a/AgentInterface/Api/Win32/Win32Exception.cs b/RemoteTaskServer/Api/Win32/Win32Exception.cs similarity index 99% rename from AgentInterface/Api/Win32/Win32Exception.cs rename to RemoteTaskServer/Api/Win32/Win32Exception.cs index 94d92be..88a088e 100644 --- a/AgentInterface/Api/Win32/Win32Exception.cs +++ b/RemoteTaskServer/Api/Win32/Win32Exception.cs @@ -9,7 +9,7 @@ #endregion -namespace AgentInterface.Api.Win32 +namespace UlteriusServer.Api.Win32 { [SuppressUnmanagedCodeSecurity] [HostProtection(SecurityAction.LinkDemand, SharedState = true)] diff --git a/AgentInterface/Api/Win32/WinApi.cs b/RemoteTaskServer/Api/Win32/WinApi.cs similarity index 99% rename from AgentInterface/Api/Win32/WinApi.cs rename to RemoteTaskServer/Api/Win32/WinApi.cs index ea39397..e8806a0 100644 --- a/AgentInterface/Api/Win32/WinApi.cs +++ b/RemoteTaskServer/Api/Win32/WinApi.cs @@ -9,7 +9,7 @@ #endregion -namespace AgentInterface.Api.Win32 +namespace UlteriusServer.Api.Win32 { public class WinApi { diff --git a/AgentInterface/Api/Win32/WindowsIdentityImpersonator.cs b/RemoteTaskServer/Api/Win32/WindowsIdentityImpersonator.cs similarity index 98% rename from AgentInterface/Api/Win32/WindowsIdentityImpersonator.cs rename to RemoteTaskServer/Api/Win32/WindowsIdentityImpersonator.cs index 1e4a773..6b31eac 100644 --- a/AgentInterface/Api/Win32/WindowsIdentityImpersonator.cs +++ b/RemoteTaskServer/Api/Win32/WindowsIdentityImpersonator.cs @@ -6,7 +6,7 @@ using System.Security.Principal; using Microsoft.Win32.SafeHandles; -namespace AgentInterface.Api.Win32 +namespace UlteriusServer.Api.Win32 { /// Impersonates a windows identity. /// Based on: http://msdn.microsoft.com/en-us/library/w070t6ka.aspx diff --git a/RemoteTaskServer/App.config b/RemoteTaskServer/App.config index a80256e..beda779 100644 --- a/RemoteTaskServer/App.config +++ b/RemoteTaskServer/App.config @@ -1,72 +1,72 @@ - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RemoteTaskServer/FodyWeavers.xml b/RemoteTaskServer/FodyWeavers.xml deleted file mode 100644 index dd5af76..0000000 --- a/RemoteTaskServer/FodyWeavers.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - WindowsInput - AgentInterface - Newtonsoft.Json - - - \ No newline at end of file diff --git a/RemoteTaskServer/Forms/Utilities/UlteriusTray.cs b/RemoteTaskServer/Forms/Utilities/UlteriusTray.cs index 3bed889..fa11cde 100644 --- a/RemoteTaskServer/Forms/Utilities/UlteriusTray.cs +++ b/RemoteTaskServer/Forms/Utilities/UlteriusTray.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; -using AgentInterface.Settings; using UlteriusServer.Api.Services.Network; using UlteriusServer.Properties; using UlteriusServer.Utilities; diff --git a/RemoteTaskServer/Program.cs b/RemoteTaskServer/Program.cs index 5115023..308abae 100644 --- a/RemoteTaskServer/Program.cs +++ b/RemoteTaskServer/Program.cs @@ -7,12 +7,7 @@ using System.Reflection; using System.Runtime; using System.Runtime.InteropServices; -using AgentInterface.Settings; -using Topshelf; -using UlteriusServer.Forms.Utilities; using UlteriusServer.Utilities; -using UlteriusServer.Utilities.Usage; - #endregion namespace UlteriusServer @@ -38,50 +33,31 @@ private static void Main(string[] args) HideWindow(); try { - if ( - Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location)) - .Count() > 1) return; - - ProfileOptimization.SetProfileRoot(AppEnvironment.DataPath); - ProfileOptimization.StartProfile("Startup.Profile"); - - if (args.Length > 0) + if (Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location)) + .Count() > 1) { - HostFactory.Run(x => //1 - { - x.Service(s => //2 - { - s.ConstructUsing(name => new UlteriusAgent()); //3 - s.WhenStarted(tc => tc.Start()); //4 - s.WhenStopped(tc => tc.Stop()); - s.WhenSessionChanged((se, e, id) => { se.HandleEvent(e, id); }); //5 - }); - x.RunAsLocalSystem(); //6 - x.EnableSessionChanged(); - x.EnableServiceRecovery(r => { r.RestartService(1); }); - x.SetDescription("The server that powers Ulterius"); //7 - x.SetDisplayName("Ulterius Server"); //8 - x.SetServiceName("UlteriusServer"); //9 - }); + return; } - else + if (!Directory.Exists(AppEnvironment.DataPath)) { - var ulterius = new Ulterius(); - ulterius.Start(); - var hardware = new HardwareSurvey(); - hardware.Setup(); - if (Tools.RunningPlatform() == Tools.Platform.Windows) - UlteriusTray.ShowTray(); - else - Console.ReadKey(true); + Directory.CreateDirectory(AppEnvironment.DataPath); } + if (!Debugger.IsAttached) + { + ExceptionHandler.AddGlobalHandlers(); + Console.WriteLine("Exception Handlers Attached"); + } + ProfileOptimization.SetProfileRoot(AppEnvironment.DataPath); + ProfileOptimization.StartProfile("Startup.Profile"); + var ulterius = new Ulterius(); + ulterius.Start(); + Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("Something unexpected occured"); Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); - Console.Read(); } } diff --git a/RemoteTaskServer/Properties/AssemblyInfo.cs b/RemoteTaskServer/Properties/AssemblyInfo.cs index c633a38..dd8adff 100644 --- a/RemoteTaskServer/Properties/AssemblyInfo.cs +++ b/RemoteTaskServer/Properties/AssemblyInfo.cs @@ -39,5 +39,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.9.2.0")] -[assembly: AssemblyFileVersion("1.9.2.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.9.5.0")] +[assembly: AssemblyFileVersion("1.9.5.0")] \ No newline at end of file diff --git a/RemoteTaskServer/TerminalServer/Messaging/TerminalControl/Handlers/InputTerminalRequestHandler.cs b/RemoteTaskServer/TerminalServer/Messaging/TerminalControl/Handlers/InputTerminalRequestHandler.cs index cee0c06..722d51d 100644 --- a/RemoteTaskServer/TerminalServer/Messaging/TerminalControl/Handlers/InputTerminalRequestHandler.cs +++ b/RemoteTaskServer/TerminalServer/Messaging/TerminalControl/Handlers/InputTerminalRequestHandler.cs @@ -1,10 +1,10 @@ #region using System; -using AgentInterface.Settings; using UlteriusServer.TerminalServer.Infrastructure; using UlteriusServer.TerminalServer.Messaging.TerminalControl.Requests; using UlteriusServer.TerminalServer.Session; +using UlteriusServer.Utilities; using UlteriusServer.Utilities.Security; #endregion diff --git a/RemoteTaskServer/TerminalServer/Messaging/WebSocketHandler.cs b/RemoteTaskServer/TerminalServer/Messaging/WebSocketHandler.cs index e9bd2e6..f295f68 100644 --- a/RemoteTaskServer/TerminalServer/Messaging/WebSocketHandler.cs +++ b/RemoteTaskServer/TerminalServer/Messaging/WebSocketHandler.cs @@ -10,6 +10,7 @@ using UlteriusServer.TerminalServer.Messaging.Serialization; using UlteriusServer.TerminalServer.Session; using vtortola.WebSockets; +using ILogger = UlteriusServer.TerminalServer.Infrastructure.ILogger; #endregion @@ -73,7 +74,7 @@ public async Task HandleConnectionAsync(CancellationToken cancellation) { continue; } - Console.WriteLine(queueRequest); + queueRequest.ConnectionId = connectionId; _queue.Publish(queueRequest, type); @@ -84,7 +85,7 @@ public async Task HandleConnectionAsync(CancellationToken cancellation) _log.Error("Error Handling connection", aex.GetBaseException()); try { - _ws.Close(); + _ws.CloseAsync(); } catch { diff --git a/RemoteTaskServer/TerminalServer/Messaging/WebSocketQueueServer.cs b/RemoteTaskServer/TerminalServer/Messaging/WebSocketQueueServer.cs index 423caed..fc19b86 100644 --- a/RemoteTaskServer/TerminalServer/Messaging/WebSocketQueueServer.cs +++ b/RemoteTaskServer/TerminalServer/Messaging/WebSocketQueueServer.cs @@ -11,6 +11,7 @@ using UlteriusServer.TerminalServer.Session; using vtortola.WebSockets; using vtortola.WebSockets.Rfc6455; +using ILogger = UlteriusServer.TerminalServer.Infrastructure.ILogger; #endregion @@ -38,14 +39,13 @@ public WebSocketQueueServer(IPEndPoint endpoint, ISystemInfo sysinfo, ILogger lo sbc.UseBinarySerializer(); sbc.ReceiveFrom("loopback://localhost/queue"); }); - - _wsServer = new WebSocketListener(endpoint, new WebSocketListenerOptions + var options = new WebSocketListenerOptions { PingTimeout = Timeout.InfiniteTimeSpan, - OnHttpNegotiation = HttpNegotiation - }); - var rfc6455 = new WebSocketFactoryRfc6455(_wsServer); - _wsServer.Standards.RegisterStandard(rfc6455); + HttpAuthenticationHandler = this.HttpNegotiation + }; + options.Standards.RegisterRfc6455(); + _wsServer = new WebSocketListener(endpoint, options); } public void Dispose() @@ -58,13 +58,13 @@ public void Dispose() public Task StartAsync() { - _wsServer.Start(); - _log.Info("Echo Server started"); + _wsServer.StartAsync().GetAwaiter().GetResult(); return AcceptWebSocketClientsAsync(_wsServer); } - private void HttpNegotiation(WebSocketHttpRequest request, WebSocketHttpResponse response) + private async Task HttpNegotiation(WebSocketHttpRequest request, WebSocketHttpResponse response) { + await Task.Delay(TimeSpan.FromMilliseconds(1)); var connectionId = Guid.Empty; if (request.RequestUri == null || request.RequestUri.OriginalString.Length < 1 || !Guid.TryParse(request.RequestUri.OriginalString.Substring(1), out connectionId)) @@ -97,8 +97,10 @@ private void HttpNegotiation(WebSocketHttpRequest request, WebSocketHttpResponse res => { response.Cookies.Add(new Cookie(ConnectionManager.UserSessionCookieName, res.UserId.ToString())); + }); }); + return true; } private async Task AcceptWebSocketClientsAsync(WebSocketListener server) @@ -120,10 +122,11 @@ private async Task AcceptWebSocketClientsAsync(WebSocketListener server) } catch (Exception aex) { - _log.Error("Error Accepting clients", aex.GetBaseException()); + // _log.Error("Error Accepting clients", aex.GetBaseException()); } } - _log.Info("Server Stop accepting clients"); + + //_log.Info("Server Stop accepting clients"); } } } \ No newline at end of file diff --git a/RemoteTaskServer/Ulterius.cs b/RemoteTaskServer/Ulterius.cs index 3ac19b4..d7e8f4a 100644 --- a/RemoteTaskServer/Ulterius.cs +++ b/RemoteTaskServer/Ulterius.cs @@ -4,9 +4,9 @@ using System.Diagnostics; using System.IO; using System.Reflection; -using AgentInterface.Settings; using UlteriusServer.Api; using UlteriusServer.Api.Services.LocalSystem; +using UlteriusServer.Api.Win32; using UlteriusServer.TerminalServer; using UlteriusServer.Utilities; using UlteriusServer.WebCams; @@ -25,24 +25,6 @@ public void Start(bool serviceMode = false) { isService = serviceMode; - if (Process.GetProcessesByName( - Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location)) - .Length > 1) - { - Process.GetCurrentProcess().Kill(); - } - if (!Directory.Exists(AppEnvironment.DataPath)) - { - Directory.CreateDirectory(AppEnvironment.DataPath); - } - - if (!Debugger.IsAttached) - { - - ExceptionHandler.AddGlobalHandlers(); - Console.WriteLine("Exception Handlers Attached"); - } - Setup(); } @@ -53,18 +35,11 @@ private void Setup() { - + Tools.RestartDaemon(); Console.WriteLine("Creating settings"); - - var settings = Config.Load(); - - - + var settings = Config.Load(); Console.WriteLine("Configuring up server"); Tools.ConfigureServer(); - - - Console.WriteLine(Assembly.GetExecutingAssembly().GetName().Version); var useTerminal = settings.Terminal.AllowTerminal; var useWebServer = settings.WebServer.ToggleWebServer; @@ -83,7 +58,11 @@ private void Setup() Console.WriteLine("Creating system service"); systemService.Start(); UlteriusApiServer.RunningAsService = Tools.RunningAsService(); - Console.Write($"Service: {UlteriusApiServer.RunningAsService}"); + if (UlteriusApiServer.RunningAsService) + { + Console.Write($"Service: {UlteriusApiServer.RunningAsService}"); + DesktopWatcher.Start(); + } UlteriusApiServer.Start(); if (useTerminal) diff --git a/RemoteTaskServer/UlteriusAgent.cs b/RemoteTaskServer/UlteriusAgent.cs deleted file mode 100644 index 761cf29..0000000 --- a/RemoteTaskServer/UlteriusAgent.cs +++ /dev/null @@ -1,82 +0,0 @@ -#region - -using System; -using System.Diagnostics; -using Topshelf; -using UlteriusServer.Utilities; -using UlteriusServer.Utilities.Usage; - -#endregion - -namespace UlteriusServer -{ - public class UlteriusAgent - { - - - private Ulterius _ulterius; - - public void Start() - { - _ulterius = new Ulterius(); - _ulterius.Start(true); - HandleMonitor(); - var hardware = new HardwareSurvey(); - hardware.Setup(true); - Console.ReadLine(); - } - - - public void HandleMonitor() - { - Tools.RestartAgent(); - } - - - public void Stop() - { - var agentList = Process.GetProcessesByName("UlteriusAgent"); - foreach (var agent in agentList) - { - try - { - agent.Kill(); - } - catch (Exception) - { - // ignored - } - } - var managerList = Process.GetProcessesByName("DaemonManager"); - foreach (var manager in managerList) - { - try - { - manager.Kill(); - } - catch (Exception) - { - // ignored - } - } - - var serverInstanceList = Process.GetProcessesByName("Ulterius Server"); - foreach (var server in serverInstanceList) - { - try - { - server.Kill(); - } - catch (Exception) - { - // ignored - } - } - } - - public void HandleEvent(HostControl hostControl, SessionChangedArguments arg3) - { - HandleMonitor(); - } - } -} \ No newline at end of file diff --git a/RemoteTaskServer/UlteriusServer.csproj b/RemoteTaskServer/UlteriusServer.csproj index a0dcfcb..e6ca98d 100644 --- a/RemoteTaskServer/UlteriusServer.csproj +++ b/RemoteTaskServer/UlteriusServer.csproj @@ -1,461 +1,437 @@ - - - - - - - Debug - AnyCPU - {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46} - Exe - Properties - UlteriusServer - Ulterius Server - v4.6.1 - 512 - - - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - true - false - true - - - AnyCPU - true - full - true - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - 6 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - 6 - false - true - - - - - - app.manifest - - - 256.ico - - - - - - false - - - - - - - false - - - - - - - - - - - - - - - - - - - - - Form - - - AboutBox.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - - - - - - - - - - - - AboutBox.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - Designer - - - - - - - - - - - - - ..\packages\AForge.2.2.5\lib\AForge.dll - True - - - ..\packages\AForge.Imaging.2.2.5\lib\AForge.Imaging.dll - True - - - ..\packages\AForge.Math.2.2.5\lib\AForge.Math.dll - True - - - ..\packages\AForge.Video.2.2.5\lib\AForge.Video.dll - True - - - ..\packages\AForge.Video.DirectShow.2.2.5\lib\AForge.Video.DirectShow.dll - True - - - ..\packages\AForge.Vision.2.2.5\lib\AForge.Vision.dll - True - - - ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll - True - - - ..\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll - True - - - ..\packages\log4net.2.0.7\lib\net45-full\log4net.dll - True - - - ..\packages\Magnum.2.1.3\lib\NET40\Magnum.dll - True - - - ..\packages\MassTransit.2.9.0\lib\net40\MassTransit.dll - True - - - ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.3\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll - True - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll - True - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll - True - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll - True - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll - True - - - ..\packages\Newtonsoft.Json.10.0.1-beta1\lib\net45\Newtonsoft.Json.dll - - - ..\packages\Open.NAT.2.1.0.0\lib\net45\Open.Nat.dll - True - - - - - - - - - - - False - C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll - - - - - - - ..\packages\System.Threading.Tasks.Dataflow.4.7.0\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll - True - - - - - - - - ..\packages\Topshelf.4.0.3\lib\net452\Topshelf.dll - True - - - ..\packages\vtortola.WebSocketListener.2.2.2.0\lib\net45\vtortola.WebSockets.dll - True - - - ..\packages\vtortola.WebSocketListener.2.2.2.0\lib\net45\vtortola.WebSockets.Deflate.dll - True - - - ..\packages\vtortola.WebSocketListener.2.2.2.0\lib\net45\vtortola.WebSockets.Rfc6455.dll - True - - - - ..\packages\ZetaLongPaths.1.0.0.14\lib\net40-full\ZetaLongPaths.dll - True - - - - - {58FBCF7C-E7A9-467C-80B3-FC65E8FCCA08} - 1 - 0 - 0 - tlbimp - False - True - - - - - {3549cd6f-80f8-450f-b99e-cf0a736b1f2a} - WindowsInput - - - {5c3b0b17-cbb7-4b4b-b527-1fab2bb96466} - AgentInterface - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (); -var attribute = config.Attribute("ExcludeAssemblies"); -if (attribute != null) - foreach (var item in attribute.Value.Split('|').Select(x => x.Trim()).Where(x => x != string.Empty)) - excludedAssemblies.Add(item); -var element = config.Element("ExcludeAssemblies"); -if (element != null) - foreach (var item in element.Value.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Where(x => x != string.Empty)) - excludedAssemblies.Add(item); - -var filesToCleanup = Files.Select(f => f.ItemSpec).Where(f => !excludedAssemblies.Contains(Path.GetFileNameWithoutExtension(f), StringComparer.InvariantCultureIgnoreCase)); - -foreach (var item in filesToCleanup) - File.Delete(item); -]]> - - - - - - - - + + + + + + + Debug + AnyCPU + {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46} + Exe + Properties + UlteriusServer + Ulterius Server + v4.6.1 + 512 + + + + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + true + false + true + + + AnyCPU + true + full + true + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + 7 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + 7 + false + true + + + + + + app.manifest + + + 256.ico + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + AboutBox.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + AboutBox.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + + + + + + + + ..\packages\AForge.2.2.5\lib\AForge.dll + True + + + ..\packages\AForge.Imaging.2.2.5\lib\AForge.Imaging.dll + True + + + ..\packages\AForge.Math.2.2.5\lib\AForge.Math.dll + True + + + ..\packages\AForge.Video.2.2.5\lib\AForge.Video.dll + True + + + ..\packages\AForge.Video.DirectShow.2.2.5\lib\AForge.Video.DirectShow.dll + True + + + ..\packages\AForge.Vision.2.2.5\lib\AForge.Vision.dll + True + + + ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll + True + + + ..\packages\deniszykov.WebSocketListener.4.0.0\lib\net45\deniszykov.WebSocketListener.dll + + + ..\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll + True + + + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll + + + ..\packages\Magnum.2.1.3\lib\NET40\Magnum.dll + True + + + ..\packages\MassTransit.2.9.0\lib\net40\MassTransit.dll + True + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.3\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + True + + + ..\packages\JonSkeet.MiscUtil.0.2.0\lib\net35\MiscUtil.dll + + + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll + True + + + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll + True + + + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll + True + + + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll + True + + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Open.NAT.2.1.0.0\lib\net45\Open.Nat.dll + True + + + ..\Deps\OpenHardwareMonitorLib.dll + + + + ..\packages\SharpDX.4.0.1\lib\net45\SharpDX.dll + + + ..\packages\SharpDX.Direct3D11.4.0.1\lib\net45\SharpDX.Direct3D11.dll + + + ..\packages\SharpDX.DXGI.4.0.1\lib\net45\SharpDX.DXGI.dll + + + ..\packages\SharpDX.Mathematics.4.0.1\lib\net45\SharpDX.Mathematics.dll + + + + + + + + + + False + C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll + + + + + + + ..\packages\System.Threading.Tasks.Dataflow.4.8.0\lib\netstandard2.0\System.Threading.Tasks.Dataflow.dll + + + + + + + + ..\packages\Topshelf.4.0.3\lib\net452\Topshelf.dll + True + + + ..\packages\TurboJpegWrapper.1.4.2.6\lib\net35\TurboJpegWrapper.dll + + + + ..\packages\ZetaLongPaths.1.0.0.19\lib\net40-full\ZetaLongPaths.dll + + + + + {58FBCF7C-E7A9-467C-80B3-FC65E8FCCA08} + 1 + 0 + 0 + tlbimp + False + True + + + + + {3549cd6f-80f8-450f-b99e-cf0a736b1f2a} + WindowsInput + + + {686ae4c4-791f-45eb-9414-029d6115905d} + UlteriusAgent + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RemoteTaskServer/UlteriusServer.csproj.DotSettings b/RemoteTaskServer/UlteriusServer.csproj.DotSettings new file mode 100644 index 0000000..c54c126 --- /dev/null +++ b/RemoteTaskServer/UlteriusServer.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp70 \ No newline at end of file diff --git a/AgentInterface/Settings/AppEnvironment.cs b/RemoteTaskServer/Utilities/AppEnvironment.cs similarity index 99% rename from AgentInterface/Settings/AppEnvironment.cs rename to RemoteTaskServer/Utilities/AppEnvironment.cs index e80d3ff..95f4f28 100644 --- a/AgentInterface/Settings/AppEnvironment.cs +++ b/RemoteTaskServer/Utilities/AppEnvironment.cs @@ -6,7 +6,7 @@ #endregion -namespace AgentInterface.Settings +namespace UlteriusServer.Utilities { public class AppEnvironment { diff --git a/AgentInterface/Settings/Config.cs b/RemoteTaskServer/Utilities/Config.cs similarity index 99% rename from AgentInterface/Settings/Config.cs rename to RemoteTaskServer/Utilities/Config.cs index 1b44834..28dc8e0 100644 --- a/AgentInterface/Settings/Config.cs +++ b/RemoteTaskServer/Utilities/Config.cs @@ -6,7 +6,7 @@ #endregion -namespace AgentInterface.Settings +namespace UlteriusServer.Utilities { public class Config { diff --git a/RemoteTaskServer/Utilities/ExceptionHandler.cs b/RemoteTaskServer/Utilities/ExceptionHandler.cs index d52406d..d511464 100644 --- a/RemoteTaskServer/Utilities/ExceptionHandler.cs +++ b/RemoteTaskServer/Utilities/ExceptionHandler.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Threading; -using AgentInterface.Settings; using Newtonsoft.Json; #endregion diff --git a/RemoteTaskServer/Utilities/Extensions/ProcessExtensions.cs b/RemoteTaskServer/Utilities/Extensions/ProcessExtensions.cs new file mode 100644 index 0000000..7e596d1 --- /dev/null +++ b/RemoteTaskServer/Utilities/Extensions/ProcessExtensions.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Management; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace UlteriusServer.Utilities.Extensions +{ + public static class ProcessExtensions + { + [DllImport("user32.dll")] + public static extern IntPtr FindWindowEx(IntPtr parentWindow, IntPtr previousChildWindow, string windowClass, + string windowTitle); + + [DllImport("user32.dll")] + private static extern IntPtr GetWindowThreadProcessId(IntPtr window, out int process); + + public static IntPtr[] GetProcessWindows(this Process process) + { + var apRet = new IntPtr[256]; + var iCount = 0; + var pLast = IntPtr.Zero; + do + { + pLast = FindWindowEx(IntPtr.Zero, pLast, null, null); + int iProcess_; + GetWindowThreadProcessId(pLast, out iProcess_); + if (iProcess_ == process.Id) + { + apRet[iCount++] = pLast; + } + } while (pLast != IntPtr.Zero); + Array.Resize(ref apRet, iCount); + return apRet; + } + + private static string FindIndexedProcessName(int pid) + { + try + { + var processName = Process.GetProcessById(pid).ProcessName; + var processesByName = Process.GetProcessesByName(processName); + string processIndexdName = null; + for (var index = 0; index < processesByName.Length; index++) + { + processIndexdName = index == 0 ? processName : processName + "#" + index; + var processId = new PerformanceCounter("Process", "ID Process", processIndexdName); + if ((int)processId.NextValue() == pid) + { + return processIndexdName; + } + } + return processIndexdName; + } + catch (Exception) + { + return null; + } + } + + public static List GetChildrenById(int id) + { + try + { + var mos = new ManagementObjectSearcher( + $"Select * From Win32_Process Where ParentProcessID={id}"); + return (from ManagementObject mo in mos.Get() + select Process.GetProcessById(Convert.ToInt32(mo["ProcessID"]))).ToList(); + } + catch (Exception) + { + return null; + } + } + + public static List GetChildProcesses(this Process process) + { + try + { + var mos = new ManagementObjectSearcher( + $"Select * From Win32_Process Where ParentProcessID={process.Id}"); + return (from ManagementObject mo in mos.Get() + select Process.GetProcessById(Convert.ToInt32(mo["ProcessID"]))).ToList(); + } + catch (Exception) + { + return null; + } + } + + public static bool IsService(this Process process) + { + if (process == null) + { + return false; + } + using (var searcher = + new ManagementObjectSearcher("SELECT * FROM Win32_Service WHERE ProcessId =" + "\"" + process.Id + + "\"")) + { + if (searcher.Get().Cast().Any()) + { + return true; + } + } + return false; + } + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId); + + [DllImport("psapi.dll")] + private static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, + [In] [MarshalAs(UnmanagedType.U4)] int nSize); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CloseHandle(IntPtr hObject); + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetProcessName(this Process proc) + { + var processHandle = OpenProcess(0x0400 | 0x0010, false, proc.Id); + + if (processHandle == IntPtr.Zero) + { + return null; + } + + const int lengthSb = 4000; + + var sb = new StringBuilder(lengthSb); + + string result = null; + + if (GetModuleFileNameEx(processHandle, IntPtr.Zero, sb, lengthSb) > 0) + { + result = Path.GetFileName(sb.ToString()); + } + + CloseHandle(processHandle); + + return result; + } + + public static Process GetProcess(string path) + { + try + { + return Process.GetProcessesByName(Path.GetFileNameWithoutExtension(path)).FirstOrDefault(); + } + catch + { + + return null; + } + } + + private static Process FindPidFromIndexedProcessName(string indexedProcessName) + { + try + { + var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName); + return Process.GetProcessById((int)parentId.NextValue()); + } + catch (Exception) + { + return null; + } + } + + + public static void KillProcessAndChildren(this Process process) + { + try + { + var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + process.Id); + var moc = searcher.Get(); + foreach (var o in moc) + { + var mo = (ManagementObject)o; + var child = Process.GetProcessById(Convert.ToInt32(mo["ProcessID"])); + KillProcessAndChildren(child); + child.Kill(); + } + process.Kill(); + } + catch + { + // Process already exited. + } + } + + public static bool ProgramIsRunning(string fullPath) + { + return Process.GetProcessesByName(Path.GetFileNameWithoutExtension(fullPath)).Count() > 1; + } + + public static Process Parent(this Process process) + { + return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id)); + } + } +} \ No newline at end of file diff --git a/RemoteTaskServer/Utilities/Extensions/StringExtensions.cs b/RemoteTaskServer/Utilities/Extensions/StringExtensions.cs index 6388c7f..7f63dea 100644 --- a/RemoteTaskServer/Utilities/Extensions/StringExtensions.cs +++ b/RemoteTaskServer/Utilities/Extensions/StringExtensions.cs @@ -1,427 +1,505 @@ -#region - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Web.UI; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -#endregion - -namespace UlteriusServer.Utilities.Extensions -{ - /// - /// Enum FormatTokenFlags - /// - public enum FormatTokenFlags - { - /// - /// Uses the specifier token as the replacement token, matches '%' but not '%%' - /// - SpecifierToken, - - /// - /// Like String.Format - /// - IndexToken, - - /// - /// Extracts an Object's Members by name. - /// - MemberToken - } - - /// - /// Class StringExtensions - /// - public static class StringExtensions - { - - // Returns the human-readable file totalSize for an arbitrary, 64-bit file totalSize - // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" - - public static string UnicodeUtf8(this string strFrom) - { - var bytSrc = Encoding.Unicode.GetBytes(strFrom); - var bytDestination = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, bytSrc); - var strTo = Encoding.UTF8.GetString(bytDestination); - return strTo; - } - - /// - /// Implement's VB's Like operator logic. - /// - public static bool IsLike(this string s, string pattern) - { - // Characters matched so far - int matched = 0; - - // Loop through pattern string - for (int i = 0; i < pattern.Length;) - { - // Check for end of string - if (matched > s.Length) - return false; - - // Get next pattern character - char c = pattern[i++]; - if (c == '[') // Character list - { - // Test for exclude character - bool exclude = (i < pattern.Length && pattern[i] == '!'); - if (exclude) - i++; - // Build character list - int j = pattern.IndexOf(']', i); - if (j < 0) - j = s.Length; - HashSet charList = CharListToSet(pattern.Substring(i, j - i)); - i = j + 1; - - if (charList.Contains(s[matched]) == exclude) - return false; - matched++; - } - else if (c == '?') // Any single character - { - matched++; - } - else if (c == '#') // Any single digit - { - if (!Char.IsDigit(s[matched])) - return false; - matched++; - } - else if (c == '*') // Zero or more characters - { - if (i < pattern.Length) - { - // Matches all characters until - // next character in pattern - char next = pattern[i]; - int j = s.IndexOf(next, matched); - if (j < 0) - return false; - matched = j; - } - else - { - // Matches all remaining characters - matched = s.Length; - break; - } - } - else // Exact character - { - if (matched >= s.Length || c != s[matched]) - return false; - matched++; - } - } - // Return true if all characters matched - return (matched == s.Length); - } - - /// - /// Converts a string of characters to a HashSet of characters. If the string - /// contains character ranges, such as A-Z, all characters in the range are - /// also added to the returned set of characters. - /// - /// Character list string - private static HashSet CharListToSet(string charList) - { - HashSet set = new HashSet(); - - for (int i = 0; i < charList.Length; i++) - { - if ((i + 1) < charList.Length && charList[i + 1] == '-') - { - // Character range - char startChar = charList[i++]; - i++; // Hyphen - char endChar = (char)0; - if (i < charList.Length) - endChar = charList[i++]; - for (int j = startChar; j <= endChar; j++) - set.Add((char)j); - } - else set.Add(charList[i]); - } - return set; - } - - public static string GetBytesReadable(long i) - { - // Get absolute value - var absoluteI = i < 0 ? -i : i; - // Determine the suffix and readable value - string suffix; - double readable; - if (absoluteI >= 0x1000000000000000) // Exabyte - { - suffix = "EB"; - readable = i >> 50; - } - else if (absoluteI >= 0x4000000000000) // Petabyte - { - suffix = "PB"; - readable = i >> 40; - } - else if (absoluteI >= 0x10000000000) // Terabyte - { - suffix = "TB"; - readable = i >> 30; - } - else if (absoluteI >= 0x40000000) // Gigabyte - { - suffix = "GB"; - readable = i >> 20; - } - else if (absoluteI >= 0x100000) // Megabyte - { - suffix = "MB"; - readable = i >> 10; - } - else if (absoluteI >= 0x400) // Kilobyte - { - suffix = "KB"; - readable = i; - } - else - { - return i.ToString("0 B"); // Byte - } - // Divide by 1024 to get fractional value - readable = readable / 1024; - // Return formatted number with suffix - return readable.ToString("0.### ") + suffix; - } - - /// - /// Returns a Secure string from the source string - /// - /// - /// - public static SecureString ToSecureString(this string Source) - { - if (string.IsNullOrWhiteSpace(Source)) - return null; - var Result = new SecureString(); - foreach (var c in Source.ToCharArray()) - Result.AppendChar(c); - return Result; - } - public static string ToUnsecureString(this SecureString secureString) - { - if (secureString == null) throw new ArgumentNullException("secureString"); - - var unmanagedString = IntPtr.Zero; - try - { - unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString); - return Marshal.PtrToStringUni(unmanagedString); - } - finally - { - Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); - } - } - - - /// - /// The Reformatting function called to format the string with a list of arguments. - /// - private static Func, string, IList, string> _regexReformatter; - - /// - /// The Regex which matches the specifier token in strings. - /// - private static Regex _specifierTokenRegex; - - /// - /// The Regex which matches parameter indexes in strings, same as string.Format. - /// - private static Regex _argumentIndexRegex; - - /// - /// The Regex which matches object member names in strings. - /// - private static Regex _objectMemberRegex; - - /// - /// Splits a string into an array of strings, the string is split by commas. - /// - /// The string instance to split by commas. - /// The string that has been comma separated into substrings. - public static string[] CommaSeparate(this string @this) - { - return @this.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - } - - /// - /// Determines whether two String objects contain the same data, ignoring the case of the letters in the String; uses - /// Ordinal comparison. - /// - /// The current string to be compared to. - /// The other string to compare against the current String for equality. - /// true if the two strings are equal, false otherwise - public static bool EqualsIgnoreCase(this string @this, string other) - { - return string.Equals(@this, other, StringComparison.OrdinalIgnoreCase); - } - - public static bool IsBase64String(this string s) - { - s = s.Trim(); - return (s.Length%4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); - } - - public static bool IsValidJson(this string strInput) - { - strInput = strInput.Trim(); - if ((strInput.StartsWith("{") && strInput.EndsWith("}")) || //For object - (strInput.StartsWith("[") && strInput.EndsWith("]"))) //For array - { - try - { - JToken.Parse(strInput); - return true; - } - catch (JsonReaderException) - { - //Exception in parsing json - return false; - } - catch (Exception) //some other exception - { - return false; - } - } - return false; - } - - - /// - /// Replaces one or more format items in a specified string with the string representation of a specified object. - /// - /// - /// A copy of in which any format items are replaced by the string representation of - /// . - /// - /// A composite format string. - /// The arguments to use in formatting . - public static string Form(this string format, params object[] values) - { - var specifierTokenMatches = _specifierTokenRegex.Matches(format); - var indexTokenMatches = _argumentIndexRegex.Matches(format); - var memberNameMatches = _objectMemberRegex.Matches(format); - - if (memberNameMatches.Count > 0 && values.Length == 1) - { - format = FormatString(format, FormatTokenFlags.MemberToken, values); - } - else - { - if (indexTokenMatches.Count > 0) - { - format = FormatString(format, FormatTokenFlags.IndexToken, values); - } - - if (specifierTokenMatches.Count > 0) - { - format = FormatString(format, FormatTokenFlags.SpecifierToken, values); - } - } - return format; - } - - public static string CreateMD5(this string input) - { - // Use input string to calculate MD5 hash - using (var md5 = MD5.Create()) - { - var inputBytes = Encoding.ASCII.GetBytes(input); - var hashBytes = md5.ComputeHash(inputBytes); - - // Convert the byte array to hexadecimal string - var sb = new StringBuilder(); - foreach (var t in hashBytes) - { - sb.Append(t.ToString("X2")); - } - return sb.ToString(); - } - } - - /// - /// The Function which does all the heavy lifting. - /// - /// A composite format string - /// The flags which specify how the string should be interpreted. - /// The arguments to format the parameter with. - /// System.String. - public static string FormatString(string format, FormatTokenFlags flags, params object[] arguments) - { - switch (flags) - { - case FormatTokenFlags.IndexToken: - return string.Format(format, arguments); - - case FormatTokenFlags.SpecifierToken: - var stringBuilder = new StringBuilder(); - for (int i = 0, - argIndex = 0; - i < format.Length; - i++) - { - stringBuilder.Append(format[i] == '%' && argIndex < arguments.Length - ? "{" + argIndex++ + "}" - : format.Substring(i, 1)); - } - return string.Format(stringBuilder.ToString(), arguments); - - case FormatTokenFlags.MemberToken: - return - string.Format( - _regexReformatter(name => name == "0" ? arguments[0] : DataBinder.Eval(arguments[0], name), - format, arguments), arguments); - - default: - return format; - } - } - - public static void Initialize() - { - const RegexOptions regexOptions = RegexOptions.Compiled | RegexOptions.CultureInvariant; - _objectMemberRegex = new Regex(@"(?(\{))+(?[\w\.]+)(?(\}))+", regexOptions); - _specifierTokenRegex = new Regex(@"(? - { - var argumentCollection = new List(); - var rewrittenFormat = _objectMemberRegex.Replace(format, match => - { - Group startGroup = match.Groups["start"], - propertyGroup = match.Groups["property"], - endGroup = match.Groups["end"]; - - var result = valueFetcher(propertyGroup.Value); - - argumentCollection.Add(result); - var index = argumentCollection.Count - 1; - var fmt = new string('{', startGroup.Captures.Count) + index + - new string('}', endGroup.Captures.Count); - return string.Format(fmt, argumentCollection.ToArray()); - }); - - return rewrittenFormat; - }; - } - } +#region + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Web.UI; +using MassTransit.Util; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +#endregion + +namespace UlteriusServer.Utilities.Extensions +{ + /// + /// Enum FormatTokenFlags + /// + public enum FormatTokenFlags + { + /// + /// Uses the specifier token as the replacement token, matches '%' but not '%%' + /// + SpecifierToken, + + /// + /// Like String.Format + /// + IndexToken, + + /// + /// Extracts an Object's Members by name. + /// + MemberToken + } + + /// + /// Class StringExtensions + /// + public static class StringExtensions + { + + // Returns the human-readable file totalSize for an arbitrary, 64-bit file totalSize + // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" + + public static string UnicodeUtf8(this string strFrom) + { + var bytSrc = Encoding.Unicode.GetBytes(strFrom); + var bytDestination = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, bytSrc); + var strTo = Encoding.UTF8.GetString(bytDestination); + return strTo; + } + + /// + /// Implement's VB's Like operator logic. + /// + public static bool IsLike(this string s, string pattern) + { + // Characters matched so far + var matched = 0; + + // Loop through pattern string + for (var i = 0; i < pattern.Length;) + { + // Check for end of string + if (matched > s.Length) + return false; + + // Get next pattern character + var c = pattern[i++]; + if (c == '[') // Character list + { + // Test for exclude character + var exclude = (i < pattern.Length && pattern[i] == '!'); + if (exclude) + i++; + // Build character list + var j = pattern.IndexOf(']', i); + if (j < 0) + j = s.Length; + var charList = CharListToSet(pattern.Substring(i, j - i)); + i = j + 1; + + if (charList.Contains(s[matched]) == exclude) + return false; + matched++; + } + else if (c == '?') // Any single character + { + matched++; + } + else if (c == '#') // Any single digit + { + if (!Char.IsDigit(s[matched])) + return false; + matched++; + } + else if (c == '*') // Zero or more characters + { + if (i < pattern.Length) + { + // Matches all characters until + // next character in pattern + var next = pattern[i]; + var j = s.IndexOf(next, matched); + if (j < 0) + return false; + matched = j; + } + else + { + // Matches all remaining characters + matched = s.Length; + break; + } + } + else // Exact character + { + if (matched >= s.Length || c != s[matched]) + return false; + matched++; + } + } + // Return true if all characters matched + return (matched == s.Length); + } + /// + /// Checks if a string is a valid email + /// + /// + /// + public static bool IsValidEmail(this string email) + { + try + { + var addr = new System.Net.Mail.MailAddress(email); + return addr.Address == email; + } + catch + { + return false; + } + } + + /// + /// Returns true if starts with the path . + /// The comparison is case-insensitive, handles / and \ slashes as folder separators and + /// only matches if the base dir folder name is matched exactly ("c:\foobar\file.txt" is not a sub path of "c:\foo"). + /// + public static bool IsSubPathOf(this string path, string baseDirPath) + { + var normalizedPath = Path.GetFullPath(path.Replace('/', '\\') + .WithEnding("\\")); + + var normalizedBaseDirPath = Path.GetFullPath(baseDirPath.Replace('/', '\\') + .WithEnding("\\")); + + return normalizedPath.StartsWith(normalizedBaseDirPath, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Returns with the minimal concatenation of (starting from end) that + /// results in satisfying .EndsWith(ending). + /// + /// "hel".WithEnding("llo") returns "hello", which is the result of "hel" + "lo". + public static string WithEnding([CanBeNull] this string str, string ending) + { + if (str == null) + return ending; + + var result = str; + + // Right() is 1-indexed, so include these cases + // * Append no characters + // * Append up to N characters, where N is ending length + for (var i = 0; i <= ending.Length; i++) + { + var tmp = result + ending.Right(i); + if (tmp.EndsWith(ending)) + return tmp; + } + + return result; + } + + /// Gets the rightmost characters from a string. + /// The string to retrieve the substring from. + /// The number of characters to retrieve. + /// The substring. + public static string Right([NotNull] this string value, int length) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + if (length < 0) + { + throw new ArgumentOutOfRangeException("length", length, "Length is less than zero"); + } + + return (length < value.Length) ? value.Substring(value.Length - length) : value; + } + + /// + /// Converts a string of characters to a HashSet of characters. If the string + /// contains character ranges, such as A-Z, all characters in the range are + /// also added to the returned set of characters. + /// + /// Character list string + private static HashSet CharListToSet(string charList) + { + var set = new HashSet(); + + for (var i = 0; i < charList.Length; i++) + { + if ((i + 1) < charList.Length && charList[i + 1] == '-') + { + // Character range + var startChar = charList[i++]; + i++; // Hyphen + var endChar = (char)0; + if (i < charList.Length) + endChar = charList[i++]; + for (int j = startChar; j <= endChar; j++) + set.Add((char)j); + } + else set.Add(charList[i]); + } + return set; + } + + public static string GetBytesReadable(long i) + { + // Get absolute value + var absoluteI = i < 0 ? -i : i; + // Determine the suffix and readable value + string suffix; + double readable; + if (absoluteI >= 0x1000000000000000) // Exabyte + { + suffix = "EB"; + readable = i >> 50; + } + else if (absoluteI >= 0x4000000000000) // Petabyte + { + suffix = "PB"; + readable = i >> 40; + } + else if (absoluteI >= 0x10000000000) // Terabyte + { + suffix = "TB"; + readable = i >> 30; + } + else if (absoluteI >= 0x40000000) // Gigabyte + { + suffix = "GB"; + readable = i >> 20; + } + else if (absoluteI >= 0x100000) // Megabyte + { + suffix = "MB"; + readable = i >> 10; + } + else if (absoluteI >= 0x400) // Kilobyte + { + suffix = "KB"; + readable = i; + } + else + { + return i.ToString("0 B"); // Byte + } + // Divide by 1024 to get fractional value + readable = readable / 1024; + // Return formatted number with suffix + return readable.ToString("0.### ") + suffix; + } + + /// + /// Returns a Secure string from the source string + /// + /// + /// + public static SecureString ToSecureString(this string Source) + { + if (string.IsNullOrWhiteSpace(Source)) + return null; + var Result = new SecureString(); + foreach (var c in Source.ToCharArray()) + Result.AppendChar(c); + return Result; + } + public static string ToUnsecureString(this SecureString secureString) + { + if (secureString == null) throw new ArgumentNullException("secureString"); + + var unmanagedString = IntPtr.Zero; + try + { + unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString); + return Marshal.PtrToStringUni(unmanagedString); + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); + } + } + + + /// + /// The Reformatting function called to format the string with a list of arguments. + /// + private static Func, string, IList, string> _regexReformatter; + + /// + /// The Regex which matches the specifier token in strings. + /// + private static Regex _specifierTokenRegex; + + /// + /// The Regex which matches parameter indexes in strings, same as string.Format. + /// + private static Regex _argumentIndexRegex; + + /// + /// The Regex which matches object member names in strings. + /// + private static Regex _objectMemberRegex; + + /// + /// Splits a string into an array of strings, the string is split by commas. + /// + /// The string instance to split by commas. + /// The string that has been comma separated into substrings. + public static string[] CommaSeparate(this string @this) + { + return @this.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + } + + /// + /// Determines whether two String objects contain the same data, ignoring the case of the letters in the String; uses + /// Ordinal comparison. + /// + /// The current string to be compared to. + /// The other string to compare against the current String for equality. + /// true if the two strings are equal, false otherwise + public static bool EqualsIgnoreCase(this string @this, string other) + { + return string.Equals(@this, other, StringComparison.OrdinalIgnoreCase); + } + + public static bool IsBase64String(this string s) + { + s = s.Trim(); + return (s.Length%4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); + } + + public static bool IsValidJson(this string strInput) + { + strInput = strInput.Trim(); + if ((strInput.StartsWith("{") && strInput.EndsWith("}")) || //For object + (strInput.StartsWith("[") && strInput.EndsWith("]"))) //For array + { + try + { + JToken.Parse(strInput); + return true; + } + catch (JsonReaderException) + { + //Exception in parsing json + return false; + } + catch (Exception) //some other exception + { + return false; + } + } + return false; + } + + + /// + /// Replaces one or more format items in a specified string with the string representation of a specified object. + /// + /// + /// A copy of in which any format items are replaced by the string representation of + /// . + /// + /// A composite format string. + /// The arguments to use in formatting . + public static string Form(this string format, params object[] values) + { + var specifierTokenMatches = _specifierTokenRegex.Matches(format); + var indexTokenMatches = _argumentIndexRegex.Matches(format); + var memberNameMatches = _objectMemberRegex.Matches(format); + + if (memberNameMatches.Count > 0 && values.Length == 1) + { + format = FormatString(format, FormatTokenFlags.MemberToken, values); + } + else + { + if (indexTokenMatches.Count > 0) + { + format = FormatString(format, FormatTokenFlags.IndexToken, values); + } + + if (specifierTokenMatches.Count > 0) + { + format = FormatString(format, FormatTokenFlags.SpecifierToken, values); + } + } + return format; + } + + public static string CreateMD5(this string input) + { + // Use input string to calculate MD5 hash + using (var md5 = MD5.Create()) + { + var inputBytes = Encoding.ASCII.GetBytes(input); + var hashBytes = md5.ComputeHash(inputBytes); + + // Convert the byte array to hexadecimal string + var sb = new StringBuilder(); + foreach (var t in hashBytes) + { + sb.Append(t.ToString("X2")); + } + return sb.ToString(); + } + } + + /// + /// The Function which does all the heavy lifting. + /// + /// A composite format string + /// The flags which specify how the string should be interpreted. + /// The arguments to format the parameter with. + /// System.String. + public static string FormatString(string format, FormatTokenFlags flags, params object[] arguments) + { + switch (flags) + { + case FormatTokenFlags.IndexToken: + return string.Format(format, arguments); + + case FormatTokenFlags.SpecifierToken: + var stringBuilder = new StringBuilder(); + for (int i = 0, + argIndex = 0; + i < format.Length; + i++) + { + stringBuilder.Append(format[i] == '%' && argIndex < arguments.Length + ? "{" + argIndex++ + "}" + : format.Substring(i, 1)); + } + return string.Format(stringBuilder.ToString(), arguments); + + case FormatTokenFlags.MemberToken: + return + string.Format( + _regexReformatter(name => name == "0" ? arguments[0] : DataBinder.Eval(arguments[0], name), + format, arguments), arguments); + + default: + return format; + } + } + + public static void Initialize() + { + const RegexOptions regexOptions = RegexOptions.Compiled | RegexOptions.CultureInvariant; + _objectMemberRegex = new Regex(@"(?(\{))+(?[\w\.]+)(?(\}))+", regexOptions); + _specifierTokenRegex = new Regex(@"(? + { + var argumentCollection = new List(); + var rewrittenFormat = _objectMemberRegex.Replace(format, match => + { + Group startGroup = match.Groups["start"], + propertyGroup = match.Groups["property"], + endGroup = match.Groups["end"]; + + var result = valueFetcher(propertyGroup.Value); + + argumentCollection.Add(result); + var index = argumentCollection.Count - 1; + var fmt = new string('{', startGroup.Captures.Count) + index + + new string('}', endGroup.Captures.Count); + return string.Format(fmt, argumentCollection.ToArray()); + }); + + return rewrittenFormat; + }; + } + } } \ No newline at end of file diff --git a/RemoteTaskServer/Utilities/Security/AuthUtils.cs b/RemoteTaskServer/Utilities/Security/AuthUtils.cs index cf2d9e3..2562058 100644 --- a/RemoteTaskServer/Utilities/Security/AuthUtils.cs +++ b/RemoteTaskServer/Utilities/Security/AuthUtils.cs @@ -3,8 +3,9 @@ using System; using System.Diagnostics; using System.Security.Principal; -using AgentInterface.Api.Win32; using UlteriusServer.Api.Network.Models; +using UlteriusServer.Api.Win32; +using UlteriusServer.Utilities.Extensions; #endregion @@ -48,7 +49,7 @@ private static bool AuthMacOs(string password) return false; } } - + public static LoginInformation AuthWindows(string username, string password) { var info = new LoginInformation(); @@ -56,6 +57,10 @@ public static LoginInformation AuthWindows(string username, string password) { var domainName = Environment.UserDomainName; + if (username.IsValidEmail()) + { + domainName = "MicrosoftAccount"; + } if (username.Contains("\\")) { var splitName = username.Split('\\'); diff --git a/RemoteTaskServer/Utilities/Tools.cs b/RemoteTaskServer/Utilities/Tools.cs index e6b8123..7b9c173 100644 --- a/RemoteTaskServer/Utilities/Tools.cs +++ b/RemoteTaskServer/Utilities/Tools.cs @@ -15,12 +15,12 @@ using System.Threading; using System.Threading.Tasks; using System.Web; -using AgentInterface.Api.ScreenShare; -using AgentInterface.Api.Win32; -using AgentInterface.Settings; using Ionic.Zip; using NetFwTypeLib; using Open.Nat; +using UlteriusServer.Api.Win32; +using UlteriusServer.Api.Win32.ScreenShare; +using UlteriusServer.Utilities.Extensions; using static System.Security.Principal.WindowsIdentity; using Task = System.Threading.Tasks.Task; @@ -82,17 +82,14 @@ public static void RestartDaemon() try { if (Process.GetProcessesByName("DaemonManager").Length != 0) return; - ProcessStarter.PROCESS_INFORMATION managerInfo; + var managerPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "DaemonManager.exe"); - ProcessStarter.StartProcessAndBypassUAC(managerPath, - out managerInfo); - managerProcess = Process.GetProcessById((int)managerInfo.dwProcessId); + managerProcess = Process.Start(managerPath); } catch (Exception) { - - + // } } @@ -111,17 +108,11 @@ public static void RestartAgent() } Thread.Sleep(3000); - ProcessStarter.PROCESS_INFORMATION agentInfo; - - - var agentPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),"UlteriusAgent.exe"); - + var agentPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),"UlteriusAgent.exe"); - ProcessStarter.StartProcessAndBypassUAC(agentPath, - out agentInfo); + agentProcess = Process.Start(agentPath); - agentProcess = Process.GetProcessById((int)agentInfo.dwProcessId); if (agentProcess != null) { Console.WriteLine("Started Monitor on " + _CurrentSession); @@ -343,10 +334,7 @@ public static void ConfigureServer() { Console.WriteLine("Logs Ready"); } - if (!RunningAsService()) - { - ScreenData.SetupDuplication(); - } + ScreenData.SetupDuplication(); if (Config.Empty) { if (RunningPlatform() == Platform.Windows) @@ -361,15 +349,7 @@ public static void ConfigureServer() var username = Environment.GetEnvironmentVariable("USERNAME"); var userdomain = Environment.GetEnvironmentVariable("USERDOMAIN"); var command = $@"/C netsh http add urlacl url={prefix} user={userdomain}\{username} listen=yes"; - if (RunningAsService()) - { - ProcessStarter.PROCESS_INFORMATION procInfo; - ProcessStarter.StartProcessAndBypassUAC("CMD.exe " + command, out procInfo); - } - else - { - Process.Start("CMD.exe", command); - } + Process.Start("CMD.exe", command); OpenFirewallPort(webcamPort, "Ulterius Web Cams"); OpenFirewallPort(webServerPort, "Ulterius Web Server"); OpenFirewallPort(apiPort, "Ulterius Task Server"); @@ -399,17 +379,17 @@ private static void OpenFirewallForProgram(string exeFileName, string displayNam { FileName = "netsh", Arguments = - string.Format( - "firewall add allowedprogram program=\"{0}\" name=\"{1}\" profile=\"ALL\"", - exeFileName, displayName), + $"firewall add allowedprogram program=\"{exeFileName}\" name=\"{displayName}\" profile=\"ALL\"", WindowStyle = ProcessWindowStyle.Hidden }); - proc.WaitForExit(); + proc?.WaitForExit(); } public static bool RunningAsService() { - return GetCurrent().Name.ToLower().Contains(@"nt authority\system"); + var me = Process.GetCurrentProcess(); + var parent = me.Parent(); + return parent != null && parent.IsService(); } diff --git a/RemoteTaskServer/Utilities/Trace.cs b/RemoteTaskServer/Utilities/Trace.cs index 9195037..f3e4a8e 100644 --- a/RemoteTaskServer/Utilities/Trace.cs +++ b/RemoteTaskServer/Utilities/Trace.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; -using AgentInterface.Settings; using UlteriusServer.Utilities.Extensions; #endregion diff --git a/RemoteTaskServer/Utilities/Usage/HardwareSurvey.cs b/RemoteTaskServer/Utilities/Usage/HardwareSurvey.cs deleted file mode 100644 index 3f1d8de..0000000 --- a/RemoteTaskServer/Utilities/Usage/HardwareSurvey.cs +++ /dev/null @@ -1,151 +0,0 @@ -#region - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Management; -using System.Net.Http; -using System.Reflection; -using System.Windows.Forms; -using Microsoft.Win32; -using Newtonsoft.Json; -using UlteriusServer.Api.Network.Models; -using UlteriusServer.Properties; -using SystemInformation = UlteriusServer.Api.Network.Models.SystemInformation; - -#endregion - -namespace UlteriusServer.Utilities.Usage -{ - public class HardwareSurvey - { - private static readonly string results = "surveryresults.json"; - - private List GetGpuInformation() - { - var searcher = - new ManagementObjectSearcher("SELECT * FROM Win32_VideoController"); - - var gpus = (from ManagementBaseObject mo in searcher.Get() - select new GpuInformation - { - Name = mo["Name"]?.ToString(), - ScreenInfo = mo["VideoModeDescription"]?.ToString(), - DriverVersion = mo["DriverVersion"]?.ToString(), - RefreshRate = int.Parse(mo["CurrentRefreshRate"]?.ToString() ?? "0"), - AdapterRam = mo["AdapterRAM"]?.ToString(), - VideoArchitecture = int.Parse(mo["VideoArchitecture"]?.ToString() ?? "0"), - VideoMemoryType = int.Parse(mo["VideoMemoryType"]?.ToString() ?? "0"), - InstalledDisplayDrivers = mo["InstalledDisplayDrivers"]?.ToString()?.Split(','), - AdapterCompatibility = mo["AdapterCompatibility"]?.ToString(), - Status = mo["Status"]?.ToString(), - Availability = int.Parse(mo["Availability"]?.ToString() ?? "0") - }).ToList(); - return gpus; - } - - private void Prompt() - { - var dialogResult = MessageBox.Show(Resources.HardwareSurvey_Setup_, - Resources.HardwareSurvey_Setup_Usage_statistics_reporting, MessageBoxButtons.YesNo); - switch (dialogResult) - { - case DialogResult.Yes: - SendData(); - break; - case DialogResult.No: - File.WriteAllText(results, "false"); - break; - } - } - - public void Setup(bool service = false) - { - if (File.Exists(results)) - { - var days = (DateTime.Now - File.GetCreationTime(results)).TotalDays; - if (days > 30) - { - SendData(); - } - } - else - { - if (service) - { - SendData(); - } - else - { - Prompt(); - } - } - } - - public string GetMachineGuid() - { - try - { - var location = @"SOFTWARE\Microsoft\Cryptography"; - var name = "MachineGuid"; - using (var localMachineX64View = - RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)) - { - using (var rk = localMachineX64View.OpenSubKey(location)) - { - if (rk == null) - throw new KeyNotFoundException( - $"Key Not Found: {location}"); - - var machineGuid = rk.GetValue(name); - if (machineGuid == null) - throw new IndexOutOfRangeException( - $"Index Not Found: {name}"); - return machineGuid.ToString().Replace("-", "").ToUpper(); - } - } - } - catch (Exception) - { - return Guid.NewGuid().ToString("N").ToUpper(); - } - } - - private async void SendData() - { - try - { - var data = new - { - ServerInfo = OperatingSystemInformation.ToObject(), - SystemInfo = SystemInformation.ToObject(), - UlteriusVersion = Assembly.GetExecutingAssembly().GetName().Version, - GpuInfo = GetGpuInformation(), - NetworkInformation = NetworkInformation.ToObject(), - CpuInfo = CpuInformation.ToObject() - }; - var json = JsonConvert.SerializeObject(data); - var content = new FormUrlEncodedContent(new[] - { - new KeyValuePair("guid", GetMachineGuid()), - new KeyValuePair("results", json) - }); - using (var client = new HttpClient()) - { - client.Timeout = new TimeSpan(0, 0, 0, 5); - var result = await client.PostAsync("https://api.ulterius.io/hardware/", content); - if (result.IsSuccessStatusCode) - { - Console.WriteLine("Hardware Survery Completed"); - File.WriteAllText(results, "true"); - } - } - } - catch (Exception) - { - //just fail - } - } - } -} \ No newline at end of file diff --git a/RemoteTaskServer/WebCams/WebCamManager.cs b/RemoteTaskServer/WebCams/WebCamManager.cs index 47d3846..ee8a24b 100644 --- a/RemoteTaskServer/WebCams/WebCamManager.cs +++ b/RemoteTaskServer/WebCams/WebCamManager.cs @@ -10,7 +10,7 @@ using AForge.Video; using AForge.Video.DirectShow; using AForge.Vision.Motion; -using AgentInterface.Settings; +using UlteriusServer.Utilities; #endregion diff --git a/RemoteTaskServer/WebServer/HttpServer.cs b/RemoteTaskServer/WebServer/HttpServer.cs index 4c7aaec..a0e9334 100644 --- a/RemoteTaskServer/WebServer/HttpServer.cs +++ b/RemoteTaskServer/WebServer/HttpServer.cs @@ -9,10 +9,11 @@ using System.Text; using System.Threading; using System.Web; -using AgentInterface.Settings; using Newtonsoft.Json; using UlteriusServer.Api.Services.Network; using UlteriusServer.Properties; +using UlteriusServer.Utilities; +using UlteriusServer.Utilities.Extensions; using UlteriusServer.Utilities.Files; using UlteriusServer.WebServer.RemoteTaskServer.WebServer; using File = System.IO.File; @@ -286,13 +287,19 @@ var indexFile in } filename = HttpUtility.UrlDecode(Path.Combine(_rootDirectory, filename)); - if (File.Exists(filename)) { + try { + var targetPath = Path.GetDirectoryName(new DirectoryInfo(filename).FullName); + //prevent directory traversal, we should only allow access to the root directory of the HTTP Server. + if (!targetPath.IsSubPathOf(_rootDirectory)) + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return; + } Stream input = new FileStream(filename, FileMode.Open); - //Adding permanent http response headers string mime; diff --git a/RemoteTaskServer/WebSocketAPI/WebSocketEventListener.cs b/RemoteTaskServer/WebSocketAPI/WebSocketEventListener.cs index 6e52b68..7b60b3d 100644 --- a/RemoteTaskServer/WebSocketAPI/WebSocketEventListener.cs +++ b/RemoteTaskServer/WebSocketAPI/WebSocketEventListener.cs @@ -26,30 +26,22 @@ namespace UlteriusServer.WebSocketAPI public class WebSocketEventListener : IDisposable { - private readonly List _listeners = new List(); + private WebSocketListener _listener; - public WebSocketEventListener(List endpoints) + public WebSocketEventListener(Uri[] endpoints) : this(endpoints, new WebSocketListenerOptions()) { } - public WebSocketEventListener(List endpoints, WebSocketListenerOptions options) + public WebSocketEventListener(Uri[] endpoints, WebSocketListenerOptions options) { - foreach (var endpoint in endpoints) - { - var listener = new WebSocketListener(endpoint, options); - var rfc6455 = new WebSocketFactoryRfc6455(listener); - listener.Standards.RegisterStandard(rfc6455); - _listeners.Add(listener); - } + options.Standards.RegisterRfc6455(); + _listener = new WebSocketListener(endpoints, options); } public void Dispose() { - foreach (var listener in _listeners) - { - listener.Dispose(); - } + _listener?.StopAsync().GetAwaiter().GetResult(); } public event WebSocketEventListenerOnConnect OnConnect; @@ -58,21 +50,15 @@ public void Dispose() public event WebSocketEventListenerOnPlainTextMessage OnPlainTextMessage; public event WebSocketEventListenerOnError OnError; - public void Start() + public async void Start() { - foreach (var listener in _listeners) - { - listener.Start(); - } + await _listener.StartAsync(); ListenAsync(); } - public void Stop() + public async void Stop() { - foreach (var listener in _listeners) - { - listener.Stop(); - } + await _listener.StopAsync(); } private async Task HandleListners(WebSocketListener listener) @@ -95,10 +81,7 @@ private async Task HandleListners(WebSocketListener listener) private void ListenAsync() { - foreach (var listener in _listeners) - { - Task.Run(() => HandleListners(listener)); - } + Task.Run(() => HandleListners(_listener)); } private async Task HandleWebSocketAsync(WebSocket websocket) diff --git a/RemoteTaskServer/packages.config b/RemoteTaskServer/packages.config index 14abec1..681595a 100644 --- a/RemoteTaskServer/packages.config +++ b/RemoteTaskServer/packages.config @@ -1,27 +1,30 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UlteriusAgent/App.config b/UlteriusAgent/App.config index 4c129f4..07549d0 100644 --- a/UlteriusAgent/App.config +++ b/UlteriusAgent/App.config @@ -1,22 +1,22 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + diff --git a/UlteriusAgent/FodyWeavers.xml b/UlteriusAgent/FodyWeavers.xml deleted file mode 100644 index dd5af76..0000000 --- a/UlteriusAgent/FodyWeavers.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - WindowsInput - AgentInterface - Newtonsoft.Json - - - \ No newline at end of file diff --git a/UlteriusAgent/Networking/FrameAgent.cs b/UlteriusAgent/Networking/FrameAgent.cs deleted file mode 100644 index 6dcd81a..0000000 --- a/UlteriusAgent/Networking/FrameAgent.cs +++ /dev/null @@ -1,87 +0,0 @@ -#region - -using System; -using System.Drawing; -using System.ServiceModel; -using AgentInterface; -using AgentInterface.Api.Models; -using AgentInterface.Api.ScreenShare; -using AgentInterface.Api.Win32; - - -#endregion - -namespace UlteriusAgent.Networking -{ - [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] - public class FrameAgent : IFrameContract - { - private string _lastDesktop; - private Desktop _lastDesktopInput; - - - public FrameInformation GetCleanFrame() - { - HandleDesktop(); - var setCurrent = Desktop.SetCurrent(_lastDesktopInput); - return !setCurrent ? null : ScreenData.DesktopCapture(); - } - - public FrameInformation GetFullFrame() - { - HandleDesktop(); - var tempBounds = Display.GetWindowRectangle(); - var frameInfo = new FrameInformation - { - Bounds = tempBounds, - ScreenImage = ScreenData.CaptureDesktop() - }; - if (frameInfo.ScreenImage != null) return frameInfo; - var bmp = new Bitmap(frameInfo.Bounds.Width, frameInfo.Bounds.Height); - using (var gfx = Graphics.FromImage(bmp)) - using (var brush = new SolidBrush(Color.FromArgb(67, 75, 99))) - { - gfx.FillRectangle(brush, 0, 0, frameInfo.Bounds.Width, frameInfo.Bounds.Height); - } - frameInfo.ScreenImage = bmp; - return frameInfo; - } - - public bool KeepAlive() - { - return true; - } - - private void HandleDesktop() - { - using (var inputDesktop = new Desktop()) - { - inputDesktop.OpenInput(); - if (!inputDesktop.DesktopName.Equals(_lastDesktop)) - { - var switched = inputDesktop.Show(); - - if (switched) - { - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - Console.WriteLine($"Desktop switched from {_lastDesktop} to {inputDesktop.DesktopName}"); - _lastDesktop = inputDesktop.DesktopName; - _lastDesktopInput = inputDesktop; - } - else - { - _lastDesktopInput.Close(); - } - } - } - else - { - inputDesktop.Close(); - } - } - - } - } -} \ No newline at end of file diff --git a/UlteriusAgent/Networking/InputAgent.cs b/UlteriusAgent/Networking/InputAgent.cs deleted file mode 100644 index 89bd962..0000000 --- a/UlteriusAgent/Networking/InputAgent.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.ExceptionServices; -using System.ServiceModel; -using System.Text; -using System.Threading.Tasks; -using WindowsInput; -using WindowsInput.Native; -using AgentInterface; -using AgentInterface.Api.Models; -using AgentInterface.Api.System; -using AgentInterface.Api.Win32; - -namespace UlteriusAgent.Networking -{ - [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] - public class InputAgent : IInputContract - { - - public void HandleRightMouseDown() - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - new InputSimulator().Mouse.RightButtonDown(); - } - } - - public void HandleRightMouseUp() - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - new InputSimulator().Mouse.RightButtonUp(); - } - - } - - public void MoveMouse(int x, int y) - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - var bounds = Display.GetWindowRectangle(); - x = checked((int)Math.Round(x * (65535 / (double)bounds.Width))); - y = checked((int)Math.Round(y * (65535 / (double)bounds.Height))); - new InputSimulator().Mouse.MoveMouseTo(x, y); - } - } - - public void MouseScroll(bool positive) - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - var direction = positive ? 10 : -10; - new InputSimulator().Mouse.VerticalScroll(direction); - } - - } - - - public void HandleLeftMouseDown() - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - new InputSimulator().Mouse.LeftButtonDown(); - } - - - } - - public void HandleLeftMouseUp() - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - new InputSimulator().Mouse.LeftButtonUp(); - } - - - } - - public void HandleKeyDown(List keyCodes) - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - foreach (var code in keyCodes) - { - var virtualKey = (VirtualKeyCode)code; - new InputSimulator().Keyboard.KeyDown(virtualKey); - - } - } - - } - - public void HandleKeyUp(List keyCodes) - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - foreach (var code in keyCodes) - { - var virtualKey = (VirtualKeyCode)code; - new InputSimulator().Keyboard.KeyUp(virtualKey); - } - } - - } - - public void HandleRightClick() - { - var inputDesktop = new Desktop(); - inputDesktop.OpenInput(); - var setCurrent = Desktop.SetCurrent(inputDesktop); - if (setCurrent) - { - new InputSimulator().Mouse.RightButtonClick(); - } - } - - [HandleProcessCorruptedStateExceptions] - public float GetGpuTemp(string gpuName) - { - return SystemData.GetGpuTemp(gpuName); - } - - public List GetDisplayInformation() - { - return Display.DisplayInformation(); - } - - - public List GetCpuTemps() - { - return SystemData.GetCpuTemps(); - } - } -} diff --git a/UlteriusAgent/Networking/Tools.cs b/UlteriusAgent/Networking/Tools.cs deleted file mode 100644 index e076639..0000000 --- a/UlteriusAgent/Networking/Tools.cs +++ /dev/null @@ -1,34 +0,0 @@ -#region - -using System; -using System.Diagnostics; -using System.Linq; - -#endregion - -namespace UlteriusAgent.Networking -{ - public static class Tools - { - public static void KillAllButMe() - { - try - { - var current = Process.GetCurrentProcess(); - //kill any other agent that may be running - var processes = Process.GetProcessesByName(current.ProcessName) - .Where(t => t.Id != current.Id) - .ToList(); - foreach (var process in processes) - { - process.Kill(); - process.WaitForExit(); - } - } - catch (Exception) - { - // ignored - } - } - } -} \ No newline at end of file diff --git a/UlteriusAgent/Program.cs b/UlteriusAgent/Program.cs index cc4f4aa..39a12a7 100644 --- a/UlteriusAgent/Program.cs +++ b/UlteriusAgent/Program.cs @@ -1,101 +1,44 @@ #region -using System; -using System.Collections.ObjectModel; -using System.Net.Security; -using System.Runtime.InteropServices; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.ServiceModel.Description; -using System.ServiceModel.Dispatcher; -using AgentInterface; -using AgentInterface.Api.ScreenShare; -using UlteriusAgent.Networking; +using Topshelf; +using Warden.Core; #endregion namespace UlteriusAgent { - internal class Program { - private const int SW_HIDE = 0; - private const int SW_SHOW = 5; - - [DllImport("user32.dll")] - private static extern bool SetProcessDPIAware(); - - [DllImport("kernel32.dll")] - private static extern IntPtr GetConsoleWindow(); - - [DllImport("user32.dll")] - private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - - private static void Main(string[] args) { - if (Environment.OSVersion.Version.Major >= 6) + WardenManager.Initialize(new WardenOptions { - SetProcessDPIAware(); - } - var handle = GetConsoleWindow(); - - // Hide - ShowWindow(handle, SW_HIDE); - - Tools.KillAllButMe(); - try + DeepKill = true, + CleanOnExit = true, + ReadFileHeaders = false + }); + HostFactory.Run(x => //1 { - ScreenData.SetupDuplication(); - var inputAddress = "net.tcp://localhost/ulterius/agent/input/"; - var frameAddress = "net.pipe://localhost/ulterius/agent/frames/"; - - var inputService = new ServiceHost(typeof(InputAgent)); - var frameService = new ServiceHost(typeof(FrameAgent)); - var inputBinding = new NetTcpBinding - { - Security = new NetTcpSecurity - { - Transport = {ProtectionLevel = ProtectionLevel.None}, - Mode = SecurityMode.None - }, - MaxReceivedMessageSize = int.MaxValue - }; - var frameBinding = new NetNamedPipeBinding + x.Service(s => //2 { - Security = new NetNamedPipeSecurity - { - Transport = { ProtectionLevel = ProtectionLevel.None }, - Mode = NetNamedPipeSecurityMode.None - }, - MaxReceivedMessageSize = int.MaxValue - }; - inputService.AddServiceEndpoint(typeof(IInputContract), inputBinding, inputAddress); - frameService.AddServiceEndpoint(typeof(IFrameContract), frameBinding, frameAddress); - inputService.Opened += delegate(object sender, EventArgs eventArgs) - { - Console.WriteLine("Input started"); - }; - frameService.Opened += delegate (object sender, EventArgs eventArgs) + s.ConstructUsing(name => new UlteriusAgent()); //3 + s.WhenStarted(tc => tc.Start()); //4 + s.WhenStopped(tc => tc.Stop()); + s.WhenSessionChanged((se, e, id) => { se.HandleEvent(e, id); }); //5 + }); + x.OnException(ex => { - Console.WriteLine("Frame started"); - }; - inputService.Open(); - frameService.Open(); - Console.WriteLine("Test"); - Console.Read(); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message + " \n " + ex.StackTrace); - } - Console.Read(); - } - - private static void host_faulted(object sender, EventArgs e) - { - + //TODO Logging + }); + x.RunAsLocalSystem(); //6 + x.EnableSessionChanged(); + x.EnableServiceRecovery(r => { r.RestartService(1); }); + x.SetDescription("The server that powers Ulterius"); //7 + x.SetDisplayName("Ulterius Server"); //8 + x.SetServiceName("UlteriusServer"); //9 + x.StartAutomaticallyDelayed(); + }); } } } \ No newline at end of file diff --git a/UlteriusAgent/UlteriusAgent.cs b/UlteriusAgent/UlteriusAgent.cs new file mode 100644 index 0000000..d02fccd --- /dev/null +++ b/UlteriusAgent/UlteriusAgent.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Topshelf; +using Warden.Core; +using Warden.Core.Utils; + +namespace UlteriusAgent +{ + public class UlteriusAgent + { + private readonly string _ulteriusPath; + private WardenProcess _ulteriusInstance; + + + public UlteriusAgent() + { + const string ulteriusFileName = "Ulterius Server.exe"; + _ulteriusPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ulteriusFileName); + } + + public void Start() + { + if (!File.Exists(_ulteriusPath)) + { + throw new InvalidOperationException($"Unable to locate Ulterius at {_ulteriusPath}"); + } + if (Respawn()) + { + Console.WriteLine("Rainway started as a service!"); + Console.ReadLine(); + } + else + { + Console.WriteLine("Failed to start Rainway!"); + Environment.Exit(1); + } + } + + private bool Respawn() + { + _ulteriusInstance = null; + _ulteriusInstance = WardenProcess.Start(_ulteriusPath, string.Empty, null, true).GetAwaiter().GetResult(); + if (_ulteriusInstance == null || !_ulteriusInstance.IsTreeActive()) + { + return false; + } + _ulteriusInstance.OnStateChange += UlteriusInstanceOnOnStateChange; + return true; + } + + public void Stop() + { + const string ulteriusFileName = "Ulterius Server.exe"; + if (_ulteriusInstance != null) + { + _ulteriusInstance?.Kill(); + WardenManager.Flush(_ulteriusInstance.Id); + } + _ulteriusInstance = null; + EndProcessTree(ulteriusFileName); + } + + private void EndProcessTree(string imageName) + { + try + { + var taskKill = new TaskKill + { + Arguments = new List() + { + TaskSwitch.Force, + TaskSwitch.TerminateChildren, + TaskSwitch.ImageName.SetValue(imageName) + } + }; + taskKill.Execute(out var output, out var errror); + } + catch + { + // + } + } + + private void UlteriusInstanceOnOnStateChange(object sender, StateEventArgs stateEventArgs) + { + if (stateEventArgs.Id == _ulteriusInstance.Id && stateEventArgs.State == ProcessState.Dead) + { + //Kill the entire tree. + _ulteriusInstance.Kill(); + WardenManager.Flush(_ulteriusInstance.Id); + if (Respawn()) + { + Console.WriteLine("Rainway restarted!"); + } + } + } + + public void HandleEvent(HostControl hostControl, SessionChangedArguments arg3) + { + + } + } +} diff --git a/UlteriusAgent/UlteriusAgent.csproj b/UlteriusAgent/UlteriusAgent.csproj index 730ede3..1c07b1b 100644 --- a/UlteriusAgent/UlteriusAgent.csproj +++ b/UlteriusAgent/UlteriusAgent.csproj @@ -1,121 +1,71 @@ - - - - - Debug - AnyCPU - {686AE4C4-791F-45EB-9414-029D6115905D} - Exe - Properties - UlteriusAgent - UlteriusAgent - v4.5.2 - 512 - true - - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - - - app.manifest - - - - - - - - - - - - - - - - - - Designer - - - - - - - - - {3549cd6f-80f8-450f-b99e-cf0a736b1f2a} - WindowsInput - - - {5c3b0b17-cbb7-4b4b-b527-1fab2bb96466} - AgentInterface - - - - - - - - - - - - - - - (); -var attribute = config.Attribute("ExcludeAssemblies"); -if (attribute != null) - foreach (var item in attribute.Value.Split('|').Select(x => x.Trim()).Where(x => x != string.Empty)) - excludedAssemblies.Add(item); -var element = config.Element("ExcludeAssemblies"); -if (element != null) - foreach (var item in element.Value.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Where(x => x != string.Empty)) - excludedAssemblies.Add(item); - -var filesToCleanup = Files.Select(f => f.ItemSpec).Where(f => !excludedAssemblies.Contains(Path.GetFileNameWithoutExtension(f), StringComparer.InvariantCultureIgnoreCase)); - -foreach (var item in filesToCleanup) - File.Delete(item); -]]> - - - - - - + + + + + Debug + AnyCPU + {686AE4C4-791F-45EB-9414-029D6115905D} + Exe + Properties + UlteriusAgent + UlteriusAgent + v4.6.1 + 512 + true + + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + 7 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + 7 + + + + + + app.manifest + + + + + + ..\packages\Topshelf.4.0.3\lib\net452\Topshelf.dll + + + ..\packages\Warden.NET.1.2.3\lib\net45\Warden.dll + True + + + + + + + + + + Designer + + + + + \ No newline at end of file diff --git a/UlteriusAgent/packages.config b/UlteriusAgent/packages.config new file mode 100644 index 0000000..1c40d5b --- /dev/null +++ b/UlteriusAgent/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/UlteriusServer.sln b/UlteriusServer.sln index 87dc310..c327ab5 100644 --- a/UlteriusServer.sln +++ b/UlteriusServer.sln @@ -1,49 +1,44 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UlteriusServer", "RemoteTaskServer\UlteriusServer.csproj", "{E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}" - ProjectSection(ProjectDependencies) = postProject - {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466} = {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466} - {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} = {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} - {686AE4C4-791F-45EB-9414-029D6115905D} = {686AE4C4-791F-45EB-9414-029D6115905D} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UlteriusAgent", "UlteriusAgent\UlteriusAgent.csproj", "{686AE4C4-791F-45EB-9414-029D6115905D}" - ProjectSection(ProjectDependencies) = postProject - {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466} = {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466} - {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} = {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentInterface", "AgentInterface\AgentInterface.csproj", "{5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInput", "..\inputsimulator2\WindowsInput\WindowsInput.csproj", "{3549CD6F-80F8-450F-B99E-CF0A736B1F2A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Release|Any CPU.Build.0 = Release|Any CPU - {686AE4C4-791F-45EB-9414-029D6115905D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {686AE4C4-791F-45EB-9414-029D6115905D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {686AE4C4-791F-45EB-9414-029D6115905D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {686AE4C4-791F-45EB-9414-029D6115905D}.Release|Any CPU.Build.0 = Release|Any CPU - {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5C3B0B17-CBB7-4B4B-B527-1FAB2BB96466}.Release|Any CPU.Build.0 = Release|Any CPU - {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2005 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UlteriusServer", "RemoteTaskServer\UlteriusServer.csproj", "{E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}" + ProjectSection(ProjectDependencies) = postProject + {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} = {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} + {686AE4C4-791F-45EB-9414-029D6115905D} = {686AE4C4-791F-45EB-9414-029D6115905D} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UlteriusAgent", "UlteriusAgent\UlteriusAgent.csproj", "{686AE4C4-791F-45EB-9414-029D6115905D}" + ProjectSection(ProjectDependencies) = postProject + {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} = {3549CD6F-80F8-450F-B99E-CF0A736B1F2A} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInput", "..\..\..\inputsimulator2\WindowsInput\WindowsInput.csproj", "{3549CD6F-80F8-450F-B99E-CF0A736B1F2A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9A365D5-AEBE-4AF3-86C5-D17BC65ADE46}.Release|Any CPU.Build.0 = Release|Any CPU + {686AE4C4-791F-45EB-9414-029D6115905D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {686AE4C4-791F-45EB-9414-029D6115905D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {686AE4C4-791F-45EB-9414-029D6115905D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {686AE4C4-791F-45EB-9414-029D6115905D}.Release|Any CPU.Build.0 = Release|Any CPU + {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3549CD6F-80F8-450F-B99E-CF0A736B1F2A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9CC02739-8FB9-4530-9C74-0436AF2D60A7} + EndGlobalSection +EndGlobal